diff --git a/build/artifacts/artifacts.lock.yaml b/build/artifacts/artifacts.lock.yaml index 8205692c0223..6bf156400c3f 100644 --- a/build/artifacts/artifacts.lock.yaml +++ b/build/artifacts/artifacts.lock.yaml @@ -2,19 +2,3 @@ metadata: version: "1.0" artifacts: - # ms-vscode.js-debug-companion - - download_url: https://open-vsx.org/api/ms-vscode/js-debug-companion/1.1.3/file/ms-vscode.js-debug-companion-1.1.3.vsix - filename: ms-vscode.js-debug-companion.1.1.3.vsix - checksum: sha256:7380a890787452f14b2db7835dfa94de538caf358ebc263f9d46dd68ac52de93 - # ms-vscode.js-debug - - download_url: https://open-vsx.org/api/ms-vscode/js-debug/1.117.0/file/ms-vscode.js-debug-1.117.0.vsix - filename: ms-vscode.js-debug.1.117.0.vsix - checksum: sha256:854eeb8a785b1f41ba2bd02d7ccd4fdbe10021b61473293973c7e96d036c7fb8 - # ms-vscode.vscode-js-profile-table - - download_url: https://open-vsx.org/api/ms-vscode/vscode-js-profile-table/1.0.10/file/ms-vscode.vscode-js-profile-table-1.0.10.vsix - filename: ms-vscode.vscode-js-profile-table.1.0.10.vsix - checksum: sha256:7361748ddf9fd09d8a2ed1f2a2d7376a2cf9aae708692820b799708385c38e08 - # devfile.vscode-devfile - - download_url: https://open-vsx.org/api/devfile/vscode-devfile/0.0.4/file/devfile.vscode-devfile-0.0.4.vsix - filename: devfile.vscode-devfile.0.0.4.vsix - checksum: sha256:c55a6c1d087e7715bfacb01c0c2b52ef0f935d85f0707265d9068cd64143744c diff --git a/code/build/copy-extension-build-scripts.js b/code/build/copy-extension-build-scripts.js new file mode 100644 index 000000000000..22b914e28a08 --- /dev/null +++ b/code/build/copy-extension-build-scripts.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +import fs from 'fs'; +import path from 'path'; + +const scriptDir = import.meta.dirname; +const codeDir = path.join(scriptDir, '..'); + +// Map of build script filename -> target extension directory +const scripts = { + 'js-debug.esbuild.ts': 'extensions/js-debug', + 'js-debug-companion.esbuild.ts': 'extensions/js-debug-companion', + 'js-profile-visualizer.esbuild.ts': 'extensions/js-profile-visualizer', + 'js-profile-visualizer.webpack.override.js': 'extensions/js-profile-visualizer', + 'devfile.esbuild.ts': 'extensions/devfile', +}; + +for (const [scriptFile, extensionDir] of Object.entries(scripts)) { + const src = path.join(scriptDir, 'extension-build-scripts', scriptFile); + const destDir = path.join(codeDir, extensionDir); + + // Extract the actual filename (remove the prefix before the first dot) + let destFile; + if (scriptFile.includes('.esbuild.ts')) { + destFile = '.esbuild.ts'; + } else if (scriptFile.includes('.webpack.override.js')) { + destFile = 'webpack.override.js'; + } else { + destFile = scriptFile; + } + + const dest = path.join(destDir, destFile); + + if (!fs.existsSync(src)) { + console.warn(`Warning: ${src} not found, skipping`); + continue; + } + + if (!fs.existsSync(destDir)) { + console.warn(`Warning: ${destDir} not found, skipping`); + continue; + } + + fs.copyFileSync(src, dest); + console.log(`Copied ${scriptFile} -> ${extensionDir}/${destFile}`); +} diff --git a/code/build/extension-build-scripts/devfile.esbuild.ts b/code/build/extension-build-scripts/devfile.esbuild.ts new file mode 100644 index 000000000000..37396155f8f2 --- /dev/null +++ b/code/build/extension-build-scripts/devfile.esbuild.ts @@ -0,0 +1,20 @@ +const path = require('path'); +const esbuild = require('esbuild'); + +const srcDir = path.join(__dirname, 'src'); +const outDir = path.join(__dirname, 'dist'); + +esbuild.build({ + platform: 'node', + bundle: true, + minify: true, + treeShaking: true, + sourcemap: true, + target: ['es2020'], + external: ['vscode'], + format: 'cjs', + entryPoints: { + 'devfile-extension': path.join(srcDir, 'devfile-extension.ts'), + }, + outdir: outDir, +}).catch(() => process.exit(1)); diff --git a/code/build/extension-build-scripts/js-debug-companion.esbuild.ts b/code/build/extension-build-scripts/js-debug-companion.esbuild.ts new file mode 100644 index 000000000000..dadd38d9ac4c --- /dev/null +++ b/code/build/extension-build-scripts/js-debug-companion.esbuild.ts @@ -0,0 +1,13 @@ +const path = require('path'); +const esbuild = require('esbuild'); + +esbuild.build({ + entryPoints: [path.join(__dirname, 'src', 'extension.ts')], + tsconfig: path.join(__dirname, 'tsconfig.json'), + bundle: true, + external: ['vscode'], + minify: true, + platform: 'node', + outdir: path.join(__dirname, 'dist'), + packages: 'bundle', +}).catch(() => process.exit(1)); diff --git a/code/build/extension-build-scripts/js-debug.esbuild.ts b/code/build/extension-build-scripts/js-debug.esbuild.ts new file mode 100644 index 000000000000..c4937112cff1 --- /dev/null +++ b/code/build/extension-build-scripts/js-debug.esbuild.ts @@ -0,0 +1,16 @@ +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// Run js-debug's own gulp build which populates dist/ +cp.execFileSync(process.execPath, [path.join(__dirname, 'node_modules', 'gulp', 'bin', 'gulp.js'), 'compile'], { + cwd: __dirname, + stdio: 'inherit', +}); + +// Rewrite main to point into dist/ so the packaged extension resolves correctly +const pkgPath = path.join(__dirname, 'package.json'); +const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); +pkg.main = './dist/src/extension.js'; +pkg.activationEvents = []; +fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); diff --git a/code/build/extension-build-scripts/js-profile-visualizer.esbuild.ts b/code/build/extension-build-scripts/js-profile-visualizer.esbuild.ts new file mode 100644 index 000000000000..39fe34faf00e --- /dev/null +++ b/code/build/extension-build-scripts/js-profile-visualizer.esbuild.ts @@ -0,0 +1,36 @@ +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const rootDir = __dirname; +const coreDir = path.join(rootDir, 'packages', 'vscode-js-profile-core'); +const tableDir = path.join(rootDir, 'packages', 'vscode-js-profile-table'); +const tsc = path.join(rootDir, 'node_modules', '.bin', 'tsc'); +const cpy = path.join(rootDir, 'node_modules', '.bin', 'cpy'); +const webpack = path.join(rootDir, 'node_modules', '.bin', 'webpack'); + +// Build the core package first (table depends on it) +// Type errors are non-fatal - tsc still emits output even when it exits with error code +try { + cp.execFileSync(tsc, ['-p', 'tsconfig.json'], { cwd: coreDir, stdio: 'inherit' }); +} catch (e) { + // Ignore tsc errors - output files are still generated +} +try { + cp.execFileSync(tsc, ['-p', 'tsconfig.browser.json'], { cwd: coreDir, stdio: 'inherit' }); +} catch (e) { + // Ignore tsc errors - output files are still generated +} +cp.execFileSync(cpy, ['src/**/*.css', 'out/esm'], { cwd: coreDir, stdio: 'inherit' }); + +// Build the table package with webpack using override config that disables type-checking +cp.execFileSync(webpack, ['--mode', 'production', '--config', path.join(rootDir, 'webpack.override.js')], { cwd: tableDir, stdio: 'inherit' }); + +// Copy the sub-package's package.json to root so the build system picks up +// the correct extension manifest (with engines, contributes, main, etc.) +const tablePkg = JSON.parse(fs.readFileSync(path.join(tableDir, 'package.json'), 'utf8')); +tablePkg.main = './packages/vscode-js-profile-table/out/extension.js'; +if (tablePkg.browser) { + tablePkg.browser = './packages/vscode-js-profile-table/out/extension.web.js'; +} +fs.writeFileSync(path.join(rootDir, 'package.json'), JSON.stringify(tablePkg, null, 2)); diff --git a/code/build/extension-build-scripts/js-profile-visualizer.webpack.override.js b/code/build/extension-build-scripts/js-profile-visualizer.webpack.override.js new file mode 100644 index 000000000000..773844e57b85 --- /dev/null +++ b/code/build/extension-build-scripts/js-profile-visualizer.webpack.override.js @@ -0,0 +1,20 @@ +// Webpack config override for .esbuild.ts build - disables type-checking to avoid build failures +const baseConfig = require('./packages/vscode-js-profile-table/webpack.config.js'); + +module.exports = baseConfig.map(config => { + if (config.module && config.module.rules) { + config.module.rules = config.module.rules.map(rule => { + if (rule.loader === 'ts-loader') { + return { + ...rule, + options: { + ...rule.options, + transpileOnly: true, // Disable type-checking to avoid build failures + }, + }; + } + return rule; + }); + } + return config; +}); diff --git a/code/build/gulpfile.extensions.ts b/code/build/gulpfile.extensions.ts index 37f35a353b7a..dd4d15db4cc3 100644 --- a/code/build/gulpfile.extensions.ts +++ b/code/build/gulpfile.extensions.ts @@ -65,6 +65,7 @@ const compilations = [ 'extensions/css-language-features/server/tsconfig.json', 'extensions/debug-auto-launch/tsconfig.json', 'extensions/debug-server-ready/tsconfig.json', + 'extensions/devfile/tsconfig.json', 'extensions/emmet/tsconfig.json', 'extensions/extension-editing/tsconfig.json', 'extensions/git/tsconfig.json', @@ -77,6 +78,9 @@ const compilations = [ 'extensions/html-language-features/server/tsconfig.json', 'extensions/ipynb/tsconfig.json', 'extensions/jake/tsconfig.json', + 'extensions/js-debug/tsconfig.json', + 'extensions/js-debug-companion/tsconfig.json', + 'extensions/js-profile-visualizer/packages/vscode-js-profile-table/tsconfig.json', 'extensions/json-language-features/client/tsconfig.json', 'extensions/json-language-features/server/tsconfig.json', 'extensions/markdown-language-features/tsconfig.json', diff --git a/code/build/npm/dirs.ts b/code/build/npm/dirs.ts index 6c035c163085..68d0386607e4 100644 --- a/code/build/npm/dirs.ts +++ b/code/build/npm/dirs.ts @@ -28,6 +28,7 @@ export const dirs = [ 'extensions/css-language-features/server', 'extensions/debug-auto-launch', 'extensions/debug-server-ready', + 'extensions/devfile', 'extensions/emmet', 'extensions/extension-editing', 'extensions/git', @@ -40,6 +41,9 @@ export const dirs = [ 'extensions/html-language-features/server', 'extensions/ipynb', 'extensions/jake', + 'extensions/js-debug', + 'extensions/js-debug-companion', + 'extensions/js-profile-visualizer', 'extensions/json-language-features', 'extensions/json-language-features/server', 'extensions/markdown-language-features', diff --git a/code/extensions/devfile/.eslintrc.json b/code/extensions/devfile/.eslintrc.json new file mode 100644 index 000000000000..f9b22b793c29 --- /dev/null +++ b/code/extensions/devfile/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/naming-convention": "warn", + "@typescript-eslint/semi": "warn", + "curly": "warn", + "eqeqeq": "warn", + "no-throw-literal": "warn", + "semi": "off" + }, + "ignorePatterns": [ + "out", + "dist", + "**/*.d.ts" + ] +} diff --git a/code/extensions/devfile/.github/dependabot.yml b/code/extensions/devfile/.github/dependabot.yml new file mode 100644 index 000000000000..e948f5ac6eba --- /dev/null +++ b/code/extensions/devfile/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "sunday" + time: "16:00" + groups: + all-actions: + patterns: [ "*" ] + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "sunday" + time: "16:00" diff --git a/code/extensions/devfile/.github/workflows/pr-verify.yaml b/code/extensions/devfile/.github/workflows/pr-verify.yaml new file mode 100644 index 000000000000..9b120258ab31 --- /dev/null +++ b/code/extensions/devfile/.github/workflows/pr-verify.yaml @@ -0,0 +1,21 @@ +name: pr-verify + +on: [push, pull_request] + +jobs: + pr-verify-job: + runs-on: ubuntu-latest + steps: + - name: Check Out Code + uses: actions/checkout@v2 + - name: Set Up NodeJS + uses: actions/setup-node@v2 + with: + node-version: '18' + - run: npm install -g typescript "vsce" + - run: npm install + - run: npm run compile + - run: npm run vscode:prepublish + - run: npm run build-vsix + - run: ls -ll *.vsix + - run: npm run eslint diff --git a/code/extensions/devfile/.github/workflows/release.yaml b/code/extensions/devfile/.github/workflows/release.yaml new file mode 100644 index 000000000000..0963888042a2 --- /dev/null +++ b/code/extensions/devfile/.github/workflows/release.yaml @@ -0,0 +1,94 @@ +name: release + +on: + workflow_dispatch: + inputs: + publishToMarketPlace: + description: "Publish to VS Code Marketplace ?" + required: true + type: choice + options: + - "true" + - "false" + default: "false" + publishToOVSX: + description: "Publish to OpenVSX Registry ?" + required: true + type: choice + options: + - "true" + - "false" + default: "false" +jobs: + packaging-job: + runs-on: ubuntu-latest + steps: + - name: Check Out vscode-devfile + uses: actions/checkout@v2 + - name: Set Up NodeJS + uses: actions/setup-node@v2 + with: + node-version: "18" + - name: Install NodeJS dependencies + run: npm install -g typescript "@vscode/vsce" "ovsx" + - name: Build vscode-devfile + run: | + npm install + npm run vscode:prepublish + npm run compile + echo "EXT_VERSION=$(cat package.json | jq -r .version)" >> $GITHUB_ENV + - name: Package vscode-devfile + run: | + vsce package -o vscode-devfile-${{ env.EXT_VERSION }}-${GITHUB_RUN_NUMBER}.vsix + ls -lash *.vsix + - name: Calculate SHA256 Hash + run: | + sha256sum vscode-devfile-${{ env.EXT_VERSION }}-${GITHUB_RUN_NUMBER}.vsix + - name: Upload VSIX Artifacts + uses: actions/upload-artifact@v4.6.1 + with: + name: vscode-devfile + path: | + vscode-devfile-${{ env.EXT_VERSION }}-${{ github.run_number }}.vsix + if-no-files-found: error + - name: Publish to GH Release Tab + if: ${{ inputs.publishToMarketPlace == 'true' && inputs.publishToOVSX == 'true' }} + uses: "marvinpinto/action-automatic-releases@919008cf3f741b179569b7a6fb4d8860689ab7f0" + with: + repo_token: "${{ secrets.RELEASE_GITHUB_TOKEN }}" + automatic_release_tag: "v${{ env.EXT_VERSION }}" + title: "${{ env.EXT_VERSION }}" + draft: true + files: | + vscode-devfile-${{ env.EXT_VERSION }}-${{ github.run_number }}.vsix + release-job: + environment: ${{ (inputs.publishToMarketPlace == 'true' || inputs.publishToOVSX == 'true') && 'release' || 'pre-release' }} + runs-on: ubuntu-latest + needs: packaging-job + steps: + - name: Check Out vscode-devfile + uses: actions/checkout@v2 + - name: Set Up NodeJS + uses: actions/setup-node@v2 + with: + node-version: "18" + - name: Install dependencies + run: | + npm install -g typescript "@vscode/vsce" "ovsx" + - name: Download VSIX + uses: actions/download-artifact@v4.2.0 + with: + merge-multiple: true + path: . + - name: List downloaded files + run: ls -ll *.vsix + - name: Set EXT_VERSION variable + run: echo "EXT_VERSION=$(cat package.json | jq -r .version)" >> $GITHUB_ENV + - name: Publish to VS Code Marketplace + if: ${{ inputs.publishToMarketPlace == 'true' }} + run: | + vsce publish -p ${{ secrets.VSCODE_MARKETPLACE_TOKEN }} --packagePath vscode-devfile-${{ env.EXT_VERSION }}-${GITHUB_RUN_NUMBER}.vsix + - name: Publish to OpenVSX Registry + if: ${{ inputs.publishToOVSX == 'true' }} + run: | + ovsx publish -p ${{ secrets.OVSX_MARKETPLACE_TOKEN }} --packagePath vscode-devfile-${{ env.EXT_VERSION }}-${GITHUB_RUN_NUMBER}.vsix diff --git a/code/extensions/devfile/.gitignore b/code/extensions/devfile/.gitignore new file mode 100644 index 000000000000..76b5a59d20b9 --- /dev/null +++ b/code/extensions/devfile/.gitignore @@ -0,0 +1,3 @@ +out +node_modules +*.vsix diff --git a/code/extensions/devfile/.vscodeignore b/code/extensions/devfile/.vscodeignore new file mode 100644 index 000000000000..2b68c9c92727 --- /dev/null +++ b/code/extensions/devfile/.vscodeignore @@ -0,0 +1,11 @@ +.vscode/** +.vscode-test/** +src/** +.gitignore +.yarnrc +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts +node_modules diff --git a/code/extensions/devfile/CHANGELOG.md b/code/extensions/devfile/CHANGELOG.md new file mode 100644 index 000000000000..b202c5d2a383 --- /dev/null +++ b/code/extensions/devfile/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log + +All notable changes to the "devfile.vscode-devfile" extension will be documented in this file. + +Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. + +## [Unreleased] + +- Initial release diff --git a/code/extensions/devfile/CONTRIBUTING.md b/code/extensions/devfile/CONTRIBUTING.md new file mode 100644 index 000000000000..578102d161db --- /dev/null +++ b/code/extensions/devfile/CONTRIBUTING.md @@ -0,0 +1,45 @@ +Contribute to VS Code Devfile extension +================ + +### Step 1 : Open this repository in an Eclipse Che cloud development environment + +Use this GitHub repository URL in Eclipse Che Dashboard or in a direct link `https:///#https://github.com/devfile/vscode-walkthrough-extension/`. + +Click on the link below to open it in Red Hat Developer Sandbox: + +[![Contribute](https://img.shields.io/static/v1?label=Open%20in%20Red%20Hat%20Developer%20Sandbox...&message=Free%20as%20free%20🍺%20and%20free%20💬..&logo=eclipseche&color=FDB940&labelColor=525C86)](https://workspaces.openshift.com#https://github.com/devfile/vscode-walkthrough-extension/) + +### Step 2 : Compile extension + +The first you need to install node dependencies by running the task `devfile: Install dependencies`. +The task progress will be shown in VS Code Terminal output. + +Once dependencies have been installed, compile the extension with task `devfile: Compile`. +It will create an `out` directory containing the compiled extension. + +### Step 3 : Run the extension in separte VS Code instance + +Now you can test the extension in a separate VS Code instance. + +> Note that it is not possible to launch the extension until you compile it as described in step 2. + +To run a separate VS Code instance focus the editor or the `Explorer`, press `F5`. After a few seconds VS Code starts a separate instance in a new browser tab. + +In the new VS Code instance a `Welcome` tab is opened with a link to the `Get Started with Devfile` VS Code Walkthrough. +If the VS Code Walkthrough link is not there try expanding the Walkthroughs by clicking `More...` on the right. + +### Step 4 : Build `vsix` binary + +Run task `devfile: Build vsix binary` to build the extension binary. + +In a terminal you may be warned with a message below: + +> **WARNING** Using '*' activation is usually a bad idea as it impacts performance. + +Just type `y` to the terminal and press `Enter` to confirm the build. + +When build finished, a new file `devfile-vscode-devfile-0.0.1.vsix` will appear in the project root. + +The file can be downloaded and used in other local or remote VS Code instances. + +> Installing a vsix binary in VS Code is easy: drag and drop the file into the `Extensions` view. diff --git a/code/extensions/devfile/LICENSE b/code/extensions/devfile/LICENSE new file mode 100644 index 000000000000..e72929ee9931 --- /dev/null +++ b/code/extensions/devfile/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + \ No newline at end of file diff --git a/code/extensions/devfile/README.md b/code/extensions/devfile/README.md new file mode 100644 index 000000000000..978bbc6b546b --- /dev/null +++ b/code/extensions/devfile/README.md @@ -0,0 +1,9 @@ +# @devfile.vscode-devfile + +With a wizard you can easily create a Devfile to customize the Cloud Development Environment. + +![vscode-devfile](media/extension-screenshot.png) + +--- + +To build the extension, use Node 16 or later. diff --git a/code/extensions/devfile/devfile.yaml b/code/extensions/devfile/devfile.yaml new file mode 100644 index 000000000000..93663a5f3a9a --- /dev/null +++ b/code/extensions/devfile/devfile.yaml @@ -0,0 +1,34 @@ +schemaVersion: 2.2.0 +metadata: + name: vscode-devfile +components: + - name: tools + container: + image: quay.io/devfile/universal-developer-image:ubi8-latest + memoryRequest: 1G + memoryLimit: 4G + cpuRequest: '1' + cpuLimit: '2' + +commands: + - id: install-dependencies + exec: + label: 1. Install dependencies + component: tools + commandLine: npm install + - id: compile + exec: + label: 2. Compile + component: tools + commandLine: npm run compile + group: + kind: build + isDefault: true + - id: build-vsix + exec: + label: 3. Build vsix binary + component: tools + commandLine: npm run build-vsix + group: + kind: build + isDefault: false diff --git a/code/extensions/devfile/media/devfile-icon.png b/code/extensions/devfile/media/devfile-icon.png new file mode 100644 index 000000000000..5953f1774592 Binary files /dev/null and b/code/extensions/devfile/media/devfile-icon.png differ diff --git a/code/extensions/devfile/media/extension-screenshot.png b/code/extensions/devfile/media/extension-screenshot.png new file mode 100644 index 000000000000..289a88067534 Binary files /dev/null and b/code/extensions/devfile/media/extension-screenshot.png differ diff --git a/code/extensions/devfile/media/install-yaml.md b/code/extensions/devfile/media/install-yaml.md new file mode 100644 index 000000000000..730fa2ea80ae --- /dev/null +++ b/code/extensions/devfile/media/install-yaml.md @@ -0,0 +1,6 @@ +# YAML +![Image](https://raw.githubusercontent.com/redhat-developer/vscode-yaml/main/icon/icon128.png) + +YAML Language Support by Red Hat, with built-in Kubernetes syntax support. + +Provides comprehensive YAML Language support to Visual Studio Code, via the yaml-language-server, with built-in Kubernetes syntax support. diff --git a/code/extensions/devfile/media/new-command.md b/code/extensions/devfile/media/new-command.md new file mode 100644 index 000000000000..a079674f7e27 --- /dev/null +++ b/code/extensions/devfile/media/new-command.md @@ -0,0 +1,25 @@ +# Adding commands + +You can use a devfile to specify some commands used recurrently in the development environment. For example the commands to build and test the application. + +```yaml +commands: +- id: command-1 + exec: + label: Show Welcome Message + component: dev + commandLine: echo "${WELCOME}" + workingDir: ${PROJECT_SOURCE} + +``` + +After restarting the workspace with the Devfile, the specified commands are available as tasks in Visual Studio Code. + +![vscode-devfile-task](./vscode-devfile-task.gif) + +References: +- [Corresponding article in the Devfile documentation][def1] +- [`commands` in the Devfile API reference][def2] + +[def1]: https://devfile.io/docs/2.2.2/adding-commands +[def2]: https://devfile.io/docs/2.2.2/devfile-schema#commands \ No newline at end of file diff --git a/code/extensions/devfile/media/new-container.md b/code/extensions/devfile/media/new-container.md new file mode 100644 index 000000000000..07190315d81c --- /dev/null +++ b/code/extensions/devfile/media/new-container.md @@ -0,0 +1,21 @@ +# Adding a container component + +To customize the container that hosts the Cloud Development Environment, provide a specific image using the `container` component type. + +```yaml +components: + - name: dev + container: + image: quay.io/devfile/universal-developer-image:latest + memoryRequest: 256Mi + memoryLimit: 2048Mi + cpuRequest: 0.1 + cpuLimit: 0.5 +``` + +References: +- [Corresponding article in the Devfile documentation][def1] +- [`container` in the Devfile API reference][def2] + +[def1]: https://devfile.io/docs/2.2.2/adding-a-container-component +[def2]: https://devfile.io/docs/2.2.2/devfile-schema#components-container \ No newline at end of file diff --git a/code/extensions/devfile/media/new-endpoint.md b/code/extensions/devfile/media/new-endpoint.md new file mode 100644 index 000000000000..f64a4220fec0 --- /dev/null +++ b/code/extensions/devfile/media/new-endpoint.md @@ -0,0 +1,17 @@ +# Defining endpoints + +This section describes how to define endpoints and specify their properties. Endpoints help connecting to the applications running in a Cloud Development Environment. An endpoint defined in Devfile can use an existing Kubernetes ingress (the default) or require a dedicated one (when the attribute `urlRewriteSupported` is set to `false`). + +```yaml +endpoints: + - name: api + targetPort: 8080 + exposure: public +``` + +References: +- [Corresponding article in the Devfile documentation][def1] +- [`endpoints` definition in the Devfile API reference][def2] + +[def1]: https://devfile.io/docs/2.2.2/defining-endpoints +[def2]: https://devfile.io/docs/2.2.2/devfile-schema#components-container-endpoints diff --git a/code/extensions/devfile/media/new-environment-variable.md b/code/extensions/devfile/media/new-environment-variable.md new file mode 100644 index 000000000000..fe9e9b920d52 --- /dev/null +++ b/code/extensions/devfile/media/new-environment-variable.md @@ -0,0 +1,16 @@ +# Adding a container environment variable + +Environment variables defined in the Devfile will be propagated to every process running in the container. Including the IDE (e.g. Visual Studio Code), the commands specified in the Devfile itself and the terminal. + +```yaml +env: + - name: WELCOME + value: "Hello World" +``` + +References: +- [Corresponding article in the Devfile documentation][def1] +- [`env` definition in the Devfile API reference][def2] + +[def1]: https://devfile.io/docs/2.2.2/defining-environment-variables +[def2]: https://devfile.io/docs/2.2.2/devfile-schema#components-container-env \ No newline at end of file diff --git a/code/extensions/devfile/media/restart-from-cmd-palette.png b/code/extensions/devfile/media/restart-from-cmd-palette.png new file mode 100644 index 000000000000..1f03831cafdf Binary files /dev/null and b/code/extensions/devfile/media/restart-from-cmd-palette.png differ diff --git a/code/extensions/devfile/media/restart-from-status-bar.png b/code/extensions/devfile/media/restart-from-status-bar.png new file mode 100644 index 000000000000..472fcfd0215b Binary files /dev/null and b/code/extensions/devfile/media/restart-from-status-bar.png differ diff --git a/code/extensions/devfile/media/restart-workspace.md b/code/extensions/devfile/media/restart-workspace.md new file mode 100644 index 000000000000..c16f96079396 --- /dev/null +++ b/code/extensions/devfile/media/restart-workspace.md @@ -0,0 +1,12 @@ +# Workspace Restart + +Restart the Cloud Development Environment to try out the Devfile. + +The command to restart the Cloud Development Environment from the Devfile is also available in Visual Studio Code command palette (CMD/CTR+P). + +![restart from command palette screenshot](./restart-from-cmd-palette.png) + +And from the button at the very left of the status bar too. + +![restart from status bar screenshot](./restart-from-status-bar.png) + diff --git a/code/extensions/devfile/media/vscode-devfile-task.gif b/code/extensions/devfile/media/vscode-devfile-task.gif new file mode 100644 index 000000000000..d5a22d68da65 Binary files /dev/null and b/code/extensions/devfile/media/vscode-devfile-task.gif differ diff --git a/code/extensions/devfile/package-lock.json b/code/extensions/devfile/package-lock.json new file mode 100644 index 000000000000..6b32029f9f1a --- /dev/null +++ b/code/extensions/devfile/package-lock.json @@ -0,0 +1,3219 @@ +{ + "name": "vscode-devfile", + "version": "0.0.4", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vscode-devfile", + "version": "0.0.4", + "dependencies": { + "inversify": "^5.0.1", + "js-yaml": "^3.13.1", + "reflect-metadata": "^0.1.13" + }, + "devDependencies": { + "@types/node": "16.x", + "@types/vscode": "^1.74.0", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "esbuild": "^0.19.12", + "eslint": "^8.56.0", + "typescript": "^4.9.4", + "vsce": "^2.15.0" + }, + "engines": { + "vscode": "^1.74.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", + "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "node_modules/@types/vscode": { + "version": "1.75.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.75.1.tgz", + "integrity": "sha512-emg7wdsTFzdi+elvoyoA+Q8keEautdQHyY5LNmHVM4PTpY8JgOTVADrGVyXGepJ6dVW2OS5/xnLUWh+nZxvdiA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.52.0.tgz", + "integrity": "sha512-lHazYdvYVsBokwCdKOppvYJKaJ4S41CgKBcPvyd0xjZNbvQdhn/pnJlGtQksQ/NhInzdaeaSarlBjDXHuclEbg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/type-utils": "5.52.0", + "@typescript-eslint/utils": "5.52.0", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", + "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", + "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.52.0.tgz", + "integrity": "sha512-tEKuUHfDOv852QGlpPtB3lHOoig5pyFQN/cUiZtpw99D93nEBjexRLre5sQZlkMoHry/lZr8qDAt2oAHLKA6Jw==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/utils": "5.52.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", + "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", + "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/visitor-keys": "5.52.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.52.0.tgz", + "integrity": "sha512-As3lChhrbwWQLNk2HC8Ree96hldKIqk98EYvypd3It8Q1f8d5zWyIoaZEp2va5667M4ZyE7X8UUR+azXrFl+NA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.52.0", + "@typescript-eslint/types": "5.52.0", + "@typescript-eslint/typescript-estree": "5.52.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", + "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.52.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-devops-node-api": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", + "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "dev": true, + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "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.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inversify": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.1.1.tgz", + "integrity": "sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-abi": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.47.0.tgz", + "integrity": "sha512-2s6B2CWZM//kPgwnuI0KrYwNjfdByE25zvAaEpq9IH4zcNsarH8Ihu/UuX6XMPEogDAxkuUFeZn60pXNHAqn3A==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "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": "^1.0.1", + "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" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vsce": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.15.0.tgz", + "integrity": "sha512-P8E9LAZvBCQnoGoizw65JfGvyMqNGlHdlUXD1VAuxtvYAaHBKLBdKPnpy60XKVDAkQCfmMu53g+gq9FM+ydepw==", + "deprecated": "vsce has been renamed to @vscode/vsce. Install using @vscode/vsce instead.", + "dev": true, + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "keytar": "^7.7.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.4.23", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/vsce/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/vsce/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/vsce/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/vsce/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/vsce/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/code/extensions/devfile/package.json b/code/extensions/devfile/package.json new file mode 100644 index 000000000000..e1c9ad6873d4 --- /dev/null +++ b/code/extensions/devfile/package.json @@ -0,0 +1,137 @@ +{ + "name": "vscode-devfile", + "publisher": "devfile", + "displayName": "Devfile Walkthrough", + "description": "Generate and edit Devfiles in a flash.", + "version": "0.0.4", + "repository": { + "type": "git", + "url": "https://github.com/devfile/vscode-walkthrough-extension" + }, + "engines": { + "vscode": "^1.74.0" + }, + "categories": [ + "Other" + ], + "icon": "media/devfile-icon.png", + "activationEvents": [ + "onStartupFinished" + ], + "main": "./out/devfile-extension.js", + "scripts": { + "esbuild-base": "rimraf out && esbuild ./src/devfile-extension.ts --bundle --outfile=out/devfile-extension.js --external:vscode --format=cjs --platform=node", + "compile": "npm run esbuild-base -- --sourcemap", + "watch": "npm run esbuild-base -- --sourcemap --watch", + "build-vsix": "vsce package", + "build-vsix-ignorewarnings": "vsce package --allow-star-activation --out devfile-vscode-devfile-0.0.1.vsix", + "vscode:prepublish": "npm run -S esbuild-base -- --minify", + "pretest": "npm run compile && npm run eslint", + "eslint": "eslint src --ext ts", + "cleanup": "rm -f ./devfile-vscode-devfile-0.0.1.vsix && rm -rf ./out" + }, + "dependencies": { + "inversify": "^5.0.1", + "js-yaml": "^3.13.1", + "reflect-metadata": "^0.1.13" + }, + "devDependencies": { + "@types/node": "16.x", + "@types/vscode": "^1.74.0", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "esbuild": "^0.19.12", + "eslint": "^8.56.0", + "typescript": "^4.9.4", + "vsce": "^2.15.0" + }, + "overrides": { + "es5-ext": "npm:@unes/es5-ext@0.10.64-1" + }, + "contributes": { + "commands": [ + { + "command": "vscode-devfile.new-container", + "title": "Devfile: New Container" + }, + { + "command": "vscode-devfile.new-endpoint", + "title": "Devfile: New Container Endpoint" + }, + { + "command": "vscode-devfile.new-environment-variable", + "title": "Devfile: New Container Environment Variable" + }, + { + "command": "vscode-devfile.new-command", + "title": "Devfile: New Command" + }, + { + "command": "vscode-devfile.install-yaml", + "title": "Devfile: Install YAML Extension" + } + ], + "walkthroughs": [ + { + "id": "get-started-with-devfile", + "title": "Get Started with the Devfile", + "description": "Generate a Devfile to customize your Cloud Development Environment", + "icon": "media/devfile-icon.png", + "steps": [ + { + "id": "add-container", + "title": "Add Container", + "description": "Adding a container\n[New Container](command:vscode-devfile.new-container)", + "media": { + "markdown": "media/new-container.md" + }, + "completionEvents": [] + }, + { + "id": "add-endpoint", + "title": "Add Container Endpoint", + "description": "Adding a container endpoint\n[New Endpoint](command:vscode-devfile.new-endpoint)", + "media": { + "markdown": "media/new-endpoint.md" + }, + "completionEvents": [] + }, + { + "id": "add-environment-variable", + "title": "Add Container Environment Variable", + "description": "Adding a container environment variable\n[New Variable](command:vscode-devfile.new-environment-variable)", + "media": { + "markdown": "media/new-environment-variable.md" + }, + "completionEvents": [] + }, + { + "id": "add-command", + "title": "Add Command", + "description": "Adding a command\n[New Command](command:vscode-devfile.new-command)", + "media": { + "markdown": "media/new-command.md" + }, + "completionEvents": [] + }, + { + "id": "install-yaml", + "title": "Add YAML Extension", + "description": "Provide Devfile code assistance \n[Install](command:vscode-devfile.install-yaml)", + "media": { + "markdown": "media/install-yaml.md" + } + }, + { + "id": "restart-workspace", + "title": "Restart Your Workspace From Devfile", + "description": "Apply the Devfile and restart \n[Restart](command:che-remote.command.restartFromLocalDevfile)", + "media": { + "markdown": "media/restart-workspace.md" + } + } + ] + } + ] + } +} diff --git a/code/extensions/devfile/src/bindings.ts b/code/extensions/devfile/src/bindings.ts new file mode 100644 index 000000000000..5a32f4422362 --- /dev/null +++ b/code/extensions/devfile/src/bindings.ts @@ -0,0 +1,48 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { Container } from 'inversify'; +import { NewCommandImpl } from './command/new-command'; +import { NewContainerImpl } from './command/new-container'; +import { SaveDevfileImpl } from './command/save-devfile'; +import { DevfileExtensionImpl } from './devfile-extension'; +import { DevfileService } from './devfile/devfile-service'; +import { DevfileExtension, NewCommand, NewContainer, NewEndpoint, NewEnvironmentVariable, SaveDevfile } from './model/extension-model'; +import { NewEndpointImpl } from './command/new-endpoint'; +import { NewEnvironmentVariableImpl } from './command/new-environment-variable'; +import { InstallYaml } from './command/install-yaml'; + +export function initBindings(): Container { + const container = new Container(); + + container.bind(DevfileExtensionImpl).toSelf().inSingletonScope(); + container.bind(DevfileExtension).toService(DevfileExtensionImpl); + + container.bind(DevfileService).toSelf().inSingletonScope(); + + container.bind(NewCommandImpl).toSelf().inSingletonScope(); + container.bind(NewCommand).toService(NewCommandImpl); + + container.bind(SaveDevfileImpl).toSelf().inSingletonScope(); + container.bind(SaveDevfile).toService(SaveDevfileImpl); + + container.bind(NewContainerImpl).toSelf().inSingletonScope(); + container.bind(NewContainer).toService(NewContainerImpl); + + container.bind(NewEndpointImpl).toSelf().inSingletonScope(); + container.bind(NewEndpoint).toService(NewEndpointImpl); + + container.bind(NewEnvironmentVariableImpl).toSelf().inSingletonScope(); + container.bind(NewEnvironmentVariable).toService(NewEnvironmentVariableImpl); + + container.bind(InstallYaml).toSelf().inSingletonScope(); + + return container; +} diff --git a/code/extensions/devfile/src/command/install-yaml.ts b/code/extensions/devfile/src/command/install-yaml.ts new file mode 100644 index 000000000000..e7d5b38c2259 --- /dev/null +++ b/code/extensions/devfile/src/command/install-yaml.ts @@ -0,0 +1,65 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { injectable } from "inversify"; +import * as vscode from 'vscode'; + +const SEARCH = 'Show in Marketplace'; +const VSCODE_YAML = 'redhat.vscode-yaml'; + +@injectable() +export class InstallYaml { + + async run(): Promise { + const e = vscode.extensions.getExtension(VSCODE_YAML); + if (e) { + + const answer = await vscode.window.showInformationMessage('YAML extension is already installed', SEARCH); + if (SEARCH === answer) { + await vscode.commands.executeCommand('workbench.extensions.search', VSCODE_YAML); + } + return true; + } else { + + vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: "Installing YAML extension...", + }, async (progress) => { + progress.report({ increment: 10 }); + + try { + await vscode.commands.executeCommand('workbench.extensions.installExtension', VSCODE_YAML); + progress.report({ increment: 50 }); + // it's just to have a nice UX + await new Promise(resolve => setTimeout(resolve, 1000)); + progress.report({ increment: 100 }); + + vscode.window.showInformationMessage('YAML extension has been installed', SEARCH).then(answer => { + if (SEARCH === answer) { + vscode.commands.executeCommand('workbench.extensions.search', VSCODE_YAML); + } + }); + + return Promise.resolve(); + } catch (err) { + if (err.message) { + vscode.window.showWarningMessage(err.message); + } + + return Promise.reject(); + } + }); + + } + + return false; + } + +} diff --git a/code/extensions/devfile/src/command/new-command.ts b/code/extensions/devfile/src/command/new-command.ts new file mode 100644 index 000000000000..f4947d7f7ef2 --- /dev/null +++ b/code/extensions/devfile/src/command/new-command.ts @@ -0,0 +1,204 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { inject, injectable } from "inversify"; +import * as vscode from 'vscode'; +import { log } from "../logger"; +import * as devfile from "../devfile"; +import { DevfileService } from "../devfile/devfile-service"; +import { NewCommand, NewContainer, SaveDevfile } from "../model/extension-model"; + +@injectable() +export class NewCommandImpl implements NewCommand { + + @inject(DevfileService) + private service: DevfileService; + + @inject(NewContainer) + private newContainer: NewContainer; + + @inject(SaveDevfile) + private saveDevfile: SaveDevfile; + + private idCounter = 0; + + async run(): Promise { + if (!await this.service.initDevfileFromProjectRoot()) { + return; + } + + try { + if (!await this.newContainer.ensureAtLeastOneContainerExist()) { + return; + } + + const command = await this.defineCommand(); + if (command) { + if (!this.service.getDevfile().commands) { + this.service.getDevfile().commands = []; + } + + this.service.getDevfile().commands.push(command); + + // update Devfile, show a popup with proposal to open the Devfile + await this.saveDevfile.onDidDevfileUpdate(`Command '${command.id}' has been created successfully`); + return true; + } + + } catch (err) { + log(`ERROR occured: ${err.message}`); + } + + return false; + } + + private async defineCommand(): Promise { + const label = await this.enterLabel(); + if (!label) { + return undefined; + } + + const component = await this.selectComponent(); + if (!component) { + return undefined; + } + + const commandLine = await this.enterCommandLine(); + if (!commandLine) { + return undefined; + } + + // form command ID + let commandID; + do { + this.idCounter++; + commandID = `command-${this.idCounter}`; + } while (this.isCommandExist(commandID)); + + return { + id: commandID, + exec: { + label, + component, + commandLine, + workingDir: '${PROJECT_SOURCE}' + } + }; + } + + /** + * Asks user for the command label + */ + private async enterLabel(): Promise { + return await vscode.window.showInputBox({ + value: 'Sample Command', + title: 'Add Command Label', + prompt: 'This label will be visible in the VS Code tasks view', + + validateInput: (value): string | vscode.InputBoxValidationMessage | undefined | null | + Thenable => { + + if (!value) { + return { + message: 'Command label cannot be empty', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + + const commands = this.service.getDevfile().commands; + if (commands) { + for (const c of commands) { + if (c.exec.label && c.exec.label === value) { + return { + message: 'A command with this label alredy exists', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + } + + } + + return undefined; + } + + }); + } + + /** + * Asks user for the component to run + */ + private async selectComponent(): Promise { + const componentNames: string[] = this.service.getDevfile().components + .filter(c => c.container) + .map(c => c.name); + + if (componentNames.length === 1) { + return componentNames[0]; + } + + const items: vscode.QuickPickItem[] = this.service.getDevfile().components + .filter(c => c.container).map(c => { + return { + label: c.name, + detail: c.container.image, + } as vscode.QuickPickItem; + }); + + const item = await vscode.window.showQuickPick(items, { + title: 'Select a container in which the command will be executed', + }); + + if (item) { + return item.label; + } else { + return undefined; + } + } + + /** + * Asks user to enter command line + */ + private async enterCommandLine(): Promise { + return await vscode.window.showInputBox({ + value: 'echo "${WELCOME}"', + title: 'Enter command line to be executed', + + validateInput: (value): string | vscode.InputBoxValidationMessage | undefined | null | + Thenable => { + + if (!value) { + return { + message: 'Command line cannot be empty', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + + return undefined; + } + + }); + } + + private isCommandExist(id: string): boolean { + const devfile = this.service.getDevfile(); + if (!devfile.commands) { + return false; + } + + for (const command of devfile.commands) { + if (command.id === id) { + return true; + } + } + + return false; + } + +} diff --git a/code/extensions/devfile/src/command/new-container.ts b/code/extensions/devfile/src/command/new-container.ts new file mode 100644 index 000000000000..a8bb8546d211 --- /dev/null +++ b/code/extensions/devfile/src/command/new-container.ts @@ -0,0 +1,154 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { inject, injectable } from "inversify"; +import * as vscode from 'vscode'; +import { log } from "../logger"; +import { DevfileService } from "../devfile/devfile-service"; +import { NewContainer, SaveDevfile } from "../model/extension-model"; + +@injectable() +export class NewContainerImpl implements NewContainer { + + @inject(DevfileService) + private service: DevfileService; + + @inject(SaveDevfile) + private savedevfile: SaveDevfile; + + async run(skipDevfileUpdate?: boolean): Promise { + if (!skipDevfileUpdate && !await this.service.initDevfileFromProjectRoot()) { + return; + } + + try { + // container component image + const containerImage = await this.defineComponentImage(); + if (!containerImage) { + return false; + } + + // component name + const componentName = this.formGenericComponentName(); + + // add new component + if (!this.service.getDevfile().components) { + this.service.getDevfile().components = []; + } + + if (this.countContainerComponents() === 0) { + this.service.getDevfile().components.push({ + name: componentName, + container: { + image: containerImage, + // set defaults + mountSources: true, + memoryRequest: '500Mi', + memoryLimit: '6G', + cpuRequest: '1000m', + cpuLimit: '4000m' + } + }); + } else { + this.service.getDevfile().components.push({ + name: componentName, + container: { + image: containerImage, + // set defaults + mountSources: true + } + }); + } + + // update Devfile, show a popup with proposal to open the Devfile + await this.savedevfile.onDidDevfileUpdate(`Container '${componentName}' has been created successfully`); + return true; + } catch (err) { + log(`ERROR occured: ${err.message}`); + } + + return false; + } + + private formGenericComponentName(): string { + const devfile = this.service.getDevfile(); + + let counter = 0; + let name; + do { + counter++; + name = `container-${counter}`; + + if (!devfile.components) { + return name; + } + + } while (devfile.components.find(c => c.name === name) !== undefined); + + return name; + + } + + private async defineComponentImage(): Promise { + const containerComponents = this.countContainerComponents(); + + return await vscode.window.showInputBox({ + value: containerComponents === 0 ? 'quay.io/devfile/universal-developer-image:latest' : '', + title: 'Container Image', + + validateInput: (value): string | vscode.InputBoxValidationMessage | undefined | null | + Thenable => { + + if (!value) { + return { + message: 'Container image cannot be empty', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + + if (this.service.getDevfile().components) { + for (const c of this.service.getDevfile().components) { + if (c.container && c.container.image === value) { + return { + message: 'Container with this image already exists', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + } + } + + } + }); + } + + async ensureAtLeastOneContainerExist(): Promise { + // there should be at least one container component created + if (this.countContainerComponents() === 0) { + const answer = await vscode.window.showWarningMessage('The first you need to add at least one container', 'New Container'); + + if ('New Container' !== answer) { + return false; + } + + return await this.run(true); + } + + return true; + } + + private countContainerComponents(): number { + if (!this.service.getDevfile().components) { + return 0; + } + + return this.service.getDevfile().components.filter(c => c.container).length; + } + +} diff --git a/code/extensions/devfile/src/command/new-endpoint.ts b/code/extensions/devfile/src/command/new-endpoint.ts new file mode 100644 index 000000000000..dd5890fd0f7a --- /dev/null +++ b/code/extensions/devfile/src/command/new-endpoint.ts @@ -0,0 +1,195 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { inject, injectable } from "inversify"; +import { log } from "../logger"; +import { DevfileService } from "../devfile/devfile-service"; +import { NewContainer, NewEndpoint, SaveDevfile } from "../model/extension-model"; +import * as vscode from 'vscode'; +import * as devfile from "../devfile"; + +@injectable() +export class NewEndpointImpl implements NewEndpoint { + + @inject(DevfileService) + private service: DevfileService; + + @inject(NewContainer) + private newContainer: NewContainer; + + @inject(SaveDevfile) + private saveDevfile: SaveDevfile; + + async run(): Promise { + if (!await this.service.initDevfileFromProjectRoot()) { + return; + } + + try { + if (!await this.newContainer.ensureAtLeastOneContainerExist()) { + return; + } + + const endpoint = await this.defineEndpoint(); + if (endpoint) { + // update Devfile, show a popup with proposal to open the Devfile + await this.saveDevfile.onDidDevfileUpdate(`Endpoint '${endpoint.name}' has been created successfully`); + return true; + } + + } catch (err) { + log(`ERROR occured: ${err.message}`); + } + + return false; + } + + private async defineEndpoint(): Promise { + // select component container + const component = await this.selectComponent(); + if (!component) { + return undefined; + } + + // enter port + const exposedPort = await this.enterExposedPort(component); + if (!exposedPort) { + return undefined; + } + + // enter exposure + const exposure = await this.enterExposure(); + if (!exposure) { + return undefined; + } + + if (!component.container.endpoints) { + component.container.endpoints = []; + } + + const endpoint: devfile.Endpoint = { + name: `port-${exposedPort}`, + targetPort: exposedPort, + exposure + }; + + component.container.endpoints.push(endpoint); + + return endpoint; + } + + /** + * Asks user to select a container for the endpoint + */ + private async selectComponent(): Promise { + const componentNames: string[] = this.service.getDevfile().components + .filter(c => c.container) + .map(c => c.name); + + if (componentNames.length === 1) { + return this.service.getDevfile().components.find(c => c.name === componentNames[0]); + } + + const items: vscode.QuickPickItem[] = this.service.getDevfile().components + .filter(c => c.container).map(c => { + return { + label: c.name, + detail: c.container.image, + } as vscode.QuickPickItem; + }); + + const item = await vscode.window.showQuickPick(items, { + title: 'Select a container to which the new endpoint will be added', + }); + + if (item) { + return this.service.getDevfile().components.find(c => c.name === item.label); + } else { + return undefined; + } + } + + private async enterExposedPort(component: devfile.Component): Promise { + const port = await vscode.window.showInputBox({ + value: '8080', + title: 'Exposed Port', + + validateInput: (value): string | vscode.InputBoxValidationMessage | undefined | null | + Thenable => { + if (!value) { + return { + message: 'Exposed port cannot be empty', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + + const pValue: number = Number.parseInt(value); + if (!Number.isInteger(pValue)) { + return { + message: 'Only Integer is allowed', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + + if (component.container && component.container.endpoints) { + if (component.container.endpoints.find(e => e.targetPort === pValue)) { + return { + message: 'This port is already exposed', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + } + + return undefined; + } + }); + + return Number.parseInt(port); + } + + private async enterExposure(): Promise<'public' | 'internal' | 'none' | undefined> { + const dPublic = 'Endpoint will be exposed on the public network'; + const dInternal = 'Endpoint will be exposed internally outside of the main devworkspace POD'; + const dNone = 'Endpoint will not be exposed and will only be accessible inside the main devworkspace POD'; + + const items: vscode.QuickPickItem[] = [ + { + label: 'public', + detail: dPublic + }, + { + label: 'internal', + detail: dInternal + }, + { + label: 'none', + detail: dNone + } + ]; + + const item = await vscode.window.showQuickPick(items, { + title: 'Describe how the port should be exposed on the network' + }); + + if (item) { + switch (item.label) { + case 'public': + return 'public'; + case 'internal': + return 'internal'; + case 'none': + return 'none'; + } + } + + return undefined; + } + +} diff --git a/code/extensions/devfile/src/command/new-environment-variable.ts b/code/extensions/devfile/src/command/new-environment-variable.ts new file mode 100644 index 000000000000..b227b222d019 --- /dev/null +++ b/code/extensions/devfile/src/command/new-environment-variable.ts @@ -0,0 +1,155 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { inject, injectable } from "inversify"; +import * as vscode from 'vscode'; +import { log } from "../logger"; +import * as devfile from "../devfile"; +import { DevfileService } from "../devfile/devfile-service"; +import { NewContainer, NewEnvironmentVariable, SaveDevfile } from "../model/extension-model"; + +@injectable() +export class NewEnvironmentVariableImpl implements NewEnvironmentVariable { + + @inject(DevfileService) + private service: DevfileService; + + @inject(NewContainer) + private newContainer: NewContainer; + + @inject(SaveDevfile) + private saveDevfile: SaveDevfile; + + async run(): Promise { + if (!await this.service.initDevfileFromProjectRoot()) { + return; + } + + try { + if (!await this.newContainer.ensureAtLeastOneContainerExist()) { + return; + } + + const environmentVariable = await this.defineEnvironmentVariable(); + if (environmentVariable) { + // update Devfile, show a popup with proposal to open the Devfile + await this.saveDevfile.onDidDevfileUpdate(`Environment variable '${environmentVariable.name}' has been created successfully`); + return true; + } + + } catch (err) { + log(`ERROR occured: ${err.message}`); + } + + return false; + } + + private async defineEnvironmentVariable(): Promise { + // select component container + const component = await this.selectComponent(); + if (!component) { + return undefined; + } + + // enter name + const name = await this.enterEnvironmentVariableName(component); + if (!name) { + return undefined; + } + + const value = await this.enterEnvironmentVariableValue(); + // empty value is allowed + if (value === undefined) { + return undefined; + } + + const environmentVariable: devfile.EnvironmentVariable = { + name, + value + }; + + if (!component.container.env) { + component.container.env = []; + } + + component.container.env.push(environmentVariable); + return environmentVariable; + } + + /** + * Asks user to select a container for the environment variable + */ + private async selectComponent(): Promise { + const componentNames: string[] = this.service.getDevfile().components + .filter(c => c.container) + .map(c => c.name); + + if (componentNames.length === 1) { + return this.service.getDevfile().components.find(c => c.name === componentNames[0]); + } + + const items: vscode.QuickPickItem[] = this.service.getDevfile().components + .filter(c => c.container).map(c => { + return { + label: c.name, + detail: c.container.image, + } as vscode.QuickPickItem; + }); + + const item = await vscode.window.showQuickPick(items, { + title: 'Select a container to which the new environment variable will be added', + }); + + if (item) { + return this.service.getDevfile().components.find(c => c.name === item.label); + } else { + return undefined; + } + } + + /** + * Ask user to enter environment variable name + */ + private async enterEnvironmentVariableName(component: devfile.Component): Promise { + return await vscode.window.showInputBox({ + value: 'WELCOME', + title: 'Environment Variable Name', + + validateInput: (value): string | vscode.InputBoxValidationMessage | undefined | null | + Thenable => { + if (!value) { + return { + message: 'Environment variable name cannot be empty', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + + if (component.container && component.container.env) { + if (component.container.env.find(e => e.name === value)) { + return { + message: 'Enviroment variable with this name already exists', + severity: vscode.InputBoxValidationSeverity.Error + } as vscode.InputBoxValidationMessage; + } + } + + return undefined; + } + }); + } + + private async enterEnvironmentVariableValue(): Promise { + return await vscode.window.showInputBox({ + value: 'Hello World', + title: 'Environment Variable Value' + }); + } + +} diff --git a/code/extensions/devfile/src/command/save-devfile.ts b/code/extensions/devfile/src/command/save-devfile.ts new file mode 100644 index 000000000000..5e8cce0eba85 --- /dev/null +++ b/code/extensions/devfile/src/command/save-devfile.ts @@ -0,0 +1,45 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import * as vscode from 'vscode'; +import { inject, injectable } from "inversify"; +import { DevfileService } from "../devfile/devfile-service"; +import { log } from '../logger'; +import { SaveDevfile } from '../model/extension-model'; + +@injectable() +export class SaveDevfileImpl implements SaveDevfile { + + @inject(DevfileService) + private service: DevfileService; + + async onDidDevfileUpdate(message?: string): Promise { + if (this.service.getDevfileSource() === 'unset') { + return; + } + + try { + await this.service.saveToFileSystem(); + + if (message) { + vscode.window.showInformationMessage(message, 'Open Devfile').then(async answer => { + if ('Open Devfile' === answer) { + const devfileURI = this.service.getDevfileURI(); + vscode.window.showTextDocument(devfileURI); + } + }); + } + + } catch (err) { + log(`ERROR occured: ${err.message}`); + } + } + +} diff --git a/code/extensions/devfile/src/devfile-extension.ts b/code/extensions/devfile/src/devfile-extension.ts new file mode 100644 index 000000000000..92d81147453f --- /dev/null +++ b/code/extensions/devfile/src/devfile-extension.ts @@ -0,0 +1,75 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import 'reflect-metadata'; + +import * as vscode from 'vscode'; +import { DevfileExtension, NewCommand, NewContainer, NewEndpoint, NewEnvironmentVariable, SaveDevfile } from './model/extension-model'; +import { inject, injectable } from 'inversify'; +import { initBindings } from './bindings'; +import { InstallYaml } from './command/install-yaml'; + +export async function activate(context: vscode.ExtensionContext): Promise { + // Due to the bug in the upstream https://github.com/microsoft/vscode/issues/214787 it is not possible to show + // several sequential popups. To prevent popup disappear it needs to add a small delay between two popups. + + // Keep the original functions + const _showQuickPick = vscode.window.showQuickPick; + const _showInputBox = vscode.window.showInputBox; + + // Replace with functions with a small delay + Object.assign(vscode.window, { + showQuickPick: async (items, options, token) => { + const result = await _showQuickPick(items, options, token); + await new Promise(resolve => setTimeout(resolve, 300)); + return result; + }, + + showInputBox: async (options, token) => { + const result = await _showInputBox(options, token); + await new Promise(resolve => setTimeout(resolve, 300)); + return result; + } + }); + + const container = initBindings(); + container.get(DevfileExtensionImpl).start(context); +} + +// This method is called when your extension is deactivated +export function deactivate() { } + +@injectable() +export class DevfileExtensionImpl implements DevfileExtension { + + @inject(NewContainer) + private newContainer: NewContainer; + + @inject(NewEndpoint) + private newEndpoint: NewEndpoint; + + @inject(NewEnvironmentVariable) + private newEnvironmentVariable: NewEnvironmentVariable; + + @inject(NewCommand) + private newCommand: NewCommand; + + @inject(InstallYaml) + private installYaml: InstallYaml; + + public async start(context: vscode.ExtensionContext): Promise { + context.subscriptions.push(vscode.commands.registerCommand('vscode-devfile.new-container', async () => this.newContainer.run())); + context.subscriptions.push(vscode.commands.registerCommand('vscode-devfile.new-endpoint', async () => this.newEndpoint.run())); + context.subscriptions.push(vscode.commands.registerCommand('vscode-devfile.new-environment-variable', async () => this.newEnvironmentVariable.run())); + context.subscriptions.push(vscode.commands.registerCommand('vscode-devfile.new-command', async () => this.newCommand.run())); + context.subscriptions.push(vscode.commands.registerCommand('vscode-devfile.install-yaml', async () => this.installYaml.run())); + } + +} diff --git a/code/extensions/devfile/src/devfile.ts b/code/extensions/devfile/src/devfile.ts new file mode 100644 index 000000000000..c9c46cc4ec1a --- /dev/null +++ b/code/extensions/devfile/src/devfile.ts @@ -0,0 +1,59 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +export interface Devfile { + schemaVersion?: string; + metadata?: Metadata; + components?: Component[]; + commands?: Command[]; +} + +export interface Metadata { + name?: string; +} + +export interface Component { + name: string; + container?: ComponentContainer; +} + +export interface ComponentContainer { + image: string; + memoryRequest?: string; + memoryLimit?: string; + cpuRequest?: string; + cpuLimit?: string; + mountSources?: boolean; + endpoints?: Endpoint[]; + env?: EnvironmentVariable[]; +} + +export interface Endpoint { + name: string; + targetPort: number; + exposure?: 'public' | 'internal' | 'none'; +} + +export interface EnvironmentVariable { + name: string; + value: string; +} + +export interface Command { + id: string; + exec: CommandExec; +} + +export interface CommandExec { + component: string; + commandLine: string; + workingDir: string; + label: string; +} diff --git a/code/extensions/devfile/src/devfile/devfile-service.ts b/code/extensions/devfile/src/devfile/devfile-service.ts new file mode 100644 index 000000000000..6ce21e5f8ada --- /dev/null +++ b/code/extensions/devfile/src/devfile/devfile-service.ts @@ -0,0 +1,185 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import { injectable } from "inversify"; +import * as devfile from '../devfile'; +import { log } from "../logger"; +import * as vscode from 'vscode'; +import { posix } from 'path'; +import { env } from "process"; + +import { safeDump, safeLoad } from 'js-yaml'; + +export const DEFAULT_SCHEMA_VERSION = '2.2.0'; + +@injectable() +export class DevfileService { + + private devfile: devfile.Devfile = { + schemaVersion: DEFAULT_SCHEMA_VERSION + }; + + private devfileSource: 'unset' | '.devfile.yaml' | 'devfile.yaml' = 'unset'; + + public async initDevfileFromProjectRoot(): Promise { + + if (!vscode.workspace.workspaceFolders || + vscode.workspace.workspaceFolders.length === 0) { + vscode.window.showWarningMessage('Please open the project to create a Devfile'); + return undefined; + } + + let devfileYaml: { + devfile: devfile.Devfile, + source: '.devfile.yaml' | 'devfile.yaml' + } | undefined; + + try { + devfileYaml = await this.fetchDevfileFromFile('.devfile.yaml') || await this.fetchDevfileFromFile('devfile.yaml'); + } catch (err) { + if ('Not a file' === err.message) { + return undefined; + } + } + + if (devfileYaml) { + // validate devfile + if (!this.isDevfileValid(devfileYaml.devfile)) { + if ('Open Devfile' === await vscode.window.showWarningMessage( + `Devfile ${devfileYaml.source} at the root of your project has invalid format`, 'Open Devfile')) { + + const folderUri = vscode.workspace.workspaceFolders[0].uri; + const devfileUri = folderUri.with({ path: posix.join(folderUri.path, devfileYaml.source) }); + vscode.window.showTextDocument(devfileUri); + } + + return undefined; + } + + } else { + devfileYaml = { + devfile: { + schemaVersion: DEFAULT_SCHEMA_VERSION + }, + source: '.devfile.yaml' + }; + } + + this.ensureNameIsSet(devfileYaml.devfile); + + this.devfile = devfileYaml.devfile; + this.devfileSource = devfileYaml.source; + + return devfileYaml.devfile; + } + + private async fetchDevfileFromFile(source: '.devfile.yaml' | 'devfile.yaml'): Promise<{ + devfile: devfile.Devfile, + source: '.devfile.yaml' | 'devfile.yaml' + } | undefined> { + + const wsFolderUri = vscode.workspace.workspaceFolders[0].uri; + const dotDevfileUri = wsFolderUri.with({ path: posix.join(wsFolderUri.path, source) }); + + try { + const stat = await vscode.workspace.fs.stat(dotDevfileUri); + if (stat.type === vscode.FileType.File) { + const readData = await vscode.workspace.fs.readFile(dotDevfileUri); + const readStr = Buffer.from(readData).toString('utf8'); + const devfile = safeLoad(readStr) as devfile.Devfile; + return { + devfile, + source + }; + } + + await vscode.window.showWarningMessage(`Found ${source}, but it is not a file`, 'Close'); + } catch (err) { + if (err instanceof vscode.FileSystemError && 'FileNotFound' === err.code) { + // Devfile is not found. It's normal behavior + } else { + log(err.message); + } + + return undefined; + } + + throw new Error('Not a file'); + } + + private ensureNameIsSet(devfile: devfile.Devfile): void { + if (!devfile.metadata) { + devfile.metadata = {}; + } else if (devfile.metadata.name) { + // name is already set + return; + } + + // DevWorkspace specific + if (env.DEVWORKSPACE_NAME) { + devfile.metadata.name = env.DEVWORKSPACE_NAME; + return; + } + + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length) { + devfile.metadata.name = vscode.workspace.workspaceFolders[0].name; + return; + } + + devfile.metadata.name = 'devfile-sample'; + } + + public getDevfile(): devfile.Devfile | undefined { + return this.devfile; + } + + public getDevfileSource() { + return this.devfileSource; + } + + public getDevfileURI(): vscode.Uri | undefined { + if (!vscode.workspace.workspaceFolders || + vscode.workspace.workspaceFolders.length === 0) { + return undefined; + } + + const folderUri = vscode.workspace.workspaceFolders[0].uri; + const devfileUri = folderUri.with({ path: posix.join(folderUri.path, this.devfileSource) }); + + return devfileUri; + } + + public async saveToFileSystem(): Promise { + const content = safeDump(this.devfile); + const devfileUri = this.getDevfileURI(); + await vscode.workspace.fs.writeFile(devfileUri!, Buffer.from(content, 'utf8')); + } + + private isDevfileValid(devfile: devfile.Devfile): boolean { + try { + if (!devfile) { + return false; + } + + // dummy check + if (devfile.schemaVersion) { + return true; + } + + // need to find a way how to validate the Devfile + + } catch (e) { + log(e.message); + } + + return false; + } + +} diff --git a/code/extensions/devfile/src/logger.ts b/code/extensions/devfile/src/logger.ts new file mode 100644 index 000000000000..ad10e3cf8e41 --- /dev/null +++ b/code/extensions/devfile/src/logger.ts @@ -0,0 +1,22 @@ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +import * as vscode from 'vscode'; + +let output: vscode.OutputChannel | undefined = undefined; + +export function log(msg: string) { + if (!output) { + output = vscode.window.createOutputChannel('devfile-extension'); + // output.show(true); + } + + output?.appendLine(msg); +} diff --git a/code/extensions/devfile/src/model/extension-model.ts b/code/extensions/devfile/src/model/extension-model.ts new file mode 100644 index 000000000000..aa26fc3a4a42 --- /dev/null +++ b/code/extensions/devfile/src/model/extension-model.ts @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +/********************************************************************** + * Copyright (c) 2023 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ***********************************************************************/ + +export const DevfileExtension = Symbol('DevfileExtension'); +export interface DevfileExtension { +} + +export const NewContainer = Symbol('NewContainer'); +export interface NewContainer { + /** + * Returns true if a container was created successfully + */ + run(): Promise; + + /** + * If the devfile does not have a component container, proposes the user to create it + */ + ensureAtLeastOneContainerExist(): Promise; +} + +export const NewEndpoint = Symbol('NewEndpoint'); +export interface NewEndpoint { + /** + * Returns true if an endpoint was created successfully + */ + run(): Promise; +} + +export const NewEnvironmentVariable = Symbol('NewEnvironmentVariable'); +export interface NewEnvironmentVariable { + /** + * Returns true if the environment variable was created successfully + */ + run(): Promise; +} + +export const NewCommand = Symbol('NewCommand'); +export interface NewCommand { + /** + * Returns true if a command was created successfully + */ + run(): Promise; +} + +export const SaveDevfile = Symbol('SaveDevfile'); +export interface SaveDevfile { + onDidDevfileUpdate(message?: string): Promise; +} diff --git a/code/extensions/devfile/tsconfig.json b/code/extensions/devfile/tsconfig.json new file mode 100644 index 000000000000..c2df49b3af40 --- /dev/null +++ b/code/extensions/devfile/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "lib": [ + "ES2020" + ], + "sourceMap": true, + "rootDir": "src", + "experimentalDecorators": true, + "strict": false /* enable all strict type-checking options */ + + /* Additional Checks */ + // "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. */ + } +} diff --git a/code/extensions/js-debug-companion/.esbuild.js b/code/extensions/js-debug-companion/.esbuild.js new file mode 100644 index 000000000000..561574c49a6a --- /dev/null +++ b/code/extensions/js-debug-companion/.esbuild.js @@ -0,0 +1,23 @@ +const esbuild = require('esbuild'); + +const watch = process.argv.includes('--watch'); +const minify = watch ? process.argv.includes('--minify') : !process.argv.includes('--no-minify'); + +const ctx = esbuild.context({ + entryPoints: ['src/extension.ts'], + tsconfig: './tsconfig.json', + bundle: true, + external: ['vscode'], + sourcemap: !minify, + minify, + platform: 'node', + outdir: 'out', + packages: 'bundle', +}); + +ctx + .then(ctx => (watch ? ctx.watch() : ctx.rebuild())) + .then( + () => !watch && process.exit(0), + () => process.exit(1), + ); diff --git a/code/extensions/js-debug-companion/.gitignore b/code/extensions/js-debug-companion/.gitignore new file mode 100644 index 000000000000..5fe00fea85e9 --- /dev/null +++ b/code/extensions/js-debug-companion/.gitignore @@ -0,0 +1,4 @@ +out +node_modules +.vscode-test/ +*.vsix diff --git a/code/extensions/js-debug-companion/.vscodeignore b/code/extensions/js-debug-companion/.vscodeignore new file mode 100644 index 000000000000..4caae637c401 --- /dev/null +++ b/code/extensions/js-debug-companion/.vscodeignore @@ -0,0 +1,11 @@ +.vscode/** +.vscode-test/** +out/test/** +src/** +.gitignore +vsc-extension-quickstart.md +**/tsconfig.json +**/tslint.json +**/*.map +**/*.ts +node_modules/**/* diff --git a/code/extensions/js-debug-companion/CODE_OF_CONDUCT.md b/code/extensions/js-debug-companion/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..f9ba8cf65f3e --- /dev/null +++ b/code/extensions/js-debug-companion/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/code/extensions/js-debug-companion/LICENSE b/code/extensions/js-debug-companion/LICENSE new file mode 100644 index 000000000000..9e841e7a26e4 --- /dev/null +++ b/code/extensions/js-debug-companion/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + 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/code/extensions/js-debug-companion/README.md b/code/extensions/js-debug-companion/README.md new file mode 100644 index 000000000000..1674009237ff --- /dev/null +++ b/code/extensions/js-debug-companion/README.md @@ -0,0 +1,7 @@ +# js-debug-companion + +A companion extension to [js-debug](https://github.com/microsoft/vscode-js-debug) to enable remote Chrome debugging. You probably don't want to install this extension by itself, but for your interest, this is what it does. + +The scenario is if you are developing in a remote environment—like WSL, a container, ssh, or [VS Codespaces](https://visualstudio.microsoft.com/services/visual-studio-codespaces/)—and are port-forwarding a server to develop (and debug) in a browser locally. For remote development, VS Code runs two sets of extensions: one on the remote machine, and one on your local computer. `js-debug` is a "workspace" extension that runs on the remote machine, but we need to launch and talk to Chrome locally. + +That's where this companion extension comes in. This helper extension runs on the local machine (in the "UI") and registers a command that `js-debug` can call to launch a server. `js-debug` requests a port to be forwarded for debug traffic, and once launching a browser the companion will connect to and forward traffic over that socket. diff --git a/code/extensions/js-debug-companion/SECURITY.md b/code/extensions/js-debug-companion/SECURITY.md new file mode 100644 index 000000000000..e0dfff56a956 --- /dev/null +++ b/code/extensions/js-debug-companion/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). + + diff --git a/code/extensions/js-debug-companion/ci.yml b/code/extensions/js-debug-companion/ci.yml new file mode 100644 index 000000000000..34e2be0fa1c6 --- /dev/null +++ b/code/extensions/js-debug-companion/ci.yml @@ -0,0 +1,36 @@ +trigger: + branches: + include: + - main +pr: none + +resources: + repositories: + - repository: templates + type: github + name: microsoft/vscode-engineering + endpoint: Monaco + +parameters: + - name: publishExtension + displayName: 🚀 Publish Extension + type: boolean + default: false + +extends: + template: azure-pipelines/extension/stable.yml@templates + parameters: + publishExtension: ${{ parameters.publishExtension }} + ghCreateRelease: true + ghReleaseAddChangeLog: true + buildSteps: + - script: npm install + displayName: Install dependencies + + - script: npm run vscode:prepublish + displayName: Compile + tsa: + config: + areaPath: 'Visual Studio Code Debugging Extensions' + serviceTreeID: '053e3ba6-924d-456c-ace0-67812c5ccc52' + enabled: true diff --git a/code/extensions/js-debug-companion/eslint.config.mjs b/code/extensions/js-debug-companion/eslint.config.mjs new file mode 100644 index 000000000000..02a0a712cf7c --- /dev/null +++ b/code/extensions/js-debug-companion/eslint.config.mjs @@ -0,0 +1,7 @@ +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, +); diff --git a/code/extensions/js-debug-companion/package-lock.json b/code/extensions/js-debug-companion/package-lock.json new file mode 100644 index 000000000000..aa92abbacbd2 --- /dev/null +++ b/code/extensions/js-debug-companion/package-lock.json @@ -0,0 +1,4622 @@ +{ + "name": "js-debug-companion", + "version": "1.1.3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "js-debug-companion", + "version": "1.1.3", + "license": "MIT", + "dependencies": { + "@vscode/js-debug-browsers": "^1.1.2", + "default-browser": "^5.2.1", + "duplexer3": "^1.0.0", + "execa": "^5.1.1", + "split2": "^4.2.0", + "ws": "^8.17.1" + }, + "devDependencies": { + "@eslint/js": "^9.6.0", + "@types/duplexer3": "^0.1.4", + "@types/eslint__js": "^8.42.3", + "@types/mocha": "^10.0.7", + "@types/node": "^20.14.9", + "@types/split2": "^4.2.3", + "@types/vscode": "^1.90.0", + "@types/ws": "^8.5.10", + "esbuild": "^0.22.0", + "eslint": "^8.57.0", + "prettier": "^3.3.2", + "rimraf": "^5.0.7", + "typescript": "^5.5.3", + "typescript-eslint": "^7.15.0" + }, + "engines": { + "vscode": "^1.90.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.22.0.tgz", + "integrity": "sha512-uvQR2crZ/zgzSHDvdygHyNI+ze9zwS8mqz0YtGXotSqvEE0UkYE9s+FZKQNTt1VtT719mfP3vHrUdCpxBNQZhQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.22.0.tgz", + "integrity": "sha512-PBnyP+r8vJE4ifxsWys9l+Mc2UY/yYZOpX82eoyGISXXb3dRr0M21v+s4fgRKWMFPMSf/iyowqPW/u7ScSUkjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.22.0.tgz", + "integrity": "sha512-UKhPb3o2gAB/bfXcl58ZXTn1q2oVu1rEu/bKrCtmm+Nj5MKUbrOwR5WAixE2v+lk0amWuwPvhnPpBRLIGiq7ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.22.0.tgz", + "integrity": "sha512-IjTYtvIrjhR41Ijy2dDPgYjQHWG/x/A4KXYbs1fiU3efpRdoxMChK3oEZV6GPzVEzJqxFgcuBaiX1kwEvWUxSw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.22.0.tgz", + "integrity": "sha512-mqt+Go4y9wRvEz81bhKd9RpHsQR1LwU8Xm6jZRUV/xpM7cIQFbFH6wBCLPTNsdELBvfoHeumud7X78jQQJv2TA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.22.0.tgz", + "integrity": "sha512-vTaTQ9OgYc3VTaWtOE5pSuDT6H3d/qSRFRfSBbnxFfzAvYoB3pqKXA0LEbi/oT8GUOEAutspfRMqPj2ezdFaMw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.22.0.tgz", + "integrity": "sha512-0e1ZgoobJzaGnR4reD7I9rYZ7ttqdh1KPvJWnquUoDJhL0rYwdneeLailBzd2/4g/U5p4e5TIHEWa68NF2hFpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.22.0.tgz", + "integrity": "sha512-BFgyYwlCwRWyPQJtkzqq2p6pJbiiWgp0P9PNf7a5FQ1itKY4czPuOMAlFVItirSmEpRPCeImuwePNScZS0pL5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.22.0.tgz", + "integrity": "sha512-KEMWiA9aGuPUD4BH5yjlhElLgaRXe+Eri6gKBoDazoPBTo1BXc/e6IW5FcJO9DoL19FBeCxgONyh95hLDNepIg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.22.0.tgz", + "integrity": "sha512-V/K2rctCUgC0PCXpN7AqT4hoazXKgIYugFGu/myk2+pfe6jTW2guz/TBwq4cZ7ESqusR/IzkcQaBkcjquuBWsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.22.0.tgz", + "integrity": "sha512-r2ZZqkOMOrpUhzNwxI7uLAHIDwkfeqmTnrv1cjpL/rjllPWszgqmprd/om9oviKXUBpMqHbXmppvjAYgISb26Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.22.0.tgz", + "integrity": "sha512-qaowLrV/YOMAL2RfKQ4C/VaDzAuLDuylM2sd/LH+4OFirMl6CuDpRlCq4u49ZBaVV8pkI/Y+hTdiibvQRhojCA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.22.0.tgz", + "integrity": "sha512-hgrezzjQTRxjkQ5k08J6rtZN5PNnkWx/Rz6Kmj9gnsdCAX1I4Dn4ZPqvFRkXo55Q3pnVQJBwbdtrTO7tMGtyVA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.22.0.tgz", + "integrity": "sha512-ewxg6FLLUio883XgSjfULEmDl3VPv/TYNnRprVAS3QeGFLdCYdx1tIudBcd7n9jIdk82v1Ajov4jx87qW7h9+g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.22.0.tgz", + "integrity": "sha512-Az5XbgSJC2lE8XK8pdcutsf9RgdafWdTpUK/+6uaDdfkviw/B4JCwAfh1qVeRWwOohwdsl4ywZrWBNWxwrPLFg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.22.0.tgz", + "integrity": "sha512-8j4a2ChT9+V34NNNY9c/gMldutaJFmfMacTPq4KfNKwv2fitBCLYjee7c+Vxaha2nUhPK7cXcZpJtJ3+Y7ZdVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.22.0.tgz", + "integrity": "sha512-JUQyOnpbAkkRFOk/AhsEemz5TfWN4FJZxVObUlnlNCbe7QBl61ZNfM4cwBXayQA6laMJMUcqLHaYQHAB6YQ95Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.22.0.tgz", + "integrity": "sha512-11PoCoHXo4HFNbLsXuMB6bpMPWGDiw7xETji6COdJss4SQZLvcgNoeSqWtATRm10Jj1uEHiaIk4N0PiN6x4Fcg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.22.0.tgz", + "integrity": "sha512-Ezlhu/YyITmXwKSB+Zu/QqD7cxrjrpiw85cc0Rbd3AWr2wsgp+dWbWOE8MqHaLW9NKMZvuL0DhbJbvzR7F6Zvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.22.0.tgz", + "integrity": "sha512-ufjdW5tFJGUjlH9j/5cCE9lrwRffyZh+T4vYvoDKoYsC6IXbwaFeV/ENxeNXcxotF0P8CDzoICXVSbJaGBhkrw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.22.0.tgz", + "integrity": "sha512-zY6ly/AoSmKnmNTowDJsK5ehra153/5ZhqxNLfq9NRsTTltetr+yHHcQ4RW7QDqw4JC8A1uC1YmeSfK9NRcK1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.22.0.tgz", + "integrity": "sha512-Kml5F7tv/1Maam0pbbCrvkk9vj046dPej30kFzlhXnhuCtYYBP6FGy/cLbc5yUT1lkZznGLf2OvuvmLjscO5rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.22.0.tgz", + "integrity": "sha512-IOgwn+mYTM3RrcydP4Og5IpXh+ftN8oF+HELTXSmbWBlujuci4Qa3DTeO+LEErceisI7KUSfEIiX+WOUlpELkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.22.0.tgz", + "integrity": "sha512-4bDHJrk2WHBXJPhy1y80X7/5b5iZTZP3LGcKIlAP1J+KqZ4zQAPMLEzftGyjjfcKbA4JDlPt/+2R/F1ZTeRgrw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", + "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha512-dvMpf/6D5nUKK/ATVSo2guk3Ya1cWmEbwg7j+dtzuwpG3dPCVjptzFGBx4GfrQC9VZRWkY3yUoq4C2H7uMBMGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "dependencies": { + "@types/eslint": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", + "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/split2": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@types/split2/-/split2-4.2.3.tgz", + "integrity": "sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/vscode": { + "version": "1.90.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.90.0.tgz", + "integrity": "sha512-oT+ZJL7qHS9Z8bs0+WKf/kQ27qWYR3trsXpq46YDjFqBsMLG4ygGGjPaJ2tyrH0wJzjOEmDyg9PDJBBhWg9pkQ==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vscode/js-debug-browsers": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/js-debug-browsers/-/js-debug-browsers-1.1.2.tgz", + "integrity": "sha512-NIBJzVAzHjq6ez6TU+4QMUMRUfC9vKddr2a8NdEkp0wQSfjNxkYzT12TCAV3v8EOHA/Am/fxJbJuH97WvM33aA==", + "dependencies": { + "execa": "^4.0.0" + } + }, + "node_modules/@vscode/js-debug-browsers/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@vscode/js-debug-browsers/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/js-debug-browsers/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/acorn": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", + "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/duplexer3": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-1.0.0.tgz", + "integrity": "sha512-6O5ndCyJ9CGF9cR2Yi3VFq1OvXXLEgX848InIOl8xUBPYwb8jn/93j10lGaZyLnMRa71IT5OHhURlOiVjH9OVg==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/esbuild": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.22.0.tgz", + "integrity": "sha512-zNYA6bFZsVnsU481FnGAQjLDW0Pl/8BGG7EvAp15RzUvGC+ME7hf1q7LvIfStEQBz/iEHuBJCYcOwPmNCf1Tlw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.22.0", + "@esbuild/android-arm": "0.22.0", + "@esbuild/android-arm64": "0.22.0", + "@esbuild/android-x64": "0.22.0", + "@esbuild/darwin-arm64": "0.22.0", + "@esbuild/darwin-x64": "0.22.0", + "@esbuild/freebsd-arm64": "0.22.0", + "@esbuild/freebsd-x64": "0.22.0", + "@esbuild/linux-arm": "0.22.0", + "@esbuild/linux-arm64": "0.22.0", + "@esbuild/linux-ia32": "0.22.0", + "@esbuild/linux-loong64": "0.22.0", + "@esbuild/linux-mips64el": "0.22.0", + "@esbuild/linux-ppc64": "0.22.0", + "@esbuild/linux-riscv64": "0.22.0", + "@esbuild/linux-s390x": "0.22.0", + "@esbuild/linux-x64": "0.22.0", + "@esbuild/netbsd-x64": "0.22.0", + "@esbuild/openbsd-arm64": "0.22.0", + "@esbuild/openbsd-x64": "0.22.0", + "@esbuild/sunos-x64": "0.22.0", + "@esbuild/win32-arm64": "0.22.0", + "@esbuild/win32-ia32": "0.22.0", + "@esbuild/win32-x64": "0.22.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "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.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", + "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/jackspeak": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", + "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", + "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.15.0.tgz", + "integrity": "sha512-Ta40FhMXBCwHura4X4fncaCVkVcnJ9jnOq5+Lp4lN8F4DzHZtOwZdRvVBiNUGznUDHPwdGnrnwxmUOU2fFQqFA==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "7.15.0", + "@typescript-eslint/parser": "7.15.0", + "@typescript-eslint/utils": "7.15.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz", + "integrity": "sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/type-utils": "7.15.0", + "@typescript-eslint/utils": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.15.0.tgz", + "integrity": "sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", + "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz", + "integrity": "sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/utils": "7.15.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", + "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", + "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", + "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", + "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.15.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@esbuild/aix-ppc64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.22.0.tgz", + "integrity": "sha512-uvQR2crZ/zgzSHDvdygHyNI+ze9zwS8mqz0YtGXotSqvEE0UkYE9s+FZKQNTt1VtT719mfP3vHrUdCpxBNQZhQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.22.0.tgz", + "integrity": "sha512-PBnyP+r8vJE4ifxsWys9l+Mc2UY/yYZOpX82eoyGISXXb3dRr0M21v+s4fgRKWMFPMSf/iyowqPW/u7ScSUkjQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.22.0.tgz", + "integrity": "sha512-UKhPb3o2gAB/bfXcl58ZXTn1q2oVu1rEu/bKrCtmm+Nj5MKUbrOwR5WAixE2v+lk0amWuwPvhnPpBRLIGiq7ig==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.22.0.tgz", + "integrity": "sha512-IjTYtvIrjhR41Ijy2dDPgYjQHWG/x/A4KXYbs1fiU3efpRdoxMChK3oEZV6GPzVEzJqxFgcuBaiX1kwEvWUxSw==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.22.0.tgz", + "integrity": "sha512-mqt+Go4y9wRvEz81bhKd9RpHsQR1LwU8Xm6jZRUV/xpM7cIQFbFH6wBCLPTNsdELBvfoHeumud7X78jQQJv2TA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.22.0.tgz", + "integrity": "sha512-vTaTQ9OgYc3VTaWtOE5pSuDT6H3d/qSRFRfSBbnxFfzAvYoB3pqKXA0LEbi/oT8GUOEAutspfRMqPj2ezdFaMw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.22.0.tgz", + "integrity": "sha512-0e1ZgoobJzaGnR4reD7I9rYZ7ttqdh1KPvJWnquUoDJhL0rYwdneeLailBzd2/4g/U5p4e5TIHEWa68NF2hFpQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.22.0.tgz", + "integrity": "sha512-BFgyYwlCwRWyPQJtkzqq2p6pJbiiWgp0P9PNf7a5FQ1itKY4czPuOMAlFVItirSmEpRPCeImuwePNScZS0pL5Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.22.0.tgz", + "integrity": "sha512-KEMWiA9aGuPUD4BH5yjlhElLgaRXe+Eri6gKBoDazoPBTo1BXc/e6IW5FcJO9DoL19FBeCxgONyh95hLDNepIg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.22.0.tgz", + "integrity": "sha512-V/K2rctCUgC0PCXpN7AqT4hoazXKgIYugFGu/myk2+pfe6jTW2guz/TBwq4cZ7ESqusR/IzkcQaBkcjquuBWsw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.22.0.tgz", + "integrity": "sha512-r2ZZqkOMOrpUhzNwxI7uLAHIDwkfeqmTnrv1cjpL/rjllPWszgqmprd/om9oviKXUBpMqHbXmppvjAYgISb26Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.22.0.tgz", + "integrity": "sha512-qaowLrV/YOMAL2RfKQ4C/VaDzAuLDuylM2sd/LH+4OFirMl6CuDpRlCq4u49ZBaVV8pkI/Y+hTdiibvQRhojCA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.22.0.tgz", + "integrity": "sha512-hgrezzjQTRxjkQ5k08J6rtZN5PNnkWx/Rz6Kmj9gnsdCAX1I4Dn4ZPqvFRkXo55Q3pnVQJBwbdtrTO7tMGtyVA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.22.0.tgz", + "integrity": "sha512-ewxg6FLLUio883XgSjfULEmDl3VPv/TYNnRprVAS3QeGFLdCYdx1tIudBcd7n9jIdk82v1Ajov4jx87qW7h9+g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.22.0.tgz", + "integrity": "sha512-Az5XbgSJC2lE8XK8pdcutsf9RgdafWdTpUK/+6uaDdfkviw/B4JCwAfh1qVeRWwOohwdsl4ywZrWBNWxwrPLFg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.22.0.tgz", + "integrity": "sha512-8j4a2ChT9+V34NNNY9c/gMldutaJFmfMacTPq4KfNKwv2fitBCLYjee7c+Vxaha2nUhPK7cXcZpJtJ3+Y7ZdVQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.22.0.tgz", + "integrity": "sha512-JUQyOnpbAkkRFOk/AhsEemz5TfWN4FJZxVObUlnlNCbe7QBl61ZNfM4cwBXayQA6laMJMUcqLHaYQHAB6YQ95Q==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.22.0.tgz", + "integrity": "sha512-11PoCoHXo4HFNbLsXuMB6bpMPWGDiw7xETji6COdJss4SQZLvcgNoeSqWtATRm10Jj1uEHiaIk4N0PiN6x4Fcg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.22.0.tgz", + "integrity": "sha512-Ezlhu/YyITmXwKSB+Zu/QqD7cxrjrpiw85cc0Rbd3AWr2wsgp+dWbWOE8MqHaLW9NKMZvuL0DhbJbvzR7F6Zvg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.22.0.tgz", + "integrity": "sha512-ufjdW5tFJGUjlH9j/5cCE9lrwRffyZh+T4vYvoDKoYsC6IXbwaFeV/ENxeNXcxotF0P8CDzoICXVSbJaGBhkrw==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.22.0.tgz", + "integrity": "sha512-zY6ly/AoSmKnmNTowDJsK5ehra153/5ZhqxNLfq9NRsTTltetr+yHHcQ4RW7QDqw4JC8A1uC1YmeSfK9NRcK1w==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.22.0.tgz", + "integrity": "sha512-Kml5F7tv/1Maam0pbbCrvkk9vj046dPej30kFzlhXnhuCtYYBP6FGy/cLbc5yUT1lkZznGLf2OvuvmLjscO5rw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.22.0.tgz", + "integrity": "sha512-IOgwn+mYTM3RrcydP4Og5IpXh+ftN8oF+HELTXSmbWBlujuci4Qa3DTeO+LEErceisI7KUSfEIiX+WOUlpELkw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.22.0.tgz", + "integrity": "sha512-4bDHJrk2WHBXJPhy1y80X7/5b5iZTZP3LGcKIlAP1J+KqZ4zQAPMLEzftGyjjfcKbA4JDlPt/+2R/F1ZTeRgrw==", + "dev": true, + "optional": true + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", + "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@types/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha512-dvMpf/6D5nUKK/ATVSo2guk3Ya1cWmEbwg7j+dtzuwpG3dPCVjptzFGBx4GfrQC9VZRWkY3yUoq4C2H7uMBMGQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint__js": { + "version": "8.42.3", + "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", + "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", + "dev": true, + "requires": { + "@types/eslint": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/mocha": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", + "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "dev": true + }, + "@types/node": { + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/split2": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@types/split2/-/split2-4.2.3.tgz", + "integrity": "sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/vscode": { + "version": "1.90.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.90.0.tgz", + "integrity": "sha512-oT+ZJL7qHS9Z8bs0+WKf/kQ27qWYR3trsXpq46YDjFqBsMLG4ygGGjPaJ2tyrH0wJzjOEmDyg9PDJBBhWg9pkQ==", + "dev": true + }, + "@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "@vscode/js-debug-browsers": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/js-debug-browsers/-/js-debug-browsers-1.1.2.tgz", + "integrity": "sha512-NIBJzVAzHjq6ez6TU+4QMUMRUfC9vKddr2a8NdEkp0wQSfjNxkYzT12TCAV3v8EOHA/Am/fxJbJuH97WvM33aA==", + "requires": { + "execa": "^4.0.0" + }, + "dependencies": { + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" + } + } + }, + "acorn": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", + "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "requires": { + "run-applescript": "^7.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "requires": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + } + }, + "default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "duplexer3": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-1.0.0.tgz", + "integrity": "sha512-6O5ndCyJ9CGF9cR2Yi3VFq1OvXXLEgX848InIOl8xUBPYwb8jn/93j10lGaZyLnMRa71IT5OHhURlOiVjH9OVg==" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "esbuild": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.22.0.tgz", + "integrity": "sha512-zNYA6bFZsVnsU481FnGAQjLDW0Pl/8BGG7EvAp15RzUvGC+ME7hf1q7LvIfStEQBz/iEHuBJCYcOwPmNCf1Tlw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.22.0", + "@esbuild/android-arm": "0.22.0", + "@esbuild/android-arm64": "0.22.0", + "@esbuild/android-x64": "0.22.0", + "@esbuild/darwin-arm64": "0.22.0", + "@esbuild/darwin-x64": "0.22.0", + "@esbuild/freebsd-arm64": "0.22.0", + "@esbuild/freebsd-x64": "0.22.0", + "@esbuild/linux-arm": "0.22.0", + "@esbuild/linux-arm64": "0.22.0", + "@esbuild/linux-ia32": "0.22.0", + "@esbuild/linux-loong64": "0.22.0", + "@esbuild/linux-mips64el": "0.22.0", + "@esbuild/linux-ppc64": "0.22.0", + "@esbuild/linux-riscv64": "0.22.0", + "@esbuild/linux-s390x": "0.22.0", + "@esbuild/linux-x64": "0.22.0", + "@esbuild/netbsd-x64": "0.22.0", + "@esbuild/openbsd-arm64": "0.22.0", + "@esbuild/openbsd-x64": "0.22.0", + "@esbuild/sunos-x64": "0.22.0", + "@esbuild/win32-arm64": "0.22.0", + "@esbuild/win32-ia32": "0.22.0", + "@esbuild/win32-x64": "0.22.0" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "foreground-child": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "glob": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", + "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "jackspeak": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", + "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "requires": { + "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" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", + "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", + "dev": true + } + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "dev": true, + "requires": { + "glob": "^10.3.7" + } + }, + "run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + } + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "requires": {} + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true + }, + "typescript-eslint": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.15.0.tgz", + "integrity": "sha512-Ta40FhMXBCwHura4X4fncaCVkVcnJ9jnOq5+Lp4lN8F4DzHZtOwZdRvVBiNUGznUDHPwdGnrnwxmUOU2fFQqFA==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "7.15.0", + "@typescript-eslint/parser": "7.15.0", + "@typescript-eslint/utils": "7.15.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz", + "integrity": "sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/type-utils": "7.15.0", + "@typescript-eslint/utils": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/parser": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.15.0.tgz", + "integrity": "sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", + "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz", + "integrity": "sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/utils": "7.15.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/types": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", + "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", + "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/utils": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", + "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", + "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.15.0", + "eslint-visitor-keys": "^3.4.3" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "requires": {} + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/code/extensions/js-debug-companion/package.json b/code/extensions/js-debug-companion/package.json new file mode 100644 index 000000000000..872a124c4c78 --- /dev/null +++ b/code/extensions/js-debug-companion/package.json @@ -0,0 +1,85 @@ +{ + "name": "js-debug-companion", + "displayName": "JavaScript Debugger Companion Extension", + "description": "Companion extension to js-debug that provides capability for remote debugging", + "version": "1.1.3", + "publisher": "ms-vscode", + "engines": { + "vscode": "^1.90.0" + }, + "icon": "resources/logo.png", + "categories": [ + "Other" + ], + "repository": { + "type": "git", + "url": "https://github.com/microsoft/vscode-js-debug-companion.git" + }, + "author": "Connor Peet ", + "license": "MIT", + "bugs": { + "url": "https://github.com/microsoft/vscode-js-debug-companion/issues" + }, + "homepage": "https://github.com/microsoft/vscode-js-debug-companion#readme", + "capabilities": { + "virtualWorkspaces": false, + "untrustedWorkspaces": { + "supported": true + } + }, + "activationEvents": [ + "onCommand:js-debug-companion.launchAndAttach", + "onCommand:js-debug-companion.kill", + "onCommand:js-debug-companion.launch", + "onCommand:js-debug-companion.defaultBrowser" + ], + "main": "./out/extension.js", + "contributes": {}, + "extensionKind": [ + "ui" + ], + "api": "none", + "scripts": { + "vscode:prepublish": "rimraf out && node .esbuild.js --minify", + "compile": "node .esbuild.js --minify", + "watch": "node .esbuild.js --watch", + "test": "tsc --noEmit && npm run test:lint && npm run test:fmt", + "test:lint": "eslint \"src/**/*.ts\"", + "test:fmt": "prettier --list-different \"src/**/*.ts\"", + "fmt": "prettier --write \"src/**/*.ts\"&& npm run test:lint -- --fix" + }, + "prettier": { + "trailingComma": "all", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "arrowParens": "avoid" + }, + "devDependencies": { + "@eslint/js": "^9.6.0", + "@types/duplexer3": "^0.1.4", + "@types/eslint__js": "^8.42.3", + "@types/mocha": "^10.0.7", + "@types/node": "^20.14.9", + "@types/split2": "^4.2.3", + "@types/vscode": "^1.90.0", + "@types/ws": "^8.5.10", + "esbuild": "^0.22.0", + "eslint": "^8.57.0", + "prettier": "^3.3.2", + "rimraf": "^5.0.7", + "typescript": "^5.5.3", + "typescript-eslint": "^7.15.0" + }, + "dependencies": { + "@vscode/js-debug-browsers": "^1.1.2", + "default-browser": "^5.2.1", + "duplexer3": "^1.0.0", + "execa": "^5.1.1", + "split2": "^4.2.0", + "ws": "^8.17.1" + }, + "overrides": { + "es5-ext": "npm:@unes/es5-ext@0.10.64-1" + } +} diff --git a/code/extensions/js-debug-companion/resources/logo.png b/code/extensions/js-debug-companion/resources/logo.png new file mode 100644 index 000000000000..12828f8a5308 Binary files /dev/null and b/code/extensions/js-debug-companion/resources/logo.png differ diff --git a/code/extensions/js-debug-companion/src/errors.ts b/code/extensions/js-debug-companion/src/errors.ts new file mode 100644 index 000000000000..bf4c67851ae5 --- /dev/null +++ b/code/extensions/js-debug-companion/src/errors.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +/** + * Expected error meant to be shown to users. + */ +export class UserError extends Error {} diff --git a/code/extensions/js-debug-companion/src/extension.ts b/code/extensions/js-debug-companion/src/extension.ts new file mode 100644 index 000000000000..d7788f9ed69e --- /dev/null +++ b/code/extensions/js-debug-companion/src/extension.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import defaultBrowser from 'default-browser'; +import { tmpdir } from 'os'; +import * as vscode from 'vscode'; +import { SessionManager } from './sessionManager'; +import { BrowserSpawner } from './spawn'; + +/** + * Info about the WSL distro, if any. A common issue across scenarios is WSL + * ports randomly not getting forwarded. Instead, in WSL, we can try to make + * a connection via stdin/stdout on the nested WSL instance. + */ +export interface IWslInfo { + execPath: string; + distro: string; + user: string; +} + +export interface ILaunchParams { + type: 'chrome' | 'edge'; + path: string; + proxyUri: string; + launchId: number; + browserArgs: string[]; + wslInfo?: IWslInfo; + attach?: { + host: string; + port: number; + }; + // See IChromiumLaunchConfiguration in js-debug for the full type, a subset of props are here: + params: { + env: Readonly<{ [key: string]: string | null }>; + runtimeExecutable: string; + userDataDir: boolean | string; + cwd: string | null; + webRoot: string | null; + }; +} + +let manager: SessionManager | undefined; + +export function activate(context: vscode.ExtensionContext) { + const browserSpawner = new BrowserSpawner(context.storageUri?.fsPath ?? tmpdir(), context); + manager = new SessionManager(browserSpawner); + + context.subscriptions.push( + vscode.commands.registerCommand('js-debug-companion.defaultBrowser', async () => { + const b = await defaultBrowser(); + return b.name; + }), + vscode.commands.registerCommand('js-debug-companion.launchAndAttach', params => { + manager?.create(params).catch(err => vscode.window.showErrorMessage(err.message)); + }), + vscode.commands.registerCommand('js-debug-companion.kill', ({ launchId }) => { + manager?.destroy(launchId); + }), + vscode.commands.registerCommand( + 'js-debug-companion.launch', + ({ browserType: type, URL: url }) => { + browserSpawner.launchBrowserOnly(type, url); + }, + ), + ); +} + +export function deactivate() { + manager?.dispose(); + manager = undefined; +} diff --git a/code/extensions/js-debug-companion/src/fs.ts b/code/extensions/js-debug-companion/src/fs.ts new file mode 100644 index 000000000000..3f920b89d65a --- /dev/null +++ b/code/extensions/js-debug-companion/src/fs.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { promises } from 'fs'; + +/** + * Returns whether the given path exists. + */ +export async function exists(path: string) { + try { + await promises.access(path); + return true; + } catch (e) { + return false; + } +} diff --git a/code/extensions/js-debug-companion/src/getWsEndpoint.ts b/code/extensions/js-debug-companion/src/getWsEndpoint.ts new file mode 100644 index 000000000000..f0b3b53c2a0c --- /dev/null +++ b/code/extensions/js-debug-companion/src/getWsEndpoint.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as http from 'http'; +import { resolve as resolveUrl, URL } from 'url'; +import { CancellationToken, Disposable } from 'vscode'; + +/** + * Attempts to retrieve the debugger websocket URL for a process listening + * at the given address, retrying until available. + * @param browserURL -- Address like `http://localhost:1234` + * @param cancellationToken -- Optional cancellation for this operation + */ +export async function retryGetWSEndpoint( + browserURL: string, + cancellationToken: CancellationToken, +): Promise { + try { + return await getWSEndpoint(browserURL, cancellationToken); + } catch (e) { + if (cancellationToken.isCancellationRequested) { + throw new Error(`Could not connect to debug target at ${browserURL}: ${e}`); + } + + await new Promise(r => setTimeout(r, 200)); + return retryGetWSEndpoint(browserURL, cancellationToken); + } +} + +/** + * Returns the debugger websocket URL a process listening at the given address. + * @param browserURL -- Address like `http://localhost:1234` + * @param cancellationToken -- Optional cancellation for this operation + */ +export async function getWSEndpoint( + browserURL: string, + cancellationToken: CancellationToken, +): Promise { + const jsonVersion = await fetchJson<{ webSocketDebuggerUrl?: string }>( + resolveUrl(browserURL, '/json/version'), + cancellationToken, + ); + + if (jsonVersion?.webSocketDebuggerUrl) { + return fixRemoteUrl(browserURL, jsonVersion.webSocketDebuggerUrl); + } + + // Chrome its top-level debugg on /json/version, while Node does not. + // Request both and return whichever one got us a string. + const jsonList = await fetchJson<{ webSocketDebuggerUrl: string }[]>( + resolveUrl(browserURL, '/json/list'), + cancellationToken, + ); + + if (jsonList?.length) { + return fixRemoteUrl(browserURL, jsonList[0].webSocketDebuggerUrl); + } + + throw new Error('Could not find any debuggable target'); +} + +async function fetchJson(url: string, cancellationToken: CancellationToken): Promise { + return JSON.parse(await fetchHttp(url, cancellationToken)); +} + +function fetchHttp(url: string, cancellationToken: CancellationToken) { + const disposables: Disposable[] = []; + + return new Promise((fulfill, reject) => { + const request = http.request( + url, + { + headers: { + host: 'localhost', + }, + }, + response => { + disposables.push(cancellationToken.onCancellationRequested(() => response.destroy())); + + let data = ''; + response.setEncoding('utf8'); + response.on('data', (chunk: string) => (data += chunk)); + response.on('end', () => fulfill(data)); + response.on('error', reject); + }, + ); + + disposables.push( + cancellationToken.onCancellationRequested(() => { + request.destroy(); + reject(new Error(`Cancelled GET ${url}`)); + }), + ); + + request.on('error', reject); + request.end(); + }).finally(() => disposables.forEach(d => d.dispose())); +} + +function fixRemoteUrl(rawBrowserUrl: string, rawWebSocketUrl: string) { + const browserUrl = new URL(rawBrowserUrl); + const websocketUrl = new URL(rawWebSocketUrl); + websocketUrl.host = browserUrl.host; + return websocketUrl.toString(); +} diff --git a/code/extensions/js-debug-companion/src/session.ts b/code/extensions/js-debug-companion/src/session.ts new file mode 100644 index 000000000000..393a532bd029 --- /dev/null +++ b/code/extensions/js-debug-companion/src/session.ts @@ -0,0 +1,202 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { spawn } from 'child_process'; +import duplexer3 from 'duplexer3'; +import { Agent } from 'http'; +import { Socket } from 'net'; +import { Duplex } from 'stream'; +import { URL } from 'url'; +import { Disposable, EventEmitter } from 'vscode'; +import WebSocket from 'ws'; +import { IWslInfo } from './extension'; +import { ITarget } from './target'; + +class MessageQueue { + private qOrFn: T[] | ((value: T) => void) = []; + + public push(value: T) { + if (typeof this.qOrFn === 'function') { + this.qOrFn(value); + } else { + this.qOrFn.push(value); + } + } + + public connect(fn: (value: T) => void) { + if (typeof this.qOrFn === 'function') { + throw new Error('Already connected'); + } + + const prev = this.qOrFn; + this.qOrFn = fn; + for (const queued of prev) { + fn(queued); + } + } +} + +/** + * The Session manages the lifecycle for a single top-level browser debug sesssion. + */ +export class Session implements Disposable { + private readonly errorEmitter = new EventEmitter(); + public readonly onError = this.errorEmitter.event; + + private readonly closeEmitter = new EventEmitter(); + public readonly onClose = this.closeEmitter.event; + + private disposed = false; + private browserProcess?: ITarget; + private socket?: WebSocket; + + private fromSocketQueue = new MessageQueue(); + private fromBrowserQueue = new MessageQueue(); + + constructor() { + this.onClose(() => this.dispose()); + this.onError(() => this.dispose()); + } + + /** + * Attaches the socket looping back up to js-debug. + */ + public attachSocket(host: string, port: number, path: string, wslInfo?: IWslInfo) { + const url = new URL(`ws://${host}:${port}${path}`); + const deadline = Date.now() + 5000; + if (wslInfo) { + this.attachSocketWsl(url, wslInfo, deadline); + } else { + this.attachSocketLoop(url, deadline); + } + } + + /** + * Attaches the browser child process. + */ + public attachChild(target: ITarget) { + if (this.disposed) { + target.dispose(); + return; + } + + this.browserProcess = target; + target.onClose(() => this.closeEmitter.fire()); + target.onError(err => this.errorEmitter.fire(err)); + target.onMessage(msg => this.fromBrowserQueue.push(msg)); + this.fromSocketQueue.connect(data => target.send(data)); + } + + /** + * @inheritdoc + */ + public dispose() { + if (!this.disposed) { + this.browserProcess?.dispose(); + this.socket?.close(); + this.disposed = true; + } + } + + private attachSocketWsl(url: URL, wslInfo: IWslInfo, deadline: number) { + const agent = new Agent(); + + // Make a fake connection that attaches to stdin/out, as in my original + // unrelated https://github.com/websockets/ws/issues/1944 + // + // The maintainer suggested using setSocket there, but that happens after + // the socket is upgraded, and we want the actual HTTP request and upgrade + // to happen on these pipes. + // + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (agent as any).createConnection = ( + _options: unknown, + callback: (err?: Error | null, stream?: Socket) => void, + ) => { + const process = spawn('wsl.exe', [ + '-d', + wslInfo.distro, + '-u', + wslInfo.user, + '--', + wslInfo.execPath, + '-e', + `'s=net.connect(${url.port});s.pipe(process.stdout);process.stdin.pipe(s)'`, + ]); + + process.on('error', callback); + + process.on('spawn', () => { + callback(null, makeNetSocketFromDuplexStream(duplexer3(process.stdin, process.stdout))); + }); + }; + + // intentionally don't perMessageDeflate here, since we're local in wsl + const ws = new WebSocket(url, { agent }); + this.setupSocket(ws, url, deadline); + } + + private attachSocketLoop(url: URL, deadline: number) { + if (this.disposed) { + return; + } + + const socket = new WebSocket(url, { perMessageDeflate: true }); + this.setupSocket(socket, url, deadline); + } + + private setupSocket(socket: WebSocket, url: URL, deadline: number) { + socket.on('open', () => { + if (this.disposed) { + socket.close(); + return; + } + + this.socket = socket; + this.socket.on('close', () => this.closeEmitter.fire()); + this.socket.on('message', data => this.fromSocketQueue.push(data)); + this.fromBrowserQueue.connect(data => socket.send(data)); + }); + + socket.on('error', err => { + if (this.socket === socket || Date.now() > deadline) { + this.errorEmitter.fire(err); + } else { + setTimeout(() => this.attachSocketLoop(url, deadline), 100); + } + }); + } +} + +const makeNetSocketFromDuplexStream = (stream: Duplex): Socket => { + const cast = stream as Socket; + const patched: { [K in keyof Omit]: Socket[K] } = { + bufferSize: 0, + bytesRead: 0, + bytesWritten: 0, + connecting: false, + localAddress: '127.0.0.1', + localPort: 1, + remoteAddress: '127.0.0.1', + remoteFamily: 'tcp', + remotePort: 1, + address: () => ({ address: '127.0.0.1', family: 'tcp', port: 1 }), + unref: () => cast, + ref: () => cast, + connect: (_port: unknown, _host?: unknown, connectionListener?: () => void) => { + if (connectionListener) { + setImmediate(connectionListener); + } + return cast; + }, + setKeepAlive: () => cast, + setNoDelay: () => cast, + setTimeout: (_timeout: number, callback?: () => void) => { + callback?.(); + return cast; + }, + }; + + return Object.assign(stream, patched) as Socket; +}; diff --git a/code/extensions/js-debug-companion/src/sessionManager.ts b/code/extensions/js-debug-companion/src/sessionManager.ts new file mode 100644 index 000000000000..df933af2fe4b --- /dev/null +++ b/code/extensions/js-debug-companion/src/sessionManager.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Disposable } from 'vscode'; +import { ILaunchParams } from './extension'; +import { Session } from './session'; +import { BrowserSpawner } from './spawn'; +import { AttachTarget } from './target'; + +export class SessionManager implements Disposable { + private readonly sessions = new Map(); + + constructor(private readonly spawn: BrowserSpawner) {} + + /** + * Creates a session with the set of launch parameters. + */ + public async create(params: ILaunchParams) { + const session = new Session(); + this.sessions.set(params.launchId, session); + session.onClose(() => this.sessions.delete(params.launchId)); + session.onError(err => { + vscode.window.showErrorMessage(`Error running browser: ${err.message || err.stack}`); + this.sessions.delete(params.launchId); + }); + + await Promise.all([ + this.addChildSocket(session, params), + params.attach + ? this.addChildAttach(session, params.attach) + : this.addChildBrowser(session, params), + ]); + } + + /** + * Destroys a session with the given launch ID. + */ + public destroy(launchId: number) { + const session = this.sessions.get(launchId); + session?.dispose(); + this.sessions.delete(launchId); + } + + /** + * @inheritdoc + */ + public dispose() { + for (const session of this.sessions.values()) { + session.dispose(); + } + + this.sessions.clear(); + } + + private async addChildSocket(session: Session, params: ILaunchParams) { + const [host, port] = params.proxyUri.split(':'); + session.attachSocket(host, Number(port), params.path, params.wslInfo); + } + + private async addChildBrowser(session: Session, params: ILaunchParams) { + const browser = await this.spawn.launch(params); + session.attachChild(browser); + } + + private async addChildAttach(session: Session, params: { host: string; port: number }) { + const target = await AttachTarget.create(params.host, params.port); + session.attachChild(target); + } +} diff --git a/code/extensions/js-debug-companion/src/spawn.ts b/code/extensions/js-debug-companion/src/spawn.ts new file mode 100644 index 000000000000..a8e5858c7092 --- /dev/null +++ b/code/extensions/js-debug-companion/src/spawn.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { + ChromeBrowserFinder, + EdgeBrowserFinder, + FirefoxBrowserFinder, + IBrowserFinder, + IExecutable, + isQuality, +} from '@vscode/js-debug-browsers'; +import { spawn } from 'child_process'; +import execa from 'execa'; +import { promises as fs } from 'fs'; +import { join } from 'path'; +import * as vscode from 'vscode'; +import { UserError } from './errors'; +import { ILaunchParams } from './extension'; +import { exists } from './fs'; +import { PipedTarget, ServerTarget } from './target'; + +const debugPortPrefix = '--remote-debugging-port='; +const debugPipeArg = '--remote-debugging-port='; +const availableBrowserKey = 'availableBrowsers_'; + +export class BrowserSpawner { + private readonly finders = { + edge: new EdgeBrowserFinder(process.env, fs, execa), + chrome: new ChromeBrowserFinder(process.env, fs, execa), + firefox: new FirefoxBrowserFinder(process.env, fs, execa), + }; + + constructor( + private readonly storagePath: string, + private readonly context: vscode.ExtensionContext, + ) {} + + private async findBrowserPath(type: 'edge' | 'chrome' | 'firefox', runtimeExecutable: string) { + if (runtimeExecutable !== '*' && !isQuality(runtimeExecutable)) { + return runtimeExecutable; + } + + if (!(type in this.finders)) { + throw new UserError(`Browser type "${type}" is not supported.`); + } + + const available = + this.context.globalState.get(availableBrowserKey + type) || + (await this.finders[type].findAll()); + + const resolved = + runtimeExecutable === '*' + ? available.find(r => r.quality === 'stable') ?? available[0] + : available.find(r => r.quality === runtimeExecutable); + + if (!resolved) { + await this.context.globalState.update(availableBrowserKey + type, undefined); + + if (runtimeExecutable === /* Quality.Stable */ 'stable' && !available.length) { + throw new UserError( + vscode.l10n.t( + 'Unable to find a {0} installation on your system. Try installing it, or providing an absolute path to the browser in the "runtimeExecutable" in your launch.json.', + type, + ), + ); + } else { + throw new UserError( + vscode.l10n.t( + 'Unable to find {0} version {1}. Available auto-discovered versions are: {2}. You can set the "runtimeExecutable" in your launch.json to one of these, or provide an absolute path to the browser executable.', + type, + runtimeExecutable, + JSON.stringify([...new Set(available)]), + ), + ); + } + } + + await this.context.globalState.update(availableBrowserKey + type, available); + + return resolved.path; + } + + protected async findBrowserByExe( + finder: IBrowserFinder, + executablePath: string, + ): Promise { + if (executablePath === '*') { + // try to find the stable browser, but if that fails just get any browser + // that's available on the system + const found = + (await finder.findWhere(r => r.quality === /* Quality.Stable */ 'stable')) || + (await finder.findAll())[0]; + return found?.path; + } else if (isQuality(executablePath)) { + return (await finder.findWhere(r => r.quality === executablePath))?.path; + } else { + return executablePath; + } + } + + private async getUserDataDir(params: ILaunchParams) { + const requested = params.params.userDataDir; + if (requested === false) { + return; + } + + const defaultDir = join( + this.storagePath, + params.browserArgs?.includes('--headless') ? '.headless-profile' : '.profile', + ); + + if (requested === true) { + return defaultDir; + } + + if (!(await exists(requested))) { + return defaultDir; + } + + return requested; + } + + /** + * Launches a browser using a specific browser type and url. + */ + public async launchBrowserOnly(type: 'edge' | 'chrome' | 'firefox', url: string) { + const binary = await this.findBrowserPath(type, '*'); + spawn(binary, [url], { + detached: true, + stdio: 'ignore', + }).on('error', err => { + vscode.window.showErrorMessage(`Error running browser: ${err.message || err.stack}`); + }); + } + + /** + * Launches and returns a child process for the browser specified by the + * given parameters. + * @throws UserError if the launch fails + */ + public async launch(params: ILaunchParams) { + const binary = await this.findBrowserPath(params.type, params.params.runtimeExecutable); + + const args = params.browserArgs.slice(); + const userDataDir = await this.getUserDataDir(params); + // prepend args to not interfere with any positional arguments (e.g. url to open) + if (userDataDir !== undefined) { + args.unshift(`--user-data-dir=${userDataDir}`); + } + + // The cwd defaults to the working directory of the remote extension, but + // this probably won't exist on the local host. If it doesn't just set it + // to the process' cwd. + let cwd = params.params.cwd || params.params.webRoot; + if (!cwd || !(await exists(cwd))) { + cwd = process.cwd(); + } + + const port = args.find(a => a.startsWith(debugPortPrefix))?.slice(debugPortPrefix.length); + if (!port) { + return new PipedTarget( + spawn(binary, args, { + detached: process.platform !== 'win32', + env: { + ...process.env, + GDK_PIXBUF_MODULEDIR: undefined, + GDK_PIXBUF_MODULE_FILE: undefined, + ELECTRON_RUN_AS_NODE: undefined, + ...params.params.env, + }, + stdio: ['ignore', 'ignore', 'ignore', 'pipe', 'pipe'], + cwd, + }), + ); + } + + if (!args.includes(debugPipeArg)) { + // back compat with older js-debug versions + args.unshift(debugPipeArg); + } + + const child = spawn(binary, args, { + detached: process.platform !== 'win32', + env: { ELECTRON_RUN_AS_NODE: undefined, ...params.params.env }, + stdio: 'ignore', + cwd, + }); + + return await ServerTarget.create(child, Number(port)); + } +} diff --git a/code/extensions/js-debug-companion/src/target.ts b/code/extensions/js-debug-companion/src/target.ts new file mode 100644 index 000000000000..2021120c9140 --- /dev/null +++ b/code/extensions/js-debug-companion/src/target.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { ChildProcess } from 'child_process'; +import split from 'split2'; +import { CancellationTokenSource, Event, EventEmitter } from 'vscode'; +import WebSocket from 'ws'; +import { retryGetWSEndpoint } from './getWsEndpoint'; + +export interface ITarget { + readonly onMessage: Event; + readonly onError: Event; + readonly onClose: Event; + + send(message: WebSocket.RawData): void; + dispose(): Promise; +} + +const waitForExit = async (process: ChildProcess) => { + if (process.exitCode) { + return; + } + + await Promise.race([ + new Promise(r => process.on('exit', r)), + new Promise(r => setTimeout(r, 1000)), + ]); +}; + +/** + * A debug target that sends data through the target's stdio streams. + */ +export class PipedTarget implements ITarget { + private errorEmitter = new EventEmitter(); + private closeEmitter = new EventEmitter(); + private messageEmitter = new EventEmitter(); + + public readonly onError = this.errorEmitter.event; + public readonly onClose = this.closeEmitter.event; + public readonly onMessage = this.messageEmitter.event; + + constructor(private readonly process: ChildProcess) { + if (this.process.stdio.length < 5) { + throw new Error('Insufficient fd number on child process'); + } + + process.on('error', e => this.errorEmitter.fire(e)); + process.on('exit', () => this.closeEmitter.fire()); + + (process.stdio[4] as NodeJS.ReadableStream) + .pipe(split('\0')) + .on('data', data => this.messageEmitter.fire(data)) + .resume(); + } + + public send(message: WebSocket.RawData): void { + const w = this.process.stdio[3] as NodeJS.WritableStream; + if (message instanceof Uint8Array) { + w.write(message); + } else if (message instanceof ArrayBuffer) { + w.write(new Uint8Array(message)); + } else { + for (const chunk of message) { + w.write(chunk); + } + } + + w.write('\0'); + } + + public async dispose() { + await waitForExit(this.process); + this.process.kill(); + } +} + +/** + * Attaches to a debug target on the given host and port. + */ +export class AttachTarget implements ITarget { + private errorEmitter = new EventEmitter(); + private closeEmitter = new EventEmitter(); + private messageEmitter = new EventEmitter(); + + public readonly onError = this.errorEmitter.event; + public readonly onClose = this.closeEmitter.event; + public readonly onMessage = this.messageEmitter.event; + + public static async create(host: string, port: number) { + const cts = new CancellationTokenSource(); + setTimeout(() => cts.cancel(), 10 * 1000); + + const endpoint = await retryGetWSEndpoint(`http://${host}:${port}`, cts.token); + const ws = new WebSocket(endpoint, [], { + headers: { host: 'localhost' }, + perMessageDeflate: false, + maxPayload: 256 * 1024 * 1024, + followRedirects: true, + }); + + return await new Promise((resolve, reject) => { + ws.addEventListener('open', () => resolve(new AttachTarget(ws))); + ws.addEventListener('error', errorEvent => reject(errorEvent.error)); + }); + } + + protected constructor(private readonly ws: WebSocket) { + ws.on('error', evt => this.errorEmitter.fire(evt)); + ws.on('close', () => this.closeEmitter.fire()); + ws.on('message', m => this.messageEmitter.fire(m)); + } + + public send(message: WebSocket.RawData): void { + this.ws.send(message.toString()); + } + + public async dispose() { + await new Promise(r => { + this.ws.on('close', r); + this.ws.close(); + }); + } +} + +/** + * Target that attaches to the process as a server. + * Dispose will also kill the process. + */ +export class ServerTarget implements ITarget { + private errorEmitter = new EventEmitter(); + private closeEmitter = new EventEmitter(); + private messageEmitter = new EventEmitter(); + + public readonly onError = this.errorEmitter.event; + public readonly onClose = this.closeEmitter.event; + public readonly onMessage = this.messageEmitter.event; + + public static async create(child: ChildProcess, port: number) { + const cts = new CancellationTokenSource(); + setTimeout(() => cts.cancel(), 10 * 1000); + try { + const target = await AttachTarget.create('localhost', port); + return new ServerTarget(child, target); + } catch (e) { + child.kill(); + throw e; + } + } + + protected constructor(private readonly process: ChildProcess, private readonly attach: ITarget) { + process.on('error', e => this.errorEmitter.fire(e)); + process.on('close', () => this.closeEmitter.fire()); + attach.onError(err => this.errorEmitter.fire(err)); + attach.onClose(() => this.closeEmitter.fire()); + attach.onMessage(evt => this.messageEmitter.fire(evt)); + } + + public send(message: WebSocket.RawData): void { + this.attach.send(message); + } + + public async dispose() { + this.attach.dispose(); + await waitForExit(this.process); + this.process.kill(); + } +} diff --git a/code/extensions/js-debug-companion/tsconfig.json b/code/extensions/js-debug-companion/tsconfig.json new file mode 100644 index 000000000000..b5eea4d8840a --- /dev/null +++ b/code/extensions/js-debug-companion/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "ESNext", + "target": "ES2022", + "outDir": "out", + "lib": ["ES2023"], + "sourceMap": true, + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUnusedParameters": true + }, + "exclude": ["node_modules", ".vscode-test"] +} diff --git a/code/extensions/js-debug/.ci/common-validation.yml b/code/extensions/js-debug/.ci/common-validation.yml new file mode 100644 index 000000000000..09de3795e807 --- /dev/null +++ b/code/extensions/js-debug/.ci/common-validation.yml @@ -0,0 +1,78 @@ +parameters: + runTests: true + runFrameworkTests: false + +steps: +- task: NodeTool@0 + displayName: Use Node + inputs: + versionSpec: $(node_version) + +- bash: | + /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + echo ">>> Started xvfb" + displayName: Start xvfb + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + +- task: Npm@1 + displayName: npm install + inputs: + command: custom + customCommand: install --legacy-peer-deps + verbose: false + +- task: NodeTool@0 + displayName: Use Node + inputs: + versionSpec: $(node_version) + +- task: CmdLine@2 + displayName: test echo environment + inputs: + script: node -e "console.log(process.env.PATH)" # debug failures on osx + condition: eq(${{ parameters.runTests }}, true) + env: + JSDBG_TEST_VERSION: insiders + DISPLAY: ':99.0' + +# A shell script here due to https://github.com/microsoft/azure-pipelines-tasks/issues/12650 +- task: CmdLine@2 + displayName: npm test + inputs: + script: npm test + publishJUnitResults: true + timeoutInMinutes: 12 + condition: eq(${{ parameters.runTests }}, true) + env: + JSDBG_TEST_VERSION: insiders + JSDBG_USE_NODE_VERSION: $(node_version) + DISPLAY: ':99.0' + +- task: CmdLine@2 + displayName: npm test (framework tests) + inputs: + script: npm test + timeoutInMinutes: 10 + condition: eq(${{ parameters.runFrameworkTests }}, true) + env: + FRAMEWORK_TESTS: 1 + DISPLAY: ':99.0' + ONLY_MINSPEC: $(only_minspec) + +- task: Gulp@0 + displayName: gulp lint + inputs: + targets: lint + condition: and(eq(${{ parameters.runTests }}, true), ne(variables.only_minspec, true)) + +- task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: '*-results.xml' + searchFolder: '$(Build.ArtifactStagingDirectory)/test-results' + condition: and(succeededOrFailed(), eq(${{ parameters.runTests }}, true)) + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + inputs: + ignoreDirectories: 'testdata,demos,.vscode-test' diff --git a/code/extensions/js-debug/.ci/pipeline.yml b/code/extensions/js-debug/.ci/pipeline.yml new file mode 100644 index 000000000000..96c3acb21d34 --- /dev/null +++ b/code/extensions/js-debug/.ci/pipeline.yml @@ -0,0 +1,63 @@ +trigger: + batch: true + branches: + include: + - main + +pr: [main] + +resources: + repositories: + - repository: 1esPipelines + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines + parameters: + sdl: + sourceAnalysisPool: 1es-windows-2022-x64 + tsa: + enabled: false + stages: + - stage: Build + jobs: + - job: macOS + timeoutInMinutes: 20 + pool: + name: Azure Pipelines + vmImage: 'macOS-latest' + os: macOS + steps: + - template: .ci/common-validation.yml@self + variables: + node_version: 18 + + - job: Linux + pool: + name: 1es-ubuntu-22.04-x64 + os: linux + steps: + - template: .ci/common-validation.yml@self + variables: + node_version: 18 + + - job: LinuxMinspec + pool: + name: 1es-ubuntu-22.04-x64 + os: linux + steps: + - template: .ci/common-validation.yml@self + variables: + node_version: 18 + only_minspec: true + + - job: Windows + pool: + name: 1es-windows-2022-x64 + os: windows + steps: + - template: .ci/common-validation.yml@self + variables: + node_version: 18 diff --git a/code/extensions/js-debug/.ci/publish-nightly.yml b/code/extensions/js-debug/.ci/publish-nightly.yml new file mode 100644 index 000000000000..27adb50192f3 --- /dev/null +++ b/code/extensions/js-debug/.ci/publish-nightly.yml @@ -0,0 +1,39 @@ +pr: none + +resources: + repositories: + - repository: templates + type: github + name: microsoft/vscode-engineering + ref: main + endpoint: Monaco + +parameters: + - name: publishExtension + displayName: 🚀 Publish Extension + type: boolean + default: true + +extends: + template: azure-pipelines/extension/pre-release.yml@templates + parameters: + publishExtension: ${{ parameters.publishExtension }} + usePreReleaseChannel: false + vscePackageArgs: --no-dependencies + cgIgnoreDirectories: 'testdata,demos,.vscode-test,src/test,testWorkspace' + l10nShouldProcess: false + ghCreateTag: false + buildSteps: + - script: npm install --legacy-peer-deps + displayName: Install dependencies + + - script: npx -y @vscode/l10n-dev export --outDir ./l10n-extract ./src + displayName: Extract localization + + - script: npm run compile -- package:hoist --nightly + displayName: Package + tsa: + config: + areaPath: 'Visual Studio Code Debugging Extensions' + serviceTreeID: "053e3ba6-924d-456c-ace0-67812c5ccc52" + enabled: true \ No newline at end of file diff --git a/code/extensions/js-debug/.ci/publish.yml b/code/extensions/js-debug/.ci/publish.yml new file mode 100644 index 000000000000..6742d5203858 --- /dev/null +++ b/code/extensions/js-debug/.ci/publish.yml @@ -0,0 +1,65 @@ +trigger: none +pr: none + +resources: + repositories: + - repository: templates + type: github + name: microsoft/vscode-engineering + ref: main + endpoint: Monaco + +parameters: + - name: publishExtension + displayName: 🚀 Publish Extension + type: boolean + default: false + - name: publishGhRelease + displayName: ☁️ Publish Github release + type: boolean + default: true + +extends: + template: azure-pipelines/extension/stable.yml@templates + parameters: + publishExtension: ${{ parameters.publishExtension }} + vscePackageArgs: --no-dependencies + apiScanExcludes: '**/w32appcontainertokens-*.node' + cgIgnoreDirectories: 'testdata,demos,.vscode-test,src/test,testWorkspace' + ${{ if eq(parameters.publishGhRelease, true) }}: + ghCreateRelease: true + ghReleaseAddChangeLog: true + l10nShouldOnlyPush: true + l10nPackageNlsPath: package.nls.json + l10nSourcePaths: src + buildSteps: + - script: npm install --legacy-peer-deps + displayName: Install dependencies + + - script: npm run compile -- dapDebugServer + displayName: Compile DAP Debug Server Bundle + + - script: node src/build/archiveDapBundle $(Build.ArtifactStagingDirectory)/dap-server + displayName: Package DAP Debug Server Bundle + + - script: mkdir $(Build.ArtifactStagingDirectory)/sbom-dap-server + condition: ${{ eq(parameters.publishExtension, true) }} + displayName: Create SBOM drop path + + - task: 1ES.PublishPipelineArtifact@1 + inputs: + artifactName: 'Publish DAP Debug Server Bundle' + sbomBuildComponentPath: $(Build.SourcesDirectory)/dist + sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/sbom-dap-server + sbomEnabled: ${{ eq(parameters.publishExtension, true) }} + targetPath: $(Build.ArtifactStagingDirectory)/dap-server + displayName: Publish DAP Debug Server Bundle + + - script: npm run compile -- package:hoist + displayName: Package Stable + tsa: + config: + areaPath: 'Visual Studio Code Debugging Extensions' + serviceTreeID: '053e3ba6-924d-456c-ace0-67812c5ccc52' + enabled: true + apiScanSoftwareVersion: '1' diff --git a/code/extensions/js-debug/.eslintrc.js b/code/extensions/js-debug/.eslintrc.js new file mode 100644 index 000000000000..65abe1818438 --- /dev/null +++ b/code/extensions/js-debug/.eslintrc.js @@ -0,0 +1,43 @@ +module.exports = { + ignorePatterns: ['**/*.d.ts', 'src/test/**/*.ts', 'demos/**/*', '**/*.js', 'testWorkspace/**'], + parser: '@typescript-eslint/parser', + extends: ['plugin:react/recommended', 'plugin:@typescript-eslint/recommended'], + plugins: ['header'], + parserOptions: { + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + }, + settings: { + react: { + pragma: 'h', + version: '16.3', + }, + }, + rules: { + // Temporary until CDP is moved out, which is where most violations are: + '@typescript-eslint/ban-types': 'off', + + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-namespace': 'off', + 'prefer-const': ['error', { destructuring: 'all' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + 'header/header': [ + 'error', + 'block', + '---------------------------------------------------------\n * Copyright (C) Microsoft Corporation. All rights reserved.\n *--------------------------------------------------------', + ], + 'react/no-unescaped-entities': 'off', + 'react/prop-types': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + varsIgnorePattern: '^h$', + argsIgnorePattern: '^_', + }, + ], + // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs + // e.g. "@typescript-eslint/explicit-function-return-type": "off", + }, +}; diff --git a/code/extensions/js-debug/.gitattributes b/code/extensions/js-debug/.gitattributes new file mode 100644 index 000000000000..074494fe2870 --- /dev/null +++ b/code/extensions/js-debug/.gitattributes @@ -0,0 +1,8 @@ +# Declare files that will always have LF line endings on checkout. +*.txt eol=lf +*.ts eol=lf +*.tsx eol=lf +*.json eol=lf +*.js eol=lf +*.md eol=lf + diff --git a/code/extensions/js-debug/.github/ISSUE_TEMPLATE/bug_report.md b/code/extensions/js-debug/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000000..332fc7b0f8dc --- /dev/null +++ b/code/extensions/js-debug/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: connor4312 +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Log File** + + + +**VS Code Version:** Replace me! + +**Additional context** +Add any other context about the problem here. diff --git a/code/extensions/js-debug/.github/ISSUE_TEMPLATE/feature_request.md b/code/extensions/js-debug/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000000..16eca6a8fc35 --- /dev/null +++ b/code/extensions/js-debug/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,13 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: feature-request +assignees: connor4312 +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. diff --git a/code/extensions/js-debug/.github/ISSUE_TEMPLATE/other.md b/code/extensions/js-debug/.github/ISSUE_TEMPLATE/other.md new file mode 100644 index 000000000000..aaa3ef058f4b --- /dev/null +++ b/code/extensions/js-debug/.github/ISSUE_TEMPLATE/other.md @@ -0,0 +1,7 @@ +--- +name: Other +about: Something else +title: '' +labels: '' +assignees: '' +--- diff --git a/code/extensions/js-debug/.github/prompts/rev.prompt.md b/code/extensions/js-debug/.github/prompts/rev.prompt.md new file mode 100644 index 000000000000..d11fb216633e --- /dev/null +++ b/code/extensions/js-debug/.github/prompts/rev.prompt.md @@ -0,0 +1,15 @@ +--- +name: rev +description: Update the version +--- + + + +The user will give you a target revision version. + +0. Check out a new branch `rev/` +1. Review the changes since the last released version +2. Update the CHANGELOG.md as necessary, creating a header for the current version, adding any new notes in there in the style of existing notes, and then adding a new "Unreleased" header +3. Use the "ask questions" tool to confirm with the user that the notes are good or ask for their feedback on the generated notes +4. Update the version in the package.json, then run `npm install` so the package-lock is updated to +5. Commit everything as `chore: prep v`, create a PR, and set it to auto-merge diff --git a/code/extensions/js-debug/.github/workflows/ci.yml b/code/extensions/js-debug/.github/workflows/ci.yml new file mode 100644 index 000000000000..45828baf84df --- /dev/null +++ b/code/extensions/js-debug/.github/workflows/ci.yml @@ -0,0 +1,70 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + validation: + strategy: + fail-fast: false + matrix: + include: + - name: macOS + os: macos-latest + node_version: '18' + only_minspec: false + - name: Linux + os: ubuntu-22.04 + node_version: '18' + only_minspec: false + - name: LinuxMinspec + os: ubuntu-22.04 + node_version: '18' + only_minspec: true + - name: Windows + os: windows-latest + node_version: '18' + only_minspec: false + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + env: + # TODO: Re-enable lint checks after current lint baseline failures are resolved. + DISABLE_LINT_FOR_NOW: 'true' + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node_version }} + + - name: Start xvfb + if: runner.os == 'Linux' + run: | + /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + echo ">>> Started xvfb" + + - name: npm install + run: npm ci --legacy-peer-deps + + - name: Test echo environment + run: node -e "console.log(process.env.PATH)" + env: + JSDBG_TEST_VERSION: insiders + DISPLAY: ':99.0' + + - name: Type checking + run: npm run test:types + + - name: Lint checking + if: ${{ env.DISABLE_LINT_FOR_NOW != 'true' && !matrix.only_minspec }} + run: npm run test:lint diff --git a/code/extensions/js-debug/.gitignore b/code/extensions/js-debug/.gitignore new file mode 100644 index 000000000000..b979223b565f --- /dev/null +++ b/code/extensions/js-debug/.gitignore @@ -0,0 +1,21 @@ +.cache/ +.profile/ +.cdp-profile/ +.headless-profile/ +.vscode-test/ +.DS_Store +node_modules/ +out/ +dist +/coverage +/.nyc_output +demos/web-worker/vscode-pwa-dap.log +demos/web-worker/vscode-pwa-cdp.log +.dynamic-testWorkspace +**/test/**/*.actual +/testWorkspace/web/tmp +/testWorkspace/**/debug.log +/testWorkspace/webview/win/true/ +*.cpuprofile +*.heapsnapshot +*.heapprofile diff --git a/code/extensions/js-debug/.husky/.gitignore b/code/extensions/js-debug/.husky/.gitignore new file mode 100644 index 000000000000..31354ec13899 --- /dev/null +++ b/code/extensions/js-debug/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/code/extensions/js-debug/.husky/pre-commit b/code/extensions/js-debug/.husky/pre-commit new file mode 100755 index 000000000000..84aacf94525b --- /dev/null +++ b/code/extensions/js-debug/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run -S precommit diff --git a/code/extensions/js-debug/.mocharc.unit.js b/code/extensions/js-debug/.mocharc.unit.js new file mode 100644 index 000000000000..44705d13f1dc --- /dev/null +++ b/code/extensions/js-debug/.mocharc.unit.js @@ -0,0 +1,5 @@ +module.exports = { + require: ['source-map-support/register', './src/test/testHooks.ts'], + spec: 'src/**/*.test.ts', + ignore: ['src/test/**/*.ts'], +}; diff --git a/code/extensions/js-debug/.vscodeignore b/code/extensions/js-debug/.vscodeignore new file mode 100644 index 000000000000..8bcc53b68255 --- /dev/null +++ b/code/extensions/js-debug/.vscodeignore @@ -0,0 +1,6 @@ +# note: this is moved into `dist` during compilation, and does not actually apply here +**/*.map +src/build/** +src/testRunner.js +testWorkspace/ +src/ diff --git a/code/extensions/js-debug/CHANGELOG.md b/code/extensions/js-debug/CHANGELOG.md new file mode 100644 index 000000000000..ebefb7eaeafa --- /dev/null +++ b/code/extensions/js-debug/CHANGELOG.md @@ -0,0 +1,680 @@ +# Changelog + +This changelog records changes to stable releases since 1.50.2. "TBA" changes here may be available in the [nightly release](https://github.com/microsoft/vscode-js-debug/#nightly-extension) before they're in stable. Note that the minor version (`v1.X.0`) corresponds to the VS Code version js-debug is shipped in, but the patch version (`v1.50.X`) is not meaningful. + +## Unreleased + +Nothing, yet + +## 1.117 (April 2026) + +- feat: add debug configuration provider for integrated browser ([#2347](https://github.com/microsoft/vscode-js-debug/pull/2347)) +- fix: capture early breakpoints when launching integrated browser ([#2347](https://github.com/microsoft/vscode-js-debug/pull/2347)) +- fix: prevent duplicate worker attachments in integrated browser ([#2347](https://github.com/microsoft/vscode-js-debug/pull/2347)) + +## 1.112 (March 2026) + +- feat: support debugging integrated browser ([#2329](https://github.com/microsoft/vscode-js-debug/pull/2329)) +- fix: filter empty string args to prevent node from hanging ([#2328](https://github.com/microsoft/vscode-js-debug/issues/2328)) +- fix: debug session not terminating when browser closed from elevated VS ([#2327](https://github.com/microsoft/vscode-js-debug/issues/2327)) + +## 1.110 (February 2026) + +- feat: add `Symbol.for("debug.properties")` for custom property replacement in debugger ([vscode#102181](https://github.com/microsoft/vscode/issues/102181)) +- feat: add focus emulation support ([#2316](https://github.com/microsoft/vscode-js-debug/pull/2316)) +- fix: label module scopes as expensive ([#2312](https://github.com/microsoft/vscode-js-debug/issues/2312)) +- fix: don't duplicate --allow-all for deno debug configuration ([#2308](https://github.com/microsoft/vscode-js-debug/issues/2308)) +- fix: dereferences of undefined at various places ([#2297](https://github.com/microsoft/vscode-js-debug/issues/2297)) + +## 1.105 (September 2025) + +- fix: slow sourcemap parsing for minified code ([#2265](https://github.com/microsoft/vscode-js-debug/issues/2265)) +- fix: assorted race conditions on dwarf and breakpoint initialization + +## 1.104 (August 2025) + +- fix: handle localhost hostname in constructInspectorWSUri function ([#2260](https://github.com/microsoft/vscode-js-debug/issues/2260)) +- chore: adopt renderer attachment changes from vscode#262546 ([#2267](https://github.com/microsoft/vscode-js-debug/issues/2267)) +- fix: toString not work in Local Variables panel until all variables defined in scope ([#2255](https://github.com/microsoft/vscode-js-debug/issues/2255)) +- fix: throw a cancellation error if pickProcess is aborted ([vscode#254852](https://github.com/microsoft/vscode/issues/254852)) + +## 1.102 (June 2025) + +- fix: subdirectory names to debug script configurations in monorepos ([#2242](https://github.com/microsoft/vscode-js-debug/issues/2242)) +- fix: async stackframe separators not using unique ID ([#2235](https://github.com/microsoft/vscode-js-debug/issues/2235)) +- feat: add killBehavior as a browser launch option ([#2238](https://github.com/microsoft/vscode-js-debug/issues/2238)) +- fix: inherit parent console options and use the right inspector opts ([#2226](https://github.com/microsoft/vscode-js-debug/issues/2226)) +- fix: default url for turbopack ([#2223](https://github.com/microsoft/vscode-js-debug/issues/2223)) +- fix: only add --experimental-networking for node/npm/etc binaries ([#2222](https://github.com/microsoft/vscode-js-debug/issues/2222)) +- fix: set version manually when using deno ([#2219](https://github.com/microsoft/vscode-js-debug/issues/2219)) +- fix: avoid extraneous rename mapping ([#2217](https://github.com/microsoft/vscode-js-debug/issues/2217)) +- fix: wasmworker memory reuse ([#2211](https://github.com/microsoft/vscode-js-debug/issues/2211)) + +## 1.100 (April 2025) + +- fix: explicitly specify completion ranges ([vscode#243409](https://github.com/microsoft/vscode/issues/243409)) +- fix: memory leak between debug sessions ([#2173](https://github.com/microsoft/vscode-js-debug/issues/2173)) +- fix: support `npm.scriptRunner: node` +- fix: copy multiline values as template literals ([vscode#241008](https://github.com/microsoft/vscode/issues/241008)) +- fix: support "attach to node process" without wmic.exe and on arm64 ([vscode#244139](https://github.com/microsoft/vscode/issues/244139)) +- fix: support webview2 debugging on arm64 +- fix: race condition when opening attached windows ([vscode#239769](https://github.com/microsoft/vscode/issues/239769)) +- chore: enable experimental networking by default on recent Node versions + +## 1.97 (January 2025) + +### 1.97.1 + +- fix: don't look for binaries outside the workspace folder ([vscode#240407](https://github.com/microsoft/vscode/issues/240407)) + +### 1.97.0 + +- fix: allow pretty printing sources when not paused ([#2138](https://github.com/microsoft/vscode-js-debug/issues/2138)) +- fix: breakpoints not registered in transpiled file in remoteRoot ([#2122](https://github.com/microsoft/vscode-js-debug/issues/2122)) +- fix: content verification of files in Node.js failing with UTF-8 BOM +- fix: extraneous threads continued event during shutdown at bp +- fix: webworker extension host not debuging ([#2147](https://github.com/microsoft/vscode-js-debug/issues/2147)) +- fix: add a basic language configuration for WAT ([vscode#224937](https://github.com/microsoft/vscode/issues/224937)) + +## v1.96 (November 2024) + +- fix: performance degredation of repeated variable calls ([#2120](https://github.com/microsoft/vscode-js-debug/issues/2120)) +- feat: add extension API for debug terminal contributions ([#2136](https://github.com/microsoft/vscode-js-debug/pull/2136)) + +## v1.95 (October 2024) + +- fix: apply sourceMapPathOverrides to file URIs ([vscode-dwarf-debugging-ext#7](https://github.com/microsoft/vscode-dwarf-debugging-ext/issues/7)) +- fix: allow hitting breakpoints early in webassembly ([vscode#230875](https://github.com/microsoft/vscode/issues/230875)) +- fix: only autofill "debug link" input if the hostname resolves ([vscode#228950](https://github.com/microsoft/vscode/issues/228950)) +- fix: support ANSI colorization in stdout logged strings ([vscode#230441](https://github.com/microsoft/vscode/issues/230441)) +- fix: disable entrypoint breakpoint at first pause in script ([vscode#230201](https://github.com/microsoft/vscode/issues/230201)) +- fix: avoid generating extra rebased paths in relative `rebaseLocalToRemote` ([#2091](https://github.com/microsoft/vscode-js-debug/issues/2091)) + +## v1.94 (September 2024) + +- feat: improve display of HTML elements in the debugger +- feat: add node tool picker completion for launch.json ([#1997](https://github.com/microsoft/vscode-js-debug/issues/1997)) +- fix: process attachment with `--inspect=:1234` style ([#2063](https://github.com/microsoft/vscode-js-debug/issues/2063)) +- fix: running new npm scripts in internal terminal ([vscode#227285](https://github.com/microsoft/vscode/issues/227285)) + +## v1.93 (August 2024) + +- feat: add basic network view, support experimental networking for node ([#2051](https://github.com/microsoft/vscode-js-debug/issues/2051)) +- feat: support "debug url" in terminals created through the `node-terminal` launch type ([#2049](https://github.com/microsoft/vscode-js-debug/issues/2049)) +- feat: adopt location references to link function locations +- fix: hover evaluation incorrectly showing undefined ([vscode#221503](https://github.com/microsoft/vscode/issues/221503)) + +## v1.92 (July 2024) + +- fix: automatically guess outFiles in extension development ([#2032](https://github.com/microsoft/vscode-js-debug/issues/2032)) +- fix: breakpoints at unmapped locations setting in wrong locations ([vscode#219031](https://github.com/microsoft/vscode/issues/219031)) +- fix: debug targets orphaned when a detached child starts after a parent exits ([vscode#219673](https://github.com/microsoft/vscode/issues/219673)) +- fix: adopt changes required for CVE-2024-27980 patch + +## v1.91 (June 2024) + +- feat: show correct values of shadowed variables in hovers ([#2022](https://github.com/microsoft/vscode-js-debug/issues/2022)) +- fix: hanging on certain Linux environments ([vscode#214872](https://github.com/microsoft/vscode/issues/214872)) + +## v1.90 (May 2024) + +- fix: improve main-thread performance of source map rename ([vscode#210518](https://github.com/microsoft/vscode/issues/210518)) +- fix: improve protocol handling performance in all cases ([#2001](https://github.com/microsoft/vscode-js-debug/issues/2001)) +- fix: debugging hanging if there's a localhost firewall ([#2004](https://github.com/microsoft/vscode-js-debug/issues/2004)) + +## v1.89 (April 2024) + +- feat: resolve executables from `node_modules/.bin` automatically ([#1984](https://github.com/microsoft/vscode-js-debug/issues/1984)) +- fix: add new source map path patterns for turbopack ([#1996](https://github.com/microsoft/vscode-js-debug/issues/1996)) +- fix: avoid forcing attach PID into debug mode with default args ([vscode#206683](https://github.com/microsoft/vscode/issues/206683)) + +## v1.88 (March 2024) + +- fix: stacktraces during process shutdown not being sourcemapped ([vscode#178814](https://github.com/microsoft/vscode/issues/178814)) +- fix: inconsistent display of strings with quotes ([vscode#182835](https://github.com/microsoft/vscode/issues/182835)) +- fix: 'start without debugging' not working ([vscode#206524](https://github.com/microsoft/vscode/issues/206524)) +- fix: resolve sourcemap coming from empty URLs ([vscode#205952](https://github.com/microsoft/vscode/issues/205952)) +- fix: race when stopping on node-internals exceptions ([vscode#204581](https://github.com/microsoft/vscode/issues/204581)) +- fix: off-by-one error leading to invalid renames ([#1948](https://github.com/microsoft/vscode-js-debug/issues/1948)) +- fix: apply sourceMapPathOverrides to sourceURL scripts ([vscode#204784](https://github.com/microsoft/vscode/issues/204784)) +- fix: attempt both ipv4 and ipv6 loopbacks for DWARF symbols + +## v1.87 (February 2024) + +- feat: lazily announce evaluated scripts ([#1939](https://github.com/microsoft/vscode-js-debug/issues/1939)) +- feat: support running extension test CLI from launch.json ([vscode#199211](https://github.com/microsoft/vscode/issues/199211)) +- fix: support object property shorthand in logpoints ([#1788](https://github.com/microsoft/vscode-js-debug/issues/1788)) +- fix: pages not loading in browser after attach browser disconnect ([#1795](https://github.com/microsoft/vscode-js-debug/issues/1795)) +- fix: skipFiles not matching/negating with special chars ([vscode#203408](https://github.com/microsoft/vscode/issues/203408)) + +## v1.86 (January 2024) + +- fix: respect resolveSourceMapLocations with remoteRoot ([#1921](https://github.com/microsoft/vscode-js-debug/issues/1921)) +- fix: match mjs and cjs in outFiles by default ([vscode#200665](https://github.com/microsoft/vscode/issues/200665)) +- fix: show errors from conditional breakpoints ([vscode#195062](https://github.com/microsoft/vscode/issues/195062)) +- fix: pausing on exceptions caused by internal scripts ([vscode#195062](https://github.com/microsoft/vscode/issues/195062)) +- fix: automatically reconnect when debugging browsers in port mode ([vscode#174033](https://github.com/microsoft/vscode/issues/174033)) + +## v1.85 (November 2023) + +- feat: support XHR breakpoints ([#1856](https://github.com/microsoft/vscode-js-debug/issues/1856)) +- feat: improve instrumentation breakpoints view ([#1853](https://github.com/microsoft/vscode-js-debug/issues/1853)) +- fix: reuse the webassembly worker across sessions in the debug tree ([#1830](https://github.com/microsoft/vscode-js-debug/issues/1830)) +- fix: respect sourceMapResolveLocations in the web extension host ([vscode#196781](https://github.com/microsoft/vscode/issues/196781)) +- fix: path diff display in diagnostic tool ([vscode#195891](https://github.com/microsoft/vscode/issues/195891)) +- fix: allow variable substitutions for ports properties ([vscode#192014](https://github.com/microsoft/vscode/issues/192014)) + +## v1.84 (October 2023) + +- feat: improve event listener breakpoints view ([#1853](https://github.com/microsoft/vscode-js-debug/issues/1853)) +- fix: envFiles variables appending rather than replacing in attach ([vscode#1935510](https://github.com/microsoft/vscode/issues/1935510)) +- fix: cache-bust sourcemaps if the underlying content changes ([#1803](https://github.com/microsoft/vscode-js-debug/issues/1803)) +- fix: make source map renames scope-aware +- fix: breakpoints not setting in webpack `eval`-type sourcemaps ([vscode#194988](https://github.com/microsoft/vscode/issues/194988)) +- fix: error when processing private properties with a map ([#1824](https://github.com/microsoft/vscode-js-debug/issues/1824)) + +## v1.83 (September 2023) + +- feat: enable DWARF-based WebAssembly debugging ([#1789](https://github.com/microsoft/vscode-js-debug/issues/1789)) +- feat: show class names of methods in call stack view ([#1770](https://github.com/microsoft/vscode-js-debug/issues/1770)) +- fix: edge devtools incorrectly ask for local forwarding ([vscode#193110](https://github.com/microsoft/vscode/issues/193110)) +- fix: authentication sourcemap fallback failing for some maps ([#1814](https://github.com/microsoft/vscode-js-debug/issues/1814)) +- fix: source map stepping command registered multiple times ([#1817](https://github.com/microsoft/vscode-js-debug/issues/1817)) + +## v1.82 (August 2023) + +- feat: allow basic webassembly debugging ([vscode#102181](https://github.com/microsoft/vscode/issues/102181)) +- feat: add `Symbol.for("debug.description")` as a way to generate object descriptions ([vscode#102181](https://github.com/microsoft/vscode/issues/102181)) +- feat: adopt supportTerminateDebuggee for browsers and node ([#1733](https://github.com/microsoft/vscode-js-debug/issues/1733)) +- fix: child processes from extension host not getting spawned during debug +- fix: support vite HMR source replacements ([#1761](https://github.com/microsoft/vscode-js-debug/issues/1761)) +- fix: immediately log stdout/err unless EXT is encountered ([vscode#181785](https://github.com/microsoft/vscode/issues/181785)) +- fix: hint content type for sources with query strings ([vscode#181746](https://github.com/microsoft/vscode/issues/181746)) +- chore: trigger perScriptSourceMaps for vite dev server ([#1739](https://github.com/microsoft/vscode-js-debug/issues/1739)) + +## v1.81 (July 2023) + +- fix: child process tree not terminating on all Linux distros ([#1747](https://github.com/microsoft/vscode-js-debug/issues/1747)) +- fix: set breakpoints predictably when launching with files ([#1748](https://github.com/microsoft/vscode-js-debug/issues/1748)) +- fix: don't overwrite custom NODE_OPTIONS ([#1746](https://github.com/microsoft/vscode-js-debug/issues/1746)) + +## v1.80 (June 2023) + +- fix: ECONNREFUSED when debugging from WSL (requires VS Code Insiders until release) ([#1603](https://github.com/microsoft/vscode-js-debug/issues/1603)) +- fix: terminal launches sometimes sending commands too soon ([#1642](https://github.com/microsoft/vscode-js-debug/issues/1642)) +- fix: step into `eval` when `pauseForSourceMap` is true does not pause on next available line ([#1692](https://github.com/microsoft/vscode-js-debug/issues/1692)) +- fix: useWebview debug sessions getting stuck if program exits without attaching ([#1666](https://github.com/microsoft/vscode-js-debug/issues/1666)) +- fix: improve the display of map and set entries +- fix: do not to translate "promise rejection" ([#1658](https://github.com/microsoft/vscode-js-debug/issues/1658)) +- fix: breakpoints not hitting early on in nested sourcemapped programs ([#1704](https://github.com/microsoft/vscode-js-debug/issues/1704)) +- fix: sourcemap predictor not filtering nested session on windows ([#1719](https://github.com/microsoft/vscode-js-debug/issues/1719)) +- fix: increase smart step backout threshold for better stepping ([#1700](https://github.com/microsoft/vscode-js-debug/issues/1700)) +- fix: Blazor sources sometimes being missing ([dotnet/runtime#86754](https://github.com/dotnet/runtime/issues/86754)) +- fix: possible bad state when resuming multiple times with a slow client + +## v1.78 (April 2023) + +### v1.78.0 - 2023-04-26 + +- fix: vite sources on posix not setting breakpoints correctly ([#1661](https://github.com/microsoft/vscode-js-debug/issues/1661)) +- fix: debugger failing on Node <=12 ([#1624](https://github.com/microsoft/vscode-js-debug/issues/1624)) +- fix: sourcemap lookups on ipv6 localhost addresses ([vscode#167353](https://github.com/microsoft/vscode/issues/167353)) +- fix: breakpoints not binding in certain cases if localRoot is a path child of remoteRoot ([#1617](https://github.com/microsoft/vscode-js-debug/issues/1617)) +- fix: browser debugging in remotes not working ([#1628](https://github.com/microsoft/vscode-js-debug/issues/1628)) +- fix: allow userDataDir in windows directory junctions ([#1656](https://github.com/microsoft/vscode-js-debug/issues/1656)) +- feat: support ETX in stdio console endings ([vscode#175763](https://github.com/microsoft/vscode/issues/175763)) +- feat: add 'remoteHostHeader' option for node attach ([#1664](https://github.com/microsoft/vscode-js-debug/issues/1664)) + +## v1.77 (March 2023) + +### v1.77.0 - 2023-03-21 + +- fix: repl stacktrace with renames showing too much info ([#1259](https://github.com/microsoft/vscode-js-debug/issues/1259#issuecomment-1409443564)) +- fix: recursive source map resolution parsing ignored locations ([vscode#169733](https://github.com/microsoft/vscode/issues/169733)) +- fix: evaluateName in watch variables not being set correctly ([vscode#175758](https://github.com/microsoft/vscode/issues/175758)) +- fix: unbound breakpoints in sourcemaps on Chrome 112 ([#1567](https://github.com/microsoft/vscode-js-debug/issues/1567)) +- fix: assorted bad source behaviors when reloading a page ([#1582](https://github.com/microsoft/vscode-js-debug/issues/1582)) +- fix: step over eval/new Function with sourcemaps not working ([#1556](https://github.com/microsoft/vscode-js-debug/issues/1556)) +- fix: 'break on caught exceptions' pausing on worker threads ([#1591](https://github.com/microsoft/vscode-js-debug/issues/1591)) +- chore: remove webpack, adopt esbuild + +## v1.76 (February 2023) + +### v1.76.0 - 2023-02-22 + +- fix: typeerror for users of vsDebugServer.bundle.js ([#1502](https://github.com/microsoft/vscode-js-debug/issues/1502)) +- fix: don't fail on dynamic config provisioning if no package.json's exist ([vscode#172522](https://github.com/microsoft/vscode/issues/172522)) +- fix: expansion of non-primitive getters not working ([#1525](https://github.com/microsoft/vscode-js-debug/issues/1525)) +- fix: support rich ANSI output for complex logs ([vscode#172868](https://github.com/microsoft/vscode/issues/172868)) +- fix: source map resolution in parent workspace folder paths not working ([#1554 comment](https://github.com/microsoft/vscode-js-debug/issues/1554#issuecomment-1420520834)) +- fix: revert support for renamed property accessors ([#1561](https://github.com/microsoft/vscode-js-debug/issues/1561)) +- fix: resolveSourceMapLocations not being auto-filled for ext host debug ([#1554 comment](https://github.com/microsoft/vscode-js-debug/issues/1554#issuecomment-1420520834)) + +## v1.75 (January 2023) + +### v1.75.0 - 2023-01-23 + +- fix: js files with sourceURLs opening readonly versions ([#1476](https://github.com/microsoft/vscode-js-debug/issues/1476)) +- fix: breakpoints not setting in paths with special glob characters ([vscode#166400](https://github.com/microsoft/vscode/issues/166400)) +- fix: better handling of multiple glob patterns and negations ([#1479](https://github.com/microsoft/vscode-js-debug/issues/1479)) +- fix: skipFiles making catastrophic regexes ([#1469](https://github.com/microsoft/vscode-js-debug/issues/1469)) +- fix: private properties in Blazor apps not grouping correctly ([#1331](https://github.com/microsoft/vscode-js-debug/issues/1331)) +- fix: perScriptSourcemaps not reliably breaking ([vscode#166369](https://github.com/microsoft/vscode/issues/166369)) +- fix: custom object `toString()` previews being too short ([vscode#155142](https://github.com/microsoft/vscode/issues/155142)) +- fix: show warning if console output length is hit ([vscode#154479](https://github.com/microsoft/vscode/issues/154479)) +- fix: improve variable and repl performance in large projects ([#1433](https://github.com/microsoft/vscode-js-debug/issues/1433)) +- fix: add ipv4->6 fallback ([vscode#167353](https://github.com/microsoft/vscode/issues/167353)) +- fix: js-debug in the browser showing extraneous error ([#1440](https://github.com/microsoft/vscode-js-debug/issues/1440)) +- fix: sourcemap renames not resolving property accessors ([#1383](https://github.com/microsoft/vscode-js-debug/issues/1383)) +- fix: breakpoint in blazor files set in JS not applying ([#1488](https://github.com/microsoft/vscode-js-debug/issues/1488)) +- fix: reduce number of ports used by debugger ([vscode#169182](https://github.com/microsoft/vscode/issues/169182)) +- fix: support launching chrome dev/beta as default fallbacks ([#1489](https://github.com/microsoft/vscode-js-debug/issues/1489)) +- fix: show memory refrence button for top-level watch expressions ([vscode#164124](https://github.com/microsoft/vscode/issues/164124)) +- fix: don't hardcode generated source types as javascript ([vscode#168013](https://github.com/microsoft/vscode/issues/168013)) +- refactor: improve breakpoint scanning speed 2-3x ([#1498](https://github.com/microsoft/vscode-js-debug/issues/1498)) + +## v1.74 (November 2022) + +### v1.74.0 - 2022-11-28 + +- feat: add automatic support for nested sourcemaps ([#1390](https://github.com/microsoft/vscode-js-debug/issues/1390)) +- feat: add an `ignoreLaunchArgs` option ([vscode#162957](https://github.com/microsoft/vscode/issues/162957)) +- feat: add support for `console.profile` ([#1443](https://github.com/microsoft/vscode-js-debug/issues/1443)) +- fix: copying a date object resulting in an empty object ([vscode#162747](https://github.com/microsoft/vscode/issues/162747)) +- fix: improve performance when using skipFiles in large projects ([#1179](https://github.com/microsoft/vscode-js-debug/issues/1179)) +- fix: breakpoints failing to set on paths with multibyte URL characters ([#1364](https://github.com/microsoft/vscode-js-debug/issues/1364)) +- fix: properly handle UNC paths ([#1148](https://github.com/microsoft/vscode-js-debug/issues/1148)) +- fix: discover npm scripts in nested workspace folders ([#1321](https://github.com/microsoft/vscode-js-debug/issues/1321)) +- chore: loosen restriction around enabling auto attach ([#1392](https://github.com/microsoft/vscode-js-debug/issues/1392)) +- fix: use platform preferred case in launcher ([#1448](https://github.com/microsoft/vscode-js-debug/1448)) Contributed on behalf of STMicroelectronics + +## v1.72 (September 2022) + +### v1.72.0 - 2022-09-27 + +- fix: request loop on certain kinds of Node.js attach failures ([vscode#156810](https://github.com/microsoft/vscode/issues/156810)) +- fix: breakpoints not being removed during startup ([#1371](https://github.com/microsoft/vscode-js-debug/issues/1371)) + +## v1.71 (August 2022) + +### v1.71.0 - 2022-08-24 + +- feat: make Deno easier to configure +- fix: path display issues in breakpoint diagnostic tool ([#1343](https://github.com/microsoft/vscode-js-debug/issues/1343)) +- fix: improve breakpoint resolution in webpack HMR ([vscode#155331](https://github.com/microsoft/vscode/issues/155331)) +- fix: allow overriding resolution of workspaceFolder in pathMapping ([#1308](https://github.com/microsoft/vscode-js-debug/issues/1308)) +- fix: extraneous warnings when restarting debugging ([vscode#156432](https://github.com/microsoft/vscode/issues/156432)) +- fix: webview debugging ([#1344](https://github.com/microsoft/vscode-js-debug/issues/1344)) +- fix: stack traces logged immediately before exit not being sourcemapped ([vscode#142197](https://github.com/microsoft/vscode/issues/142197)) + +## v1.70 (July 2022) + +### v1.70.0 - 2022-07-27 + +- feat: support providing terminal args as a string to avoid escaping ([#1335](https://github.com/microsoft/vscode-js-debug/issues/1335)) +- fix: performance improvements for setting breakpoints in large projects ([vscode#153470](https://github.com/microsoft/vscode/issues/153470)) +- fix: completions not returning stack variables ([vscode#153651](https://github.com/microsoft/vscode/issues/153651)) +- fix: react native windows direct debugging not showing variables ([vscode#154976](https://github.com/microsoft/vscode/issues/154976)) +- fix: previews showing in some cases `[object Object]` ([#1338](https://github.com/microsoft/vscode-js-debug/issues/1338)) + +## v1.69 (June 2022) + +### v1.69.0 - 2022-06-27 + +- feat: simplify pretty print to align with devtools ([vscode#151410](https://github.com/microsoft/vscode/issues/151410)) +- feat: add a new **Debug: Save Diagnostic JS Debug Logs** command ([#1301](https://github.com/microsoft/vscode-js-debug/issues/1301)) +- feat: use custom `toString()` methods to generate object descriptions ([#1284](https://github.com/microsoft/vscode-js-debug/issues/1284)) +- feat: allow easy toggling between compiled and sourcemapped sources ([vscode#151412](https://github.com/microsoft/vscode/issues/151412)) +- feat: implement step in targets ([vscode#123879](https://github.com/microsoft/vscode/issues/123879)) +- fix: debugged child processes in ext host causing teardown ([#1289](https://github.com/microsoft/vscode-js-debug/issues/1289)) +- fix: errors thrown in process tree lookup not being visible ([vscode#150754](https://github.com/microsoft/vscode/issues/150754)) +- fix: extension debugging not working with two ipv6 interfaces ([vscode#144315](https://github.com/microsoft/vscode/issues/144315)) +- fix: rare freezes if browsers logged information to stdout +- chore: adopt new restartFrame semantics from Chrome 104 ([#1283](https://github.com/microsoft/vscode-js-debug/issues/1283)) + +## v1.68 (May 2022) + +### v1.68.0 - 2022-05-30 + +- chore: support new sha script hashes from chrome ([#1244](https://github.com/microsoft/vscode-js-debug/issues/1244)) +- fix: bigint value previews not working in some cases ([#1277](https://github.com/microsoft/vscode-js-debug/issues/1277)) +- fix: snap versions in alternate install locations resulting in warning ([#1239](https://github.com/microsoft/vscode-js-debug/issues/1239)) +- fix: align hoverEvaluation config suggestion with actual default +- fix: remove query strings from sourcemapped URLs ([#1225](https://github.com/microsoft/vscode-js-debug/issues/1225)) +- fix: prefer to parse source map directly before failling back to path mapping ([vscode#148864](https://github.com/microsoft/vscode/issues/148864)) +- fix: only enter debug mode on f11 when debug view is visible ([vscode#141157](https://github.com/microsoft/vscode/issues/141157)) + +## v1.67 (April 2022) + +### v1.67.2 - 2022-04-29 + +- fix: data URI sourcemaps not loading + +### v1.67.1 - 2022-04-28 + +- fix: debug not working on Node 12 or lower + +### v1.67.0 - 2022-04-26 + +- feat: apply pathMapping when loading sourcemaps ([#1240](https://github.com/microsoft/vscode-js-debug/issues/1240)) +- feat: apply pathMapping when loading sourcemaps ([#1242](https://github.com/microsoft/vscode-js-debug/issues/1242)) +- fix: sourcemap renames replacing in invalid contexts ([#1201](https://github.com/microsoft/vscode-js-debug/issues/1201)) + +## v1.66 (March 2022) + +### v1.66.1 - 2022-03-24 + +- feat: adopt `CompletionItem.detail` ([vscode#145645](https://github.com/microsoft/vscode/issues/145645)) +- feat: support for debugging webviews in UWPs ([#1209](https://github.com/microsoft/vscode-js-debug/issues/1209)) +- fix: accessor properties not being writable ([vscode#146001](https://github.com/microsoft/vscode/issues/146001)) +- fix: completions sometimes throwing issue on accessor ([#1218](https://github.com/microsoft/vscode-js-debug/issues/1218)) + +### v1.66.0 - 2022-03-03 + +- feat: add heap profiler +- fix: properly support DAP `valueFormat` ([#1188](https://github.com/microsoft/vscode-js-debug/issues/1188)) +- fix: don't use `pwa-` prefixed launch types in snippets ([#1138](https://github.com/microsoft/vscode-js-debug/issues/1138)) +- fix: readonly attribute not being applied to getter values ([vscode#143790](https://github.com/microsoft/vscode/issues/143790)) +- fix: cwd being lost causing resolution errors in auto attach ([#1212](https://github.com/microsoft/vscode-js-debug/issues/1212)) +- fix: avoid nesting `localRoot`'s in programmatic starts ([#1140](https://github.com/microsoft/vscode-js-debug/issues/1140)) +- fix: icon in "stop profiling" button not spinning ([vscode#136742](https://github.com/microsoft/vscode/issues/136742)) +- refactor: simplify and improve browser connection in WSL and remotes + +## v1.65 (February 2022) + +### v1.65.0 - 2022-02-23 + +- feat: adopt `isTransient` to avoid persisting debug terminal ([#1196](https://github.com/microsoft/vscode-js-debug/issues/1196)) +- feat: adopt new presentationHint.lazy for getters ([#1211](https://github.com/microsoft/vscode-js-debug/issues/1211)) +- fix: don't use `pwa-` prefixed launch types in snippets ([#1138](https://github.com/microsoft/vscode-js-debug/issues/1138)) +- fix: logpoints causing pauses if console.log returns truthy ([#1191](https://github.com/microsoft/vscode-js-debug/issues/1191)) +- fix: handle query string and path fragments in `file`s within the launch config ([vscode#142199](https://github.com/microsoft/vscode/issues/142199)) +- fix: do not narrow outFiles within the workspace folder automatically ([vscode#142641](https://github.com/microsoft/vscode/issues/142641)) +- fix: improve formatting of errors in output ([vscode#122870](https://github.com/microsoft/vscode/issues/122870)) +- fix: improve labels in Excluded Caller view ([vscode#141669](https://github.com/microsoft/vscode/issues/141669)) +- refactor: remove usage of `Debugger.callFrame.url` ([#1136](https://github.com/microsoft/vscode-js-debug/issues/1136)) +- refactor: clean debt around output, fix previews not updating on memory changes + +## v1.64 (January 2022) + +### v1.64.3 - 2022-02-08 + +- fix: blazor attachment not working ([#1190](https://github.com/microsoft/vscode-js-debug/issues/1190)) + +### v1.64.2 - 2022-01-27 + +- fix: excluded callers not working consistently + +### v1.64.1 - 2022-01-25 + +- fix: excluded callers not updating during same session +- fix: capitalization of label in exclude callers ([vscode#141454](https://github.com/microsoft/vscode/issues/141454)) +- fix: respect bytesOffset/byteLength when reading/writing memory ([vscode#141449](https://github.com/microsoft/vscode/issues/141449)) + +### v1.64.0 - 2022-01-24 + +- feat: support debugging Edge on Linux ([vscode#138495](https://github.com/microsoft/vscode/issues/138495)) +- feat: support readMemory/writeMemory requests ([#1167](https://github.com/microsoft/vscode/issues/1167)) +- feat: copy binary types better ([#1168](https://github.com/microsoft/vscode-js-debug/issues/1168)) +- feat: add excluded callers ([vscode#127775](https://github.com/microsoft/vscode/issues/127775)) +- fix: use default NVM directory if NVM_DIR is not set ([vscode#133521](https://github.com/microsoft/vscode/issues/133521)) +- fix: lines offset when debugging web worker extensions ([vscode#136242](https://github.com/microsoft/vscode/issues/136242)) +- fix: "copy as expression" and "add to watch" for private fields ([vscode#135944](https://github.com/microsoft/vscode/issues/135944)) +- fix: `autoAttachChildProcesses` in extension host sometimes not working ([#1134](https://github.com/microsoft/vscode-js-debug/issues/1134)) +- fix: improve sourcemap resolution when code is outside of the workspaceFolder ([vscode#139086](https://github.com/microsoft/vscode/issues/139086)) +- fix: automatically try 127.0.0.1 if requests to localhost fail ([vscode#140536](https://github.com/microsoft/vscode/issues/140536)) +- fix: make node process regex more permissive ([vscode#137084](https://github.com/microsoft/vscode/issues/137084)) +- fix: breakpoints in paths with URI component entities not binding ([#1174](https://github.com/microsoft/vscode-js-debug/issues/1174)) + +## v1.62 (October 2021) + +### v1.62.0 - 2021-10-26 + +- feat: allow multiline values in envFiles ([#1116](https://github.com/microsoft/vscode-js-debug/issues/1116)) +- feat: rewrite old `.scripts` command to new diagnostic tool +- feat: sort non-enumerable properties to match Chrome devtools ([vscode#73061](https://github.com/microsoft/vscode/issues/73061)) +- fix: update path handling when debugging vscode webviews ([vscode#133867](https://github.com/microsoft/vscode/issues/133867)) +- fix: allow webpacked path with special characters to map ([#1080](https://github.com/microsoft/vscode-js-debug/issues/1080)) +- fix: provide explicit warning if cwd is invalid ([vscode#133310](https://github.com/microsoft/vscode/issues/133310)) +- fix: don't change url when restarting the debug session ([#1103](https://github.com/microsoft/vscode-js-debug/issues/1103)) +- fix: breakpoint diagnostic tool not working +- fix: use proper default resolution for sourceMapPathOverrides for node-terminal ([vscode#114076](https://github.com/microsoft/vscode/issues/114076)) +- fix: private class fields not working in repl ([#1113](https://github.com/microsoft/vscode-js-debug/issues/1113)) +- chore: update docstring on `debugWebviews` ([#1127](https://github.com/microsoft/vscode-js-debug/issues/1127)) + +## v1.61 (September 2021) + +### v1.61.0 - 2021-09-28 + +- fix: sourcemap locations not resolving on remotes ([vscode#131729](https://github.com/microsoft/vscode/issues/131729)) +- fix: remove redundant `__proto__` prop on recent V8 versions ([vscode#130365](https://github.com/microsoft/vscode/issues/130365)) +- fix: debug ports being auto forwarded after detach ([#1092](https://github.com/microsoft/vscode-js-debug/issues/1092)) +- fix: don't incorrectly scope sourcemap resolution to node_modules ([#1100](https://github.com/microsoft/vscode-js-debug/issues/1100)) +- fix: sourcemaps not working in preloads in older Electron versions ([#1099](https://github.com/microsoft/vscode-js-debug/issues/1099)) +- fix: duplicate entries in launch.json creator ([vscode#132932](https://github.com/microsoft/vscode/issues/132932)) +- feat: add node_internals to skipFiles by default ([#1091](https://github.com/microsoft/vscode-js-debug/issues/1091)) +- feat: allow using a .ps1 script as a runtimeExectuable ([#1093](https://github.com/microsoft/vscode-js-debug/issues/1093)) +- feat: avoid attaching to scripts in .rc files ([vscode#127717](https://github.com/microsoft/vscode/issues/127717)) + +## v1.60 (August 2021) + +### v1.60.1 - 2021-08-23 + +- fix: fall back to any installed browser version if stable is not available ([vscode#129013](https://github.com/microsoft/vscode/issues/129013)) +- fix: workspaceFolder error in workspace launch configs ([vscode#128922](https://github.com/microsoft/vscode/issues/128922)) +- fix: console logs being slow when run without debugging ([#1068](https://github.com/microsoft/vscode-js-debug/issues/1068)) +- fix: not pausing on unhandled promise rejections ([vscode#130265](https://github.com/microsoft/vscode/issues/130265)) +- feat: support setExpression for updating WATCH view variables ([#1075](https://github.com/microsoft/vscode-js-debug/issues/1075)) +- feat: integrate skipFiles with smartStepping to step through blackbox failures ([#1085](https://github.com/microsoft/vscode-js-debug/issues/1085)) +- fix: extension host not always being torn down when stopping debugging ([vscode#126911](https://github.com/microsoft/vscode/issues/126911)) +- fix: args list not updating when session is restarted ([vscode#128058](https://github.com/microsoft/vscode/issues/128058)) + +### v1.60.0 - 2021-08-03 + +- chore: take ownership of the default launch types ([#1065](https://github.com/microsoft/vscode-js-debug/issues/1065)) +- fix: apply electron updates for debugging vscode webviews ([vscode#128637](https://github.com/microsoft/vscode/issues/128637)) + +## v1.59 (July 2021) + +### v1.59.0 - 2021-07-27 + +- feat: support $returnValue in conditional breakpoints ([vscode#129328](https://github.com/microsoft/vscode/issues/129328)) +- fix: pausing on first line of worker_thread when created with empty env ([vscode#125451](https://github.com/microsoft/vscode/issues/125451)) +- fix: exclude electron from chrome attach reload ([#1058](https://github.com/microsoft/vscode-js-debug/issues/1058)) +- fix: retry websocket connections instead of waiting for timeout +- chore: adopt new terminal icon + +## v1.58 (June 2021) + +### v1.58.2 - 2021-07-01 + +- fix: breakpoints not being set when debugging file uris ([#1035](https://github.com/microsoft/vscode-js-debug/issues/1035)) + +### v1.58.1 - 2021-06-30 + +- feat: allow disabling sourcemap renames ([#1033](https://github.com/microsoft/vscode-js-debug/issues/1033)) +- fix: show welcome view for all common languages ([#1039](https://github.com/microsoft/vscode-js-debug/issues/1039)) +- fix: apply skipFile exception checking for promise rejections + +### v1.58.0 - 2021-06-16 + +- feat: reload page on attached restart ([#1004](https://github.com/microsoft/vscode-js-debug/issues/1004)) +- feat: allow taking heap snapshots with profiler ([#1031](https://github.com/microsoft/vscode-js-debug/issues/1031)) +- fix: default F5 not working on files outside workspace ([vscode#125796](https://github.com/microsoft/vscode/issues/125796)) +- fix: debugging with no launch config fails when tsc task detection is disabled ([vscode#69572](https://github.com/microsoft/vscode/issues/69572)) +- fix: race causing lost sessions when attaching to many concurrent processes in the debug terminal ([vscode#124060](https://github.com/microsoft/vscode/issues/124060)) +- fix: pathMapping not working if url in browser launch is undefined ([#1003](https://github.com/microsoft/vscode-js-debug/issues/1003)) +- fix: error when trying to set a breakpoint in index.html ([#1028](https://github.com/microsoft/vscode-js-debug/issues/1028)) +- fix: only request source content for sourcemaps with renames ([#1033](https://github.com/microsoft/vscode-js-debug/issues/1033)) +- chore: update terminal profile contributions ([vscode#120369](https://github.com/microsoft/vscode/issues/120369)) + +## v1.57 (May 2021) + +### v1.57.0 - 2021-06-02 + +- feat: support renamed sourcemap identifiers ([vscode#12066](https://github.com/microsoft/vscode/issues/12066)) +- feat: support DAP `hitBreakpointIds` ([#994](https://github.com/microsoft/vscode-js-debug/issues/994)) +- feat: add Edge inspector integration +- feat: allow limited adjustment of launch config options during restart ([vscode#118196](https://github.com/microsoft/vscode/issues/118196)) +- fix: make sure servers are listening before returning +- fix: don't send infinite telemetry requests for React Native ([#981](https://github.com/microsoft/vscode-js-debug/issues/981)) +- fix: skipFiles working inconsistently in `attach` mode ([vscode#118282](https://github.com/microsoft/vscode/issues/118282)) +- fix: contribute js-debug to html ([vscode#123106](https://github.com/microsoft/vscode/issues/123106)) +- chore: log errors activating auto attach +- fix: intermittent debug failures with browsers, especially Electron ([vscode#123420](https://github.com/microsoft/vscode/issues/123420))) +- fix: add additional languages for browser debugging ([vscode#123484](https://github.com/microsoft/vscode/issues/123484)) +- fix: worker processes breaking sessions when attaching multiple times ([vscode#124045](https://github.com/microsoft/vscode/issues/124045)) +- fix: wrong name of autogenerated edge debug config +- fix: add warning for outdated or buggy Node.js versions ([#1017](https://github.com/microsoft/vscode-js-debug/issues/1017)) +- refactor: include a mandatory path in the CDP proxy ([#987](https://github.com/microsoft/vscode-js-debug/issues/987)) +- chore: adopt new terminal profile contribution point ([vscode#120369](https://github.com/microsoft/vscode/issues/120369)) + +## v1.56 (April 2021) + +### v1.56.2 - 2021-04-39 + +- fix: string previews not working in RN Windows + +### v1.56.1 - 2021-04-23 + +- feat: show private properties in the inspector ([#892](https://github.com/microsoft/vscode-js-debug/issues/892)) +- fix: sources not working in RN Windows ([vscode#121136](https://github.com/microsoft/vscode/issues/121136)) +- fix: improve suggest tool behavior ([#970](https://github.com/microsoft/vscode-js-debug/issues/970)) +- fix: re-apply breakpoints if pages crash + +### v1.56.0 - 2021-04-07 + +- feat: 'intelligently' suggest using diagnostic tool for breakpoint issues ([vscode#57590](https://github.com/microsoft/vscode/issues/57590)) +- feat: add cdp sharing for extensions to interact with debugging, see [docs](./CDP_SHARE.md) ([#892](https://github.com/microsoft/vscode-js-debug/issues/893)) +- fix: runtimeVersion overwriting default PATH ([vscode#120140](https://github.com/microsoft/vscode/issues/120140)) +- fix: skipFiles not skipping ranges in sourcemapped scripts ([vscode#118282](https://github.com/microsoft/vscode/issues/118282)) +- chore: update wording on debug terminal label to match new profiles +- fix: 'node version is outdated' incorrectly showing with auto attach ([#957](https://github.com/microsoft/vscode-js-debug/issues/957)) +- fix: programs not terminating in 'run without debugging' with break on exception ([vscode#119340](https://github.com/microsoft/vscode/issues/119340)) +- fix: browser debugging when using a WSL remote ([vscode#120227](https://github.com/microsoft/vscode/issues/120227)) + +## v1.55 (March 2021) + +### v1.55.1 - 2021-03-24 + +- fix: sessions hanging if exception is thrown immediately before or during shutdown +- fix: track DAP servers in ports manager as well ([#942 comment](https://github.com/microsoft/vscode-js-debug/issues/942#event-4501887036)) + +### v1.55.0 - 2021-03-22 + +- feat: implement 'start debugging and stop on entry' command/keybinding ([vscode#49855](https://github.com/microsoft/vscode/issues/49855)) +- feat: improve handling of symbolic links ([#776](https://github.com/microsoft/vscode-js-debug/issues/776)) +- feat: add forwarded port attributes ([#942](https://github.com/microsoft/vscode-js-debug/issues/942)) +- fix: pretty print not working when evaling sources ([#929](https://github.com/microsoft/vscode-js-debug/issues/929)) +- fix: browser debugging in remote not working on some Linux systems ([#908](https://github.com/microsoft/vscode-js-debug/issues/908)) +- fix: edge not launching if VS Code is run in admin mode on windows ([vscode#117005](https://github.com/microsoft/vscode/issues/117005)) +- fix: exception breakpoint toggle getting stuck ([919](https://github.com/microsoft/vscode-js-debug/issues/919)) +- fix: spooky race that could incorrectly break when entering hot-transpiled code + +## v1.54 (February 2021) + +### v1.54.4 - 2021-03-04 + +- fix: worker_thread debugging node working on Node >14.5.0 ([933](https://github.com/microsoft/vscode-js-debug/issues/933)) + +### v1.54.3 - 2021-02-24 + +- fix: auto attach failing when entering node repl + +### v1.54.2 - 2021-02-23 + +- fix: auto attach only to workspace scripts by default ([#856](https://github.com/microsoft/vscode-js-debug/issues/856)) +- fix: do not show restart frame action on async stacktraces ([vscode#116345](https://github.com/microsoft/vscode/issues/116345)) +- fix: do not attach to node-gyp fixing install failures ([vscode#117312](https://github.com/microsoft/vscode/issues/117312)) +- fix: sessions being mixed up or not initializing when attaching concurrently ([vscode#115996](https://github.com/microsoft/vscode/issues/115996)) + +### v1.54.1 - 2021-02-04 + +- fix: wrong command used in create debug terminal command + +### v1.54.0 - 2021-02-08 + +- fix: allow copying values from watch expressions ([vscode#115049](https://github.com/microsoft/vscode/issues/115049)) +- fix: reuse debug terminals when running npm scripts, when possible +- refactor: move script lens functionality into built-in npm extension + +## v1.53 (January 2021) + +### v1.53.0 - 2021-01-25 + +- feat: allow debugging node worker_threads +- feat: allow pausing on conditional exceptions ([vscode#104453](https://github.com/microsoft/vscode/issues/104453)) +- feat: make the line on log messages take into account skipFiles ([#882](https://github.com/microsoft/vscode-js-debug/issues/882)) +- feat: allow specifying request options used to request sourcemaps and content ([#904](https://github.com/microsoft/vscode-js-debug/issues/904)) +- fix: persist state in the diagnostic tool ([#879](https://github.com/microsoft/vscode-js-debug/issues/879)) +- fix: allow outdated node dialog to be bypassed ([vscode#111642](https://github.com/microsoft/vscode/issues/111642)) +- fix: syntax errors in chrome not showing locations ([#867](https://github.com/microsoft/vscode-js-debug/issues/867)) +- fix: handle certain types of webpack source maps in attachments ([#854](https://github.com/microsoft/vscode-js-debug/issues/854)) +- fix: attachment issue on Node 15 ([#895](https://github.com/microsoft/vscode-js-debug/issues/895)) +- fix: default node cwd to the localRoot if set ([#894](https://github.com/microsoft/vscode-js-debug/issues/894)) +- fix: fix: better handle html served as index and without extensions ([#883](https://github.com/microsoft/vscode-js-debug/issues/883), [#884](https://github.com/microsoft/vscode-js-debug/issues/884)) +- docs: remove preview terminology from js-debug ([#894](https://github.com/microsoft/vscode-js-debug/issues/894)) +- fix: debugger statements being missed if directly stepped on the first executable line of a new script early in execution +- fix: source map warning on node 15 ([#903](https://github.com/microsoft/vscode-js-debug/issues/903)) + +## v1.52 (November/December 2020) + +### v1.52.2 - 2020-12-07 + +- fix: issue preventing breakpoint predictor from running in ext host ([vscode#112052](https://github.com/microsoft/vscode/issues/112052)) + +### v1.52.1 - 2020-12-01 + +- fix: processes not being killed on posix ([#864](https://github.com/microsoft/vscode-js-debug/issues/864)) + +### v1.52.0 - 2020-11-30 + +- feat: allow debugging node internals ([#823](https://github.com/microsoft/vscode-js-debug/issues/823)) +- feat: show diagnostic tool in a webview and integrate with vscode theme ([vscode#109526](https://github.com/microsoft/vscode/issues/109526), [vscode#109529](https://github.com/microsoft/vscode/issues/109529), [vscode#109531](https://github.com/microsoft/vscode/issues/109531)) +- feat: allow specifying defaults runtimeExecutables ([#836](https://github.com/microsoft/vscode-js-debug/issues/836)) +- feat: support vscode webview resource uri sourcemaps ([#820](https://github.com/microsoft/vscode-js-debug/pull/820)) +- feat: allow configuring the debugger killBehavior ([#630](https://github.com/microsoft/vscode-js-debug/issues/630)) +- fix: support chrome dev and beta builds ([ref](https://github.com/OmniSharp/omnisharp-vscode/issues/4108)) +- fix: race causing potentially corrupted log files ([#825](https://github.com/microsoft/vscode-js-debug/issues/825)) +- fix: extension host debugging pausing in internals ([vscode#105047](https://github.com/microsoft/vscode/issues/105047)) +- fix: make urls ending in `/*` also match the base path ([#834](https://github.com/microsoft/vscode-js-debug/issues/834)) +- fix: ignore hash portion of url when determining matches ([#840](https://github.com/microsoft/vscode-js-debug/issues/840)) +- fix: automatically add a \* suffix to sourceMapPathOverrides that lack one ([#841](https://github.com/microsoft/vscode-js-debug/issues/841)) +- fix: don't show `Debug: Open Link` command in web where it doesn't work +- fix: handle exceptions thrown dealing with sourcemaps in prediction ([#845](https://github.com/microsoft/vscode-js-debug/issues/845)) +- fix: don't show quick pick when there is only a single npm script ([#851](https://github.com/microsoft/vscode-js-debug/issues/851)) +- fix: don't narrow outfiles on any remoteRoot ([#854](https://github.com/microsoft/vscode-js-debug/issues/854)) +- fix: more thoroughly clean VS Code-specific environment variables from launch ([#64897](https://github.com/microsoft/vscode/issues/64897), [#38428](https://github.com/microsoft/vscode/issues/38428)) +- fix: node internals not skipping on Node 15 ([#862](https://github.com/microsoft/vscode-js-debug/issues/862)) +- fix: don't scan outfiles when sourceMaps is false ([#866](https://github.com/microsoft/vscode-js-debug/issues/866)) +- fix: skipfiles not working for paths in dotfiles/folders ([vscode#111301](https://github.com/microsoft/vscode/issues/111301)) + +## v1.51 (October 2020) + +### v1.51.0 - 2020-10-26 + +- feat: add a diagnostic tool under the `Create Diagnostic Information` command ([#260](https://github.com/microsoft/vscode-js-debug/issues/260)) +- feat: add an advanced `perScriptSourcemaps` option, when loading individual unbundled scripts +- feat: suffix rather than prefix setter/getters ([vscode#108036](https://github.com/microsoft/vscode/issues/108036)) +- fix: include the response body in sourcemap http error info +- fix: extensions being able to activate before the debugger attaches ([vscode#108141](https://github.com/microsoft/vscode/pull/108141)) +- fix: debugger failing to connect on Node 14 on Windows 7 ([#791](https://github.com/microsoft/vscode-js-debug/issues/791)) +- fix: inherit the system's NODE_OPTIONS if set ([#790](https://github.com/microsoft/vscode-js-debug/issues/790)) +- fix: use `*` as a urlFilter by default only for launching (not attaching) ([ref](https://github.com/microsoft/vscode-chrome-debug/issues/719)) +- fix: exclude `nvm`-installed binaries from auto attach ([#794](https://github.com/microsoft/vscode-js-debug/issues/794)) +- fix: smart auto attaching briefly debugging a process when using `code` from the CLI ([#783](https://github.com/microsoft/vscode-js-debug/issues/783)) +- fix: realtime performance not being shown when a webworker is selected ([ref](https://github.com/microsoft/vscode-js-profile-visualizer/issues/23)) +- fix: breakpoints sometimes not being rebound after navigating away from and back to a page ([#807](https://github.com/microsoft/vscode-js-debug/issues/807)) +- fix: breakpoints not being bound correctly on Blazor apps ([#796](https://github.com/microsoft/vscode-js-debug/issues/796)) +- fix: remote source maps don't resolve correctly with an absolute sourceroot shorter than the local path ([vscode#108418](https://github.com/microsoft/vscode/issues/108418)) +- fix: terminal links not setting the first workspace folder ([#701](https://github.com/microsoft/vscode-js-debug/issues/701)) +- fix: send ctrl+c to kill nodemon running in debug terminal ([vscode#108289](https://github.com/microsoft/vscode/issues/108289)) +- fix: increase auto attach timeout ([#806](https://github.com/microsoft/vscode-js-debug/issues/806)) +- fix: stepping into function on the first line of a file with a breakpoint ([vscode#107859](https://github.com/microsoft/vscode/issues/107859)) +- fix: webpage opening twice when using `serverReadyAction` with `console: integratedTerminal` ([#814](https://github.com/microsoft/vscode-js-debug/issues/814)) +- refactor: improve performance when loading very many sourcemaps for pages that don't need authentication +- refactor: remove runtime dependency on TypeScript ([vscode#107680](https://github.com/microsoft/vscode/issues/107680)) + +## 1.50.2 - 2020-10-02 + +Start of changelog records diff --git a/code/extensions/js-debug/CODE_OF_CONDUCT.md b/code/extensions/js-debug/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..f9ba8cf65f3e --- /dev/null +++ b/code/extensions/js-debug/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/code/extensions/js-debug/COMMON_PROBLEMS.md b/code/extensions/js-debug/COMMON_PROBLEMS.md new file mode 100644 index 000000000000..ee0ccd77d183 --- /dev/null +++ b/code/extensions/js-debug/COMMON_PROBLEMS.md @@ -0,0 +1,43 @@ +# Common Problems + +## My app doesn't start run, and I use `--inspect-brk` to load it + +### Symptoms + +The app doesn't run, breakpoints don't bind. It "hangs" indefinitely, and either directly or indirectly you use `--inspect-brk` to set it up. + +### Solution + +In most cases, `--inspect-brk` is not needed. You can remove it entirely, or use only `--inspect`. + +### Reason + +This debugger attaches to scripts by using `NODE_OPTIONS` to tell Node.js to `--require` a bootloader script before running your program. This bootloader sets up the communication between VS Code and your application, and doing it in this way lets us do a whole lot of really neat things (like implement the Debug Terminal and debug child processes automatically.) + +However, `--inspect-brk` will cause Node.js to pause on the first line of the executed script and wait for a debugger to attach. Unfortunately, this pauses at the first line of the _bootloader_, so it never tells VS Code that there's something to debug. + +In most cases, `--inspect-brk` was used to make sure the VS Code attached completely before running your program. The bootloader does the same thing, so this is no longer necessary. + +## My app doesn't run, and I have an antivirus/firewall running + +### Symptoms + +You launch your app and VS Code enters debug mode, but it doesn't attach to the application and the "Pause" and "Step" buttons in the debug toolbar are disabled, and you're running an antivirus/firewall. + +### Solution + +We've seen some cases where an antivirus or firewall prevents VS Code from attaching to the process. To fix this, in order of preference: + +- You can allow local/'loopback' connections +- By default, we use a random free port. You can pass an `--inspect` flag to your `runtimeArgs` to use the default port 9229, however we will still be unable to debug child processes. +- You can disable your firewall, or allowlist VS Code/Node.js. + +## My app doesn't run using a Node 10 release before around 10.18.0 + +### Symptoms + +You launch your app, but the debugger doesn't connect to it. You're using an older Node 10 release. + +### Solution + +We've seen some transient issues with early Node 10 point releases. To fix this we, recommend updating to a later Node 10 release (10.22.1 being the most recent at the time of writing), or to a newer version of Node altogether if nothing is keeping you on 10. diff --git a/code/extensions/js-debug/CONTRIBUTING.md b/code/extensions/js-debug/CONTRIBUTING.md new file mode 100644 index 000000000000..63ffb08e077a --- /dev/null +++ b/code/extensions/js-debug/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Development + +For basic development of the extension you will need the [nightly extension](https://github.com/microsoft/vscode-js-debug#nightly-extension) locally, and you can then: + +1. Clone the repo and run `npm install` +2. Run `npm run watch` in a terminal. This will compile and watch for changes in sources. +3. Run the `Extension` launch configuration. + +For debugging the companion app used to launch browsers from remotes, the process is similar: + +- Also clone `vscode-js-debug-companion` as a sibling directory to `vscode-js-debug`. +- Run `npm run watch` for the companion. +- Run the `Extension and Companion` launch configuration. +- Set `"browserLaunchLocation": "ui"` in your launch.json to route requests through the companion extension. diff --git a/code/extensions/js-debug/CodeQL.yml b/code/extensions/js-debug/CodeQL.yml new file mode 100644 index 000000000000..758b542e9faa --- /dev/null +++ b/code/extensions/js-debug/CodeQL.yml @@ -0,0 +1,12 @@ +path_classifiers: + test: + - "**/*.test.ts" + - "src/test/**/*.ts" + - .vscode-test + + generated: + - testWorkspace + - dist + + library: + - "**/node_modules/**" diff --git a/code/extensions/js-debug/EXTENSION_AUTHORS.md b/code/extensions/js-debug/EXTENSION_AUTHORS.md new file mode 100644 index 000000000000..9d03c827dd2e --- /dev/null +++ b/code/extensions/js-debug/EXTENSION_AUTHORS.md @@ -0,0 +1,43 @@ +# Extensibility + +js-debug has a few ways other extensions can 'plug into' js-debug and provide additional extensibility. + +## Extension API + +js-debug provides an extension API you can use to do certain things. Please refer to [the typings](https://github.com/microsoft/vscode-js-debug/blob/main/src/typings/vscode-js-debug.d.ts) for capabilities. + +To use this, you would: + +1. Add a step in your build process to download the typings from https://github.com/microsoft/vscode-js-debug/blob/main/src/typings/vscode-js-debug.d.ts to somewhere in your source tree. +2. Access the API like so: + + ```js + const jsDebugExt = vscode.extensions.getExtension('ms-vscode.js-debug-nightly') || vscode.extensions.getExtension('ms-vscode.js-debug'); + await jsDebugExt.activate() + const jsDebug: import('@vscode/js-debug').IExports = jsDebugExt.exports; + ``` + +## CDP Sharing Mechanism + +This file documents the CDP sharing mechanism in js-debug. It can be useful for advanced extensions and plugins. The original feature request can be found in [#892](https://github.com/microsoft/vscode-js-debug/issues/893). + +### Requesting a CDP Connection + +js-debug can be asked to share its CDP connection by running the `extension.js-debug.requestCDPProxy` command with the debug session ID you wish to connect to. js-debug will respond with an object containing a WebSocket server address in the form `{ host: string, port: string }`. You can see a sample extension that requests this information [here](https://github.com/connor4312/cdp-proxy-requestor/blob/main/extension.js). + +Note that the server will always be running in the workspace. If you have a UI extension, you may need to forward the port. We also recommend using `permessage-deflate` on the WebSocket for better performance over remote connections. + +### Protocol + +The protocol spoken over the WebSocket is, unsurprisingly, CDP. Over the websocket, the `sessionId` will never be used and will always be ignored. This is because a single js-debug debug session corresponds to exactly one CDP session. Other targets--like iframes, workers, and subprocesses--are represented as separate debug sessions which you can connect to separately. + +Additionally, by default, you will not receive any CDP events on the socket. This is because the underlying CDP connection is shared between js-debug and consumers of the mechanism, and we want to avoid doing extra work to send events you don't care about. To listen to events, you can use the JsDebug domain: + +#### JsDebug domain + +js-debug exposes a `JsDebug` CDP domain for meta-communication. For example, you would call the method `JsDebug.subscribe` to subscribe to evetns. + +- The TypeScript definition of the available methods can be found [here](https://github.com/microsoft/vscode-js-debug/blob/main/src/adapter/cdpProxy.ts#L22). +- The PDL definition can be found [here](https://github.com/microsoft/vscode-js-debug/blob/main/src/adapter/cdpProxy.pdl). + +These definitions will be published in an npm package soon. diff --git a/code/extensions/js-debug/LICENSE b/code/extensions/js-debug/LICENSE new file mode 100644 index 000000000000..21071075c245 --- /dev/null +++ b/code/extensions/js-debug/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + 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/code/extensions/js-debug/OPTIONS.md b/code/extensions/js-debug/OPTIONS.md new file mode 100644 index 000000000000..f59a48a3fda6 --- /dev/null +++ b/code/extensions/js-debug/OPTIONS.md @@ -0,0 +1,529 @@ +# Options + +### node: attach + +

address

TCP/IP address of process to be debugged. Default is 'localhost'.

+
Default value:
"localhost"

attachExistingChildren

Whether to attempt to attach to already-spawned child processes.

+
Default value:
true

autoAttachChildProcesses

Attach debugger to new child processes automatically.

+
Default value:
true

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

continueOnAttach

If true, we'll automatically resume programs launched and waiting on --inspect-brk

+
Default value:
false

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

cwd

Absolute path to the working directory of the program being debugged. If you've set localRoot then cwd will match that value otherwise it falls back to your workspaceFolder

+
Default value:
localRoot || ${workspaceFolder}

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

env

Environment variables passed to the program. The value null removes the variable from the environment.

+
Default value:
{}

envFile

Absolute path to a file containing environment variable definitions.

+
Default value:
null

localRoot

Path to the local directory containing the program.

+
Default value:
null

nodeVersionHint

Allows you to explicitly specify the Node version that's running, which can be used to disable or enable certain behaviors in cases where the automatic version detection does not work.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
false

port

Debug port to attach to. Default is 9229.

+
Default value:
9229

processId

ID of process to attach to.

+
Default value:
undefined

remoteHostHeader

Explicit Host header to use when connecting to the websocket of inspector. If unspecified, the host header will be set to 'localhost'. This is useful when the inspector is running behind a proxy that only accept particular Host header.

+
Default value:
undefined

remoteRoot

Absolute path to the remote directory containing the program.

+
Default value:
null

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
[
+  "**",
+  "!**/node_modules/**"
+]

restart

Try to reconnect to the program if we lose connection. If set to true, we'll try once a second, forever. You can customize the interval and maximum number of attempts by specifying the delay and maxAttempts in an object instead.

+
Default value:
false

runtimeSourcemapPausePatterns

A list of patterns at which to manually insert entrypoint breakpoints. This can be useful to give the debugger an opportunity to set breakpoints when using sourcemaps that don't exist or can't be detected before launch, such as with the Serverless framework.

+
Default value:
[]

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[
+  "/**"
+]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${workspaceFolder}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${workspaceFolder}/*",
+  "webpack://?:*/*": "${workspaceFolder}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${workspaceFolder}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false

websocketAddress

Exact websocket address to attach to. If unspecified, it will be discovered from the address and port.

+
Default value:
undefined
+ +### node: launch + +

args

Command line arguments passed to the program.

Can be an array of strings or a single string. When the program is launched in a terminal, setting this property to a single string will result in the arguments not being escaped for the shell.

+
Default value:
[]

attachSimplePort

If set, attaches to the process via the given port. This is generally no longer necessary for Node.js programs and loses the ability to debug child processes, but can be useful in more esoteric scenarios such as with Deno and Docker launches. If set to 0, a random port will be chosen and --inspect-brk added to the launch arguments automatically.

+
Default value:
null

autoAttachChildProcesses

Attach debugger to new child processes automatically.

+
Default value:
true

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

console

Where to launch the debug target.

+
Default value:
"internalConsole"

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

cwd

Absolute path to the working directory of the program being debugged. If you've set localRoot then cwd will match that value otherwise it falls back to your workspaceFolder

+
Default value:
"${workspaceFolder}"

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

env

Environment variables passed to the program. The value null removes the variable from the environment.

+
Default value:
{}

envFile

Absolute path to a file containing environment variable definitions.

+
Default value:
null

experimentalNetworking

Enable experimental inspection in Node.js. When set to auto this is enabled for versions of Node.js that support it. It can be set to on or off to enable or disable it explicitly.

+
Default value:
"auto"

killBehavior

Configures how debug processes are killed when stopping the session. Can be:

- forceful (default): forcefully tears down the process tree. Sends SIGKILL on posix, or taskkill.exe /F on Windows.
- polite: gracefully tears down the process tree. It's possible that misbehaving processes continue to run after shutdown in this way. Sends SIGTERM on posix, or taskkill.exe with no /F (force) flag on Windows.
- none: no termination will happen.

+
Default value:
"forceful"

localRoot

Path to the local directory containing the program.

+
Default value:
null

nodeVersionHint

Allows you to explicitly specify the Node version that's running, which can be used to disable or enable certain behaviors in cases where the automatic version detection does not work.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
false

profileStartup

If true, will start profiling as soon as the process launches

+
Default value:
false

program

Absolute path to the program. Generated value is guessed by looking at package.json and opened files. Edit this attribute.

+
Default value:
""

remoteRoot

Absolute path to the remote directory containing the program.

+
Default value:
null

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
[
+  "**",
+  "!**/node_modules/**"
+]

restart

Try to reconnect to the program if we lose connection. If set to true, we'll try once a second, forever. You can customize the interval and maximum number of attempts by specifying the delay and maxAttempts in an object instead.

+
Default value:
false

runtimeArgs

Optional arguments passed to the runtime executable.

+
Default value:
[]

runtimeExecutable

Runtime to use. Either an absolute path or the name of a runtime available on the PATH. If omitted node is assumed.

+
Default value:
"node"

runtimeSourcemapPausePatterns

A list of patterns at which to manually insert entrypoint breakpoints. This can be useful to give the debugger an opportunity to set breakpoints when using sourcemaps that don't exist or can't be detected before launch, such as with the Serverless framework.

+
Default value:
[]

runtimeVersion

Version of node runtime to use. Requires nvm.

+
Default value:
"default"

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[
+  "/**"
+]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${workspaceFolder}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${workspaceFolder}/*",
+  "webpack://?:*/*": "${workspaceFolder}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${workspaceFolder}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

stopOnEntry

Automatically stop program after launch.

+
Default value:
false

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false
+ +### node-terminal: launch + +

autoAttachChildProcesses

Attach debugger to new child processes automatically.

+
Default value:
true

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

command

Command to run in the launched terminal. If not provided, the terminal will open without launching a program.

+
Default value:
undefined

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

cwd

Absolute path to the working directory of the program being debugged. If you've set localRoot then cwd will match that value otherwise it falls back to your workspaceFolder

+
Default value:
localRoot || ${workspaceFolder}

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

env

Environment variables passed to the program. The value null removes the variable from the environment.

+
Default value:
{}

envFile

Absolute path to a file containing environment variable definitions.

+
Default value:
null

localRoot

Path to the local directory containing the program.

+
Default value:
null

nodeVersionHint

Allows you to explicitly specify the Node version that's running, which can be used to disable or enable certain behaviors in cases where the automatic version detection does not work.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
false

remoteRoot

Absolute path to the remote directory containing the program.

+
Default value:
null

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
[
+  "**",
+  "!**/node_modules/**"
+]

runtimeSourcemapPausePatterns

A list of patterns at which to manually insert entrypoint breakpoints. This can be useful to give the debugger an opportunity to set breakpoints when using sourcemaps that don't exist or can't be detected before launch, such as with the Serverless framework.

+
Default value:
[]

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
{
+  "onceBreakpointResolved": 16
+}

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[
+  "/**"
+]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${workspaceFolder}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${workspaceFolder}/*",
+  "webpack://?:*/*": "${workspaceFolder}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${workspaceFolder}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false
+ +### extensionHost: launch + +

args

Command line arguments passed to the program.

Can be an array of strings or a single string. When the program is launched in a terminal, setting this property to a single string will result in the arguments not being escaped for the shell.

+
Default value:
[
+  "--extensionDevelopmentPath=${workspaceFolder}"
+]

autoAttachChildProcesses

Attach debugger to new child processes automatically.

+
Default value:
false

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

cwd

Absolute path to the working directory of the program being debugged. If you've set localRoot then cwd will match that value otherwise it falls back to your workspaceFolder

+
Default value:
localRoot || ${workspaceFolder}

debugWebviews

Configures whether we should try to attach to webviews in the launched VS Code instance. This will only work in desktop VS Code.

+
Default value:
false

debugWebWorkerHost

Configures whether we should try to attach to the web worker extension host.

+
Default value:
false

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

env

Environment variables passed to the program. The value null removes the variable from the environment.

+
Default value:
{}

envFile

Absolute path to a file containing environment variable definitions.

+
Default value:
null

localRoot

Path to the local directory containing the program.

+
Default value:
null

nodeVersionHint

Allows you to explicitly specify the Node version that's running, which can be used to disable or enable certain behaviors in cases where the automatic version detection does not work.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/out/**/*.js"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
false

remoteRoot

Absolute path to the remote directory containing the program.

+
Default value:
null

rendererDebugOptions

Chrome launch options used when attaching to the renderer process, with debugWebviews or debugWebWorkerHost.

+
Default value:
{}

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
[
+  "${workspaceFolder}/**",
+  "!**/node_modules/**"
+]

runtimeExecutable

Absolute path to VS Code.

+
Default value:
"${execPath}"

runtimeSourcemapPausePatterns

A list of patterns at which to manually insert entrypoint breakpoints. This can be useful to give the debugger an opportunity to set breakpoints when using sourcemaps that don't exist or can't be detected before launch, such as with the Serverless framework.

+
Default value:
[]

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[
+  "/**"
+]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${workspaceFolder}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${workspaceFolder}/*",
+  "webpack://?:*/*": "${workspaceFolder}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${workspaceFolder}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

testConfiguration

Path to a test configuration file for the test CLI.

+
Default value:
undefined

testConfigurationLabel

A single configuration to run from the file. If not specified, you may be asked to pick.

+
Default value:
undefined

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false
+ +### chrome: launch + +

browserLaunchLocation

Forces the browser to be launched in one location. In a remote workspace (through ssh or WSL, for example) this can be used to open the browser on the remote machine rather than locally.

+
Default value:
"workspace"

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

cleanUp

What clean-up to do after the debugging session finishes. Close only the tab being debug, vs. close the whole browser.

+
Default value:
"wholeBrowser"

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

cwd

Optional working directory for the runtime executable.

+
Default value:
null

disableNetworkCache

Controls whether to skip the network cache for each request

+
Default value:
true

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

env

Optional dictionary of environment key/value pairs for the browser.

+
Default value:
{}

file

A local html file to open in the browser

+
Default value:
null

includeDefaultArgs

Whether default browser launch arguments (to disable features that may make debugging harder) will be included in the launch.

+
Default value:
true

includeLaunchArgs

Advanced: whether any default launch/debugging arguments are set on the browser. The debugger will assume the browser will use pipe debugging such as that which is provided with --remote-debugging-pipe.

+
Default value:
true

inspectUri

Format to use to rewrite the inspectUri: It's a template string that interpolates keys in {curlyBraces}. Available keys are:
- url.* is the parsed address of the running application. For instance, {url.port}, {url.hostname}
- port is the debug port that Chrome is listening on.
- browserInspectUri is the inspector URI on the launched browser
- browserInspectUriPath is the path part of the inspector URI on the launched browser (e.g.: "/devtools/browser/e9ec0098-306e-472a-8133-5e42488929c2").
- wsProtocol is the hinted websocket protocol. This is set to wss if the original URL is https, or ws otherwise.

+
Default value:
undefined

killBehavior

Configures how browser processes are killed when stopping the session with cleanUp: wholeBrowser. Can be:

- forceful (default): forcefully tears down the process tree. Sends SIGKILL on posix, or taskkill.exe /F on Windows.
- polite: gracefully tears down the process tree. It's possible that misbehaving processes continue to run after shutdown in this way. Sends SIGTERM on posix, or taskkill.exe with no /F (force) flag on Windows.
- none: no termination will happen.

+
Default value:
"forceful"

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pathMapping

A mapping of URLs/paths to local folders, to resolve scripts in the Browser to scripts on disk

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
true

perScriptSourcemaps

Whether scripts are loaded individually with unique sourcemaps containing the basename of the source file. This can be set to optimize sourcemap handling when dealing with lots of small scripts. If set to "auto", we'll detect known cases where this is appropriate.

+
Default value:
"auto"

port

Port for the browser to listen on. Defaults to "0", which will cause the browser to be debugged via pipes, which is generally more secure and should be chosen unless you need to attach to the browser from another tool.

+
Default value:
0

profileStartup

If true, will start profiling soon as the process launches

+
Default value:
false

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
null

runtimeArgs

Optional arguments passed to the runtime executable.

+
Default value:
null

runtimeExecutable

Either 'canary', 'stable', 'custom' or path to the browser executable. Custom means a custom wrapper, custom build or CHROME_PATH environment variable.

+
Default value:
"*"

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${webRoot}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${webRoot}/*",
+  "webpack://?:*/*": "${webRoot}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${webRoot}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false

url

Will search for a tab with this exact url and attach to it, if found

+
Default value:
null

urlFilter

Will search for a page with this url and attach to it, if found. Can have * wildcards.

+
Default value:
"*"

userDataDir

By default, the browser is launched with a separate user profile in a temp folder. Use this option to override it. Set to false to launch with your default user profile. A new browser can't be launched if an instance is already running from userDataDir.

+
Default value:
true

vueComponentPaths

A list of file glob patterns to find *.vue components. By default, searches the entire workspace. This needs to be specified due to extra lookups that Vue's sourcemaps require in Vue CLI 4. You can disable this special handling by setting this to an empty array.

+
Default value:
[
+  "${workspaceFolder}/**/*.vue",
+  "!**/node_modules/**"
+]

webRoot

This specifies the workspace absolute path to the webserver root. Used to resolve paths like /app.js to files on disk. Shorthand for a pathMapping for "/"

+
Default value:
"${workspaceFolder}"
+ +### chrome: attach + +

address

IP address or hostname the debugged browser is listening on.

+
Default value:
"localhost"

browserAttachLocation

Forces the browser to attach in one location. In a remote workspace (through ssh or WSL, for example) this can be used to attach to a browser on the remote machine rather than locally.

+
Default value:
"workspace"

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

disableNetworkCache

Controls whether to skip the network cache for each request

+
Default value:
true

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

inspectUri

Format to use to rewrite the inspectUri: It's a template string that interpolates keys in {curlyBraces}. Available keys are:
- url.* is the parsed address of the running application. For instance, {url.port}, {url.hostname}
- port is the debug port that Chrome is listening on.
- browserInspectUri is the inspector URI on the launched browser
- browserInspectUriPath is the path part of the inspector URI on the launched browser (e.g.: "/devtools/browser/e9ec0098-306e-472a-8133-5e42488929c2").
- wsProtocol is the hinted websocket protocol. This is set to wss if the original URL is https, or ws otherwise.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pathMapping

A mapping of URLs/paths to local folders, to resolve scripts in the Browser to scripts on disk

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
true

perScriptSourcemaps

Whether scripts are loaded individually with unique sourcemaps containing the basename of the source file. This can be set to optimize sourcemap handling when dealing with lots of small scripts. If set to "auto", we'll detect known cases where this is appropriate.

+
Default value:
"auto"

port

Port to use to remote debugging the browser, given as --remote-debugging-port when launching the browser.

+
Default value:
0

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
null

restart

Whether to reconnect if the browser connection is closed

+
Default value:
false

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${webRoot}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${webRoot}/*",
+  "webpack://?:*/*": "${webRoot}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${webRoot}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

targetSelection

Whether to attach to all targets that match the URL filter ("automatic") or ask to pick one ("pick").

+
Default value:
"automatic"

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false

url

Will search for a tab with this exact url and attach to it, if found

+
Default value:
null

urlFilter

Will search for a page with this url and attach to it, if found. Can have * wildcards.

+
Default value:
""

vueComponentPaths

A list of file glob patterns to find *.vue components. By default, searches the entire workspace. This needs to be specified due to extra lookups that Vue's sourcemaps require in Vue CLI 4. You can disable this special handling by setting this to an empty array.

+
Default value:
[
+  "${workspaceFolder}/**/*.vue",
+  "!**/node_modules/**"
+]

webRoot

This specifies the workspace absolute path to the webserver root. Used to resolve paths like /app.js to files on disk. Shorthand for a pathMapping for "/"

+
Default value:
"${workspaceFolder}"
+ +### msedge: launch + +

address

When debugging webviews, the IP address or hostname the webview is listening on. Will be automatically discovered if not set.

+
Default value:
"localhost"

browserLaunchLocation

Forces the browser to be launched in one location. In a remote workspace (through ssh or WSL, for example) this can be used to open the browser on the remote machine rather than locally.

+
Default value:
"workspace"

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

cleanUp

What clean-up to do after the debugging session finishes. Close only the tab being debug, vs. close the whole browser.

+
Default value:
"wholeBrowser"

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

cwd

Optional working directory for the runtime executable.

+
Default value:
null

disableNetworkCache

Controls whether to skip the network cache for each request

+
Default value:
true

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

env

Optional dictionary of environment key/value pairs for the browser.

+
Default value:
{}

file

A local html file to open in the browser

+
Default value:
null

includeDefaultArgs

Whether default browser launch arguments (to disable features that may make debugging harder) will be included in the launch.

+
Default value:
true

includeLaunchArgs

Advanced: whether any default launch/debugging arguments are set on the browser. The debugger will assume the browser will use pipe debugging such as that which is provided with --remote-debugging-pipe.

+
Default value:
true

inspectUri

Format to use to rewrite the inspectUri: It's a template string that interpolates keys in {curlyBraces}. Available keys are:
- url.* is the parsed address of the running application. For instance, {url.port}, {url.hostname}
- port is the debug port that Chrome is listening on.
- browserInspectUri is the inspector URI on the launched browser
- browserInspectUriPath is the path part of the inspector URI on the launched browser (e.g.: "/devtools/browser/e9ec0098-306e-472a-8133-5e42488929c2").
- wsProtocol is the hinted websocket protocol. This is set to wss if the original URL is https, or ws otherwise.

+
Default value:
undefined

killBehavior

Configures how browser processes are killed when stopping the session with cleanUp: wholeBrowser. Can be:

- forceful (default): forcefully tears down the process tree. Sends SIGKILL on posix, or taskkill.exe /F on Windows.
- polite: gracefully tears down the process tree. It's possible that misbehaving processes continue to run after shutdown in this way. Sends SIGTERM on posix, or taskkill.exe with no /F (force) flag on Windows.
- none: no termination will happen.

+
Default value:
"forceful"

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pathMapping

A mapping of URLs/paths to local folders, to resolve scripts in the Browser to scripts on disk

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
true

perScriptSourcemaps

Whether scripts are loaded individually with unique sourcemaps containing the basename of the source file. This can be set to optimize sourcemap handling when dealing with lots of small scripts. If set to "auto", we'll detect known cases where this is appropriate.

+
Default value:
"auto"

port

When debugging webviews, the port the webview debugger is listening on. Will be automatically discovered if not set.

+
Default value:
0

profileStartup

If true, will start profiling soon as the process launches

+
Default value:
false

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
null

runtimeArgs

Optional arguments passed to the runtime executable.

+
Default value:
null

runtimeExecutable

Either 'canary', 'stable', 'dev', 'custom' or path to the browser executable. Custom means a custom wrapper, custom build or EDGE_PATH environment variable.

+
Default value:
"*"

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${webRoot}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${webRoot}/*",
+  "webpack://?:*/*": "${webRoot}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${webRoot}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false

url

Will search for a tab with this exact url and attach to it, if found

+
Default value:
null

urlFilter

Will search for a page with this url and attach to it, if found. Can have * wildcards.

+
Default value:
"*"

userDataDir

By default, the browser is launched with a separate user profile in a temp folder. Use this option to override it. Set to false to launch with your default user profile. A new browser can't be launched if an instance is already running from userDataDir.

+
Default value:
true

useWebView

When 'true', the debugger will treat the runtime executable as a host application that contains a WebView allowing you to debug the WebView script content.

+
Default value:
false

vueComponentPaths

A list of file glob patterns to find *.vue components. By default, searches the entire workspace. This needs to be specified due to extra lookups that Vue's sourcemaps require in Vue CLI 4. You can disable this special handling by setting this to an empty array.

+
Default value:
[
+  "${workspaceFolder}/**/*.vue",
+  "!**/node_modules/**"
+]

webRoot

This specifies the workspace absolute path to the webserver root. Used to resolve paths like /app.js to files on disk. Shorthand for a pathMapping for "/"

+
Default value:
"${workspaceFolder}"
+ +### msedge: attach + +

address

IP address or hostname the debugged browser is listening on.

+
Default value:
"localhost"

browserAttachLocation

Forces the browser to attach in one location. In a remote workspace (through ssh or WSL, for example) this can be used to attach to a browser on the remote machine rather than locally.

+
Default value:
"workspace"

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

disableNetworkCache

Controls whether to skip the network cache for each request

+
Default value:
true

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

inspectUri

Format to use to rewrite the inspectUri: It's a template string that interpolates keys in {curlyBraces}. Available keys are:
- url.* is the parsed address of the running application. For instance, {url.port}, {url.hostname}
- port is the debug port that Chrome is listening on.
- browserInspectUri is the inspector URI on the launched browser
- browserInspectUriPath is the path part of the inspector URI on the launched browser (e.g.: "/devtools/browser/e9ec0098-306e-472a-8133-5e42488929c2").
- wsProtocol is the hinted websocket protocol. This is set to wss if the original URL is https, or ws otherwise.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pathMapping

A mapping of URLs/paths to local folders, to resolve scripts in the Browser to scripts on disk

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
true

perScriptSourcemaps

Whether scripts are loaded individually with unique sourcemaps containing the basename of the source file. This can be set to optimize sourcemap handling when dealing with lots of small scripts. If set to "auto", we'll detect known cases where this is appropriate.

+
Default value:
"auto"

port

Port to use to remote debugging the browser, given as --remote-debugging-port when launching the browser.

+
Default value:
0

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
null

restart

Whether to reconnect if the browser connection is closed

+
Default value:
false

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${webRoot}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${webRoot}/*",
+  "webpack://?:*/*": "${webRoot}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${webRoot}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

targetSelection

Whether to attach to all targets that match the URL filter ("automatic") or ask to pick one ("pick").

+
Default value:
"automatic"

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false

url

Will search for a tab with this exact url and attach to it, if found

+
Default value:
null

urlFilter

Will search for a page with this url and attach to it, if found. Can have * wildcards.

+
Default value:
""

useWebView

An object containing the pipeName of a debug pipe for a UWP hosted Webview2. This is the "MyTestSharedMemory" when creating the pipe "\.\pipe\LOCAL\MyTestSharedMemory"

+
Default value:
false

vueComponentPaths

A list of file glob patterns to find *.vue components. By default, searches the entire workspace. This needs to be specified due to extra lookups that Vue's sourcemaps require in Vue CLI 4. You can disable this special handling by setting this to an empty array.

+
Default value:
[
+  "${workspaceFolder}/**/*.vue",
+  "!**/node_modules/**"
+]

webRoot

This specifies the workspace absolute path to the webserver root. Used to resolve paths like /app.js to files on disk. Shorthand for a pathMapping for "/"

+
Default value:
"${workspaceFolder}"
+ +### editor-browser: launch + +

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

disableNetworkCache

Controls whether to skip the network cache for each request

+
Default value:
true

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

inspectUri

Format to use to rewrite the inspectUri: It's a template string that interpolates keys in {curlyBraces}. Available keys are:
- url.* is the parsed address of the running application. For instance, {url.port}, {url.hostname}
- port is the debug port that Chrome is listening on.
- browserInspectUri is the inspector URI on the launched browser
- browserInspectUriPath is the path part of the inspector URI on the launched browser (e.g.: "/devtools/browser/e9ec0098-306e-472a-8133-5e42488929c2").
- wsProtocol is the hinted websocket protocol. This is set to wss if the original URL is https, or ws otherwise.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pathMapping

A mapping of URLs/paths to local folders, to resolve scripts in the Browser to scripts on disk

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
true

perScriptSourcemaps

Whether scripts are loaded individually with unique sourcemaps containing the basename of the source file. This can be set to optimize sourcemap handling when dealing with lots of small scripts. If set to "auto", we'll detect known cases where this is appropriate.

+
Default value:
"auto"

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
null

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${webRoot}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${webRoot}/*",
+  "webpack://?:*/*": "${webRoot}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${webRoot}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false

url

Will search for a tab with this exact url and attach to it, if found

+
Default value:
"http://localhost:8080"

urlFilter

Will search for a page with this url and attach to it, if found. Can have * wildcards.

+
Default value:
""

vueComponentPaths

A list of file glob patterns to find *.vue components. By default, searches the entire workspace. This needs to be specified due to extra lookups that Vue's sourcemaps require in Vue CLI 4. You can disable this special handling by setting this to an empty array.

+
Default value:
[
+  "${workspaceFolder}/**/*.vue",
+  "!**/node_modules/**"
+]

webRoot

This specifies the workspace absolute path to the webserver root. Used to resolve paths like /app.js to files on disk. Shorthand for a pathMapping for "/"

+
Default value:
"${workspaceFolder}"
+ +### editor-browser: attach + +

cascadeTerminateToConfigurations

A list of debug sessions which, when this debug session is terminated, will also be stopped.

+
Default value:
[]

customDescriptionGenerator

Customize the textual description the debugger shows for objects (local variables, etc...). Samples:
1. this.toString() // will call toString to print all objects
2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue
3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue

+
Default value:
undefined

customPropertiesGenerator

Customize the properties shown for an object in the debugger (local variables, etc...). Samples:
1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects
2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)
3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties

Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181

+
Default value:
undefined

disableNetworkCache

Controls whether to skip the network cache for each request

+
Default value:
true

enableContentValidation

Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.

+
Default value:
true

enableDWARF

Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the ms-vscode.wasm-dwarf-debugging extension to function.

+
Default value:
true

inspectUri

Format to use to rewrite the inspectUri: It's a template string that interpolates keys in {curlyBraces}. Available keys are:
- url.* is the parsed address of the running application. For instance, {url.port}, {url.hostname}
- port is the debug port that Chrome is listening on.
- browserInspectUri is the inspector URI on the launched browser
- browserInspectUriPath is the path part of the inspector URI on the launched browser (e.g.: "/devtools/browser/e9ec0098-306e-472a-8133-5e42488929c2").
- wsProtocol is the hinted websocket protocol. This is set to wss if the original URL is https, or ws otherwise.

+
Default value:
undefined

outFiles

If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with ! the files are excluded. If not specified, the generated code is expected in the same directory as its source.

+
Default value:
[
+  "${workspaceFolder}/**/*.(m|c|)js",
+  "!**/node_modules/**"
+]

outputCapture

From where to capture output messages: the default debug API if set to console, or stdout/stderr streams if set to std.

+
Default value:
"console"

pathMapping

A mapping of URLs/paths to local folders, to resolve scripts in the Browser to scripts on disk

+
Default value:
{}

pauseForSourceMap

Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as rootPath is not disabled.

+
Default value:
true

perScriptSourcemaps

Whether scripts are loaded individually with unique sourcemaps containing the basename of the source file. This can be set to optimize sourcemap handling when dealing with lots of small scripts. If set to "auto", we'll detect known cases where this is appropriate.

+
Default value:
"auto"

resolveSourceMapLocations

A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with "!" to exclude them. May be set to an empty array or null to avoid restriction.

+
Default value:
null

showAsyncStacks

Show the async calls that led to the current call stack.

+
Default value:
true

skipFiles

An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, ["**/node_modules/**", "!**/node_modules/my-module/**"]

+
Default value:
[]

smartStep

Automatically step through generated code that cannot be mapped back to the original source.

+
Default value:
true

sourceMapPathOverrides

A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.

+
Default value:
{
+  "webpack:///./~/*": "${webRoot}/node_modules/*",
+  "webpack:////*": "/*",
+  "webpack://@?:*/?:*/*": "${webRoot}/*",
+  "webpack://?:*/*": "${webRoot}/*",
+  "webpack:///([a-z]):/(.+)": "$1:/$2",
+  "meteor://💻app/*": "${webRoot}/*",
+  "turbopack://[project]/*": "${workspaceFolder}/*",
+  "turbopack:///[project]/*": "${workspaceFolder}/*"
+}

sourceMapRenames

Whether to use the "names" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.

+
Default value:
true

sourceMaps

Use JavaScript source maps (if they exist).

+
Default value:
true

timeout

Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.

+
Default value:
10000

timeouts

Timeouts for several debugger operations.

+
Default value:
{}

trace

Configures what diagnostic output is produced.

+
Default value:
false

url

Will search for a tab with this exact url and attach to it, if found

+
Default value:
null

urlFilter

Will search for a page with this url and attach to it, if found. Can have * wildcards.

+
Default value:
""

vueComponentPaths

A list of file glob patterns to find *.vue components. By default, searches the entire workspace. This needs to be specified due to extra lookups that Vue's sourcemaps require in Vue CLI 4. You can disable this special handling by setting this to an empty array.

+
Default value:
[
+  "${workspaceFolder}/**/*.vue",
+  "!**/node_modules/**"
+]

webRoot

This specifies the workspace absolute path to the webserver root. Used to resolve paths like /app.js to files on disk. Shorthand for a pathMapping for "/"

+
Default value:
"${workspaceFolder}"
diff --git a/code/extensions/js-debug/README.md b/code/extensions/js-debug/README.md new file mode 100644 index 000000000000..ba017c389ba0 --- /dev/null +++ b/code/extensions/js-debug/README.md @@ -0,0 +1,134 @@ +

+ vscode-js-debug +

+ +This is a [DAP](https://microsoft.github.io/debug-adapter-protocol/)-based JavaScript debugger. It debugs Node.js, Chrome, Edge, WebView2, VS Code extensions, Blazor, React Native, and more. It is the default JavaScript debugger in Visual Studio Code and Visual Studio, and the standalone debug server can also be used in other tools such as [Neovim](https://github.com/mfussenegger/nvim-dap). + +## Usage + +If you're using Visual Studio or Visual Studio Code, `js-debug` is already installed. Otherwise, please consult your editor's documentation for possible installation instructions. Builds of the VS Code extension and standalone DAP server are available on the [releases](https://github.com/microsoft/vscode-js-debug/releases) page. + +See [OPTIONS.md](./OPTIONS.md) for a list of options you can use in your launch configurations. + +- For usage in VS Code, please check out our guides for [Node.js debugging](https://code.visualstudio.com/docs/nodejs/nodejs-debugging), [Browser debugging](https://code.visualstudio.com/docs/nodejs/browser-debugging). +- For debugging React Native, install and read through the [React Native](https://marketplace.visualstudio.com/items?itemName=msjsdiag.vscode-react-native) extension which builds upon `js-debug`. +- For debugging Blazor, check out [its documentation here](https://learn.microsoft.com/en-us/aspnet/core/blazor/debug?view=aspnetcore-8.0&tabs=visual-studio-code). +- For debugging WebView2 apps, check out [documentation here](https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/debug-visual-studio-code). + +### Nightly Extension + +The shipped version of VS Code includes the js-debug version at the time of its release, however you may want to install our nightly build to get the latest fixes and features. The nightly build runs at 5PM PST on each day that there are changes ([see pipeline](https://dev.azure.com/vscode/VS%20Code%20debug%20adapters/_build?definitionId=28)). To get the build: + +1. Open the extensions view (ctrl+shift+x) and search for `@builtin @id:ms-vscode.js-debug` +2. Right click on the `JavaScript Debugger` extension and `Disable` it. +3. Search for `@id:ms-vscode.js-debug-nightly` in the extensions view. +4. Install that extension. + +## Notable Features + +In `js-debug` we aim to provide rich debugging for modern applications, with no or minimal configuration required. Here are a few distinguishing features of `js-debug` beyond basic debugging capabilities. Please refer to the VS Code documentation for a complete overview of capabilities. + +### Debug child processes, web workers, service workers, and worker threads + +In Node.js, child processes and worker threads will automatically be debugged. In browsers, service workers, webworkers, and iframes will be debugged as well. While debugging workers, you can also step through `postMessage()` calls. + +
+ Preview + +
+ +### Debug WebAssembly with DWARF symbols + +The debugger automatically reads DWARF symbols from WebAssembly binaries, and debugs them. The usual debugging features are available, including limited evaluation support via `lldb-eval`. + +
+ Preview + +
+ +### Debug Node.js processes in the terminal + +You can debug any Node.js process you run in the terminal with Auto Attach. If auto attach isn't on, you can run the command `Debug: Toggle Auto Attach` to turn it on. Next time you run a command like `npm start`, we'll debug it. + +
+ Preview + +
+ +Once enabled, you can toggle Auto Attach by clicking the `Auto Attach: On/Off` button in the status bar on the bottom of your screen. You can also create a one-off terminal for debugging via the `Debug: Create JavaScript Debug Terminal` command. + +### Profiling Support + +You can capture and view performance profiles natively in VS Code, by clicking on the ⚪ button in the Call Stack view, or through the `Debug: Take Performance Profile` command. The profile information collected through VS Code is sourcemap-aware. + +We support taking and visualizating CPU profiles, heap profiles, and heap snapshots. + +
+ Preview + +
+ +### Instrumentation breakpoints + +When debugging web apps, you can configure instrumentation breakpoints from VS Code in the "Event Listener Breakpoints" view. + +
+ Preview + + +
+ +### Return value interception + +On a function's return statement, you can use, inspect, and modify the `$returnValue`. + +
+ Preview + +
+ +Note that you can use and modify properties on the `$returnValue`, but not assign it to--it is effectively a `const` variable. + +### Pretty-print minified sources + +The debugger can pretty print files, especially useful when dealing with minified sources. You can trigger pretty printing by clicking on the braces `{}` icon in editor actions, or via the `Debug: Pretty print for debugging` command. + +
+ Preview + +
+ +### Experimental Network View + +The debugger allows viewing network traffic of browser targets and Node.js >22.6.0. This requires enabling the `debug.javascript.enableNetworkView` setting. + +
+ Preview + +
+ +### Advanced Rename Support + +When using a tool that emits renames in its sourcemap, the debugger maps renamed variables in all displayed views, and also rewrites evaluation requests to use the renamed identifiers, allowing near-source-level debugging of minified code. + +### Conditional Exception Breakpoints + +As in most debuggers, you can pause on caught exceptions, but you can also filter the exceptions you want to pause on by checking against the `error` object. In VS Code, you can do this by clicking the pencil icon in the Breakpoints view. + +
+ Preview + +
+ +### Excluded Callers + +If you have a breakpoint you want to pause on, but not when called from certain frames, you can right click on call frames in the stack trace view to "exclude caller" which prevents pausing on that breakpoint when the requested caller is in the stack trace. + +
+ Preview + +
+ +### Step-in Targets + +When paused on a location with multiple calls or expressions, the debugger supports the **Debug: Step Into Target** action that allows you to request a specific expression you wish to step into. diff --git a/code/extensions/js-debug/README.nightly.md b/code/extensions/js-debug/README.nightly.md new file mode 100644 index 000000000000..935ec9f31fe1 --- /dev/null +++ b/code/extensions/js-debug/README.nightly.md @@ -0,0 +1 @@ +> **This is a nightly version of this extension for early feedback and testing. This extension works best with [VS Code Insiders](https://code.visualstudio.com/insiders)** diff --git a/code/extensions/js-debug/SECURITY.md b/code/extensions/js-debug/SECURITY.md new file mode 100644 index 000000000000..1488eb5dc991 --- /dev/null +++ b/code/extensions/js-debug/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + +- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) +- Full paths of source file(s) related to the manifestation of the issue +- The location of the affected source code (tag/branch/commit or direct URL) +- Any special configuration required to reproduce the issue +- Step-by-step instructions to reproduce the issue +- Proof-of-concept or exploit code (if possible) +- Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/code/extensions/js-debug/dprint.json b/code/extensions/js-debug/dprint.json new file mode 100644 index 000000000000..68f66b857d75 --- /dev/null +++ b/code/extensions/js-debug/dprint.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://dprint.dev/schemas/v0.json", + "lineWidth": 100, + "indentWidth": 2, + "newLineKind": "lf", + "typescript": { + "useTabs": false, + "quoteStyle": "preferSingle", + "trailingCommas": "onlyMultiLine", + "arrowFunction.useParentheses": "preferNone" + }, + "excludes": [ + "**/node_modules", + "**/testWorkspace", + "**/*-lock.json", + "**/*.d.ts", + "!src/{dap,cdp}/*.d.ts" + ], + "plugins": [ + "https://plugins.dprint.dev/typescript-0.91.6.wasm", + "https://plugins.dprint.dev/json-0.19.3.wasm", + "https://plugins.dprint.dev/markdown-0.17.8.wasm" + ] +} diff --git a/code/extensions/js-debug/eslint.config.js b/code/extensions/js-debug/eslint.config.js new file mode 100644 index 000000000000..2bb18ff05712 --- /dev/null +++ b/code/extensions/js-debug/eslint.config.js @@ -0,0 +1,63 @@ +const { FlatCompat } = require('@eslint/eslintrc'); +const tsParser = require('@typescript-eslint/parser'); +const tsPlugin = require('@typescript-eslint/eslint-plugin'); +const reactPlugin = require('eslint-plugin-react'); +const headersPlugin = require('eslint-plugin-headers'); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +module.exports = [ + { + ignores: ['**/*.d.ts', 'src/test/**/*.ts', 'demos/**/*', '**/*.js', 'testWorkspace/**'], + }, + { + linterOptions: { + reportUnusedDisableDirectives: 'off', + }, + }, + ...compat.extends('plugin:react/recommended', 'plugin:@typescript-eslint/recommended'), + { + files: ['src/**/*.ts'], + languageOptions: { + parser: tsParser, + ecmaVersion: 2018, + sourceType: 'module', + }, + plugins: { + '@typescript-eslint': tsPlugin, + react: reactPlugin, + headers: headersPlugin, + }, + settings: { + react: { + pragma: 'h', + version: '16.3', + }, + }, + rules: { + // Temporary until CDP is moved out, which is where most violations are: + '@typescript-eslint/ban-types': 'off', + + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-require-imports': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', + '@typescript-eslint/no-unsafe-function-type': 'off', + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + 'prefer-const': ['error', { destructuring: 'all' }], + '@typescript-eslint/explicit-module-boundary-types': 'off', + // Current repository headers use a legacy style that would require mass edits to older files. + 'headers/header-format': 'off', + 'react/no-unescaped-entities': 'off', + 'react/prop-types': 'off', + '@typescript-eslint/no-unused-vars': 'off', + }, + }, +]; diff --git a/code/extensions/js-debug/gulpfile.js b/code/extensions/js-debug/gulpfile.js new file mode 100644 index 000000000000..fdbc4e51ad25 --- /dev/null +++ b/code/extensions/js-debug/gulpfile.js @@ -0,0 +1,491 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +const gulp = require('gulp'); +const glob = require('glob'); +const path = require('path'); +const rename = require('gulp-rename'); +const merge = require('merge2'); +const vsce = require('@vscode/vsce'); +const execSync = require('child_process').execSync; +const fs = require('fs'); +const cp = require('child_process'); +const util = require('util'); +const esbuild = require('esbuild'); +const esbuildPlugins = require('./src/build/esbuildPlugins'); +const got = require('got').default; +const { HttpsProxyAgent } = require('https-proxy-agent'); +const jszip = require('jszip'); +const stream = require('stream'); + +const pipelineAsync = util.promisify(stream.pipeline); + +const dirname = 'js-debug'; +const sources = ['src/**/*.{ts,tsx}']; +const externalModules = ['@vscode/dwarf-debugging']; +const allPackages = []; + +const srcDir = 'src'; +const buildDir = 'dist'; +const buildSrcDir = `${buildDir}/src`; +const nodeTargetsDir = `targets/node`; + +const isWatch = process.argv.includes('watch') || process.argv.includes('--watch'); +const isDebug = process.argv.includes('--debug'); + +/** + * Whether we're running a nightly build. + */ +const isNightly = process.argv.includes('--nightly') || isWatch; + +/** + * Extension ID to build. Appended with '-nightly' as necessary. + */ +const extensionName = isNightly ? 'js-debug-nightly' : 'js-debug'; + +async function runBuildScript(name) { + return new Promise((resolve, reject) => + cp.execFile( + process.execPath, + [path.join(__dirname, buildDir, 'src', 'build', name)], + (err, stdout, stderr) => { + process.stderr.write(stderr); + if (err) { + return reject(err); + } + + const outstr = stdout.toString('utf-8'); + try { + resolve(JSON.parse(outstr)); + } catch { + resolve(outstr); + } + }, + ) + ); +} + +const writeFile = util.promisify(fs.writeFile); +const readFile = util.promisify(fs.readFile); + +async function readJson(file) { + const contents = await readFile(path.join(__dirname, file), 'utf-8'); + return JSON.parse(contents); +} + +const del = async patterns => { + const files = glob.sync(patterns, { cwd: __dirname }); + await Promise.all( + files.map(f => fs.promises.rm(path.join(__dirname, f), { force: true, recursive: true })), + ); +}; + +gulp.task('clean-assertions', () => del(['src/test/**/*.txt.actual'])); + +gulp.task('clean', () => del(['dist/**', 'src/*/package.nls.*.json', 'packages/**', '*.vsix'])); + +async function fixNightlyReadme() { + const readmePath = `${buildDir}/README.md`; + const readmeText = await readFile(readmePath); + const readmeNightlyText = await readFile(`README.nightly.md`); + + await writeFile(readmePath, readmeNightlyText + '\n' + readmeText); +} + +const getVersionNumber = () => { + if (process.env.JS_DEBUG_VERSION) { + return process.env.JS_DEBUG_VERSION; + } + + const date = new Date(new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })); + const monthMinutes = (date.getDate() - 1) * 24 * 60 + date.getHours() * 60 + date.getMinutes(); + + return [ + // YY + date.getFullYear(), + // MM, + date.getMonth() + 1, + // DDHH + `${date.getDate()}${String(date.getHours()).padStart(2, '0')}`, + ].join('.'); +}; + +const cachedBuilds = new Map(); +const incrementalEsbuild = async (/** @type {esbuild.BuildOptions} */ options) => { + const key = JSON.stringify(options); + if (cachedBuilds.has(key)) { + return cachedBuilds.get(key).rebuild(); + } + + if (!isWatch) { + const r = await esbuild.build(options); + if (r.metafile) { + console.log(await esbuild.analyzeMetafile(r.metafile)); + } + return; + } + + const ctx = await esbuild.context(options); + cachedBuilds.set(key, ctx); + + await ctx.rebuild(); +}; + +gulp.task('compile:build-scripts', async () => + incrementalEsbuild({ + entryPoints: fs + .readdirSync('src/build') + .filter(f => f.endsWith('.ts')) + .map(f => `src/build/${f}`), + outdir: `${buildDir}/src/build`, + define: await getConstantDefines(), + bundle: true, + platform: 'node', + })); + +gulp.task('compile:dynamic', async () => { + const [contributions] = await Promise.all([ + runBuildScript('generate-contributions'), + runBuildScript('documentReadme'), + ]); + + let packageJson = await readJson('package.json'); + packageJson.name = extensionName; + if (isNightly) { + packageJson.displayName += ' (Nightly)'; + packageJson.version = getVersionNumber(); + packageJson.preview = true; + await fixNightlyReadme(); + } + + packageJson = Object.assign(packageJson, contributions); + + await writeFile(`${buildDir}/package.json`, JSON.stringify(packageJson)); +}); + +gulp.task('compile:static', () => + merge( + gulp.src( + [ + 'LICENSE', + 'resources/**/*', + 'README.md', + 'package.nls.json', + 'src/**/*.sh', + 'src/ui/basic-wat.tmLanguage.json', + 'src/ui/basic-wat.configuration.json', + '.vscodeignore', + ], + { + base: '.', + }, + ), + gulp.src(['node_modules/@c4312/chromehash/pkg/*.wasm']).pipe(rename({ dirname: 'src' })), + ).pipe(gulp.dest(buildDir))); + +const resolveDefaultExts = ['.tsx', '.ts', '.jsx', '.js', '.css', '.json']; + +async function getConstantDefines() { + const packageJson = await readJson('package.json'); + return { + EXTENSION_NAME: JSON.stringify(extensionName), + EXTENSION_VERSION: JSON.stringify(isNightly ? getVersionNumber() : packageJson.version), + EXTENSION_PUBLISHER: JSON.stringify(packageJson.publisher), + }; +} + +function compileVendorLibrary(name) { + return { + name, + entryPoints: [require.resolve(name)], + outdir: `${buildSrcDir}/vendor`, + entryNames: `${name}`, + }; +} + +async function compileTs({ + packages = [], + sourcemap = false, + compileInPlace = false, + minify = isWatch ? false : true, + watch = false, +} = options) { + const vendorPrefix = 'vendor'; + + // don't watch these, they won't really change: + const vendors = new Map( + await Promise.all( + [ + { + ...compileVendorLibrary('acorn-loose'), + plugins: [esbuildPlugins.hackyVendorBundle(new Map([['acorn', './acorn']]))], + }, + compileVendorLibrary('acorn'), + ].map(async ({ name, ...opts }) => { + await esbuild.build({ + ...opts, + sourcemap, + bundle: true, + platform: 'node', + format: 'cjs', + target: 'node20', + minify, + }); + + return [name, `./${vendorPrefix}/${name}.js`]; + }), + ), + ); + + // add the entrypoints common to both vscode and vs here + packages = [ + ...packages, + { entry: `${srcDir}/common/hash/hash.ts`, library: false }, + { entry: `${srcDir}/common/sourceMaps/renameWorker.ts`, library: false }, + { entry: `${srcDir}/targets/node/bootloader.ts`, library: false, target: 'node10' }, + { entry: `${srcDir}/targets/node/watchdog.ts`, library: false, target: 'node10' }, + { + entry: `${srcDir}/diagnosticTool/diagnosticTool.tsx`, + library: false, + target: 'chrome102', + platform: 'browser', + }, + ]; + + const define = await getConstantDefines(); + + let todo = []; + for ( + const { + entry, + platform = 'node', + library, + isInVsCode, + nodePackages, + target = 'node20', + } of packages + ) { + todo.push( + incrementalEsbuild({ + entryPoints: [entry], + platform, + bundle: true, + outdir: buildSrcDir, + resolveExtensions: isInVsCode + ? ['.extensionOnly.ts', ...resolveDefaultExts] + : resolveDefaultExts, + external: isInVsCode ? ['vscode', ...externalModules] : externalModules, + sourcemap: !!sourcemap, + sourcesContent: false, + packages: nodePackages, + minify, + define, + target, + alias: platform === 'node' ? {} : { path: 'path-browserify' }, + plugins: [ + esbuildPlugins.nativeNodeModulesPlugin(), + esbuildPlugins.importGlobLazy(), + esbuildPlugins.dirname(/src.test./), + esbuildPlugins.hackyVendorBundle(vendors), + ], + format: library ? 'cjs' : 'iife', + }), + ); + } + + await Promise.all(todo); + + await fs.promises.appendFile( + path.resolve(buildSrcDir, 'bootloader.js'), + '\n//# sourceURL=bootloader.bundle.cdp', + ); +} + +/** Run webpack to bundle the extension output files */ +gulp.task('compile:extension', async () => { + const packages = [ + { entry: `${srcDir}/extension.ts`, library: true, isInVsCode: true }, + { + entry: `${srcDir}/test/testRunner.ts`, + library: true, + isInVsCode: true, + nodePackages: 'external', + }, + ]; + return compileTs({ packages, sourcemap: true }); +}); + +gulp.task( + 'compile', + gulp.series('compile:static', 'compile:build-scripts', 'compile:dynamic', 'compile:extension'), +); + +/** Run webpack to bundle into the flat session launcher (for VS or standalone debug server) */ +gulp.task('flatSessionBundle:webpack-bundle', async () => { + const packages = [{ entry: `${srcDir}/flatSessionLauncher.ts`, library: true }]; + return compileTs({ packages, sourcemap: isWatch }); +}); + +/** Run webpack to bundle into the standard DAP debug server */ +gulp.task('dapDebugServer:webpack-bundle', async () => { + const packages = [{ entry: `${srcDir}/dapDebugServer.ts`, library: false }]; + return compileTs({ packages, sourcemap: isWatch }); +}); + +/** Run webpack to bundle into the VS debug server */ +gulp.task('vsDebugServerBundle:webpack-bundle', async () => { + const packages = [{ entry: `${srcDir}/vsDebugServer.ts`, library: true }]; + return compileTs({ packages, sourcemap: isDebug, minify: !isDebug }); +}); + +const vsceUrls = { + baseContentUrl: 'https://github.com/microsoft/vscode-js-debug/blob/main', + baseImagesUrl: 'https://github.com/microsoft/vscode-js-debug/raw/main', +}; + +/** Create a VSIX package using the vsce command line tool */ +gulp.task('package:createVSIX', () => + vsce.createVSIX({ + ...vsceUrls, + cwd: buildDir, + dependencies: false, + packagePath: path.join(buildDir, `${extensionName}.vsix`), + })); + +gulp.task('l10n:bundle-download', async () => { + const opts = {}; + const proxy = process.env.https_proxy || process.env.HTTPS_PROXY || null; + if (proxy) { + opts.agent = { + https: new HttpsProxyAgent(proxy), + }; + } + + const res = await got('https://github.com/microsoft/vscode-loc/archive/main.zip', opts).buffer(); + const content = await jszip.loadAsync(res); + + for (const fileName of Object.keys(content.files)) { + const match = /vscode-language-pack-(.*?)\/.+ms-vscode\.js-debug.*?\.i18n\.json$/.exec( + fileName, + ); + if (match) { + const locale = match[1]; + const file = content.files[fileName]; + const extractPath = path.join(buildDir, `nls.bundle.${locale}.json`); + await pipelineAsync(file.nodeStream(), fs.createWriteStream(extractPath)); + } + } +}); + +/** Clean, compile, bundle, and create vsix for the extension */ +gulp.task( + 'package:prepare', + gulp.series( + 'clean', + 'compile:static', + 'compile:build-scripts', + 'compile:dynamic', + 'compile:extension', + 'package:createVSIX', + ), +); + +/** Prepares the package and then hoists it to the root directory. Destructive. */ +gulp.task( + 'package:hoist', + gulp.series('package:prepare', async () => { + const srcFiles = await fs.promises.readdir(buildDir); + const ignoredFiles = new Set(await fs.promises.readdir(__dirname)); + + ignoredFiles.delete('l10n-extract'); // special case: made in the pipeline + + for (const file of srcFiles) { + ignoredFiles.delete(file); + await fs.promises.rm(path.join(__dirname, file), { force: true, recursive: true }); + await fs.promises.rename(path.join(buildDir, file), path.join(__dirname, file)); + } + await fs.promises.appendFile( + path.join(__dirname, '.vscodeignore'), + [...ignoredFiles].join('\n'), + ); + }), +); + +gulp.task('package', gulp.series('package:prepare', 'package:createVSIX')); + +gulp.task('flatSessionBundle', gulp.series('clean', 'compile', 'flatSessionBundle:webpack-bundle')); + +gulp.task( + 'dapDebugServer', + gulp.series('clean', 'compile:static', 'dapDebugServer:webpack-bundle'), +); + +gulp.task( + 'vsDebugServerBundle', + gulp.series('clean', 'compile', 'vsDebugServerBundle:webpack-bundle', 'l10n:bundle-download'), +); + +/** Publishes the build extension to the marketplace */ +gulp.task('publish:vsce', () => + vsce.publish({ + ...vsceUrls, + noVerify: true, // for proposed API usage + pat: process.env.MARKETPLACE_TOKEN, + dependencies: false, + cwd: buildDir, + })); + +gulp.task('publish', gulp.series('package', 'publish:vsce')); +gulp.task('default', gulp.series('compile')); + +gulp.task( + 'watch', + gulp.series('clean', 'compile', done => { + gulp.watch([...sources, '*.json'], gulp.series('compile')); + done(); + }), +); + +const runFormatting = (onlyStaged, fix, callback) => { + const child = cp.fork('./node_modules/dprint/bin.js', [fix ? 'fmt' : 'check'], { + stdio: 'inherit', + }); + + child.on('exit', code => (code ? callback(`Formatter exited with code ${code}`) : callback())); +}; + +const runEslint = (fix, callback) => { + const child = cp.fork( + './node_modules/eslint/bin/eslint.js', + ['--color', 'src/**/*.ts', fix ? '--fix' : ['--max-warnings=0']], + { stdio: 'inherit' }, + ); + + child.on('exit', code => (code ? callback(`Eslint exited with code ${code}`) : callback())); +}; + +gulp.task('format:code', callback => runFormatting(false, true, callback)); +gulp.task('format:eslint', callback => runEslint(true, callback)); +gulp.task('format', gulp.series('format:code', 'format:eslint')); + +gulp.task('lint:code', callback => runFormatting(false, false, callback)); +gulp.task('lint:eslint', callback => runEslint(false, callback)); +gulp.task('lint', gulp.parallel('lint:code', 'lint:eslint')); + +/** + * Run a command in the terminal using exec, and wrap it in a promise + * @param {string} cmd The command line command + args to execute + * @param {ExecOptions} options, see here for options: https://nodejs.org/docs/latest-v10.x/api/child_process.html#child_process_child_process_exec_command_options_callback + */ +function runCommand(cmd, options) { + return new Promise((resolve, reject) => { + let execError = undefined; + try { + execSync(cmd, { stdio: 'inherit', ...options }); + } catch (err) { + reject(err); + } + resolve(); + }); +} diff --git a/code/extensions/js-debug/package-lock.json b/code/extensions/js-debug/package-lock.json new file mode 100644 index 000000000000..e4db6a8fc9a1 --- /dev/null +++ b/code/extensions/js-debug/package-lock.json @@ -0,0 +1,29013 @@ +{ + "name": "js-debug", + "version": "1.117.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "js-debug", + "version": "1.117.0", + "license": "MIT", + "dependencies": { + "@c4312/chromehash": "^0.3.1", + "@jridgewell/gen-mapping": "^0.3.13", + "@jridgewell/trace-mapping": "^0.3.31", + "@vscode/js-debug-browsers": "^1.1.2", + "@vscode/l10n": "^0.0.18", + "@vscode/win32-app-container-tokens": "^0.2.0", + "acorn": "^8.11.3", + "acorn-loose": "^8.4.0", + "astring": "^1.8.6", + "color": "^4.2.3", + "data-uri-to-buffer": "^6.0.1", + "default-browser": "^5.2.1", + "dotenv": "^16.4.1", + "eslint-visitor-keys": "^3.4.3", + "execa": "^5.1.1", + "glob-stream": "^8.0.0", + "got": "^11.8.6", + "inversify": "^6.0.2", + "js-xxhash": "^3.0.1", + "jsonc-parser": "^3.3.1", + "linkifyjs": "^4.3.2", + "micromatch": "^4.0.5", + "npm-run-all2": "^7.0.1", + "path-browserify": "^1.0.1", + "picomatch": "connor4312/picomatch#2fbe90b12eafa7dde816ff8c16be9e77271b0e0b", + "preact": "^10.19.3", + "reflect-metadata": "^0.2.1", + "signale": "^1.4.0", + "source-map-support": "^0.5.21", + "to-absolute-glob": "^3.0.0", + "vscode-tas-client": "^0.1.84", + "ws": "^8.17.1" + }, + "devDependencies": { + "@c4312/matcha": "^1.3.1", + "@pptr/testrunner": "^0.8.0", + "@types/chai": "^4.3.11", + "@types/chai-as-promised": "^7.1.8", + "@types/chai-string": "^1.4.5", + "@types/chai-subset": "^1.3.5", + "@types/color": "^3.0.6", + "@types/debug": "^4.1.12", + "@types/diff": "^5.0.9", + "@types/estree": "1.0.5", + "@types/express": "^4.17.21", + "@types/glob-stream": "^8.0.2", + "@types/gulp": "^4.0.17", + "@types/js-beautify": "^1.14.3", + "@types/json-schema": "^7.0.15", + "@types/linkifyjs": "^2.1.7", + "@types/long": "^4.0.2", + "@types/marked": "^5.0.2", + "@types/micromatch": "^4.0.6", + "@types/minimist": "^1.2.5", + "@types/mkdirp": "^1.0.2", + "@types/mocha": "^10.0.6", + "@types/node": "^20.11.11", + "@types/signale": "^1.4.7", + "@types/sinon": "^17.0.3", + "@types/stream-buffers": "^3.0.7", + "@types/tmp": "^0.2.6", + "@types/to-absolute-glob": "^2.0.3", + "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", + "@vscode/dwarf-debugging": "^0.0.2", + "@vscode/test-electron": "^2.4.1", + "@vscode/vsce": "^3.7.1", + "chai": "^4.3.6", + "chai-as-promised": "^7.1.1", + "chai-string": "^1.5.0", + "chai-subset": "^1.6.0", + "diff": "^5.1.0", + "dprint": "^0.47.2", + "esbuild": "^0.25.0", + "eslint": "^8.56.0", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-react": "^7.33.2", + "express": "^4.22.1", + "glob": "^11.1.0", + "gulp": "^4.0.2", + "gulp-cli": "^2.3.0", + "gulp-rename": "^2.0.0", + "https-proxy-agent": "^7.0.4", + "husky": "^9.0.7", + "jszip": "^3.10.1", + "marked": "^11.2.0", + "merge2": "^1.4.1", + "minimist": "^1.2.8", + "mocha": "^10.8.2", + "mocha-junit-reporter": "^2.2.1", + "mocha-multi-reporters": "^1.5.1", + "nyc": "^15.1.0", + "sinon": "^17.0.1", + "stream-buffers": "^3.0.2", + "ts-node": "^10.9.2", + "tsx": "^4.20.3", + "typescript": "^5.5.2" + }, + "engines": { + "node": ">=10", + "vscode": "^1.80.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.23.0.tgz", + "integrity": "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==", + "dev": true, + "license": "MIT", + "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.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "dev": true, + "license": "MIT", + "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": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.6.3.tgz", + "integrity": "sha512-sTjMtUm+bJpENU/1WlRzHEsgEHppZDZ1EtNyaOODg/sQBtMxxJzGB+MOCM+T2Q5Qe1fKBrdxUmjyRxm0r7Ez9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.4.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.4.1.tgz", + "integrity": "sha512-Bl8f+w37xkXsYh7QRkAKCFGYtWMYuOVO7Lv+BxILrvGz3HbIEF22Pt0ugyj0QPOl6NLrHcnNUQ9yeew98P/5iw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.1.2.tgz", + "integrity": "sha512-DoeSJ9U5KPAIZoHsPywvfEj2MhBniQe0+FSpjLUTdWoIkI999GB5USkW6nNEHnIaLVxROHXvprWA1KzdS1VQ4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.4.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", + "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz", + "integrity": "sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.5", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helpers": "^7.14.6", + "@babel/parser": "^7.14.6", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", + "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", + "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", + "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-simple-access": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", + "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@c4312/chromehash": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@c4312/chromehash/-/chromehash-0.3.1.tgz", + "integrity": "sha512-WmQTccHowTwvMsSHOuUlFabWz5aK8ZZyRKMh2/E7jjZJwY7VnpzyXyeyMj2TBqp9l7Nvs4j56JrFi/ebpLosDg==" + }, + "node_modules/@c4312/matcha": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@c4312/matcha/-/matcha-1.3.1.tgz", + "integrity": "sha512-JfkUCcWH5ez5N9UOEq7qFAbavkaASaGx9mQfmv+XIFrzp3YC08PKVWZfzv+u5kRQVrQmbZ+hLrnyqzEmLLkKlA==", + "dev": true, + "dependencies": { + "benchmark": "^2.1.4", + "chalk": "^3.0.0", + "commander": "^4.1.0", + "microtime": "^3.0.0" + }, + "bin": { + "matcha": "dist/cli.js" + } + }, + "node_modules/@c4312/matcha/node_modules/ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "dependencies": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@c4312/matcha/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@c4312/matcha/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@c4312/matcha/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@c4312/matcha/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@c4312/matcha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@c4312/matcha/node_modules/supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@dprint/darwin-arm64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/darwin-arm64/-/darwin-arm64-0.47.2.tgz", + "integrity": "sha512-mVPFBJsXxGDKHHCAY8wbqOyS4028g1bN15H9tivCnPAjwaZhkUimZHXWejXADjhGn+Xm2SlakugY9PY/68pH3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@dprint/darwin-x64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/darwin-x64/-/darwin-x64-0.47.2.tgz", + "integrity": "sha512-T7wzlc+rBV+6BRRiBjoqoy5Hj4TR2Nv2p2s9+ycyPGs10Kj/JXOWD8dnEHeBgUr2r4qe/ZdcxmsFQ5Hf2n0WuA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@dprint/linux-arm64-glibc": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-glibc/-/linux-arm64-glibc-0.47.2.tgz", + "integrity": "sha512-B0m1vT5LdVtrNOVdkqpLPrSxuCD+l5bTIgRzPaDoIB1ChWQkler9IlX8C+RStpujjPj6SYvwo5vTzjQSvRdQkA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-arm64-musl": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-musl/-/linux-arm64-musl-0.47.2.tgz", + "integrity": "sha512-zID6wZZqpg2/Q2Us+ERQkbhLwlW3p3xaeEr00MPf49bpydmEjMiPuSjWPkNv+slQSIyIsVovOxF4lbNZjsdtvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-x64-glibc": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-glibc/-/linux-x64-glibc-0.47.2.tgz", + "integrity": "sha512-rB3WXMdINnRd33DItIp7mObS7dzHW90ZzeJSsoKJLPp+Z7wXjjb27UUowfqVI4baa/1pd7sdbX54DPohMtfu/A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-x64-musl": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-musl/-/linux-x64-musl-0.47.2.tgz", + "integrity": "sha512-E0+TNbzYdTXJ/jCVjUctVxkda/faw++aDQLfyWGcmdMJnbM7NZz+W4fUpDXzMPsjy+zTWxXcPK7/q2DZz2gnbg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/win32-arm64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/win32-arm64/-/win32-arm64-0.47.2.tgz", + "integrity": "sha512-K1EieTCFjfOCmyIhw9zFSduE6qVCNHEveupqZEfbSkVGw5T9MJQ1I9+n7MDb3RIDYEUk0enJ58/w82q8oDKCyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@dprint/win32-x64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/win32-x64/-/win32-x64-0.47.2.tgz", + "integrity": "sha512-LhizWr8VrhHvq4ump8HwOERyFmdLiE8C6A42QSntGXzKdaa2nEOq20x/o56ZIiDcesiV+1TmosMKimPcOZHa+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@gulpjs/to-absolute-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz", + "integrity": "sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==", + "dependencies": { + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pptr/testrunner": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@pptr/testrunner/-/testrunner-0.8.0.tgz", + "integrity": "sha512-Wp+TM8BVQ9tXVFgQFtqzgZTCdIU8uB/WfUobGcvu7c+fijgMfpEFJDas8YxWD7IsAdPDtf6nzczv6w4i/J9k+A==", + "dev": true + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@secretlint/config-loader/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/formatter/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.5.2.tgz", + "integrity": "sha512-fCaOxoup5LIyBEo7R1oYWE7V4bSX0KQeHh66twon9e9usaLE3ijgF8QjYsR6joCssdeCHVd0wHm7ppsEyTr6vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.5.2.tgz", + "integrity": "sha512-jAw7jWM8+wU9cG6Uu31jGyD1B+PAVePCvnPKC/oov+2iBPKk3ao30zc/Itmi7FvXo4oPaL9PmzPPQhyniPVgVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.5.2", + "@textlint/resolver": "15.5.2", + "@textlint/types": "15.5.2", + "chalk": "^4.1.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "lodash": "^4.17.23", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@textlint/linter-formatter/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.5.2.tgz", + "integrity": "sha512-mg6rMQ3+YjwiXCYoQXbyVfDucpTa1q5mhspd/9qHBxUq4uY6W8GU42rmT3GW0V1yOfQ9z/iRrgPtkp71s8JzXg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.5.2.tgz", + "integrity": "sha512-YEITdjRiJaQrGLUWxWXl4TEg+d2C7+TNNjbGPHPH7V7CCnXm+S9GTjGAL7Q2WSGJyFEKt88Jvx6XdJffRv4HEA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.5.2.tgz", + "integrity": "sha512-sJOrlVLLXp4/EZtiWKWq9y2fWyZlI8GP+24rnU5avtPWBIMm/1w97yzKrAqYF8czx2MqR391z5akhnfhj2f/AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.5.2" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/braces": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.1.tgz", + "integrity": "sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==", + "dev": true + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/chai": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", + "dev": true + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/chai-string": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.5.tgz", + "integrity": "sha512-IecXRMSnpUvRnTztdpSdjcmcW7EdNme65bfDCQMi7XrSEPGmyDYYTEfc5fcactWDA6ioSm8o7NUqg9QxjBCCEw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/chai-subset": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", + "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/color": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.6.tgz", + "integrity": "sha512-NMiNcZFRUAiUUCCf7zkAelY8eV3aKqfbzyFQlXpPIEeoNDbsEHGpb854V3gzTsGKYj830I5zPuOwU/TP5/cW6A==", + "dev": true, + "dependencies": { + "@types/color-convert": "*" + } + }, + "node_modules/@types/color-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz", + "integrity": "sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==", + "dev": true, + "dependencies": { + "@types/color-name": "*" + } + }, + "node_modules/@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/diff": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.0.9.tgz", + "integrity": "sha512-RWVEhh/zGXpAVF/ZChwNnv7r4rvqzJ7lYNSmZSVTxjV0PBLf6Qu7RNg+SUtkpzxmiNkjCx0Xn2tPp7FIkshJwQ==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.42", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz", + "integrity": "sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/glob-stream": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-8.0.2.tgz", + "integrity": "sha512-kyuRfGE+yiSJWzSO3t74rXxdZNdYfLcllO0IUha4eX1fl40pm9L02Q/TEc3mykTLjoWz4STBNwYnUWdFu3I0DA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/picomatch": "*", + "@types/streamx": "*" + } + }, + "node_modules/@types/gulp": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.17.tgz", + "integrity": "sha512-+pKQynu2C/HS16kgmDlAicjtFYP8kaa86eE9P0Ae7GB5W29we/E2TIdbOWtEZD5XkpY+jr8fyqfwO6SWZecLpQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/undertaker": ">=1.2.6", + "@types/vinyl-fs": "*", + "chokidar": "^3.3.1" + } + }, + "node_modules/@types/gulp/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/gulp/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@types/gulp/node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@types/gulp/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@types/gulp/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@types/gulp/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/@types/gulp/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/gulp/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@types/gulp/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/@types/gulp/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@types/gulp/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/js-beautify": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@types/js-beautify/-/js-beautify-1.14.3.tgz", + "integrity": "sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/linkifyjs": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/linkifyjs/-/linkifyjs-2.1.7.tgz", + "integrity": "sha512-+SIYXs1lajyD7t/2+V9GLfdFlc/6Nr2tr65kjA2F5oOzBlPH+NiPqySJDHzREoGcL91Au9Qef8M5JdZiRXsaJw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "node_modules/@types/marked": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", + "integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==", + "dev": true + }, + "node_modules/@types/micromatch": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.6.tgz", + "integrity": "sha512-2eulCHWqjEpk9/vyic4tBhI8a9qQEl6DaK2n/sF7TweX9YESlypgKyhXMDGt4DAOy/jhLPvVrZc8pTDAMsplJA==", + "dev": true, + "dependencies": { + "@types/braces": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/mkdirp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.2.tgz", + "integrity": "sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.11.tgz", + "integrity": "sha512-PlJCXfb57Jrman0H1BxO2+Q7qwih2Mwk7T6Gvixj+SK4mqs4RWOGMMoP6p/LFa3UrP2CZOO6ai6otd7J/TB6Ug==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/picomatch": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.3.tgz", + "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/react": { + "version": "17.0.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", + "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/signale": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/signale/-/signale-1.4.7.tgz", + "integrity": "sha512-nc0j37QupTT7OcYeH3gRE1ZfzUalEUsDKJsJ3IsJr0pjjFZTjtrX1Bsn6Kv56YXI/H9rNSwAkIPRxNlZI8GyQw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "dev": true, + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, + "node_modules/@types/stream-buffers": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.7.tgz", + "integrity": "sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/streamx": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@types/streamx/-/streamx-2.9.5.tgz", + "integrity": "sha512-IHYsa6jYrck8VEdSwpY141FTTf6D7boPeMq9jy4qazNrFMA4VbRz/sw5LSsfR7jwdDcx0QKWkUexZvsWBC2eIQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "dev": true + }, + "node_modules/@types/to-absolute-glob": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/to-absolute-glob/-/to-absolute-glob-2.0.3.tgz", + "integrity": "sha512-jF1VfVBjC3v2e341igGlTGOHmFLiuLf/BYYCjkpxp+/XCrOHxY+ZN4y2CY8PPebJtnH6biIi75ciH3zr12DY0w==", + "dev": true + }, + "node_modules/@types/undertaker": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.7.tgz", + "integrity": "sha512-xuY7nBwo1zSRoY2aitp/HArHfTulFAKql2Fr4b4mWbBBP+F50n7Jm6nwISTTMaDk2xvl92O10TTejVF0Q9mInw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/undertaker-registry": "*", + "async-done": "~1.3.2" + } + }, + "node_modules/@types/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==", + "dev": true + }, + "node_modules/@types/vinyl": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.6.tgz", + "integrity": "sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g==", + "dev": true, + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "node_modules/@types/vinyl-fs": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.12.tgz", + "integrity": "sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==", + "dev": true, + "dependencies": { + "@types/glob-stream": "*", + "@types/node": "*", + "@types/vinyl": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.56.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "dependencies": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "dev": true, + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.4.tgz", + "integrity": "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vscode/dwarf-debugging": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@vscode/dwarf-debugging/-/dwarf-debugging-0.0.2.tgz", + "integrity": "sha512-u/sQV5SBYOzAFE9Wy0N9oH+FbpZ/KJCl9ESv+3I6G7IAQXvmzFOdkA+BCTFLgZl89viT28SoHmZk4ZPwjQhIkA==", + "dev": true, + "dependencies": { + "ws": "^8.14.1" + } + }, + "node_modules/@vscode/js-debug-browsers": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/js-debug-browsers/-/js-debug-browsers-1.1.2.tgz", + "integrity": "sha512-NIBJzVAzHjq6ez6TU+4QMUMRUfC9vKddr2a8NdEkp0wQSfjNxkYzT12TCAV3v8EOHA/Am/fxJbJuH97WvM33aA==", + "dependencies": { + "execa": "^4.0.0" + } + }, + "node_modules/@vscode/js-debug-browsers/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@vscode/js-debug-browsers/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/js-debug-browsers/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/@vscode/js-debug-browsers/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" + }, + "node_modules/@vscode/test-electron": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", + "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^7.0.1", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/test-electron/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vscode/vsce": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz", + "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@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.2", + "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" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", + "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vscode/vsce/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@vscode/vsce/node_modules/azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/@vscode/vsce/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@vscode/vsce/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@vscode/vsce/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/vsce/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vscode/vsce/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/vsce/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vscode/vsce/node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/@vscode/vsce/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vscode/vsce/node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "dev": true, + "license": "MIT", + "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" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/@vscode/vsce/node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/vsce/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vscode/vsce/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/vsce/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/vsce/node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@vscode/win32-app-container-tokens": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@vscode/win32-app-container-tokens/-/win32-app-container-tokens-0.2.0.tgz", + "integrity": "sha512-l2Xvw0q5dPT9jNg+Nj/ohqyaqJaCC0KvZkP1wkgfFbJFAtGHFjVp++Kghni3CZEXOErOGkzlverT6kakpmY9TQ==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-loose": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.4.0.tgz", + "integrity": "sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/anymatch/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "dev": true, + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true, + "optional": true + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "dev": true, + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", + "dev": true, + "dependencies": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-initial/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "dependencies": { + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "dependencies": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "node_modules/async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", + "dev": true, + "dependencies": { + "async-done": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", + "dev": true, + "dependencies": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "dev": true, + "dependencies": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, + "node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001239", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz", + "integrity": "sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 5" + } + }, + "node_modules/chai-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz", + "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==", + "dev": true, + "peerDependencies": { + "chai": "^4.1.2" + } + }, + "node_modules/chai-subset": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz", + "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", + "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "dev": true, + "dependencies": { + "cheerio-select": "^1.5.0", + "dom-serializer": "^1.3.2", + "domhandler": "^4.2.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", + "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "css-what": "^5.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0", + "domutils": "^2.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "optional": true + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/cloneable-readable/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", + "dev": true, + "dependencies": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true, + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-props": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz", + "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==", + "dev": true, + "dependencies": { + "each-props": "^1.3.2", + "is-plain-object": "^5.0.0" + } + }, + "node_modules/copy-props/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", + "dev": true + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/d/node_modules/es5-ext": { + "name": "@unes/es5-ext", + "version": "0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", + "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-require-extensions/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/dprint": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.47.2.tgz", + "integrity": "sha512-geUcVIIrmLaY+YtuOl4gD7J/QCjsXZa5gUqre9sO6cgH0X/Fa9heBN3l/AWVII6rKPw45ATuCSDWz1pyO+HkPQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "dprint": "bin.js" + }, + "optionalDependencies": { + "@dprint/darwin-arm64": "0.47.2", + "@dprint/darwin-x64": "0.47.2", + "@dprint/linux-arm64-glibc": "0.47.2", + "@dprint/linux-arm64-musl": "0.47.2", + "@dprint/linux-x64-glibc": "0.47.2", + "@dprint/linux-x64-musl": "0.47.2", + "@dprint/win32-arm64": "0.47.2", + "@dprint/win32-x64": "0.47.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.3.755", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.755.tgz", + "integrity": "sha512-BJ1s/kuUuOeo1bF/EM2E4yqW9te0Hpof3wgwBx40AWJE18zsD1Tqo0kr7ijnOc+lRsrlrqKPauJAHqaxOItoUA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "dev": true, + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-iterator/node_modules/es5-ext": { + "name": "@unes/es5-ext", + "version": "0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-weak-map/node_modules/es5-ext": { + "name": "@unes/es5-ext", + "version": "0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-header": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz", + "integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==", + "dev": true, + "peerDependencies": { + "eslint": ">=7.7.0" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/es5-ext": { + "name": "@unes/es5-ext", + "version": "0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/event-emitter/node_modules/es5-ext": { + "name": "@unes/es5-ext", + "version": "0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/findup-sync/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/flush-write-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/flush-write-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "optional": true + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "optional": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "dev": true, + "optional": true + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.0.tgz", + "integrity": "sha512-CdIUuwOkYNv9ZadR3jJvap8CMooKziQZ/QCSPhEb7zqfsEI5YnPmvca7IvbaVE3z58ZdUYD2JsU6AUWjL8WZJA==", + "dependencies": { + "@gulpjs/to-absolute-glob": "^4.0.0", + "anymatch": "^3.1.3", + "fastq": "^1.13.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "is-negated-glob": "^1.0.0", + "normalize-path": "^3.0.0", + "streamx": "^2.12.5" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-stream/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "dev": true, + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "dependencies": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-rename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", + "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "dependencies": { + "glogg": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasha/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.7.tgz", + "integrity": "sha512-vWdusw+y12DUEeoZqW1kplOFqk3tedGV8qlga8/SF6a3lOiWLqGZZQvfWvY0fQYdfiRi/u1DFNpudTSV9l1aCg==", + "dev": true, + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/inversify": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/inversify/-/inversify-6.0.2.tgz", + "integrity": "sha512-i9m8j/7YIv4mDuYXUAcrpKPSaju/CIly9AHK5jvCBeoiM/2KEsuCQTTP+rzSWWpLYWRukdXFSl6ZTk2/uumbiA==" + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-xxhash": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-xxhash/-/js-xxhash-3.0.1.tgz", + "integrity": "sha512-Y2NSC77RIxJrvi2NoXjMi2LYsVDTlVqBoQRi8PXQg4PtP29wdtIOhsp8Ujw4EjEkBFheCPx8bMOmI9zoxx/3jQ==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "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" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", + "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.3", + "object.assign": "^4.1.2" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/just-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", + "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", + "dev": true + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keytar": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz", + "integrity": "sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-addon-api": "^3.0.0", + "prebuild-install": "^6.0.0" + } + }, + "node_modules/keytar/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", + "dev": true, + "dependencies": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", + "dev": true, + "dependencies": { + "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/linkifyjs": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz", + "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==" + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/marked": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", + "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/matchdep/node_modules/findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/matchdep/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/micromatch/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/micromatch/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/micromatch/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/microtime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/microtime/-/microtime-3.0.0.tgz", + "integrity": "sha512-SirJr7ZL4ow2iWcb54bekS4aWyBQNVcEDBiwAz9D/sTgY59A+uE8UJU15cp5wyZmPBwg/3zf8lyCJ5NUe1nVlQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^1.2.0", + "node-gyp-build": "^3.8.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dev": true, + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "optional": true + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha-junit-reporter": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", + "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" + }, + "peerDependencies": { + "mocha": ">=2.2.5" + } + }, + "node_modules/mocha-junit-reporter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha-junit-reporter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha-multi-reporters": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz", + "integrity": "sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "lodash": "^4.17.15" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "mocha": ">=3.1.2" + } + }, + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/mocha/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/mocha/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true, + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/nise": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.7.tgz", + "integrity": "sha512-wWtNUhkT7k58uvWTB/Gy26eA/EJKtPZFVAhEilN5UYVmmGRYOURbejRUyKm0Uu9XVEW7K5nBOZfR8VMB4QR2RQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + } + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "dev": true, + "optional": true, + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true + }, + "node_modules/node-gyp-build": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz", + "integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "dev": true + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "dependencies": { + "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-7.0.1.tgz", + "integrity": "sha512-Adbv+bJQ8UTAM03rRODqrO5cx0YU5KCG2CvHtSURiadvdTjjgGJXdbc1oQ9CXBh9dnGfHSoSB1Web/0Dzp6kOQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.3", + "memorystream": "^0.3.1", + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0", + "npm": ">= 9" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/npm-run-all2/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm-run-all2/node_modules/minimatch": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "optional": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/nyc/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nyc/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/nyc/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/nyc/node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/nyc/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", + "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "dev": true, + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.9.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.3.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", + "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^10.2.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=", + "dev": true, + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true + }, + "node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "git+ssh://git@github.com/connor4312/picomatch.git#2fbe90b12eafa7dde816ff8c16be9e77271b0e0b", + "integrity": "sha512-NFpH2Othy/6fk2qamg3cjFa4P3RDgDpTNQGqZWT07WL80xef9hoLlIfHNRvWp4VDEOJVIzgChnDOHwvf5KP+jA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/preact": { + "version": "10.19.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", + "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "dev": true, + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prebuild-install/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.4.tgz", + "integrity": "sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "json5": "^2.2.3", + "require-from-string": "^2.0.2" + } + }, + "node_modules/rc-config-loader/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/rc-config-loader/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/readdirp/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "dev": true, + "dependencies": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "node_modules/resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", + "dev": true, + "dependencies": { + "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "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" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/secretlint/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/secretlint/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/secretlint/node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/secretlint/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/secretlint/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/secretlint/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "dependencies": { + "sver-compat": "^1.5.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "dev": true, + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/simple-get/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stdin-discarder/node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/stdin-discarder/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "node_modules/stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "node_modules/streamx": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", + "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "dependencies": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "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" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "optional": true, + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tas-client": { + "version": "0.2.33", + "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.2.33.tgz", + "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", + "dev": true, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-absolute-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-3.0.0.tgz", + "integrity": "sha512-loO/XEWTRqpfcpI7+Jr2RR2Umaaozx1t6OSVWtMi0oy5F/Fxg3IC+D/TToDnxyAGs7uZBGT/6XmyDUxgsObJXA==", + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", + "dev": true, + "dependencies": { + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dev": true, + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.6.tgz", + "integrity": "sha512-xcQpTEAJw2DP7GqVNECh4dD+riS+C1qndXLfBCJ3xk0kqprtGN491P5KlmrDbKdtuW8NEcP/5ChxiJI3S9WYTA==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undertaker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker/node_modules/fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", + "dev": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "dependencies": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vinyl-fs/node_modules/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/vinyl-fs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/vinyl-fs/node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", + "dev": true, + "dependencies": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vscode-tas-client": { + "version": "0.1.84", + "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.1.84.tgz", + "integrity": "sha512-rUTrUopV+70hvx1hW5ebdw1nd6djxubkLvVxjGdyD/r5v/wcVF41LIfiAtbm5qLZDtQdsMH1IaCuDoluoIa88w==", + "dependencies": { + "tas-client": "0.2.33" + }, + "engines": { + "vscode": "^1.85.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", + "dev": true + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "node_modules/yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, + "@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true + }, + "@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "requires": { + "@azu/format-text": "^1.0.1" + } + }, + "@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "requires": { + "tslib": "^2.6.2" + } + }, + "@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "dev": true, + "requires": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + } + }, + "@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", + "dev": true, + "requires": { + "@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": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.23.0.tgz", + "integrity": "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==", + "dev": true, + "requires": { + "@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.4", + "tslib": "^2.6.2" + } + }, + "@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", + "dev": true, + "requires": { + "tslib": "^2.6.2" + } + }, + "@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "dev": true, + "requires": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + } + }, + "@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "dev": true, + "requires": { + "@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": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + } + }, + "@azure/logger": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", + "dev": true, + "requires": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + } + }, + "@azure/msal-browser": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.6.3.tgz", + "integrity": "sha512-sTjMtUm+bJpENU/1WlRzHEsgEHppZDZ1EtNyaOODg/sQBtMxxJzGB+MOCM+T2Q5Qe1fKBrdxUmjyRxm0r7Ez9w==", + "dev": true, + "requires": { + "@azure/msal-common": "16.4.1" + } + }, + "@azure/msal-common": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.4.1.tgz", + "integrity": "sha512-Bl8f+w37xkXsYh7QRkAKCFGYtWMYuOVO7Lv+BxILrvGz3HbIEF22Pt0ugyj0QPOl6NLrHcnNUQ9yeew98P/5iw==", + "dev": true + }, + "@azure/msal-node": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.1.2.tgz", + "integrity": "sha512-DoeSJ9U5KPAIZoHsPywvfEj2MhBniQe0+FSpjLUTdWoIkI999GB5USkW6nNEHnIaLVxROHXvprWA1KzdS1VQ4A==", + "dev": true, + "requires": { + "@azure/msal-common": "16.4.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } + } + }, + "@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + } + }, + "@babel/compat-data": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", + "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", + "dev": true + }, + "@babel/core": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.6.tgz", + "integrity": "sha512-gJnOEWSqTk96qG5BoIrl5bVtc23DCycmIePPYnamY9RboYdI4nFy5vAQMSl81O5K/W0sLDWfGysnOECC+KUUCA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.14.5", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helpers": "^7.14.6", + "@babel/parser": "^7.14.6", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "requires": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", + "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", + "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.5.tgz", + "integrity": "sha512-iXpX4KW8LVODuAieD7MzhNjmM6dzYY5tfRqT+R9HDXWl0jPn/djKmA+G9s/2C2T9zggw5tK1QNqZ70USfedOwA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-simple-access": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.5.tgz", + "integrity": "sha512-nfBN9xvmCt6nrMZjfhkl7i0oTV3yxR4/FztsbOASyTvVcoYd0TRHh7eMLdlEcCqobydC0LAF3LtC92Iwxo0wyw==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "requires": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + } + }, + "@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "requires": { + "@babel/types": "^7.29.0" + } + }, + "@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + } + }, + "@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + } + }, + "@c4312/chromehash": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@c4312/chromehash/-/chromehash-0.3.1.tgz", + "integrity": "sha512-WmQTccHowTwvMsSHOuUlFabWz5aK8ZZyRKMh2/E7jjZJwY7VnpzyXyeyMj2TBqp9l7Nvs4j56JrFi/ebpLosDg==" + }, + "@c4312/matcha": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@c4312/matcha/-/matcha-1.3.1.tgz", + "integrity": "sha512-JfkUCcWH5ez5N9UOEq7qFAbavkaASaGx9mQfmv+XIFrzp3YC08PKVWZfzv+u5kRQVrQmbZ+hLrnyqzEmLLkKlA==", + "dev": true, + "requires": { + "benchmark": "^2.1.4", + "chalk": "^3.0.0", + "commander": "^4.1.0", + "microtime": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@dprint/darwin-arm64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/darwin-arm64/-/darwin-arm64-0.47.2.tgz", + "integrity": "sha512-mVPFBJsXxGDKHHCAY8wbqOyS4028g1bN15H9tivCnPAjwaZhkUimZHXWejXADjhGn+Xm2SlakugY9PY/68pH3Q==", + "dev": true, + "optional": true + }, + "@dprint/darwin-x64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/darwin-x64/-/darwin-x64-0.47.2.tgz", + "integrity": "sha512-T7wzlc+rBV+6BRRiBjoqoy5Hj4TR2Nv2p2s9+ycyPGs10Kj/JXOWD8dnEHeBgUr2r4qe/ZdcxmsFQ5Hf2n0WuA==", + "dev": true, + "optional": true + }, + "@dprint/linux-arm64-glibc": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-glibc/-/linux-arm64-glibc-0.47.2.tgz", + "integrity": "sha512-B0m1vT5LdVtrNOVdkqpLPrSxuCD+l5bTIgRzPaDoIB1ChWQkler9IlX8C+RStpujjPj6SYvwo5vTzjQSvRdQkA==", + "dev": true, + "optional": true + }, + "@dprint/linux-arm64-musl": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-musl/-/linux-arm64-musl-0.47.2.tgz", + "integrity": "sha512-zID6wZZqpg2/Q2Us+ERQkbhLwlW3p3xaeEr00MPf49bpydmEjMiPuSjWPkNv+slQSIyIsVovOxF4lbNZjsdtvw==", + "dev": true, + "optional": true + }, + "@dprint/linux-x64-glibc": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-glibc/-/linux-x64-glibc-0.47.2.tgz", + "integrity": "sha512-rB3WXMdINnRd33DItIp7mObS7dzHW90ZzeJSsoKJLPp+Z7wXjjb27UUowfqVI4baa/1pd7sdbX54DPohMtfu/A==", + "dev": true, + "optional": true + }, + "@dprint/linux-x64-musl": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-musl/-/linux-x64-musl-0.47.2.tgz", + "integrity": "sha512-E0+TNbzYdTXJ/jCVjUctVxkda/faw++aDQLfyWGcmdMJnbM7NZz+W4fUpDXzMPsjy+zTWxXcPK7/q2DZz2gnbg==", + "dev": true, + "optional": true + }, + "@dprint/win32-arm64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/win32-arm64/-/win32-arm64-0.47.2.tgz", + "integrity": "sha512-K1EieTCFjfOCmyIhw9zFSduE6qVCNHEveupqZEfbSkVGw5T9MJQ1I9+n7MDb3RIDYEUk0enJ58/w82q8oDKCyA==", + "dev": true, + "optional": true + }, + "@dprint/win32-x64": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/@dprint/win32-x64/-/win32-x64-0.47.2.tgz", + "integrity": "sha512-LhizWr8VrhHvq4ump8HwOERyFmdLiE8C6A42QSntGXzKdaa2nEOq20x/o56ZIiDcesiV+1TmosMKimPcOZHa+Q==", + "dev": true, + "optional": true + }, + "@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "dev": true, + "optional": true + }, + "@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.4.3" + } + }, + "@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true + }, + "@gulpjs/to-absolute-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz", + "integrity": "sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==", + "requires": { + "is-negated-glob": "^1.0.0" + } + }, + "@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pptr/testrunner": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@pptr/testrunner/-/testrunner-0.8.0.tgz", + "integrity": "sha512-Wp+TM8BVQ9tXVFgQFtqzgZTCdIU8uB/WfUobGcvu7c+fijgMfpEFJDas8YxWD7IsAdPDtf6nzczv6w4i/J9k+A==", + "dev": true + }, + "@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "requires": { + "@secretlint/types": "^10.2.2" + } + }, + "@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "requires": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + } + }, + "@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true + }, + "strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "requires": { + "ansi-regex": "^6.2.2" + } + } + } + }, + "@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "requires": { + "@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": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true + }, + "@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true + }, + "@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "requires": { + "node-sarif-builder": "^3.2.0" + } + }, + "@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "requires": { + "@secretlint/types": "^10.2.2" + } + }, + "@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true + }, + "@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "requires": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + } + }, + "@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" + }, + "@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dev": true, + "requires": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@textlint/ast-node-types": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.5.2.tgz", + "integrity": "sha512-fCaOxoup5LIyBEo7R1oYWE7V4bSX0KQeHh66twon9e9usaLE3ijgF8QjYsR6joCssdeCHVd0wHm7ppsEyTr6vg==", + "dev": true + }, + "@textlint/linter-formatter": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.5.2.tgz", + "integrity": "sha512-jAw7jWM8+wU9cG6Uu31jGyD1B+PAVePCvnPKC/oov+2iBPKk3ao30zc/Itmi7FvXo4oPaL9PmzPPQhyniPVgVg==", + "dev": true, + "requires": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.5.2", + "@textlint/resolver": "15.5.2", + "@textlint/types": "15.5.2", + "chalk": "^4.1.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "lodash": "^4.17.23", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@textlint/module-interop": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.5.2.tgz", + "integrity": "sha512-mg6rMQ3+YjwiXCYoQXbyVfDucpTa1q5mhspd/9qHBxUq4uY6W8GU42rmT3GW0V1yOfQ9z/iRrgPtkp71s8JzXg==", + "dev": true + }, + "@textlint/resolver": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.5.2.tgz", + "integrity": "sha512-YEITdjRiJaQrGLUWxWXl4TEg+d2C7+TNNjbGPHPH7V7CCnXm+S9GTjGAL7Q2WSGJyFEKt88Jvx6XdJffRv4HEA==", + "dev": true + }, + "@textlint/types": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.5.2.tgz", + "integrity": "sha512-sJOrlVLLXp4/EZtiWKWq9y2fWyZlI8GP+24rnU5avtPWBIMm/1w97yzKrAqYF8czx2MqR391z5akhnfhj2f/AQ==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "15.5.2" + } + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/braces": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.1.tgz", + "integrity": "sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==", + "dev": true + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "@types/chai": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", + "dev": true + }, + "@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/chai-string": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@types/chai-string/-/chai-string-1.4.5.tgz", + "integrity": "sha512-IecXRMSnpUvRnTztdpSdjcmcW7EdNme65bfDCQMi7XrSEPGmyDYYTEfc5fcactWDA6ioSm8o7NUqg9QxjBCCEw==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/chai-subset": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.5.tgz", + "integrity": "sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/color": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.6.tgz", + "integrity": "sha512-NMiNcZFRUAiUUCCf7zkAelY8eV3aKqfbzyFQlXpPIEeoNDbsEHGpb854V3gzTsGKYj830I5zPuOwU/TP5/cW6A==", + "dev": true, + "requires": { + "@types/color-convert": "*" + } + }, + "@types/color-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz", + "integrity": "sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==", + "dev": true, + "requires": { + "@types/color-name": "*" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, + "@types/diff": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.0.9.tgz", + "integrity": "sha512-RWVEhh/zGXpAVF/ZChwNnv7r4rvqzJ7lYNSmZSVTxjV0PBLf6Qu7RNg+SUtkpzxmiNkjCx0Xn2tPp7FIkshJwQ==", + "dev": true + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true + }, + "@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.42", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz", + "integrity": "sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "@types/glob-stream": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@types/glob-stream/-/glob-stream-8.0.2.tgz", + "integrity": "sha512-kyuRfGE+yiSJWzSO3t74rXxdZNdYfLcllO0IUha4eX1fl40pm9L02Q/TEc3mykTLjoWz4STBNwYnUWdFu3I0DA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/picomatch": "*", + "@types/streamx": "*" + } + }, + "@types/gulp": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.17.tgz", + "integrity": "sha512-+pKQynu2C/HS16kgmDlAicjtFYP8kaa86eE9P0Ae7GB5W29we/E2TIdbOWtEZD5XkpY+jr8fyqfwO6SWZecLpQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/undertaker": ">=1.2.6", + "@types/vinyl-fs": "*", + "chokidar": "^3.3.1" + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "@types/js-beautify": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@types/js-beautify/-/js-beautify-1.14.3.tgz", + "integrity": "sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "requires": { + "@types/node": "*" + } + }, + "@types/linkifyjs": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/linkifyjs/-/linkifyjs-2.1.7.tgz", + "integrity": "sha512-+SIYXs1lajyD7t/2+V9GLfdFlc/6Nr2tr65kjA2F5oOzBlPH+NiPqySJDHzREoGcL91Au9Qef8M5JdZiRXsaJw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "@types/marked": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz", + "integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg==", + "dev": true + }, + "@types/micromatch": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.6.tgz", + "integrity": "sha512-2eulCHWqjEpk9/vyic4tBhI8a9qQEl6DaK2n/sF7TweX9YESlypgKyhXMDGt4DAOy/jhLPvVrZc8pTDAMsplJA==", + "dev": true, + "requires": { + "@types/braces": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "@types/mkdirp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.2.tgz", + "integrity": "sha512-o0K1tSO0Dx5X6xlU5F1D6625FawhC3dU3iqr25lluNv/+/QIVH8RLNEiVokgIZo+mz+87w/3Mkg/VvQS+J51fQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "@types/node": { + "version": "20.11.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.11.tgz", + "integrity": "sha512-PlJCXfb57Jrman0H1BxO2+Q7qwih2Mwk7T6Gvixj+SK4mqs4RWOGMMoP6p/LFa3UrP2CZOO6ai6otd7J/TB6Ug==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "@types/picomatch": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.3.tgz", + "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "@types/react": { + "version": "17.0.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", + "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "requires": { + "@types/node": "*" + } + }, + "@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/signale": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/signale/-/signale-1.4.7.tgz", + "integrity": "sha512-nc0j37QupTT7OcYeH3gRE1ZfzUalEUsDKJsJ3IsJr0pjjFZTjtrX1Bsn6Kv56YXI/H9rNSwAkIPRxNlZI8GyQw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, + "@types/stream-buffers": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.7.tgz", + "integrity": "sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/streamx": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@types/streamx/-/streamx-2.9.5.tgz", + "integrity": "sha512-IHYsa6jYrck8VEdSwpY141FTTf6D7boPeMq9jy4qazNrFMA4VbRz/sw5LSsfR7jwdDcx0QKWkUexZvsWBC2eIQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "dev": true + }, + "@types/to-absolute-glob": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/to-absolute-glob/-/to-absolute-glob-2.0.3.tgz", + "integrity": "sha512-jF1VfVBjC3v2e341igGlTGOHmFLiuLf/BYYCjkpxp+/XCrOHxY+ZN4y2CY8PPebJtnH6biIi75ciH3zr12DY0w==", + "dev": true + }, + "@types/undertaker": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.7.tgz", + "integrity": "sha512-xuY7nBwo1zSRoY2aitp/HArHfTulFAKql2Fr4b4mWbBBP+F50n7Jm6nwISTTMaDk2xvl92O10TTejVF0Q9mInw==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/undertaker-registry": "*", + "async-done": "~1.3.2" + } + }, + "@types/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==", + "dev": true + }, + "@types/vinyl": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.6.tgz", + "integrity": "sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g==", + "dev": true, + "requires": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "@types/vinyl-fs": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.12.tgz", + "integrity": "sha512-LgBpYIWuuGsihnlF+OOWWz4ovwCYlT03gd3DuLwex50cYZLmX3yrW+sFF9ndtmh7zcZpS6Ri47PrIu+fV+sbXw==", + "dev": true, + "requires": { + "@types/glob-stream": "*", + "@types/node": "*", + "@types/vinyl": "*" + } + }, + "@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "dependencies": { + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3" + } + }, + "@typescript-eslint/project-service": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "dev": true, + "requires": { + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", + "debug": "^4.4.3" + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" + } + }, + "@typescript-eslint/tsconfig-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "dev": true, + "requires": {} + }, + "@typescript-eslint/type-utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + } + }, + "@typescript-eslint/types": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "dev": true, + "requires": { + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, + "brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "requires": { + "balanced-match": "^4.0.2" + } + }, + "minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "dev": true, + "requires": { + "brace-expansion": "^5.0.2" + } + }, + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + } + } + }, + "@typescript-eslint/utils": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true + } + } + }, + "@typespec/ts-http-runtime": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.4.tgz", + "integrity": "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ==", + "dev": true, + "requires": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + } + }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "@vscode/dwarf-debugging": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@vscode/dwarf-debugging/-/dwarf-debugging-0.0.2.tgz", + "integrity": "sha512-u/sQV5SBYOzAFE9Wy0N9oH+FbpZ/KJCl9ESv+3I6G7IAQXvmzFOdkA+BCTFLgZl89viT28SoHmZk4ZPwjQhIkA==", + "dev": true, + "requires": { + "ws": "^8.14.1" + } + }, + "@vscode/js-debug-browsers": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/js-debug-browsers/-/js-debug-browsers-1.1.2.tgz", + "integrity": "sha512-NIBJzVAzHjq6ez6TU+4QMUMRUfC9vKddr2a8NdEkp0wQSfjNxkYzT12TCAV3v8EOHA/Am/fxJbJuH97WvM33aA==", + "requires": { + "execa": "^4.0.0" + }, + "dependencies": { + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" + }, + "@vscode/test-electron": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", + "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "dev": true, + "requires": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^7.0.1", + "semver": "^7.6.2" + }, + "dependencies": { + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true + } + } + }, + "@vscode/vsce": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz", + "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==", + "dev": true, + "requires": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@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", + "keytar": "^7.7.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.2", + "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" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dev": true, + "requires": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "requires": { + "uc.micro": "^2.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "dev": true, + "requires": { + "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" + } + }, + "mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + } + } + }, + "@vscode/vsce-sign": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", + "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", + "dev": true, + "requires": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "dev": true, + "optional": true + }, + "@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "dev": true, + "optional": true + }, + "@vscode/win32-app-container-tokens": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@vscode/win32-app-container-tokens/-/win32-app-container-tokens-0.2.0.tgz", + "integrity": "sha512-l2Xvw0q5dPT9jNg+Nj/ohqyaqJaCC0KvZkP1wkgfFbJFAtGHFjVp++Kghni3CZEXOErOGkzlverT6kakpmY9TQ==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-loose": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.4.0.tgz", + "integrity": "sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==", + "requires": { + "acorn": "^8.11.0" + } + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "requires": { + "debug": "^4.3.4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "requires": { + "environment": "^1.0.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "dev": true, + "requires": { + "buffer-equal": "^1.0.0" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true, + "optional": true + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + } + }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", + "dev": true, + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==" + }, + "async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", + "dev": true, + "requires": { + "async-done": "^1.2.2" + } + }, + "asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "requires": { + "editions": "^6.21.0" + } + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "dev": true, + "requires": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "requires": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "optional": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "requires": { + "run-applescript": "^7.0.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + } + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001239", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001239.tgz", + "integrity": "sha512-cyBkXJDMeI4wthy8xJ2FvDU6+0dtcZSJW3voUF8+e9f1bBeuvyZfc3PNbkOETyhbR+dGCPzn9E7MA3iwzusOhQ==", + "dev": true + }, + "chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + } + }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, + "chai-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz", + "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==", + "dev": true, + "requires": {} + }, + "chai-subset": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz", + "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "dev": true + }, + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } + }, + "cheerio": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", + "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "dev": true, + "requires": { + "cheerio-select": "^1.5.0", + "dom-serializer": "^1.3.2", + "domhandler": "^4.2.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "tslib": "^2.2.0" + } + }, + "cheerio-select": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", + "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", + "dev": true, + "requires": { + "css-select": "^4.1.3", + "css-what": "^5.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0", + "domutils": "^2.7.0" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "optional": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "requires": { + "restore-cursor": "^4.0.0" + } + }, + "cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", + "dev": true, + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true, + "optional": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-props": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz", + "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==", + "dev": true, + "requires": { + "each-props": "^1.3.2", + "is-plain-object": "^5.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "dev": true + }, + "css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true + }, + "csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", + "dev": true + }, + "d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "requires": { + "es5-ext": "npm:@unes/es5-ext@0.10.64-1", + "type": "^2.7.2" + }, + "dependencies": { + "es5-ext": { + "version": "npm:@unes/es5-ext@0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + } + } + }, + "data-uri-to-buffer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", + "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==" + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "optional": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "requires": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + } + }, + "default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==" + }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", + "dev": true + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "optional": true + }, + "diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dotenv": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.1.tgz", + "integrity": "sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==" + }, + "dprint": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.47.2.tgz", + "integrity": "sha512-geUcVIIrmLaY+YtuOl4gD7J/QCjsXZa5gUqre9sO6cgH0X/Fa9heBN3l/AWVII6rKPw45ATuCSDWz1pyO+HkPQ==", + "dev": true, + "requires": { + "@dprint/darwin-arm64": "0.47.2", + "@dprint/darwin-x64": "0.47.2", + "@dprint/linux-arm64-glibc": "0.47.2", + "@dprint/linux-arm64-musl": "0.47.2", + "@dprint/linux-x64-glibc": "0.47.2", + "@dprint/linux-x64-musl": "0.47.2", + "@dprint/win32-arm64": "0.47.2", + "@dprint/win32-x64": "0.47.2" + } + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "requires": { + "version-range": "^4.15.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.755", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.755.tgz", + "integrity": "sha512-BJ1s/kuUuOeo1bF/EM2E4yqW9te0Hpof3wgwBx40AWJE18zsD1Tqo0kr7ijnOc+lRsrlrqKPauJAHqaxOItoUA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + } + } + }, + "es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + } + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-iterator-helpers": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "dev": true, + "requires": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" + } + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "requires": { + "hasown": "^2.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "npm:@unes/es5-ext@0.10.64-1", + "es6-symbol": "^3.1.1" + }, + "dependencies": { + "es5-ext": { + "version": "npm:@unes/es5-ext@0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + } + } + }, + "es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "requires": { + "d": "^1.0.2", + "ext": "^1.7.0" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "npm:@unes/es5-ext@0.10.64-1", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + }, + "dependencies": { + "es5-ext": { + "version": "npm:@unes/es5-ext@0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + } + } + }, + "esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-plugin-header": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz", + "integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==", + "dev": true, + "requires": {} + }, + "eslint-plugin-react": { + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "dev": true, + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "requires": { + "d": "^1.0.1", + "es5-ext": "npm:@unes/es5-ext@0.10.64-1", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "dependencies": { + "es5-ext": { + "version": "npm:@unes/es5-ext@0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + } + } + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "npm:@unes/es5-ext@0.10.64-1" + }, + "dependencies": { + "es5-ext": { + "version": "npm:@unes/es5-ext@0.10.64-1", + "resolved": "https://registry.npmjs.org/@unes/es5-ext/-/es5-ext-0.10.64-1.tgz", + "integrity": "sha512-nZSbffWxU0SleuK9kPrC9zwsbNmzkrSxQSa0+UOR8ghBQSlnj1wmtZZA5+ZRtgk8Xn+kaoAYPT9aOBwFZzXfFA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + } + } + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "optional": true + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "requires": { + "type": "^2.7.2" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" + }, + "fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "optional": true + }, + "fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "requires": { + "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-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "dev": true, + "optional": true + }, + "glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "dev": true, + "requires": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "dependencies": { + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, + "brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "requires": { + "balanced-match": "^4.0.2" + } + }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + } + }, + "minimatch": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "dev": true, + "requires": { + "brace-expansion": "^5.0.2" + } + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-stream": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.0.tgz", + "integrity": "sha512-CdIUuwOkYNv9ZadR3jJvap8CMooKziQZ/QCSPhEb7zqfsEI5YnPmvca7IvbaVE3z58ZdUYD2JsU6AUWjL8WZJA==", + "requires": { + "@gulpjs/to-absolute-glob": "^4.0.0", + "anymatch": "^3.1.3", + "fastq": "^1.13.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "is-negated-glob": "^1.0.0", + "normalize-path": "^3.0.0", + "streamx": "^2.12.5" + }, + "dependencies": { + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + } + } + }, + "glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true + } + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true + }, + "got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "requires": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + } + }, + "gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + } + }, + "gulp-rename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", + "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", + "dev": true + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "^1.0.0" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "husky": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.7.tgz", + "integrity": "sha512-vWdusw+y12DUEeoZqW1kplOFqk3tedGV8qlga8/SF6a3lOiWLqGZZQvfWvY0fQYdfiRi/u1DFNpudTSV9l1aCg==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "inversify": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/inversify/-/inversify-6.0.2.tgz", + "integrity": "sha512-i9m8j/7YIv4mDuYXUAcrpKPSaju/CIly9AHK5jvCBeoiM/2KEsuCQTTP+rzSWWpLYWRukdXFSl6ZTk2/uumbiA==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + } + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "requires": { + "hasown": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, + "is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true + }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.11" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "requires": { + "is-inside-container": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "requires": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + } + }, + "iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "requires": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-xxhash": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-xxhash/-/js-xxhash-3.0.1.tgz", + "integrity": "sha512-Y2NSC77RIxJrvi2NoXjMi2LYsVDTlVqBoQRi8PXQg4PtP29wdtIOhsp8Ujw4EjEkBFheCPx8bMOmI9zoxx/3jQ==" + }, + "js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" + }, + "jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "requires": { + "jws": "^4.0.1", + "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" + }, + "dependencies": { + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + } + } + }, + "jsx-ast-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", + "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "object.assign": "^4.1.2" + } + }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "just-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", + "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", + "dev": true + }, + "just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true + }, + "jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "requires": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "requires": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "keytar": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.7.0.tgz", + "integrity": "sha512-YEY9HWqThQc5q5xbXbRwsZTh2PJ36OSYRjSv3NN2xf5s5dpLTjEZnC2YikR29OaVybf9nQ0dJ/80i40RS97t/A==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "prebuild-install": "^6.0.0" + }, + "dependencies": { + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + } + } + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", + "dev": true, + "requires": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + } + }, + "lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", + "dev": true, + "requires": { + "flush-write-stream": "^1.0.2" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "linkifyjs": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz", + "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==" + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.1" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "marked": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", + "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", + "dev": true + }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", + "dev": true, + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true + }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==" + }, + "merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "dependencies": { + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "requires": { + "fill-range": "^7.1.1" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "microtime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/microtime/-/microtime-3.0.0.tgz", + "integrity": "sha512-SirJr7ZL4ow2iWcb54bekS4aWyBQNVcEDBiwAz9D/sTgY59A+uE8UJU15cp5wyZmPBwg/3zf8lyCJ5NUe1nVlQ==", + "dev": true, + "requires": { + "node-addon-api": "^1.2.0", + "node-gyp-build": "^3.8.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "dev": true + }, + "mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dev": true, + "requires": { + "mime-db": "1.51.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "optional": true + }, + "mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": ">=7.0.5", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "minimatch": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.8.tgz", + "integrity": "sha512-7RN35vit8DeBclkofOVmBY0eDAZZQd1HzmukRdSyz95CRh8FT54eqnbj0krQr3mrHR6sfRyYkyhwBWjoV5uqlQ==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "mocha-junit-reporter": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz", + "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "md5": "^2.3.0", + "mkdirp": "^3.0.0", + "strip-ansi": "^6.0.1", + "xml": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "mocha-multi-reporters": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/mocha-multi-reporters/-/mocha-multi-reporters-1.5.1.tgz", + "integrity": "sha512-Yb4QJOaGLIcmB0VY7Wif5AjvLMUFAdV57D2TWEva1Y0kU/3LjKpeRVmlMIfuO1SVbauve459kgtIizADqxMWPg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "lodash": "^4.17.15" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true, + "optional": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "nise": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.7.tgz", + "integrity": "sha512-wWtNUhkT7k58uvWTB/Gy26eA/EJKtPZFVAhEilN5UYVmmGRYOURbejRUyKm0Uu9XVEW7K5nBOZfR8VMB4QR2RQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" + }, + "dependencies": { + "path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true + } + } + }, + "node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "dev": true, + "optional": true, + "requires": { + "semver": "^5.4.1" + } + }, + "node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true + }, + "node-gyp-build": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz", + "integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==", + "dev": true + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "dev": true + }, + "node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "requires": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "requires": { + "once": "^1.3.2" + } + }, + "npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==" + }, + "npm-run-all2": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-7.0.1.tgz", + "integrity": "sha512-Adbv+bJQ8UTAM03rRODqrO5cx0YU5KCG2CvHtSURiadvdTjjgGJXdbc1oQ9CXBh9dnGfHSoSB1Web/0Dzp6kOQ==", + "requires": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.3", + "memorystream": "^0.3.1", + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==" + }, + "brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "requires": { + "balanced-match": "^4.0.2" + } + }, + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" + }, + "minimatch": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", + "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", + "requires": { + "brace-expansion": "^5.0.2" + } + }, + "which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "requires": { + "isexe": "^3.1.1" + } + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "object.hasown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "dev": true, + "requires": { + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "requires": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "ora": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", + "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "dev": true, + "requires": { + "chalk": "^5.3.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.9.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.3.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + }, + "emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true + }, + "log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "requires": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + } + }, + "string-width": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", + "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^10.2.1", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + }, + "dependencies": { + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + } + } + }, + "p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg=", + "dev": true, + "requires": { + "semver": "^5.1.0" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "requires": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + } + }, + "path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true + }, + "path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "picomatch": { + "version": "git+ssh://git@github.com/connor4312/picomatch.git#2fbe90b12eafa7dde816ff8c16be9e77271b0e0b", + "integrity": "sha512-NFpH2Othy/6fk2qamg3cjFa4P3RDgDpTNQGqZWT07WL80xef9hoLlIfHNRvWp4VDEOJVIzgChnDOHwvf5KP+jA==", + "from": "picomatch@connor4312/picomatch#2fbe90b12eafa7dde816ff8c16be9e77271b0e0b" + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + } + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "preact": { + "version": "10.19.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.19.3.tgz", + "integrity": "sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==" + }, + "prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true + }, + "qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "dev": true, + "requires": { + "side-channel": "^1.1.0" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "requires": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "requires": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + } + }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "optional": true + } + } + }, + "rc-config-loader": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.4.tgz", + "integrity": "sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==", + "dev": true, + "requires": { + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "json5": "^2.2.3", + "require-from-string": "^2.0.2" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "requires": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" + }, + "reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "dev": true, + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", + "dev": true, + "requires": { + "value-or-function": "^3.0.0" + } + }, + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "requires": { + "lru-cache": "^10.0.1" + } + }, + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "requires": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + } + }, + "parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + } + }, + "read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "requires": { + "@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" + } + }, + "semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true + }, + "type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true + }, + "unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true + } + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "requires": { + "sver-compat": "^1.5.0" + } + }, + "send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", + "dev": true + }, + "serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "requires": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + } + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" + }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "requires": { + "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" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "requires": { + "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" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "requires": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "dependencies": { + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + } + } + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "optional": true + }, + "simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "dev": true, + "optional": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "optional": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true, + "optional": true + } + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "sinon": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.1.tgz", + "integrity": "sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", + "supports-color": "^7.2.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, + "stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "requires": { + "bl": "^5.0.0" + }, + "dependencies": { + "bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "requires": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } + } + }, + "stream-buffers": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz", + "integrity": "sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==", + "dev": true + }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "streamx": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", + "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "requires": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + } + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "requires": { + "boundary": "^2.0.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "optional": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "tas-client": { + "version": "0.2.33", + "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.2.33.tgz", + "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" + }, + "terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "requires": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "requires": { + "editions": "^6.21.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "requires": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "dependencies": { + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "requires": {} + }, + "picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true + } + } + }, + "tmp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", + "dev": true + }, + "to-absolute-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-3.0.0.tgz", + "integrity": "sha512-loO/XEWTRqpfcpI7+Jr2RR2Umaaozx1t6OSVWtMi0oy5F/Fxg3IC+D/TToDnxyAGs7uZBGT/6XmyDUxgsObJXA==", + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", + "dev": true, + "requires": { + "through2": "^2.0.3" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "requires": {} + }, + "ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true + } + } + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dev": true, + "requires": { + "esbuild": "~0.25.0", + "fsevents": "~2.3.3", + "get-tsconfig": "^4.7.5" + }, + "dependencies": { + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + } + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + } + }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, + "typed-rest-client": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.6.tgz", + "integrity": "sha512-xcQpTEAJw2DP7GqVNECh4dD+riS+C1qndXLfBCJ3xk0kqprtGN491P5KlmrDbKdtuW8NEcP/5ChxiJI3S9WYTA==", + "dev": true, + "requires": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true + }, + "undertaker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + }, + "dependencies": { + "fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", + "dev": true + } + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", + "dev": true + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true + }, + "vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, + "vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", + "dev": true, + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + } + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", + "dev": true, + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "vscode-tas-client": { + "version": "0.1.84", + "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.1.84.tgz", + "integrity": "sha512-rUTrUopV+70hvx1hW5ebdw1nd6djxubkLvVxjGdyD/r5v/wcVF41LIfiAtbm5qLZDtQdsMH1IaCuDoluoIa88w==", + "requires": { + "tas-client": "0.2.33" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "requires": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "requires": {} + }, + "wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "requires": { + "is-wsl": "^3.1.0" + } + }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", + "dev": true + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + } + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/code/extensions/js-debug/package.json b/code/extensions/js-debug/package.json new file mode 100644 index 000000000000..e259a6f43e60 --- /dev/null +++ b/code/extensions/js-debug/package.json @@ -0,0 +1,165 @@ +{ + "name": "js-debug", + "displayName": "JavaScript Debugger", + "version": "1.117.0", + "publisher": "ms-vscode", + "author": { + "name": "Microsoft Corporation" + }, + "keywords": [ + "pwa", + "javascript", + "node", + "chrome", + "debugger" + ], + "description": "An extension for debugging Node.js programs and Chrome.", + "license": "MIT", + "engines": { + "vscode": "^1.80.0", + "node": ">=10" + }, + "icon": "resources/logo.png", + "categories": [ + "Debuggers" + ], + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-pwa.git" + }, + "bugs": { + "url": "https://github.com/Microsoft/vscode-pwa/issues" + }, + "scripts": { + "compile": "gulp", + "watch": "gulp watch", + "fmt": "dprint fmt", + "prepare": "husky install", + "package": "gulp package", + "publish": "gulp publish", + "precommit": "npm-run-all --parallel test:lint test:types", + "updatetypes": "cd src/typings && npx -y @vscode/dts dev && npx -y @vscode/dts master", + "updatenodeapi": "python src/build/getNodePdl.py && dprint fmt", + "generateapis": "tsx src/build/generateDap.ts && tsx src/build/generateCdp.ts && dprint fmt", + "test": "gulp && npm-run-all --parallel test:unit test:types test:golden test:lint", + "test:types": "tsc --noEmit", + "test:unit": "tsx node_modules/mocha/bin/mocha.js --config .mocharc.unit.js", + "test:golden": "node ./src/test/runTest.js", + "test:lint": "gulp lint" + }, + "dependencies": { + "@c4312/chromehash": "^0.3.1", + "@jridgewell/gen-mapping": "^0.3.13", + "@jridgewell/trace-mapping": "^0.3.31", + "@vscode/js-debug-browsers": "^1.1.2", + "@vscode/l10n": "^0.0.18", + "@vscode/win32-app-container-tokens": "^0.2.0", + "acorn": "^8.11.3", + "acorn-loose": "^8.4.0", + "astring": "^1.8.6", + "color": "^4.2.3", + "data-uri-to-buffer": "^6.0.1", + "default-browser": "^5.2.1", + "dotenv": "^16.4.1", + "eslint-visitor-keys": "^3.4.3", + "execa": "^5.1.1", + "glob-stream": "^8.0.0", + "got": "^11.8.6", + "inversify": "^6.0.2", + "js-xxhash": "^3.0.1", + "jsonc-parser": "^3.3.1", + "linkifyjs": "^4.3.2", + "micromatch": "^4.0.5", + "npm-run-all2": "^7.0.1", + "path-browserify": "^1.0.1", + "picomatch": "connor4312/picomatch#2fbe90b12eafa7dde816ff8c16be9e77271b0e0b", + "preact": "^10.19.3", + "reflect-metadata": "^0.2.1", + "signale": "^1.4.0", + "source-map-support": "^0.5.21", + "to-absolute-glob": "^3.0.0", + "vscode-tas-client": "^0.1.84", + "ws": "^8.17.1" + }, + "devDependencies": { + "@c4312/matcha": "^1.3.1", + "@pptr/testrunner": "^0.8.0", + "@types/chai": "^4.3.11", + "@types/chai-as-promised": "^7.1.8", + "@types/chai-string": "^1.4.5", + "@types/chai-subset": "^1.3.5", + "@types/color": "^3.0.6", + "@types/debug": "^4.1.12", + "@types/diff": "^5.0.9", + "@types/estree": "1.0.5", + "@types/express": "^4.17.21", + "@types/glob-stream": "^8.0.2", + "@types/gulp": "^4.0.17", + "@types/js-beautify": "^1.14.3", + "@types/json-schema": "^7.0.15", + "@types/linkifyjs": "^2.1.7", + "@types/long": "^4.0.2", + "@types/marked": "^5.0.2", + "@types/micromatch": "^4.0.6", + "@types/minimist": "^1.2.5", + "@types/mkdirp": "^1.0.2", + "@types/mocha": "^10.0.6", + "@types/node": "^20.11.11", + "@types/signale": "^1.4.7", + "@types/sinon": "^17.0.3", + "@types/stream-buffers": "^3.0.7", + "@types/tmp": "^0.2.6", + "@types/to-absolute-glob": "^2.0.3", + "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", + "@vscode/dwarf-debugging": "^0.0.2", + "@vscode/test-electron": "^2.4.1", + "chai": "^4.3.6", + "chai-as-promised": "^7.1.1", + "chai-string": "^1.5.0", + "chai-subset": "^1.6.0", + "diff": "^5.1.0", + "dprint": "^0.47.2", + "esbuild": "^0.25.0", + "eslint": "^8.56.0", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-react": "^7.33.2", + "express": "^4.22.1", + "glob": "^11.1.0", + "gulp": "^4.0.2", + "gulp-cli": "^2.3.0", + "gulp-rename": "^2.0.0", + "https-proxy-agent": "^7.0.4", + "husky": "^9.0.7", + "jszip": "^3.10.1", + "marked": "^11.2.0", + "merge2": "^1.4.1", + "minimist": "^1.2.8", + "mocha": "^10.8.2", + "mocha-junit-reporter": "^2.2.1", + "mocha-multi-reporters": "^1.5.1", + "nyc": "^15.1.0", + "sinon": "^17.0.1", + "stream-buffers": "^3.0.2", + "ts-node": "^10.9.2", + "tsx": "^4.20.3", + "typescript": "^5.5.2", + "@vscode/vsce": "^3.7.1" + }, + "main": "./src/extension.js", + "enabledApiProposals": [ + "portsAttributes", + "workspaceTrust", + "tunnels", + "browser" + ], + "extensionKind": [ + "workspace" + ], + "overrides": { + "serialize-javascript": ">=7.0.5", + "es5-ext": "npm:@unes/es5-ext@0.10.64-1" + } +} diff --git a/code/extensions/js-debug/package.nls.json b/code/extensions/js-debug/package.nls.json new file mode 100644 index 000000000000..d9f51caf6d98 --- /dev/null +++ b/code/extensions/js-debug/package.nls.json @@ -0,0 +1,240 @@ +{ + "add.eventListener.breakpoint": "Toggle Event Listener Breakpoints", + "add.xhr.breakpoint": "Add XHR/fetch Breakpoint", + "breakpoint.xhr.contains": "Break when URL contains:", + "breakpoint.xhr.any": "Any XHR/fetch", + "edit.xhr.breakpoint": "Edit XHR/fetch Breakpoint", + "attach.node.process": "Attach to Node Process", + "base.cascadeTerminateToConfigurations.label": "A list of debug sessions which, when this debug session is terminated, will also be stopped.", + "base.enableDWARF.label": "Toggles whether the debugger will try to read DWARF debug symbols from WebAssembly, which can be resource intensive. Requires the `ms-vscode.wasm-dwarf-debugging` extension to function.", + "browser.address.description": "IP address or hostname the debugged browser is listening on.", + "browser.attach.port.description": "Port to use to remote debugging the browser, given as `--remote-debugging-port` when launching the browser.", + "browser.baseUrl.description": "Base URL to resolve paths baseUrl. baseURL is trimmed when mapping URLs to the files on disk. Defaults to the launch URL domain.", + "browser.browserAttachLocation.description": "Forces the browser to attach in one location. In a remote workspace (through ssh or WSL, for example) this can be used to attach to a browser on the remote machine rather than locally.", + "browser.browserLaunchLocation.description": "Forces the browser to be launched in one location. In a remote workspace (through ssh or WSL, for example) this can be used to open the browser on the remote machine rather than locally.", + "browser.cleanUp.description": "What clean-up to do after the debugging session finishes. Close only the tab being debug, vs. close the whole browser.", + "browser.cwd.description": "Optional working directory for the runtime executable.", + "browser.disableNetworkCache.description": "Controls whether to skip the network cache for each request", + "browser.env.description": "Optional dictionary of environment key/value pairs for the browser.", + "browser.file.description": "A local html file to open in the browser", + "browser.includeDefaultArgs.description": "Whether default browser launch arguments (to disable features that may make debugging harder) will be included in the launch.", + "browser.includeLaunchArgs.description": "Advanced: whether any default launch/debugging arguments are set on the browser. The debugger will assume the browser will use pipe debugging such as that which is provided with `--remote-debugging-pipe`.", + "browser.inspectUri.description": "Format to use to rewrite the inspectUri: It's a template string that interpolates keys in `{curlyBraces}`. Available keys are:\n - `url.*` is the parsed address of the running application. For instance, `{url.port}`, `{url.hostname}`\n - `port` is the debug port that Chrome is listening on.\n - `browserInspectUri` is the inspector URI on the launched browser\n - `browserInspectUriPath` is the path part of the inspector URI on the launched browser (e.g.: \"/devtools/browser/e9ec0098-306e-472a-8133-5e42488929c2\").\n - `wsProtocol` is the hinted websocket protocol. This is set to `wss` if the original URL is `https`, or `ws` otherwise.\n", + "browser.launch.port.description": "Port for the browser to listen on. Defaults to \"0\", which will cause the browser to be debugged via pipes, which is generally more secure and should be chosen unless you need to attach to the browser from another tool.", + "browser.pathMapping.description": "A mapping of URLs/paths to local folders, to resolve scripts in the Browser to scripts on disk", + "browser.perScriptSourcemaps.description": "Whether scripts are loaded individually with unique sourcemaps containing the basename of the source file. This can be set to optimize sourcemap handling when dealing with lots of small scripts. If set to \"auto\", we'll detect known cases where this is appropriate.", + "browser.profileStartup.description": "If true, will start profiling soon as the process launches", + "browser.restart": "Whether to reconnect if the browser connection is closed", + "browser.revealPage": "Focus Tab", + "browser.runtimeArgs.description": "Optional arguments passed to the runtime executable.", + "browser.runtimeExecutable.description": "Either 'canary', 'stable', 'custom' or path to the browser executable. Custom means a custom wrapper, custom build or CHROME_PATH environment variable.", + "browser.runtimeExecutable.edge.description": "Either 'canary', 'stable', 'dev', 'custom' or path to the browser executable. Custom means a custom wrapper, custom build or EDGE_PATH environment variable.", + "browser.server.description": "Configures a web server to start up. Takes the same configuration as the 'node' launch task.", + "browser.skipFiles.description": "An array of file or folder names, or path globs, to skip when debugging. Star patterns and negations are allowed, for example, `[\"**/node_modules/**\", \"!**/node_modules/my-module/**\"]`", + "browser.smartStep.description": "Automatically step through unmapped lines in sourcemapped files. For example, code that TypeScript produces automatically when downcompiling async/await or other features.", + "browser.sourceMapPathOverrides.description": "A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk. See README for details.", + "browser.sourceMapRenames.description": "Whether to use the \"names\" mapping in sourcemaps. This requires requesting source content, which can be slow with certain debuggers.", + "browser.sourceMaps.description": "Use JavaScript source maps (if they exist).", + "browser.targetSelection": "Whether to attach to all targets that match the URL filter (\"automatic\") or ask to pick one (\"pick\").", + "browser.timeout.description": "Retry for this number of milliseconds to connect to the browser. Default is 10000 ms.", + "browser.url.description": "Will search for a tab with this exact url and attach to it, if found", + "browser.urlFilter.description": "Will search for a page with this url and attach to it, if found. Can have * wildcards.", + "browser.userDataDir.description": "By default, the browser is launched with a separate user profile in a temp folder. Use this option to override it. Set to false to launch with your default user profile. A new browser can't be launched if an instance is already running from `userDataDir`.", + "browser.vueComponentPaths": "A list of file glob patterns to find `*.vue` components. By default, searches the entire workspace. This needs to be specified due to extra lookups that Vue's sourcemaps require in Vue CLI 4. You can disable this special handling by setting this to an empty array.", + "browser.webRoot.description": "This specifies the workspace absolute path to the webserver root. Used to resolve paths like `/app.js` to files on disk. Shorthand for a pathMapping for \"/\"", + "chrome.attach.description": "Attach to an instance of Chrome already in debug mode", + "chrome.attach.label": "Chrome: Attach", + "chrome.label": "Web App (Chrome)", + "chrome.launch.description": "Launch Chrome to debug a URL", + "chrome.launch.label": "Chrome: Launch", + "editorBrowser.attach.description": "Attach to an open VS Code integrated browser", + "editorBrowser.attach.label": "Integrated Browser: Attach", + "editorBrowser.label": "Web App (Integrated Browser)", + "editorBrowser.launch.description": "Launch a VS Code integrated browser to debug a URL", + "editorBrowser.launch.label": "Integrated Browser: Launch", + "commands.callersAdd.label": "Exclude Caller", + "commands.callersAdd.paletteLabel": "Exclude caller from pausing in the current location", + "commands.callersGoToCaller.label": "Go to caller location", + "commands.callersGoToTarget.label": "Go to target location", + "commands.callersRemove.label": "Remove excluded caller", + "commands.callersRemoveAll.label": "Remove all excluded callers", + "commands.disableSourceMapStepping.label": "Disable Source Mapped Stepping", + "commands.enableSourceMapStepping.label": "Enable Source Mapped Stepping", + "configuration.autoAttachMode.always": "Auto attach to every Node.js process launched in the terminal.", + "configuration.autoAttachMode.disabled": "Auto attach is disabled and not shown in status bar.", + "configuration.autoAttachMode.explicit": "Only auto attach when the `--inspect` is given.", + "configuration.autoAttachMode.smart": "Auto attach when running scripts that aren't in a node_modules folder.", + "configuration.autoAttachMode": "Configures which processes to automatically attach and debug when `#debug.node.autoAttach#` is on. A Node process launched with the `--inspect` flag will always be attached to, regardless of this setting.", + "configuration.autoAttachSmartPatterns": "Configures glob patterns for determining when to attach in \"smart\" `#debug.javascript.autoAttachFilter#` mode. `$KNOWN_TOOLS$` is replaced with a list of names of common test and code runners. [Read more on the VS Code docs](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_auto-attach-smart-patterns).", + "configuration.automaticallyTunnelRemoteServer": "When debugging a remote web app, configures whether to automatically tunnel the remote server to your local machine.", + "configuration.breakOnConditionalError": "Whether to stop when conditional breakpoints throw an error.", + "configuration.debugByLinkOptions": "Options used when debugging open links clicked from inside the JavaScript Debug Terminal. Can be set to \"off\" to disable this behavior, or \"always\" to enable debugging in all terminals.", + "configuration.defaultRuntimeExecutables": "The default `runtimeExecutable` used for launch configurations, if unspecified. This can be used to config custom paths to Node.js or browser installations.", + "configuration.npmScriptLensLocation": "Where a \"Run\" and \"Debug\" code lens should be shown in your npm scripts. It may be on \"all\", scripts, on \"top\" of the script section, or \"never\".", + "configuration.pickAndAttachOptions": "Default options used when debugging a process through the `Debug: Attach to Node.js Process` command", + "configuration.resourceRequestOptions": "Request options to use when loading resources, such as source maps, in the debugger. You may need to configure this if your sourcemaps require authentication or use a self-signed certificate, for instance. Options are used to create a request using the [`got`](https://github.com/sindresorhus/got) library.\n\nA common case to disable certificate verification can be done by passing `{ \"https\": { \"rejectUnauthorized\": false } }`.", + "configuration.terminalOptions": "Default launch options for the JavaScript debug terminal and npm scripts.", + "configuration.unmapMissingSources": "Configures whether sourcemapped file where the original file can't be read will automatically be unmapped. If this is false (default), a prompt is shown.", + "configuration.enableNetworkView": "Enables the experimental network view for targets that support it.", + "createDiagnostics.label": "Diagnose Breakpoint Problems", + "customDescriptionGenerator.description": "Customize the textual description the debugger shows for objects (local variables, etc...). Samples:\n 1. this.toString() // will call toString to print all objects\n 2. this.customDescription ? this.customDescription() : defaultValue // Use customDescription method if available, if not return defaultValue\n 3. function (def) { return this.customDescription ? this.customDescription() : def } // Use customDescription method if available, if not return defaultValue\n ", + "customPropertiesGenerator.description": "Customize the properties shown for an object in the debugger (local variables, etc...). Samples:\n 1. { ...this, extraProperty: '12345' } // Add an extraProperty 12345 to all objects\n 2. this.customProperties ? this.customProperties() : this // Use customProperties method if available, if not use the properties in this (the default properties)\n 3. function () { return this.customProperties ? this.customProperties() : this } // Use customDescription method if available, if not return the default properties\n\n Deprecated: This is a temporary implementation of this feature until we have time to implement it in the way described here: https://github.com/microsoft/vscode/issues/102181", + "debug.npm.edit": "Edit package.json", + "debug.npm.noScripts": "No npm scripts found in your package.json", + "debug.npm.noWorkspaceFolder": "You need to open a workspace folder to debug npm scripts.", + "debug.npm.parseError": "Could not read {0}: {1}", + "debug.npm.script": "Debug npm Script", + "debug.terminal.attach": "Attach to Node.js Terminal Process", + "debug.terminal.label": "JavaScript Debug Terminal", + "debug.terminal.program.description": "Command to run in the launched terminal. If not provided, the terminal will open without launching a program.", + "debug.terminal.snippet.label": "Run \"npm start\" in a debug terminal", + "debug.terminal.toggleAuto": "Toggle Terminal Node.js Auto Attach", + "debug.terminal.welcome": { + "message": "[JavaScript Debug Terminal](command:extension.js-debug.createDebuggerTerminal)\n\nYou can use the JavaScript Debug Terminal to debug Node.js processes run on the command line.", + "comment": ["{Locked='](command:extension.js-debug.createDebuggerTerminal)'}"] + }, + "debug.terminal.welcomeWithLink": { + "message": "[JavaScript Debug Terminal](command:extension.js-debug.createDebuggerTerminal)\n\nYou can use the JavaScript Debug Terminal to debug Node.js processes run on the command line.\n\n[Debug URL](command:extension.js-debug.debugLink)", + "comment": [ + "{Locked='](command:extension.js-debug.createDebuggerTerminal)'}", + "{Locked='](command:extension.js-debug.debugLink)'}" + ] + }, + "debug.unverifiedBreakpoints": { + "message": "Some of your breakpoints could not be set. If you're having an issue, you can [troubleshoot your launch configuration](command:extension.js-debug.createDiagnostics).", + "comment": ["{Locked='](command:extension.js-debug.createDiagnostics)'}"] + }, + "debugLink.label": "Open Link", + "edge.address.description": "When debugging webviews, the IP address or hostname the webview is listening on. Will be automatically discovered if not set.", + "edge.attach.description": "Attach to an instance of Edge already in debug mode", + "edge.attach.label": "Edge: Attach", + "edge.label": "Web App (Edge)", + "edge.launch.description": "Launch Edge to debug a URL", + "edge.launch.label": "Edge: Launch", + "edge.port.description": "When debugging webviews, the port the webview debugger is listening on. Will be automatically discovered if not set.", + "edge.useWebView.attach.description": "An object containing the `pipeName` of a debug pipe for a UWP hosted Webview2. This is the \"MyTestSharedMemory\" when creating the pipe \"\\\\.\\pipe\\LOCAL\\MyTestSharedMemory\"", + "edge.useWebView.launch.description": "When 'true', the debugger will treat the runtime executable as a host application that contains a WebView allowing you to debug the WebView script content.", + "enableContentValidation.description": "Toggles whether we verify the contents of files on disk match the ones loaded in the runtime. This is useful in a variety of scenarios and required in some, but can cause issues if you have server-side transformation of scripts, for example.", + "errors.timeout": "{0}: timeout after {1}ms", + "extension.description": "An extension for debugging Node.js programs and Chrome.", + "extensionHost.label": "VS Code Extension Development", + "extensionHost.launch.config.name": "Launch Extension", + "extensionHost.launch.debugWebviews": "Configures whether we should try to attach to webviews in the launched VS Code instance. This will only work in desktop VS Code.", + "extensionHost.launch.debugWebWorkerHost": "Configures whether we should try to attach to the web worker extension host.", + "extensionHost.launch.env.description": "Environment variables passed to the extension host.", + "extensionHost.launch.rendererDebugOptions": "Chrome launch options used when attaching to the renderer process, with `debugWebviews` or `debugWebWorkerHost`.", + "extensionHost.launch.testConfiguration": "Path to a test configuration file for the [test CLI](https://code.visualstudio.com/api/working-with-extensions/testing-extension#quick-setup-the-test-cli).", + "extensionHost.launch.testConfigurationLabel": "A single configuration to run from the file. If not specified, you may be asked to pick.", + "extensionHost.launch.runtimeExecutable.description": "Absolute path to VS Code.", + "extensionHost.launch.stopOnEntry.description": "Automatically stop the extension host after launch.", + "extensionHost.snippet.launch.description": "Launch a VS Code extension in debug mode", + "extensionHost.snippet.launch.label": "VS Code Extension Development", + "getDiagnosticLogs.label": "Save Diagnostic JS Debug Logs", + "longPredictionWarning.disable": "Don't show again", + "longPredictionWarning.message": "It's taking a while to configure your breakpoints. You can speed this up by updating the 'outFiles' in your launch.json.", + "longPredictionWarning.noFolder": "No workspace folder open.", + "longPredictionWarning.open": "Open launch.json", + "node.address.description": "TCP/IP address of process to be debugged. Default is 'localhost'.", + "node.attach.attachExistingChildren.description": "Whether to attempt to attach to already-spawned child processes.", + "node.attach.attachSpawnedProcesses.description": "Whether to set environment variables in the attached process to track spawned children.", + "node.attach.config.name": "Attach", + "node.attach.continueOnAttach": "If true, we'll automatically resume programs launched and waiting on `--inspect-brk`", + "node.attach.processId.description": "ID of process to attach to.", + "node.attach.restart.description": "Try to reconnect to the program if we lose connection. If set to `true`, we'll try once a second, forever. You can customize the interval and maximum number of attempts by specifying the `delay` and `maxAttempts` in an object instead.", + "node.attachSimplePort.description": "If set, attaches to the process via the given port. This is generally no longer necessary for Node.js programs and loses the ability to debug child processes, but can be useful in more esoteric scenarios such as with Deno and Docker launches. If set to 0, a random port will be chosen and --inspect-brk added to the launch arguments automatically.", + "node.console.title": "Node Debug Console", + "node.disableOptimisticBPs.description": "Don't set breakpoints in any file until a sourcemap has been loaded for that file.", + "node.enableTurboSourcemaps.description": "Configures whether to use a new, faster mechanism for sourcemap discovery", + "node.killBehavior.description": "Configures how debug processes are killed when stopping the session. Can be:\n\n- forceful (default): forcefully tears down the process tree. Sends SIGKILL on posix, or `taskkill.exe /F` on Windows.\n- polite: gracefully tears down the process tree. It's possible that misbehaving processes continue to run after shutdown in this way. Sends SIGTERM on posix, or `taskkill.exe` with no `/F` (force) flag on Windows.\n- none: no termination will happen.", + "browser.killBehavior.description": "Configures how browser processes are killed when stopping the session with `cleanUp: wholeBrowser`. Can be:\n\n- forceful (default): forcefully tears down the process tree. Sends SIGKILL on posix, or `taskkill.exe /F` on Windows.\n- polite: gracefully tears down the process tree. It's possible that misbehaving processes continue to run after shutdown in this way. Sends SIGTERM on posix, or `taskkill.exe` with no `/F` (force) flag on Windows.\n- none: no termination will happen.", + "node.label": "Node.js", + "node.launch.args.description": "Command line arguments passed to the program.\n\nCan be an array of strings or a single string. When the program is launched in a terminal, setting this property to a single string will result in the arguments not being escaped for the shell.", + "node.launch.autoAttachChildProcesses.description": "Attach debugger to new child processes automatically.", + "node.launch.config.name": "Launch", + "node.launch.console.description": "Where to launch the debug target.", + "node.launch.console.externalTerminal.description": "External terminal that can be configured via user settings", + "node.launch.console.integratedTerminal.description": "VS Code's integrated terminal", + "node.launch.console.internalConsole.description": "VS Code Debug Console (which doesn't support to read input from a program)", + "node.launch.cwd.description": "Absolute path to the working directory of the program being debugged. If you've set localRoot then cwd will match that value otherwise it falls back to your workspaceFolder", + "node.launch.env.description": "Environment variables passed to the program. The value `null` removes the variable from the environment.", + "node.launch.envFile.description": "Absolute path to a file containing environment variable definitions.", + "node.launch.logging.cdp": "Path to the log file for Chrome DevTools Protocol messages", + "node.launch.logging.dap": "Path to the log file for Debug Adapter Protocol messages", + "node.launch.logging": "Logging configuration", + "node.launch.outputCapture.description": "From where to capture output messages: the default debug API if set to `console`, or stdout/stderr streams if set to `std`.", + "node.launch.program.description": "Absolute path to the program. Generated value is guessed by looking at package.json and opened files. Edit this attribute.", + "node.launch.restart.description": "Try to restart the program if it exits with a non-zero exit code.", + "node.launch.runtimeArgs.description": "Optional arguments passed to the runtime executable.", + "node.launch.runtimeExecutable.description": "Runtime to use. Either an absolute path or the name of a runtime available on the PATH. If omitted `node` is assumed.", + "node.launch.runtimeSourcemapPausePatterns": "A list of patterns at which to manually insert entrypoint breakpoints. This can be useful to give the debugger an opportunity to set breakpoints when using sourcemaps that don't exist or can't be detected before launch, such as [with the Serverless framework](https://github.com/microsoft/vscode-js-debug/issues/492).", + "node.launch.runtimeVersion.description": "Version of `node` runtime to use. Requires `nvm`.", + "node.launch.useWSL.deprecation": "'useWSL' is deprecated and support for it will be dropped. Use the 'Remote - WSL' extension instead.", + "node.launch.useWSL.description": "Use Windows Subsystem for Linux.", + "node.localRoot.description": "Path to the local directory containing the program.", + "node.pauseForSourceMap.description": "Whether to wait for source maps to load for each incoming script. This has a performance overhead, and might be safely disabled when running off of disk, so long as `rootPath` is not disabled.", + "node.port.description": "Debug port to attach to. Default is 9229.", + "node.processattach.config.name": "Attach to Process", + "node.profileStartup.description": "If true, will start profiling as soon as the process launches", + "node.remoteRoot.description": "Absolute path to the remote directory containing the program.", + "node.resolveSourceMapLocations.description": "A list of minimatch patterns for locations (folders and URLs) in which source maps can be used to resolve local files. This can be used to avoid incorrectly breaking in external source mapped code. Patterns can be prefixed with \"!\" to exclude them. May be set to an empty array or null to avoid restriction.", + "node.showAsyncStacks.description": "Show the async calls that led to the current call stack.", + "node.snippet.attach.description": "Attach to a running node program", + "node.snippet.attach.label": "Node.js: Attach", + "node.snippet.attachProcess.description": "Open process picker to select node process to attach to", + "node.snippet.attachProcess.label": "Node.js: Attach to Process", + "node.snippet.electron.description": "Debug the Electron main process", + "node.snippet.electron.label": "Node.js: Electron Main", + "node.snippet.gulp.description": "Debug gulp task (make sure to have a local gulp installed in your project)", + "node.snippet.gulp.label": "Node.js: Gulp task", + "node.snippet.launch.description": "Launch a node program in debug mode", + "node.snippet.launch.label": "Node.js: Launch Program", + "node.snippet.mocha.description": "Debug mocha tests", + "node.snippet.mocha.label": "Node.js: Mocha Tests", + "node.snippet.nodemon.description": "Use nodemon to relaunch a debug session on source changes", + "node.snippet.nodemon.label": "Node.js: Nodemon Setup", + "node.snippet.npm.description": "Launch a node program through an npm `debug` script", + "node.snippet.npm.label": "Node.js: Launch via npm", + "node.snippet.remoteattach.description": "Attach to the debug port of a remote node program", + "node.snippet.remoteattach.label": "Node.js: Attach to Remote Program", + "node.snippet.yo.description": "Debug yeoman generator (install by running `npm link` in project folder)", + "node.snippet.yo.label": "Node.js: Yeoman generator", + "node.sourceMapPathOverrides.description": "A set of mappings for rewriting the locations of source files from what the sourcemap says, to their locations on disk.", + "node.sourceMaps.description": "Use JavaScript source maps (if they exist).", + "node.stopOnEntry.description": "Automatically stop program after launch.", + "node.timeout.description": "Retry for this number of milliseconds to connect to Node.js. Default is 10000 ms.", + "node.versionHint.description": "Allows you to explicitly specify the Node version that's running, which can be used to disable or enable certain behaviors in cases where the automatic version detection does not work.", + "node.websocket.address.description": "Exact websocket address to attach to. If unspecified, it will be discovered from the address and port.", + "node.remote.host.header.description": "Explicit Host header to use when connecting to the websocket of inspector. If unspecified, the host header will be set to 'localhost'. This is useful when the inspector is running behind a proxy that only accept particular Host header.", + "node.experimentalNetworking.description": "Enable experimental inspection in Node.js. When set to `auto` this is enabled for versions of Node.js that support it. It can be set to `on` or `off` to enable or disable it explicitly.", + "openEdgeDevTools.label": "Open Browser Devtools", + "outFiles.description": "If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with `!` the files are excluded. If not specified, the generated code is expected in the same directory as its source.", + "pretty.print.script": "Pretty print for debugging", + "profile.start": "Take Performance Profile", + "profile.stop": "Stop Performance Profile", + "remove.eventListener.breakpoint.all": "Remove All Event Listener Breakpoints", + "remove.xhr.breakpoint.all": "Remove All XHR/fetch Breakpoints", + "remove.xhr.breakpoint": "Remove XHR/fetch Breakpoint", + "requestCDPProxy.label": "Request CDP Proxy for Debug Session", + "skipFiles.description": "An array of glob patterns for files to skip when debugging. The pattern `/**` matches all internal Node.js modules.", + "smartStep.description": "Automatically step through generated code that cannot be mapped back to the original source.", + "start.with.stop.on.entry": "Start Debugging and Stop on Entry", + "startWithStopOnEntry.label": "Start Debugging and Stop on Entry", + "timeouts.generalDescription.markdown": "Timeouts for several debugger operations.", + "timeouts.generalDescription": "Timeouts for several debugger operations.", + "timeouts.hoverEvaluation.description": "Time until value evaluation for hovered symbols is aborted. If set to 0, hover evaluation does never time out.", + "timeouts.sourceMaps.description": "Timeouts related to source maps operations.", + "timeouts.sourceMaps.sourceMapCumulativePause.description": "Extra time in milliseconds allowed per session to be spent waiting for source-maps to be processed, after the minimum time (sourceMapMinPause) has been exhausted", + "timeouts.sourceMaps.sourceMapMinPause.description": "Minimum time in milliseconds spent waiting for each source-map to be processed when a script is being parsed", + "toggle.skipping.this.file": "Toggle Skipping this File", + "trace.boolean.description": "Trace may be set to 'true' to write diagnostic logs to the disk.", + "trace.description": "Configures what diagnostic output is produced.", + "trace.logFile.description": "Configures where on disk logs are written.", + "trace.stdio.description": "Whether to return trace data from the launched application or browser.", + "workspaceTrust.description": "Trust is required to debug code in this workspace.", + "commands.networkViewRequest.label": "View Request as cURL", + "commands.networkOpenBody.label": "Open Response Body", + "commands.networkOpenBodyInHexEditor.label": "Open Response Body in Hex Editor", + "commands.networkReplayXHR.label": "Replay Request", + "commands.networkCopyURI.label": "Copy Request URL", + "commands.networkClear.label": "Clear Network Log" +} diff --git a/code/extensions/js-debug/resources/dark/configure.svg b/code/extensions/js-debug/resources/dark/configure.svg new file mode 100644 index 000000000000..f191b0ffe43b --- /dev/null +++ b/code/extensions/js-debug/resources/dark/configure.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/dark/connect.svg b/code/extensions/js-debug/resources/dark/connect.svg new file mode 100644 index 000000000000..8c7f5c499eae --- /dev/null +++ b/code/extensions/js-debug/resources/dark/connect.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/dark/disconnect.svg b/code/extensions/js-debug/resources/dark/disconnect.svg new file mode 100644 index 000000000000..71aae0dd887f --- /dev/null +++ b/code/extensions/js-debug/resources/dark/disconnect.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/dark/node.svg b/code/extensions/js-debug/resources/dark/node.svg new file mode 100644 index 000000000000..1f5011a60b72 --- /dev/null +++ b/code/extensions/js-debug/resources/dark/node.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/dark/open-file.svg b/code/extensions/js-debug/resources/dark/open-file.svg new file mode 100644 index 000000000000..ed302ae13984 --- /dev/null +++ b/code/extensions/js-debug/resources/dark/open-file.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/dark/page.svg b/code/extensions/js-debug/resources/dark/page.svg new file mode 100644 index 000000000000..4a16ab1d5072 --- /dev/null +++ b/code/extensions/js-debug/resources/dark/page.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/dark/pause.svg b/code/extensions/js-debug/resources/dark/pause.svg new file mode 100644 index 000000000000..718f66a400f4 --- /dev/null +++ b/code/extensions/js-debug/resources/dark/pause.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/dark/restart.svg b/code/extensions/js-debug/resources/dark/restart.svg new file mode 100644 index 000000000000..fc48916d5a64 --- /dev/null +++ b/code/extensions/js-debug/resources/dark/restart.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/dark/resume.svg b/code/extensions/js-debug/resources/dark/resume.svg new file mode 100644 index 000000000000..66f7f4f134ab --- /dev/null +++ b/code/extensions/js-debug/resources/dark/resume.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/dark/service-worker.svg b/code/extensions/js-debug/resources/dark/service-worker.svg new file mode 100644 index 000000000000..ee43cb5ccb35 --- /dev/null +++ b/code/extensions/js-debug/resources/dark/service-worker.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/dark/stop-profiling.svg b/code/extensions/js-debug/resources/dark/stop-profiling.svg new file mode 100644 index 000000000000..d2b90adadea5 --- /dev/null +++ b/code/extensions/js-debug/resources/dark/stop-profiling.svg @@ -0,0 +1,4 @@ + + + + diff --git a/code/extensions/js-debug/resources/dark/stop.svg b/code/extensions/js-debug/resources/dark/stop.svg new file mode 100644 index 000000000000..288d46cc73fe --- /dev/null +++ b/code/extensions/js-debug/resources/dark/stop.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/dark/worker.svg b/code/extensions/js-debug/resources/dark/worker.svg new file mode 100644 index 000000000000..f929ce19f32b --- /dev/null +++ b/code/extensions/js-debug/resources/dark/worker.svg @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/configure.svg b/code/extensions/js-debug/resources/light/configure.svg new file mode 100644 index 000000000000..2e7159f27c6a --- /dev/null +++ b/code/extensions/js-debug/resources/light/configure.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/connect.svg b/code/extensions/js-debug/resources/light/connect.svg new file mode 100644 index 000000000000..8a01b07bb6ac --- /dev/null +++ b/code/extensions/js-debug/resources/light/connect.svg @@ -0,0 +1,4 @@ + + + + diff --git a/code/extensions/js-debug/resources/light/disconnect.svg b/code/extensions/js-debug/resources/light/disconnect.svg new file mode 100644 index 000000000000..06fc4c31553e --- /dev/null +++ b/code/extensions/js-debug/resources/light/disconnect.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/light/node.svg b/code/extensions/js-debug/resources/light/node.svg new file mode 100644 index 000000000000..1f5011a60b72 --- /dev/null +++ b/code/extensions/js-debug/resources/light/node.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/open-file.svg b/code/extensions/js-debug/resources/light/open-file.svg new file mode 100644 index 000000000000..392a840c5ef6 --- /dev/null +++ b/code/extensions/js-debug/resources/light/open-file.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/light/page.svg b/code/extensions/js-debug/resources/light/page.svg new file mode 100644 index 000000000000..d7e8343f98ab --- /dev/null +++ b/code/extensions/js-debug/resources/light/page.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/pause.svg b/code/extensions/js-debug/resources/light/pause.svg new file mode 100644 index 000000000000..8fbe2d0460c8 --- /dev/null +++ b/code/extensions/js-debug/resources/light/pause.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/restart.svg b/code/extensions/js-debug/resources/light/restart.svg new file mode 100644 index 000000000000..4964d5bfaf19 --- /dev/null +++ b/code/extensions/js-debug/resources/light/restart.svg @@ -0,0 +1,3 @@ + + + diff --git a/code/extensions/js-debug/resources/light/resume.svg b/code/extensions/js-debug/resources/light/resume.svg new file mode 100644 index 000000000000..a5f970e7d29d --- /dev/null +++ b/code/extensions/js-debug/resources/light/resume.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/service-worker.svg b/code/extensions/js-debug/resources/light/service-worker.svg new file mode 100644 index 000000000000..20a861e2fd92 --- /dev/null +++ b/code/extensions/js-debug/resources/light/service-worker.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/stop.svg b/code/extensions/js-debug/resources/light/stop.svg new file mode 100644 index 000000000000..e1d92f084a32 --- /dev/null +++ b/code/extensions/js-debug/resources/light/stop.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/light/worker.svg b/code/extensions/js-debug/resources/light/worker.svg new file mode 100644 index 000000000000..0c4843378d4f --- /dev/null +++ b/code/extensions/js-debug/resources/light/worker.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/code/extensions/js-debug/resources/logo.png b/code/extensions/js-debug/resources/logo.png new file mode 100644 index 000000000000..f32611bef420 Binary files /dev/null and b/code/extensions/js-debug/resources/logo.png differ diff --git a/code/extensions/js-debug/resources/logo.svg b/code/extensions/js-debug/resources/logo.svg new file mode 100644 index 000000000000..618090d8a776 --- /dev/null +++ b/code/extensions/js-debug/resources/logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/resources/readme/auto-attach.png b/code/extensions/js-debug/resources/readme/auto-attach.png new file mode 100644 index 000000000000..05726e156944 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/auto-attach.png differ diff --git a/code/extensions/js-debug/resources/readme/conditional-exception-breakpoints.png b/code/extensions/js-debug/resources/readme/conditional-exception-breakpoints.png new file mode 100644 index 000000000000..150b14ded4c6 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/conditional-exception-breakpoints.png differ diff --git a/code/extensions/js-debug/resources/readme/exclude-caller.png b/code/extensions/js-debug/resources/readme/exclude-caller.png new file mode 100644 index 000000000000..c477b782ab86 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/exclude-caller.png differ diff --git a/code/extensions/js-debug/resources/readme/flame-chart.png b/code/extensions/js-debug/resources/readme/flame-chart.png new file mode 100644 index 000000000000..8efa34555f6e Binary files /dev/null and b/code/extensions/js-debug/resources/readme/flame-chart.png differ diff --git a/code/extensions/js-debug/resources/readme/instrumentation-breakpoints.png b/code/extensions/js-debug/resources/readme/instrumentation-breakpoints.png new file mode 100644 index 000000000000..af1d8bb0ddb5 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/instrumentation-breakpoints.png differ diff --git a/code/extensions/js-debug/resources/readme/instrumentation-breakpoints2.png b/code/extensions/js-debug/resources/readme/instrumentation-breakpoints2.png new file mode 100644 index 000000000000..48545577d42b Binary files /dev/null and b/code/extensions/js-debug/resources/readme/instrumentation-breakpoints2.png differ diff --git a/code/extensions/js-debug/resources/readme/logo-with-text.png b/code/extensions/js-debug/resources/readme/logo-with-text.png new file mode 100644 index 000000000000..0bbc7b870215 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/logo-with-text.png differ diff --git a/code/extensions/js-debug/resources/readme/network-view.png b/code/extensions/js-debug/resources/readme/network-view.png new file mode 100644 index 000000000000..0e3e5c0ab1d5 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/network-view.png differ diff --git a/code/extensions/js-debug/resources/readme/pretty-print.png b/code/extensions/js-debug/resources/readme/pretty-print.png new file mode 100644 index 000000000000..dbfeec5af48e Binary files /dev/null and b/code/extensions/js-debug/resources/readme/pretty-print.png differ diff --git a/code/extensions/js-debug/resources/readme/returnvalue.png b/code/extensions/js-debug/resources/readme/returnvalue.png new file mode 100644 index 000000000000..48a4cf6a408f Binary files /dev/null and b/code/extensions/js-debug/resources/readme/returnvalue.png differ diff --git a/code/extensions/js-debug/resources/readme/top-level-await.png b/code/extensions/js-debug/resources/readme/top-level-await.png new file mode 100644 index 000000000000..32b7ba832aa5 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/top-level-await.png differ diff --git a/code/extensions/js-debug/resources/readme/wasm-dwarf.png b/code/extensions/js-debug/resources/readme/wasm-dwarf.png new file mode 100644 index 000000000000..4b084d927352 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/wasm-dwarf.png differ diff --git a/code/extensions/js-debug/resources/readme/web-worker.png b/code/extensions/js-debug/resources/readme/web-worker.png new file mode 100644 index 000000000000..3e6808a2a916 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/web-worker.png differ diff --git a/code/extensions/js-debug/resources/readme/webview2.png b/code/extensions/js-debug/resources/readme/webview2.png new file mode 100644 index 000000000000..6e12f0b9a841 Binary files /dev/null and b/code/extensions/js-debug/resources/readme/webview2.png differ diff --git a/code/extensions/js-debug/src/adapter/asyncStackPolicy.ts b/code/extensions/js-debug/src/adapter/asyncStackPolicy.ts new file mode 100644 index 000000000000..4b119c41a91c --- /dev/null +++ b/code/extensions/js-debug/src/adapter/asyncStackPolicy.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../cdp/api'; +import { DisposableList, IDisposable, noOpDisposable } from '../common/disposable'; +import { EventEmitter } from '../common/events'; +import { AsyncStackMode } from '../configuration'; + +/** + * Controls when async stack traces are enabled in the debugee, either + * at start or only when we resolve a breakpoint. + * + * This is useful because requesting async stacktraces increases bookkeeping + * that V8 needs to do and can cause significant slowdowns. + */ +export interface IAsyncStackPolicy { + /** + * Installs the policy on the given CDP API. + */ + connect(cdp: Cdp.Api): Promise; +} + +const disabled: IAsyncStackPolicy = { connect: async () => noOpDisposable }; + +const eager = (maxDepth: number): IAsyncStackPolicy => ({ + async connect(cdp) { + await cdp.Debugger.setAsyncCallStackDepth({ maxDepth }); + return noOpDisposable; + }, +}); + +const onceBp = (maxDepth: number): IAsyncStackPolicy => { + const onEnable: EventEmitter | undefined = new EventEmitter(); + let enabled = false; + const tryEnable = () => { + if (!enabled) { + enabled = true; + onEnable.fire(); + } + }; + + return { + async connect(cdp) { + if (enabled) { + await cdp.Debugger.setAsyncCallStackDepth({ maxDepth }); + return noOpDisposable; + } + + const disposable = new DisposableList(); + + disposable.push( + // Another session enabled breakpoints. Turn this on as well, e.g. if + // we have a parent page and webworkers, when we debug the webworkers + // should also have their async stacks turned on. + onEnable.event(() => { + disposable.dispose(); + cdp.Debugger.setAsyncCallStackDepth({ maxDepth }); + }), + // when a breakpoint resolves, turn on stacks because we're likely to + // pause sooner or later + cdp.Debugger.on('breakpointResolved', tryEnable), + // start collecting on a pause event. This can be from source map + // instrumentation, entrypoint breakpoints, debugger statements, or user + // defined breakpoints. Instrumentation points happen all the time and + // can be ignored. For others, including entrypoint breaks (which + // indicate there's a user break somewhere in the file) we should turn on. + cdp.Debugger.on('paused', evt => { + if (evt.reason !== 'instrumentation') { + tryEnable(); + } + }), + ); + + return disposable; + }, + }; +}; + +const defaultPolicy = eager(32); + +export const getAsyncStackPolicy = (mode: AsyncStackMode) => { + if (mode === false) { + return disabled; + } + + if (mode === true) { + return defaultPolicy; + } + + if ('onAttach' in mode) { + return eager(mode.onAttach); + } + + if ('onceBreakpointResolved' in mode) { + return onceBp(mode.onceBreakpointResolved); + } + + return defaultPolicy; +}; diff --git a/code/extensions/js-debug/src/adapter/breakpointPredictor.ts b/code/extensions/js-debug/src/adapter/breakpointPredictor.ts new file mode 100644 index 000000000000..3c319cfa3677 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpointPredictor.ts @@ -0,0 +1,401 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { promises as fs } from 'fs'; +import { inject, injectable } from 'inversify'; +import * as path from 'path'; +import { Event } from 'vscode'; +import { EventEmitter } from '../common/events'; +import { OutFiles } from '../common/fileGlobList'; +import { ILogger, LogTag } from '../common/logging'; +import { fixDriveLetterAndSlashes } from '../common/pathUtils'; +import { ISourceMapMetadata } from '../common/sourceMaps/sourceMap'; +import { ISourceMapFactory } from '../common/sourceMaps/sourceMapFactory'; +import { ISearchStrategy, ISourcemapStreamOptions } from '../common/sourceMaps/sourceMapRepository'; +import { ISourcePathResolver } from '../common/sourcePathResolver'; +import { getOptimalCompiledPosition, parseSourceMappingUrl } from '../common/sourceUtils'; +import * as urlUtils from '../common/urlUtils'; +import { AnyLaunchConfiguration } from '../configuration'; +import Dap from '../dap/api'; +import { logPerf } from '../telemetry/performance'; + +export interface IWorkspaceLocation { + absolutePath: string; + lineNumber: number; // 1-based + columnNumber: number; // 1-based +} + +// Symbol we we use to keep the inline source map URL in DisoveredMetadata that +// we create in _this_ session. We have it as a symbol since we don't want to +// serialize it. +const InlineSourceMapUrl = Symbol('InlineSourceMapUrl'); + +type DiscoveredMetadata = Omit & { + sourceMapUrl: { [InlineSourceMapUrl]: string } | string; + sourceUrl: string; + resolvedPath: string; +}; +type MetadataMap = Map>; + +const longPredictionWarning = 10 * 1000; + +@injectable() +export class BreakpointPredictorCachedState { + private value: T | undefined; + private readonly path?: string; + + constructor(@inject(AnyLaunchConfiguration) launchConfig: AnyLaunchConfiguration) { + if (launchConfig.__workspaceCachePath) { + this.path = path.join(launchConfig.__workspaceCachePath, 'bp-predict.json'); + } + } + + public async load(): Promise { + if (this.value || !this.path) { + return this.value; + } + + try { + this.value = JSON.parse(await fs.readFile(this.path, 'utf-8')); + } catch { + // ignored + } + + return this.value; + } + + public async store(value: T) { + this.value = value; + if (this.path) { + await fs.mkdir(path.dirname(this.path), { recursive: true }); + await fs.writeFile(this.path, JSON.stringify(value)); + } + } +} + +@injectable() +export abstract class BreakpointSearch { + constructor( + @inject(OutFiles) private readonly outFiles: OutFiles, + @inject(ISearchStrategy) private readonly repo: ISearchStrategy, + @inject(ILogger) protected readonly logger: ILogger, + @inject(ISourceMapFactory) private readonly sourceMapFactory: ISourceMapFactory, + @inject(ISourcePathResolver) private readonly sourcePathResolver: + | ISourcePathResolver + | undefined, + @inject(BreakpointPredictorCachedState) private readonly state: BreakpointPredictorCachedState< + unknown + >, + ) {} + + public abstract getMetadataForPaths( + sourcePaths: readonly string[], + ): Promise<(Set | undefined)[]>; + + protected async createMapping( + opts?: Partial< + ISourcemapStreamOptions<{ discovered: DiscoveredMetadata[]; compiledPath: string }, void> + >, + ): Promise { + if (this.outFiles.empty) { + return new Map(); + } + + const sourcePathToCompiled: MetadataMap = urlUtils.caseNormalizedMap(); + const cachedState = await this.state.load(); + + try { + const { state } = await this.repo.streamChildrenWithSourcemaps< + { discovered: DiscoveredMetadata[]; compiledPath: string }, + void + >({ + files: this.outFiles, + processMap: async metadata => { + const discovered: DiscoveredMetadata[] = []; + const map = await this.sourceMapFactory.load(metadata); + for (const url of map.sources) { + if (url === null) { + continue; + } + + const resolvedPath = this.sourcePathResolver + ? await this.sourcePathResolver.urlToAbsolutePath({ url, map }) + : urlUtils.fileUrlToAbsolutePath(url); + + if (!resolvedPath) { + continue; + } + + discovered.push({ + ...metadata, + sourceMapUrl: urlUtils.isDataUri(metadata.sourceMapUrl) + ? { [InlineSourceMapUrl]: metadata.sourceMapUrl } + : metadata.sourceMapUrl, + resolvedPath, + sourceUrl: url, + }); + } + + return { discovered, compiledPath: fixDriveLetterAndSlashes(metadata.compiledPath) }; + }, + onProcessedMap: ({ discovered }) => { + for (const discovery of discovered) { + let set = sourcePathToCompiled.get(discovery.resolvedPath); + if (!set) { + set = new Set(); + sourcePathToCompiled.set(discovery.resolvedPath, set); + } + + set.add(discovery); + } + }, + lastState: cachedState, + ...opts, + }); + + // don't await, we can return early + if (state) { + this.state + .store(state) + .catch(e => + this.logger.warn(LogTag.RuntimeException, 'Error saving sourcemap cache', { + error: e, + }) + ); + } + } catch (error) { + this.logger.warn(LogTag.RuntimeException, 'Error reading sourcemaps from disk', { error }); + } + + return sourcePathToCompiled; + } +} + +@injectable() +export class GlobalBreakpointSearch extends BreakpointSearch { + private sourcePathToCompiled?: Promise; + + /** + * @inheritdoc + */ + public override async getMetadataForPaths(sourcePaths: readonly string[]) { + if (!this.sourcePathToCompiled) { + this.sourcePathToCompiled = this.createInitialMapping(); + } + + const sourcePathToCompiled = await this.sourcePathToCompiled; + return sourcePaths.map(p => sourcePathToCompiled.get(p)); + } + + private async createInitialMapping(): Promise { + return logPerf( + this.logger, + `BreakpointsPredictor.createInitialMapping`, + () => this.createMapping(), + ); + } +} + +/** + * Breakpoint search that only + */ +@injectable() +export class TargetedBreakpointSearch extends BreakpointSearch { + private readonly sourcePathToCompiled = new Map>(); + + /** + * @inheritdoc + */ + public override async getMetadataForPaths(sourcePaths: readonly string[]) { + const existing = sourcePaths.map(sp => this.sourcePathToCompiled.get(sp)); + const toFind = sourcePaths.map((_, i) => i).filter(i => !existing[i]); + + // if some paths have not been found yet, do one operation to find all of them + if (toFind.length) { + const spSet = new Set(toFind.map(i => fixDriveLetterAndSlashes(sourcePaths[i]))); + const entry = this.createMapping({ + filter: (_, meta) => !meta || meta.discovered.some(d => spSet.has(d.resolvedPath)), + }); + for (const i of toFind) { + this.sourcePathToCompiled.set(sourcePaths[i], entry); + existing[i] = entry; + } + } + + const r = await Promise.all( + existing.map(async (entry, i) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const map: MetadataMap = await entry!; + return map.get(sourcePaths[i]); + }), + ); + + return r; + } +} + +export const IBreakpointsPredictor = Symbol('IBreakpointsPredictor'); + +/** + * Determines ahead of time where to set breakpoints in the target files + * by looking at source maps on disk. + */ +export interface IBreakpointsPredictor { + /** + * Event emitted if a performance issue is detected parsing outFiles. + */ + onLongParse: Event; + + /** + * Gets prediction data for the given source file path, if it exists. + */ + getPredictionForSource(sourceFile: string): Promise | undefined>; + + /** + * Returns a promise that resolves when breakpoints for the given location + * are predicted. + */ + predictBreakpoints(params: Dap.SetBreakpointsParams): Promise; + + /** + * Returns predicted breakpoint locations for the provided source. + */ + predictedResolvedLocations(location: IWorkspaceLocation): IWorkspaceLocation[]; +} + +@injectable() +export class BreakpointsPredictor implements IBreakpointsPredictor { + private readonly predictedLocations = new Map(); + private readonly longParseEmitter = new EventEmitter(); + + /** + * If set, when asked for breakpoints for a path, the breakpoint predictor + * will not re-scan all files, but only look at new files or files where + * it's known a given source correlated to. + */ + public targetedMode = false; + + /** + * Event that fires if it takes a long time to predict sourcemaps. + */ + public readonly onLongParse = this.longParseEmitter.event; + + constructor( + @inject(BreakpointSearch) private readonly bpSearch: BreakpointSearch, + @inject(OutFiles) private readonly outFiles: OutFiles, + @inject(ILogger) private readonly logger: ILogger, + @inject(ISourceMapFactory) private readonly sourceMapFactory: ISourceMapFactory, + ) {} + /** + * Returns a promise that resolves when breakpoints for the given location + * are predicted. + */ + public async predictBreakpoints(params: Dap.SetBreakpointsParams): Promise { + if (!params.source.path || !params.breakpoints?.length) { + return; + } + + const topLevel = await this.getMetadataForPaths([params.source.path]).then(m => m[0]); + if (!topLevel) { + return; + } + + const addSourceMapLocations = async ( + line: number, + col: number, + metadata: DiscoveredMetadata, + ): Promise => { + const sourceMapUrl = typeof metadata.sourceMapUrl === 'string' + ? metadata.sourceMapUrl + : metadata.sourceMapUrl.hasOwnProperty(InlineSourceMapUrl) + ? metadata.sourceMapUrl[InlineSourceMapUrl] + : await fs.readFile(metadata.compiledPath, 'utf8').then(parseSourceMappingUrl); + + if (!sourceMapUrl) { + return []; + } + + const map = await this.sourceMapFactory.load({ ...metadata, sourceMapUrl }); + const entry = this.sourceMapFactory.guardSourceMapFn( + map, + () => + getOptimalCompiledPosition( + metadata.sourceUrl, + { + lineNumber: line, + columnNumber: col || 1, + }, + map, + ), + () => null, + ); + + if (!entry || entry.line === null) { + return []; + } + + const nested = await this.getMetadataForPaths([metadata.compiledPath]).then(m => m[0]); + if (nested) { + const n = await Promise.all( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + [...nested].map(n => addSourceMapLocations(entry.line!, entry.column!, n)), + ); + return n.flat(); + } + + return [ + { + absolutePath: metadata.compiledPath, + lineNumber: entry.line || 1, + columnNumber: entry.column ? entry.column + 1 : 1, + }, + ]; + }; + + for (const b of params.breakpoints ?? []) { + const key = `${params.source.path}:${b.line}:${b.column || 1}`; + if (this.predictedLocations.has(key)) { + return; + } + + const locations: IWorkspaceLocation[] = []; + this.predictedLocations.set(key, locations); + + for (const metadata of topLevel) { + locations.push(...(await addSourceMapLocations(b.line, b.column || 0, metadata))); + } + } + } + + /** + * @inheritdoc + */ + public async getPredictionForSource(sourcePath: string) { + return this.getMetadataForPaths([sourcePath]).then(m => m[0]); + } + + private async getMetadataForPaths(sourcePaths: readonly string[]) { + const warnLongRuntime = setTimeout(() => { + this.longParseEmitter.fire(); + this.logger.warn(LogTag.RuntimeSourceMap, 'Long breakpoint predictor runtime', { + longPredictionWarning, + patterns: [...this.outFiles.explode()].join(', '), + }); + }, longPredictionWarning); + + const result = await this.bpSearch.getMetadataForPaths(sourcePaths); + + clearTimeout(warnLongRuntime); + + return result; + } + + /** + * Returns predicted breakpoint locations for the provided source. + */ + public predictedResolvedLocations(location: IWorkspaceLocation): IWorkspaceLocation[] { + const key = `${location.absolutePath}:${location.lineNumber}:${location.columnNumber || 1}`; + return this.predictedLocations.get(key) ?? []; + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints.ts b/code/extensions/js-debug/src/adapter/breakpoints.ts new file mode 100644 index 000000000000..a86598ea1837 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints.ts @@ -0,0 +1,861 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import Cdp from '../cdp/api'; +import { ILogger, LogTag } from '../common/logging'; +import { bisectArrayAsync, flatten } from '../common/objUtils'; +import { fixDriveLetterAndSlashes } from '../common/pathUtils'; +import { IPosition } from '../common/positions'; +import { delay } from '../common/promiseUtil'; +import { SourceMap } from '../common/sourceMaps/sourceMap'; +import * as urlUtils from '../common/urlUtils'; +import { AnyLaunchConfiguration, IChromiumBaseConfiguration } from '../configuration'; +import Dap from '../dap/api'; +import { IDapApi } from '../dap/connection'; +import { ProtocolError } from '../dap/protocolError'; +import { BreakpointsStatisticsCalculator } from '../statistics/breakpointsStatistics'; +import { IBreakpointPathAndId } from '../targets/targets'; +import { logPerf } from '../telemetry/performance'; +import { IBreakpointsPredictor } from './breakpointPredictor'; +import { Breakpoint } from './breakpoints/breakpointBase'; +import { IBreakpointConditionFactory } from './breakpoints/conditions'; +import { EntryBreakpoint } from './breakpoints/entryBreakpoint'; +import { NeverResolvedBreakpoint } from './breakpoints/neverResolvedBreakpoint'; +import { PatternEntryBreakpoint } from './breakpoints/patternEntrypointBreakpoint'; +import { UserDefinedBreakpoint } from './breakpoints/userDefinedBreakpoint'; +import { DiagnosticToolSuggester } from './diagnosticToolSuggester'; +import { base0To1, base1To0, ISourceWithMap, isSourceWithMap, IUiLocation, Source } from './source'; +import { SourceContainer } from './sourceContainer'; +import { ScriptWithSourceMapHandler, Thread } from './threads'; + +/** + * Differential result used internally in setBreakpoints. + */ +interface ISetBreakpointResult { + /** + * Breakpoints that previous existed which can be destroyed. + */ + unbound: UserDefinedBreakpoint[]; + /** + * Newly created breakpoints; + */ + new: UserDefinedBreakpoint[]; + /** + * All old and new breakpoints. + */ + list: UserDefinedBreakpoint[]; +} + +const isSetAtEntry = (bp: Breakpoint) => + bp.originalPosition.columnNumber === 1 && bp.originalPosition.lineNumber === 1; + +const breakpointSetTimeout = 500; + +export type BreakpointEnableFilter = (breakpoint: Breakpoint) => boolean; + +const DontCompare = Symbol('DontCompare'); + +/** + * Determines the coarseness at which entry breakpoints are set. + * @see Thread._handleWebpackModuleEval for usage information. + */ +export const enum EntryBreakpointMode { + Exact, + Greedy, +} + +export interface IPossibleBreakLocation { + uiLocations: IUiLocation[]; + breakLocation: Cdp.Debugger.BreakLocation; +} + +@injectable() +export class BreakpointManager { + _dap: Dap.Api; + _sourceContainer: SourceContainer; + _thread: Thread | undefined; + _resolvedBreakpoints = new Map(); + _totalBreakpointsCount = 0; + _scriptSourceMapHandler: ScriptWithSourceMapHandler; + private _launchBlocker: Set> = new Set(); + private _predictorDisabledForTest = false; + private _breakpointsStatisticsCalculator = new BreakpointsStatisticsCalculator(); + private entryBreakpointMode: EntryBreakpointMode = EntryBreakpointMode.Exact; + + /** + * A filter function that enables/disables breakpoints. + */ + private _enabledFilter: BreakpointEnableFilter = () => true; + + /** + * User-defined breakpoints by their DAP ID. + */ + private readonly _byDapId = new Map(); + + /** + * User-defined breakpoints by path on disk. + */ + private _byPath: Map = urlUtils.caseNormalizedMap(); + + /** + * Returns user-defined breakpoints set by ref. + */ + public get appliedByPath(): ReadonlyMap { + return this._byPath; + } + + /** + * Object set once the source map handler is installed. Contains a promise + * that resolves to true/false based on whether a sourcemap instrumentation + * breakpoint (or equivalent) was able to be set. + */ + private _sourceMapHandlerInstalled?: { entryBpSet: Promise }; + + /** + * User-defined breakpoints by `sourceReference`. + */ + private _byRef: Map = new Map(); + + /** + * Returns user-defined breakpoints set by ref. + */ + public get appliedByRef(): ReadonlyMap { + return this._byRef; + } + + /** + * Mapping of source paths to entrypoint breakpoint IDs we set there. + */ + private readonly moduleEntryBreakpoints = urlUtils.caseNormalizedMap(); + + constructor( + @inject(IDapApi) dap: Dap.Api, + @inject(SourceContainer) sourceContainer: SourceContainer, + @inject(ILogger) public readonly logger: ILogger, + @inject(AnyLaunchConfiguration) private readonly launchConfig: AnyLaunchConfiguration, + @inject(IBreakpointConditionFactory) private readonly conditionFactory: + IBreakpointConditionFactory, + @inject(DiagnosticToolSuggester) private readonly suggester: DiagnosticToolSuggester, + @inject(IBreakpointsPredictor) public readonly _breakpointsPredictor?: IBreakpointsPredictor, + ) { + this._dap = dap; + this._sourceContainer = sourceContainer; + + _breakpointsPredictor?.onLongParse(() => dap.longPrediction({})); + + sourceContainer.onScript(script => { + script.source.then(source => { + const thread = this._thread; + if (thread) { + this._byRef + .get(source.sourceReference) + ?.forEach(bp => bp.updateForNewLocations(thread, script)); + } + }); + }); + + sourceContainer.onSourceMappedSteppingChange(() => { + if (this._thread) { + for (const bp of this._byDapId.values()) { + bp.refreshUiLocations(this._thread); + } + } + }); + + this._scriptSourceMapHandler = async (script, sources) => { + if ( + !logger.assert( + this._thread, + 'Expected thread to be set for the breakpoint source map handler', + ) + ) { + return []; + } + + const todo: Promise[] = []; + + // New script arrived, pointing to |sources| through a source map. + // We search for all breakpoints in |sources| and set them to this + // particular script. + const queue: Iterable[] = [sources]; + for (let i = 0; i < queue.length; i++) { + for (const source of queue[i]) { + const path = source.absolutePath; + const byPath = path ? this._byPath.get(path) : undefined; + for (const breakpoint of byPath || []) { + todo.push(breakpoint.updateForNewLocations(this._thread, script)); + } + const byRef = this._byRef.get(source.sourceReference); + for (const breakpoint of byRef || []) { + todo.push(breakpoint.updateForNewLocations(this._thread, script)); + } + + if (source.sourceMap) { + queue.push(source.sourceMap.sourceByUrl.values()); + } + } + } + + return flatten(await Promise.all(todo)); + }; + } + + /** + * Returns whether a breakpoint is set at the given UI location. + */ + public hasAtLocation(location: IUiLocation) { + const breakpointsAtPath = this._byPath.get(location.source.absolutePath) || []; + const breakpointsAtSource = this._byRef.get(location.source.sourceReference) || []; + return breakpointsAtPath + .concat(breakpointsAtSource) + .some( + bp => + bp.originalPosition.columnNumber === location.columnNumber + && bp.originalPosition.lineNumber === location.lineNumber, + ); + } + + /** + * Moves all breakpoints set in the `fromSource` to their corresponding + * location in the `toSource`, using the provided source map. Breakpoints + * are don't have a corresponding location won't be moved. + */ + public async moveBreakpoints( + thread: Thread, + fromSource: Source, + sourceMap: SourceMap, + toSource: Source, + ) { + const tryUpdateLocations = (breakpoints: UserDefinedBreakpoint[]) => + bisectArrayAsync(breakpoints, async bp => { + const gen = await this._sourceContainer.getOptiminalOriginalPosition( + sourceMap, + bp.originalPosition, + ); + if (!gen) { + return false; + } + + const base1 = gen.position.base1; + bp.updateSourceLocation( + thread, + { + path: toSource.absolutePath, + sourceReference: toSource.sourceReference, + }, + { lineNumber: base1.lineNumber, columnNumber: base1.columnNumber, source: toSource }, + ); + return false; + }); + + const fromPath = fromSource.absolutePath; + const toPath = toSource.absolutePath; + const byPath = fromPath ? this._byPath.get(fromPath) : undefined; + if (byPath && toPath) { + const [remaining, moved] = await tryUpdateLocations(byPath); + this._byPath.set(fromPath, remaining); + this._byPath.set(toPath, moved); + } + + const byRef = this._byRef.get(fromSource.sourceReference); + if (byRef) { + const [remaining, moved] = await tryUpdateLocations(byRef); + this._byRef.set(fromSource.sourceReference, remaining); + this._byRef.set(toSource.sourceReference, moved); + } + } + + /** + * Update the entry breakpoint mode. Returns a promise that resolves + * once all breakpoints are adjusted. + * @see Thread._handleWebpackModuleEval for usage information. + */ + public async updateEntryBreakpointMode(thread: Thread, mode: EntryBreakpointMode) { + if (mode === this.entryBreakpointMode) { + return; + } + + const previous = [...this.moduleEntryBreakpoints.values()]; + this.moduleEntryBreakpoints.clear(); + this.entryBreakpointMode = mode; + await Promise.all(previous.map(p => this.ensureModuleEntryBreakpoint(thread, p.source))); + } + + /** + * Adds and applies a filter to enable/disable breakpoints based on + * the predicate function. If a "compare" is provided, the filter will + * only be updated if the current filter matches the given one. + */ + public async applyEnabledFilter( + filter: BreakpointEnableFilter | undefined, + compare: BreakpointEnableFilter | typeof DontCompare = DontCompare, + ) { + if (compare !== DontCompare && this._enabledFilter !== compare) { + return; + } + + this._enabledFilter = filter || (() => true); + + const thread = this._thread; + if (!thread) { + return; + } + + await Promise.all( + [...this._byDapId.values()].map(bp => + this._enabledFilter(bp) ? bp.enable(thread) : bp.disable() + ), + ); + } + + /** + * Returns possible breakpoint locations for the given range. + */ + public async getBreakpointLocations( + thread: Thread, + source: Source, + start: IPosition, + end: IPosition, + ) { + const start1 = start.base1; + const end1 = end.base1; + const [startLocations, endLocations] = await Promise.all([ + this._sourceContainer.currentSiblingUiLocations({ + source, + lineNumber: start1.lineNumber, + columnNumber: start1.columnNumber, + }), + this._sourceContainer.currentSiblingUiLocations({ + source, + lineNumber: end1.lineNumber, + columnNumber: end1.columnNumber, + }), + ]); + + // As far as I know the number of start and end locations should be the + // same, log if this is not the case. + if (startLocations.length !== endLocations.length) { + this.logger.warn( + LogTag.Internal, + 'Expected to have the same number of start and end locations', + ); + return []; + } + + // For each viable location, attempt to identify its script ID and then ask + // Chrome for the breakpoints in the given range. For almost all scripts + // we'll only every find one viable location with a script. + const todo: Promise[] = []; + const result: IPossibleBreakLocation[] = []; + for (let i = 0; i < startLocations.length; i++) { + const start = startLocations[i]; + const end = endLocations[i]; + + if (start.source !== end.source) { + this.logger.warn( + LogTag.Internal, + 'Expected to have the same number of start and end scripts', + ); + continue; + } + + // Only take the last script that matches this source. The breakpoints + // are all coming from the same source code, so possible breakpoints + // at one location where this source is present should match every other. + const lsrc = start.source; + if (!lsrc.scripts.length) { + continue; + } + + const { scriptId } = lsrc.scripts[lsrc.scripts.length - 1]; + todo.push( + thread + .cdp() + .Debugger.getPossibleBreakpoints({ + restrictToFunction: false, + start: { scriptId, ...lsrc.offsetSourceToScript(base1To0(start)) }, + end: { scriptId, ...lsrc.offsetSourceToScript(base1To0(end)) }, + }) + .then(r => { + // locations can be undefined in Hermes, #1837 + if (!r?.locations) { + return; + } + + // Map the locations from CDP back to their original source positions. + // Discard any that map outside of the source we're interested in, + // which is possible (e.g. if a section of code from one source is + // inlined amongst the range we request). + return Promise.all( + r.locations.map(async breakLocation => { + const { lineNumber, columnNumber = 0 } = breakLocation; + const uiLocations = await this._sourceContainer.currentSiblingUiLocations({ + source: lsrc, + ...lsrc.offsetScriptToSource(base0To1({ lineNumber, columnNumber })), + }); + + result.push({ breakLocation, uiLocations }); + }), + ); + }), + ); + } + await Promise.all(todo); + + return result; + } + + /** + * Updates the thread the breakpoint manager is attached to. + */ + public setThread(thread: Thread) { + this._thread = thread; + this._thread.cdp().Debugger.on('breakpointResolved', async event => { + // Sometimes V8 says a breakpoint is unverified and then _immediately_ verifies + // it which, due to event ordering, can happen before before the reply to 'set' + // is processed. Try to find the breakpoint on the next microtask if that + // might have happened. + const breakpoint = this._resolvedBreakpoints.get(event.breakpointId) + || await delay(0).then(() => this._resolvedBreakpoints.get(event.breakpointId)); + if (breakpoint) { + breakpoint.updateUiLocations(thread, event.breakpointId, [event.location]); + } + }); + + this._thread.setSourceMapDisabler(breakpointIds => { + const sources: ISourceWithMap[] = []; + for (const id of breakpointIds) { + const breakpoint = this._resolvedBreakpoints.get(id); + if (breakpoint) { + const source = this._sourceContainer.source(breakpoint.source); + if (isSourceWithMap(source)) sources.push(source); + } + } + return sources; + }); + + for (const breakpoints of this._byPath.values()) { + breakpoints.forEach(b => this._setBreakpoint(b, thread)); + this.ensureModuleEntryBreakpoint(thread, breakpoints[0]?.source); + } + + for (const breakpoints of this._byRef.values()) { + breakpoints.forEach(b => this._setBreakpoint(b, thread)); + } + + if ( + 'runtimeSourcemapPausePatterns' in this.launchConfig + && this.launchConfig.runtimeSourcemapPausePatterns.length + ) { + this.setRuntimeSourcemapPausePatterns( + thread, + this.launchConfig.runtimeSourcemapPausePatterns, + ); // will update the launchblocker + } + + if (this._byDapId.size > 0) { + this._installSourceMapHandler(this._thread); + } + } + + /** + * Returns a promise that resolves when all breakpoints that can be set, + * have been set. The debugger waits on this to avoid running too early + * and missing breakpoints. + */ + public async launchBlocker(): Promise { + logPerf(this.logger, 'BreakpointManager.launchBlocker', async () => { + if (!this._predictorDisabledForTest) { + await Promise.all([...this._launchBlocker]); + } + }); + } + + private setRuntimeSourcemapPausePatterns(thread: Thread, patterns: ReadonlyArray) { + return Promise.all( + patterns.map(pattern => + this._setBreakpoint(new PatternEntryBreakpoint(this, pattern), thread) + ), + ); + } + + private addLaunchBlocker(...promises: ReadonlyArray>) { + for (const promise of promises) { + this._launchBlocker.add(promise); + promise.finally(() => this._launchBlocker.delete(promise)); + } + } + + setPredictorDisabledForTest(disabled: boolean) { + this._predictorDisabledForTest = disabled; + } + + private _installSourceMapHandler(thread: Thread) { + const perScriptSm = + (this.launchConfig as IChromiumBaseConfiguration).perScriptSourcemaps === 'yes'; + + let entryBpSet: Promise; + if (perScriptSm) { + entryBpSet = Promise.all([ + this.updateEntryBreakpointMode(thread, EntryBreakpointMode.Greedy), + thread.setScriptSourceMapHandler(false, this._scriptSourceMapHandler), + ]).then(() => true); + } else if (this._breakpointsPredictor && !this.launchConfig.pauseForSourceMap) { + entryBpSet = thread.setScriptSourceMapHandler(false, this._scriptSourceMapHandler); + } else { + entryBpSet = thread.setScriptSourceMapHandler(true, this._scriptSourceMapHandler); + } + this._sourceMapHandlerInstalled = { entryBpSet }; + } + + private async _uninstallSourceMapHandler(thread: Thread) { + thread.setScriptSourceMapHandler(false); + this._sourceMapHandlerInstalled = undefined; + } + + private _setBreakpoint(b: Breakpoint, thread: Thread): void { + if (!this._enabledFilter(b)) { + return; + } + + this.addLaunchBlocker(Promise.race([delay(breakpointSetTimeout), b.enable(thread)])); + } + + public async setBreakpoints( + params: Dap.SetBreakpointsParams, + ids: number[], + ): Promise { + if (!this._sourceMapHandlerInstalled && this._thread && params.breakpoints?.length) { + this._installSourceMapHandler(this._thread); + } + + const wasEntryBpSet = await this._sourceMapHandlerInstalled?.entryBpSet; + params.source.path = fixDriveLetterAndSlashes(params.source.path, true); + const containedSource = this._sourceContainer.source(params.source); + + // Wait until the breakpoint predictor finishes to be sure that we + // can place correctly in breakpoint.set(), if: + // 1) We don't have a instrumentation bp, which will be able + // to pause before we hit the breakpoint + // 2) We already have loaded the source at least once in the runtime. + // It's possible the source can be loaded again from a different script, + // but we'd prefer to verify the breakpoint ASAP. + if (!wasEntryBpSet && this._breakpointsPredictor && !containedSource) { + const promise = this._breakpointsPredictor.predictBreakpoints(params); + this.addLaunchBlocker(promise); + await promise; + } + + const thread = this._thread; + if (thread?.debuggerReady.hasSettled() === false) { + const promise = thread.debuggerReady.promise; + this.addLaunchBlocker(promise); + await promise; + } + + // Creates new breakpoints for the parameters, unsetting any previous + // breakpoints that don't still exist in the params. + const mergeInto = (previous: UserDefinedBreakpoint[]): ISetBreakpointResult => { + const result: ISetBreakpointResult = { unbound: previous.slice(), new: [], list: [] }; + if (!params.breakpoints) { + return result; + } + + for (let index = 0; index < params.breakpoints.length; index++) { + const bpParams = params.breakpoints[index]; + + let created: UserDefinedBreakpoint; + try { + created = new UserDefinedBreakpoint( + this, + ids[index], + params.source, + bpParams, + this.conditionFactory.getConditionFor(bpParams), + ); + } catch (e) { + if (!(e instanceof ProtocolError)) { + throw e; + } + + this._dap.output({ category: 'stderr', output: e.message }); + created = new NeverResolvedBreakpoint(this, ids[index], params.source, bpParams); + } + + const existingIndex = result.unbound.findIndex(p => p.equivalentTo(created)); + const existing = result.unbound[existingIndex]; + if (existing?.equivalentTo?.(created)) { + result.list.push(existing); + result.unbound.splice(existingIndex, 1); + } else { + result.new.push(created); + result.list.push(created); + this._byDapId.set(created.dapId, created); + } + } + + return result; + }; + + const getCurrent = () => + params.source.sourceReference + ? this._byRef.get(params.source.sourceReference) + : params.source.path + ? this._byPath.get(params.source.path) + : undefined; + + const result = mergeInto(getCurrent() ?? []); + if (params.source.sourceReference) { + this._byRef.set(params.source.sourceReference, result.list); + } else if (params.source.path) { + this._byPath.set(params.source.path, result.list); + } else { + return { breakpoints: [] }; + } + + // Ignore no-op breakpoint sets. These can come in from VS Code at the start + // of the session (if a file only has disabled breakpoints) and make it look + // like the user had removed all breakpoints they previously set, causing + // us to uninstall/re-install the SM handler repeatedly. + if (result.unbound.length === 0 && result.new.length === 0) { + return { breakpoints: [] }; + } + + // Cleanup existing breakpoints before setting new ones. + this._totalBreakpointsCount -= result.unbound.length; + await Promise.all( + result.unbound.map(b => { + this._byDapId.delete(b.dapId); + return b.disable(); + }), + ); + + this._totalBreakpointsCount += result.new.length; + + if (this._thread) { + if (this._totalBreakpointsCount === 0 && this._sourceMapHandlerInstalled) { + this._uninstallSourceMapHandler(this._thread); + } else if (this._totalBreakpointsCount > 0 && !this._sourceMapHandlerInstalled) { + this._installSourceMapHandler(this._thread); + } + } + + if (thread && result.new.length) { + // This will add itself to the launch blocker if needed: + this.ensureModuleEntryBreakpoint(thread, params.source); + + // double-checking the current list fixes: + // https://github.com/microsoft/vscode-js-debug/issues/679 + const currentList = getCurrent(); + const promise = Promise.all( + result.new + .filter(this._enabledFilter) + .filter(bp => currentList?.includes(bp)) + .map(b => b.enable(thread)), + ); + + this.addLaunchBlocker(Promise.race([delay(breakpointSetTimeout), promise])); + await promise; + } + + const dapBreakpoints = await Promise.all(result.list.map(b => b.toDap())); + this._breakpointsStatisticsCalculator.registerBreakpoints(dapBreakpoints); + + // In the next task after we send the response to the adapter, mark the + // breakpoints as having been set. + delay(0).then(() => result.new.forEach(bp => bp.markSetCompleted())); + + return { breakpoints: dapBreakpoints }; + } + + /** + * Emits a message on DAP notifying of a state update in this breakpoint. + */ + public async notifyBreakpointChange( + breakpoint: UserDefinedBreakpoint, + emitChange: boolean, + ): Promise { + // check if it was removed (#1406) + if (!this._byDapId.has(breakpoint.dapId)) { + return; + } + + const dap = await breakpoint.toDap(); + if (dap.verified) { + this._breakpointsStatisticsCalculator.registerResolvedBreakpoint(breakpoint.dapId); + this.suggester.notifyVerifiedBreakpoint(); + } + + if (emitChange) { + this._dap.breakpoint({ + reason: 'changed', + breakpoint: dap, + }); + } + } + + /** + * Returns whether any of the given breakpoints are an entrypoint breakpoint. + */ + public isEntrypointBreak( + hitBreakpointIds: ReadonlyArray, + scriptId: string, + ) { + // Fix: if we stopped in a script where an active entrypoint breakpoint + // exists, regardless of the reason, treat this as a breakpoint. + // ref: https://github.com/microsoft/vscode/issues/107859 + const entryInScript = [...this.moduleEntryBreakpoints.values()].some( + bp => bp.enabled && bp.cdpScriptIds.has(scriptId), + ); + + if (entryInScript) { + return true; + } + + return hitBreakpointIds.some(id => { + const bp = this._resolvedBreakpoints.get(id); + return bp && (bp instanceof EntryBreakpoint || isSetAtEntry(bp)); + }); + } + + /** + * Disables entrypoint breakpoints set in the given script ID. This is + * needed so they don't vote to continue later + * @see https://github.com/microsoft/vscode/issues/230201 + */ + public async disableEntrypointBreaks(scriptId: string) { + await Promise.all([...this.moduleEntryBreakpoints.values()].map(bp => { + if (bp.enabled && bp.cdpScriptIds.has(scriptId)) { + return bp.disable(); + } + })); + } + + /** Gets whether the CDP breakpoint ID refers to an entrypoint breakpoint. */ + public isEntrypointCdpBreak(cdpId: string) { + const bp = this._resolvedBreakpoints.get(cdpId); + return bp instanceof EntryBreakpoint; + } + + /** + * Handler that should be called *after* source map resolution on an entry + * breakpoint. Returns whether the debugger should remain paused. + */ + public async shouldPauseAt( + pausedEvent: Cdp.Debugger.PausedEvent, + hitBreakpointIds: ReadonlyArray, + delegateEntryBreak: IBreakpointPathAndId | undefined, + continueByDefault = false, + ) { + if (!hitBreakpointIds.length) { + return pausedEvent.reason !== 'instrumentation'; + } + + // To automatically continue, we need *no* breakpoints to want to pause and + // at least one breakpoint who wants to continue. See + // {@link HitCondition} for more details here. + let votesForPause = 0; + let votesForContinue = continueByDefault ? 1 : 0; + + await Promise.all( + hitBreakpointIds.map(async breakpointId => { + if (delegateEntryBreak?.cdpId === breakpointId) { + votesForPause++; + return; + } + + const breakpoint = this._resolvedBreakpoints.get(breakpointId); + if (breakpoint instanceof EntryBreakpoint) { + // we intentionally don't remove the record from the map; it's kept as + // an indicator that it did exist and was hit, so that if further + // breakpoints are set in the file it doesn't get re-applied. + if ( + this.entryBreakpointMode === EntryBreakpointMode.Exact + && !(breakpoint instanceof PatternEntryBreakpoint) + ) { + breakpoint.disable(); + } + votesForContinue++; + return; + } + + if (!(breakpoint instanceof UserDefinedBreakpoint)) { + return; + } + + if (await breakpoint.testHitCondition(pausedEvent)) { + votesForPause++; + } else { + votesForContinue++; + } + }), + ); + + return votesForPause > 0 || votesForContinue === 0; + } + + /** + * Registers that the given breakpoints were hit for statistics. + */ + public registerBreakpointsHit(hitBreakpointIds: ReadonlyArray) { + for (const breakpointId of hitBreakpointIds) { + const breakpoint = this._resolvedBreakpoints.get(breakpointId); + if (breakpoint instanceof UserDefinedBreakpoint) { + this._breakpointsStatisticsCalculator.registerBreakpointHit(breakpoint.dapId); + } + } + } + + public statisticsForTelemetry() { + return this._breakpointsStatisticsCalculator.statistics(); + } + + /** + * Ensures an entry breakpoint is present for the given source, creating + * one if there's not already one. + */ + private ensureModuleEntryBreakpoint(thread: Thread, source: Dap.Source) { + if (!source.path) { + return; + } + + // Don't apply a custom breakpoint here if the user already has one. + const byPath = this._byPath.get(source.path) ?? []; + if (byPath.some(isSetAtEntry)) { + return; + } + + const key = EntryBreakpoint.getModeKeyForSource(this.entryBreakpointMode, source.path); + if (!source.path || this.moduleEntryBreakpoints.has(key)) { + return; + } + + const bp = new EntryBreakpoint(this, source, this.entryBreakpointMode); + this.moduleEntryBreakpoints.set(source.path, bp); + this._setBreakpoint(bp, thread); + } + + /** + * Should be called when the execution context is cleared. Breakpoints set + * on a script ID will no longer be bound correctly. + */ + public executionContextWasCleared() { + for (const bp of this._byDapId.values()) { + bp.executionContextWasCleared(); + } + } + + /** + * Reapplies any currently-set user defined breakpoints. + */ + public async reapply() { + const all = [...this._byDapId.values()]; + await Promise.all(all.map(a => a.disable())); + if (this._thread) { + const thread = this._thread; + await Promise.all(all.map(a => a.enable(thread))); + } + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/breakpointBase.ts b/code/extensions/js-debug/src/adapter/breakpoints/breakpointBase.ts new file mode 100644 index 000000000000..e20db4493d08 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/breakpointBase.ts @@ -0,0 +1,716 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../cdp/api'; +import { LogTag } from '../../common/logging'; +import { IPosition } from '../../common/positions'; +import { absolutePathToFileUrl } from '../../common/urlUtils'; +import Dap from '../../dap/api'; +import { BreakpointManager } from '../breakpoints'; +import { base1To0, ISourceScript, IUiLocation, SourceFromMap } from '../source'; +import { Script, Thread } from '../threads'; + +export type LineColumn = { lineNumber: number; columnNumber: number }; // 1-based + +const lcEqual = (a: Partial, b: Partial) => + a.lineNumber === b.lineNumber && a.columnNumber === b.columnNumber; + +/** + * State of the IBreakpointCdpReference. + */ +export const enum CdpReferenceState { + // We're still working on the initial 'set breakpoint' request for this. + Pending, + // CDP has resolved this breakpoint to a source location locations. + Applied, +} + +type AnyCdpBreakpointArgs = + | Cdp.Debugger.SetBreakpointByUrlParams + | Cdp.Debugger.SetBreakpointParams; + +const isSetByUrl = ( + params: AnyCdpBreakpointArgs, +): params is Cdp.Debugger.SetBreakpointByUrlParams => !('location' in params); +const isSetByLocation = ( + params: AnyCdpBreakpointArgs, +): params is Cdp.Debugger.SetBreakpointParams => 'location' in params; + +const breakpointIsForUrl = (params: Cdp.Debugger.SetBreakpointByUrlParams, url: string) => + (params.url && url === params.url) + || (params.urlRegex && new RegExp(params.urlRegex).test(url)); + +/** + * We're currently working on sending the breakpoint to CDP. + */ +export interface IBreakpointCdpReferencePending { + state: CdpReferenceState.Pending; + // If deadletter is true, it indicates we want to invalidate this breakpoint; + // this will cause it to be unset as soon as it is applied. + deadletter: boolean; + // Promise that resolves to the 'applied' state once applied, or void if an + // error or deadletter occurred. + done: Promise; + // Arguments used to set the breakpoint. + args: AnyCdpBreakpointArgs; +} + +/** + * The breakpoint has been acknowledged by CDP and mapped to one or more locations. + */ +export interface IBreakpointCdpReferenceApplied { + state: CdpReferenceState.Applied; + // ID of the breakpoint on CDP. + cdpId: Cdp.Debugger.BreakpointId; + // Locations where CDP told us the breakpoint has bound. + locations: ReadonlyArray; + // Arguments used to set the breakpoint. + args: AnyCdpBreakpointArgs; + // A list of UI locations whether this breakpoint was resolved. + uiLocations: IUiLocation[]; +} + +/** + * An entry in the Breakpoint class that references a breakpoint in CDP/the + * debug target. A single "Breakpoint" class might resolve to + * multiple source locations, so there is a list of these. + */ +export type BreakpointCdpReference = + | IBreakpointCdpReferencePending + | Readonly; + +export abstract class Breakpoint { + protected isEnabled = false; + private readonly setInCdpScriptIds = new Set(); + + /** + * Returns script IDs whether this breakpoint has been resolved. + */ + public readonly cdpScriptIds: ReadonlySet = this.setInCdpScriptIds; + + /** + * Returns whether this breakpoint is enabled. + */ + public get enabled() { + return this.isEnabled; + } + + /** + * Gets all CDP breakpoint IDs under which this breakpoint currently exists. + */ + public get cdpIds(): ReadonlySet { + return this._cdpIds; + } + + /** + * A list of the CDP breakpoints that have been set from this one. Note that + * this can be set, only through {@link Breakpoint#updateCdpRefs} + */ + protected readonly cdpBreakpoints: ReadonlyArray = []; + + /** + * Gets the source the breakpoint is set in. + */ + public get source() { + return this._source; + } + + /** + * Gets the location where the breakpoint was originally set. + */ + public get originalPosition() { + return this._originalPosition; + } + + private _cdpIds = new Set(); + + /** + * @param manager - Associated breakpoint manager + * @param originalPosition - The position in the UI this breakpoint was placed at + * @param source - Source in which this breakpoint is placed + */ + constructor( + protected readonly _manager: BreakpointManager, + private _source: Dap.Source, + private _originalPosition: LineColumn, + ) {} + + /** + * Updates the source location for the breakpoint. It is assumed that the + * updated location is equivalent to the original one. This is used to move + * the breakpoints when we pretty print a source. This is dangerous with + * sharp edges, use with caution. + */ + public async updateSourceLocation(thread: Thread, source: Dap.Source, uiLocation: IUiLocation) { + this._source = source; + this._originalPosition = uiLocation; + + const todo: Promise[] = []; + for (const ref of this.cdpBreakpoints) { + if (ref.state === CdpReferenceState.Applied) { + todo.push(this.updateUiLocations(thread, ref.cdpId, ref.locations)); + } + } + + await Promise.all(todo); + } + + /** + * Refreshes the `uiLocations` of the breakpoint. Should be used when the + * resolution strategy for sources change. + */ + public async refreshUiLocations(thread: Thread) { + await Promise.all( + this.cdpBreakpoints + .filter( + (bp): bp is IBreakpointCdpReferenceApplied => bp.state === CdpReferenceState.Applied, + ) + .map(bp => this.updateUiLocations(thread, bp.cdpId, bp.locations)), + ); + } + + /** + * Sets the breakpoint in the provided thread and marks the "enabled" bit. + */ + public async enable(thread: Thread): Promise { + if (this.isEnabled) { + return; + } + + this.isEnabled = true; + const promises: Promise[] = [this._setPredicted(thread)]; + const source = this._manager._sourceContainer.source(this.source); + if (!source || !(source instanceof SourceFromMap)) { + promises.push( + // For breakpoints set before launch, we don't know whether they are in a compiled or + // a source map source. To make them work, we always set by url to not miss compiled. + // Additionally, if we have two sources with the same url, but different path (or no path), + // this will make breakpoint work in all of them. + this._setByPath( + thread, + source?.offsetSourceToScript(this.originalPosition) || this.originalPosition, + ), + ); + } + + await Promise.all(promises); + + // double check still enabled to avoid racing + if (source && this.isEnabled) { + const uiLocations = await this._manager._sourceContainer.currentSiblingUiLocations({ + lineNumber: this.originalPosition.lineNumber, + columnNumber: this.originalPosition.columnNumber, + source, + }); + + await Promise.all( + uiLocations.map(uiLocation => + this._setByUiLocation(thread, source.offsetSourceToScript(uiLocation)) + ), + ); + } + } + + /** + * Updates the breakpoint's locations in the UI. Should be called whenever + * a breakpoint set completes or a breakpointResolved event is received. + */ + public async updateUiLocations( + thread: Thread, + cdpId: Cdp.Debugger.BreakpointId, + resolvedLocations: readonly Cdp.Debugger.Location[], + ) { + // Update with the resolved locations immediately and synchronously. This + // prevents a race conditions where a source is parsed immediately before + // a breakpoint it hit and not returning correctly in `BreakpointManager.isEntrypointBreak`. + // This _can_ be fairly prevelant, especially when resolving UI locations + // involves loading or waiting for source maps. + this.updateExistingCdpRef(cdpId, bp => ({ ...bp, locations: resolvedLocations })); + + const uiLocation = ( + await Promise.all( + resolvedLocations.map(l => thread.rawLocationToUiLocation(thread.rawLocation(l))), + ) + ).find(l => !!l); + + if (!uiLocation) { + return; + } + + const source = this._manager._sourceContainer.source(this.source); + if (!source) { + return; + } + + const locations = await this._manager._sourceContainer.currentSiblingUiLocations(uiLocation); + + this.updateExistingCdpRef(cdpId, bp => { + const inPreferredSource = locations.filter(l => l.source === source); + return { + ...bp, + locations: resolvedLocations, + uiLocations: inPreferredSource.length ? inPreferredSource : locations, + }; + }); + } + + /** + * Compares this breakpoint with the other. String comparison-style return: + * - a negative number if this breakpoint is before the other one + * - zero if they're the same location + * - a positive number if this breakpoint is after the other one + */ + public compare(other: Breakpoint) { + const lca = this.originalPosition; + const lcb = other.originalPosition; + return lca.lineNumber !== lcb.lineNumber + ? lca.lineNumber - lcb.lineNumber + : lca.columnNumber - lcb.columnNumber; + } + + /** + * Removes the breakpoint from CDP and sets the "enabled" bit to false. + */ + public async disable(): Promise { + if (!this.isEnabled) { + return; + } + + this.isEnabled = false; + const promises: Promise[] = this.cdpBreakpoints.map(bp => + this.removeCdpBreakpoint(bp) + ); + await Promise.all(promises); + } + + /** + * Updates breakpoint placements in the debugee in responce to a new script + * getting parsed. This is useful in two cases: + * + * 1. Where the source was sourcemapped, in which case a new sourcemap tells + * us scripts to set BPs in. + * 2. Where a source was set by script ID, which happens for sourceReferenced + * sources. + */ + public async updateForNewLocations(thread: Thread, script: Script) { + const source = this._manager._sourceContainer.source(this.source); + if (!source) { + return []; + } + + // Find all locations for this breakpoint in the new script. + const uiLocations = await this._manager._sourceContainer.currentSiblingUiLocations( + { + lineNumber: this.originalPosition.lineNumber, + columnNumber: this.originalPosition.columnNumber, + source, + }, + await script.source, + ); + + if (!uiLocations.length) { + return []; + } + + const promises: Promise[] = []; + for (const uiLocation of uiLocations) { + promises.push( + this._setForSpecific(thread, script, source.offsetSourceToScript(uiLocation)), + ); + } + + // If we get a source map that references this script exact URL, then + // remove any URL-set breakpoints because they are probably not correct. + // This oft happens with Node.js loaders which rewrite sources on the fly. + for (const bp of this.cdpBreakpoints) { + if (!isSetByUrl(bp.args)) { + continue; + } + + if (!breakpointIsForUrl(bp.args, script.url)) { + continue; + } + + // Don't remove if we just set at the same location: https://github.com/microsoft/vscode/issues/102152 + const args = bp.args; + if ( + uiLocations.some( + l => l.columnNumber - 1 === args.columnNumber && l.lineNumber - 1 === args.lineNumber, + ) + ) { + continue; + } + + this._manager.logger.verbose( + LogTag.RuntimeSourceMap, + 'Adjusted breakpoint due to overlaid sourcemap', + { + url: source.url, + }, + ); + promises.push(this.removeCdpBreakpoint(bp)); + } + + await Promise.all(promises); + + return uiLocations; + } + + /** + * Should be called when the execution context is cleared. Breakpoints set + * on a script ID will no longer be bound correctly. + */ + public executionContextWasCleared() { + // only url-set breakpoints are still valid + this.updateCdpRefs(l => l.filter(bp => isSetByUrl(bp.args))); + } + + /** + * Gets whether this breakpoint has resolved to the given position. + */ + public hasResolvedAt(scriptId: string, position: IPosition) { + const { lineNumber, columnNumber } = position.base0; + + return this.cdpBreakpoints.some( + bp => + bp.state === CdpReferenceState.Applied + && bp.locations.some( + l => + l.scriptId === scriptId + && l.lineNumber === lineNumber + && (l.columnNumber === undefined || l.columnNumber === columnNumber), + ), + ); + } + + /** + * Gets the condition under which this breakpoint should be hit. + */ + protected getBreakCondition(): string | undefined { + return undefined; + } + + /** + * Updates an existing applied CDP breakpoint, by its CDP ID. + */ + protected updateExistingCdpRef( + cdpId: string, + mutator: (l: Readonly) => Readonly, + ) { + this.updateCdpRefs(list => + list.map(bp => + bp.state !== CdpReferenceState.Applied || bp.cdpId !== cdpId ? bp : mutator(bp) + ) + ); + } + + /** + * Updates the list of CDP breakpoint references. Used to provide lifecycle + * hooks to consumers and internal caches. + */ + protected updateCdpRefs( + mutator: (l: ReadonlyArray) => ReadonlyArray, + ) { + const cast = this as unknown as { cdpBreakpoints: ReadonlyArray }; + cast.cdpBreakpoints = mutator(this.cdpBreakpoints); + + const nextIdSet = new Set(); + this.setInCdpScriptIds.clear(); + + for (const bp of this.cdpBreakpoints) { + if (bp.state === CdpReferenceState.Applied) { + nextIdSet.add(bp.cdpId); + + for (const location of bp.locations) { + this.setInCdpScriptIds.add(location.scriptId); + } + } + } + + this._cdpIds = nextIdSet; + } + + protected async _setPredicted(thread: Thread): Promise { + if (!this.source.path || !this._manager._breakpointsPredictor) { + return; + } + + const workspaceLocations = this._manager._breakpointsPredictor.predictedResolvedLocations({ + absolutePath: this.source.path, + lineNumber: this.originalPosition.lineNumber, + columnNumber: this.originalPosition.columnNumber, + }); + + const promises: Promise[] = []; + for (const workspaceLocation of workspaceLocations) { + const re = this._manager._sourceContainer.sourcePathResolver.absolutePathToUrlRegexp( + workspaceLocation.absolutePath, + ); + if (re === undefined) { + continue; + } else if (typeof re === 'string') { + promises.push(this._setByUrlRegexp(thread, re, workspaceLocation)); + } else { + promises.push( + re.then(re => (re ? this._setByUrlRegexp(thread, re, workspaceLocation) : undefined)), + ); + } + } + + await Promise.all(promises); + } + + private async _setByUiLocation(thread: Thread, uiLocation: IUiLocation): Promise { + await Promise.all( + uiLocation.source.scripts.map(script => this._setForSpecific(thread, script, uiLocation)), + ); + } + + protected async _setByPath(thread: Thread, lineColumn: LineColumn): Promise { + const sourceByPath = this._manager._sourceContainer.source({ path: this.source.path }); + + // If the source has been mapped in-place, don't set anything by path, + // we'll depend only on the mapped locations. + if (sourceByPath instanceof SourceFromMap) { + const mappedInPlace = [...sourceByPath.compiledToSourceUrl.keys()].some( + s => s.absolutePath === this.source.path, + ); + + if (mappedInPlace) { + return; + } + } + + if (this.source.path) { + const urlRegexp = await this._manager._sourceContainer.sourcePathResolver + .absolutePathToUrlRegexp( + this.source.path, + ); + if (!urlRegexp) { + return; + } + + await this._setByUrlRegexp(thread, urlRegexp, lineColumn); + } else { + const source = this._manager._sourceContainer.source(this.source); + const url = source?.url; + + if (!url) { + return; + } + + await this._setByUrl(thread, url, lineColumn); + if (this.source.path !== url && this.source.path !== undefined) { + await this._setByUrl(thread, absolutePathToFileUrl(this.source.path), lineColumn); + } + } + } + + /** + * Returns whether a breakpoint has been set on the given line and column + * at the provided script already. This is used to deduplicate breakpoint + * requests to avoid triggering any logpoint breakpoints multiple times, + * as would happen if we set a breakpoint both by script and URL. + */ + protected hasSetOnLocation(script: ISourceScript, lineColumn: LineColumn) { + return this.cdpBreakpoints.find( + bp => + (script.scriptId + && isSetByLocation(bp.args) + && bp.args.location.scriptId === script.scriptId + && lcEqual(bp.args.location, lineColumn)) + || (script.url + && !script.hasSourceURL + && isSetByUrl(bp.args) + && (bp.args.urlRegex + ? new RegExp(bp.args.urlRegex).test(script.url) + : script.url === bp.args.url) + && lcEqual(bp.args, lineColumn)), + ); + } + + /** + * Returns whether a breakpoint has been set on the given line and column + * at the provided script by url regexp already. This is used to deduplicate breakpoint + * requests to avoid triggering any logpoint breakpoints multiple times. + */ + protected hasSetOnLocationByUrl(kind: 're' | 'url', input: string, lineColumn: LineColumn) { + return this.cdpBreakpoints.find(bp => { + if (isSetByUrl(bp.args)) { + if (!lcEqual(bp.args, lineColumn)) { + return false; + } + + if (kind === 'url') { + return bp.args.urlRegex + ? new RegExp(bp.args.urlRegex).test(input) + : bp.args.url === input; + } else { + return kind === 're' && bp.args.urlRegex === input; + } + } + + const script = this._manager._sourceContainer.getScriptById(bp.args.location.scriptId); + if (script) { + return lcEqual(bp.args.location, lineColumn) && kind === 're' + ? new RegExp(input).test(script.url) + : script.url === input; + } + + return undefined; + }); + } + + protected async _setForSpecific(thread: Thread, script: ISourceScript, lineColumn: LineColumn) { + // prefer to set on script URL for non-anonymous scripts, since url breakpoints + // will survive and be hit on reload. But don't set if the script has + // a source URL, since V8 doesn't resolve these + if ( + script.url + && !script.hasSourceURL + && (!script.embedderName || script.embedderName === script.url) + ) { + return this._setByUrl(thread, script.url, lineColumn); + } else { + return this._setByScriptId(thread, script, lineColumn); + } + } + + protected async _setByUrl(thread: Thread, url: string, lineColumn: LineColumn): Promise { + lineColumn = base1To0(lineColumn); + + const previous = this.hasSetOnLocationByUrl('url', url, lineColumn); + if (previous) { + if (previous.state === CdpReferenceState.Pending) { + await previous.done; + } + + return; + } + + return this._setAny(thread, { + url, + condition: this.getBreakCondition(), + ...lineColumn, + }); + } + + protected async _setByUrlRegexp( + thread: Thread, + urlRegex: string, + lineColumn: LineColumn, + ): Promise { + lineColumn = base1To0(lineColumn); + + const previous = this.hasSetOnLocationByUrl('re', urlRegex, lineColumn); + if (previous) { + if (previous.state === CdpReferenceState.Pending) { + await previous.done; + } + + return; + } + + return this._setAny(thread, { + urlRegex, + condition: this.getBreakCondition(), + ...lineColumn, + }); + } + + private async _setByScriptId( + thread: Thread, + script: ISourceScript, + lineColumn: LineColumn, + ): Promise { + lineColumn = base1To0(lineColumn); + + // Avoid setting duplicate breakpoints + const previous = this.hasSetOnLocation(script, lineColumn); + if (previous) { + if (previous.state === CdpReferenceState.Pending) { + await previous.done; + } + + return; + } + + return this._setAny(thread, { + condition: this.getBreakCondition(), + location: { + scriptId: script.scriptId, + ...lineColumn, + }, + }); + } + + /** + * Sets a breakpoint on the thread using the given set of arguments + * to Debugger.setBreakpoint or Debugger.setBreakpointByUrl. + */ + protected async _setAny(thread: Thread, args: AnyCdpBreakpointArgs) { + // If disabled while still setting, don't go on to try to set it and leak. + // If we're disabled after this point, we'll be recorded in the CDP refs + // list and will be deadlettered. + if (!this.isEnabled) { + return; + } + + const state: Partial = { + state: CdpReferenceState.Pending, + args, + deadletter: false, + }; + + state.done = (async () => { + const result = isSetByLocation(args) + ? await thread.cdp().Debugger.setBreakpoint(args) + : await thread.cdp().Debugger.setBreakpointByUrl(args); + if (!result) { + return; + } + + if (state.deadletter) { + await thread.cdp().Debugger.removeBreakpoint({ breakpointId: result.breakpointId }); + return; + } + + const locations = 'actualLocation' in result ? [result.actualLocation] : result.locations; + this._manager._resolvedBreakpoints.set(result.breakpointId, this); + + // Note that we add the record after calling breakpointResolved() + // to avoid duplicating locations. + const next: IBreakpointCdpReferenceApplied = { + state: CdpReferenceState.Applied, + cdpId: result.breakpointId, + args, + locations, + uiLocations: [], + }; + this.updateCdpRefs(list => list.map(r => (r === state ? next : r))); + await this.updateUiLocations(thread, result.breakpointId, locations); + return next; + })(); + + this.updateCdpRefs(list => [...list, state as IBreakpointCdpReferencePending]); + await state.done; + } + + /** + * Removes a CDP breakpoint attached to this one. Deadletters it if it + * hasn't been applied yet, deletes it immediately otherwise. + */ + private async removeCdpBreakpoint(breakpoint: BreakpointCdpReference) { + this.updateCdpRefs(bps => bps.filter(bp => bp !== breakpoint)); + if (breakpoint.state === CdpReferenceState.Pending) { + breakpoint.deadletter = true; + await breakpoint.done; + } else { + await this._manager._thread + ?.cdp() + .Debugger.removeBreakpoint({ breakpointId: breakpoint.cdpId }); + this._manager._resolvedBreakpoints.delete(breakpoint.cdpId); + } + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/conditions/expression.ts b/code/extensions/js-debug/src/adapter/breakpoints/conditions/expression.ts new file mode 100644 index 000000000000..92a273ef1586 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/conditions/expression.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../../cdp/api'; +import { getSyntaxErrorIn } from '../../../common/sourceUtils'; +import { Dap } from '../../../dap/api'; +import { invalidBreakPointCondition } from '../../../dap/errors'; +import { ProtocolError } from '../../../dap/protocolError'; +import { IEvaluator, PreparedCallFrameExpr } from '../../evaluator'; +import { IBreakpointCondition } from '.'; + +/** + * Conditional breakpoint using a user-defined expression. + */ +export class ExpressionCondition implements IBreakpointCondition { + public static parse( + params: Dap.SourceBreakpoint, + breakCondition: string, + breakOnError: boolean, + evaluator: IEvaluator, + ) { + breakCondition = wrapBreakCondition(breakCondition, breakOnError); + + const err = breakCondition && getSyntaxErrorIn(breakCondition); + if (err) { + throw new ProtocolError(invalidBreakPointCondition(params, err.message)); + } + + const { canEvaluateDirectly, invoke } = evaluator.prepare(breakCondition); + return new ExpressionCondition(canEvaluateDirectly ? breakCondition : invoke); + } + + private readonly invoke?: PreparedCallFrameExpr; + + /** @inheritdoc */ + public readonly breakCondition: string | undefined; + + constructor(breakCondition: string | PreparedCallFrameExpr) { + if (typeof breakCondition === 'function') { + this.invoke = breakCondition; + } else { + this.breakCondition = breakCondition; + } + } + + /** @inheritdoc */ + public async shouldStayPaused(details: Cdp.Debugger.PausedEvent) { + if (!this.invoke) { + return Promise.resolve(true); + } + + const evaluated = await this.invoke({ + callFrameId: details.callFrames[0].callFrameId, + returnByValue: true, + }); + + return evaluated?.result.value === true; + } +} + +export const wrapBreakCondition = (condition: string, breakOnError: boolean) => + `(()=>{try{return ${condition};}catch(e){console.error(\`Breakpoint condition error: \${e.message||e}\`);return ${!!breakOnError}}})()`; diff --git a/code/extensions/js-debug/src/adapter/breakpoints/conditions/hitCount.ts b/code/extensions/js-debug/src/adapter/breakpoints/conditions/hitCount.ts new file mode 100644 index 000000000000..2f9d6b0e5668 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/conditions/hitCount.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { invalidHitCondition } from '../../../dap/errors'; +import { ProtocolError } from '../../../dap/protocolError'; +import { IBreakpointCondition } from '.'; + +/** + * Regex used to match hit conditions. It matches the operator in group 1 and + * the constant in group 2. + */ +const hitConditionRe = /^(>|>=|={1,3}|<|<=|%)?\s*([0-9]+)$/; + +/** + * A hit condition breakpoint encapsulates the handling of breakpoints hit on + * a certain "nth" times we pause on them. For instance, a user could define + * a hit condition breakpoint to pause the second time we reach it. + * + * This is used and exposed by the {@link Breakpoint} class. + */ +export class HitCondition implements IBreakpointCondition { + private hits = 0; + public readonly breakCondition = undefined; + + constructor(private readonly predicate: (n: number) => boolean) {} + + /** + * @inheritdoc + */ + public shouldStayPaused() { + return Promise.resolve(this.predicate(++this.hits)); + } + + /** + * Parses the hit condition expression, like "> 42", into a {@link HitCondition}. + * @throws {ProtocolError} if the expression is invalid + */ + public static parse(expression: string): IBreakpointCondition { + const parts = hitConditionRe.exec(expression); + if (!parts) { + throw new ProtocolError(invalidHitCondition(expression)); + } + + const [, op = '=', valueStr] = parts; + return new HitCondition(makeTester(expression, op, Number(valueStr))); + } +} + +const makeTester = (expression: string, op: string, value: number): (n: number) => boolean => { + switch (op) { + case '=': + case '==': + case '===': + return n => n === value; + case '>': + return n => n > value; + case '>=': + return n => n >= value; + case '<': + return n => n < value; + case '<=': + return n => n <= value; + case '%': + return n => n % value === 0; + default: + throw new ProtocolError(invalidHitCondition(expression)); + } +}; diff --git a/code/extensions/js-debug/src/adapter/breakpoints/conditions/index.ts b/code/extensions/js-debug/src/adapter/breakpoints/conditions/index.ts new file mode 100644 index 000000000000..a0dba05b9a01 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/conditions/index.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import Cdp from '../../../cdp/api'; +import { AnyLaunchConfiguration } from '../../../configuration'; +import Dap from '../../../dap/api'; +import { IEvaluator } from '../../evaluator'; +import { ExpressionCondition } from './expression'; +import { HitCondition } from './hitCount'; +import { LogPointCompiler } from './logPoint'; +import { SimpleCondition } from './simple'; + +/** + * A condition provided to the {@link UserDefinedBreakpoint} + */ +export interface IBreakpointCondition { + /** + * Expression to evaluate that returns whether Chrome should paused on the + * breakpoint. + */ + readonly breakCondition: string | undefined; + + /** + * Called when Chrome pauses on a breakpoint returns whether the debugger + * should stay paused there. + */ + shouldStayPaused(details: Cdp.Debugger.PausedEvent): Promise; +} + +/** + * Condition that indicates we should always break at the give spot. + */ +export const AlwaysBreak = new SimpleCondition({ line: 0 }, undefined); + +/** + * Condition that indicates we should never break at the give spot. + */ +export const NeverBreak = new SimpleCondition({ line: 0 }, 'false'); + +/** + * Creates breakpoint conditions for source breakpoints. + */ +export interface IBreakpointConditionFactory { + /** + * Gets a condition for the given breakpoint. + */ + getConditionFor(params: Dap.SourceBreakpoint): IBreakpointCondition; +} + +export const IBreakpointConditionFactory = Symbol('IBreakpointConditionFactory'); + +@injectable() +export class BreakpointConditionFactory implements IBreakpointConditionFactory { + private breakOnError: boolean; + + constructor( + @inject(LogPointCompiler) private readonly logPointCompiler: LogPointCompiler, + @inject(IEvaluator) private readonly evaluator: IEvaluator, + @inject(AnyLaunchConfiguration) launchConfig: AnyLaunchConfiguration, + ) { + this.breakOnError = launchConfig.__breakOnConditionalError; + } + + public getConditionFor(params: Dap.SourceBreakpoint): IBreakpointCondition { + if (params.condition) { + return ExpressionCondition.parse( + params, + params.condition, + this.breakOnError, + this.evaluator, + ); + } + + if (params.logMessage) { + return this.logPointCompiler.compile(params, params.logMessage); + } + + if (params.hitCondition) { + return HitCondition.parse(params.hitCondition); + } + + return AlwaysBreak; + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/conditions/logPoint.ts b/code/extensions/js-debug/src/adapter/breakpoints/conditions/logPoint.ts new file mode 100644 index 000000000000..1189860aded1 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/conditions/logPoint.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { generate } from 'astring'; +import { createHash } from 'crypto'; +import { Statement } from 'estree'; +import { inject, injectable } from 'inversify'; +import { parseSource, returnErrorsFromStatements } from '../../../common/sourceCodeManipulations'; +import { getSyntaxErrorIn } from '../../../common/sourceUtils'; +import Dap from '../../../dap/api'; +import { invalidBreakPointCondition } from '../../../dap/errors'; +import { ProtocolError } from '../../../dap/protocolError'; +import { IEvaluator } from '../../evaluator'; +import { IBreakpointCondition } from '.'; +import { RuntimeLogPoint } from './runtimeLogPoint'; +import { SimpleCondition } from './simple'; + +/** + * Compiles log point expressions to breakpoints. + */ +@injectable() +export class LogPointCompiler { + constructor(@inject(IEvaluator) private readonly evaluator: IEvaluator) {} + + /** + * Compiles the log point to an IBreakpointCondition. + * @throws {ProtocolError} if the expression is invalid + */ + public compile(params: Dap.SourceBreakpoint, logMessage: string): IBreakpointCondition { + const expression = this.logMessageToExpression(logMessage); + const err = getSyntaxErrorIn(expression); + if (err) { + throw new ProtocolError(invalidBreakPointCondition(params, err.message)); + } + + const { canEvaluateDirectly, invoke } = this.evaluator.prepare(expression); + if (canEvaluateDirectly) { + return new SimpleCondition(params, this.logMessageToExpression(logMessage)); + } + + return new RuntimeLogPoint(invoke); + } + + private serializeLogStatements(statements: ReadonlyArray) { + return returnErrorsFromStatements([], statements, false); + } + + /** + * Converts the log message in the form of `hello {name}!` to an expression + * like `console.log('hello %O!', name);` (with some extra wrapping). This is + * used to implement logpoint breakpoints. + */ + private logMessageToExpression(msg: string) { + const unescape = (str: string) => str.replace(/%/g, '%%'); + const formatParts = []; + const args: string[] = []; + + let end = 0; + + // Parse each interpolated {code} in the message as a TS program. TS will + // parse the first {code} as a "Block", the first statement in the program. + // We want to reach to the end of that block and evaluate any code therein. + while (true) { + const start = msg.indexOf('{', end); + if (start === -1) { + formatParts.push(unescape(msg.slice(end))); + break; + } + + formatParts.push(unescape(msg.slice(end, start))); + + const [block] = parseSource(msg.slice(start)); + end = start + block.end; + + // unclosed or empty bracket is not valid, emit it as text + if (end - 1 === start + 1 || msg[end - 1] !== '}') { + formatParts.push(unescape(msg.slice(start, end))); + continue; + } + + if (block.type !== 'BlockStatement') { + break; + } + + // tranform property shortand `{{foo}}` to `{({foo})}`, reparse: + if (block.body.length === 1 && block.body[0].type === 'BlockStatement') { + block.body = parseSource(`(${msg.slice(start + 1, end - 2)}})`); + } + + args.push(generate(this.serializeLogStatements(block.body))); + formatParts.push('%O'); + } + + const evalArgs = [JSON.stringify(formatParts.join('')), ...args].join(', '); + const result = `console.log(${evalArgs}), false`; // false for #1191 + const hash = createHash('sha256').update(result).digest('hex').slice(0, 7); + + return result + `\n//# sourceURL=logpoint-${hash}.cdp`; + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/conditions/runtimeLogPoint.ts b/code/extensions/js-debug/src/adapter/breakpoints/conditions/runtimeLogPoint.ts new file mode 100644 index 000000000000..308b542c7019 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/conditions/runtimeLogPoint.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../../cdp/api'; +import { PreparedCallFrameExpr } from '../../evaluator'; +import { IBreakpointCondition } from '.'; + +/** + * A logpoint that requires being paused and running a custom expression to + * log correctly. + */ +export class RuntimeLogPoint implements IBreakpointCondition { + public readonly breakCondition = undefined; + + constructor(private readonly invoke: PreparedCallFrameExpr) {} + + public async shouldStayPaused(details: Cdp.Debugger.PausedEvent) { + await this.invoke({ callFrameId: details.callFrames[0].callFrameId }); + return false; + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/conditions/simple.ts b/code/extensions/js-debug/src/adapter/breakpoints/conditions/simple.ts new file mode 100644 index 000000000000..aff6befdef98 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/conditions/simple.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { getSyntaxErrorIn } from '../../../common/sourceUtils'; +import { Dap } from '../../../dap/api'; +import { invalidBreakPointCondition } from '../../../dap/errors'; +import { ProtocolError } from '../../../dap/protocolError'; +import { IBreakpointCondition } from '.'; + +/** + * Simple conditional breakpoint with an expression evaluated on the browser + * side of things. + */ +export class SimpleCondition implements IBreakpointCondition { + constructor(params: Dap.SourceBreakpoint, public readonly breakCondition: string | undefined) { + const err = breakCondition && getSyntaxErrorIn(breakCondition); + if (err) { + throw new ProtocolError(invalidBreakPointCondition(params, err.message)); + } + } + + public shouldStayPaused() { + return Promise.resolve(true); // if Chrome paused on us, it means the expression passed + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/entryBreakpoint.ts b/code/extensions/js-debug/src/adapter/breakpoints/entryBreakpoint.ts new file mode 100644 index 000000000000..dbf3bdb0342c --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/entryBreakpoint.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { basename, extname } from 'path'; +import { escapeRegexSpecialChars } from '../../common/stringUtils'; +import Dap from '../../dap/api'; +import { BreakpointManager, EntryBreakpointMode } from '../breakpoints'; +import { Thread } from '../threads'; +import { Breakpoint, LineColumn } from './breakpointBase'; + +/** + * A breakpoint set automatically on module entry. + */ +export class EntryBreakpoint extends Breakpoint { + public static getModeKeyForSource(mode: EntryBreakpointMode, path: string) { + return mode === EntryBreakpointMode.Greedy + ? basename(path, extname(path) || undefined) + : path; + } + + constructor( + manager: BreakpointManager, + source: Dap.Source, + private readonly mode: EntryBreakpointMode, + ) { + super(manager, source, { lineNumber: 1, columnNumber: 1 }); + } + + protected _setPredicted() { + return Promise.resolve(); + } + + protected _setByPath(thread: Thread, lineColumn: LineColumn) { + if (!this.source.path) { + return Promise.resolve(); + } + + const key = EntryBreakpoint.getModeKeyForSource(this.mode, this.source.path); + return this.mode === EntryBreakpointMode.Greedy + ? super._setByUrlRegexp(thread, escapeRegexSpecialChars(key), lineColumn) + : super._setByPath(thread, lineColumn); + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/neverResolvedBreakpoint.ts b/code/extensions/js-debug/src/adapter/breakpoints/neverResolvedBreakpoint.ts new file mode 100644 index 000000000000..e1c7d55e8bc4 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/neverResolvedBreakpoint.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Dap from '../../dap/api'; +import { BreakpointManager } from '../breakpoints'; +import { HitCondition } from './conditions/hitCount'; +import { UserDefinedBreakpoint } from './userDefinedBreakpoint'; + +/** + * A breakpoint that's never resolved or hit. This is used to place an invalid + * condition or hit count breakpoint; DAP does not have a representation for + * a single breakpoint failing to set, so on a failure we show an error as + * standard out and place one of these virtual breakpoints. + * + * In CDP they do end up being 'real' breakpoints so the aren't the most + * efficient construct, but they do the job without additional work or special + * casing. + */ +export class NeverResolvedBreakpoint extends UserDefinedBreakpoint { + constructor( + manager: BreakpointManager, + dapId: number, + source: Dap.Source, + dapParams: Dap.SourceBreakpoint, + ) { + super(manager, dapId, source, dapParams, new HitCondition(() => false)); + } + + /** + * @override + */ + protected getResolvedUiLocation() { + return undefined; + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/patternEntrypointBreakpoint.ts b/code/extensions/js-debug/src/adapter/breakpoints/patternEntrypointBreakpoint.ts new file mode 100644 index 000000000000..e1d4b9f24776 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/patternEntrypointBreakpoint.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { makeRe } from 'micromatch'; +import { forceForwardSlashes } from '../../common/pathUtils'; +import { BreakpointManager, EntryBreakpointMode } from '../breakpoints'; +import { Thread } from '../threads'; +import { EntryBreakpoint } from './entryBreakpoint'; + +/** + * A breakpoint set from the `runtimeSourcemapPausePatterns`. Unlike a normal + * entrypoint breakpoint, it's always applied from the "path" as its pattern. + */ +export class PatternEntryBreakpoint extends EntryBreakpoint { + constructor(manager: BreakpointManager, private readonly pattern: string) { + super(manager, { path: pattern }, EntryBreakpointMode.Greedy); + } + + /** + * @override + */ + public async enable(thread: Thread): Promise { + if (this.isEnabled) { + return; + } + + this.isEnabled = true; + const re = makeRe(forceForwardSlashes(this.pattern), { contains: true, lookbehinds: false }); + await this._setAny(thread, { + // fix case sensitivity on drive letter: + urlRegex: re.source.replace( + /([a-z]):/i, + (m, drive) => `[${drive.toLowerCase()}${drive.toUpperCase()}]:`, + ), + lineNumber: 0, + columnNumber: 0, + }); + } +} diff --git a/code/extensions/js-debug/src/adapter/breakpoints/userDefinedBreakpoint.ts b/code/extensions/js-debug/src/adapter/breakpoints/userDefinedBreakpoint.ts new file mode 100644 index 000000000000..f2b6458220a5 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/breakpoints/userDefinedBreakpoint.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import Cdp from '../../cdp/api'; +import { getDeferred } from '../../common/promiseUtil'; +import Dap from '../../dap/api'; +import { BreakpointManager } from '../breakpoints'; +import { IUiLocation } from '../source'; +import { Breakpoint, BreakpointCdpReference, CdpReferenceState } from './breakpointBase'; +import { IBreakpointCondition } from './conditions'; + +export class UserDefinedBreakpoint extends Breakpoint { + /** + * A deferred that resolves once the breakpoint 'set' response has been + * returned to the UI. We should wait for this to finish before sending any + * notifications about breakpoint changes. + */ + private readonly completedSet = getDeferred(); + + /** + * Last UI location this breakpoint announced to the wold. + */ + private lastAnnouncedUiLocation: IUiLocation | undefined; + + /** + * @param hitCondition - Hit condition for this breakpoint. See + * {@link HitCondition} for more information. + * @throws ProtocolError - if an invalid logpoint message is given + */ + constructor( + manager: BreakpointManager, + public readonly dapId: number, + source: Dap.Source, + private readonly dapParams: Dap.SourceBreakpoint, + private readonly condition: IBreakpointCondition, + ) { + super(manager, source, { lineNumber: dapParams.line, columnNumber: dapParams.column || 1 }); + } + + /** + * Returns whether this breakpoint is equivalent on DAP to the other one. + */ + public equivalentTo(other: UserDefinedBreakpoint) { + return ( + other.dapParams.column === this.dapParams.column + && other.dapParams.line === this.dapParams.line + && other.dapParams.hitCondition === this.dapParams.hitCondition + && other.dapParams.condition === this.dapParams.condition + && other.dapParams.logMessage === this.dapParams.logMessage + ); + } + + /** + * Returns a promise that resolves once the breakpoint 'set' response + */ + public untilSetCompleted() { + return this.completedSet.promise; + } + + /** + * Marks the breakpoint 'set' as having finished. + */ + public markSetCompleted() { + this.completedSet.resolve(); + } + + /** + * Returns whether the debugger should remain paused on this breakpoint + * according to the hit condition. + */ + public testHitCondition(event: Cdp.Debugger.PausedEvent) { + return this.condition.shouldStayPaused(event); + } + + /** + * Returns a DAP representation of the breakpoint. If the breakpoint is + * resolved, this will be fulfilled with the complete source location. + */ + public async toDap(): Promise { + const resolvedUiLocation = this.getResolvedUiLocation(); + this.lastAnnouncedUiLocation = resolvedUiLocation; + const location = this.enabled && resolvedUiLocation; + + if (location) { + return { + id: this.dapId, + verified: true, + source: await location.source.toDap(), + line: location.lineNumber, + column: location.columnNumber, + }; + } + + return { + id: this.dapId, + verified: false, + message: l10n.t('Unbound breakpoint'), // TODO: Put a useful message here + }; + } + + /** + * Returns information for the diagnostic dump. + */ + public diagnosticDump() { + return { + source: this.source, + params: this.dapParams, + cdp: this.cdpBreakpoints, + }; + } + + /** + * @override + */ + protected getBreakCondition() { + return this.condition.breakCondition; + } + + /** + * @override + */ + protected updateCdpRefs( + mutator: (l: ReadonlyArray) => ReadonlyArray, + ) { + super.updateCdpRefs(mutator); + + if (this.getResolvedUiLocation() !== this.lastAnnouncedUiLocation) { + this.notifyResolved(); + } + } + + /** + * Gets the location whether this breakpoint is resolved, if any. + */ + protected getResolvedUiLocation() { + for (const bp of this.cdpBreakpoints) { + if (bp.state === CdpReferenceState.Applied && bp.uiLocations.length) { + return bp.uiLocations[0]; + } + } + + return undefined; + } + + /** + * Called the breakpoint manager to notify that the breakpoint is resolved, + * used for statistics and notifying the UI. + */ + private async notifyResolved(): Promise { + await this.completedSet.promise; + await this._manager.notifyBreakpointChange(this, true); + } +} diff --git a/code/extensions/js-debug/src/adapter/cdpProxy.pdl b/code/extensions/js-debug/src/adapter/cdpProxy.pdl new file mode 100644 index 000000000000..f16f0e85c9ed --- /dev/null +++ b/code/extensions/js-debug/src/adapter/cdpProxy.pdl @@ -0,0 +1,12 @@ +version + major 1 + minor 0 + +experimental domain JsDebug + # Subscribes to the given CDP event(s). Events will not be sent through the + # connection unless you subscribe to them + command subscribe + parameters + # List of events to subscribe to. Supports wildcards, for example + # you can subscribe to `Debugger.scriptParsed` or `Debugger.*` + array of string events diff --git a/code/extensions/js-debug/src/adapter/cdpProxy.test.ts b/code/extensions/js-debug/src/adapter/cdpProxy.test.ts new file mode 100644 index 000000000000..5ba72bb30d0b --- /dev/null +++ b/code/extensions/js-debug/src/adapter/cdpProxy.test.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import Cdp from '../cdp/api'; +import Connection, { ProtocolError } from '../cdp/connection'; +import { NullTransport } from '../cdp/nullTransport'; +import { CdpProtocol } from '../cdp/protocol'; +import { WebSocketTransport } from '../cdp/webSocketTransport'; +import { NeverCancelled } from '../common/cancellation'; +import { Logger } from '../common/logging/logger'; +import { delay } from '../common/promiseUtil'; +import { NullTelemetryReporter } from '../telemetry/nullTelemetryReporter'; +import { CdpProxyProvider } from './cdpProxy'; +import { PortLeaseTracker } from './portLeaseTracker'; + +describe('CdpProxyProvider', () => { + let transport: NullTransport; + let provider: CdpProxyProvider; + let clientConn: Connection; + let client: Cdp.Api; + + beforeEach(async () => { + transport = new NullTransport(); + const cdp = new Connection(transport, Logger.null, new NullTelemetryReporter()); + provider = new CdpProxyProvider( + cdp.createSession('sesh'), + new PortLeaseTracker('local'), + Logger.null, + ); + + const addr = await provider.proxy(); + clientConn = new Connection( + await WebSocketTransport.create( + `ws://${addr.host}:${addr.port}${addr.path}`, + NeverCancelled, + ), + Logger.null, + new NullTelemetryReporter(), + ); + + client = clientConn.rootSession(); + }); + + afterEach(() => Promise.all([clientConn.close(), provider.dispose()])); + + it('round trips a request', async () => { + transport.onDidSendEmitter.event(async message => { + const cast = message as CdpProtocol.ICommand; + expect(cast.id).to.be.a('number'); + expect(cast.method).to.equal('Runtime.evaluate'); + expect(cast.params).to.deep.equal({ expression: 'hello!' }); + await delay(0); + transport.injectMessage({ + id: cast.id as number, + result: { ok: true }, + sessionId: message.sessionId, + }); + }); + + expect(await client.Runtime.evaluate({ expression: 'hello!' })).to.deep.equal({ + ok: true, + }); + }); + + it('bubbles errors', async () => { + transport.onDidSendEmitter.event(async message => { + await delay(0); + transport.injectMessage({ + id: message.id as number, + error: { code: 1234, message: 'something went wrong' }, + sessionId: message.sessionId, + }); + }); + + try { + await client.session.sendOrDie('Runtime.evaluate', { expression: 'hello!' }); + throw new Error('expected to reject'); + } catch (e) { + if (!(e instanceof ProtocolError)) { + throw e; + } + + expect(e.cause).to.deep.equal({ code: 1234, message: 'something went wrong' }); + } + }); + + it('deals with unknown method in JsDebug domain', async () => { + try { + await client.session.sendOrDie('JsDebug.constructor', {}); + throw new Error('expected to reject'); + } catch (e) { + if (!(e instanceof ProtocolError)) { + throw e; + } + + expect(e.cause).to.deep.equal({ code: -32601, message: 'JsDebug.constructor not found' }); + } + }); + + it('subscribes', async () => { + transport.onDidSendEmitter.event(async message => { + await delay(0); + [ + 'Runtime.consoleAPICalled', + 'Runtime.exceptionThrown', + 'Debugger.scriptParsed', + 'Animation.animationStarted', + ].forEach(method => + transport.injectMessage({ method, sessionId: message.sessionId, params: {} }) + ); + + transport.injectMessage({ + id: message.id as number, + result: { ok: true }, + sessionId: message.sessionId, + }); + }); + + const recv: string[] = []; + client.Runtime.on('consoleAPICalled', () => recv.push('Runtime.consoleAPICalled')); + client.Runtime.on('exceptionThrown', () => recv.push('Runtime.exceptionThrown')); + client.Debugger.on('scriptParsed', () => recv.push('Debugger.scriptParsed')); + client.Animation.on('animationStarted', () => recv.push('Animation.animationStarted')); + + await client.Runtime.evaluate({ expression: '' }); + expect(recv).to.be.empty; + + await client.JsDebug.subscribe({ + events: ['Debugger.*', 'Runtime.consoleAPICalled'], + }); + await client.session.sendOrDie('Runtime.evaluate', { expression: '' }); + expect(recv).to.deep.equal(['Runtime.consoleAPICalled', 'Debugger.scriptParsed']); + }); + + describe('replays', () => { + it('CSS', async () => { + transport.onDidSendEmitter.event(async message => { + await delay(0); + transport.injectMessage({ + id: message.id as number, + result: {}, + sessionId: message.sessionId, + }); + }); + + transport.injectMessage({ + method: 'CSS.styleSheetAdded', + params: { styleSheetId: '42' }, + sessionId: 'sesh', + }); + transport.injectMessage({ + method: 'CSS.styleSheetAdded', + params: { styleSheetId: '43' }, + sessionId: 'sesh', + }); + transport.injectMessage({ + method: 'CSS.styleSheetRemoved', + params: { styleSheetId: '43' }, + sessionId: 'sesh', + }); + + const events: unknown[] = []; + client.CSS.on('styleSheetAdded', evt => events.push(evt)); + client.CSS.on('styleSheetRemoved', evt => events.push(evt)); + + expect(await client.CSS.enable({})).to.deep.equal({}); + expect(events).to.deep.equal([{ styleSheetId: '42' }]); + }); + + it('caps replays', async () => { + transport.onDidSendEmitter.event(async message => { + await delay(0); + transport.injectMessage({ + id: message.id as number, + result: {}, + sessionId: message.sessionId, + }); + }); + + const events: Cdp.Runtime.RemoteObject[] = []; + client.Runtime.on('consoleAPICalled', evt => events.push(evt.args[0])); + + for (let i = 0; i < 1000; i++) { + transport.injectMessage({ + method: 'Runtime.consoleAPICalled', + params: { args: [{ objectId: String(i) }] }, + sessionId: 'sesh', + }); + } + + expect(await client.Runtime.enable({})).to.deep.equal({}); + expect(events.length).to.equal(50); + expect(events[0].objectId).to.equal('950'); + expect(events[events.length - 1].objectId).to.equal('999'); + }); + }); +}); diff --git a/code/extensions/js-debug/src/adapter/cdpProxy.ts b/code/extensions/js-debug/src/adapter/cdpProxy.ts new file mode 100644 index 000000000000..2b8484c01dc6 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/cdpProxy.ts @@ -0,0 +1,352 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { randomBytes } from 'crypto'; +import { inject, injectable } from 'inversify'; +import WebSocket from 'ws'; +import { Cdp } from '../cdp/api'; +import { ICdpApi, ProtocolError } from '../cdp/connection'; +import { CdpProtocol } from '../cdp/protocol'; +import { LinkedList } from '../common/datastructure/linkedList'; +import { DisposableList, IDisposable } from '../common/disposable'; +import { ILogger, LogTag } from '../common/logging'; +import Dap from '../dap/api'; +import { acquireTrackedWebSocketServer, IPortLeaseTracker } from './portLeaseTracker'; + +const jsDebugDomain = 'JsDebug'; +const eventWildcard = '*'; + +/** + * Method domain under the `jsDebugDomain`. + * @todo move to external protocol package + */ +interface IJsDebugDomain { + /** + * Subscribes to the given CDP event. Events will not be sent through the + * connection unless you subscribe to them. Supports wildcards, for example + * you can subscribe to `Debugger.scriptParsed` or `Debugger.*` + * @param event event to subscribe to + */ + subscribe(handle: ClientHandle, params: { events: string[] }): {}; +} + +// @see https://source.chromium.org/chromium/chromium/src/+/master:v8/third_party/inspector_protocol/crdtp/dispatch.h;drc=3573d5e0faf3098600993625b3f07b83f8753867 +const enum ProxyErrors { + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, + ServerError = -32000, +} + +/** + * Implementation for the adapter-side of a CDP proxy server. A proxy server + * is unique per debug session and target, and is therefore associated with + * a single CDP session. + * + * @see https://github.com/microsoft/vscode-js-debug/issues/893 + */ +export interface ICdpProxyProvider extends IDisposable { + /** + * Acquires the proxy server, and returns its address. + */ + proxy(): Promise; +} + +type ReplayMethod = { event: string; params: Record }; + +const enum CaptureBehavior { + Append, + CappedAppend, + Replace, +} + +type CaptureBehaviorParams = + | CaptureBehavior.Append + | CaptureBehavior.Replace + | { type: CaptureBehavior.CappedAppend; cap: number }; + +/** + * Handles replaying events from domains. Certain events are only fired when + * a domain is first enabled, so subsequent connections may not receive it. + */ +class DomainReplays { + private replays = new Map>(); + + /** + * Adds a message to be replayed. + */ + public addReplay(domain: keyof Cdp.Api, event: string, params: unknown) { + let ll = this.replays.get(domain); + if (!ll) { + ll = new LinkedList(); + this.replays.set(domain, ll); + } + + return ll.push({ event: `${domain}.${event}`, params: params as Record }); + } + + /** + * Captures replay for the event on CDP. + */ + public capture( + cdp: Cdp.Api, + domain: keyof Cdp.Api, + event: string, + behavior: CaptureBehaviorParams, + ) { + const handler = cdp[domain] as { + on(event: string, fn: (arg: Record) => void): void; + }; + + if (behavior === CaptureBehavior.Append) { + handler.on(event, args => this.addReplay(domain, event, args)); + } else if (behavior === CaptureBehavior.Replace) { + let rmPrevious: (() => void) | undefined; + handler.on(event, args => { + rmPrevious?.(); + rmPrevious = this.addReplay(domain, event, args); + }); + } else { + const rmQueue = new LinkedList<() => void>(); + handler.on(event, args => { + if (rmQueue.size === behavior.cap) { + rmQueue.shift()?.(); + } + rmQueue.push(this.addReplay(domain, event, args)); + }); + } + } + + /** + * Filters replayed events. + */ + public filter(domain: keyof Cdp.Api, filterFn: (r: ReplayMethod) => boolean) { + const ll = this.replays.get(domain); + if (!ll) { + return; + } + + ll.applyFilter(filterFn); + } + + /** + * Removes all of the event from the replay. + */ + public clearEvent(domain: TKey, event: string) { + const e = `${domain}.${event}`; + this.filter(domain, r => r.event !== e); + } + + /** + * Removes all replay info for a domain. + */ + public clearDomain(domain: keyof Cdp.Api) { + this.replays.delete(domain); + } + + /** + * Gets replay messages for the given domain. + */ + public read(domain: keyof Cdp.Api) { + return this.replays.get(domain) ?? []; + } +} + +export const ICdpProxyProvider = Symbol('ICdpProxyProvider'); + +/** + * Implementation of the {@link ICdpProxyProvider} + */ +@injectable() +export class CdpProxyProvider implements ICdpProxyProvider { + private server?: Promise<{ server: WebSocket.Server; path: string }>; + private readonly disposables = new DisposableList(); + private readonly replay = new DomainReplays(); + + private jsDebugApi: IJsDebugDomain = { + /** @inheritdoc */ + subscribe: (handle, { events }) => { + for (const event of events) { + if (event.endsWith(eventWildcard)) { + handle.pushDisposable( + this.cdp.session.onPrefix( + event.slice(0, -eventWildcard.length), + c => handle.send({ method: c.method, params: c.params }), + ), + ); + } else { + handle.pushDisposable( + this.cdp.session.on(event, params => handle.send({ method: event, params })), + ); + } + } + + return {}; + }, + }; + + constructor( + @inject(ICdpApi) private readonly cdp: Cdp.Api, + @inject(IPortLeaseTracker) private readonly portTracker: IPortLeaseTracker, + @inject(ILogger) private readonly logger: ILogger, + ) { + this.replay.capture(cdp, 'CSS', 'styleSheetAdded', CaptureBehavior.Append); + this.replay.capture(cdp, 'Debugger', 'paused', CaptureBehavior.Replace); + this.replay.capture(cdp, 'Runtime', 'executionContextCreated', { + cap: 50, + type: CaptureBehavior.CappedAppend, + }); + this.replay.capture(cdp, 'Runtime', 'consoleAPICalled', { + cap: 50, + type: CaptureBehavior.CappedAppend, + }); + cdp.Debugger.on('resumed', () => { + this.replay.clearEvent('Debugger', 'paused'); + }); + + cdp.CSS.on('fontsUpdated', evt => { + if (evt.font) { + this.replay.addReplay('CSS', 'fontsUpdated', evt); + } + }); + + cdp.CSS.on( + 'styleSheetRemoved', + evt => this.replay.filter('CSS', s => s.params.styleSheetId !== evt.styleSheetId), + ); + } + + /** + * Acquires the proxy server, and returns its address. + */ + public async proxy() { + if (!this.server) { + this.server = this.createServer(); + } + + const { server, path } = await this.server; + const addr = server.address() as WebSocket.AddressInfo; + return { host: addr.address, port: addr.port, path }; + } + + private async createServer() { + const path = `/${randomBytes(20).toString('hex')}`; + const server = await acquireTrackedWebSocketServer(this.portTracker, { + perMessageDeflate: true, + path, + }); + + this.logger.info(LogTag.ProxyActivity, 'activated cdp proxy'); + + server.on('connection', client => { + const clientHandle = new ClientHandle(client, this.logger); + this.logger.info(LogTag.ProxyActivity, 'accepted proxy connection', { + id: clientHandle.id, + }); + + client.on('close', () => { + this.logger.verbose(LogTag.ProxyActivity, 'closed proxy connection', { + id: clientHandle.id, + }); + this.disposables.disposeObject(clientHandle); + }); + + client.on('message', async d => { + let message: CdpProtocol.ICommand; + try { + message = JSON.parse(d.toString()); + } catch (e) { + return clientHandle.send({ + id: 0, + error: { code: ProxyErrors.ParseError, message: e.message }, + }); + } + + this.logger.verbose(LogTag.ProxyActivity, 'received proxy message', message); + + const { method, params, id = 0 } = message; + const [domain, fn] = method.split('.'); + try { + const result = domain === jsDebugDomain + ? await this.invokeJsDebugDomainMethod(clientHandle, fn, params) + : await this.invokeCdpMethod(clientHandle, domain, fn, params); + clientHandle.send({ id, result }); + } catch (e) { + const error = e instanceof ProtocolError && e.cause + ? e.cause + : { code: 0, message: e.message }; + clientHandle.send({ id, error }); + } + }); + }); + + return { server, path }; + } + + /** + * @inheritdoc + */ + public dispose() { + this.disposables.dispose(); + this.server?.then(s => s.server.close()); + this.server = undefined; + } + + private invokeCdpMethod(client: ClientHandle, domain: string, method: string, params: object) { + const promise = this.cdp.session.sendOrDie(`${domain}.${method}`, params); + switch (method) { + case 'enable': + for (const m of this.replay.read(domain as keyof Cdp.Api)) { + client.send({ method: m.event, params: m.params }); + } + break; + case 'disable': + this.replay.clearDomain(domain as keyof Cdp.Api); + break; + default: + // no-op + } + + // it's intentional that replay is sent before the + // enabled response; this is what Chrome does. + return promise; + } + + private invokeJsDebugDomainMethod(handle: ClientHandle, method: string, params: unknown) { + if (!this.jsDebugApi.hasOwnProperty(method)) { + throw new ProtocolError(method).setCause( + ProxyErrors.MethodNotFound, + `${jsDebugDomain}.${method} not found`, + ); + } + + type MethodMap = { [key: string]: (handle: ClientHandle, arg: unknown) => Promise }; + return (this.jsDebugApi as unknown as MethodMap)[method](handle, params); + } +} + +let connectionIdCounter = 0; + +class ClientHandle implements IDisposable { + private readonly disposables = new DisposableList(); + public readonly id = connectionIdCounter++; + + constructor(readonly webSocket: WebSocket, private readonly logger: ILogger) {} + + pushDisposable(d: IDisposable): void { + this.disposables.push(d); + } + + dispose() { + this.disposables.dispose(); + this.webSocket.close(); + } + + public send(message: CdpProtocol.Message) { + this.logger.verbose(LogTag.ProxyActivity, 'send proxy message', message); + this.webSocket.send(JSON.stringify(message)); + } +} diff --git a/code/extensions/js-debug/src/adapter/clientCapabilities.ts b/code/extensions/js-debug/src/adapter/clientCapabilities.ts new file mode 100644 index 000000000000..e5c65ca48494 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/clientCapabilities.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { injectable } from 'inversify'; +import Dap from '../dap/api'; + +export interface IClientCapabilies { + value?: Dap.InitializeParams; +} + +export const IClientCapabilies = Symbol('IClientCapabilies'); + +@injectable() +export class ClientCapabilities implements IClientCapabilies { + value?: Dap.InitializeParams | undefined; +} diff --git a/code/extensions/js-debug/src/adapter/completions.ts b/code/extensions/js-debug/src/adapter/completions.ts new file mode 100644 index 000000000000..2c30fb72a54e --- /dev/null +++ b/code/extensions/js-debug/src/adapter/completions.ts @@ -0,0 +1,444 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Node as AcornNode } from 'acorn'; +import { isDummy } from 'acorn-loose'; +import { Identifier, MemberExpression, Node, Program } from 'estree'; +import { inject, injectable } from 'inversify'; +import Cdp from '../cdp/api'; +import { ICdpApi } from '../cdp/connection'; +import { IPosition } from '../common/positions'; +import { + getEnd, + getStart, + getText, + parseProgram, + traverse, + VisitorOption, +} from '../common/sourceCodeManipulations'; +import { PositionToOffset } from '../common/stringUtils'; +import Dap from '../dap/api'; +import { IEvaluator, returnValueStr } from './evaluator'; +import { StackFrame } from './stackTrace'; +import { enumerateProperties, enumeratePropertiesTemplate } from './templates/enumerateProperties'; + +/** + * Context in which a completion is being evaluated. + */ +export interface ICompletionContext { + expression: string; + executionContextId: number | undefined; + stackFrame: StackFrame | undefined; +} + +/** + * A completion expresson to be evaluated. + */ +export interface ICompletionExpression { + expression: string; + position: IPosition; +} + +export interface ICompletionWithSort extends Dap.CompletionItem { + sortText: string; +} + +/** + * Completion kinds known to VS Code. This isn't formally restricted on the DAP. + * @see https://github.com/microsoft/vscode/blob/71eb6ad17eaf49a46fd176ca74a083001e17f7de/src/vs/editor/common/modes.ts#L329 + */ +export const enum CompletionKind { + Method = 'method', + Function = 'function', + Constructor = 'constructor', + Field = 'field', + Variable = 'variable', + Class = 'class', + Struct = 'struct', + Interface = 'interface', + Module = 'module', + Property = 'property', + Event = 'event', + Operator = 'operator', + Unit = 'unit', + Value = 'value', + Constant = 'constant', + Enum = 'enum', + EnumMember = 'enumMember', + Keyword = 'keyword', + Snippet = 'snippet', + Text = 'text', + Color = 'color', + File = 'file', + Reference = 'reference', + Customcolor = 'customcolor', + Folder = 'folder', + Type = 'type', + TypeParameter = 'typeParameter', +} + +/** + * Tries to infer the completion kind for the given Acorn node. + */ +const inferCompletionInfoForDeclaration = (node: Node) => { + switch (node.type) { + case 'ClassDeclaration': + case 'ClassExpression': + return { type: CompletionKind.Class, id: node.id }; + case 'MethodDefinition': + return { + type: node.key?.type === 'Identifier' && node.key.name === 'constructor' + ? CompletionKind.Constructor + : CompletionKind.Method, + id: node.key, + }; + case 'VariableDeclarator': + return { + type: + node.init?.type === 'FunctionExpression' || node.init?.type === 'ArrowFunctionExpression' + ? CompletionKind.Function + : CompletionKind.Variable, + id: node.id, + }; + } +}; + +function maybeHasSideEffects(node: Node): boolean { + let result = false; + traverse(node, { + enter(node) { + if ( + node.type === 'CallExpression' + || node.type === 'NewExpression' + || (node.type === 'UnaryExpression' && node.operator === 'delete') + || node.type === 'ClassBody' + ) { + result = true; + return VisitorOption.Break; + } + }, + }); + + return result; +} + +export const ICompletions = Symbol('ICompletions'); + +/** + * Gets autocompletion results for an expression. + */ +export interface ICompletions { + completions(options: ICompletionContext & ICompletionExpression): Promise; +} + +/** + * Provides REPL completions for the debug session. + */ +@injectable() +export class Completions { + constructor( + @inject(IEvaluator) private readonly evaluator: IEvaluator, + @inject(ICdpApi) private readonly cdp: Cdp.Api, + ) {} + + public async completions( + options: ICompletionContext & ICompletionExpression, + ): Promise { + const source = parseProgram(options.expression); + const offset = new PositionToOffset(options.expression).convert(options.position); + let candidate: () => Promise = () => Promise.resolve([]); + + traverse(source, { + enter: (node, parent) => { + const asAcorn = node as AcornNode; + if (asAcorn.start < offset && offset <= asAcorn.end) { + if ( + node.type === 'MemberExpression' + || (node.type === 'Identifier' + && parent?.type === 'MemberExpression' + && !parent.computed + && parent.object !== node) + ) { + const memberExpression = node.type === 'MemberExpression' + ? node + : (parent as MemberExpression); + candidate = memberExpression.computed + ? () => this.elementAccessCompleter(options, memberExpression, offset) + : () => this.propertyAccessCompleter(options, memberExpression, offset); + } else if (node.type === 'Identifier') { + candidate = () => this.identifierCompleter(options, source, node, offset); + } + parent = node; + } + }, + }); + + return candidate().then(v => v.sort((a, b) => (a.sortText > b.sortText ? 1 : -1))); + } + + /** + * Completer for a TS element access, via bracket syntax. + */ + private async elementAccessCompleter( + options: ICompletionContext, + node: MemberExpression, + offset: number, + ) { + if (node.property.type !== 'Literal' || typeof node.property.value !== 'string') { + // If this is not a string literal, either they're typing a number (where + // autocompletion would be quite silly) or a complex expression where + // trying to complete by property name is inappropriate. + return []; + } + + const prefix = options.expression.slice(getStart(node.property) + 1, offset); + const completions = await this.defaultCompletions(options, prefix); + + // Filter out the array access, adjust replacement ranges + return completions + .filter(c => c.sortText !== '~~[') + .map(item => ({ + ...item, + text: JSON.stringify(item.text ?? item.label) + ']', + start: getStart(node.property), + length: getEnd(node.property) - getStart(node.property), + })); + } + + /** + * Completer for an arbitrary identifier. + */ + private async identifierCompleter( + options: ICompletionContext, + source: Program, + node: Identifier, + offset: number, + ) { + // Walk through the expression and look for any locally-declared variables or identifiers. + const localIdentifiers: ICompletionWithSort[] = []; + const start = getStart(node); + traverse(source, { + enter(node) { + const completion = inferCompletionInfoForDeclaration(node); + if (completion?.id?.type === 'Identifier') { + localIdentifiers.push({ + label: completion.id.name, + type: completion.type, + sortText: completion.id.name, + }); + } + }, + }); + + const prefix = options.expression.slice(start, offset); + const completions = [ + ...localIdentifiers, + ...(await this.defaultCompletions(options, prefix)), + ]; + + if ( + this.evaluator.hasReturnValue + && options.executionContextId !== undefined + && returnValueStr.startsWith(prefix) + ) { + completions.push({ + sortText: `~${returnValueStr}`, + label: returnValueStr, + type: 'variable', + }); + } + + for (const completion of completions) { + completion.start = start; + completion.length = offset - start; + } + + return completions; + } + + /** + * Completes a property access on an object. + */ + async propertyAccessCompleter( + options: ICompletionContext, + node: MemberExpression, + offset: number, + ): Promise { + const { result, isArray } = await this.completePropertyAccess({ + executionContextId: options.executionContextId, + stackFrame: options.stackFrame, + expression: getText(options.expression, node.object), + prefix: isDummy(node.property) + ? '' + : options.expression.slice(getStart(node.property), offset), + // If we see the expression might have a side effect, still try to get + // completions, but tell V8 to throw if it sees a side effect. This is a + // fairly conservative checker, we don't enable it if not needed. + throwOnSideEffect: maybeHasSideEffects(node), + }); + + const start = getStart(node.property) - 1; + + // For any properties are aren't valid identifiers, (erring on the side of + // caution--not checking unicode and such), quote them as foo['bar!'] + const validIdentifierRe = /^[$a-z_][0-9a-z_$]*$/i; + for (const item of result) { + if (!validIdentifierRe.test(item.label)) { + item.text = `[${JSON.stringify(item.label)}]`; + item.start = start; + item.length = 1; + } + } + + if (isArray) { + const placeholder = 'index'; + result.unshift({ + label: `[${placeholder}]`, + text: `[${placeholder}]`, + type: 'property', + sortText: '~~[', + start, + selectionStart: 1, + selectionLength: placeholder.length, + length: 1, + }); + } + + return result; + } + + private async completePropertyAccess({ + executionContextId, + stackFrame, + expression, + prefix, + isInGlobalScope = false, + throwOnSideEffect = false, + }: { + executionContextId?: number; + stackFrame?: StackFrame; + expression: string; + prefix: string; + throwOnSideEffect?: boolean; + isInGlobalScope?: boolean; + }): Promise<{ result: ICompletionWithSort[]; isArray: boolean }> { + const params = { + expression: `(${expression})`, + objectGroup: 'console', + silent: true, + throwOnSideEffect, + }; + + const callFrameId = stackFrame && stackFrame.callFrameId(); + const objRefResult = await this.evaluator.evaluate( + callFrameId ? { ...params, callFrameId } : { ...params, contextId: executionContextId }, + { stackFrame }, + ); + + if (!objRefResult || objRefResult.exceptionDetails) { + return { result: [], isArray: false }; + } + + // No object ID indicates a primitive. Call enumeration on the value + // directly. We don't do this all the time, since our enumeration logic + // triggers Chrome's side-effect detect and fails. + if (!objRefResult.result.objectId) { + const primitiveParams = { + ...params, + returnByValue: true, + throwOnSideEffect: false, + expression: enumeratePropertiesTemplate.expr( + `(${expression})`, + JSON.stringify(prefix), + JSON.stringify(isInGlobalScope), + ), + }; + + const propsResult = await this.evaluator.evaluate( + callFrameId + ? { ...primitiveParams, callFrameId } + : { ...primitiveParams, contextId: executionContextId }, + ); + + return !propsResult || propsResult.exceptionDetails + ? { result: [], isArray: false } + : propsResult.result.value; + } + + // Otherwise, invoke the property enumeration on the returned object ID. + try { + const propsResult = await enumerateProperties({ + cdp: this.cdp, + args: [undefined, prefix, isInGlobalScope], + objectId: objRefResult.result.objectId, + returnByValue: true, + }); + + return propsResult.value; + } catch { + return { result: [], isArray: false }; + } finally { + this.cdp.Runtime.releaseObject({ objectId: objRefResult.result.objectId }); // no await + } + } + + /** + * Returns completion for globally scoped variables. Used for a fallback + * if we can't find anything more specific to complete. + */ + private async defaultCompletions( + options: ICompletionContext, + prefix = '', + ): Promise { + for (const global of ['self', 'global', 'this']) { + const { result: items } = await this.completePropertyAccess({ + executionContextId: options.executionContextId, + stackFrame: options.stackFrame, + expression: global, + prefix, + isInGlobalScope: true, + }); + + if (options.stackFrame) { + // When evaluating on a call frame, also autocomplete with scope variables. + const lowerPrefix = prefix.toLowerCase(); + const names = new Set(items.map(item => item.label)); + for (const completion of await options.stackFrame.completions()) { + if ( + names.has(completion.label) + || !completion.label.toLowerCase().includes(lowerPrefix) + ) { + continue; + } + + names.add(completion.label); + items.push(completion as ICompletionWithSort); + } + } + + items.push(...this.syntheticCompletions(options, prefix)); + + return items; + } + + return this.syntheticCompletions(options, prefix); + } + + private syntheticCompletions( + _options: ICompletionContext, + prefix: string, + ): ICompletionWithSort[] { + if (this.evaluator.hasReturnValue && returnValueStr.startsWith(prefix)) { + return [ + { + sortText: `~${returnValueStr}`, + label: returnValueStr, + type: 'variable', + }, + ]; + } + + return []; + } +} diff --git a/code/extensions/js-debug/src/adapter/console/console.ts b/code/extensions/js-debug/src/adapter/console/console.ts new file mode 100644 index 000000000000..8881c3872aae --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/console.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import Cdp from '../../cdp/api'; +import { assertNever } from '../../common/objUtils'; +import Dap from '../../dap/api'; +import { IDapApi } from '../../dap/connection'; +import { IShutdownParticipants, ShutdownOrder } from '../../ui/shutdownParticipants'; +import { Thread } from '../threads'; +import { IConsole } from '.'; +import { ClearMessage, EndGroupMessage, IConsoleMessage } from './consoleMessage'; +import { ReservationQueue } from './reservationQueue'; +import { + AssertMessage, + ErrorMessage, + LogMessage, + StartGroupMessage, + TableMessage, + TraceMessage, + WarningMessage, +} from './textualMessage'; + +const duplicateNodeJsLogFunctions = new Set(['group', 'assert', 'count']); + +@injectable() +export class Console implements IConsole { + private isDirty = false; + + private readonly queue = new ReservationQueue(events => { + for (const event of events) { + this.dap.output(event); + } + }); + + /** + * Fires when the queue is drained. + */ + public readonly onDrained = this.queue.onDrained; + + /** + * Gets the current length of the queue. + */ + public get length() { + return this.queue.length; + } + + constructor( + @inject(IDapApi) private readonly dap: Dap.Api, + @inject(IShutdownParticipants) shutdown: IShutdownParticipants, + ) { + shutdown.register(ShutdownOrder.ExecutionContexts, async final => { + if (this.length) { + await new Promise(r => this.onDrained(r)); + } + if (final) { + this.dispose(); + } + }); + } + + /** + * @inheritdoc + */ + public dispose() { + this.queue.dispose(); + } + + /** + * @inheritdoc + */ + public dispatch(thread: Thread, event: Cdp.Runtime.ConsoleAPICalledEvent) { + const parsed = this.parse(event); + if (parsed) { + this.enqueue(thread, parsed); + } + } + + /** + * @inheritdoc + */ + public enqueue(thread: Thread, message: IConsoleMessage) { + if (!(message instanceof ClearMessage)) { + this.isDirty = true; + } else if (this.isDirty) { + this.isDirty = false; + } else { + return; + } + + this.queue.enqueue(message.toDap(thread)); + } + + /** + * @inheritdoc + */ + public parse(event: Cdp.Runtime.ConsoleAPICalledEvent): IConsoleMessage | undefined { + if (event.type === 'log') { + // Ignore the duplicate group events that Node.js can emit: + // See: https://github.com/nodejs/node/issues/31973 + const firstFrame = event.stackTrace?.callFrames[0]; + if ( + firstFrame + && firstFrame.url === 'internal/console/constructor.js' + && duplicateNodeJsLogFunctions.has(firstFrame.functionName) + ) { + return; + } + } + + switch (event.type) { + case 'clear': + return new ClearMessage(); + case 'endGroup': + return new EndGroupMessage(); + case 'assert': + return new AssertMessage(event); + case 'table': + return new TableMessage(event); + case 'startGroup': + case 'startGroupCollapsed': + return new StartGroupMessage(event); + case 'debug': + case 'log': + case 'info': + return new LogMessage(event); + case 'trace': + return new TraceMessage(event); + case 'error': + return new ErrorMessage(event); + case 'warning': + return new WarningMessage(event); + case 'dir': + case 'dirxml': + return new LogMessage(event); // a normal object inspection + case 'count': + return new LogMessage(event); // contents are like a normal log + case 'profile': + case 'profileEnd': + return new LogMessage(event); // non-standard events, not implemented in Chrome it seems + case 'timeEnd': + return new LogMessage(event); // contents are like a normal log + default: + try { + assertNever(event.type, 'unknown console message type'); + } catch { + // ignore + } + } + } +} diff --git a/code/extensions/js-debug/src/adapter/console/consoleMessage.ts b/code/extensions/js-debug/src/adapter/console/consoleMessage.ts new file mode 100644 index 000000000000..b288c65fc651 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/consoleMessage.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Dap from '../../dap/api'; +import { Thread } from '../threads'; + +export interface IConsoleMessage { + toDap(thread: Thread): Promise | Dap.OutputEventParams; +} + +export class ClearMessage implements IConsoleMessage { + /** + * @inheritdoc + */ + public toDap(): Dap.OutputEventParams { + return { + category: 'console', + output: '\x1b[2J', + }; + } +} + +export class EndGroupMessage implements IConsoleMessage { + /** + * @inheritdoc + */ + public toDap(): Dap.OutputEventParams { + return { category: 'stdout', output: '', group: 'end' }; + } +} diff --git a/code/extensions/js-debug/src/adapter/console/exceptionMessage.ts b/code/extensions/js-debug/src/adapter/console/exceptionMessage.ts new file mode 100644 index 000000000000..b2a1df17b907 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/exceptionMessage.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../cdp/api'; +import { once } from '../../common/objUtils'; +import { StackTraceParser } from '../../common/stackTraceParser'; +import Dap from '../../dap/api'; +import { previewException } from '../objectPreview'; +import { StackTrace } from '../stackTrace'; +import { Thread } from '../threads'; +import { TextualMessage } from './textualMessage'; + +/** + * Special console message formed from an unhandled exception. + */ +export class ExceptionMessage extends TextualMessage { + /** + * @override + */ + protected readonly stackTrace = once((thread: Thread) => { + if (this.event.stackTrace) { + return StackTrace.fromRuntime(thread, this.event.stackTrace); + } + + if (this.event.scriptId) { + // script parsed errors will not have a stacktrace + return StackTrace.fromRuntime(thread, { + callFrames: [ + { + functionName: '(program)', + lineNumber: this.event.lineNumber, + columnNumber: this.event.columnNumber, + scriptId: this.event.scriptId, + url: this.event.url || '', + }, + ], + }); + } + + return undefined; + }); + + /** + * @override + */ + public async toDap(thread: Thread): Promise { + const preview = this.event.exception ? previewException(this.event.exception) : { title: '' }; + + let message = preview.title; + if (!message.startsWith('Uncaught')) { + message = `Uncaught ${this.event.exception?.className ?? 'Error'} ` + message; + } + + const stackTrace = this.stackTrace(thread); + const args = this.event.exception && !preview.stackTrace ? [this.event.exception] : []; + + // If there is a stacktrace in the exception message, beautify its paths. + // If there isn't (and there isn't always) then add one. + if (StackTraceParser.isStackLike(message)) { + message = await thread.replacePathsInStackTrace(message); + } else if (stackTrace) { + message += '\n' + (await stackTrace.formatAsNative()); + } + + return { + category: 'stderr', + output: message, + variablesReference: stackTrace || args.length + ? thread.replVariables.createVariableForOutput(message, args, stackTrace).id + : undefined, + ...(await this.getUiLocation(thread)), + }; + } +} diff --git a/code/extensions/js-debug/src/adapter/console/index.ts b/code/extensions/js-debug/src/adapter/console/index.ts new file mode 100644 index 000000000000..5332489276bf --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/index.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import type { Event } from 'vscode'; +import Cdp from '../../cdp/api'; +import { IDisposable } from '../../common/disposable'; +import { Thread } from '../threads'; +import { IConsoleMessage } from './consoleMessage'; + +export * from './exceptionMessage'; +export * from './queryObjectsMessage'; + +export const IConsole = Symbol('IConsole'); + +export interface IConsole extends IDisposable { + /** + * Fires when the output queue is drained. + */ + readonly onDrained: Event; + + /** + * Gets the current length of the output queue. + */ + readonly length: number; + + /** + * Translates and sends the event to the underlying DAP connection. + */ + dispatch(thread: Thread, event: Cdp.Runtime.ConsoleAPICalledEvent): void; + + /** + * Parses the event to a console message. Returns undefined if the message + * cannot be parsed or should not be sent to the client. + */ + parse(event: Cdp.Runtime.ConsoleAPICalledEvent): IConsoleMessage | undefined; + + /** + * Schedules the message, or promise of a message, to be written to the console. + */ + enqueue(thread: Thread, message: IConsoleMessage): void; +} diff --git a/code/extensions/js-debug/src/adapter/console/queryObjectsMessage.ts b/code/extensions/js-debug/src/adapter/console/queryObjectsMessage.ts new file mode 100644 index 000000000000..804ac17d4b3a --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/queryObjectsMessage.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import Cdp from '../../cdp/api'; +import Dap from '../../dap/api'; +import { previewRemoteObject } from '../objectPreview'; +import { previewThis } from '../templates/previewThis'; +import { Thread } from '../threads'; +import { IConsoleMessage } from './consoleMessage'; + +/** + * Message sent as the result of querying objects on the runtime. + */ +export class QueryObjectsMessage implements IConsoleMessage { + constructor( + private readonly protoObj: Cdp.Runtime.RemoteObject, + private readonly cdp: Cdp.Api, + ) {} + + public async toDap(thread: Thread): Promise { + if (!this.protoObj.objectId) { + return { + category: 'stderr', + output: l10n.t('Only objects can be queried'), + }; + } + + const response = await this.cdp.Runtime.queryObjects({ + prototypeObjectId: this.protoObj.objectId, + objectGroup: 'console', + }); + + await this.cdp.Runtime.releaseObject({ objectId: this.protoObj.objectId }); + if (!response) { + return { + category: 'stderr', + output: l10n.t('Could not query the provided object'), + }; + } + + let withPreview: Cdp.Runtime.RemoteObject; + try { + withPreview = await previewThis({ + cdp: this.cdp, + args: [], + objectId: response.objects.objectId, + objectGroup: 'console', + generatePreview: true, + }); + } catch (e) { + return { + category: 'stderr', + output: l10n.t(e.message), + }; + } + + const text = '\x1b[32mobjects: ' + previewRemoteObject(withPreview, 'repl') + '\x1b[0m'; + const variablesReference = thread.replVariables.createVariableForOutput(text, [withPreview]).id; + + return { + category: 'stdout', + output: '', + variablesReference, + }; + } +} diff --git a/code/extensions/js-debug/src/adapter/console/reservationQueue.test.ts b/code/extensions/js-debug/src/adapter/console/reservationQueue.test.ts new file mode 100644 index 000000000000..212a1ce638b9 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/reservationQueue.test.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { delay, getDeferred } from '../../common/promiseUtil'; +import { ReservationQueue } from './reservationQueue'; + +describe('ReservationQueue', () => { + let sunk: number[][]; + let queue: ReservationQueue; + + beforeEach(() => { + sunk = []; + queue = new ReservationQueue(items => { + sunk.push(items); + if (items.includes(-1)) { + queue.dispose(); + } + }); + }); + + it('enqueues sync', () => { + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + expect(sunk).to.deep.equal([[1], [2], [3]]); + }); + + it('enqueues async with order', async () => { + const gate1 = getDeferred(); + const gate2 = getDeferred(); + + queue.enqueue(gate2.promise.then(() => 1)); + queue.enqueue( + delay(1).then(() => { + gate1.resolve(); + return 2; + }), + ); + queue.enqueue( + gate1.promise.then(() => { + gate2.resolve(); + return 3; + }), + ); + await delay(10); + expect(sunk).to.deep.equal([[1, 2, 3]]); + }); + + it('bulks after async resolution', async () => { + queue.enqueue(1); + queue.enqueue(delay(6).then(() => 2)); + queue.enqueue(delay(2).then(() => 3)); + queue.enqueue(4); + queue.enqueue(delay(4).then(() => 5)); + queue.enqueue(delay(8).then(() => 6)); + await delay(10); + expect(sunk.length).to.be.lessThanOrEqual(3, JSON.stringify(sunk)); + expect(sunk.flat()).to.deep.equal([1, 2, 3, 4, 5, 6]); + }); + + it('stops when disposed', async () => { + queue.enqueue(delay(2).then(() => 1)); + queue.enqueue(delay(4).then(() => -1)); + queue.enqueue(delay(6).then(() => 3)); + await delay(4); + expect(sunk).to.deep.equal([[1], [-1]]); + }); +}); diff --git a/code/extensions/js-debug/src/adapter/console/reservationQueue.ts b/code/extensions/js-debug/src/adapter/console/reservationQueue.ts new file mode 100644 index 000000000000..0f2136cee327 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/reservationQueue.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { IDisposable } from '../../common/disposable'; +import { EventEmitter } from '../../common/events'; + +/** + * A queue that allows inserting items that are built asynchronously, while + * preserving insertion order. + */ +export class ReservationQueue implements IDisposable { + private q: Reservation[] = []; + private disposed = false; + private onDrainedEmitter = new EventEmitter(); + + /** + * Fires when the queue is drained. + */ + public readonly onDrained = this.onDrainedEmitter.event; + + /** + * Gets the current length of the queue. + */ + public get length() { + return this.q.length; + } + + constructor(private readonly sink: (items: T[]) => void) {} + + /** + * Enqueues an item or a promise for an item in the queue. + */ + public enqueue(value: T | Promise) { + if (this.disposed) { + return; + } + + this.q.push(new Reservation(value)); + if (this.q.length === 1) { + this.process(); + } + } + + /** + * Cancels processing of all pending items. + * @inheritdoc + */ + public dispose() { + this.disposed = true; + this.q = []; + } + + private async process(): Promise { + const toIndex = this.q.findIndex(r => r.value === unsettled); + if (toIndex === 0) { + await this.q[0].wait; + } else if (toIndex === -1) { + this.sink(extractResolved(this.q)); + this.q = []; + } else { + this.sink(extractResolved(this.q.slice(0, toIndex))); + this.q = this.q.slice(toIndex); + } + + if (this.q.length) { + this.process(); + } else { + this.onDrainedEmitter.fire(); + } + } +} + +const extractResolved = (list: ReadonlyArray>) => + list.map(i => i.value).filter((v): v is T => v !== rejected); + +const unsettled = Symbol('unsettled'); +const rejected = Symbol('unsettled'); + +/** + * Item in the queue. + */ +class Reservation { + /** + * Promise that is resolved when `value` is rejected or resolved. + */ + public wait?: Promise; + + /** + * Current value, or an indication that the promise is pending or rejected. + */ + public value: typeof unsettled | typeof rejected | T = unsettled; + + constructor(rawValue: T | Promise) { + if (!(rawValue instanceof Promise)) { + this.value = rawValue; + this.wait = Promise.resolve(); + } else { + this.wait = rawValue.then( + r => { + this.value = r; + }, + () => { + this.value = rejected; + }, + ); + } + } +} diff --git a/code/extensions/js-debug/src/adapter/console/textualMessage.ts b/code/extensions/js-debug/src/adapter/console/textualMessage.ts new file mode 100644 index 000000000000..529a3440e280 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/console/textualMessage.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import Cdp from '../../cdp/api'; +import { once } from '../../common/objUtils'; +import { StackTraceParser } from '../../common/stackTraceParser'; +import Dap from '../../dap/api'; +import { formatMessage } from '../messageFormat'; +import { messageFormatters, previewAsObject } from '../objectPreview'; +import { AnyObject } from '../objectPreview/betterTypes'; +import { IUiLocation } from '../source'; +import { StackFrame, StackTrace } from '../stackTrace'; +import { Thread } from '../threads'; +import { IConsoleMessage } from './consoleMessage'; + +export abstract class TextualMessage + implements IConsoleMessage +{ + protected readonly stackTrace = once((thread: Thread) => + this.event.stackTrace ? StackTrace.fromRuntime(thread, this.event.stackTrace) : undefined + ); + + constructor(protected readonly event: T) {} + + /** + * Returns the DAP representation of the console message. + */ + public abstract toDap(thread: Thread): Promise | Dap.OutputEventParams; + + /** + * Gets the UI location where the message was logged. + */ + protected readonly getUiLocation = once(async (thread: Thread) => { + const stackTrace = this.stackTrace(thread); + if (!stackTrace) { + return; + } + + let firstExistingLocation: IUiLocation | undefined; + for (const frame of stackTrace.frames) { + if (!(frame instanceof StackFrame)) { + continue; + } + + const uiLocation = await frame.uiLocation(); + if (!uiLocation) { + continue; + } + + if (!firstExistingLocation) { + firstExistingLocation = uiLocation; + } + + if (uiLocation.source.blackboxed()) { + continue; + } + + return { + source: await uiLocation.source.toDap(), + line: uiLocation.lineNumber, + column: uiLocation.columnNumber, + }; + } + + // if all the stack is blackboxed, fall back to the original location + if (firstExistingLocation) { + return { + source: await firstExistingLocation.source.toDap(), + line: firstExistingLocation.lineNumber, + column: firstExistingLocation.columnNumber, + }; + } + }); + + /** + * Default message string formatter. Tries to create a simple string, and + * but if it can't it'll return a variable reference. + * + * Intentionally not async-await as it's a hot path in console logging. + */ + protected formatDefaultString( + thread: Thread, + args: ReadonlyArray, + includeStackInVariables = false, + ) { + const useMessageFormat = args.length > 1 && args[0].type === 'string'; + const formatResult = useMessageFormat + ? formatMessage(args[0].value, args.slice(1) as AnyObject[], messageFormatters) + : formatMessage('', args as AnyObject[], messageFormatters); + + const output = formatResult.result + '\n'; + + if (formatResult.usedAllSubs && !args.some(previewAsObject)) { + return { output }; + } else { + return this.formatComplexStringOutput(thread, output, args, includeStackInVariables); + } + } + + private async formatComplexStringOutput( + thread: Thread, + output: string, + args: ReadonlyArray, + includeStackInVariables: boolean, + ) { + if (args.some(a => a.subtype === 'error') || StackTraceParser.isStackLike(output)) { + await this.getUiLocation(thread); // ensure the source is loaded before decoding stack + output = await thread.replacePathsInStackTrace(output); + includeStackInVariables = true; + } + + const outputVar = thread.replVariables.createVariableForOutput( + output, + args, + includeStackInVariables ? this.stackTrace(thread) : undefined, + ); + + return { output, variablesReference: outputVar.id }; + } +} + +export class AssertMessage extends TextualMessage { + /** + * @override + */ + public async toDap(thread: Thread): Promise { + if (this.event.args[0]?.value === 'console.assert') { + this.event.args[0].value = l10n.t('Assertion failed'); + } + + return { + category: 'stderr', + ...(await this.formatDefaultString(thread, this.event.args, /* includeStack= */ true)), + ...(await this.getUiLocation(thread)), + }; + } +} + +class DefaultMessage extends TextualMessage { + constructor( + event: Cdp.Runtime.ConsoleAPICalledEvent, + private readonly includeStack: boolean, + private readonly category: Required, + ) { + super(event); + } + /** + * @override + */ + public async toDap(thread: Thread): Promise { + return { + category: this.category, + ...(await this.formatDefaultString(thread, this.event.args, this.includeStack)), + ...(await this.getUiLocation(thread)), + }; + } +} + +export class LogMessage extends DefaultMessage { + constructor(event: Cdp.Runtime.ConsoleAPICalledEvent) { + super(event, false, 'stdout'); + } +} + +export class TraceMessage extends DefaultMessage { + constructor(event: Cdp.Runtime.ConsoleAPICalledEvent) { + super(event, true, 'stdout'); + } +} + +export class WarningMessage extends DefaultMessage { + constructor(event: Cdp.Runtime.ConsoleAPICalledEvent) { + super(event, true, 'stderr'); + } +} + +export class ErrorMessage extends DefaultMessage { + constructor(event: Cdp.Runtime.ConsoleAPICalledEvent) { + super(event, true, 'stderr'); + } +} + +export class StartGroupMessage extends TextualMessage { + /** + * @override + */ + public async toDap(thread: Thread): Promise { + return { + category: 'stdout', + group: this.event.type === 'startGroupCollapsed' ? 'startCollapsed' : 'start', + ...(await this.formatDefaultString(thread, this.event.args)), + ...(await this.getUiLocation(thread)), + }; + } +} + +export class TableMessage extends DefaultMessage { + constructor(event: Cdp.Runtime.ConsoleAPICalledEvent) { + super(event, false, 'stdout'); + } + + /** + * @override + */ + public async toDap(thread: Thread): Promise { + if (this.event.args[0]?.preview) { + return { + category: 'stdout', + output: '', + variablesReference: thread.replVariables.createVariableForOutput( + '', + this.event.args, + undefined, + this.event.type, + ).id, + ...(await this.getUiLocation(thread)), + }; + } + + return super.toDap(thread); + } +} diff --git a/code/extensions/js-debug/src/adapter/customBreakpoints.ts b/code/extensions/js-debug/src/adapter/customBreakpoints.ts new file mode 100644 index 000000000000..7b1152902d6d --- /dev/null +++ b/code/extensions/js-debug/src/adapter/customBreakpoints.ts @@ -0,0 +1,316 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import Cdp from '../cdp/api'; + +export interface IXHRBreakpoint { + match: string; +} + +export interface ICustomBreakpoint { + id: string; + title: string; + group: string; + details: (data: object) => { short: string; long: string }; + apply: (cdp: Cdp.Api, enabled: boolean) => Promise; +} + +const map: Map = new Map(); + +export function customBreakpoints(): Map { + if (map.size) return map; + + function g(group: string, breakpoints: ICustomBreakpoint[]) { + for (const b of breakpoints) { + b.group = group; + map.set(b.id, b); + } + } + + function i(instrumentation: string, maybeTitle?: string): ICustomBreakpoint { + const title = maybeTitle || instrumentation; + return { + id: 'instrumentation:' + instrumentation, + title, + group: '', + // eslint-disable-next-line + details: (data: any): { short: string; long: string } => { + if (instrumentation === 'webglErrorFired') { + let errorName = data['webglErrorName']; + // If there is a hex code of the error, display only this. + errorName = errorName.replace(/^.*(0x[0-9a-f]+).*$/i, '$1'); + return { + short: errorName, + long: l10n.t( + 'Paused on WebGL Error instrumentation breakpoint, error "{0}"', + errorName, + ), + }; + } + if (instrumentation === 'scriptBlockedByCSP' && data['directiveText']) { + return { + short: l10n.t('CSP violation "{0}"', data['directiveText']), + long: l10n.t( + 'Paused on Content Security Policy violation instrumentation breakpoint, directive "{0}"', + data['directiveText'], + ), + }; + } + return { + short: title, + long: l10n.t('Paused on instrumentation breakpoint "{0}"', title), + }; + }, + apply: async (cdp: Cdp.Api, enabled: boolean): Promise => { + // DOMDebugger.setInstrumentationBreakpoint was very recently deprecated, + // try the old method as a fallback. + const ok1 = enabled + ? await cdp.EventBreakpoints.setInstrumentationBreakpoint({ + eventName: instrumentation, + }) + : await cdp.EventBreakpoints.removeInstrumentationBreakpoint({ + eventName: instrumentation, + }); + + if (ok1) { + return true; + } + + const ok2 = enabled + ? await cdp.DOMDebugger.setInstrumentationBreakpoint({ eventName: instrumentation }) + : await cdp.DOMDebugger.removeInstrumentationBreakpoint({ + eventName: instrumentation, + }); + + return !!ok2; + }, + }; + } + + function e(eventName: string, target?: string | string[], title?: string): ICustomBreakpoint { + const eventTargets = target === undefined + ? '*' + : typeof target === 'string' + ? [target] + : target; + return { + id: 'listener:' + eventName, + title: title || eventName, + group: '', + details: (data: { targetName?: string }): { short: string; long: string } => { + const eventTargetName = (data.targetName || '*').toLowerCase(); + return { + short: eventTargetName + '.' + eventName, + long: l10n.t( + 'Paused on event listener breakpoint "{0}", triggered on "{1}"', + eventName, + eventTargetName, + ), + }; + }, + apply: async (cdp: Cdp.Api, enabled: boolean): Promise => { + let result = true; + for (const eventTarget of eventTargets) { + if (enabled) { + result = result + && !!(await cdp.DOMDebugger.setEventListenerBreakpoint({ + eventName, + targetName: eventTarget, + })); + } else { + result = result + && !!(await cdp.DOMDebugger.removeEventListenerBreakpoint({ + eventName, + targetName: eventTarget, + })); + } + } + return result; + }, + }; + } + + g(`Ad Auction Worklet`, [ + i('beforeBidderWorkletBiddingStart', l10n.t('Bidder Bidding Phase Start')), + i('beforeBidderWorkletReportingStart', l10n.t('Bidder Reporting Phase Start')), + i('beforeSellerWorkletScoringStart', l10n.t('Seller Scoring Phase Start')), + i('beforeSellerWorkletReportingStart', l10n.t('Seller Reporting Phase Start')), + ]); + g(`Animation`, [ + i('requestAnimationFrame', l10n.t('Request Animation Frame')), + i('cancelAnimationFrame', l10n.t('Cancel Animation Frame')), + i('requestAnimationFrame.callback', l10n.t('Animation Frame Fired')), + ]); + g(`Canvas`, [ + i('canvasContextCreated', l10n.t('Create canvas context')), + i('webglErrorFired', l10n.t('WebGL Error Fired')), + i('webglWarningFired', l10n.t('WebGL Warning Fired')), + ]); + g(`Clipboard`, [ + e('copy'), + e('cut'), + e('paste'), + e('beforecopy'), + e('beforecut'), + e('beforepaste'), + ]); + g(`Control`, [ + e('resize'), + e('scroll'), + e('scrollend'), + e('zoom'), + e('focus'), + e('blur'), + e('select'), + e('change'), + e('submit'), + e('reset'), + ]); + g(`Device`, [e('deviceorientation'), e('devicemotion')]); + g(`DOM Mutation`, [ + e('DOMActivate'), + e('DOMFocusIn'), + e('DOMFocusOut'), + e('DOMAttrModified'), + e('DOMCharacterDataModified'), + e('DOMNodeInserted'), + e('DOMNodeInsertedIntoDocument'), + e('DOMNodeRemoved'), + e('DOMNodeRemovedFromDocument'), + e('DOMSubtreeModified'), + e('DOMContentLoaded'), + ]); + g(`Drag / Drop`, [ + e('drag'), + e('dragstart'), + e('dragend'), + e('dragenter'), + e('dragover'), + e('dragleave'), + e('drop'), + ]); + g(`Geolocation`, [ + i('Geolocation.getCurrentPosition', `getCurrentPosition`), + i('Geolocation.watchPosition', `watchPosition`), + ]); + g(`Keyboard`, [e('keydown'), e('keyup'), e('keypress'), e('input')]); + g(`Load`, [ + e('load'), + e('beforeunload'), + e('unload'), + e('abort'), + e('error'), + e('hashchange'), + e('popstate'), + e('navigate'), + e('navigatesuccess'), + e('navigateerror'), + e('currentchange'), + e('nagivateto'), + e('navigatefrom'), + e('finish'), + e('dispose'), + ]); + const av = ['audio', 'video']; + g(`Media`, [ + e('play', av), + e('pause', av), + e('playing', av), + e('canplay', av), + e('canplaythrough', av), + e('seeking', av), + e('seeked', av), + e('timeupdate', av), + e('ended', av), + e('ratechange', av), + e('durationchange', av), + e('volumechange', av), + e('loadstart', av), + e('progress', av), + e('suspend', av), + e('abort', av), + e('error', av), + e('emptied', av), + e('stalled', av), + e('loadedmetadata', av), + e('loadeddata', av), + e('waiting', av), + ]); + g(`Mouse`, [ + e('auxclick'), + e('click'), + e('dblclick'), + e('mousedown'), + e('mouseup'), + e('mouseover'), + e('mousemove'), + e('mouseout'), + e('mouseenter'), + e('mouseleave'), + e('mousewheel'), + e('wheel'), + e('contextmenu'), + ]); + g(`Notification`, [i('Notification.requestPermission', `requestPermission`)]); + g(`Parse`, [ + i('Element.setInnerHTML', l10n.t('Set innerHTML')), + i('Document.write', `document.write`), + ]); + g(`Picture-in-Picture`, [ + e('enterpictureinpicture', 'video'), + e('leavepictureinpicture', 'video'), + e('resize', 'PictureInPictureWindow'), + e('enter', 'documentPictureInPicture'), + ]); + g(`Pointer`, [ + e('pointerover'), + e('pointerout'), + e('pointerenter'), + e('pointerleave'), + e('pointerdown'), + e('pointerup'), + e('pointermove'), + e('pointercancel'), + e('gotpointercapture'), + e('lostpointercapture'), + e('pointerrawupdate'), + ]); + g(`Script`, [ + i('scriptFirstStatement', l10n.t('Script First Statement')), + i('scriptBlockedByCSP', l10n.t('Script Blocked by Content Security Policy')), + ]); + g(`Timer`, [ + i('setTimeout'), + i('clearTimeout'), + i('setInterval'), + i('clearInterval'), + i('setTimeout.callback', l10n.t('setTimeout fired')), + i('setInterval.callback', l10n.t('setInterval fired')), + ]); + g(`Touch`, [e('touchstart'), e('touchmove'), e('touchend'), e('touchcancel')]); + g(`WebAudio`, [ + i('audioContextCreated', l10n.t('Create AudioContext')), + i('audioContextClosed', l10n.t('Close AudioContext')), + i('audioContextResumed', l10n.t('Resume AudioContext')), + i('audioContextSuspended', l10n.t('Suspend AudioContext')), + ]); + g(`Window`, [i('DOMWindow.close', `window.close`)]); + g(`Worker`, [e('message'), e('messageerror')]); + const xhr = ['xmlhttprequest', 'xmlhttprequestupload']; + g(`XHR`, [ + e('readystatechange', xhr), + e('load', xhr), + e('loadstart', xhr), + e('loadend', xhr), + e('abort', xhr), + e('error', xhr), + e('progress', xhr), + e('timeout', xhr), + ]); + + return map; +} + +export default customBreakpoints; diff --git a/code/extensions/js-debug/src/adapter/debugAdapter.ts b/code/extensions/js-debug/src/adapter/debugAdapter.ts new file mode 100644 index 000000000000..ac2307ea15c4 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/debugAdapter.ts @@ -0,0 +1,664 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { Container } from 'inversify'; +import { Cdp } from '../cdp/api'; +import { DisposableList, IDisposable } from '../common/disposable'; +import { ILogger, LogTag } from '../common/logging'; +import { posInt32Counter, truthy } from '../common/objUtils'; +import { Base1Position } from '../common/positions'; +import { getDeferred, IDeferred } from '../common/promiseUtil'; +import { IRenameProvider } from '../common/sourceMaps/renameProvider'; +import * as sourceUtils from '../common/sourceUtils'; +import * as urlUtils from '../common/urlUtils'; +import { AnyLaunchConfiguration } from '../configuration'; +import Dap from '../dap/api'; +import * as errors from '../dap/errors'; +import { ProtocolError } from '../dap/protocolError'; +import { disposeContainer, FS, FsPromises } from '../ioc-extras'; +import { ITarget } from '../targets/targets'; +import { ITelemetryReporter } from '../telemetry/telemetryReporter'; +import { IShutdownParticipants } from '../ui/shutdownParticipants'; +import { IAsyncStackPolicy } from './asyncStackPolicy'; +import { BreakpointManager } from './breakpoints'; +import { ICdpProxyProvider } from './cdpProxy'; +import { IClientCapabilies } from './clientCapabilities'; +import { ICompletions } from './completions'; +import { IConsole } from './console'; +import { Diagnostics } from './diagnosics'; +import { DiagnosticToolSuggester } from './diagnosticToolSuggester'; +import { IEvaluator } from './evaluator'; +import { IExceptionPauseService, PauseOnExceptionsState } from './exceptionPauseService'; +import { IPerformanceProvider } from './performance'; +import { IProfileController } from './profileController'; +import { BasicCpuProfiler } from './profiling/basicCpuProfiler'; +import { ScriptSkipper } from './scriptSkipper/implementation'; +import { IScriptSkipper } from './scriptSkipper/scriptSkipper'; +import { SmartStepper } from './smartStepping'; +import { ISourceWithMap, Source, SourceFromMap } from './source'; +import { SourceContainer } from './sourceContainer'; +import { Thread } from './threads'; +import { VariableStore } from './variableStore'; + +// This class collects configuration issued before "launch" request, +// to be applied after launch. +export class DebugAdapter implements IDisposable { + readonly dap: Dap.Api; + readonly sourceContainer: SourceContainer; + readonly breakpointManager: BreakpointManager; + private _disposables = new DisposableList(); + private _customBreakpoints: string[] = []; + private _xhrBreakpoints: string[] = []; + private _emulationSupported: boolean | undefined; + private _thread: Thread | undefined; + private _threadDeferred = getDeferred(); + private _configurationDoneDeferred: IDeferred; + private breakpointIdCounter = posInt32Counter(); + private readonly _cdpProxyProvider = this._services.get(ICdpProxyProvider); + + constructor( + dap: Dap.Api, + private readonly asyncStackPolicy: IAsyncStackPolicy, + private readonly launchConfig: AnyLaunchConfiguration, + private readonly _services: Container, + ) { + this._configurationDoneDeferred = getDeferred(); + + this.sourceContainer = _services.get(SourceContainer); + + // It seems that the _onSetBreakpoints callback might be called while this method is being executed + // so we initialize this before configuring the event handlers for the dap + this.breakpointManager = _services.get(BreakpointManager); + + const performanceProvider = _services.get(IPerformanceProvider); + const telemetry = _services.get(ITelemetryReporter); + telemetry.onFlush(() => { + telemetry.report('breakpointStats', this.breakpointManager.statisticsForTelemetry()); + telemetry.report('statistics', this.sourceContainer.statistics()); + }); + + this.dap = dap; + this.dap.on('initialize', params => this.onInitialize(params)); + this.dap.on('setBreakpoints', params => this._onSetBreakpoints(params)); + this.dap.on('setExceptionBreakpoints', params => this.setExceptionBreakpoints(params)); + this.dap.on('configurationDone', () => this.configurationDone()); + this.dap.on('loadedSources', () => this._onLoadedSources()); + this.dap.on('disableSourcemap', params => this._onDisableSourcemap(params)); + this.dap.on('source', params => this._onSource(params)); + this.dap.on('threads', () => this._onThreads()); + this.dap.on('stackTrace', params => this._withThread(thread => thread.stackTrace(params))); + this.dap.on('variables', params => this._onVariables(params)); + this.dap.on('readMemory', params => this._onReadMemory(params)); + this.dap.on('writeMemory', params => this._onWriteMemory(params)); + this.dap.on('setVariable', params => this._onSetVariable(params)); + this.dap.on('setExpression', params => this._onSetExpression(params)); + this.dap.on('continue', () => this._withThread(thread => thread.resume())); + this.dap.on('pause', () => this._withThread(thread => thread.pause())); + this.dap.on('next', () => this._withThread(thread => thread.stepOver())); + this.dap.on('stepIn', params => this._withThread(thread => thread.stepInto(params.targetId))); + this.dap.on('stepOut', () => this._withThread(thread => thread.stepOut())); + this.dap.on( + 'restartFrame', + params => this._withThread(thread => thread.restartFrame(params)), + ); + this.dap.on('scopes', params => this._withThread(thread => thread.scopes(params))); + this.dap.on('evaluate', params => this.onEvaluate(params)); + this.dap.on('completions', params => this._withThread(thread => thread.completions(params))); + this.dap.on('exceptionInfo', () => this._withThread(thread => thread.exceptionInfo())); + this.dap.on('setCustomBreakpoints', params => this.setCustomBreakpoints(params)); + this.dap.on('toggleSkipFileStatus', params => this._toggleSkipFileStatus(params)); + this.dap.on('toggleSkipFileStatus', params => this._toggleSkipFileStatus(params)); + this.dap.on('prettyPrintSource', params => this._prettyPrintSource(params)); + this.dap.on('locations', params => this._onLocations(params)); + this.dap.on('revealPage', () => this._withThread(thread => thread.revealPage())); + this.dap.on( + 'getPerformance', + () => this._withThread(thread => performanceProvider.retrieve(thread.cdp())), + ); + this.dap.on('breakpointLocations', params => this._breakpointLocations(params)); + this.dap.on('createDiagnostics', params => this._dumpDiagnostics(params)); + this.dap.on('requestCDPProxy', () => this._requestCDPProxy()); + this.dap.on('setExcludedCallers', params => this._onSetExcludedCallers(params)); + this.dap.on('saveDiagnosticLogs', ({ toFile }) => this._saveDiagnosticLogs(toFile)); + this.dap.on('setSourceMapStepping', params => this._setSourceMapStepping(params)); + this.dap.on('stepInTargets', params => this._stepInTargets(params)); + this.dap.on('setDebuggerProperty', params => this._setDebuggerProperty(params)); + this.dap.on('setSymbolOptions', params => this._setSymbolOptions(params)); + this.dap.on('networkCall', params => this._doNetworkCall(params)); + this.dap.on('enableNetworking', params => this._withThread(t => t.enableNetworking(params))); + this.dap.on('canEmulate', () => this._canEmulate()); + this.dap.on('setFocusEmulation', params => this._setFocusEmulation(params)); + this.dap.on( + 'getPreferredUILocation', + params => this._getPreferredUILocation(params), + ); + } + + private async _getPreferredUILocation( + params: Dap.GetPreferredUILocationParams, + ): Promise { + let source: Source | undefined = undefined; + if (params.originalUrl) { + source = this.sourceContainer.getSourceByOriginalUrl(params.originalUrl); + } + if (!source && params.source) { + source = this.sourceContainer.source(params.source); + } + + if (!source) { + if (params.source) { + // Return unmodified input source + return { + column: params.column, + line: params.line, + source: params.source, + }; + } else { + throw new ProtocolError(errors.missingSourceInformation()); + } + } + + const location = await this.sourceContainer.preferredUiLocation({ + columnNumber: params.column + 1, + lineNumber: params.line + 1, + source, + }); + + return { + column: location.columnNumber - 1, + line: location.lineNumber - 1, + source: await location.source.toDap(), + }; + } + + private async _doNetworkCall({ method, params }: Dap.NetworkCallParams) { + if (!this._thread) { + return Promise.resolve({}); + } + + // ugly casts :( + const networkDomain = this._thread.cdp().Network as unknown as Record< + string, + (method: unknown) => Promise + >; + + return networkDomain[method](params); + } + + private async _canEmulate(): Promise { + const thread = await this._threadDeferred.promise; + const cdp = thread.cdp(); + + // Check if Emulation domain is available (not present in Node.js targets) + if (this._emulationSupported === undefined) { + const domainsResult = await cdp.Schema.getDomains({}); + this._emulationSupported = domainsResult?.domains.some(d => d.name === 'Emulation') ?? false; + } + + return { supported: this._emulationSupported }; + } + + private async _setFocusEmulation( + params: Dap.SetFocusEmulationParams, + ): Promise { + const thread = await this._threadDeferred.promise; + await thread.cdp().Emulation.setFocusEmulationEnabled({ enabled: params.enabled }); + return {}; + } + + private _setDebuggerProperty( + params: Dap.SetDebuggerPropertyParams, + ): Promise { + this._thread?.cdp().DotnetDebugger.setDebuggerProperty(params); + return Promise.resolve({}); + } + + private _setSymbolOptions( + params: Dap.SetSymbolOptionsParams, + ): Promise { + this._thread?.cdp().DotnetDebugger.setSymbolOptions(params); + return Promise.resolve({}); + } + + private _breakpointLocations( + params: Dap.BreakpointLocationsParams, + ): Promise { + return this._withThread(async thread => { + const source = this.sourceContainer.source(params.source); + if (!source) { + return { breakpoints: [] }; + } + + const possibleBps = await this.breakpointManager.getBreakpointLocations( + thread, + source, + new Base1Position(params.line, params.column || 1), + new Base1Position( + params.endLine || params.line + 1, + params.endColumn || params.column || 1, + ), + ); + + return { + breakpoints: possibleBps + .map(bp => bp.uiLocations.find(l => l.source === source)) + .filter(truthy) + .map(bp => ({ line: bp.lineNumber, column: bp.columnNumber })), + }; + }); + } + + private _stepInTargets(params: Dap.StepInTargetsParams): Promise { + return this._withThread(async thread => ({ + targets: await thread.getStepInTargets(params.frameId), + })); + } + + private _setSourceMapStepping({ + enabled, + }: Dap.SetSourceMapSteppingParams): Promise { + this.sourceContainer.doSourceMappedStepping = enabled; + return Promise.resolve({}); + } + + private async _saveDiagnosticLogs(toFile: string): Promise { + const logs = this._services.get(ILogger).getRecentLogs(); + await this._services + .get(FS) + .writeFile(toFile, logs.map(l => JSON.stringify(l)).join('\n')); + return {}; + } + + public async launchBlocker(): Promise { + await this._configurationDoneDeferred.promise; + await this._thread?.debuggerReady.promise; + await this._services.get(IExceptionPauseService).launchBlocker; + await this.breakpointManager.launchBlocker(); + } + + async _onSetExcludedCallers({ + callers, + }: Dap.SetExcludedCallersParams): Promise { + const thread = await this._threadDeferred.promise; + thread.setExcludedCallers(callers); + return {}; + } + + public async onInitialize( + params: Dap.InitializeParams, + ): Promise { + console.assert(params.linesStartAt1); + console.assert(params.columnsStartAt1); + this._services.get(IClientCapabilies).value = params; + const capabilities = DebugAdapter.capabilities(true); + setTimeout(() => this.dap.initialized({}), 0); + setTimeout(() => this._thread?.dapInitialized(), 0); + return capabilities; + } + + static capabilities(extended = false): Dap.CapabilitiesExtended { + return { + supportsConfigurationDoneRequest: true, + supportsFunctionBreakpoints: false, + supportsConditionalBreakpoints: true, + supportsHitConditionalBreakpoints: true, + supportsEvaluateForHovers: true, + supportsReadMemoryRequest: true, + supportsWriteMemoryRequest: true, + exceptionBreakpointFilters: [ + { + filter: PauseOnExceptionsState.All, + label: l10n.t('Caught Exceptions'), + default: false, + supportsCondition: true, + description: l10n.t("Breaks on all throw errors, even if they're caught later."), + conditionDescription: `error.name == "MyError"`, + }, + { + filter: PauseOnExceptionsState.Uncaught, + label: l10n.t('Uncaught Exceptions'), + default: false, + supportsCondition: true, + description: l10n.t('Breaks only on errors or promise rejections that are not handled.'), + conditionDescription: `error.name == "MyError"`, + }, + ], + supportsStepBack: false, + supportsSetVariable: true, + supportsRestartFrame: true, + supportsGotoTargetsRequest: false, + supportsStepInTargetsRequest: true, + supportsCompletionsRequest: true, + supportsModulesRequest: false, + additionalModuleColumns: [], + supportedChecksumAlgorithms: [], + supportsRestartRequest: true, + supportsExceptionOptions: false, + supportsValueFormattingOptions: true, + supportsExceptionInfoRequest: true, + supportTerminateDebuggee: true, + supportsDelayedStackTraceLoading: true, + supportsLoadedSourcesRequest: true, + supportsLogPoints: true, + supportsTerminateThreadsRequest: false, + supportsSetExpression: true, + supportsTerminateRequest: false, + completionTriggerCharacters: ['.', '[', '"', "'"], + supportsBreakpointLocationsRequest: true, + supportsClipboardContext: true, + supportsExceptionFilterOptions: true, + supportsEvaluationOptions: extended ? true : false, + supportsDebuggerProperties: extended ? true : false, + supportsSetSymbolOptions: extended ? true : false, + supportsANSIStyling: true, + // supportsDataBreakpoints: false, + // supportsDisassembleRequest: false, + }; + } + + private async _onSetBreakpoints( + params: Dap.SetBreakpointsParams, + ): Promise { + return this.breakpointManager.setBreakpoints( + params, + params.breakpoints?.map(() => this.breakpointIdCounter()) ?? [], + ); + } + + async setExceptionBreakpoints( + params: Dap.SetExceptionBreakpointsParams, + ): Promise { + await this._services.get(IExceptionPauseService).setBreakpoints( + params, + ); + return {}; + } + + async configurationDone(): Promise { + this._configurationDoneDeferred.resolve(); + return {}; + } + + async _onLoadedSources(): Promise { + return { sources: await this.sourceContainer.loadedSources() }; + } + + private async _onDisableSourcemap(params: Dap.DisableSourcemapParams) { + const source = this.sourceContainer.source(params.source); + if (!source) { + return errors.createSilentError(l10n.t('Source not found')); + } + + if (!(source instanceof SourceFromMap)) { + return errors.createSilentError(l10n.t('Source not a source map')); + } + + for (const compiled of source.compiledToSourceUrl.keys()) { + this.sourceContainer.disableSourceMapForSource(compiled, /* permanent= */ true); + } + + await this._thread?.refreshStackTrace(); + + return {}; + } + + async _onSource(params: Dap.SourceParams): Promise { + if (!params.source) { + params.source = { sourceReference: params.sourceReference }; + } + + params.source.path = urlUtils.platformPathToPreferredCase(params.source.path); + const source = this.sourceContainer.source(params.source); + if (!source) { + return errors.createSilentError(l10n.t('Source not found')); + } + + const content = await source.content(); + if (content === undefined) { + if (source instanceof SourceFromMap) { + this.dap.suggestDisableSourcemap({ source: params.source }); + } + + return errors.createSilentError(l10n.t('Unable to retrieve source content')); + } + + return { content, mimeType: source.getSuggestedMimeType }; + } + + async _onThreads(): Promise { + const threads: Dap.Thread[] = []; + if (this._thread) threads.push({ id: this._thread.id, name: this._thread.name() }); + return { threads }; + } + + private findVariableStore(fn: (store: VariableStore) => boolean) { + if (!this._thread) { + return undefined; + } + + const pausedVariables = this._thread.pausedVariables(); + if (pausedVariables && fn(pausedVariables)) { + return pausedVariables; + } + + if (fn(this._thread.replVariables)) { + return this._thread.replVariables; + } + + return undefined; + } + + async _onLocations(params: Dap.LocationsParams): Promise { + const variableStore = this.findVariableStore(v => v.hasVariable(params.locationReference)); + if (!variableStore || !this._thread) throw errors.locationNotFound(); + const location = await variableStore.getLocations(params.locationReference); + const uiLocation = await this._thread.rawLocationToUiLocationWithWaiting( + this._thread.rawLocation(location), + ); + if (!uiLocation) throw errors.locationNotFound(); + return { + source: await uiLocation.source.toDap(), + line: uiLocation.lineNumber, + column: uiLocation.columnNumber, + }; + } + + async _onVariables(params: Dap.VariablesParams): Promise { + const variableStore = this.findVariableStore(v => v.hasVariable(params.variablesReference)); + return { variables: (await variableStore?.getVariables(params)) ?? [] }; + } + + async _onReadMemory(params: Dap.ReadMemoryParams): Promise { + const ref = params.memoryReference; + const memory = await this.findVariableStore(v => v.hasMemory(ref))?.readMemory( + ref, + params.offset ?? 0, + params.count, + ); + if (!memory) { + return { address: '0', unreadableBytes: params.count }; + } + + return { + address: '0', + data: memory.toString('base64'), + unreadableBytes: params.count - memory.length, + }; + } + + async _onWriteMemory(params: Dap.WriteMemoryParams): Promise { + const ref = params.memoryReference; + const bytesWritten = await this.findVariableStore(v => v.hasMemory(ref))?.writeMemory( + ref, + params.offset ?? 0, + Buffer.from(params.data, 'base64'), + ); + return { bytesWritten }; + } + + async _onSetExpression(params: Dap.SetExpressionParams): Promise { + if (!this._thread) { + throw new ProtocolError(errors.threadNotAvailable()); + } + + const r = await this._thread.evaluate({ + expression: `${params.expression} = ${sourceUtils.wrapObjectLiteral(params.value)}`, + context: 'repl', + frameId: params.frameId, + }); + + return { + value: r.result, + variablesReference: r.variablesReference, + indexedVariables: r.indexedVariables, + namedVariables: r.namedVariables, + presentationHint: r.presentationHint, + type: r.type, + memoryReference: r.memoryReference, + valueLocationReference: r.valueLocationReference, + }; + } + + async _onSetVariable(params: Dap.SetVariableParams): Promise { + const variableStore = this.findVariableStore(v => v.hasVariable(params.variablesReference)); + if (!variableStore) return errors.createSilentError(l10n.t('Variable not found')); + params.value = sourceUtils.wrapObjectLiteral(params.value.trim()); + return variableStore.setVariable(params); + } + + _withThread(callback: (thread: Thread) => Promise): Promise { + if (!this._thread) { + throw new ProtocolError(errors.threadNotAvailable()); + } + + return callback(this._thread); + } + + async _refreshStackTrace() { + if (!this._thread) return; + const details = this._thread.pausedDetails(); + if (details) await this._thread.refreshStackTrace(); + } + + createThread(cdp: Cdp.Api, target: ITarget): Thread { + this._thread = new Thread( + this.sourceContainer, + cdp, + this.dap, + target, + this._services.get(IRenameProvider), + this._services.get(ILogger), + this._services.get(IEvaluator), + this._services.get(ICompletions), + this.launchConfig, + this.breakpointManager, + this._services.get(IConsole), + this._services.get(IExceptionPauseService), + this._services.get(SmartStepper), + this._services.get(IShutdownParticipants), + this._services.get(IClientCapabilies), + ); + + const profile = this._services.get(IProfileController); + profile.connect(this.dap, this._thread); + if ('profileStartup' in this.launchConfig && this.launchConfig.profileStartup) { + profile.start(this.dap, this._thread, { type: BasicCpuProfiler.type }); + } + + this._thread.updateCustomBreakpoints(this._xhrBreakpoints, this._customBreakpoints); + + this.asyncStackPolicy + .connect(cdp) + .then(d => this._disposables.push(d)) + .catch(err => + this._services + .get(ILogger) + .error(LogTag.Internal, 'Error enabling async stacks', err) + ); + + this.breakpointManager.setThread(this._thread); + this._services.get(DiagnosticToolSuggester).attach(cdp); + this._threadDeferred.resolve(this._thread); + + return this._thread; + } + + async setCustomBreakpoints({ + ids, + xhr, + }: Dap.SetCustomBreakpointsParams): Promise { + await this._thread?.updateCustomBreakpoints(xhr, ids); + this._customBreakpoints = ids; + this._xhrBreakpoints = xhr; + return {}; + } + + async _toggleSkipFileStatus( + params: Dap.ToggleSkipFileStatusParams, + ): Promise { + await this._services.get(IScriptSkipper).toggleSkippingFile(params); + await this._refreshStackTrace(); + return {}; + } + + async _prettyPrintSource( + params: Dap.PrettyPrintSourceParams, + ): Promise { + if (!params.source || !this._thread) { + return {}; + } + + params.source.path = urlUtils.platformPathToPreferredCase(params.source.path); + const source = this.sourceContainer.source(params.source); + if (!source) { + return errors.createSilentError(l10n.t('Source not found')); + } + + const prettified = await source.prettyPrint(); + if (!prettified) { + return errors.createSilentError(l10n.t('Unable to pretty print')); + } + + const { map: sourceMap, source: generated } = prettified; + + await this.breakpointManager.moveBreakpoints(this._thread, source, sourceMap, generated); + this.sourceContainer.clearDisabledSourceMaps(source as ISourceWithMap); + const wasPaused = !!this._thread.pausedDetails(); + await this._refreshStackTrace(); + + return { source: await generated.toDap(), didReveal: wasPaused }; + } + + private onEvaluate(args: Dap.EvaluateParams): Promise { + // Rewrite the old ".scripts" command to the new diagnostic tool + if (args.expression === '.scripts') { + return this._dumpDiagnostics({ fromSuggestion: false }) + .then(this.dap.openDiagnosticTool) + .then(() => ({ result: 'Opening diagnostic tool...', variablesReference: 0 })); + } else { + return this._withThread(thread => thread.evaluate(args)); + } + } + + private async _dumpDiagnostics(params: Dap.CreateDiagnosticsParams) { + const out = { file: await this._services.get(Diagnostics).generateHtml() }; + if (params.fromSuggestion) { + this._services + .get(ITelemetryReporter) + .report('diagnosticPrompt', { event: 'opened' }); + } + + return out; + } + + public async _requestCDPProxy() { + return await this._cdpProxyProvider.proxy(); + } + + dispose() { + this._disposables.dispose(); + disposeContainer(this._services); + } +} diff --git a/code/extensions/js-debug/src/adapter/diagnosics.ts b/code/extensions/js-debug/src/adapter/diagnosics.ts new file mode 100644 index 000000000000..6635a4e9b75e --- /dev/null +++ b/code/extensions/js-debug/src/adapter/diagnosics.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { mapValues } from '../common/objUtils'; +import { ISourceMapMetadata } from '../common/sourceMaps/sourceMap'; +import { AnyLaunchConfiguration } from '../configuration'; +import Dap from '../dap/api'; +import { toolPath, toolStylePath } from '../diagnosticTool'; +import { FS, FsPromises } from '../ioc-extras'; +import { ITarget } from '../targets/targets'; +import { BreakpointManager } from './breakpoints'; +import { + CdpReferenceState, + IBreakpointCdpReferenceApplied, + IBreakpointCdpReferencePending, +} from './breakpoints/breakpointBase'; +import { isSourceWithSourceMap, IUiLocation, SourceFromMap } from './source'; +import { SourceContainer } from './sourceContainer'; + +export interface IDiagnosticSource { + uniqueId: number; + url: string; + sourceReference: number; + absolutePath: string; + actualAbsolutePath: string | undefined; + scriptIds: string[]; + prettyName: string; + compiledSourceRefToUrl?: [number, string][]; + sourceMap?: { + url: string; + metadata: ISourceMapMetadata; + sources: { [url: string]: number }; + }; +} + +export interface IDiagnosticUiLocation { + lineNumber: number; + columnNumber: number; + sourceReference: number; +} + +export type DiagnosticBreakpointArgs = + | Omit + | (Omit & { + uiLocations: IDiagnosticUiLocation[]; + }); + +export interface IDiagnosticBreakpoint { + source: Dap.Source; + params: Dap.SourceBreakpoint; + cdp: DiagnosticBreakpointArgs[]; +} + +export interface IDiagnosticDump { + sources: IDiagnosticSource[]; + breakpoints: IDiagnosticBreakpoint[]; + config: AnyLaunchConfiguration; +} + +@injectable() +export class Diagnostics { + constructor( + @inject(FS) private readonly fs: FsPromises, + @inject(SourceContainer) private readonly sources: SourceContainer, + @inject(BreakpointManager) private readonly breakpoints: BreakpointManager, + @inject(ITarget) private readonly target: ITarget, + ) {} + + /** + * Generates the a object containing information + * about sources, config, and breakpoints. + */ + public async generateObject() { + const [sources] = await Promise.all([this.dumpSources()]); + + return { + breakpoints: this.dumpBreakpoints(), + sources, + config: this.target.launchConfig, + }; + } + + /** + * Generates an HTML diagnostic report. + */ + public async generateHtml(file = join(tmpdir(), 'js-debug-diagnostics.html')) { + await this.fs.writeFile( + file, + ` + + + + + Document + + + + + + + `, + ); + + return file; + } + + private dumpBreakpoints() { + const output: IDiagnosticBreakpoint[] = []; + for (const list of [this.breakpoints.appliedByPath, this.breakpoints.appliedByRef]) { + for (const breakpoints of list.values()) { + for (const breakpoint of breakpoints) { + const dump = breakpoint.diagnosticDump(); + output.push({ + source: dump.source, + params: dump.params, + cdp: dump.cdp.map(bp => + bp.state === CdpReferenceState.Applied + ? { ...bp, uiLocations: bp.uiLocations.map(l => this.dumpUiLocation(l)) } + : { ...bp, done: undefined } + ), + }); + } + } + } + + return output; + } + + private dumpSources() { + const output: Promise[] = []; + let idCounter = 0; + for (const source of this.sources.sources) { + output.push( + (async () => ({ + uniqueId: idCounter++, + url: source.url, + sourceReference: source.sourceReference, + absolutePath: source.absolutePath, + actualAbsolutePath: await source.existingAbsolutePath(), + scriptIds: source.scripts.map(s => s.scriptId), + prettyName: await source.prettyName(), + compiledSourceRefToUrl: source instanceof SourceFromMap + ? [...source.compiledToSourceUrl.entries()].map( + ([k, v]) => [k.sourceReference, v] as [number, string], + ) + : undefined, + sourceMap: isSourceWithSourceMap(source) + ? { + url: source.sourceMap.metadata.sourceMapUrl, + metadata: source.sourceMap.metadata, + sources: mapValues( + Object.fromEntries(source.sourceMap.sourceByUrl), + v => v.sourceReference, + ), + } + : undefined, + }))(), + ); + } + + return Promise.all(output); + } + + private dumpUiLocation(location: IUiLocation): IDiagnosticUiLocation { + return { + lineNumber: location.lineNumber, + columnNumber: location.columnNumber, + sourceReference: location.source.sourceReference, + }; + } +} diff --git a/code/extensions/js-debug/src/adapter/diagnosticToolSuggester.ts b/code/extensions/js-debug/src/adapter/diagnosticToolSuggester.ts new file mode 100644 index 000000000000..7872475f52a1 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/diagnosticToolSuggester.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import Cdp from '../cdp/api'; +import { DisposableList } from '../common/disposable'; +import { EventEmitter } from '../common/events'; +import { disposableTimeout } from '../common/promiseUtil'; +import Dap from '../dap/api'; +import { IRootDapApi } from '../dap/connection'; +import { IExperimentationService } from '../telemetry/experimentationService'; +import { ITelemetryReporter } from '../telemetry/telemetryReporter'; + +const ignoredModulePatterns = /\/node_modules\/|^node\:/; +const consecutiveSessions = 2; +const suggestDelay = 5000; +const minDuration = suggestDelay / 2; + +/** + * Fires an event to indicate to the UI that it should suggest the user open + * the diagnostic tool. The indicator will be shown when all of the following + * are true: + * + * - At least one breakpoint was set, but no breakpoints bound, + * - For two consecutive debug sessions, + * - Where a sourcemap was used for a script outside of the node_modules, or + * a remoteRoot is present (since sourcemaps and remote are the cases where + * almost all path resolution issues happen) + */ +@injectable() +export class DiagnosticToolSuggester { + /** + * Number of sessions that qualify for help. The DiagnosticToolSuggester is + * a global singleton and we don't care about persistence, so this is fine. + */ + private static consecutiveQualifyingSessions = 0; + + /** + * Fired when a disqualifying event happens. This is global, since in a + * compound launch config many sessions might be launched but only one of + * them could end up qualifying. + */ + private static didVerifyEmitter = new EventEmitter(); + + /** + * Whether we recently suggested using the diagnostic tool. + */ + private static didSuggest = false; + + private readonly disposable = new DisposableList(); + private hadBreakpoint = false; + private didVerifyBreakpoint = false; + private hadNonModuleSourcemap = false; + private startedAt = Date.now(); + + private get currentlyQualifying() { + return this.hadBreakpoint && !this.didVerifyBreakpoint && this.hadNonModuleSourcemap; + } + + constructor( + @inject(IRootDapApi) dap: Dap.Api, + @inject(ITelemetryReporter) private readonly telemetry: ITelemetryReporter, + @inject(IExperimentationService) private readonly experimentation: IExperimentationService, + ) { + this.disposable.push( + DiagnosticToolSuggester.didVerifyEmitter.event(() => { + this.didVerifyBreakpoint = true; + }), + ); + + if (DiagnosticToolSuggester.consecutiveQualifyingSessions >= consecutiveSessions) { + this.disposable.push( + disposableTimeout(async () => { + if (!this.currentlyQualifying) { + return; + } + + if (!(await this.experimentation.getTreatment('diagnosticPrompt', true))) { + return; + } + + telemetry.report('diagnosticPrompt', { event: 'suggested' }); + DiagnosticToolSuggester.didSuggest = true; + dap.suggestDiagnosticTool({}); + }, suggestDelay), + ); + } + } + + public notifyHadBreakpoint() { + this.hadBreakpoint = true; + } + + public notifyVerifiedBreakpoint() { + if (this.didVerifyBreakpoint) { + return; + } + + DiagnosticToolSuggester.didVerifyEmitter.fire(); + + if (DiagnosticToolSuggester.didSuggest) { + DiagnosticToolSuggester.didSuggest = false; + this.telemetry.report('diagnosticPrompt', { event: 'resolved' }); + } + } + + /** + * Attaches the CDP API. Should be called for each + */ + public attach(cdp: Cdp.Api) { + if (!this.hadNonModuleSourcemap) { + const listener = this.disposable.push( + cdp.Debugger.on('scriptParsed', evt => { + if (!!evt.sourceMapURL && !ignoredModulePatterns.test(evt.url)) { + this.hadNonModuleSourcemap = true; + this.disposable.disposeObject(listener); + } + }), + ); + } + } + + /** + * Should be called before the root debug session ends. It'll fire a DAP + * message to show a notification if appropriate. + */ + public dispose() { + if (this.currentlyQualifying && Date.now() - minDuration > this.startedAt) { + DiagnosticToolSuggester.consecutiveQualifyingSessions++; + } else { + DiagnosticToolSuggester.consecutiveQualifyingSessions = 0; + } + + this.disposable.dispose(); + } +} diff --git a/code/extensions/js-debug/src/adapter/dwarf/dwarfModuleProvider.ts b/code/extensions/js-debug/src/adapter/dwarf/dwarfModuleProvider.ts new file mode 100644 index 000000000000..0332aeccbcf9 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/dwarf/dwarfModuleProvider.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export const IDwarfModuleProvider = Symbol('IDwarfModuleProvider'); + +export interface IDwarfModuleProvider { + /** + * Loads the dwarf module if it exists. + */ + load(): Promise; + + /** + * Prompts the user to install the dwarf module (called if the module is + * not installed.) + */ + prompt(): void; +} diff --git a/code/extensions/js-debug/src/adapter/dwarf/dwarfModuleProviderImpl.ts b/code/extensions/js-debug/src/adapter/dwarf/dwarfModuleProviderImpl.ts new file mode 100644 index 000000000000..2b8aaa37676c --- /dev/null +++ b/code/extensions/js-debug/src/adapter/dwarf/dwarfModuleProviderImpl.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import type * as dwf from '@vscode/dwarf-debugging'; +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import Dap from '../../dap/api'; +import { IDapApi } from '../../dap/connection'; +import { IDwarfModuleProvider } from './dwarfModuleProvider'; + +const name = '@vscode/dwarf-debugging'; + +@injectable() +export class DwarfModuleProvider implements IDwarfModuleProvider { + private didPrompt = false; + + constructor(@inject(IDapApi) private readonly dap: Dap.Api) {} + + public async load(): Promise { + try { + return await import(name); + } catch { + return undefined; + } + } + + public prompt() { + if (!this.didPrompt) { + this.didPrompt = true; + this.dap.output({ + output: l10n.t( + 'You may install the `{}` module via npm for enhanced WebAssembly debugging', + name, + ), + category: 'console', + }); + } + } +} diff --git a/code/extensions/js-debug/src/adapter/dwarf/wasmSymbolProvider.ts b/code/extensions/js-debug/src/adapter/dwarf/wasmSymbolProvider.ts new file mode 100644 index 000000000000..c90d13188936 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/dwarf/wasmSymbolProvider.ts @@ -0,0 +1,768 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import type { IWasmWorker, MethodReturn } from '@vscode/dwarf-debugging'; +import { Chrome } from '@vscode/dwarf-debugging/chrome-cxx/mnt/extension-api'; +import { randomUUID } from 'crypto'; +import { inject, injectable } from 'inversify'; +import Cdp from '../../cdp/api'; +import { ICdpApi } from '../../cdp/connection'; +import { binarySearch } from '../../common/arrayUtils'; +import { IDisposable } from '../../common/disposable'; +import { ILogger, LogTag } from '../../common/logging'; +import { flatten, once } from '../../common/objUtils'; +import { Base0Position, IPosition, Range } from '../../common/positions'; +import { AnyLaunchConfiguration } from '../../configuration'; +import * as errors from '../../dap/errors'; +import { ProtocolError } from '../../dap/protocolError'; +import { StepDirection } from '../pause'; +import { getSourceSuffix } from '../templates'; +import { IDwarfModuleProvider } from './dwarfModuleProvider'; + +export const IWasmSymbolProvider = Symbol('IWasmSymbolProvider'); + +export interface IWasmSymbolProvider { + /** Loads WebAssembly symbols for the given wasm script, returning symbol information if it exists. */ + loadWasmSymbols(script: Cdp.Debugger.ScriptParsedEvent): Promise; +} + +export interface IWasmWorkerExt extends IWasmWorker { + getStopId(id: string): string; +} + +export const IWasmWorkerFactory = Symbol('IWasmWorkerFactory'); + +/** Global factory that creates wasm workers for each session. */ +export interface IWasmWorkerFactory extends IDisposable { + /** + * Gets a handle to a wasm worker for the given session. + */ + spawn(cdp: Cdp.Api): Promise; + + /** + * Corresponds to {@link IDwarfModuleProvider.prompt} + */ + prompt(): void; +} + +export const ensureWATExtension = (path: string) => path.replace(/\.wasm$/i, '') + '.wat'; + +@injectable() +export class WasmWorkerFactory implements IWasmWorkerFactory { + private cdpCounter = 0; + private worker?: Promise; + + private readonly cdp = new Map(); + + constructor( + @inject(IDwarfModuleProvider) private readonly dwarf: IDwarfModuleProvider, + @inject(AnyLaunchConfiguration) private readonly launchConfig: AnyLaunchConfiguration, + ) {} + + /** @inheritdoc */ + public readonly prompt = once(() => this.dwarf.prompt()); + + /** @inheritdoc */ + public async spawn(cdp: Cdp.Api): Promise { + if (!this.launchConfig.enableDWARF) { + return null; + } + + this.worker ??= this.dwarf.load().then(dwarf => { + if (!dwarf) { + return null; + } + + const worker = dwarf.spawn({ + getWasmGlobal: (index, stopId) => this.loadWasmValue(`globals[${index}]`, stopId), + getWasmLocal: (index, stopId) => this.loadWasmValue(`locals[${index}]`, stopId), + getWasmOp: (index, stopId) => this.loadWasmValue(`stack[${index}]`, stopId), + getWasmLinearMemory: (offset, length, stopId) => + this.loadWasmValue( + `[].slice.call(new Uint8Array(memories[0].buffer, ${+offset}, ${+length}))`, + stopId, + ).then((v: number[]) => new Uint8Array(v).buffer), + }); + + worker.rpc.sendMessage('hello', [], false); + + return worker; + }); + + const worker = await this.worker; + if (!worker) { + return null; + } + + const cdpId = this.cdpCounter++; + this.cdp.set(cdpId, cdp); + + return { + rpc: worker.rpc, + getStopId: id => `${cdpId}:${id}`, + dispose: () => { + this.cdp.delete(cdpId); + if (this.cdp.size > 0) { + return Promise.resolve(); + } + this.worker = undefined; + return worker.dispose(); + }, + }; + } + + /** @inheritdoc */ + public async dispose() { + await this.worker?.then(w => w?.dispose()); + this.worker = Promise.resolve(null); + } + + private async loadWasmValue(expression: string, stopId: unknown) { + const cast = stopId as string; + const idx = cast.indexOf(':'); + + const cdpId = cast.substring(0, idx); + const callFrameId = cast.substring(idx + 1); + + const result = await this.cdp.get(+cdpId)?.Debugger.evaluateOnCallFrame({ + callFrameId, + expression: expression + getSourceSuffix(), + silent: true, + returnByValue: true, + throwOnSideEffect: true, + }); + + if (!result || result.exceptionDetails) { + throw new Error(`evaluate failed: ${result?.exceptionDetails?.text || 'unknown'}`); + } + + return result.result.value; + } +} + +@injectable() +export class WasmSymbolProvider implements IWasmSymbolProvider, IDisposable { + /** Running worker, `null` signals that the dwarf module was not available */ + private readonly worker = once(() => this.dwarf.spawn(this.cdp)); + + constructor( + @inject(IWasmWorkerFactory) private readonly dwarf: IWasmWorkerFactory, + @inject(ICdpApi) private readonly cdp: Cdp.Api, + @inject(ILogger) private readonly logger: ILogger, + @inject(AnyLaunchConfiguration) private readonly launchConfig: AnyLaunchConfiguration, + ) {} + + public async loadWasmSymbols(script: Cdp.Debugger.ScriptParsedEvent): Promise { + if (!this.launchConfig.enableDWARF) { + return this.defaultSymbols(script); + } + + const worker = await this.worker(); + if (!worker) { + const syms = this.defaultSymbols(script); + // disassembly is a good signal for a prompt, since that means a user + // will have stepped into and be looking at webassembly code. + syms.onDidDisassemble = this.dwarf.prompt; + return syms; + } + + const { rpc } = worker; + const moduleId = randomUUID(); + + let symbolsUrl: URL | undefined; + try { + const symbols = script.debugSymbols; + if (isLegacyDebugSymbols(symbols) && symbols.externalURL) { + symbolsUrl = new URL(symbols.externalURL); + } else if (Array.isArray(symbols)) { + const entry = script.debugSymbols?.find(s => + (s.type === 'EmbeddedDWARF' || s.type === 'ExternalDWARF') && s.externalURL + ); + if (entry?.externalURL) { + symbolsUrl = new URL(entry.externalURL); + } + } + } catch { + // ignored + } + + // Do the same ipv4/ipv6 attempts as we do in the IResourceProvider, but + // fetching is handled internally by the wasm module, so we manually + // attempt both loopbacks, which is a little less nice. + const scriptUrl = new URL(script.url); + const attemptHostname = scriptUrl.hostname === 'localhost' + ? ['127.0.0.1', '[::1]', 'localhost'] + : [scriptUrl.hostname]; + const symbolsAreLocalhostToo = symbolsUrl?.hostname === 'localhost'; + + let result: MethodReturn<'addRawModule'> | undefined; + for (const hostname of attemptHostname) { + scriptUrl.hostname = hostname; + if (symbolsUrl && symbolsAreLocalhostToo) { + symbolsUrl.hostname = hostname; + } + + try { + result = await rpc.sendMessage('addRawModule', moduleId, symbolsUrl?.toString(), { + url: scriptUrl.toString(), + code: !symbolsUrl && scriptUrl.protocol.startsWith('wasm:') + ? await this.getBytecode(script.scriptId) + : undefined, + }); + break; + } catch (e) { + this.logger.warn(LogTag.SourceMapParsing, `failed to load wasm symbols for ${scriptUrl}`, { + error: e, + }); + // ignored + } + } + + if (!result) { + return this.defaultSymbols(script); + } + + if (!(result instanceof Array) || result.length === 0) { + rpc.sendMessage('removeRawModule', moduleId); // no await necessary + return this.defaultSymbols(script); + } + + this.logger.info(LogTag.SourceMapParsing, 'parsed files from wasm', { files: result }); + + return new WasmSymbols(script, this.cdp, moduleId, worker, result); + } + + /** @inheritdoc */ + public async dispose() { + await this.worker.value?.then(w => w?.dispose()); + } + + private defaultSymbols(script: Cdp.Debugger.ScriptParsedEvent) { + return new DecompiledWasmSymbols(script, this.cdp, []); + } + + private async getBytecode(scriptId: string) { + const source = await this.cdp.Debugger.getScriptSource({ scriptId }); + const bytecode = source?.bytecode; + return bytecode ? Buffer.from(bytecode, 'base64').buffer : undefined; + } +} + +const isLegacyDebugSymbols = (symbols: unknown): symbols is Cdp.Debugger.DebugSymbols => { + return !!(symbols as Cdp.Debugger.DebugSymbols)?.externalURL; +}; + +export interface IWasmVariableEvaluation { + type: string; + description: string | undefined; + linearMemoryAddress?: number; + linearMemorySize?: number; + getChildren?: () => Promise<{ name: string; value: IWasmVariableEvaluation }[]>; +} + +export const enum WasmScope { + Local = 'LOCAL', + Global = 'GLOBAL', + Parameter = 'PARAMETER', +} + +export interface IWasmVariable { + scope: WasmScope; + name: string; + type: string; + evaluate: () => Promise; +} + +export interface IWasmSymbols extends IDisposable { + /** + * URL in `files` that refers to the dissembled version of the WASM. This + * is used as a fallback for locations that don't better map to a known symbol. + */ + readonly decompiledUrl: string; + + /** + * Files contained in the WASM symbols. + */ + readonly files: readonly string[]; + + /** + * Returns disassembled wasm lines. + */ + getDisassembly(): Promise; + + /** + * Gets the source position for the given position in compiled code. + * + * Following CDP semantics, it assumes the column is being the byte offset + * in webassembly. However, we encode the inline frame index in the line. + */ + originalPositionFor( + compiledPosition: IPosition, + ): Promise<{ url: string; position: IPosition } | undefined>; + + /** + * Gets the position in the disassembly for the given position in compiled code. + * + * Following CDP semantics, it assumes the column is being the byte offset + * in webassembly. However, we encode the inline frame index in the line. + */ + disassembledPositionFor( + compiledPosition: IPosition, + ): Promise<{ url: string; position: IPosition } | undefined>; + + /** + * Gets the compiled position for the given position in source code. + * + * Following CDP semantics, it assumes the position is line 0 with the column + * offset being the byte offset in webassembly. + */ + compiledPositionFor(sourceUrl: string, sourcePosition: IPosition): Promise; + + /** + * Gets variables in the program scope at the given position. If not + * implemented, the variable store should use its default behavior. + * + * Following CDP semantics, it assumes the column is being the byte offset + * in webassembly. However, we encode the inline frame index in the line. + */ + getVariablesInScope?(callFrameId: string, position: IPosition): Promise; + + /** + * Gets the stack of WASM functions at the given position. Generally this will + * return an element with a single item containing the function name. However, + * inlined functions may return multiple functions for a position. + * + * It may return an empty array if function information is not available. + * + * @see https://github.com/ChromeDevTools/devtools-frontend/blob/c9f204731633fd2e2b6999a2543e99b7cc489b4b/docs/language_extension_api.md#dealing-with-inlined-functions + */ + getFunctionStack?(position: IPosition): Promise<{ name: string }[]>; + + /** + * Evaluates the expression at a position. + * + * Following CDP semantics, it assumes the column is being the byte offset + * in webassembly. However, we encode the inline frame index in the line. + */ + evaluate?( + callFrameId: string, + position: IPosition, + expression: string, + ): Promise; + + /** + * Gets ranges that should be stepped for the given step kind and location. + * + * Following CDP semantics, it assumes the column is being the byte offset + * in webassembly. However, we encode the inline frame index in the line. + */ + getStepSkipList?( + direction: StepDirection, + position: IPosition, + sourceUrl?: string, + mappedPosition?: IPosition, + ): Promise; +} + +class DecompiledWasmSymbols implements IWasmSymbols { + /** @inheritdoc */ + public readonly decompiledUrl: string; + + /** @inheritdoc */ + public readonly files: readonly string[]; + + /** Called whenever disassembly is requested for a source/ */ + public onDidDisassemble?: () => void; + + constructor( + protected readonly event: Cdp.Debugger.ScriptParsedEvent, + protected readonly cdp: Cdp.Api, + files: string[], + ) { + this.decompiledUrl = ensureWATExtension(event.url); + files.push(this.decompiledUrl); + this.files = files; + } + + /** @inheritdoc */ + public async getDisassembly(): Promise { + const { lines } = await this.doDisassemble(); + this.onDidDisassemble?.(); + return lines.join('\n'); + } + + /** @inheritdoc */ + public originalPositionFor( + compiledPosition: IPosition, + ): Promise<{ url: string; position: IPosition } | undefined> { + return this.disassembledPositionFor(compiledPosition); + } + + /** @inheritdoc */ + public async disassembledPositionFor( + compiledPosition: IPosition, + ): Promise<{ url: string; position: IPosition } | undefined> { + const { byteOffsetsOfLines } = await this.doDisassemble(); + const lineNumber = binarySearch( + byteOffsetsOfLines, + compiledPosition.base0.columnNumber, + (a, b) => a - b, + ); + + if (lineNumber === byteOffsetsOfLines.length) { + return undefined; + } + + return { + url: this.decompiledUrl, + position: new Base0Position(lineNumber, 0), + }; + } + + /** @inheritdoc */ + public async compiledPositionFor( + sourceUrl: string, + sourcePosition: IPosition, + ): Promise { + if (sourceUrl !== this.decompiledUrl) { + return []; + } + + const { byteOffsetsOfLines } = await this.doDisassemble(); + const { lineNumber } = sourcePosition.base0; + if (lineNumber >= byteOffsetsOfLines.length) { + return []; + } + + const columnNumber = byteOffsetsOfLines[sourcePosition.base0.lineNumber]; + return [new Base0Position(0, columnNumber)]; + } + + public dispose(): void { + // no-op + } + + /** + * Memoized disassembly. Returns two things: + * + * 1. byteOffsetsOfLines: Mapping of bytecode offsets where line numbers + * begin. For example, line 42 begins at `byteOffsetsOfLines[42]`. + * 2. lines: disassembled WAT lines. + */ + private readonly doDisassemble = once(async () => { + let lines: string[] = []; + let byteOffsetsOfLines: Uint32Array | undefined; + + for await (const chunk of this.getDisassembledStream()) { + lines = lines.concat(chunk.lines); + + let start: number; + if (byteOffsetsOfLines) { + const newOffsets = new Uint32Array(byteOffsetsOfLines.length + chunk.lines.length); + start = byteOffsetsOfLines.length; + newOffsets.set(byteOffsetsOfLines); + byteOffsetsOfLines = newOffsets; + } else { + byteOffsetsOfLines = new Uint32Array(chunk.lines.length); + start = 0; + } + + for (let i = 0; i < chunk.lines.length; i++) { + byteOffsetsOfLines[start + i] = chunk.bytecodeOffsets[i]; + } + } + + byteOffsetsOfLines ??= new Uint32Array(0); + + return { lines, byteOffsetsOfLines }; + }); + + private async *getDisassembledStream() { + const { scriptId } = this.event; + const r = await this.cdp.Debugger.disassembleWasmModule({ scriptId }); + if (!r) { + return; + } + + yield r.chunk; + + while (r.streamId) { + const r2 = await this.cdp.Debugger.nextWasmDisassemblyChunk({ streamId: r.streamId }); + if (!r2) { + return; + } + yield r2.chunk; + } + } +} + +class WasmSymbols extends DecompiledWasmSymbols { + private readonly mappedLines = new Map>(); + private get codeOffset() { + return this.event.codeOffset || 0; + } + + constructor( + event: Cdp.Debugger.ScriptParsedEvent, + cdp: Cdp.Api, + private readonly moduleId: string, + private readonly worker: IWasmWorkerExt, + files: string[], + ) { + super(event, cdp, files); + } + + /** @inheritdoc */ + public override async originalPositionFor( + compiledPosition: IPosition, + ): Promise<{ url: string; position: IPosition } | undefined> { + const locations = await this.worker.rpc.sendMessage('rawLocationToSourceLocation', { + codeOffset: compiledPosition.base0.columnNumber - this.codeOffset, + inlineFrameIndex: compiledPosition.base0.lineNumber, + rawModuleId: this.moduleId, + }); + + if (!locations.length) { + return super.originalPositionFor(compiledPosition); + } + + return { + position: new Base0Position(locations[0].lineNumber, locations[0].columnNumber), + url: locations[0].sourceFileURL, + }; + } + + /** @inheritdoc */ + public override async compiledPositionFor( + sourceUrl: string, + sourcePosition: IPosition, + ): Promise { + if (sourceUrl === this.decompiledUrl) { + return super.compiledPositionFor(sourceUrl, sourcePosition); + } + + const { lineNumber, columnNumber } = sourcePosition.base0; + const locations = await this.worker.rpc.sendMessage('sourceLocationToRawLocation', { + lineNumber, + columnNumber: columnNumber === 0 ? -1 : columnNumber, + rawModuleId: this.moduleId, + sourceFileURL: sourceUrl, + }); + + // special case: unlike sourcemaps, if we resolve a location on a line + // with nothing on it, sourceLocationToRawLocation returns undefined. + // If we think this might have happened, verify it and then get + // the next mapped line and use that location. + if (columnNumber === 0 && locations.length === 0) { + const mappedLines = await this.getMappedLines(sourceUrl); + const next = mappedLines.find(l => l > lineNumber); + if (!mappedLines.includes(lineNumber) && next /* always > 0 */) { + return this.compiledPositionFor(sourceUrl, new Base0Position(next, 0)); + } + } + + // todo@connor4312: will there ever be a location in another module? + return locations + .filter(l => l.rawModuleId === this.moduleId) + .map(l => new Base0Position(0, this.codeOffset + l.startOffset)); + } + + /** @inheritdoc */ + public override dispose() { + return this.worker.rpc.sendMessage('removeRawModule', this.moduleId); + } + + /** @inheritdoc */ + public async getVariablesInScope( + callFrameId: string, + position: IPosition, + ): Promise { + const location = { + codeOffset: position.base0.columnNumber - this.codeOffset, + inlineFrameIndex: position.base0.lineNumber, + rawModuleId: this.moduleId, + }; + + const variables = await this.worker.rpc.sendMessage('listVariablesInScope', location); + + return variables.map( + (v): IWasmVariable => ({ + name: v.name, + scope: v.scope as WasmScope, + type: v.type, + evaluate: async () => { + const result = await this.worker.rpc.sendMessage( + 'evaluate', + v.name, + location, + this.worker.getStopId(callFrameId), + ); + return result ? new WasmVariableEvaluation(result, this.worker.rpc) : nullType; + }, + }), + ); + } + + /** @inheritdoc */ + public async getFunctionStack(position: IPosition): Promise<{ name: string }[]> { + const info = await this.worker.rpc.sendMessage('getFunctionInfo', { + codeOffset: position.base0.columnNumber - this.codeOffset, + inlineFrameIndex: position.base0.lineNumber, + rawModuleId: this.moduleId, + }); + + return 'frames' in info ? info.frames : []; + } + + /** @inheritdoc */ + public async getStepSkipList( + direction: StepDirection, + position: IPosition, + sourceUrl?: string, + mappedPosition?: IPosition, + ): Promise { + if (sourceUrl === this.decompiledUrl) { + return []; + } + + const thisLocation = { + codeOffset: position.base0.columnNumber - this.codeOffset, + inlineFrameIndex: position.base0.lineNumber, + rawModuleId: this.moduleId, + }; + + const getOwnLineRanges = () => { + if (!(mappedPosition && sourceUrl)) { + return []; + } + return this.worker.rpc.sendMessage('sourceLocationToRawLocation', { + lineNumber: mappedPosition.base0.lineNumber, + columnNumber: -1, + rawModuleId: this.moduleId, + sourceFileURL: sourceUrl, + }); + }; + + let rawRanges: Chrome.DevTools.RawLocationRange[]; + switch (direction) { + case StepDirection.Out: { + // Step out should step out of inline functions. + rawRanges = await this.worker.rpc.sendMessage('getInlinedFunctionRanges', thisLocation); + break; + } + case StepDirection.Over: { + // step over should both step over inline functions and any + // intermediary statements on this line, which may exist + // in WAT assembly but not in source code. + const ranges = await Promise.all([ + this.worker.rpc.sendMessage('getInlinedCalleesRanges', thisLocation), + getOwnLineRanges(), + ]); + rawRanges = flatten(ranges); + break; + } + case StepDirection.In: + // Step in should skip over any intermediary statements on this line + rawRanges = await getOwnLineRanges(); + break; + default: + rawRanges = []; + break; + } + + return rawRanges.map( + r => + new Range( + new Base0Position(0, r.startOffset + this.codeOffset), + new Base0Position(0, r.endOffset + this.codeOffset), + ), + ); + } + + /** @inheritdoc */ + public async evaluate( + callFrameId: string, + position: IPosition, + expression: string, + ): Promise { + try { + const result = await this.worker.rpc.sendMessage( + 'evaluate', + expression, + { + codeOffset: position.base0.columnNumber - this.codeOffset, + inlineFrameIndex: position.base0.lineNumber, + rawModuleId: this.moduleId, + }, + this.worker.getStopId(callFrameId), + ); + + return result ? new WasmVariableEvaluation(result, this.worker.rpc) : nullType; + } catch (e) { + // errors are expected here if the user tries to evaluate expressions + // the simple lldb-eval can't handle. + throw new ProtocolError(errors.createSilentError(e.message)); + } + } + + private getMappedLines(sourceURL: string) { + const prev = this.mappedLines.get(sourceURL); + if (prev) { + return prev; + } + + const value = (async () => { + try { + const lines = await this.worker.rpc.sendMessage( + 'getMappedLines', + this.moduleId, + sourceURL, + ); + return new Uint32Array(lines?.sort((a, b) => a - b) || []); + } catch { + return new Uint32Array(); + } + })(); + + this.mappedLines.set(sourceURL, value); + return value; + } +} + +const nullType: IWasmVariableEvaluation = { + type: 'null', + description: 'no properties', +}; + +class WasmVariableEvaluation implements IWasmVariableEvaluation { + public readonly type: string; + public readonly description: string | undefined; + public readonly linearMemoryAddress: number | undefined; + public readonly linearMemorySize: number | undefined; + + public readonly getChildren?: () => Promise<{ name: string; value: IWasmVariableEvaluation }[]>; + + constructor(evaluation: NonNullable>, rpc: IWasmWorker['rpc']) { + this.type = evaluation.type; + this.description = evaluation.description; + this.linearMemoryAddress = evaluation.linearMemoryAddress; + this.linearMemorySize = evaluation.linearMemoryAddress; + + if (evaluation.objectId && evaluation.hasChildren) { + const oid = evaluation.objectId; + this.getChildren = once(() => this._getChildren(rpc, oid)); + } + } + + private async _getChildren( + rpc: IWasmWorker['rpc'], + objectId: string, + ): Promise<{ name: string; value: IWasmVariableEvaluation }[]> { + const vars = await rpc.sendMessage('getProperties', objectId); + return vars.map(v => ({ + name: v.name, + value: new WasmVariableEvaluation(v.value, rpc), + })); + } +} diff --git a/code/extensions/js-debug/src/adapter/evaluator.test.ts b/code/extensions/js-debug/src/adapter/evaluator.test.ts new file mode 100644 index 000000000000..2e26da11815d --- /dev/null +++ b/code/extensions/js-debug/src/adapter/evaluator.test.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import Cdp from '../cdp/api'; +import { stubbedCdpApi, StubCdpApi } from '../cdp/stubbedApi'; +import { Logger } from '../common/logging/logger'; +import { Base01Position, Range } from '../common/positions'; +import { IRename, RenameMapping } from '../common/sourceMaps/renameProvider'; +import { ScopeNode } from '../common/sourceMaps/renameScopeTree'; +import { Evaluator } from './evaluator'; + +describe('Evaluator', () => { + let evaluator: Evaluator; + let stubCdp: StubCdpApi; + let renameMapping: RenameMapping; + + const result: Cdp.Debugger.EvaluateOnCallFrameResult = { + result: { + type: 'string', + value: 'foo', + }, + }; + + beforeEach(() => { + stubCdp = stubbedCdpApi(); + renameMapping = RenameMapping.None; + evaluator = new Evaluator( + stubCdp.actual, + { + provideForSource: () => renameMapping, + provideOnStackframe: () => renameMapping, + }, + Logger.null, + ); + }); + + it('prepares simple expressions', async () => { + const prep = evaluator.prepare('foo', { isInternalScript: false }); + expect(prep.canEvaluateDirectly).to.be.true; + stubCdp.Debugger.evaluateOnCallFrame.resolves(result); + expect(await prep.invoke({ callFrameId: '' })).to.equal(result); + expect(stubCdp.Debugger.evaluateOnCallFrame.args[0][0]).to.deep.equal({ + callFrameId: '', + expression: 'foo', + }); + }); + + it('appends eval source url to internal', async () => { + const prep = evaluator.prepare('foo'); + expect(prep.canEvaluateDirectly).to.be.true; + stubCdp.Debugger.evaluateOnCallFrame.resolves(result); + expect(await prep.invoke({ callFrameId: '' })).to.equal(result); + expect(stubCdp.Debugger.evaluateOnCallFrame.args[0][0].expression).to.match( + /^foo\n\/\/# sourceURL=eval/m, + ); + }); + + it('replaces renamed identifiers', async () => { + const node = new ScopeNode(Range.INFINITE); + node.data = [{ original: 'foo', compiled: 'bar' }]; + const prep = evaluator.prepare('foo', { + isInternalScript: false, + renames: { + mapping: new RenameMapping(node), + position: new Base01Position(0, 1), + }, + }); + expect(prep.canEvaluateDirectly).to.be.true; + stubCdp.Debugger.evaluateOnCallFrame.resolves(result); + expect(await prep.invoke({ callFrameId: '' })).to.equal(result); + expect(stubCdp.Debugger.evaluateOnCallFrame.args[0][0]).to.deep.equal({ + callFrameId: '', + expression: 'typeof bar !== "undefined" ? bar : foo;\n', + }); + }); + + it('does not replace identifiers in invalid contexts', async () => { + const node = new ScopeNode(Range.INFINITE); + node.data = [{ original: 'foo', compiled: 'bar' }]; + const prep = evaluator.prepare( + `const baz = foo; +z.find(foo => true) +const { foo } = z; +for (const { foo } of z) {} +try {} catch ({ foo }) {}`, + { + isInternalScript: false, + renames: { + mapping: new RenameMapping(node), + position: new Base01Position(0, 1), + }, + }, + ); + expect(prep.canEvaluateDirectly).to.be.true; + stubCdp.Debugger.evaluateOnCallFrame.resolves(result); + expect(await prep.invoke({ callFrameId: '' })).to.equal(result); + expect(stubCdp.Debugger.evaluateOnCallFrame.args[0][0]).to.deep.equal({ + callFrameId: '', + expression: [ + `const baz = typeof bar !== \"undefined\" ? bar : foo;`, + `z.find(bar => true);`, + `const {bar} = z;`, + `for (const {bar} of z) {}`, + `try {} catch ({bar}) {}`, + '', + ].join('\n'), + }); + }); +}); diff --git a/code/extensions/js-debug/src/adapter/evaluator.ts b/code/extensions/js-debug/src/adapter/evaluator.ts new file mode 100644 index 000000000000..3259356f3219 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/evaluator.ts @@ -0,0 +1,461 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Node as AcornNode } from 'acorn'; +import { generate } from 'astring'; +import { randomBytes } from 'crypto'; +import { ConditionalExpression, Expression } from 'estree'; +import { inject, injectable } from 'inversify'; +import Cdp from '../cdp/api'; +import { ICdpApi } from '../cdp/connection'; +import { ILogger, LogTag } from '../common/logging'; +import { Base01Position, Base1Position, IPosition, Range } from '../common/positions'; +import { findIndexAsync } from '../common/promiseUtil'; +import { parseProgram, replace } from '../common/sourceCodeManipulations'; +import { IRenameProvider, RenameMapping } from '../common/sourceMaps/renameProvider'; +import { isInPatternSlot } from '../common/sourceUtils'; +import { Source } from './source'; +import { StackFrame } from './stackTrace'; +import { getSourceSuffix } from './templates'; +import { VariableStore } from './variableStore'; + +export const returnValueStr = '$returnValue'; + +const hoistedPrefix = '__js_debug_hoisted_'; + +const makeHoistedName = () => hoistedPrefix + randomBytes(8).toString('hex'); + +export const IEvaluator = Symbol('IEvaluator'); + +export type PreparedHoistFn = ( + variable: string, +) => Promise | Cdp.Runtime.RemoteObject | undefined; + +/** + * Prepared call that can be invoked later on a callframe.. + */ +export type PreparedCallFrameExpr = ( + params: Omit, + hoist?: PreparedHoistFn, +) => Promise; + +/** + * Evaluation wraps CDP evaluation requests with additional functionality. + */ +export interface IEvaluator { + /** + * Prepares an expression for later evaluation. Returns whether the + * expression could be run immediately against Chrome without passing through + * the evaluator, and an "invoke" used to call the function. + * + * The "canEvaluate" flag is used in logpoint breakpoints to determine + * whether we actually need to pause for a custom log evaluation, or whether + * we can just send the logpoint as the breakpoint condition directly. + */ + prepare( + expression: string, + options?: IPrepareOptions, + ): { canEvaluateDirectly: boolean; invoke: PreparedCallFrameExpr }; + + /** + * Evaluates the expression on a call frame. This allows + * referencing the $returnValue + */ + evaluate( + params: Cdp.Debugger.EvaluateOnCallFrameParams, + options?: IEvaluateOptions, + ): Promise; + + /** + * Evaluates the expression the runtime. + */ + evaluate( + params: Cdp.Runtime.EvaluateParams, + options?: IEvaluateOptions, + ): Promise; + + /** + * Evaluates the expression the runtime or call frame. + */ + evaluate( + params: Cdp.Runtime.EvaluateParams | Cdp.Debugger.EvaluateOnCallFrameParams, + options?: IEvaluateOptions, + ): Promise; + + /** + * Sets or unsets the last stackframe returned value. + */ + setReturnedValue(value?: Cdp.Runtime.RemoteObject): void; + + /** + * Gets whether a return value is currently set. + */ + readonly hasReturnValue: boolean; +} + +interface IEvaluatorBaseOptions { + /** + * Whether the script is 'internal' and should + * not be shown in the sources directory. + */ + isInternalScript?: boolean; + + /** + * A list of ranges in the expression where transformations can happen. + * If this is present, any expression outside of the ranges will be + * be untouched. + */ + transformRanges?: Range[]; +} + +export interface IPrepareOptions extends IEvaluatorBaseOptions { + /** + * Replaces the identifiers in the associated script with references to the + * given remote objects. + */ + hoist?: ReadonlyArray; + + /** + * Optional information used to rename identifiers. + */ + renames?: RenamePrepareOptions; +} + +export type RenamePrepareOptions = { position: IPosition; mapping: RenameMapping }; + +export type LocationEvaluateOptions = { + source: Source; + position: IPosition; + variables: VariableStore; +}; + +export interface IEvaluateOptions extends IEvaluatorBaseOptions { + /** + * Stack frame object on which the evaluation is being run. This is + * necessary to allow for renamed properties. + */ + stackFrame?: StackFrame; + + /** + * A manually-provided source location for the evaluation, as an alternative + * to {@link stackFrame} + */ + location?: LocationEvaluateOptions; +} + +/** + * Evaluation wraps CDP evaluation requests with additional functionality. + */ +@injectable() +export class Evaluator implements IEvaluator { + private returnValue: Cdp.Runtime.RemoteObject | undefined; + + /** + * @inheritdoc + */ + public get hasReturnValue() { + return !!this.returnValue; + } + + constructor( + @inject(ICdpApi) private readonly cdp: Cdp.Api, + @inject(IRenameProvider) private readonly renameProvider: IRenameProvider, + @inject(ILogger) private readonly logger: ILogger, + ) {} + + /** + * @inheritdoc + */ + public setReturnedValue(value?: Cdp.Runtime.RemoteObject) { + this.returnValue = value; + } + + /** + * @inheritdoc + */ + public prepare( + expression: string, + { isInternalScript, hoist, renames, transformRanges }: IPrepareOptions = {}, + ): { canEvaluateDirectly: boolean; invoke: PreparedCallFrameExpr } { + if (isInternalScript !== false) { + expression += getSourceSuffix(); + } + + // CDP gives us a way to evaluate a function in the context of a given + // object ID. What we do to make returnValue work is to hoist the return + // object onto `globalThis`, replace reference in the expression, then + // evalute the expression and unhoist it from the globals. + const toHoist = new Map(); + toHoist.set(returnValueStr, makeHoistedName()); + for (const key of hoist ?? []) { + toHoist.set(key, makeHoistedName()); + } + + let { transformed, hoisted } = this.replaceVariableInExpression( + expression, + toHoist, + renames, + transformRanges, + ); + if (!hoisted.size) { + return { + canEvaluateDirectly: true, + invoke: params => + this.cdp.Debugger.evaluateOnCallFrame({ ...params, expression: transformed }), + }; + } + + return { + canEvaluateDirectly: false, + invoke: (params, doHoist) => + Promise.all( + [...toHoist].map(async ([ident, hoisted]) => { + const ok = await this.hoistValue( + ident === returnValueStr ? this.returnValue : await doHoist?.(ident), + hoisted, + ); + + if (!ok) { + // naive replace here since the identifier is a complex random + // string and not likely to exist in the expression otherwise + transformed = transformed.replaceAll(hoisted, ident); + } + }), + ).then(() => this.cdp.Debugger.evaluateOnCallFrame({ ...params, expression: transformed })), + }; + } + + /** + * @inheritdoc + */ + public evaluate( + params: Cdp.Debugger.EvaluateOnCallFrameParams, + ): Promise; + public evaluate( + params: Cdp.Runtime.EvaluateParams, + options?: IEvaluateOptions, + ): Promise; + public async evaluate( + params: Cdp.Debugger.EvaluateOnCallFrameParams | Cdp.Runtime.EvaluateParams, + options: IEvaluateOptions = {}, + ) { + // no call frame means there will not be any relevant $returnValue to reference + if (!('callFrameId' in params)) { + return this.cdp.Runtime.evaluate(params); + } + + const prepareOptions: IPrepareOptions | undefined = { ...options }; + const { location, stackFrame } = options; + let hoist: PreparedHoistFn | undefined; + if (location) { + await Promise.all([ + // 1. Get the rename mapping at the desired position + Promise.resolve(this.renameProvider.provideForSource(location.source)).then(mapping => { + prepareOptions.renames = { mapping, position: location.position }; + }), + // 2. Hoist variables that may be shadowed. Identify the scope containing + // the location and mark any variables that appear in a higher scope + // (and therefore could be shadowed) as hoistable. + stackFrame + && this.setupShadowedVariableHoisting(location, stackFrame).then(r => { + if (r) { + hoist = r.doHoist; + prepareOptions.hoist = [...r.hoistable]; + } + }), + ]); + } else if (stackFrame) { + const mapping = await this.renameProvider.provideOnStackframe(stackFrame); + prepareOptions.renames = { mapping, position: stackFrame.rawPosition }; + } + + return this.prepare(params.expression, prepareOptions).invoke(params, hoist); + } + + /** + * Hoists the return value of the expression to the `globalThis`. + * Returns whether the hoisting was successful. + */ + private async hoistValue( + object: Cdp.Runtime.RemoteObject | undefined, + hoistedVar: string, + ): Promise { + if (object === undefined) { + return false; + } + + const objectId = object?.objectId; + const dehoist = `setTimeout(() => { delete globalThis.${hoistedVar} }, 0)`; + + let r: Cdp.Runtime.CallFunctionOnResult | Cdp.Runtime.EvaluateResult | undefined; + if (objectId) { + r = await this.cdp.Runtime.callFunctionOn({ + objectId, + functionDeclaration: + `function() { globalThis.${hoistedVar} = this; ${dehoist}; ${getSourceSuffix()} }`, + }); + } else { + r = await this.cdp.Runtime.evaluate({ + expression: `globalThis.${hoistedVar} = ${JSON.stringify(object?.value)};` + + `${dehoist};` + + getSourceSuffix(), + }); + } + return !!r && !r.exceptionDetails; + } + + /** + * Returns shadowed variables at the given location in the stack's scopes + * and a function that can be used to hoist the variables. + * + * It does this by identifying the scope the evaluation is being run in, + * marking all variables found in scopes above it as hoistable, and + * creating a function that will return the RemoteObject of a given + * shadowed variable. + */ + private async setupShadowedVariableHoisting( + { position, source, variables }: LocationEvaluateOptions, + stackFrame: StackFrame, + ) { + const { scopes } = await stackFrame.scopes(); + + const scopeIndex = await findIndexAsync( + scopes, + async s => + s.source + && s.line + && s.endLine + && new Range( + new Base1Position(s.line, s.column || 1), + new Base1Position(s.endLine, s.endColumn || Infinity), + ).contains(position) + && (await source.equalsDap(s.source)), + ); + if (scopeIndex === -1) { + return; + } + + this.logger.verbose( + LogTag.Runtime, + `Evaluating expression in scope ${scopes[scopeIndex].name}`, + ); + + const hoistable = new Set(); + await Promise.all( + scopes.slice(0, scopeIndex).map(async s => { + const vars = await variables.getVariableNames({ + variablesReference: s.variablesReference, + }); + for (const { name } of vars) { + hoistable.add(name); + } + }), + ); + + const doHoist: PreparedHoistFn = async variable => { + for (let i = scopeIndex; i < scopes.length; i++) { + const vars = await variables.getVariableNames({ + variablesReference: scopes[i].variablesReference, + }); + return vars.find(v => v.name === variable)?.remoteObject; + } + }; + + return { hoistable, doHoist }; + } + + /** + * Replaces a variable in the given expression with the `hoisted` variable, + * returning the identifiers which were hoisted. + */ + private replaceVariableInExpression( + expr: string, + hoistMap: Map, + renames: RenamePrepareOptions | undefined, + transformRanges: Range[] | undefined, + ): { hoisted: Set; transformed: string } { + const hoisted = new Set(); + let mutated = false; + + const replacement = (name: string, fallback: Expression): ConditionalExpression => ({ + type: 'ConditionalExpression', + test: { + type: 'BinaryExpression', + left: { + type: 'UnaryExpression', + operator: 'typeof', + prefix: true, + argument: { type: 'Identifier', name }, + }, + operator: '!==', + right: { type: 'Literal', value: 'undefined' }, + }, + consequent: { type: 'Identifier', name }, + alternate: fallback, + }); + + const parents: Node[] = []; + const program = parseProgram(expr); + const transformed = replace(program, { + enter(node, parent) { + const asAcorn = node as AcornNode; + if (node.type !== 'Identifier' || expr[asAcorn.start - 1] === '.') { + return; + } + + if (transformRanges) { + if (!node.loc) { + throw new Error('Node should have location'); + } + + const lr = new Range( + new Base01Position(node.loc.start.line, node.loc.start.column), + new Base01Position(node.loc.end.line, node.loc.end.column), + ); + if (!transformRanges.some(r => r.containsRange(lr))) { + return; + } + } + + const hoistName = hoistMap.get(node.name); + if (hoistName) { + hoisted.add(node.name); + mutated = true; + return { + replace: isInPatternSlot(node, parent) + ? { type: 'Identifier', name: hoistName } + : replacement(hoistName, undefinedExpression), + }; + } + + const cname = renames?.mapping.getCompiledName(node.name, renames.position); + if (cname) { + mutated = true; + return { + replace: isInPatternSlot(node, parent) + ? { type: 'Identifier', name: cname } + : replacement(cname, node), + }; + } + }, + leave: () => { + parents.pop(); + }, + }); + + if (!mutated) { + return { hoisted, transformed: expr }; + } + + // preserve any trailing comment, which might be something like `sourceURL=...` + // see https://github.com/microsoft/vscode-js-debug/issues/1259#issuecomment-1442584596 + const stmtsEnd = (program.body[program.body.length - 1] as AcornNode).end; + return { hoisted, transformed: generate(transformed) + expr.slice(stmtsEnd) }; + } +} + +const undefinedExpression: Expression = { + type: 'Identifier', + name: 'undefined', +}; diff --git a/code/extensions/js-debug/src/adapter/exceptionPauseService.test.ts b/code/extensions/js-debug/src/adapter/exceptionPauseService.test.ts new file mode 100644 index 000000000000..7a8d264ecf5f --- /dev/null +++ b/code/extensions/js-debug/src/adapter/exceptionPauseService.test.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { SinonStub, stub } from 'sinon'; +import Cdp from '../cdp/api'; +import { stubbedCdpApi, StubCdpApi } from '../cdp/stubbedApi'; +import { upcastPartial } from '../common/objUtils'; +import { AnyLaunchConfiguration } from '../configuration'; +import Dap from '../dap/api'; +import { stubbedDapApi, StubDapApi } from '../dap/stubbedApi'; +import { assertNotResolved, assertResolved } from '../test/asserts'; +import { IEvaluator } from './evaluator'; +import { ExceptionPauseService, PauseOnExceptionsState } from './exceptionPauseService'; +import { ScriptSkipper } from './scriptSkipper/implementation'; +import { SourceContainer } from './sourceContainer'; + +describe('ExceptionPauseService', () => { + let prepareEval: SinonStub; + let isScriptSkipped: SinonStub; + let ep: ExceptionPauseService; + let stubDap: StubDapApi; + let stubCdp: StubCdpApi; + let getScriptById: SinonStub; + + beforeEach(() => { + prepareEval = stub(); + isScriptSkipped = stub().returns(false); + stubDap = stubbedDapApi(); + stubCdp = stubbedCdpApi(); + getScriptById = stub(); + ep = new ExceptionPauseService( + upcastPartial({ prepare: prepareEval }), + upcastPartial({ isScriptSkipped }), + stubDap as unknown as Dap.Api, + upcastPartial({}), + upcastPartial({ getScriptById, getSourceScriptById: getScriptById }), + ); + }); + + it('does not set pause state when bps not configured', async () => { + await ep.apply(stubCdp.actual); + expect(stubCdp.Debugger.setPauseOnExceptions.callCount).to.equal(0); + await assertResolved(ep.launchBlocker); + }); + + it('sets breakpoints in cdp before binding', async () => { + await ep.setBreakpoints({ filters: [PauseOnExceptionsState.All] }); + await assertNotResolved(ep.launchBlocker); + await ep.apply(stubCdp.actual); + expect(stubCdp.Debugger.setPauseOnExceptions.calledWith({ state: 'all' })).to.be.true; + await assertResolved(ep.launchBlocker); + }); + + it('sets breakpoints in cdp after binding', async () => { + await ep.apply(stubCdp.actual); + await ep.setBreakpoints({ filters: [PauseOnExceptionsState.All] }); + expect(stubCdp.Debugger.setPauseOnExceptions.calledWith({ state: 'all' })).to.be.true; + await assertResolved(ep.launchBlocker); + }); + + it('unsets pause state', async () => { + await ep.apply(stubCdp.actual); + await ep.setBreakpoints({ filters: [PauseOnExceptionsState.All] }); + expect(stubCdp.Debugger.setPauseOnExceptions.calledWith({ state: 'all' })).to.be.true; + await ep.setBreakpoints({ filters: [PauseOnExceptionsState.None] }); + expect(stubCdp.Debugger.setPauseOnExceptions.calledWith({ state: 'none' })).to.be.true; + }); + + it('changes pause state', async () => { + await ep.apply(stubCdp.actual); + await ep.setBreakpoints({ filters: [PauseOnExceptionsState.All] }); + expect(stubCdp.Debugger.setPauseOnExceptions.calledWith({ state: 'all' })).to.be.true; + await ep.setBreakpoints({ filters: [PauseOnExceptionsState.Uncaught] }); + expect(stubCdp.Debugger.setPauseOnExceptions.calledWith({ state: 'uncaught' })).to.be.true; + }); + + it('prints an error on conditional breakpoint parse error', async () => { + await ep.apply(stubCdp.actual); + await ep.setBreakpoints({ + filters: [], + filterOptions: [{ filterId: PauseOnExceptionsState.All, condition: '(' }], + }); + expect(stubDap.output.args).to.containSubset([[{ category: 'stderr' }]]); + expect(stubCdp.Debugger.setPauseOnExceptions.called).to.be.false; + }); + + it('does not pause if script skipped', async () => { + await ep.apply(stubCdp.actual); + await ep.setBreakpoints({ filters: [PauseOnExceptionsState.All] }); + getScriptById.withArgs('42').returns({ url: 'file:///skipped' }); + getScriptById.withArgs('43').returns({ url: 'file:///not-skipped' }); + isScriptSkipped.withArgs('file:///skipped').returns(true); + isScriptSkipped.withArgs('file:///not-skipped').returns(false); + + expect( + await ep.shouldPauseAt({ + callFrames: [{ location: { scriptId: '42' } } as unknown as Cdp.Debugger.CallFrame], + reason: 'exception', + }), + ).to.be.false; + + expect( + await ep.shouldPauseAt({ + callFrames: [{ location: { scriptId: '43' } } as unknown as Cdp.Debugger.CallFrame], + reason: 'exception', + }), + ).to.be.true; + }); + it('prepares an expression if a condition is given', async () => { + const expr = stub(); + prepareEval.returns({ invoke: expr }); + + await ep.apply(stubCdp.actual); + await ep.setBreakpoints({ + filters: [], + filterOptions: [{ filterId: PauseOnExceptionsState.All, condition: 'error.name == "hi"' }], + }); + expect(prepareEval.args[0]).to.deep.equal([ + '(()=>{try{return !!(error.name == "hi");}catch(e){console.error(`Breakpoint condition error: ${e.message||e}`);return false}})()', + { hoist: ['error'] }, + ]); + expect(stubDap.output.called).to.be.false; + expect(stubCdp.Debugger.setPauseOnExceptions.calledWith({ state: 'all' })).to.be.true; + + expr + .onFirstCall() + .resolves({ result: { value: true } }) + .onSecondCall() + .resolves({ result: { value: false } }); + + expect( + await ep.shouldPauseAt({ + callFrames: [ + upcastPartial({ + callFrameId: '1', + location: upcastPartial({ scriptId: '42' }), + }), + ], + reason: 'exception', + data: 'oh no!', + }), + ).to.be.true; + + expect( + await ep.shouldPauseAt({ + callFrames: [ + upcastPartial({ + callFrameId: '1', + location: upcastPartial({ scriptId: '42' }), + }), + ], + reason: 'exception', + data: 'oh no!', + }), + ).to.be.false; + }); +}); diff --git a/code/extensions/js-debug/src/adapter/exceptionPauseService.ts b/code/extensions/js-debug/src/adapter/exceptionPauseService.ts new file mode 100644 index 000000000000..d7027656e6b3 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/exceptionPauseService.ts @@ -0,0 +1,264 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import Cdp from '../cdp/api'; +import { truthy } from '../common/objUtils'; +import { getDeferred } from '../common/promiseUtil'; +import { getSyntaxErrorIn, SourceConstants } from '../common/sourceUtils'; +import { AnyLaunchConfiguration } from '../configuration'; +import Dap from '../dap/api'; +import { IDapApi } from '../dap/connection'; +import { invalidBreakPointCondition } from '../dap/errors'; +import { ProtocolError } from '../dap/protocolError'; +import { wrapBreakCondition } from './breakpoints/conditions/expression'; +import { IEvaluator, PreparedCallFrameExpr } from './evaluator'; +import { IScriptSkipper } from './scriptSkipper/scriptSkipper'; +import { SourceContainer } from './sourceContainer'; + +export interface IExceptionPauseService { + readonly launchBlocker: Promise; + + /** + * Updates the breakpoint pause state in the service. + */ + setBreakpoints(params: Dap.SetExceptionBreakpointsParams): Promise; + + /** + * Gets whether the exception pause service would like the debugger to + * remain paused at the given point. Will return false if the event is + * not an exception pause. + */ + shouldPauseAt(evt: Cdp.Debugger.PausedEvent): Promise; + + /** + * Applies the exception pause service to the CDP connection. This should + * be called only after the Debugger domain has been enabled. + */ + apply(cdp: Cdp.Api): Promise; +} + +export const IExceptionPauseService = Symbol('IExceptionPauseService'); + +export const enum PauseOnExceptionsState { + None = 'none', + All = 'all', + Uncaught = 'uncaught', +} + +type ActivePause = { + cdp: PauseOnExceptionsState.All | PauseOnExceptionsState.Uncaught; + condition: { caught?: PreparedCallFrameExpr; uncaught?: PreparedCallFrameExpr }; +}; + +/** + * Internal representation of set exception breakpoints. For conditional + * exception breakpoints, we instruct CDP to pause on all exceptions, but + * then run expressions and check their truthiness to figure out if we + * should actually stop. + */ +type PauseOnExceptions = { cdp: PauseOnExceptionsState.None } | ActivePause; + +@injectable() +export class ExceptionPauseService implements IExceptionPauseService { + private state: PauseOnExceptions = { cdp: PauseOnExceptionsState.None }; + private cdp?: Cdp.Api; + private breakOnError: boolean; + private noDebug: boolean; + private blocker = getDeferred(); + + public get launchBlocker() { + return this.blocker.promise; + } + + constructor( + @inject(IEvaluator) private readonly evaluator: IEvaluator, + @inject(IScriptSkipper) private readonly scriptSkipper: IScriptSkipper, + @inject(IDapApi) private readonly dap: Dap.Api, + @inject(AnyLaunchConfiguration) launchConfig: AnyLaunchConfiguration, + @inject(SourceContainer) private readonly sourceContainer: SourceContainer, + ) { + this.noDebug = !!launchConfig.noDebug; + this.breakOnError = launchConfig.__breakOnConditionalError; + this.blocker.resolve(); + } + + /** + * @inheritdoc + */ + public async setBreakpoints(params: Dap.SetExceptionBreakpointsParams) { + if (this.noDebug) { + return; + } + + try { + this.state = this.parseBreakpointRequest(params); + } catch (e) { + if (!(e instanceof ProtocolError)) { + throw e; + } + this.dap.output({ category: 'stderr', output: e.message }); + return; + } + + if (this.cdp) { + await this.sendToCdp(this.cdp); + } else if (this.state.cdp !== PauseOnExceptionsState.None && this.blocker.hasSettled()) { + this.blocker = getDeferred(); + } + } + + /** + * @inheritdoc + */ + public async shouldPauseAt(evt: Cdp.Debugger.PausedEvent) { + if ( + (evt.reason !== 'exception' && evt.reason !== 'promiseRejection') + || this.state.cdp === PauseOnExceptionsState.None + ) { + return false; + } + + // If there's an internal frame anywhere in the stack, this call is from + // some internally-executed script not visible for the user. Never pause + // if this results in an exception: the caller should handle it. + if ( + evt.callFrames.some(cf => + this.sourceContainer + .getSourceScriptById(cf.location.scriptId) + ?.url.endsWith(SourceConstants.InternalExtension) + ) + ) { + return false; + } + + if (this.shouldScriptSkip(evt)) { + return false; + } + + const cond = this.state.condition; + if (evt.data?.uncaught) { + if (cond.uncaught && !(await this.evalCondition(evt, cond.uncaught))) { + return false; + } + } else if (cond.caught) { + if (!(await this.evalCondition(evt, cond.caught))) { + return false; + } + } + + return true; + } + + /** + * @inheritdoc + */ + public async apply(cdp: Cdp.Api) { + this.cdp = cdp; + + if (this.state.cdp !== PauseOnExceptionsState.None) { + await this.sendToCdp(cdp); + } + } + + private async sendToCdp(cdp: Cdp.Api) { + await cdp.Debugger.setPauseOnExceptions({ state: this.state.cdp }); + this.blocker.resolve(); + } + + private async evalCondition(evt: Cdp.Debugger.PausedEvent, method: PreparedCallFrameExpr) { + const r = await method( + { callFrameId: evt.callFrames[0].callFrameId }, + v => v === 'error' ? evt.data : undefined, + ); + return !!r?.result.value; + } + + /** + * Setting blackbox patterns is asynchronous to when the source is loaded, + * so if the user asks to pause on exceptions the runtime may pause in a + * place where we don't want it to. Double check at this point and manually + * resume debugging for handled exceptions. This implementation seems to + * work identically to blackboxing (test cases represent this): + * + * - ✅ An error is thrown and caught within skipFiles. Resumed here. + * - ✅ An uncaught error is re/thrown within skipFiles. In both cases the + * stack is reported at the first non-skipped file is shown. + * - ✅ An error is thrown from skipFiles and caught in user code. In both + * blackboxing and this version, the debugger will not pause. + * - ✅ An error is thrown anywhere in user code. All good. + * + * See: https://github.com/microsoft/vscode-js-debug/issues/644 + */ + private shouldScriptSkip(evt: Cdp.Debugger.PausedEvent) { + if (evt.data?.uncaught || !evt.callFrames.length) { + return false; + } + + const script = this.sourceContainer.getScriptById(evt.callFrames[0].location.scriptId); + return !!script && this.scriptSkipper.isScriptSkipped(script.url); + } + + /** + * Parses the breakpoint request into the "PauseOnException" type for easier + * handling internally. + */ + protected parseBreakpointRequest(params: Dap.SetExceptionBreakpointsParams): PauseOnExceptions { + const filters = (params.filterOptions ?? []).concat( + params.filters.map(filterId => ({ filterId })), + ); + + let cdp = PauseOnExceptionsState.None; + const caughtConditions: string[] = []; + const uncaughtConditions: string[] = []; + + for (const { filterId, condition } of filters) { + if (filterId === PauseOnExceptionsState.All) { + cdp = PauseOnExceptionsState.All; + if (condition) { + caughtConditions.push(filterId); + } + } else if (filterId === PauseOnExceptionsState.Uncaught) { + if (cdp === PauseOnExceptionsState.None) { + cdp = PauseOnExceptionsState.Uncaught; + } + if (condition) { + uncaughtConditions.push(filterId); + } + } + } + + const compile = (condition: string[]) => { + if (condition.length === 0) { + return undefined; + } + + const expr = '!!(' + + filters + .map(f => f.condition) + .filter(truthy) + .join(') || !!(') + + ')'; + + const err = getSyntaxErrorIn(expr); + if (err) { + throw new ProtocolError( + invalidBreakPointCondition({ line: 0, condition: expr }, err.message), + ); + } + + const wrapped = wrapBreakCondition(expr, this.breakOnError); + return this.evaluator.prepare(wrapped, { hoist: ['error'] }).invoke; + }; + + if (cdp === PauseOnExceptionsState.None) { + return { cdp }; + } else { + return { + cdp, + condition: { caught: compile(caughtConditions), uncaught: compile(uncaughtConditions) }, + }; + } + } +} diff --git a/code/extensions/js-debug/src/adapter/messageFormat.ts b/code/extensions/js-debug/src/adapter/messageFormat.ts new file mode 100644 index 000000000000..b4d17e17e77c --- /dev/null +++ b/code/extensions/js-debug/src/adapter/messageFormat.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Color from 'color'; +import { BudgetStringBuilder } from '../common/budgetStringBuilder'; +import { IPreviewContext } from './objectPreview/contexts'; + +export type FormatToken = + | { type: 'string'; value: string } + | { type: 'specifier'; specifier: string; precision?: number; substitutionIndex: number }; + +const maxMessageFormatLength = 10000; + +export type Formatters = Map string>; + +function tokenizeFormatString(format: string, formatterNames: string[]): FormatToken[] { + if (!format.includes('%')) { + return [{ type: 'string', value: format }]; // happy path, no formatting needed + } + + const tokens: FormatToken[] = []; + + function addStringToken(str: string) { + if (!str) return; + const lastToken = tokens[tokens.length - 1]; + if (lastToken?.type === 'string') lastToken.value += str; + else tokens.push({ type: 'string', value: str }); + } + + function addSpecifierToken( + specifier: string, + precision: number | undefined, + substitutionIndex: number, + ) { + tokens.push({ + type: 'specifier', + specifier: specifier, + precision: precision, + substitutionIndex, + }); + } + + let textStart = 0; + let substitutionIndex = 0; + const re = new RegExp(`%%|%(?:(\\d+)\\$)?(?:\\.(\\d*))?([${formatterNames.join('')}])`, 'g'); + for (let match = re.exec(format); !!match; match = re.exec(format)) { + const matchStart = match.index; + if (matchStart > textStart) addStringToken(format.substring(textStart, matchStart)); + + if (match[0] === '%%') { + addStringToken('%'); + } else { + const [, substitionString, precisionString, specifierString] = match; + if (substitionString && Number(substitionString) > 0) { + substitutionIndex = Number(substitionString) - 1; + } + const precision = precisionString ? Number(precisionString) : undefined; + addSpecifierToken(specifierString, precision, substitutionIndex); + ++substitutionIndex; + } + textStart = matchStart + match[0].length; + } + addStringToken(format.substring(textStart)); + return tokens; +} + +export function formatMessage( + format: string, + substitutions: ReadonlyArray, + formatters: Formatters, +): { result: string; usedAllSubs: boolean } { + const tokens = tokenizeFormatString(format, Array.from(formatters.keys())); + const usedSubstitutionIndexes = new Set(); + const defaultFormatter = formatters.get(''); + if (!defaultFormatter) { + throw new Error('Expected to hav a default formatter'); + } + + const builder = new BudgetStringBuilder(maxMessageFormatLength); + let cssFormatApplied = false; + for (let i = 0; builder.checkBudget() && i < tokens.length; ++i) { + const token = tokens[i]; + if (token.type === 'string') { + builder.append(token.value); + continue; + } + + const index = token.substitutionIndex; + if (index >= substitutions.length) { + // If there are not enough substitutions for the current substitutionIndex + // just output the format specifier literally and move on. + builder.append('%' + (token.precision || '') + token.specifier); + continue; + } + usedSubstitutionIndexes.add(index); + if (token.specifier === 'c') cssFormatApplied = true; + const formatter = formatters.get(token.specifier) || defaultFormatter; + builder.append( + formatter(substitutions[index], { budget: builder.budget(), quoted: false, ansi: true }), + ); + } + + if (cssFormatApplied) builder.append('\x1b[0m'); // clear format + + for (let i = 0; builder.checkBudget() && i < substitutions.length; ++i) { + if (usedSubstitutionIndexes.has(i)) continue; + usedSubstitutionIndexes.add(i); + if (format || i) { + // either we are second argument or we had format. + builder.append(' '); + } + builder.append( + defaultFormatter(substitutions[i], { budget: builder.budget(), quoted: false, ansi: true }), + ); + } + + return { + result: builder.build(), + usedAllSubs: usedSubstitutionIndexes.size === substitutions.length, + }; +} + +function escapeAnsiColor(colorString: string): number | undefined { + try { + // Color can parse hex and color names + const color = new Color(colorString); + return color.ansi256().object().ansi256; + } catch (ex) { + // Unable to parse Color + // For instance, "inherit" color will throw + } + return undefined; +} + +export function formatCssAsAnsi(style: string): string { + const cssRegex = /\s*(.*?)\s*:\s*(.*?)\s*(?:;|$)/g; + let escapedSequence = '\x1b[0m'; + let match = cssRegex.exec(style); + while (match !== null) { + if (match.length === 3) { + switch (match[1]) { + case 'color': + const color = escapeAnsiColor(match[2]); + if (color) escapedSequence += `\x1b[38;5;${color}m`; + break; + case 'background': + case 'background-color': + const background = escapeAnsiColor(match[2]); + if (background) escapedSequence += `\x1b[48;5;${background}m`; + break; + case 'font-weight': + if (match[2] === 'bold') escapedSequence += AnsiStyles.Bold; + break; + case 'font-style': + if (match[2] === 'italic') escapedSequence += AnsiStyles.Italic; + break; + case 'text-decoration': + if (match[2] === 'underline') escapedSequence += AnsiStyles.Underline; + break; + default: + // css not mapped, skip + } + } + + match = cssRegex.exec(style); + } + + return escapedSequence; +} + +export const enum AnsiStyles { + Reset = '\x1b[0m', + Bold = '\x1b[1m', + Dim = '\x1b[2m', + Italic = '\x1b[3m', + Underline = '\x1b[4m', + Blink = '\x1b[5m', + Reverse = '\x1b[7m', + Hidden = '\x1b[8m', + Strikethrough = '\x1b[9m', + Black = '\x1b[30m', + Red = '\x1b[31m', + Green = '\x1b[32m', + Yellow = '\x1b[33m', + Blue = '\x1b[34m', + Magenta = '\x1b[35m', + Cyan = '\x1b[36m', + White = '\x1b[37m', + BrightBlack = '\x1b[30;1m', + BrightRed = '\x1b[31;1m', + BrightGreen = '\x1b[32;1m', + BrightYellow = '\x1b[33;1m', + BrightBlue = '\x1b[34;1m', + BrightMagenta = '\x1b[35;1m', + BrightCyan = '\x1b[36;1m', + BrightWhite = '\x1b[37;1m', +} diff --git a/code/extensions/js-debug/src/adapter/objectPreview/betterTypes.ts b/code/extensions/js-debug/src/adapter/objectPreview/betterTypes.ts new file mode 100644 index 000000000000..06b1b1a48577 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/objectPreview/betterTypes.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../cdp/api'; + +/** + * A collection of more strongly defined types. Derived from experimenting + * in Chrome devtools. + */ + +export type TArray = { + type: 'object'; + subtype: 'array' | 'typedarray'; + description: string; +}; +export type ArrayPreview = TArray & { + properties: (AnyPreview & Cdp.Runtime.PropertyPreview)[]; + overflow: boolean; +}; +export type ArrayObj = Cdp.Runtime.RemoteObject & TArray & { preview: ArrayPreview }; + +export type TFunction = { + type: 'function'; + subtype: undefined; + className: string; + // defined in V8, undefined in Hermes + description?: string; +}; +export type FunctionPreview = { + type: 'function'; + subtype: undefined; + description: string; + entries?: undefined; + properties?: undefined; + overflow?: undefined; +}; +export type FunctionObj = TFunction; + +export type TNode = { + type: 'object'; + subtype: 'node'; + description: string; +}; +export type NodePreview = TNode & ObjectPreview; +export type NodeObj = TNode & { preview: NodePreview }; + +export type TSet = { + type: 'object'; + subtype: 'set'; + className: string; + description: string; +}; +export type SetPreview = TSet & { + entries: { key?: undefined; value: AnyPreview }[]; + properties: PropertyPreview[]; + overflow: boolean; +}; +export type SetObj = TSet & { preview: SetPreview }; + +export type TMap = { + type: 'object'; + subtype: 'map'; + className: string; + description: string; +}; +export type MapPreview = TMap & { + entries: { key: AnyPreview; value: AnyPreview }[]; + properties: PropertyPreview[]; + overflow: boolean; +}; +export type MapObj = TMap & { preview: MapPreview }; + +export type TString = { + type: 'string'; + value: string; + subtype: undefined; + description?: string; +}; +export type StringPreview = TString; +export type StringObj = TString; + +export type TObject = { + type: 'object'; + subtype: undefined; + className: string; + description: string; +}; +export type ObjectPreview = TObject & { + properties?: PropertyPreview[]; + overflow: boolean; + entries?: { key: AnyPreview; value: AnyPreview }[]; +}; +export type ObjectObj = TObject & { preview: ObjectPreview }; + +export type TRegExp = { + type: 'object'; + subtype: 'regexp'; + className: 'RegExp'; + description: string; +}; +export type RegExpPreview = TRegExp & { overflow: boolean; properties: PropertyPreview[] }; +export type RegExpObj = TRegExp & { preview: RegExpPreview }; + +export type TError = { + type: 'object'; + subtype: 'error'; + className: string; + description: string; +}; +export type ErrorPreview = TError & { overflow: boolean }; +export type ErrorObj = TError & { preview: ErrorPreview }; + +export type TNull = { type: 'object'; subtype: 'null' }; +export type NullPreview = TNull; +export type NullObj = TNull; + +export type TUndefined = { type: 'undefined'; subtype: undefined }; +export type UndefinedPreview = TUndefined; +export type UndefinedObj = TUndefined; + +export type TNumber = { type: 'number'; subtype: undefined; value: number; description: string }; +export type NumberPreview = TNumber; +export type NumberObj = TNumber; + +export type TSpecialNumber = { + type: 'number'; + unserializableValue: 'NaN' | 'Infinity' | '-Infinity'; + description: string; +}; +export type SpecialNumberPreview = TSpecialNumber; + +export type TBigint = { + type: 'bigint'; + subtype: undefined; + unserializableValue?: string; + description: string; +}; +export type BigintPreview = TBigint; +export type BigintObj = TBigint; + +export type AnyObject = + | ObjectObj + | NodeObj + | ArrayObj + | SetObj + | MapObj + | ErrorObj + | RegExpObj + | FunctionObj + | StringObj + | NumberObj + | BigintObj + | UndefinedObj + | NullObj; +export type AnyPreview = + | ObjectPreview + | SetPreview + | MapPreview + | NodePreview + | ArrayPreview + | ErrorPreview + | RegExpPreview + | FunctionPreview + | StringPreview + | BigintPreview + | UndefinedPreview + | NullPreview; + +export type PreviewAsObjectType = + | NodePreview + | FunctionPreview + | ObjectPreview + | MapPreview + | SetPreview; +export type Numeric = NumberPreview | BigintPreview | TSpecialNumber; +export type Primitive = + | NullPreview + | UndefinedPreview + | StringPreview + | NumberPreview + | SpecialNumberPreview + | BigintPreview + | RegExpPreview + | ErrorPreview; + +export type PropertyPreview = { + name: string; + type: AnyPreview['type']; + value?: string; +} & AnyPreview; diff --git a/code/extensions/js-debug/src/adapter/objectPreview/contexts.ts b/code/extensions/js-debug/src/adapter/objectPreview/contexts.ts new file mode 100644 index 000000000000..3abc5705ee3a --- /dev/null +++ b/code/extensions/js-debug/src/adapter/objectPreview/contexts.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export interface IPreviewContext { + /** + * Max number of characters. + */ + budget: number; + + /** + * Whether strings should be quoted. + */ + quoted: boolean; + + /** + * Whether ANSI styling should be displayed literally in the string. + */ + ansi?: boolean; + + /** + * Post-processes the object preview. + */ + postProcess?(result: string): string; +} + +/** + * Known REPL preview types. + */ +export const enum PreviewContextType { + Repl = 'repl', + Hover = 'hover', + Watch = 'watch', + PropertyValue = 'propertyValue', + Copy = 'copy', + Clipboard = 'clipboard', +} + +const escape = (str: string) => + str.replace(/\n/gm, '\\n').replace(/\r/gm, '\\r').replace(/\t/gm, '\\t'); + +const repl: IPreviewContext = { budget: 100_000, quoted: true }; +const hover: IPreviewContext = { + budget: 1000, + quoted: true, + postProcess: escape, +}; +const copy: IPreviewContext = { budget: Infinity, quoted: false }; +const watch: IPreviewContext = { budget: 1000, quoted: true, postProcess: escape }; +const fallback: IPreviewContext = { budget: 100_000, quoted: true }; + +export const getContextForType = (type: PreviewContextType | string | undefined) => { + switch (type) { + case PreviewContextType.Repl: + return repl; + case PreviewContextType.Hover: + return hover; + case PreviewContextType.PropertyValue: + return hover; + case PreviewContextType.Watch: + return watch; + case PreviewContextType.Copy: + case PreviewContextType.Clipboard: + return copy; + default: + // the type is received straight from the DAP, so it's possible we might + // get unknown types in the future. Fallback rather than e.g. throwing. + return fallback; + } +}; diff --git a/code/extensions/js-debug/src/adapter/objectPreview/index.ts b/code/extensions/js-debug/src/adapter/objectPreview/index.ts new file mode 100644 index 000000000000..bdff413efd70 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/objectPreview/index.ts @@ -0,0 +1,630 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../cdp/api'; +import { BudgetStringBuilder } from '../../common/budgetStringBuilder'; +import * as stringUtils from '../../common/stringUtils'; +import Dap from '../../dap/api'; +import * as messageFormat from '../messageFormat'; +import * as ObjectPreview from './betterTypes'; +import { getContextForType, IPreviewContext } from './contexts'; + +const maxArrowFunctionCharacterLength = 30; +const maxPropertyPreviewLength = 100; +const maxEntryPreviewLength = 20; +const maxExceptionTitleLength = 10000; +const minTableCellWidth = 3; +const maxTableWidth = 120; + +/** + * Object subtypes that should be rendered without any preview. + */ +export const subtypesWithoutPreview: ReadonlySet = new Set([ + 'null', + 'regexp', + 'date', +]); + +/** + * Returns whether the given type should be previewed as an expandable + * object. + */ +export function previewAsObject( + object: Cdp.Runtime.RemoteObject | Cdp.Runtime.ObjectPreview | Cdp.Runtime.PropertyPreview, +): object is ObjectPreview.PreviewAsObjectType { + return ( + object.type === 'function' + || (object.type === 'object' && !subtypesWithoutPreview.has(object.subtype)) + ); +} + +/** + * Returns whether the given type should be previwed as an array. + */ +export function isArray(object: Cdp.Runtime.RemoteObject): object is ObjectPreview.ArrayObj; +export function isArray(object: ObjectPreview.AnyPreview): object is ObjectPreview.ArrayPreview; +export function isArray( + object: Cdp.Runtime.RemoteObject | Cdp.Runtime.ObjectPreview | Cdp.Runtime.PropertyPreview, +): boolean { + return object.subtype === 'array' || object.subtype === 'typedarray'; +} + +export function previewRemoteObject( + object: Cdp.Runtime.RemoteObject, + contextType?: string, + valueFormat?: Dap.ValueFormat, +): string { + const context = getContextForType(contextType); + const result = previewRemoteObjectInternal( + object as ObjectPreview.AnyObject, + context, + valueFormat, + ); + + if (object.preview?.subtype === 'regexp') return result; + + return context.postProcess?.(result) ?? result; +} + +function previewRemoteObjectInternal( + object: ObjectPreview.AnyObject, + context: IPreviewContext, + valueFormat?: Dap.ValueFormat, +): string { + // Evaluating function does not produce preview object for it. + if (object.type === 'function') { + return object.description + ? formatFunctionDescription(object.description, context.budget) + : ''; + } + + if (object.type === 'object' && object.subtype === 'node') { + return object.description; + } + + return 'preview' in object && object.preview + ? renderPreview(object.preview, context.budget, valueFormat) + : renderValue(object, context, valueFormat); +} + +export function propertyWeight( + prop: Cdp.Runtime.PropertyDescriptor | Cdp.Runtime.PrivatePropertyDescriptor, +): number { + if (prop.name === '__proto__') return 0; + if (prop.name.startsWith('__')) return 1; + return 100; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function privatePropertyWeight(_prop: Cdp.Runtime.PrivatePropertyDescriptor): number { + return 20; +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function internalPropertyWeight(_prop: Cdp.Runtime.InternalPropertyDescriptor): number { + return 10; +} + +function renderPreview( + preview: ObjectPreview.AnyPreview, + characterBudget: number, + valueFormat: Dap.ValueFormat | undefined, +): string { + if (isArray(preview)) { + return renderArrayPreview(preview, characterBudget); + } + + if ((preview.subtype as string) === 'internal#entry') { + return stringUtils.trimEnd( + (preview as { description: string }).description || '', + characterBudget, + ); + } + + if (preview.type === 'function') { + return formatFunctionDescription(preview.description, characterBudget); + } + + if (previewAsObject(preview)) { + return renderObjectPreview(preview, characterBudget, valueFormat); + } + + return renderPrimitivePreview(preview, characterBudget, valueFormat); +} + +function renderArrayPreview(preview: ObjectPreview.ArrayPreview, characterBudget: number): string { + const builder = new BudgetStringBuilder(characterBudget); + let description = preview.description; + const match = description.match(/[^(]*\(([\d]+)\)/); + if (!match) return description; + const arrayLength = parseInt(match[1], 10); + + if (description.startsWith('Array(')) description = description.substring('Array'.length); + builder.append(stringUtils.trimEnd(description, builder.budget())); + builder.append(' '); + const propsBuilder = new BudgetStringBuilder(builder.budget() - 2, ', '); // for [] + + // Indexed + let lastIndex = -1; + for (const prop of preview.properties) { + if (!propsBuilder.checkBudget()) break; + if (isNaN(prop.name as unknown as number)) continue; + const index = parseInt(prop.name, 10); + if (index > lastIndex + 1) propsBuilder.appendEllipsis(); + lastIndex = index; + propsBuilder.append(renderPropertyPreview(prop, propsBuilder.budget())); + } + if (arrayLength > lastIndex + 1) propsBuilder.appendEllipsis(); + + // Named + for (const prop of preview.properties) { + if (!propsBuilder.checkBudget()) break; + if (!isNaN(prop.name as unknown as number)) continue; + propsBuilder.append(renderPropertyPreview(prop, propsBuilder.budget(), prop.name)); + } + if (preview.overflow) propsBuilder.appendEllipsis(); + builder.append('[' + propsBuilder.build() + ']'); + return builder.build(); +} + +function renderObjectPreview( + preview: ObjectPreview.PreviewAsObjectType, + characterBudget: number, + format: Dap.ValueFormat | undefined, +): string { + const builder = new BudgetStringBuilder(characterBudget, ' '); + if (preview.description !== 'Object' && preview.description != null) { + builder.append(stringUtils.trimEnd(preview.description, builder.budget())); + } + + const map = new Map(); + const properties = preview.properties || []; + for (const prop of properties) { + map.set(prop.name, prop); + } + + // Handle boxed values such as Number, String. + const primitiveValue = map.get('[[PrimitiveValue]]'); + if (primitiveValue) { + builder.append(`(${renderPropertyPreview(primitiveValue, builder.budget() - 2)})`); + return builder.build(); + } + + // Promise handling. + const promiseStatus = map.get('[[PromiseStatus]]'); + const promiseValue = map.get('[[PromiseValue]]'); + if (promiseStatus && promiseValue) { + if (promiseStatus.value === 'pending') builder.append(`{<${promiseStatus.value}>}`); + else { + builder.append( + `{${ + renderPropertyPreview( + promiseValue, + builder.budget() - 2, + `<${promiseStatus.value}>`, + ) + }}`, + ); + } + return builder.build(); + } + + // Generator handling. + const generatorStatus = map.get('[[GeneratorStatus]]'); + if (generatorStatus) { + builder.append(`{<${generatorStatus.value}>}`); + return builder.build(); + } + + const propsBuilder = new BudgetStringBuilder(builder.budget() - 2, ', '); // for '{}' + for (const prop of properties) { + if (!propsBuilder.checkBudget()) break; + propsBuilder.append(renderPropertyPreview(prop, propsBuilder.budget(), prop.name)); + } + + for (const entry of preview.entries || []) { + if (!propsBuilder.checkBudget()) { + break; + } + + if (entry.key) { + const key = renderPreview( + entry.key, + Math.min(maxEntryPreviewLength, propsBuilder.budget()), + format, + ); + const value = renderPreview( + entry.value, + Math.min(maxEntryPreviewLength, propsBuilder.budget() - key.length - 4), + format, + ); + propsBuilder.append(appendKeyValue(key, ' => ', value, propsBuilder.budget())); + } else { + propsBuilder.append( + renderPreview( + entry.value, + Math.min(maxEntryPreviewLength, propsBuilder.budget()), + format, + ), + ); + } + } + + if (preview.overflow) { + propsBuilder.appendEllipsis(); + } + + const text = propsBuilder.build(); + if (text) { + builder.append('{' + text + '}'); + } else if (builder.isEmpty()) { + builder.append('{}'); + } + + return builder.build(); +} + +function valueOrEllipsis(value: string, characterBudget: number): string { + return value.length <= characterBudget ? value : '…'; +} + +function truncateValue(value: string, characterBudget: number): string { + return value.length >= characterBudget ? value.slice(0, characterBudget - 1) + '…' : value; +} + +/** + * Renders a preview of a primitive (number, undefined, null, string, etc) type. + */ +function renderPrimitivePreview( + preview: ObjectPreview.Primitive, + characterBudget: number, + valueFormat: Dap.ValueFormat | undefined, +): string { + if (preview.type === 'object' && preview.subtype === 'null') { + return valueOrEllipsis('null', characterBudget); + } + + if (preview.type === 'undefined') { + return valueOrEllipsis('undefined', characterBudget); + } + + if (preview.type === 'string') { + let str = preview.description ?? preview.value; + if (valueFormat?.hex) { + str = Buffer.from(str, 'utf8').toString('hex'); + } + return stringUtils.trimMiddle(str, characterBudget); + } + + if (preview.type === 'number' || preview.type === 'bigint') { + return formatAsNumber(preview, false, characterBudget, valueFormat); + } + + return truncateValue(preview.description || '', characterBudget); +} + +function appendKeyValue( + key: string | undefined, + separator: string, + value: string, + characterBudget: number, +) { + if (key === undefined) return stringUtils.trimMiddle(value, characterBudget); + if (key.length + separator.length > characterBudget) { + return stringUtils.trimEnd(key, characterBudget); + } + return escapeAnsiInString(`${key}${separator}${ + stringUtils.trimMiddle( + value, + characterBudget - key.length - separator.length, + ) + }`); // Keep in sync with characterBudget calculation. +} + +function renderPropertyPreview( + prop: ObjectPreview.PropertyPreview, + characterBudget: number, + name?: string, +): string { + characterBudget = Math.min(characterBudget, maxPropertyPreviewLength); + if (prop.type === 'function') return appendKeyValue(name, ': ', 'ƒ', characterBudget); // Functions don't carry preview. + if (prop.type === 'object' && prop.value === 'Object') { + return appendKeyValue(name, ': ', '{\u2026}', characterBudget); + } + if (typeof prop.value === 'undefined') { + return appendKeyValue(name, ': ', `<${prop.type}>`, characterBudget); + } + if (prop.type === 'string') { + return appendKeyValue(name, ': ', quoteStringValue(prop.value), characterBudget); + } + return appendKeyValue(name, ': ', prop.value ?? 'unknown', characterBudget); +} + +function escapeAnsiInString(value: string) { + return value.replaceAll('\x1b', '\\x1b'); +} + +function quoteStringValue(value: string) { + // Try a quote style that doesn't appear in the string, preferring/falling back to single quotes + const quoteStyle = value.includes("'") + ? value.includes('"') + ? value.includes('`') + ? "'" + : '`' + : '"' + : "'"; + + const replacer = new RegExp(`[${quoteStyle}\\\\]`, 'g'); + return `${quoteStyle}${value.replace(replacer, '\\$&')}${quoteStyle}`; +} + +function renderValue( + object: ObjectPreview.AnyObject, + { budget, quoted, ansi }: IPreviewContext, + format: Dap.ValueFormat | undefined, +): string { + if (object.type === 'string') { + let stringValue = object.value || (object.description ? object.description : ''); + if (format?.hex) { + stringValue = Buffer.from(stringValue, 'utf8').toString('hex'); + quoted = false; + } + let value = stringUtils.trimMiddle(stringValue, quoted ? budget - 2 : budget); + if (quoted) { + value = quoteStringValue(value); + } + return ansi ? value : escapeAnsiInString(value); + } + + if (object.type === 'undefined') { + return 'undefined'; + } + + if (object.subtype === 'null') { + return 'null'; + } + + if (object.type === 'bigint' || object.type === 'number') { + return formatAsNumber(object, false, budget, format); + } + + if (object.description) { + return stringUtils.trimEnd(object.description, Math.max(budget, 100000)); + } + + return stringUtils.trimEnd( + String('value' in object ? object.value : object.description), + budget, + ); +} + +function formatFunctionDescription(description: string, characterBudget: number): string { + const builder = new BudgetStringBuilder(characterBudget); + const text = description + .replace(/^function [gs]et /, 'function ') + .replace(/^function [gs]et\(/, 'function(') + .replace(/^[gs]et /, ''); + + // This set of best-effort regular expressions captures common function descriptions. + // Ideally, some parser would provide prefix, arguments, function body text separately. + const asyncMatch = text.match(/^(async\s+function)/); + const isGenerator = text.startsWith('function*'); + const isGeneratorShorthand = text.startsWith('*'); + const isBasic = !isGenerator && text.startsWith('function'); + const isClass = text.startsWith('class ') || text.startsWith('class{'); + const firstArrowIndex = text.indexOf('=>'); + const isArrow = !asyncMatch && !isGenerator && !isBasic && !isClass && firstArrowIndex > 0; + + let textAfterPrefix: string; + if (isClass) { + textAfterPrefix = text.substring('class'.length); + const classNameMatch = /^[^{\s]+/.exec(textAfterPrefix.trim()); + let className = ''; + if (classNameMatch) className = classNameMatch[0].trim() || ''; + addToken('class', textAfterPrefix, className); + } else if (asyncMatch) { + textAfterPrefix = text.substring(asyncMatch[1].length); + addToken('async ƒ', textAfterPrefix, nameAndArguments(textAfterPrefix)); + } else if (isGenerator) { + textAfterPrefix = text.substring('function*'.length); + addToken('ƒ*', textAfterPrefix, nameAndArguments(textAfterPrefix)); + } else if (isGeneratorShorthand) { + textAfterPrefix = text.substring('*'.length); + addToken('ƒ*', textAfterPrefix, nameAndArguments(textAfterPrefix)); + } else if (isBasic) { + textAfterPrefix = text.substring('function'.length); + addToken('ƒ', textAfterPrefix, nameAndArguments(textAfterPrefix)); + } else if (isArrow) { + let abbreviation = text; + if (text.length > maxArrowFunctionCharacterLength) { + abbreviation = text.substring(0, firstArrowIndex + 2) + ' {\u2026}'; + } + addToken('', text, abbreviation); + } else { + addToken('ƒ', text, nameAndArguments(text)); + } + return builder.build(); + + function nameAndArguments(contents: string): string { + const startOfArgumentsIndex = contents.indexOf('('); + const endOfArgumentsMatch = contents.match(/\)\s*{/); + const endIndex = (endOfArgumentsMatch && endOfArgumentsMatch.index) || 0; + if (startOfArgumentsIndex !== -1 && endOfArgumentsMatch && endIndex > startOfArgumentsIndex) { + const name = contents.substring(0, startOfArgumentsIndex).trim() || ''; + const args = contents.substring(startOfArgumentsIndex, endIndex + 1); + return name + args; + } + return '()'; + } + + function addToken(prefix: string, body: string, abbreviation: string) { + if (!builder.checkBudget()) return; + if (prefix.length) builder.append(prefix + ' '); + body = body.trim(); + if (body.endsWith(' { [native code] }')) { + body = body.substring(0, body.length - ' { [native code] }'.length); + } + if (builder.budget() >= body.length) builder.append(body); + else builder.append(abbreviation.replace(/\n/g, ' ')); + } +} + +export function previewException( + rawException: Cdp.Runtime.RemoteObject | ObjectPreview.AnyObject, +): { title: string; stackTrace?: string } { + const exception = rawException as ObjectPreview.AnyObject; + if (exception.type !== 'object' || exception.subtype === 'null') { + return { + title: renderValue(exception, { budget: maxExceptionTitleLength, quoted: false }, undefined), + }; + } + + const description = exception.description ?? (exception as { className?: string }).className + ?? 'Error'; + const firstCallFrame = /^\s+at\s/m.exec(description); + if (!firstCallFrame) { + const lastLineBreak = description.lastIndexOf('\n'); + if (lastLineBreak === -1) return { title: description }; + return { title: description.substring(0, lastLineBreak) }; + } + + return { + title: description.substring(0, firstCallFrame.index - 1), + stackTrace: description.substring(firstCallFrame.index + 2), + }; +} + +function formatAsNumber( + param: ObjectPreview.Numeric, + round: boolean, + characterBudget: number, + format: Dap.ValueFormat | undefined, +): string { + if (param.type === 'number') { + if ('unserializableValue' in param) { + return param.unserializableValue; + } + + const value = param.value !== undefined ? param.value : +param.description; + return format?.hex ? value.toString(16) : String(value); + } + + if (param.type === 'bigint') { + // parse unserializableValue is "1234n", slice the "n" off to parse and then base16 the number + const v = param.unserializableValue || param.description; + return format?.hex ? BigInt(v.slice(0, -1)).toString(16) : v; + } + + const fallback = param as Cdp.Runtime.RemoteObject; + const value = typeof fallback.value === 'number' + ? fallback.value + : +String(fallback.description); + return stringUtils.trimEnd(String(round ? Math.floor(value) : value), characterBudget); +} + +function formatAsString(param: ObjectPreview.StringObj, characterBudget: number): string { + return stringUtils.trimMiddle( + String(typeof param.value !== 'undefined' ? param.value : param.description), + characterBudget, + ); +} + +export function formatAsTable(param: Cdp.Runtime.ObjectPreview): string { + // Collect columns, values and measure lengths. + const rows: Map[] = []; + const colNames = new Set([undefined]); + const colLengths = new Map(); + + // Measure entries. + for (const row of param.properties.filter(r => r.valuePreview)) { + const value = new Map(); + value.set(undefined, row.name); // row index is a first column + colLengths.set(undefined, Math.max(colLengths.get(undefined) || 0, row.name.length)); + + rows.push(value); + row.valuePreview?.properties.map(prop => { + if (!prop.value) return; + colNames.add(prop.name); + value.set(prop.name, prop.value); + colLengths.set(prop.name, Math.max(colLengths.get(prop.name) || 0, prop.value.length)); + }); + } + + // Measure headers. + for (const name of colNames.values()) { + if (name) colLengths.set(name, Math.max(colLengths.get(name) || 0, name.length)); + } + + // Shrink columns if necessary. + const columnsWidth = Array.from(colLengths.values()).reduce((a, c) => a + c, 0); + const maxColumnsWidth = maxTableWidth - 4 - (colNames.size - 1) * 3; + if (columnsWidth > maxColumnsWidth) { + const ratio = maxColumnsWidth / columnsWidth; + for (const name of colLengths.keys()) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const newWidth = Math.max(minTableCellWidth, (colLengths.get(name)! * ratio) | 0); + colLengths.set(name, newWidth); + } + } + + // Template string for line separators. + const colTemplates: string[] = []; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + for (const name of colNames.values()) colTemplates.push('-'.repeat(colLengths.get(name)!)); + const rowTemplate = '[-' + colTemplates.join('-|-') + '-]'; + + const table: string[] = []; + table.push( + rowTemplate.replace('[', '╭').replace(/\|/g, '┬').replace(']', '╮').replace(/-/g, '┄'), // CodeQL [SM02383] The non-global replaces are replacing the sides of the table and do not need to be global. + ); + const header: string[] = []; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + for (const name of colNames.values()) header.push(pad(name || '', colLengths.get(name)!)); + table.push('┊ ' + header.join(' ┊ ') + ' ┊'); + table.push( + rowTemplate.replace('[', '├').replace(/\|/g, '┼').replace(']', '┤').replace(/-/g, '┄'), // CodeQL [SM02383] The non-global replaces are replacing the sides of the table and do not need to be global. + ); + + for (const value of rows) { + const row: string[] = []; + for (const colName of colNames.values()) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + row.push(pad(value.get(colName) || '', colLengths.get(colName)!)); + } + table.push('┊ ' + row.join(' ┊ ') + ' ┊'); + } + table.push( + rowTemplate.replace('[', '╰').replace(/\|/g, '┴').replace(']', '╯').replace(/-/g, '┄'), // CodeQL [SM02383] The non-global replaces are replacing the sides of the table and do not need to be global. + ); + return table.map(row => stringUtils.trimEnd(row, maxTableWidth)).join('\n'); +} + +export const messageFormatters: messageFormat.Formatters = new Map([ + ['', (param, context) => previewRemoteObjectInternal(param, context)], + ['s', (param, context) => formatAsString(param as ObjectPreview.StringObj, context.budget)], + [ + 'i', + (param, context) => + formatAsNumber(param as ObjectPreview.Numeric, true, context.budget, undefined), + ], + [ + 'd', + (param, context) => + formatAsNumber(param as ObjectPreview.Numeric, true, context.budget, undefined), + ], + [ + 'f', + (param, context) => + formatAsNumber(param as ObjectPreview.Numeric, false, context.budget, undefined), + ], + ['c', param => messageFormat.formatCssAsAnsi((param as { value: string }).value)], + ['o', (param, context) => previewRemoteObjectInternal(param, context)], + ['O', (param, context) => previewRemoteObjectInternal(param, context)], +]); + +function pad(text: string, length: number) { + if (text.length === length) return text; + if (text.length < length) return text + ' '.repeat(length - text.length); + return stringUtils.trimEnd(text, length); +} diff --git a/code/extensions/js-debug/src/adapter/pause.ts b/code/extensions/js-debug/src/adapter/pause.ts new file mode 100644 index 000000000000..1eea0b6cfa2c --- /dev/null +++ b/code/extensions/js-debug/src/adapter/pause.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../cdp/api'; +import { IPossibleBreakLocation } from './breakpoints'; +import { StackTrace } from './stackTrace'; +import { Thread } from './threads'; + +export type PausedReason = + | 'step' + | 'breakpoint' + | 'exception' + | 'pause' + | 'entry' + | 'goto' + | 'function breakpoint' + | 'data breakpoint' + | 'frame_entry'; + +export const enum StepDirection { + In, + Over, + Out, +} + +export type ExpectedPauseReason = + | { reason: Exclude; description?: string } + | { reason: 'step'; description?: string; direction: StepDirection }; + +export interface IPausedDetails { + thread: Thread; + reason: PausedReason; + event: Cdp.Debugger.PausedEvent; + description: string; + stackTrace: StackTrace; + stepInTargets?: IPossibleBreakLocation[]; + hitBreakpoints?: string[]; + text?: string; + exception?: Cdp.Runtime.RemoteObject; +} diff --git a/code/extensions/js-debug/src/adapter/performance/browserPerformanceProvider.ts b/code/extensions/js-debug/src/adapter/performance/browserPerformanceProvider.ts new file mode 100644 index 000000000000..f8887e25c0e1 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/performance/browserPerformanceProvider.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../cdp/api'; +import Dap from '../../dap/api'; +import { IPerformanceProvider } from '.'; + +export class BrowserPerformanceProvider implements IPerformanceProvider { + private readonly didEnable = new WeakSet(); + + /** + * @inheritdoc + */ + public async retrieve(cdp: Cdp.Api): Promise { + if (!this.didEnable.has(cdp)) { + this.didEnable.add(cdp); + await cdp.Performance.enable({}); + } + + const metrics = await cdp.Performance.getMetrics({}); + if (!metrics) { + return { error: 'Error in CDP' }; + } + + const obj: Record = {}; + for (const metric of metrics.metrics) { + obj[metric.name] = metric.value; + } + + return { metrics: obj }; + } +} diff --git a/code/extensions/js-debug/src/adapter/performance/index.ts b/code/extensions/js-debug/src/adapter/performance/index.ts new file mode 100644 index 000000000000..743ca403e847 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/performance/index.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import Cdp from '../../cdp/api'; +import Dap from '../../dap/api'; +import { ITarget } from '../../targets/targets'; +import { BrowserPerformanceProvider } from './browserPerformanceProvider'; +import { NodePerformanceProvider } from './nodePerformanceProvider'; + +export interface IPerformanceProvider { + /** + * Registers the performance provider to serve the DAP API. + */ + retrieve(cdp: Cdp.Api): Promise; +} + +export const IPerformanceProvider = Symbol('IPerformanceProvider'); + +@injectable() +export class PerformanceProviderFactory { + constructor(@inject(ITarget) private readonly target: ITarget) {} + + public create() { + return this.target.type() === 'node' + ? new NodePerformanceProvider() + : new BrowserPerformanceProvider(); + } +} diff --git a/code/extensions/js-debug/src/adapter/performance/nodePerformanceProvider.ts b/code/extensions/js-debug/src/adapter/performance/nodePerformanceProvider.ts new file mode 100644 index 000000000000..6b358c4c2aea --- /dev/null +++ b/code/extensions/js-debug/src/adapter/performance/nodePerformanceProvider.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../cdp/api'; +import Dap from '../../dap/api'; +import { getSourceSuffix } from '../templates'; +import { IPerformanceProvider } from '.'; + +export class NodePerformanceProvider implements IPerformanceProvider { + /** + * @inheritdoc + */ + public async retrieve(cdp: Cdp.Api): Promise { + const res = await cdp.Runtime.evaluate({ + expression: `({ + memory: process.memoryUsage(), + cpu: process.cpuUsage(), + timestamp: Date.now(), + resourceUsage: process.resourceUsage && process.resourceUsage(), + })${getSourceSuffix()}`, + returnByValue: true, + }); + + if (!res) { + return { error: 'No response from CDP' }; + } + + if (res.exceptionDetails) { + return { error: res.exceptionDetails.text }; + } + + return { metrics: res.result.value }; + } +} diff --git a/code/extensions/js-debug/src/adapter/portLeaseTracker.test.ts b/code/extensions/js-debug/src/adapter/portLeaseTracker.test.ts new file mode 100644 index 000000000000..4dd0e3f9f870 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/portLeaseTracker.test.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { DefaultJsDebugPorts } from '../common/findOpenPort'; +import { delay } from '../common/promiseUtil'; +import { PortLeaseTracker } from './portLeaseTracker'; + +describe('PortLeaseTracker', () => { + it('registers in use and not in use', async () => { + const l = new PortLeaseTracker('local'); + expect(await l.isRegistered(1000)).to.be.false; + l.register(1000); + expect(await l.isRegistered(1000)).to.be.true; + }); + + it('does not delay for ports outside default range', async () => { + const l = new PortLeaseTracker('local'); + expect(await Promise.race([l.isRegistered(1000), delay(5).then(() => 'error')])).to.be + .false; + }); + + it('delays for ports in range', async () => { + const l = new PortLeaseTracker('local'); + const p = DefaultJsDebugPorts.Min; + setTimeout(() => l.register(p), 20); + expect(await l.isRegistered(p)).to.be.true; + }); + + it('mandates correctly', async () => { + expect(new PortLeaseTracker('local').isMandated).to.be.false; + expect(new PortLeaseTracker('remote').isMandated).to.be.true; + }); +}); diff --git a/code/extensions/js-debug/src/adapter/portLeaseTracker.ts b/code/extensions/js-debug/src/adapter/portLeaseTracker.ts new file mode 100644 index 000000000000..9e9fbd75137e --- /dev/null +++ b/code/extensions/js-debug/src/adapter/portLeaseTracker.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import * as net from 'net'; +import { CancellationToken } from 'vscode'; +import * as WebSocket from 'ws'; +import { cancellableRace, NeverCancelled } from '../common/cancellation'; +import { IDisposable } from '../common/disposable'; +import { EventEmitter } from '../common/events'; +import { + DefaultJsDebugPorts, + findOpenPort, + makeAcquireTcpServer, + makeAcquireWebSocketServer, + waitForServerToListen, +} from '../common/findOpenPort'; +import { delay } from '../common/promiseUtil'; +import { ExtensionLocation } from '../ioc-extras'; + +/** + * Helper that creates a server registered with the lease tracker. + */ +export const acquireTrackedServer = async ( + tracker: IPortLeaseTracker, + onSocket: (s: net.Socket) => void, + overridePort?: number, + host?: string, + ct = NeverCancelled, +) => { + const server = overridePort + ? await waitForServerToListen(net.createServer(onSocket).listen(overridePort, host), ct) + : await findOpenPort({ tester: makeAcquireTcpServer(onSocket, host) }, ct); + const dispose = tracker.register((server.address() as net.AddressInfo).port); + server.on('close', () => dispose.dispose()); + server.on('error', () => dispose.dispose()); + return server; +}; + +/** + * Helper that creates a server registered with the lease tracker. + */ +export const acquireTrackedWebSocketServer = async ( + tracker: IPortLeaseTracker, + options?: WebSocket.ServerOptions, + ct?: CancellationToken, +) => { + const server = await findOpenPort({ tester: makeAcquireWebSocketServer(options) }, ct); + const dispose = tracker.register((server.address() as net.AddressInfo).port); + server.on('close', () => dispose.dispose()); + server.on('error', () => dispose.dispose()); + return server; +}; + +/** + * Tracks ports used by js-debug. All servers should be registered with the + * tracker. This is used for incorrectly or unnecessarily forwarding ports + * in remote scenarios. + */ +export interface IPortLeaseTracker { + /** + * Gets whether the extension must track its ports (at the possible expense + * of speed). + * + * This is set to "true" in remote cases, which triggers a slightly slower + * path in the bootloader. + */ + readonly isMandated: boolean; + + /** + * Registers a port as being in-use. Returns a Disposable that will + * unregister the port later. + */ + register(port: number): IDisposable; + + /** + * Returns whether the port is registered with the lease tracker. Can wait + * the given number of millisconds if it comes in later. + */ + isRegistered(port: number, wait?: number): Promise; +} + +export const IPortLeaseTracker = Symbol('IPortLeaseTracker'); + +@injectable() +export class PortLeaseTracker implements IPortLeaseTracker { + /** + * @inheritdoc + */ + public readonly isMandated: boolean; + + private readonly usedPorts = new Set(); + private readonly onRegistered = new EventEmitter(); + + constructor(@inject(ExtensionLocation) location: ExtensionLocation) { + this.isMandated = location === 'remote'; + } + + /** + * @inheritdoc + */ + register(port: number): IDisposable { + this.usedPorts.add(port); + this.onRegistered.fire(port); + return { dispose: () => this.usedPorts.delete(port) }; + } + + /** + * @inheritdoc + */ + isRegistered(port: number, wait = 2000): Promise { + if (this.usedPorts.has(port)) { + return Promise.resolve(true); + } + + // don't wait if this port isn't in our default range + if (port < DefaultJsDebugPorts.Min || port >= DefaultJsDebugPorts.Max) { + return Promise.resolve(false); + } + + return cancellableRace([ + () => delay(wait).then(() => false), + ct => + new Promise(resolve => { + const l = this.onRegistered.event(p => { + if (p === port) { + resolve(true); + } + }); + ct.onCancellationRequested(() => l.dispose()); + }), + ]); + } +} diff --git a/code/extensions/js-debug/src/adapter/profileController.ts b/code/extensions/js-debug/src/adapter/profileController.ts new file mode 100644 index 000000000000..43a66bcc6d36 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/profileController.ts @@ -0,0 +1,181 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { randomBytes } from 'crypto'; +import { inject, injectable } from 'inversify'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import Cdp from '../cdp/api'; +import { ICdpApi } from '../cdp/connection'; +import Dap from '../dap/api'; +import { invalidConcurrentProfile } from '../dap/errors'; +import { ProtocolError } from '../dap/protocolError'; +import { IShutdownParticipants, ShutdownOrder } from '../ui/shutdownParticipants'; +import { BreakpointEnableFilter, BreakpointManager } from './breakpoints'; +import { UserDefinedBreakpoint } from './breakpoints/userDefinedBreakpoint'; +import { getDefaultProfileName, IProfile, IProfilerFactory } from './profiling'; +import { BasicCpuProfiler } from './profiling/basicCpuProfiler'; +import { Thread } from './threads'; + +/** + * Provides profiling functionality for the debug adapter. + */ +export interface IProfileController { + connect(dap: Dap.Api, thread: Thread): void; + start(dap: Dap.Api, thread: Thread, params: Dap.StartProfileParams): Promise; +} + +export const IProfileController = Symbol('IProfileController'); + +interface IRunningProfile { + file: string; + profile: IProfile; + keptDebuggerOn: boolean; + enableFilter: BreakpointEnableFilter; +} + +@injectable() +export class ProfileController implements IProfileController { + private profile?: Promise; + private seenConsoleProfileNames = Object.create(null); + + constructor( + @inject(ICdpApi) private readonly cdp: Cdp.Api, + @inject(IProfilerFactory) private readonly factory: IProfilerFactory, + @inject(BasicCpuProfiler) private readonly basicCpuProfiler: BasicCpuProfiler, + @inject(BreakpointManager) private readonly breakpoints: BreakpointManager, + @inject(IShutdownParticipants) private readonly shutdown: IShutdownParticipants, + ) {} + + /** + * @inheritdoc + */ + connect(dap: Dap.Api, thread: Thread) { + dap.on('startProfile', async params => { + await this.start(dap, thread, params); + return {}; + }); + + dap.on('stopProfile', () => this.stopProfiling(dap)); + + this.cdp.Profiler.on('consoleProfileStarted', () => { + dap.output({ + output: l10n.t('Console profile started') + '\n', + category: 'console', + }); + }); + + this.cdp.Profiler.on('consoleProfileFinished', async evt => { + const promise = this.saveConsoleProfile(dap, evt); + const shutdownBlocker = this.shutdown.register( + ShutdownOrder.ExecutionContexts, + () => promise, + ); + await promise; + shutdownBlocker.dispose(); + }); + + thread.onPaused(() => this.stopProfiling(dap)); + } + + /** + * @inheritdoc + */ + public async start(dap: Dap.Api, thread: Thread, params: Dap.StartProfileParams): Promise { + if (this.profile) { + throw new ProtocolError(invalidConcurrentProfile()); + } + + this.profile = this.startProfileInner(dap, thread, params).catch(err => { + this.profile = undefined; + throw err; + }); + + await this.profile; + } + + private async saveConsoleProfile(dap: Dap.Api, evt: Cdp.Profiler.ConsoleProfileFinishedEvent) { + let basename: string; + if (evt.title) { + basename = evt.title.replace(/[\/\\]/g, '-'); + const nth = this.seenConsoleProfileNames[evt.title] || 0; + this.seenConsoleProfileNames[evt.title] = nth + 1; + if (nth > 0) { + basename += `-${nth}`; + } + } else { + basename = getDefaultProfileName(); + } + + basename += BasicCpuProfiler.extension; + await this.basicCpuProfiler.save(evt.profile, basename); + + dap.output({ + output: l10n.t('CPU profile saved as "{0}" in your workspace folder', basename) + '\n', + category: 'console', + }); + } + + private async startProfileInner(dap: Dap.Api, thread: Thread, params: Dap.StartProfileParams) { + let keepDebuggerOn = false; + let enableFilter: BreakpointEnableFilter; + if (params.stopAtBreakpoint?.length) { + const toBreakpoint = new Set(params.stopAtBreakpoint); + keepDebuggerOn = true; + enableFilter = bp => !(bp instanceof UserDefinedBreakpoint) || toBreakpoint.has(bp.dapId); + } else { + enableFilter = () => false; + } + + await this.breakpoints.applyEnabledFilter(enableFilter); + + const file = join(tmpdir(), `vscode-js-profile-${randomBytes(4).toString('hex')}`); + const profile = await this.factory.get(params.type).start(params, file); + const runningProfile: IRunningProfile = { + file, + profile, + enableFilter, + keptDebuggerOn: keepDebuggerOn, + }; + + profile.onUpdate(label => dap.profilerStateUpdate({ label, running: true })); + profile.onStop(() => this.disposeProfile(runningProfile)); + + const isPaused = !!thread.pausedDetails(); + + if (keepDebuggerOn) { + await thread.resume(); + } else if (isPaused) { + await this.cdp.Debugger.disable({}); + if (isPaused) { + thread.onResumed(); // see docs on this method for why we call it here + } + } + + dap.profileStarted({ file: runningProfile.file, type: params.type }); + return runningProfile; + } + + private async stopProfiling(dap: Dap.Api) { + const running = await this.profile?.catch(() => undefined); + if (!running || !this.profile) { + return {}; // guard against concurrent stops + } + + this.profile = undefined; + await running?.profile.stop(); + dap.profilerStateUpdate({ label: '', running: false }); + return {}; + } + + private async disposeProfile({ profile, enableFilter, keptDebuggerOn }: IRunningProfile) { + if (!keptDebuggerOn) { + await this.cdp.Debugger.enable({}); + } + + await this.breakpoints.applyEnabledFilter(undefined, enableFilter); + profile.dispose(); + } +} diff --git a/code/extensions/js-debug/src/adapter/profiling/basicCpuProfiler.ts b/code/extensions/js-debug/src/adapter/profiling/basicCpuProfiler.ts new file mode 100644 index 000000000000..838316bc04b9 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/profiling/basicCpuProfiler.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import { isAbsolute, join } from 'path'; +import Cdp from '../../cdp/api'; +import { ICdpApi } from '../../cdp/connection'; +import { EventEmitter } from '../../common/events'; +import { AnyLaunchConfiguration } from '../../configuration'; +import { profileCaptureError } from '../../dap/errors'; +import { ProtocolError } from '../../dap/protocolError'; +import { FS, FsPromises } from '../../ioc-extras'; +import { SourceContainer } from '../sourceContainer'; +import { IProfile, IProfiler, StartProfileParams } from '.'; +import { SourceAnnotationHelper } from './sourceAnnotationHelper'; + +export interface IBasicProfileParams { + precise: boolean; +} + +/** + * Basic profiler that uses the stable CPU `Profiler` API available everywhere. + * In Chrome, and probably in Node, this will be superceded by the Tracing API. + */ +@injectable() +export class BasicCpuProfiler implements IProfiler { + public static readonly type = 'cpu'; + public static readonly extension = '.cpuprofile'; + public static readonly label = l10n.t('CPU Profile'); + public static readonly description = l10n.t( + 'Generates a .cpuprofile file you can open in VS Code or the Edge/Chrome devtools', + ); + public static readonly editable = true; + + public static canApplyTo() { + return true; // this API is stable in all targets + } + + constructor( + @inject(ICdpApi) private readonly cdp: Cdp.Api, + @inject(FS) private readonly fs: FsPromises, + @inject(SourceContainer) private readonly sources: SourceContainer, + @inject(AnyLaunchConfiguration) private readonly launchConfig: AnyLaunchConfiguration, + ) {} + + /** + * @inheritdoc + */ + public async start(_options: StartProfileParams, file: string) { + if (!(await this.cdp.Profiler.start({}))) { + throw new ProtocolError(profileCaptureError()); + } + + return new BasicProfile( + this.cdp, + this.fs, + this.sources, + this.launchConfig.__workspaceFolder, + file, + ); + } + + /** + * Annotates and saves the profile to the file path. If the file path is + * not absolute, then it will be saved in the workspace folder. + */ + public async save(profile: Cdp.Profiler.Profile, file: string) { + const annotated = await annotateSources( + profile, + this.sources, + this.launchConfig.__workspaceFolder, + ); + if (!isAbsolute(file)) { + file = join(this.launchConfig.__workspaceFolder, file); + } + + await this.fs.writeFile(file, JSON.stringify(annotated)); + } +} + +class BasicProfile implements IProfile { + private readonly stopEmitter = new EventEmitter(); + private disposed = false; + + /** + * @inheritdoc + */ + public readonly onUpdate = new EventEmitter().event; + + /** + * @inheritdoc + */ + public readonly onStop = this.stopEmitter.event; + + constructor( + private readonly cdp: Cdp.Api, + private readonly fs: FsPromises, + private readonly sources: SourceContainer, + private readonly workspaceFolder: string, + private readonly file: string, + ) {} + + /** + * @inheritdoc + */ + public async dispose() { + if (!this.disposed) { + this.disposed = true; + this.stopEmitter.fire(); + } + } + + /** + * @inheritdoc + */ + public async stop() { + const result = await this.cdp.Profiler.stop({}); + if (!result) { + throw new ProtocolError(profileCaptureError()); + } + + await this.dispose(); + + const annotated = await annotateSources(result.profile, this.sources, this.workspaceFolder); + await this.fs.writeFile(this.file, JSON.stringify(annotated)); + } +} + +/** + * Adds source locations + */ +async function annotateSources( + profile: Cdp.Profiler.Profile, + sources: SourceContainer, + workspaceFolder: string, +) { + const helper = new SourceAnnotationHelper(sources); + const nodes = profile.nodes.map(node => ({ + ...node, + locationId: helper.getLocationIdFor(node.callFrame), + positionTicks: node.positionTicks?.map(tick => ({ + ...tick, + // weirdly, line numbers here are 1-based, not 0-based. The position tick + // only gives line-level granularity, so 'mark' the entire range of source + // code the tick refers to + startLocationId: helper.getLocationIdFor({ + ...node.callFrame, + lineNumber: tick.line - 1, + columnNumber: 0, + }), + endLocationId: helper.getLocationIdFor({ + ...node.callFrame, + lineNumber: tick.line, + columnNumber: 0, + }), + })), + })); + + return { + ...profile, + nodes, + $vscode: { + rootPath: workspaceFolder, + locations: await helper.getLocations(), + }, + }; +} diff --git a/code/extensions/js-debug/src/adapter/profiling/basicHeapProfiler.ts b/code/extensions/js-debug/src/adapter/profiling/basicHeapProfiler.ts new file mode 100644 index 000000000000..a29638a1a414 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/profiling/basicHeapProfiler.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import Cdp from '../../cdp/api'; +import { ICdpApi } from '../../cdp/connection'; +import { EventEmitter } from '../../common/events'; +import { AnyLaunchConfiguration } from '../../configuration'; +import { profileCaptureError } from '../../dap/errors'; +import { ProtocolError } from '../../dap/protocolError'; +import { FS, FsPromises } from '../../ioc-extras'; +import { SourceContainer } from '../sourceContainer'; +import { IProfile, IProfiler, StartProfileParams } from '.'; +import { SourceAnnotationHelper } from './sourceAnnotationHelper'; + +/** + * Basic profiler that uses the stable `HeapProfiler` API available everywhere. + * In Chrome, and probably in Node, this will be superceded by the Tracing API. + */ +@injectable() +export class BasicHeapProfiler implements IProfiler<{}> { + public static readonly type = 'heap'; + public static readonly extension = '.heapprofile'; + public static readonly label = l10n.t('Heap Profile'); + public static readonly description = l10n.t( + 'Generates a .heapprofile file you can open in VS Code or the Edge/Chrome devtools', + ); + public static readonly editable = true; + + public static canApplyTo() { + return true; // this API is stable in all targets + } + + constructor( + @inject(ICdpApi) private readonly cdp: Cdp.Api, + @inject(FS) private readonly fs: FsPromises, + @inject(SourceContainer) private readonly sources: SourceContainer, + @inject(AnyLaunchConfiguration) private readonly launchConfig: AnyLaunchConfiguration, + ) {} + + /** + * @inheritdoc + */ + public async start(_options: StartProfileParams<{}>, file: string) { + await this.cdp.HeapProfiler.enable({}); + + if (!(await this.cdp.HeapProfiler.startSampling({}))) { + throw new ProtocolError(profileCaptureError()); + } + + return new BasicProfile( + this.cdp, + this.fs, + this.sources, + this.launchConfig.__workspaceFolder, + file, + ); + } +} + +class BasicProfile implements IProfile { + private readonly stopEmitter = new EventEmitter(); + private disposed = false; + + /** + * @inheritdoc + */ + public readonly onUpdate = new EventEmitter().event; + + /** + * @inheritdoc + */ + public readonly onStop = this.stopEmitter.event; + + constructor( + private readonly cdp: Cdp.Api, + private readonly fs: FsPromises, + private readonly sources: SourceContainer, + private readonly workspaceFolder: string, + private readonly file: string, + ) {} + + /** + * @inheritdoc + */ + public async dispose() { + if (!this.disposed) { + this.disposed = true; + await this.cdp.HeapProfiler.disable({}); + this.stopEmitter.fire(); + } + } + + /** + * @inheritdoc + */ + public async stop() { + const result = await this.cdp.HeapProfiler.stopSampling({}); + if (!result) { + throw new ProtocolError(profileCaptureError()); + } + + await this.dispose(); + + const annotated = await this.annotateSources(result.profile); + await this.fs.writeFile(this.file, JSON.stringify(annotated)); + } + + /** + * Adds source locations + */ + private async annotateSources(profile: Cdp.HeapProfiler.SamplingHeapProfile) { + const helper = new SourceAnnotationHelper(this.sources); + + const setLocationId = ( + node: Cdp.HeapProfiler.SamplingHeapProfileNode, + destNode: Cdp.HeapProfiler.SamplingHeapProfileNode & { + locationId?: number; + }, + ) => { + destNode.locationId = helper.getLocationIdFor(node.callFrame); + + for (const child of node.children) { + const destChild = { ...child, children: [] }; + destNode.children.push(destChild); + setLocationId(child, destChild); + } + }; + + const head = { + ...profile.head, + children: [], + }; + + setLocationId(profile.head, head); + + return { + ...profile, + head, + $vscode: { + rootPath: this.workspaceFolder, + locations: await helper.getLocations(), + }, + }; + } +} diff --git a/code/extensions/js-debug/src/adapter/profiling/heapDumpProfiler.ts b/code/extensions/js-debug/src/adapter/profiling/heapDumpProfiler.ts new file mode 100644 index 000000000000..956f8130f3a1 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/profiling/heapDumpProfiler.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { createWriteStream, WriteStream } from 'fs'; +import { inject, injectable } from 'inversify'; +import Cdp from '../../cdp/api'; +import { ICdpApi } from '../../cdp/connection'; +import { EventEmitter } from '../../common/events'; +import { IProfile, IProfiler, StartProfileParams } from '.'; + +/** + * Basic instant that uses the HeapProfiler API to grab a snapshot. + */ +@injectable() +export class HeapDumpProfiler implements IProfiler { + public static readonly type = 'memory'; + public static readonly extension = '.heapsnapshot'; + public static readonly label = l10n.t('Heap Snapshot'); + public static readonly description = l10n.t( + 'Generates a .heapsnapshot file you can open in VS Code or the Edge/Chrome devtools', + ); + public static readonly instant = true; + + public static canApplyTo() { + return true; // this API is stable in all targets + } + + private currentWriter?: { + stream: WriteStream; + promise: Promise; + }; + + constructor(@inject(ICdpApi) private readonly cdp: Cdp.Api) { + this.cdp.HeapProfiler.on( + 'addHeapSnapshotChunk', + ({ chunk }) => this.currentWriter?.stream.write(chunk), + ); + } + + /** + * @inheritdoc + */ + public async start(_options: StartProfileParams, file: string): Promise { + return { + onStop: new EventEmitter().event, + onUpdate: new EventEmitter().event, + dispose: () => undefined, + stop: async () => { + await this.cdp.HeapProfiler.enable({}); + await this.dumpToFile(file); + await this.cdp.HeapProfiler.disable({}); + }, + }; + } + + private async dumpToFile(filename: string) { + const { stream, promise } = (this.currentWriter = { + stream: createWriteStream(filename), + promise: this.cdp.HeapProfiler.takeHeapSnapshot({}), + }); + + await promise; + stream.end(); + this.currentWriter = undefined; + } +} diff --git a/code/extensions/js-debug/src/adapter/profiling/index.ts b/code/extensions/js-debug/src/adapter/profiling/index.ts new file mode 100644 index 000000000000..8676b409fd3d --- /dev/null +++ b/code/extensions/js-debug/src/adapter/profiling/index.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Container, inject, injectable } from 'inversify'; +import { Event } from 'vscode'; +import { IDisposable } from '../../common/disposable'; +import { AnyLaunchConfiguration } from '../../configuration'; +import Dap from '../../dap/api'; +import { IContainer } from '../../ioc-extras'; +import { BasicCpuProfiler } from './basicCpuProfiler'; +import { BasicHeapProfiler } from './basicHeapProfiler'; +import { HeapDumpProfiler } from './heapDumpProfiler'; + +/** + * Single profile returned from the IProfiler as a RAII. + */ +export interface IProfile extends IDisposable { + /** + * Event that fires to show profile information data to the user. + */ + readonly onUpdate: Event; + + /** + * Event that fires when the profiling is stopped for any reason. + */ + readonly onStop: Event; + + /** + * Gracefully stops the profiling operation. + */ + stop(): Promise; +} + +export type StartProfileParams = Dap.StartProfileParams & { params?: T }; + +export interface IProfiler { + /** + * Starts capturing a profile. + */ + start(options: StartProfileParams, file: string): Promise; +} + +export interface IProfilerCtor { + new(...args: never[]): IProfiler; + + /** + * Profiler type given in the DAP API. + */ + readonly type: string; + + /** + * Default extension for profiles created from this profiler. + */ + readonly extension: string; + + /** + * User-readable profiler name. + */ + readonly label: string; + + /** + * Optional user-readable description of the profiler. + */ + readonly description?: string; + + /** + * Whether the profiler captures an instant snapshot versus sampling for a + * duration. Defaults to false. + */ + readonly instant?: boolean; + + /** + * Whether the resulting file can be edited in VS Code. Defaults to false. + */ + readonly editable?: boolean; + + /** + * Returns whether this profiler can apply to the given target, + */ + canApplyTo(options: AnyLaunchConfiguration): boolean; +} + +export const IProfilerFactory = Symbol('IProfilerFactory'); + +export interface IProfilerFactory { + /** + * Gets an appropriate profiler for the start params. + * @throws Error if the type is unrecognized + */ + get(type: string): IProfiler; +} + +/** + * Gets a default profile file name (without an extension) + */ +export const getDefaultProfileName = () => { + const now = new Date(); + return [ + 'vscode-profile', + now.getFullYear(), + now.getMonth() + 1, + now.getDate(), + now.getHours(), + now.getMinutes(), + now.getSeconds(), + ] + .map(n => String(n).padStart(2, '0')) + .join('-'); +}; + +/** + * Simple class that gets profilers + */ +@injectable() +export class ProfilerFactory implements IProfilerFactory { + public static readonly ctors: ReadonlyArray = [ + BasicCpuProfiler, + BasicHeapProfiler, + HeapDumpProfiler, + ]; + + constructor(@inject(IContainer) private readonly container: Container) {} + + public get(type: string): IProfiler { + const ctor = ProfilerFactory.ctors.find(p => p.type === type); + if (!ctor) { + throw new Error(`Invalid profilter type ${type}`); + } + + return this.container.get(ctor); + } +} diff --git a/code/extensions/js-debug/src/adapter/profiling/sourceAnnotationHelper.ts b/code/extensions/js-debug/src/adapter/profiling/sourceAnnotationHelper.ts new file mode 100644 index 000000000000..2ae8effb55e3 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/profiling/sourceAnnotationHelper.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Cdp from '../../cdp/api'; +import Dap from '../../dap/api'; +import { SourceContainer } from '../sourceContainer'; + +interface IEmbeddedLocation { + lineNumber: number; + columnNumber: number; + source: Dap.Source; +} + +export class SourceAnnotationHelper { + private locationIdCounter = 0; + private readonly locationsByRef = new Map< + string, + { id: number; callFrame: Cdp.Runtime.CallFrame; locations: Promise } + >(); + + constructor(private readonly sources: SourceContainer) {} + + public getLocationIdFor(callFrame: Cdp.Runtime.CallFrame) { + const ref = [ + callFrame.functionName, + callFrame.url, + callFrame.scriptId, + callFrame.lineNumber, + callFrame.columnNumber, + ].join(':'); + + const existing = this.locationsByRef.get(ref); + if (existing) { + return existing.id; + } + + const id = this.locationIdCounter++; + this.locationsByRef.set(ref, { + id, + callFrame, + locations: (async () => { + const source = await this.sources.getScriptById(callFrame.scriptId)?.source; + if (!source) { + return []; + } + + const locations = await this.sources.currentSiblingUiLocations({ + lineNumber: callFrame.lineNumber + 1, + columnNumber: callFrame.columnNumber + 1, + source, + }); + + return Promise.all( + locations.map(async loc => ({ ...loc, source: await loc.source.toDap() })), + ); + })(), + }); + + return id; + } + + public getLocations() { + return Promise.all( + [...this.locationsByRef.values()] + .sort((a, b) => a.id - b.id) + .map(async l => ({ callFrame: l.callFrame, locations: await l.locations })), + ); + } +} diff --git a/code/extensions/js-debug/src/adapter/resourceProvider/basicResourceProvider.ts b/code/extensions/js-debug/src/adapter/resourceProvider/basicResourceProvider.ts new file mode 100644 index 000000000000..422e3a4e9e65 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/resourceProvider/basicResourceProvider.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { dataUriToBuffer } from 'data-uri-to-buffer'; +import { LookupAddress, promises as dns } from 'dns'; +import got, { Headers, OptionsOfTextResponseBody, RequestError } from 'got'; +import { inject, injectable, optional } from 'inversify'; +import { CancellationToken } from 'vscode'; +import { NeverCancelled } from '../../common/cancellation'; +import { DisposableList } from '../../common/disposable'; +import { fileUrlToAbsolutePath, isAbsolute, isLoopback } from '../../common/urlUtils'; +import { FS, FsPromises } from '../../ioc-extras'; +import { HttpStatusError, IResourceProvider, Response } from '.'; +import { IRequestOptionsProvider } from './requestOptionsProvider'; + +@injectable() +export class BasicResourceProvider implements IResourceProvider { + /** + * Map of ports to fallback hosts that ended up working. Used to optimistically + * fallback (see #1694) + */ + private autoLocalhostPortFallbacks: Record = {}; + + constructor( + @inject(FS) private readonly fs: FsPromises, + @optional() @inject(IRequestOptionsProvider) private readonly options?: IRequestOptionsProvider, + ) {} + + /** + * @inheritdoc + */ + public async fetch( + url: string, + cancellationToken: CancellationToken = NeverCancelled, + headers?: { [key: string]: string }, + ): Promise> { + try { + const r = dataUriToBuffer(url); + return { ok: true, url, body: new TextDecoder().decode(r.buffer), statusCode: 200 }; + } catch { + // assume it's a remote url + } + + const absolutePath = isAbsolute(url) ? url : fileUrlToAbsolutePath(url); + if (absolutePath) { + try { + return { + ok: true, + url, + body: await this.fs.readFile(absolutePath, 'utf-8'), + statusCode: 200, + }; + } catch (error) { + return { ok: false, url, error, statusCode: 200 }; + } + } + + return this.fetchHttp(url, cancellationToken, headers); + } + /** + * Returns JSON from the given file, data, or HTTP URL. + */ + public async fetchJson( + url: string, + cancellationToken?: CancellationToken, + headers?: { [key: string]: string }, + ): Promise> { + const res = await this.fetch(url, cancellationToken, { + Accept: 'application/json', + ...headers, + }); + if (!res.ok) { + return res; + } + + try { + return { ...res, body: JSON.parse(res.body) }; + } catch (error) { + return { ...res, ok: false, url, error }; + } + } + + protected async fetchHttp( + url: string, + cancellationToken: CancellationToken, + headers?: Headers, + ): Promise> { + const parsed = new URL(url); + + const isSecure = parsed.protocol !== 'http:'; + const port = Number(parsed.port) ?? (isSecure ? 443 : 80); + const options: OptionsOfTextResponseBody = { headers, followRedirect: true }; + if (isSecure && (await isLoopback(url))) { + options.rejectUnauthorized = false; // CodeQL [SM03616] Intentional for local development. + } + + this.options?.provideOptions(options, url); + + const isLocalhost = parsed.hostname === 'localhost'; + const fallback = isLocalhost && this.autoLocalhostPortFallbacks[port]; + if (fallback) { + const response = await this.requestHttp(parsed.toString(), options, cancellationToken); + if (response.statusCode !== 503) { + return response; + } + + delete this.autoLocalhostPortFallbacks[port]; + return this.requestHttp(url, options, cancellationToken); + } + + let response = await this.requestHttp(url, options, cancellationToken); + + // Try the other net family if localhost fails, + // see https://github.com/microsoft/vscode/issues/140536#issuecomment-1011281962 + // and later https://github.com/microsoft/vscode/issues/167353 + if (response.statusCode === 503 && isLocalhost) { + let resolved: LookupAddress; + try { + resolved = await dns.lookup(parsed.hostname); + } catch { + return response; + } + + parsed.hostname = resolved.family === 6 ? '127.0.0.1' : '[::1]'; + response = await this.requestHttp(parsed.toString(), options, cancellationToken); + if (response.statusCode !== 503) { + this.autoLocalhostPortFallbacks[port] = parsed.hostname; + } + } + + return response; + } + + private async requestHttp( + url: string, + options: OptionsOfTextResponseBody, + cancellationToken: CancellationToken, + ): Promise> { + this.options?.provideOptions(options, url); + + const disposables = new DisposableList(); + + try { + const request = got(url, options); + disposables.push(cancellationToken.onCancellationRequested(() => request.cancel())); + + const response = await request; + return { ok: true, url, body: response.body, statusCode: response.statusCode }; + } catch (error) { + if (!(error instanceof RequestError)) { + throw error; + } + + const body = error.response ? String(error.response?.body) : error.message; + const statusCode = error.response?.statusCode ?? 503; + return { + ok: false, + body, + statusCode, + url, + error: new HttpStatusError(statusCode, url, body), + }; + } finally { + disposables.dispose(); + } + } +} diff --git a/code/extensions/js-debug/src/adapter/resourceProvider/helpers.ts b/code/extensions/js-debug/src/adapter/resourceProvider/helpers.ts new file mode 100644 index 000000000000..67fe04beda5d --- /dev/null +++ b/code/extensions/js-debug/src/adapter/resourceProvider/helpers.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Headers, OptionsOfTextResponseBody } from 'got'; + +/** + * Adds a header to the outgoing request. + */ +export const addHeader: (headers: Headers, key: string, value: string) => Headers = ( + options, + key, + value, +) => { + key = key.toLowerCase(); + + const existing = options?.[key]; + return { + ...options, + [key]: existing + ? existing instanceof Array + ? existing.concat(value) + : [existing as string, value] + : value, + }; +}; + +export const mergeOptions = ( + into: OptionsOfTextResponseBody, + from: Partial, +) => { + const cast = into as Record; + for (const [key, value] of Object.entries(from)) { + if (typeof value === 'object' && !!value) { + cast[key] = Object.assign((cast[key] || {}) as Record, value); + } else { + cast[key] = value; + } + } +}; diff --git a/code/extensions/js-debug/src/adapter/resourceProvider/httpError.ts b/code/extensions/js-debug/src/adapter/resourceProvider/httpError.ts new file mode 100644 index 000000000000..a81fa2a79289 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/resourceProvider/httpError.ts @@ -0,0 +1,3 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ diff --git a/code/extensions/js-debug/src/adapter/resourceProvider/index.ts b/code/extensions/js-debug/src/adapter/resourceProvider/index.ts new file mode 100644 index 000000000000..0116762cf540 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/resourceProvider/index.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { CancellationToken } from 'vscode'; + +export interface IResourceProvider { + /** + * Returns data from the given file, data, or HTTP URL. + */ + fetch(url: string, cancellationToken?: CancellationToken): Promise>; + + /** + * Returns JSON from the given file, data, or HTTP URL. + */ + fetchJson(url: string, cancellationToken?: CancellationToken): Promise>; +} + +/** + * Error type thrown for a non-2xx status code. + */ +export class HttpStatusError extends Error { + constructor( + public readonly statusCode: number, + public readonly url: string, + public readonly body?: string, + ) { + super(`Unexpected ${statusCode} response from ${url}: ${body ?? ''}`); + } +} + +/** + * Succe + */ +export interface ISuccessfulResponse { + ok: true; + url: string; + body: T; + statusCode: number; +} + +export interface IErrorResponse { + ok: false; + url: string; + statusCode: number; + error: Error; + body?: string; +} + +export type Response = ISuccessfulResponse | IErrorResponse; + +export const IResourceProvider = Symbol('IResourceProvider'); diff --git a/code/extensions/js-debug/src/adapter/resourceProvider/requestOptionsProvider.ts b/code/extensions/js-debug/src/adapter/resourceProvider/requestOptionsProvider.ts new file mode 100644 index 000000000000..b9a7726ccf33 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/resourceProvider/requestOptionsProvider.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { OptionsOfTextResponseBody } from 'got'; + +export interface IRequestOptionsProvider { + /** + * Called before requests are made, can be used to add + * extra options into the request. + */ + provideOptions(obj: OptionsOfTextResponseBody, url: string): void; +} + +export const IRequestOptionsProvider = Symbol('IRequestOptionsProvider'); diff --git a/code/extensions/js-debug/src/adapter/resourceProvider/statefulResourceProvider.ts b/code/extensions/js-debug/src/adapter/resourceProvider/statefulResourceProvider.ts new file mode 100644 index 000000000000..bc8af3080519 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/resourceProvider/statefulResourceProvider.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Headers } from 'got'; +import { inject, injectable, optional } from 'inversify'; +import { CancellationToken } from 'vscode'; +import Cdp from '../../cdp/api'; +import { ICdpApi } from '../../cdp/connection'; +import { DisposableList, IDisposable } from '../../common/disposable'; +import { ILogger, LogTag } from '../../common/logging'; +import { FS, FsPromises } from '../../ioc-extras'; +import { ITarget } from '../../targets/targets'; +import { Response } from '.'; +import { BasicResourceProvider } from './basicResourceProvider'; +import { IRequestOptionsProvider } from './requestOptionsProvider'; + +@injectable() +export class StatefulResourceProvider extends BasicResourceProvider implements IDisposable { + private readonly disposables = new DisposableList(); + + constructor( + @inject(FS) fs: FsPromises, + @inject(ILogger) private readonly logger: ILogger, + @optional() @inject(ITarget) private readonly target?: ITarget, + @optional() @inject(ICdpApi) private readonly cdp?: Cdp.Api, + @optional() @inject(IRequestOptionsProvider) options?: IRequestOptionsProvider, + ) { + super(fs, options); + } + + /** + * @inheritdoc + */ + public dispose() { + this.disposables.dispose(); + } + + protected async fetchHttp( + url: string, + cancellationToken: CancellationToken, + headers: Headers = {}, + ): Promise> { + const res = await super.fetchHttp(url, cancellationToken, headers); + if (!res.ok) { + this.logger.info(LogTag.Runtime, 'Network load failed, falling back to CDP', { url, res }); + return this.fetchOverBrowserNetwork(url, res); + } + + return res; + } + + private async fetchOverBrowserNetwork( + url: string, + original: Response, + ): Promise> { + if (!this.cdp) { + return original; + } + + const res = await this.cdp.Network.loadNetworkResource({ + // Browser targets use the frame ID as their target ID. + frameId: this.target?.targetInfo.targetId, + url, + options: { + includeCredentials: true, + disableCache: true, + }, + }); + + if (!res) { + return original; + } + + if ( + !res.resource.success + || !res.resource.httpStatusCode + || res.resource.httpStatusCode >= 400 + || !res.resource.stream + ) { + return original; + } + + // Small optimization: normally we'd need a trailing `IO.read` request to + // get an EOF, but if the response headers have a length then we can avoid that! + let maxOffset = Number(res.resource.headers?.['Content-Length']); + if (isNaN(maxOffset)) { + maxOffset = Infinity; + } + + const result: string[] = []; + let offset = 0; + while (true) { + const chunkRes = await this.cdp.IO.read({ handle: res.resource.stream, offset }); + if (!chunkRes) { + this.logger.info(LogTag.Runtime, 'Stream error encountered in middle, falling back', { + url, + }); + return original; + } + + const chunk = chunkRes.base64Encoded + ? Buffer.from(chunkRes.data, 'base64').toString() + : chunkRes.data; + // V8 uses byte length, not UTF-16 length, see #1814 + offset += Buffer.byteLength(chunk, 'utf-8'); + result.push(chunk); + if (offset >= maxOffset) { + this.cdp.IO.close({ handle: res.resource.stream }); // no await: do this in the background + break; + } + + if (chunkRes.eof) { + break; + } + } + + return { + ok: true, + body: result.join(''), + statusCode: res.resource.httpStatusCode, + url, + }; + } +} diff --git a/code/extensions/js-debug/src/adapter/scriptSkipper/implementation.ts b/code/extensions/js-debug/src/adapter/scriptSkipper/implementation.ts new file mode 100644 index 000000000000..2c0b4767e13b --- /dev/null +++ b/code/extensions/js-debug/src/adapter/scriptSkipper/implementation.ts @@ -0,0 +1,398 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import micromatch from 'micromatch'; +import { Cdp } from '../../cdp/api'; +import { ICdpApi } from '../../cdp/connection'; +import { MapUsingProjection } from '../../common/datastructure/mapUsingProjection'; +import { IDisposable } from '../../common/disposable'; +import { EventEmitter } from '../../common/events'; +import { ILogger, LogTag } from '../../common/logging'; +import { node15InternalsPrefix, nodeInternalsToken } from '../../common/node15Internal'; +import { memoizeLast, trailingEdgeThrottle, truthy } from '../../common/objUtils'; +import * as pathUtils from '../../common/pathUtils'; +import { getDeferred, IDeferred } from '../../common/promiseUtil'; +import { ISourcePathResolver } from '../../common/sourcePathResolver'; +import { escapeRegexSpecialChars } from '../../common/stringUtils'; +import * as urlUtils from '../../common/urlUtils'; +import { AnyLaunchConfiguration } from '../../configuration'; +import Dap from '../../dap/api'; +import { ITarget } from '../../targets/targets'; +import { ISourceScript, ISourceWithMap, isSourceWithMap, Source, SourceFromMap } from '../source'; +import { SourceContainer } from '../sourceContainer'; +import { getSourceSuffix } from '../templates'; +import { IScriptSkipper } from './scriptSkipper'; +import { simpleGlobsToRe } from './simpleGlobToRe'; + +interface ISharedSkipToggleEvent { + rootTargetId: string; + targetId: string; + params: Dap.ToggleSkipFileStatusParams; +} + +function preprocessNodeInternals(userSkipPatterns: ReadonlyArray): string[] | undefined { + const nodeInternalRegex = /^[\/\\](.*)$/; + + const nodeInternalPatterns = userSkipPatterns + .map(userPattern => { + userPattern = userPattern.trim(); + const nodeInternalPattern = nodeInternalRegex.exec(userPattern); + return nodeInternalPattern ? nodeInternalPattern[1] : null; + }) + .filter(truthy); + + return nodeInternalPatterns.length > 0 ? nodeInternalPatterns : undefined; +} + +function preprocessAuthoredGlobs( + spr: ISourcePathResolver, + userSkipPatterns: ReadonlyArray, +): string[] { + const authoredGlobs = userSkipPatterns + .filter(pattern => !pattern.includes(nodeInternalsToken)) + .map(pattern => + urlUtils.isAbsolute(pattern) + ? urlUtils.absolutePathToFileUrlWithDetection(spr.rebaseLocalToRemote(pattern)) + : pathUtils.forceForwardSlashes(pattern) + ) + .map(urlUtils.lowerCaseInsensitivePath); + + return authoredGlobs; +} + +@injectable() +export class ScriptSkipper implements IScriptSkipper, IDisposable { + private static sharedSkipsEmitter = new EventEmitter(); + + /** + * Globs for non- skipfiles. This might be changed over time + * if the user uses the "toggle skipping this file" command. + */ + private _authoredGlobs: readonly string[]; + + /** Memoized computer for non- skipfiles */ + private _regexForAuthored = memoizeLast((re: readonly string[]) => + simpleGlobsToRe(re, s => urlUtils.charRangeToUrlReGroup(s, 0, s.length, true, true)) + ); + + /** + * Globs for node internals. These are treated specially, at least until we + * drop support for Node <=14, since in Node 15 the internals all have a + * `node:` prefix that we can match against. + */ + private _nodeInternalsGlobs: string[] | undefined; + + /** Set of all internal modules, read from the runtime */ + private _allNodeInternals?: IDeferred>; + + /** + * Mapping of URLs from sourcemaps to a boolean indicating whether they're + * skipped. These are kept and used in addition to the authoredGlobs, since + * if a compiled file is skipped, we want to skip the sourcemapped sources + * as well. + */ + private _isUrlFromSourceMapSkipped: Map; + + /** + * A set of script ID that have one or more skipped ranges in them. Mostly + * used to avoid unnecessarily sending skip data for new scripts. + */ + private _scriptsWithSkipping = new Set(); + + private _sourceContainer!: SourceContainer; + private _updateSkippedDebounce: () => void; + private _targetId: string; + private _rootTargetId: string; + private _sharedSkipListener: IDisposable; + + constructor( + @inject(AnyLaunchConfiguration) { skipFiles }: AnyLaunchConfiguration, + @inject(ISourcePathResolver) sourcePathResolver: ISourcePathResolver, + @inject(ILogger) private readonly logger: ILogger, + @inject(ICdpApi) private readonly cdp: Cdp.Api, + @inject(ITarget) target: ITarget, + ) { + this._targetId = target.id(); + this._rootTargetId = getRootTarget(target).id(); + this._isUrlFromSourceMapSkipped = new MapUsingProjection(key => + this._normalizeUrl(key) + ); + + this._authoredGlobs = preprocessAuthoredGlobs(sourcePathResolver, skipFiles); + this._nodeInternalsGlobs = preprocessNodeInternals(skipFiles); + + this._initNodeInternals(target); // Purposely don't wait, no need to slow things down + this._updateSkippedDebounce = trailingEdgeThrottle( + 500, + () => this._updateGeneratedSkippedSources(), + ); + + if (skipFiles.length) { + this._updateGeneratedSkippedSources(); + } + + this._sharedSkipListener = ScriptSkipper.sharedSkipsEmitter.event(e => { + if (e.rootTargetId === this._rootTargetId && e.targetId !== this._targetId) { + this._toggleSkippingFile(e.params); + } + }); + } + + public dispose(): void { + this._sharedSkipListener.dispose(); + } + + public setSourceContainer(sourceContainer: SourceContainer): void { + this._sourceContainer = sourceContainer; + } + + private _testSkipNodeInternal(testString: string): boolean { + if (!this._nodeInternalsGlobs) { + return false; + } + + if (testString.startsWith(node15InternalsPrefix)) { + testString = testString.slice(node15InternalsPrefix.length); + } + + return micromatch([testString], this._nodeInternalsGlobs).length > 0; + } + + private _testSkipAuthored(testString: string): boolean { + return this._regexForAuthored(this._authoredGlobs).some(re => re.test(testString)); + } + + private _isNodeInternal(url: string, nodeInternals: ReadonlySet | undefined): boolean { + if (url.startsWith(node15InternalsPrefix)) { + return true; + } + + return nodeInternals?.has(url) || /^internal\/.+\.js$/.test(url); + } + + private async _updateGeneratedSkippedSources(): Promise { + const patterns: string[] = this._regexForAuthored(this._authoredGlobs).map(re => re.source); + + const nodeInternals = this._allNodeInternals?.settledValue; + if (nodeInternals) { + patterns.push(`^(${node15InternalsPrefix})?internal\\/`); + for (const internal of nodeInternals) { + if (this._testSkipNodeInternal(internal)) { + patterns.push(`^(${node15InternalsPrefix})?${escapeRegexSpecialChars(internal)}$`); + } + } + } + + await this.cdp.Debugger.setBlackboxPatterns({ patterns }); + } + + private _normalizeUrl(url: string): string { + return pathUtils.forceForwardSlashes(url.toLowerCase()); + } + + /** + * Gets whether the script at the URL is skipped. + */ + public isScriptSkipped(url: string): boolean { + if (this._isNodeInternal(url, this._allNodeInternals?.settledValue)) { + return this._testSkipNodeInternal(url); + } + + url = this._normalizeUrl(url); + if (this._isUrlFromSourceMapSkipped.get(url) === true) { + return true; + } + + return this._testSkipAuthored(url); + } + + private async _updateSourceWithSkippedSourceMappedSources( + source: ISourceWithMap, + scripts: readonly ISourceScript[], + ): Promise { + // Order "should" be correct + const parentIsSkipped = this.isScriptSkipped(source.url); + const skipRanges: Cdp.Debugger.ScriptPosition[] = []; + let inSkipRange = parentIsSkipped; + for (const authoredSource of source.sourceMap.sourceByUrl.values()) { + let isSkippedSource = this.isScriptSkipped(authoredSource.url); + if (typeof isSkippedSource === 'undefined') { + // If not toggled or specified in launch config, inherit the parent's status + isSkippedSource = parentIsSkipped; + } + + if (isSkippedSource !== inSkipRange) { + const [[start], [end]] = await Promise.all([ + this._sourceContainer.currentSiblingUiLocations( + { source: authoredSource, lineNumber: 1, columnNumber: 1 }, + source, + ), + this._sourceContainer.currentSiblingUiLocations( + { source: authoredSource, lineNumber: Infinity, columnNumber: 1 }, + source, + ), + ]); + if (start && end) { + skipRanges.push( + { lineNumber: start.lineNumber - 1, columnNumber: start.columnNumber - 1 }, + { lineNumber: end.lineNumber - 1, columnNumber: end.columnNumber - 1 }, + ); + inSkipRange = !inSkipRange; + } else { + this.logger.error( + LogTag.Internal, + 'Could not map script beginning for ' + authoredSource.sourceReference, + ); + } + } + } + + let targets = scripts; + if (!skipRanges.length) { + targets = targets.filter(t => this._scriptsWithSkipping.has(t.scriptId)); + targets.forEach(t => this._scriptsWithSkipping.delete(t.scriptId)); + } + + // todo(conno4312): it seems like the current version of Chrome used in + // playwright tests doesn't send a response to this method :/ + targets.map(({ scriptId }) => + this.cdp.Debugger.setBlackboxedRanges({ scriptId, positions: skipRanges }) + ); + } + + public initializeSkippingValueForSource(source: Source) { + this._initializeSkippingValueForSource(source); + } + + private _initializeSkippingValueForSource(source: Source, scripts = source.scripts) { + const url = source.url; + let skipped = this.isScriptSkipped(url); + + // Check if this source was mapped to a URL we should have skipped, but didn't (oops) + // This can happen if the user skips absolute paths which are served from a different + // place in the server. + if ( + !skipped + && source.absolutePath + && this._testSkipAuthored(urlUtils.absolutePathToFileUrl(source.absolutePath)) + ) { + this.setIsUrlBlackboxSkipped(url, true); + skipped = true; + this._updateSkippedDebounce(); + } + + if (isSourceWithMap(source)) { + if (skipped) { + // if compiled and skipped, also skip authored sources + for (const authoredSource of source.sourceMap.sourceByUrl.values()) { + this._isUrlFromSourceMapSkipped.set(authoredSource.url, true); + } + } + + for (const nestedSource of source.sourceMap.sourceByUrl.values()) { + this._initializeSkippingValueForSource(nestedSource, scripts); + } + + this._updateSourceWithSkippedSourceMappedSources(source, scripts); + } + } + + private async _initNodeInternals(target: ITarget): Promise { + if (target.type() !== 'node' || !this._nodeInternalsGlobs || this._allNodeInternals) { + return; + } + + const deferred = (this._allNodeInternals = getDeferred()); + const evalResult = await this.cdp.Runtime.evaluate({ + expression: "require('module').builtinModules" + getSourceSuffix(), + returnByValue: true, + includeCommandLineAPI: true, + }); + + if (evalResult && !evalResult.exceptionDetails) { + deferred.resolve(new Set((evalResult.result.value as string[]).map(name => name + '.js'))); + } else { + deferred.resolve(new Set()); + } + + await this._updateGeneratedSkippedSources(); // updates skips now that we loaded internals + } + + private async _toggleSkippingFile( + params: Dap.ToggleSkipFileStatusParams, + ): Promise { + let path: string | undefined = undefined; + if (params.resource) { + if (urlUtils.isAbsolute(params.resource)) { + path = params.resource; + } + } + + const sourceParams: Dap.Source = { path: path, sourceReference: params.sourceReference }; + const source = this._sourceContainer.source(sourceParams); + if (!source) { + return {}; + } + + const newSkipValue = !this.isScriptSkipped(source.url); + if (source instanceof SourceFromMap) { + this._isUrlFromSourceMapSkipped.set(source.url, newSkipValue); + + // Changed the skip value for an authored source, update it for all its compiled sources + const compiledSources = Array.from(source.compiledToSourceUrl.keys()); + await Promise.all( + compiledSources.map(compiledSource => + this._updateSourceWithSkippedSourceMappedSources(compiledSource, compiledSource.scripts) + ), + ); + } else { + if (isSourceWithMap(source)) { + // if compiled, get authored sources + for (const authoredSource of source.sourceMap.sourceByUrl.values()) { + this._isUrlFromSourceMapSkipped.set(authoredSource.url, newSkipValue); + } + } + + this.setIsUrlBlackboxSkipped(source.url, newSkipValue); + await this._updateGeneratedSkippedSources(); + } + + return {}; + } + + /** Sets whether the URL is explicitly skipped in the blackbox patterns */ + private setIsUrlBlackboxSkipped(url: string, skipped: boolean) { + const positive = url; + const negative = `!${positive}`; + + const globs = this._authoredGlobs.filter(g => g !== positive && g !== negative); + if (this._regexForAuthored(globs).some(r => r.test(url)) !== skipped) { + globs.push(skipped ? positive : negative); + this._regexForAuthored.clear(); + } + this._authoredGlobs = globs; + } + + public async toggleSkippingFile( + params: Dap.ToggleSkipFileStatusParams, + ): Promise { + const result = await this._toggleSkippingFile(params); + ScriptSkipper.sharedSkipsEmitter.fire({ + params, + rootTargetId: this._rootTargetId, + targetId: this._targetId, + }); + return result; + } +} + +function getRootTarget(target: ITarget): ITarget { + const parent = target.parent(); + if (parent) { + return getRootTarget(parent); + } else { + return target; + } +} diff --git a/code/extensions/js-debug/src/adapter/scriptSkipper/scriptSkipper.ts b/code/extensions/js-debug/src/adapter/scriptSkipper/scriptSkipper.ts new file mode 100644 index 000000000000..d2bf62ea995b --- /dev/null +++ b/code/extensions/js-debug/src/adapter/scriptSkipper/scriptSkipper.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export const IScriptSkipper = Symbol('IScriptSkipper'); + +export interface IScriptSkipper { + isScriptSkipped(url: string): boolean; +} diff --git a/code/extensions/js-debug/src/adapter/scriptSkipper/simpleGlobToRe.test.ts b/code/extensions/js-debug/src/adapter/scriptSkipper/simpleGlobToRe.test.ts new file mode 100644 index 000000000000..fee89847dd34 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/scriptSkipper/simpleGlobToRe.test.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { simpleGlobsToRe } from './simpleGlobToRe'; + +describe('simpleGlobsToRe', () => { + const truthTable = [ + { + globs: ['**/foo/**'], + matches: { + 'file:///hello/foo/bar': true, + 'file:///hello/baz/bar': false, + }, + }, + { + globs: ['**/fo$^o/**', '!**/fo$^o/bar/**'], + matches: { + 'file:///hello/fo$^o/bin/baz': true, + 'file:///hello/fo$^o/bar/baz': false, + }, + }, + { + globs: ['**/foo.js'], + matches: { + 'foo.js': true, + 'file:///hello/foo.js': true, + 'file:///hello/foo.js/bar': false, + }, + }, + { + globs: ['**/foo/**', '!**/foo/bar/**'], + matches: { + 'file:///hello/foo/bin/baz': true, + 'file:///hello/foo/bar/baz': false, + }, + }, + { + globs: ['**/foo/**', '!**/foo/bar/**', '**/other/**', '!**/filename.js'], + matches: { + 'file:///hello/foo/bin/baz': true, + 'file:///hello/foo/bar/baz': false, + 'file:///other/thing': true, + 'file:///other/filename.js': false, + 'file:///hello/foo/bin/filename.js': false, + }, + }, + ]; + + for (const { globs, matches } of truthTable) { + it(globs.join(', '), () => { + const res = simpleGlobsToRe(globs); + for (const [url, expected] of Object.entries(matches)) { + const matching = res.find(re => re.test(url)); + if (expected !== !!matching) { + if (expected) { + throw new Error(`Expected ${url} to match ${res.join(', or')}`); + } else { + throw new Error(`Expected ${url} to not match, but ${matching} did`); + } + } + } + }); + + it(`is not catastrophic: ${globs.join(', ')}`, () => { + const testStr = + 'file:///users/connor/github/vscode-remotehub/common/node_modules/%40opentelemetry/api/build/esm/trace/internal/../../../../src/trace/internal/tracestate-validators.ts'; + const res = simpleGlobsToRe(globs); + + const start = performance.now(); + for (let i = 0; i < 100; i++) { + for (const re of res) { + re.test(testStr); + } + } + + expect(performance.now() - start).to.be.lessThan(500); + }); + } +}); diff --git a/code/extensions/js-debug/src/adapter/scriptSkipper/simpleGlobToRe.ts b/code/extensions/js-debug/src/adapter/scriptSkipper/simpleGlobToRe.ts new file mode 100644 index 000000000000..89f50529502c --- /dev/null +++ b/code/extensions/js-debug/src/adapter/scriptSkipper/simpleGlobToRe.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { escapeRegexSpecialChars } from '../../common/stringUtils'; + +/** + * Smashes a list of globs into a list of matching regexes. Only + * supports basic glob features: + * + * - Wildcards (**​/foo/bar and *.js) + * - Entire negations (!**​/foo/bar) + * + * This is used in the scriptSkipper because `Debugger.setBlackboxPatterns` + * only supports a list of regexes to match again. + */ +export function simpleGlobsToRe(globs: readonly string[], processPart = escapeRegexSpecialChars) { + const res: string[] = []; + for (let i = 0; i < globs.length; i++) { + const g = globs[i]; + if (g.startsWith('!')) { + // Add each negation as a negative lookahead. This is not the fastest for + // regex engines to compute, but is far faster than previous approaches... + const re = globToRe(g.slice(1), processPart); + for (let i = 0; i < res.length; i++) { + res[i] = `^(?!${re.slice(1)})${res[i].slice(1)}`; + } + } else { + res.push(globToRe(g, processPart)); + } + } + + return res.map(re => new RegExp(re, 'i')); +} + +/** + * Simple glob to re implementation. We could use micromatch.makeRe, but that + * inclues a lot of cruft we don't care about when matching against URLs. + */ +function globToRe(glob: string, processPart = escapeRegexSpecialChars) { + const parts = glob.split('/'); + const regexParts = []; + for (let j = 0; j < parts.length; j++) { + const p = parts[j]; + if (p === '**') { + if (j === 0) { + regexParts.push('(.+/)?'); // match start, or any slash preceeding what's next... + } else if (j === parts.length - 1) { + // nothing more needed! + } else { + regexParts.push('.*/'); + } + } else { + if (p.includes('*')) { + const wildcards = p.split('*'); + regexParts.push(wildcards.map(s => processPart(s)).join('[^\\/]*')); + } else { + regexParts.push(processPart(p)); + } + + regexParts.push(j < parts.length - 1 ? '\\/' : '$'); + } + } + + return `^${regexParts.join('')}`; +} diff --git a/code/extensions/js-debug/src/adapter/selfProfile.ts b/code/extensions/js-debug/src/adapter/selfProfile.ts new file mode 100644 index 000000000000..cba48e42c59e --- /dev/null +++ b/code/extensions/js-debug/src/adapter/selfProfile.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { promises as fs } from 'fs'; +import { Session } from 'inspector'; + +/** + * Small class used to profile the extension itself. Used for collecting + * information in the VS error reporter. + */ +export class SelfProfile { + private session = new Session(); + + constructor(private readonly file: string) { + this.session.connect(); + } + + /** + * Starts the profile. + */ + public async start() { + try { + await this.post('Profiler.enable'); + } catch { + // already enabled + } + + await this.post('Profiler.start'); + } + + /** + * Stop the profile. + */ + public async stop() { + const { profile } = await this.post<{ profile: object }>('Profiler.stop'); + await fs.writeFile(this.file, JSON.stringify(profile)); + } + + public dispose() { + this.session.disconnect(); + } + + private post(method: string, params?: {}) { + return new Promise((resolve, reject) => + this.session.post(method, params, (err, result) => { + if (err) { + reject(err); + } else { + resolve(result as unknown as R); + } + }) + ); + } +} diff --git a/code/extensions/js-debug/src/adapter/smartStepping.ts b/code/extensions/js-debug/src/adapter/smartStepping.ts new file mode 100644 index 000000000000..87295c4c2993 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/smartStepping.ts @@ -0,0 +1,96 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import { ILogger, LogTag } from '../common/logging'; +import { isInstanceOf } from '../common/objUtils'; +import { AnyLaunchConfiguration } from '../configuration'; +import { ExpectedPauseReason, IPausedDetails, PausedReason, StepDirection } from './pause'; +import { isSourceWithMap } from './source'; +import { UnmappedReason } from './sourceContainer'; +import { StackFrame } from './stackTrace'; + +export const enum StackFrameStepOverReason { + NotStepped, + SmartStep, + Blackboxed, +} + +export async function shouldStepOverStackFrame( + stackFrame: StackFrame, +): Promise { + const uiLocation = await stackFrame.uiLocation(); + if (!uiLocation) { + return StackFrameStepOverReason.NotStepped; + } + + if (uiLocation.source.blackboxed()) { + return StackFrameStepOverReason.Blackboxed; + } + + if (!isSourceWithMap(uiLocation.source)) { + return StackFrameStepOverReason.NotStepped; + } + + if (!uiLocation.isMapped && uiLocation.unmappedReason === UnmappedReason.MapPositionMissing) { + return StackFrameStepOverReason.SmartStep; + } + + return StackFrameStepOverReason.NotStepped; +} + +const neverStepReasons: ReadonlySet = new Set(['breakpoint', 'exception', 'entry']); + +const smartStepBackoutThreshold = 256; + +/** + * The SmartStepper is a device that controls stepping through code that lacks + * sourcemaps when running in an application with source maps. + */ +@injectable() +export class SmartStepper { + private _smartStepCount = 0; + + constructor( + @inject(AnyLaunchConfiguration) private readonly launchConfig: AnyLaunchConfiguration, + @inject(ILogger) private readonly logger: ILogger, + ) {} + + private resetSmartStepCount(): void { + if (this._smartStepCount > 0) { + this.logger.verbose(LogTag.Internal, `smartStep: skipped ${this._smartStepCount} steps`); + this._smartStepCount = 0; + } + } + + /** + * Determines whether smart stepping should be run for the given pause + * information. If so, returns the direction of stepping. + */ + public async getSmartStepDirection( + pausedDetails: IPausedDetails, + reason?: ExpectedPauseReason, + ): Promise { + if (!this.launchConfig.smartStep) { + return; + } + + if (neverStepReasons.has(pausedDetails.reason)) { + return; + } + + const frame = (await pausedDetails.stackTrace.loadFrames(1)).find(isInstanceOf(StackFrame)); + const should = frame && (await shouldStepOverStackFrame(frame)); + if (should === StackFrameStepOverReason.NotStepped) { + this.resetSmartStepCount(); + return; + } + + if (this._smartStepCount++ > smartStepBackoutThreshold) { + return StepDirection.Out; + } + + return reason?.reason === 'step' ? reason.direction : StepDirection.In; + } +} diff --git a/code/extensions/js-debug/src/adapter/source.ts b/code/extensions/js-debug/src/adapter/source.ts new file mode 100644 index 000000000000..1e8c27b96cc8 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/source.ts @@ -0,0 +1,585 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { relative } from 'path'; +import { URL } from 'url'; +import Cdp from '../cdp/api'; +import { checkContentHash } from '../common/hash/checkContentHash'; +import { node15InternalsPrefix, nodeInternalsToken } from '../common/node15Internal'; +import { once } from '../common/objUtils'; +import { forceForwardSlashes, isSubdirectoryOf } from '../common/pathUtils'; +import { delay, getDeferred, IDeferred } from '../common/promiseUtil'; +import { ISourceMapMetadata, SourceMap } from '../common/sourceMaps/sourceMap'; +import { InlineScriptOffset } from '../common/sourcePathResolver'; +import * as sourceUtils from '../common/sourceUtils'; +import { prettyPrintAsSourceMap } from '../common/sourceUtils'; +import * as utils from '../common/urlUtils'; +import Dap from '../dap/api'; +import { IWasmSymbols } from './dwarf/wasmSymbolProvider'; +import type { SourceContainer } from './sourceContainer'; + +// Represents a text source visible to the user. +// +// Source maps flow (start with compiled1 and compiled2). Two different compiled sources +// reference to the same source map, and produce two different resolved urls leading +// to different source map sources. This is a corner case, usually there is a single +// resolved url and a single source map source per each sourceUrl in the source map. +// +// ------> sourceMapUrl -> SourceContainer._sourceMaps -> SourceMapData -> map +// | | | +// | compiled1 - - - - - - - source1 <-- resolvedUrl1 <-- sourceUrl <---- +// | | +// compiled2 - - - - - - - - - - source2 <-- resolvedUrl2 <-- sourceUrl <---- +// +// compiled1 and source1 are connected (same goes for compiled2 and source2): +// compiled1._sourceMapSourceByUrl.get(sourceUrl) === source1 +// source1._compiledToSourceUrl.get(compiled1) === sourceUrl +// + +export class Source { + public readonly sourceReference: number; + private readonly _name: string; + private readonly _fqname: string; + + /** + * Function to retrieve the content of the source. + */ + private readonly _contentGetter: ContentGetter; + + private readonly _container: SourceContainer; + + /** + * Hypothesized absolute path for the source. May or may not actually exist. + */ + public readonly absolutePath: string; + + public sourceMap?: SourceLocationProvider; + + // This is the same as |_absolutePath|, but additionally checks that file exists to + // avoid errors when page refers to non-existing paths/urls. + private readonly _existingAbsolutePath: Promise; + private _scripts: ISourceScript[] = []; + + /** + * Gets whether the source should be sent to the client lazily. + * This is true for evaluated scripts. (#1939) + */ + public get sendLazy() { + return !this.url; + } + + /** @internal */ + public hasBeenAnnounced = false; + + /** + * @param inlineScriptOffset Offset of the start location of the script in + * its source file. This is used on scripts in HTML pages, where the script + * is nested in the content. + * @param contentHash Optional hash of the file contents. This is used to + * check whether the script we get is the same one as what's on disk. This + * can be used to detect in-place transpilation. + * @param runtimeScriptOffset Offset of the start location of the script + * in the runtime *only*. This differs from the inlineScriptOffset, as the + * inline offset of also reflected in the file. This is used to deal with + * the runtime wrapping the source and offsetting locations which should + * not be shown to the user. + */ + constructor( + container: SourceContainer, + public readonly url: string, + absolutePath: string | undefined, + contentGetter: ContentGetter, + sourceMapMetadata?: ISourceMapMetadata, + public readonly inlineScriptOffset?: InlineScriptOffset, + public readonly runtimeScriptOffset?: InlineScriptOffset, + public readonly contentHash?: string, + ) { + this.sourceReference = container.getSourceReference(url); + this._contentGetter = once(contentGetter); + this._container = container; + this.absolutePath = absolutePath || ''; + this._fqname = this._fullyQualifiedName(); + this._name = this._humanName(); + this.setSourceMapUrl(sourceMapMetadata); + + this._existingAbsolutePath = this.checkContentHash(contentHash); + } + + /** Returns the absolute path if the conten hash matches. */ + protected checkContentHash(contentHash?: string) { + return checkContentHash( + this.absolutePath, + // Inline scripts will never match content of the html file. We skip the content check. + this.inlineScriptOffset || this.runtimeScriptOffset ? undefined : contentHash, + this._container._fileContentOverridesForTest.get(this.absolutePath), + ); + } + + /** Offsets a location that came from the runtime script, to where it appears in source code */ + public offsetScriptToSource(obj: T): T { + if (this.runtimeScriptOffset) { + return { + ...obj, + // Line number could go out of bounds if a location (such as a scope range) + // refers to information in a module 'wrapper'; this happens in web extensions + lineNumber: Math.max(1, obj.lineNumber - this.runtimeScriptOffset.lineOffset), + columnNumber: obj.columnNumber - this.runtimeScriptOffset.columnOffset, + }; + } + + return obj; + } + /** Offsets a location that came from source code, to where it appears in the runtime script */ + public offsetSourceToScript(obj: T): T { + if (this.runtimeScriptOffset) { + return { + ...obj, + lineNumber: obj.lineNumber + this.runtimeScriptOffset.lineOffset, + columnNumber: obj.columnNumber + this.runtimeScriptOffset.columnOffset, + }; + } + + return obj; + } + + public async equalsDap(s: Dap.Source) { + const existingAbsolutePath = await this._existingAbsolutePath; + return existingAbsolutePath + ? !s.sourceReference && existingAbsolutePath === s.path + : s.sourceReference === this.sourceReference; + } + + private setSourceMapUrl(sourceMapMetadata?: ISourceMapMetadata) { + if (!sourceMapMetadata) { + this.sourceMap = undefined; + return; + } + + this.sourceMap = { + type: SourceLocationType.SourceMap, + sourceByUrl: new Map(), + value: getDeferred(), + metadata: sourceMapMetadata, + }; + } + + /** + * Associated a script with this source. This is only valid for a source + * from the runtime, not a {@link SourceFromMap}. + */ + addScript(script: ISourceScript): void { + this._scripts.push(script); + } + + /** + * Filters scripts from a source, done when an execution context is removed. + */ + filterScripts(fn: (s: ISourceScript) => boolean): void { + this._scripts = this._scripts.filter(fn); + } + + /** + * Gets scripts associated with this source. + */ + get scripts(): ReadonlyArray { + return this._scripts; + } + + /** + * Gets a suggested mimetype for the source. + */ + get getSuggestedMimeType(): string | undefined { + if (this.url.endsWith('.wat')) { + return 'text/wat'; // does not seem to be any standard mime type for WAT + } + + // only return an explicit mimetype if the file has no extension (such as + // with node internals) or a query path. Otherwise, let the editor guess. + if (!/\.[^/]+$/.test(this.url) || this.url.includes('?')) { + return 'text/javascript'; + } + } + + async content(): Promise { + let content = await this._contentGetter(); + + // pad for the inline source offset, see + // https://github.com/microsoft/vscode-js-debug/issues/736 + if (this.inlineScriptOffset?.lineOffset) { + content = '\n'.repeat(this.inlineScriptOffset.lineOffset) + content; + } + + return content; + } + + /** + * Pretty-prints the source. Generates a beauitified source map if possible + * and it hasn't already been done, and returns the created map and created + * ephemeral source. Returns undefined if the source can't be beautified. + */ + public async prettyPrint(): Promise<{ map: SourceMap; source: Source } | undefined> { + if (!this._container) { + return undefined; + } + + if ( + isSourceWithSourceMap(this) + && this.sourceMap.metadata.sourceMapUrl.endsWith('-pretty.map') + ) { + const map = this.sourceMap.value.settledValue; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return map && { map, source: [...this.sourceMap.sourceByUrl!.values()][0] }; + } + + const content = await this.content(); + if (!content) { + return undefined; + } + + // Eval'd scripts have empty urls, give them a temporary one for the purpose + // of the sourcemap. See #929 + const baseUrl = this.url || `eval://${this.sourceReference}.js`; + const sourceMapUrl = baseUrl + '-pretty.map'; + const basename = baseUrl.split(/[\/\\]/).pop() as string; + const fileName = basename + '-pretty.js'; + const map = await prettyPrintAsSourceMap(fileName, content, baseUrl, sourceMapUrl); + if (!map) { + return undefined; + } + + // Note: this overwrites existing source map. + this.setSourceMapUrl({ + compiledPath: this.absolutePath, + sourceMapUrl: '', + }); + (this.sourceMap as ISourceMapLocationProvider).value.resolve(map); + + const asCompiled = this as ISourceWithMap; + await this._container._addSourceMapSources(asCompiled, map); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return { map, source: [...asCompiled.sourceMap.sourceByUrl.values()][0] }; + } + + /** + * Returns a DAP representation of the source. + * Has a side-effect of announcing the script if it has not yet been annoucned. + */ + public async toDap(): Promise { + const existingAbsolutePath = await this._existingAbsolutePath; + const dap: Dap.Source = { + name: this._name, + path: this._fqname, + sourceReference: this.sourceReference, + presentationHint: this.blackboxed() ? 'deemphasize' : undefined, + origin: this.blackboxed() ? l10n.t('Skipped by skipFiles') : undefined, + }; + + if (existingAbsolutePath) { + dap.sourceReference = 0; + dap.path = existingAbsolutePath; + } + + if (!this.hasBeenAnnounced) { + this.hasBeenAnnounced = true; + this._container.emitLoadedSource(this); + } + + return dap; + } + + existingAbsolutePath(): Promise { + return this._existingAbsolutePath; + } + + async prettyName(): Promise { + const path = await this._existingAbsolutePath; + if (path) return path; + return this._fqname; + } + + /** + * Gets the human-readable name of the source. + */ + private _humanName() { + if (utils.isAbsolute(this._fqname)) { + for (const root of this._container.rootPaths) { + if (isSubdirectoryOf(root, this._fqname)) { + return forceForwardSlashes(relative(root, this._fqname)); + } + } + } + + return this._fqname; + } + + /** + * Returns a pretty name for the script. This is the name displayed in + * stack traces and returned through DAP if the file does not verifiably + * exist on disk. + */ + private _fullyQualifiedName(): string { + if (!this.url) { + return '/VM' + this.sourceReference; + } + + if (this.url.endsWith(sourceUtils.SourceConstants.ReplExtension)) { + return 'repl'; + } + + if (this.url.startsWith(node15InternalsPrefix)) { + return nodeInternalsToken + '/' + this.url.slice(node15InternalsPrefix.length); + } + + if (this.absolutePath.startsWith(nodeInternalsToken)) { + return this.absolutePath; + } + + if (utils.isAbsolute(this.url)) { + return this.url; + } + + const parsedAbsolute = utils.fileUrlToAbsolutePath(this.url); + if (parsedAbsolute) { + return parsedAbsolute; + } + + let fqname = this.url; + try { + const tokens: string[] = []; + const url = new URL(this.url); + if (url.protocol === 'data:') { + return '/VM' + this.sourceReference; + } + + if (url.hostname) { + tokens.push(url.hostname); + } + + if (url.port) { + tokens.push('\uA789' + url.port); // : in unicode + } + + if (url.pathname) { + tokens.push(/^\/[a-z]:/.test(url.pathname) ? url.pathname.slice(1) : url.pathname); + } + + const searchParams = url.searchParams?.toString(); + if (searchParams) { + tokens.push('?' + searchParams); + } + + fqname = tokens.join(''); + } catch (e) { + // ignored + } + + if (fqname.endsWith('/')) { + fqname += '(index)'; + } + + if (this.inlineScriptOffset) { + fqname += `\uA789${this.inlineScriptOffset.lineOffset + 1}:${ + this.inlineScriptOffset.columnOffset + 1 + }`; + } + return fqname; + } + + /** + * Gets whether this script is blackboxed (part of the skipfiles). + */ + public blackboxed(): boolean { + return this._container.isSourceSkipped(this.url); + } +} + +export interface IWasmLocationProvider extends ISourceLocationProvider { + type: SourceLocationType.WasmSymbols; + value: IDeferred; +} +export interface ISourceScript { + executionContextId: Cdp.Runtime.ExecutionContextId; + scriptId: Cdp.Runtime.ScriptId; + embedderName?: string; + hasSourceURL: boolean; + url: string; +} + +export const enum SourceLocationType { + SourceMap, + WasmSymbols, +} + +export interface ISourceLocationProvider { + sourceByUrl: Map; +} + +export interface ISourceMapLocationProvider extends ISourceLocationProvider { + type: SourceLocationType.SourceMap; + /** Metadata from the source map. */ + metadata: ISourceMapMetadata; + /** The loaded sourcemap, or undefined if loading it failed. */ + value: IDeferred; +} + +export type SourceLocationProvider = ISourceMapLocationProvider | IWasmLocationProvider; +export namespace SourceLocationProvider { + /** Waits for the sourcemap or wasm symbols to be loaded. */ + export async function waitForValue( + p: SourceLocationProvider, + ): Promise { + return p.value.promise; + } + + /** Waits for the sourcemap or wasm symbols to be loaded. */ + export function waitForValueWithTimeout( + p: SourceLocationProvider, + timeout: number, + ): Promise { + if (p.type === SourceLocationType.SourceMap && p.value.settledValue) { + return Promise.resolve(p.value.settledValue); + } + + return Promise.race([waitForValue(p), delay(timeout) as Promise]); + } + + /** Waits for the location to be available before returning {@link ISourceLocationProvider.sourceByUrl} */ + export async function waitForSources(p: SourceLocationProvider) { + await waitForValue(p); + return p.sourceByUrl; + } +} +/** + * A Source that has an associated sourcemap. + */ + +export interface ISourceWithMap + extends Source +{ + sourceMap: T; +} +/** + * A Source generated from a sourcemap. For example, a TypeScript input file + * discovered from its compiled JavaScript code. + */ + +export class SourceFromMap extends Source { + // Sources generated from the source map are referenced by some compiled sources + // (through a source map). This map holds the original |sourceUrl| as written in the + // source map, which was used to produce this source for each compiled. + public readonly compiledToSourceUrl = new Map(); +} + +export class WasmSource extends Source implements ISourceWithMap { + public readonly sourceMap: IWasmLocationProvider; + + constructor( + container: SourceContainer, + public readonly event: Cdp.Debugger.ScriptParsedEvent, + absolutePath: string | undefined, + ) { + super( + container, + event.url, + absolutePath, + () => Promise.resolve('Binary content not shown, see the decompiled WAT file'), + undefined, + undefined, + undefined, + undefined, + ); + + this.sourceMap = { + type: SourceLocationType.WasmSymbols, + value: getDeferred(), + // todo: popular sourceByUrl when wasm symbols load + sourceByUrl: new Map(), + }; + } + + protected override checkContentHash(): Promise { + // We translate wasm to wat, so we should never use the original disk version: + return Promise.resolve(undefined); + } + + /** Offsets a location that came from the runtime script, to where it appears in source code. (Base 1 locations) */ + public override offsetScriptToSource( + obj: T, + ): T { + return obj; + } + /** Offsets a location that came from source code, to where it appears in the runtime script. (Base 1 locations) */ + public override offsetSourceToScript( + obj: T, + ): T { + return obj; + } +} + +export const isSourceWithMap = (source: unknown): source is ISourceWithMap => + !!source && source instanceof Source && !!source.sourceMap; + +export const isSourceWithSourceMap = ( + source: unknown, +): source is ISourceWithMap => + isSourceWithMap(source) && source.sourceMap.type === SourceLocationType.SourceMap; + +export const isSourceWithWasm = ( + source: unknown, +): source is ISourceWithMap => + isSourceWithMap(source) && source.sourceMap.type === SourceLocationType.WasmSymbols; + +export const isWasmSymbols = ( + source: SourceMap | IWasmSymbols | undefined, +): source is IWasmSymbols => + !!source && typeof (source as IWasmSymbols).getDisassembly === 'function'; + +export type ContentGetter = () => Promise; +export type LineColumn = { lineNumber: number; columnNumber: number }; // 1-based + +export function uiToRawOffset(lc: T, offset?: InlineScriptOffset): T { + if (!offset) { + return lc; + } + + let { lineNumber, columnNumber } = lc; + if (offset) { + lineNumber += offset.lineOffset; + if (lineNumber <= 1) columnNumber += offset.columnOffset; + } + + return { ...lc, lineNumber, columnNumber }; +} + +export function rawToUiOffset(lc: T, offset?: InlineScriptOffset): T { + if (!offset) { + return lc; + } + + let { lineNumber, columnNumber } = lc; + if (offset) { + lineNumber = Math.max(1, lineNumber - offset.lineOffset); + if (lineNumber <= 1) columnNumber = Math.max(1, columnNumber - offset.columnOffset); + } + + return { ...lc, lineNumber, columnNumber }; +} + +export const base0To1 = (lc: LineColumn) => ({ + lineNumber: lc.lineNumber + 1, + columnNumber: lc.columnNumber + 1, +}); + +export const base1To0 = (lc: LineColumn) => ({ + lineNumber: lc.lineNumber - 1, + columnNumber: lc.columnNumber - 1, +}); // This is a ui location which corresponds to a position in the document user can see (Source, Dap.Source). + +/** @todo make this use IPosition's instead */ +export interface IUiLocation { + lineNumber: number; // 1-based + columnNumber: number; // 1-based + source: Source; +} diff --git a/code/extensions/js-debug/src/adapter/sourceContainer.ts b/code/extensions/js-debug/src/adapter/sourceContainer.ts new file mode 100644 index 000000000000..e91cbb153b61 --- /dev/null +++ b/code/extensions/js-debug/src/adapter/sourceContainer.ts @@ -0,0 +1,1019 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND } from '@jridgewell/trace-mapping'; +import { inject, injectable } from 'inversify'; +import { xxHash32 } from 'js-xxhash'; +import Cdp from '../cdp/api'; +import { MapUsingProjection } from '../common/datastructure/mapUsingProjection'; +import { EventEmitter } from '../common/events'; +import { ILogger, LogTag } from '../common/logging'; +import { properResolve } from '../common/pathUtils'; +import { Base01Position, Base1Position, IPosition } from '../common/positions'; +import { ISourceMapMetadata, SourceMap } from '../common/sourceMaps/sourceMap'; +import { CachingSourceMapFactory, ISourceMapFactory } from '../common/sourceMaps/sourceMapFactory'; +import { InlineScriptOffset, ISourcePathResolver } from '../common/sourcePathResolver'; +import * as sourceUtils from '../common/sourceUtils'; +import * as utils from '../common/urlUtils'; +import { AnyLaunchConfiguration } from '../configuration'; +import Dap from '../dap/api'; +import { IDapApi } from '../dap/connection'; +import { sourceMapParseFailed } from '../dap/errors'; +import { IInitializeParams } from '../ioc-extras'; +import { IStatistics } from '../telemetry/classification'; +import { extractErrorDetails } from '../telemetry/dapTelemetryReporter'; +import { ensureWATExtension, IWasmSymbolProvider, IWasmSymbols } from './dwarf/wasmSymbolProvider'; +import { IResourceProvider } from './resourceProvider'; +import { ScriptSkipper } from './scriptSkipper/implementation'; +import { IScriptSkipper } from './scriptSkipper/scriptSkipper'; +import { + ContentGetter, + ISourceMapLocationProvider, + ISourceScript, + ISourceWithMap, + isSourceWithMap, + isSourceWithSourceMap, + isSourceWithWasm, + isWasmSymbols, + IUiLocation, + LineColumn, + rawToUiOffset, + Source, + SourceFromMap, + SourceLocationProvider, + uiToRawOffset, + WasmSource, +} from './source'; +import { Script } from './threads'; + +function isUiLocation(loc: unknown): loc is IUiLocation { + return ( + typeof (loc as IUiLocation).lineNumber === 'number' + && typeof (loc as IUiLocation).columnNumber === 'number' + && !!(loc as IUiLocation).source + ); +} + +const getFallbackPosition = () => ({ + source: null, + line: null, + column: null, + name: null, + lastColumn: null, + isSourceMapLoadFailure: true, +}); + +export type SourceMapTimeouts = { + // This is a source map loading delay used for testing. + load: number; + + // When resolving a location (e.g. to show it in the debug console), we wait no longer than + // |resolveLocation| timeout for source map to be loaded, and fallback to original location + // in the compiled source. + resolveLocation: number; + + // When pausing before script with source map, we wait no longer than |sourceMapMinPause| timeout + // for source map to be loaded and breakpoints to be set. This usually ensures that breakpoints + // won't be missed. + sourceMapMinPause: number; + + // Normally we only give each source-map sourceMapMinPause time to load per sourcemap. sourceMapCumulativePause + // adds some additional time we spend parsing source-maps, but it's spent accross all source-maps in that // // // session + sourceMapCumulativePause: number; + + // When sending multiple entities to debug console, we wait for each one to be asynchronously + // processed. If one of them stalls, we resume processing others after |output| timeout. + output: number; +}; + +const viteHMRPattern = /\?t=[0-9]+$/; + +/** Gets whether the URL is a compiled source containing a webpack HMR */ +const isHMR = (url: string) => url.endsWith('.hot-update.js') || viteHMRPattern.test(url); + +const defaultTimeouts: SourceMapTimeouts = { + load: 0, + resolveLocation: 2000, + sourceMapMinPause: 1000, + output: 1000, + sourceMapCumulativePause: 10000, +}; + +const isOriginalSourceOf = (compiled: Source, original: Source) => + original instanceof SourceFromMap + && original.compiledToSourceUrl.has(compiled as ISourceWithMap); + +export interface IPreferredUiLocation extends IUiLocation { + isMapped: boolean; + unmappedReason?: UnmappedReason; +} + +export enum UnmappedReason { + /** The map has been disabled temporarily, due to setting a breakpoint in a compiled script */ + MapDisabled, + + /** The source in the UI location has no map */ + HasNoMap, + + /** The location cannot be source mapped due to an error loading the map */ + MapLoadingFailed, + + /** The location cannot be source mapped due to its position not being present in the map */ + MapPositionMissing, + + /** + * The location cannot be sourcemapped, due to not having a sourcemap, + * failing to load the sourcemap, not having a mapping in the sourcemap, etc + */ + CannotMap, +} + +const maxInt32 = 2 ** 31 - 1; + +@injectable() +export class SourceContainer { + /** + * Project root path, if set. + */ + public readonly rootPaths: string[] = []; + + /** + * Mapping of CDP script IDs to Script objects. + */ + private readonly scriptsById: Map = new Map(); + + private onSourceMappedSteppingChangeEmitter = new EventEmitter(); + private onScriptEmitter = new EventEmitter`); + + const evaluate = p.evaluate('test()'); + + await pauseAndNext(p); // debugger statement + await pauseAndNext(p); // f=eval(... + await waitForPause(p); // should now be on f(1, 2) + + await evaluate; + p.assertLog(); + }, + ); + + itIntegrates( + 'does not interrupt stepIn with instrumentation breakpoint (#1665)', + async ({ r }) => { + const p = await r.launchAndLoad(` + `); + + const evaluate = p.evaluate('test()'); + + const a = p.log(await p.dap.once('stopped')); // debugger statement + await p.logger.logStackTrace(a.threadId); + await p.dap.stepIn({ threadId: a.threadId }); + + const b = p.log(await p.dap.once('stopped')); // f=eval(... + await p.logger.logStackTrace(b.threadId); + await p.dap.stepIn({ threadId: b.threadId }); + + await waitForPause(p); // should now be on (function (a, b) + + await evaluate; + p.assertLog(); + }, + ); + + itIntegrates('deals with removed execution contexts (#1582)', async ({ r }) => { + const p = await r.launchUrlAndLoad('iframe-1582/index.html'); + + const source: Dap.Source = { + path: p.workspacePath('web/iframe-1582/inner.js'), + }; + p.dap.setBreakpoints({ source, breakpoints: [{ line: 3 }] }); + await waitForPause(p, async () => { + await p.dap.setBreakpoints({ source, breakpoints: [] }); + p.dap.evaluate({ + expression: 'document.getElementsByTagName("IFRAME")[0].src += "?cool=true"', + context: 'repl', + }); + }); + + await p.dap.once( + 'loadedSource', + e => e.reason === 'new' && !!e.source.name?.includes('inner.js'), + ); + + // re-sets the breakpoints in the new script + p.dap.setBreakpoints({ source, breakpoints: [{ line: 3 }] }); + + await waitForPause(p); + p.assertLog(); + }); + + itIntegrates('sets file uri breakpoints predictably (#1748)', async ({ r }) => { + createFileTree(testFixturesDir, { + 'pages/main.html': '', + 'scripts/hello.js': 'console.log(42)', + }); + + const mainFile = join(testFixturesDir, 'pages/main.html'); + const p = await r.launchUrl(absolutePathToFileUrlWithDetection(mainFile), { + url: undefined, + file: mainFile, + }); + + const source: Dap.Source = { path: join(testFixturesDir, 'scripts/hello.js') }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 1 }] }); + p.load(); + + await waitForPause(p); + p.assertLog(); + }); + + itIntegrates('disables entrypoint breakpoint when in file (vscode#230201)', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': `function firstfunc(arg){ + return arg * arg +} +const a = firstfunc(2); +console.log('Finished awesome program');`, + }); + + const handle = await r.runScript('test.js', { + cwd: testFixturesDir, + }); + + await handle.dap.setBreakpoints({ + source: { path: join(testFixturesDir, 'test.js') }, + breakpoints: [{ line: 4, column: 1 }], + }); + + handle.load(); + const { threadId } = await handle.dap.once('stopped'); + await handle.dap.next({ threadId: threadId! }); + await waitForPause(handle); + r.assertLog({ substring: true }); + }); +}); diff --git a/code/extensions/js-debug/src/test/browser/blazorSourcePathResolverTest.ts b/code/extensions/js-debug/src/test/browser/blazorSourcePathResolverTest.ts new file mode 100644 index 000000000000..f0dd51828a6b --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/blazorSourcePathResolverTest.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { promises as fsPromises } from 'fs'; +import path from 'path'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { Logger } from '../../common/logging/logger'; +import { getCaseSensitivePaths } from '../../common/urlUtils'; +import { defaultSourceMapPathOverrides } from '../../configuration'; +import { BlazorSourcePathResolver } from '../../targets/browser/blazorSourcePathResolver'; +import { testVueMapper } from '../../targets/browser/browserPathResolver.test'; +import { testFixturesDir } from '../test'; + +function createBlazorSourcePathResolver( + remoteFilePrefix: string | undefined, +): BlazorSourcePathResolver { + return new BlazorSourcePathResolver( + testVueMapper, + new LocalFsUtils(fsPromises), + { + workspaceFolder: testFixturesDir, + pathMapping: { '/': path.join(testFixturesDir, 'web') }, + clientID: 'vscode', + baseUrl: 'http://localhost:1234/', + sourceMapOverrides: defaultSourceMapPathOverrides(path.join(testFixturesDir, 'web')), + localRoot: null, + remoteRoot: null, + resolveSourceMapLocations: null, + remoteFilePrefix, + }, + Logger.null, + ); +} + +const isWindows = process.platform === 'win32'; +const platformRoot = isWindows ? 'C:' : '/c'; + +describe('BlazorSourcePathResolver.absolutePathToUrlRegexp', () => { + it('generates the correct regexp in local scenarios', async () => { + const sourcePath = path.join( + platformRoot, + 'Users', + 'digeff', + 'source', + 'repos', + 'MyBlazorApp', + 'MyBlazorApp', + 'Pages', + 'Counter.razor', + ); + const regexp = await createBlazorSourcePathResolver(undefined).absolutePathToUrlRegexp( + sourcePath, + ); + + if (getCaseSensitivePaths()) { + expect(regexp).to.equal( + 'file:\\/\\/\\/c\\/Users\\/digeff\\/source\\/repos\\/MyBlazorApp\\/MyBlazorApp\\/Pages\\/Counter\\.razor($|\\?)' + + '|\\/c\\/Users\\/digeff\\/source\\/repos\\/MyBlazorApp\\/MyBlazorApp\\/Pages\\/Counter\\.razor($|\\?)' + + '|http:\\/\\/localhost:1234\\/\\.\\.\\/\\.\\.\\/\\.\\.\\/\\.\\.\\/\\.\\.\\/\\.\\.\\/\\.\\.\\/c\\/Users\\/digeff\\/source\\/repos\\/MyBlazorApp\\/MyBlazorApp\\/Pages\\/Counter\\.razor($|\\?)', + ); + } else { + // This regexp was generated from running the real scenario, verifying that the breakpoint with this regexp works, and then copying it here + expect(regexp).to.contain( + '[fF][iI][lL][eE]:\\/\\/\\/[cC]:\\/[uU][sS][eE][rR][sS]\\/[dD][iI][gG][eE][fF][fF]\\/[sS][oO][uU][rR][cC][eE]\\/' + + '[rR][eE][pP][oO][sS]\\/[mM][yY][bB][lL][aA][zZ][oO][rR][aA][pP][pP]\\/[mM][yY][bB][lL][aA][zZ][oO][rR][aA][pP][pP]\\/' + + '[pP][aA][gG][eE][sS]\\/[cC][oO][uU][nN][tT][eE][rR]\\.[rR][aA][zZ][oO][rR]($|\\?)|[cC]:\\\\[uU][sS][eE][rR][sS]\\\\[dD][iI][gG][eE][fF][fF]\\\\' + + '[sS][oO][uU][rR][cC][eE]\\\\[rR][eE][pP][oO][sS]\\\\[mM][yY][bB][lL][aA][zZ][oO][rR][aA][pP][pP]\\\\[mM][yY][bB][lL][aA][zZ][oO][rR][aA][pP][pP]\\\\' + + '[pP][aA][gG][eE][sS]\\\\[cC][oO][uU][nN][tT][eE][rR]\\.[rR][aA][zZ][oO][rR]($|\\?)', + ); + } + }); + + if (isWindows) { + // At the moment the Blazor remote scenario is only supported on VS/Windows + + it('generates the correct regexp in codespace scenarios', async () => { + const remoteFilePrefix = path.join( + platformRoot, + 'Users', + 'digeff', + 'AppData', + 'Local', + 'Temp', + '2689D069D40B1EFF4B570B2DB12506073980', + '5~~', + ); + const sourcePath = + `${remoteFilePrefix}\\C$\\workspace\\NewBlazorWASM\\NewBlazorWASM\\Pages\\Counter.razor`; + const regexp = await createBlazorSourcePathResolver(remoteFilePrefix) + .absolutePathToUrlRegexp( + sourcePath, + ); + + // This regexp was generated from running the real scenario, verifying that the breakpoint with this regexp works, and then copying it here + expect(regexp).to.equal( + 'dotnet://.*\\.dll/[cC]\\/[wW][oO][rR][kK][sS][pP][aA][cC][eE]\\/[nN][eE][wW][bB][lL][aA][zZ][oO][rR][wW][aA][sS][mM]\\/' + + '[nN][eE][wW][bB][lL][aA][zZ][oO][rR][wW][aA][sS][mM]\\/[pP][aA][gG][eE][sS]\\/[cC][oO][uU][nN][tT][eE][rR]\\.[rR][aA][zZ][oO][rR]($|\\?)', + ); + }); + } +}); diff --git a/code/extensions/js-debug/src/test/browser/browser-args.test.ts b/code/extensions/js-debug/src/test/browser/browser-args.test.ts new file mode 100644 index 000000000000..55683437df58 --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/browser-args.test.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { BrowserArgs } from '../../targets/browser/browserArgs'; + +describe('BrowserArgs', () => { + it('merge', () => { + const actual = new BrowserArgs(['--a', '--b=foo']).merge(['--b=bar', '--c']); + expect(actual.toArray()).to.deep.equal(['--a', '--b=bar', '--c']); + }); + + it('add', () => { + const actual = new BrowserArgs(['--a', '--b=foo']).add('--a').add('--b', 'bar').add('--c'); + expect(actual.toArray()).to.deep.equal(['--a', '--b=bar', '--c']); + }); + + it('remove', () => { + const actual = new BrowserArgs(['--a', '--b=foo']).remove('--b'); + expect(actual.toArray()).to.deep.equal(['--a']); + }); + + it('getSuggestedConnection', () => { + expect(new BrowserArgs(['--a', '--b=foo']).getSuggestedConnection()).to.be.undefined; + expect( + new BrowserArgs(['--a', '--remote-debugging-port=42']).getSuggestedConnection(), + ).to.equal(42); + expect(new BrowserArgs(['--a', '--remote-debugging-pipe']).getSuggestedConnection()).to + .equal( + 'pipe', + ); + }); + + it('setConnection', () => { + const original = new BrowserArgs([ + '--a', + '--remote-debugging-port=42', + '--remote-debugging-pipe', + ]); + expect(original.setConnection('pipe').toArray()).to.deep.equal([ + '--a', + '--remote-debugging-pipe', + ]); + expect(original.setConnection(1337).toArray()).to.deep.equal([ + '--a', + '--remote-debugging-port=1337', + ]); + }); + + it('filter', () => { + const actual = new BrowserArgs(['--a', '--b=42', '--c=44']).filter( + (k, v) => k === '--b' || v === '44', + ); + expect(actual.toArray()).to.deep.equal(['--b=42', '--c=44']); + }); +}); diff --git a/code/extensions/js-debug/src/test/browser/browser-launch-environment-variables.txt b/code/extensions/js-debug/src/test/browser/browser-launch-environment-variables.txt new file mode 100644 index 000000000000..2db2755da426 --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/browser-launch-environment-variables.txt @@ -0,0 +1 @@ +result: 0 diff --git a/code/extensions/js-debug/src/test/browser/browser-launch-runtime-args.txt b/code/extensions/js-debug/src/test/browser/browser-launch-runtime-args.txt new file mode 100644 index 000000000000..5be21e5cf2eb --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/browser-launch-runtime-args.txt @@ -0,0 +1,6 @@ +> result: (2) [678, 456] + 0: 678 + 1: 456 + length: 2 + > [[Prototype]]: Array(0) + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/browser/browser-launch.test.ts b/code/extensions/js-debug/src/test/browser/browser-launch.test.ts new file mode 100644 index 000000000000..e158837d3909 --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/browser-launch.test.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { mkdirSync, readdirSync } from 'fs'; +import WebSocket from 'ws'; +import { constructInspectorWSUri } from '../../targets/browser/constructInspectorWSUri'; +import { testFixturesDir } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('browser launch', () => { + itIntegrates('environment variables', async ({ r }) => { + if (process.platform === 'win32') { + return; // Chrome on windows doesn't set the TZ correctly + } + + const p = await r.launchUrlAndLoad('index.html', { + env: { + TZ: 'GMT', + }, + }); + + await p.logger.evaluateAndLog(`new Date().getTimezoneOffset()`); + r.assertLog(); + }); + + itIntegrates('runtime args', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html', { + runtimeArgs: ['--window-size=678,456'], + }); + + await p.logger.evaluateAndLog(`[window.outerWidth, window.outerHeight]`); + r.assertLog(); + }); + + itIntegrates.skip('user data dir', async ({ r }) => { + mkdirSync(testFixturesDir, { recursive: true }); + expect(readdirSync(testFixturesDir)).to.be.empty; + + await r.launchUrlAndLoad('index.html', { + userDataDir: testFixturesDir, + }); + + expect(readdirSync(testFixturesDir)).to.not.be.empty; + }); + + itIntegrates( + 'connects to rewritten websocket when using inspectUri parameter', + async ({ r }) => { + const pagePort = 5935; + const wsServer = new WebSocket.WebSocketServer({ + port: pagePort, + path: '/_framework/debug/ws-proxy', + }); + + try { + const receivedMessage = new Promise(resolve => { + wsServer.on('connection', ws => { + ws.on('message', message => { + const contents = JSON.parse(message.toString()); + ws.send( + JSON.stringify({ id: contents.id, error: { message: 'Fake websocket' } }), + ); + resolve(message.toString()); // We resolve with the contents of the first message we receive + ws.close(); + }); + }); + }); + + r.launchUrl(`index.html`, { + inspectUri: + `{wsProtocol}://{url.hostname}:${pagePort}/_framework/debug/ws-proxy?browser={browserInspectUri}`, + }); // We don't care about the launch result, as long as we connect to the WebSocket + + expect(await receivedMessage).to.be.eq( + '{"id":1001,"method":"Target.attachToBrowserTarget","params":{}}', + ); // Verify we got the first message on the WebSocket + } finally { + await new Promise(r => wsServer.close(r)); + } + }, + ); +}); + +describe('constructInspectorWSUri', () => { + const inspectUri = + '{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}'; + + const appHttpUrl = 'http://localhost:5001/'; + const browserWsInspectUri = + 'ws://127.0.0.1:36775/devtools/browser/a292f96c-7332-4ce8-82a9-7411f3bd280a'; + const encodedBrowserWsInspectUri = encodeURIComponent(browserWsInspectUri); + + const appHttpsUrl = 'https://localhost:5001/'; + it('interpolates arguments to construct inspectUri', () => { + expect(constructInspectorWSUri(inspectUri, appHttpUrl, browserWsInspectUri)).to.be.eq( + `ws://localhost:5001/_framework/debug/ws-proxy?browser=${encodedBrowserWsInspectUri}`, + ); + expect(constructInspectorWSUri(inspectUri, appHttpsUrl, browserWsInspectUri)).to.be.eq( + `wss://localhost:5001/_framework/debug/ws-proxy?browser=${encodedBrowserWsInspectUri}`, + ); + }); + + it('does not do anything with arguments that does not exist', () => { + expect( + constructInspectorWSUri( + inspectUri + '&{iDoNotExist}{meEither}', + appHttpUrl, + browserWsInspectUri, + ), + ).to.be.eq( + `ws://localhost:5001/_framework/debug/ws-proxy?browser=${encodedBrowserWsInspectUri}&{iDoNotExist}{meEither}`, + ); + }); + + it('fails with an useful error for invalid urls', () => { + expect(() => constructInspectorWSUri(inspectUri, '.not_an_url', browserWsInspectUri)).to.throw( + /Invalid URL/, + ); + expect(() => constructInspectorWSUri(inspectUri, null, browserWsInspectUri)).to.throw( + `A valid url wasn't supplied: `, + ); + expect(() => constructInspectorWSUri(inspectUri, undefined, browserWsInspectUri)).to.throw( + `A valid url wasn't supplied: `, + ); + expect(() => constructInspectorWSUri(inspectUri, '', browserWsInspectUri)).to.throw( + `A valid url wasn't supplied: <>`, + ); + }); + + const noUrlInspectUri = + 'ws://localhost:1234/_framework/debug/ws-proxy?browser={browserInspectUri}'; + it('does not fail for an invalid url if it isnt used', () => { + expect(constructInspectorWSUri(noUrlInspectUri, '.not_an_url', browserWsInspectUri)).to.be.eq( + `ws://localhost:1234/_framework/debug/ws-proxy?browser=${encodedBrowserWsInspectUri}`, + ); + expect(constructInspectorWSUri(noUrlInspectUri, undefined, browserWsInspectUri)).to.be.eq( + `ws://localhost:1234/_framework/debug/ws-proxy?browser=${encodedBrowserWsInspectUri}`, + ); + }); +}); diff --git a/code/extensions/js-debug/src/test/browser/frames-hierarchy.txt b/code/extensions/js-debug/src/test/browser/frames-hierarchy.txt new file mode 100644 index 000000000000..98d145de0c78 --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/frames-hierarchy.txt @@ -0,0 +1,11 @@ +page "localhost:8001/frames.html" [thread "http://localhost:8001/frames.html"] @ http://localhost:8001/frames.html + iframe "http://127.0.0.1:8002/child.html" [thread "http://127.0.0.1:8002/child.html"] @ http://127.0.0.1:8002/child.html + worker "http://127.0.0.1:8002/worker.js (127.0.0.1:8002/worker.js)" [thread "http://127.0.0.1:8002/worker.js"] @ http://127.0.0.1:8002/worker.js + worker "http://127.0.0.1:8002/worker.js (127.0.0.1:8002/worker.js)" [thread "http://127.0.0.1:8002/worker.js"] @ http://127.0.0.1:8002/worker.js + iframe "http://localhost:8001/grandchild.html" [thread "http://localhost:8001/grandchild.html"] @ http://localhost:8001/grandchild.html + worker "http://localhost:8001/worker.js (localhost:8001/worker.js)" [thread "http://localhost:8001/worker.js"] @ http://localhost:8001/worker.js + iframe "http://127.0.0.1:8002/grandchild.html" [thread "http://127.0.0.1:8002/grandchild.html"] @ http://127.0.0.1:8002/grandchild.html + worker "http://127.0.0.1:8002/worker.js (127.0.0.1:8002/worker.js)" [thread "http://127.0.0.1:8002/worker.js"] @ http://127.0.0.1:8002/worker.js + worker "http://localhost:8001/worker.js (localhost:8001/worker.js)" [thread "http://localhost:8001/worker.js"] @ http://localhost:8001/worker.js + worker "http://localhost:8001/worker.js (localhost:8001/worker.js)" [thread "http://localhost:8001/worker.js"] @ http://localhost:8001/worker.js + worker "http://localhost:8001/worker.js (localhost:8001/worker.js)" [thread "http://localhost:8001/worker.js"] @ http://localhost:8001/worker.js diff --git a/code/extensions/js-debug/src/test/browser/framesTest.ts b/code/extensions/js-debug/src/test/browser/framesTest.ts new file mode 100644 index 000000000000..3783c29fa8a4 --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/framesTest.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { BrowserTarget } from '../../targets/browser/browserTargets'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('frames', () => { + itIntegrates('hierarchy', async ({ r }) => { + r.setArgs(['--site-per-process']); + const p = await r.launchUrl('frames.html'); + p.load(); + + const logTarget = (t: BrowserTarget, indent: number) => { + const s = ' '.repeat(indent); + p.log( + `${s}${t.type()} "${t.name()}" [thread "${t.scriptUrlToUrl('')}"]${ + t.fileName() ? ' @ ' + t.fileName() : '' + }`, + ); + const children = t.children(); + children.sort((t1, t2) => { + return t1.name().localeCompare(t2.name()); + }); + children.forEach(child => logTarget(child as BrowserTarget, indent + 2)); + }; + + await new Promise(f => { + r.onSessionCreated(() => { + if (r.binder.targetList().length === 11) f(); + }); + }); + r.binder + .targetList() + .filter(t => !t.parent()) + .forEach(target => logTarget(target as BrowserTarget, 0)); + + p.assertLog(); + }); +}); diff --git a/code/extensions/js-debug/src/test/browser/performance.test.ts b/code/extensions/js-debug/src/test/browser/performance.test.ts new file mode 100644 index 000000000000..bb32249c99ee --- /dev/null +++ b/code/extensions/js-debug/src/test/browser/performance.test.ts @@ -0,0 +1,14 @@ +import { expect } from 'chai'; +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +import { itIntegrates } from '../testIntegrationUtils'; + +describe('performance', () => { + itIntegrates('gets performance information', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + const res = await p.dap.getPerformance({}); + expect(res.error).to.be.undefined; + expect(res.metrics).to.not.be.empty; + }); +}); diff --git a/code/extensions/js-debug/src/test/common/cancellation.test.ts b/code/extensions/js-debug/src/test/common/cancellation.test.ts new file mode 100644 index 000000000000..ad71ac0bc0e5 --- /dev/null +++ b/code/extensions/js-debug/src/test/common/cancellation.test.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as assert from 'assert'; +import { expect } from 'chai'; +import { + CancellationTokenSource, + Cancelled, + NeverCancelled, + TaskCancelledError, + timeoutPromise, +} from '../../common/cancellation'; +import { delay } from '../../common/promiseUtil'; + +describe('CancellationToken', () => { + it('None', () => { + expect(NeverCancelled.isCancellationRequested).to.equal(false); + expect(typeof NeverCancelled.onCancellationRequested).to.equal('function'); + }); + + it('cancel before token', function(done) { + const source = new CancellationTokenSource(); + expect(source.token.isCancellationRequested).to.equal(false); + source.cancel(); + + expect(source.token.isCancellationRequested).to.equal(true); + + source.token.onCancellationRequested(() => { + assert.ok(true); + done(); + }); + }); + + it('cancel happens only once', () => { + const source = new CancellationTokenSource(); + expect(source.token.isCancellationRequested).to.equal(false); + + let cancelCount = 0; + function onCancel() { + cancelCount += 1; + } + + source.token.onCancellationRequested(onCancel); + + source.cancel(); + source.cancel(); + + expect(cancelCount).to.equal(1); + }); + + it('cancel calls all listeners', () => { + let count = 0; + + const source = new CancellationTokenSource(); + source.token.onCancellationRequested(() => { + count += 1; + }); + source.token.onCancellationRequested(() => { + count += 1; + }); + source.token.onCancellationRequested(() => { + count += 1; + }); + + source.cancel(); + expect(count).to.equal(3); + }); + + it('token stays the same', () => { + let source = new CancellationTokenSource(); + let token = source.token; + assert.ok(token === source.token); // doesn't change on get + + source.cancel(); + assert.ok(token === source.token); // doesn't change after cancel + + source.cancel(); + assert.ok(token === source.token); // doesn't change after 2nd cancel + + source = new CancellationTokenSource(); + source.cancel(); + token = source.token; + assert.ok(token === source.token); // doesn't change on get + }); + + it('dispose calls no listeners', () => { + let count = 0; + + const source = new CancellationTokenSource(); + source.token.onCancellationRequested(() => { + count += 1; + }); + + source.dispose(); + source.cancel(); + expect(count).to.equal(0); + }); + + it('dispose calls no listeners (unless told to cancel)', () => { + let count = 0; + + const source = new CancellationTokenSource(); + source.token.onCancellationRequested(() => { + count += 1; + }); + + source.dispose(true); + // source.cancel(); + expect(count).to.equal(1); + }); + + it('parent cancels child', () => { + const parent = new CancellationTokenSource(); + const child = new CancellationTokenSource(parent.token); + + let count = 0; + child.token.onCancellationRequested(() => (count += 1)); + + parent.cancel(); + + expect(count).to.equal(1); + expect(child.token.isCancellationRequested).to.equal(true); + expect(parent.token.isCancellationRequested).to.equal(true); + }); + + describe('cancellableRace', () => { + it('returns the value when no cancellation is requested', async () => { + const v = await timeoutPromise(Promise.resolve(42), NeverCancelled); + expect(v).to.equal(42); + }); + + it('throws if cancellation is requested', async () => { + try { + await timeoutPromise(Promise.resolve(42), Cancelled); + throw new Error('expected to throw'); + } catch (e) { + if (e instanceof TaskCancelledError) { + expect(e.message).to.equal('Task cancelled'); + } else { + throw e; + } + } + }); + + it('throws if lazy cancellation is requested', async () => { + try { + await timeoutPromise( + delay(1000), + CancellationTokenSource.withTimeout(3).token, + 'Could not do the thing', + ); + throw new Error('expected to throw'); + } catch (e) { + if (e instanceof TaskCancelledError) { + expect(e.message).to.equal('Could not do the thing'); + } else { + throw e; + } + } + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/common/cdpTransport.test.ts b/code/extensions/js-debug/src/test/common/cdpTransport.test.ts new file mode 100644 index 000000000000..10bc7c23ae58 --- /dev/null +++ b/code/extensions/js-debug/src/test/common/cdpTransport.test.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { randomBytes } from 'crypto'; +import { stub } from 'sinon'; +import { PassThrough } from 'stream'; +import { AddressInfo, Server as WebSocketServer } from 'ws'; +import { RawPipeTransport } from '../../cdp/rawPipeTransport'; +import { ITransport } from '../../cdp/transport'; +import { WebSocketTransport } from '../../cdp/webSocketTransport'; +import { NeverCancelled } from '../../common/cancellation'; +import { Logger } from '../../common/logging/logger'; +import { eventuallyOk } from '../testIntegrationUtils'; + +describe('cdp transport', () => { + // cases where we create two transform streams linked to each other so that + // messages written to one are read by the other. + const cases: [string, () => Promise<{ a: ITransport; b: ITransport; dispose: () => void }>][] = [ + [ + 'raw pipe', + async () => { + const aIn = new PassThrough(); + const bIn = new PassThrough(); + + const a = new RawPipeTransport(Logger.null, aIn, bIn); + const b = new RawPipeTransport(Logger.null, bIn, aIn); + return { a, b, dispose: () => undefined }; + }, + ], + [ + 'websocket', + async () => { + const server = new WebSocketServer({ host: '127.0.0.1', port: 0 }); + await new Promise((resolve, reject) => { + server.on('listening', resolve); + server.on('error', reject); + }); + + const address = server.address() as AddressInfo; + const a = WebSocketTransport.create(`ws://127.0.0.1:${address.port}`, NeverCancelled); + const b = new Promise((resolve, reject) => { + server.on('connection', cnx => resolve(new WebSocketTransport(cnx))); + server.on('error', reject); + }); + + return { a: await a, b: await b, dispose: () => server.close() }; + }, + ], + ]; + + for (const [name, factory] of cases) { + describe(name, () => { + it('round-trips', async () => { + const rawData = randomBytes(100); + const { a, b, dispose } = await factory(); + const actual: string[] = []; + const expected: string[] = []; + + b.onMessage(([msg]) => actual.push(msg)); + + for (let i = 0; i < rawData.length;) { + const consume = 1 + Math.floor(Math.random() * 20); + const str = rawData.slice(i, i + consume).toString('base64'); + expected.push(str); + a.send(str); + i += consume; + } + + await eventuallyOk(() => expect(actual).to.deep.equal(expected)); + await a.dispose(); + dispose(); + }); + + it('bubbles closure', async () => { + const { a, b, dispose } = await factory(); + const aClose = stub(); + const bClose = stub(); + a.onEnd(aClose); + b.onEnd(bClose); + await a.dispose(); + + await eventuallyOk(() => expect(aClose.called).to.be.true); + await eventuallyOk(() => expect(bClose.called).to.be.true); + dispose(); + }); + }); + } +}); diff --git a/code/extensions/js-debug/src/test/common/logging.test.ts b/code/extensions/js-debug/src/test/common/logging.test.ts new file mode 100644 index 000000000000..d24d66eea4b5 --- /dev/null +++ b/code/extensions/js-debug/src/test/common/logging.test.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { SinonStub, stub } from 'sinon'; +import { LogLevel, LogTag } from '../../common/logging'; +import { Logger } from '../../common/logging/logger'; + +describe('Logger', () => { + let sink: { write: SinonStub; setup: SinonStub; dispose: SinonStub }; + let logger: Logger; + + beforeEach(() => { + sink = { write: stub(), setup: stub().resolves(), dispose: stub() }; + logger = new Logger(); + }); + + it('buffers and logs messages once sinks are attached', async () => { + logger.verbose(LogTag.Runtime, 'Hello world!'); + await logger.setup({ sinks: [sink], showWelcome: false }); + expect(sink.write.args[0][0]).to.containSubset({ + tag: LogTag.Runtime, + message: 'Hello world!', + level: LogLevel.Verbose, + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/common/mapUsingProjection.test.ts b/code/extensions/js-debug/src/test/common/mapUsingProjection.test.ts new file mode 100644 index 000000000000..95a5b4987f46 --- /dev/null +++ b/code/extensions/js-debug/src/test/common/mapUsingProjection.test.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { MapUsingProjection } from '../../common/datastructure/mapUsingProjection'; + +describe('mapUsingProjection', () => { + it('gets values', () => { + const m: Map = new MapUsingProjection(k => k.toLowerCase()); + m.set('bar', 1); + expect(m.get('foo')).to.be.undefined; + expect(m.get('bar')).to.equal(1); + expect(m.get('bAr')).to.equal(1); + }); + + it('sets values', () => { + const m: Map = new MapUsingProjection(k => k.toLowerCase()); + m.set('bar', 1); + expect(m.get('bar')).to.equal(1); + m.set('BAR', 2); + expect(m.get('bar')).to.equal(2); + }); + + it('deletes values', () => { + const m: Map = new MapUsingProjection(k => k.toLowerCase()); + m.set('bar', 1); + m.delete('bAr'); + expect(m.get('bar')).to.be.undefined; + }); + + it('gets keys', () => { + const m: Map = new MapUsingProjection(k => k.toLowerCase()); + m.set('FOO', 1); + m.set('bar', 1); + expect([...m.keys()].sort()).to.deep.equal(['FOO', 'bar']); + }); + + it('gets values', () => { + const m: Map = new MapUsingProjection(k => k.toLowerCase()); + m.set('FOO', 1); + m.set('bar', 2); + expect([...m.values()].sort()).to.deep.equal([1, 2]); + }); +}); diff --git a/code/extensions/js-debug/src/test/common/pathUtils.test.ts b/code/extensions/js-debug/src/test/common/pathUtils.test.ts new file mode 100644 index 000000000000..4d61fc3e5480 --- /dev/null +++ b/code/extensions/js-debug/src/test/common/pathUtils.test.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +import { expect } from 'chai'; +import { fixDriveLetterAndSlashes, forceForwardSlashes } from '../../common/pathUtils'; + +describe('pathUtils', () => { + describe('forceForwardSlashes', () => { + it('works for c:/... cases', () => { + expect(forceForwardSlashes('C:\\foo\\bar')).to.equal('C:/foo/bar'); + expect(forceForwardSlashes('C:\\')).to.equal('C:/'); + expect(forceForwardSlashes('C:/foo\\bar')).to.equal('C:/foo/bar'); + }); + + it('works for relative paths', () => { + expect(forceForwardSlashes('foo\\bar')).to.equal('foo/bar'); + expect(forceForwardSlashes('foo\\bar/baz')).to.equal('foo/bar/baz'); + }); + + it('fixes escaped forward slashes', () => { + expect(forceForwardSlashes('foo\\/bar')).to.equal('foo/bar'); + }); + }); + + describe('fixDriveLetterAndSlashes', () => { + it('works for c:/... cases', () => { + expect(fixDriveLetterAndSlashes('C:/path/stuff')).to.equal('c:\\path\\stuff'); + expect(fixDriveLetterAndSlashes('c:/path\\stuff')).to.equal('c:\\path\\stuff'); + expect(fixDriveLetterAndSlashes('C:\\path')).to.equal('c:\\path'); + expect(fixDriveLetterAndSlashes('C:\\')).to.equal('c:\\'); + }); + + it('works for file:/// cases', () => { + expect(fixDriveLetterAndSlashes('file:///C:/path/stuff')).to.equal( + 'file:///c:\\path\\stuff', + ); + expect(fixDriveLetterAndSlashes('file:///c:/path\\stuff')).to.equal( + 'file:///c:\\path\\stuff', + ); + expect(fixDriveLetterAndSlashes('file:///C:\\path')).to.equal('file:///c:\\path'); + expect(fixDriveLetterAndSlashes('file:///C:\\')).to.equal('file:///c:\\'); + }); + + it('does not impact posix cases', () => { + expect(fixDriveLetterAndSlashes('file:///a/b')).to.equal('file:///a/b'); + expect(fixDriveLetterAndSlashes('/a/b')).to.equal('/a/b'); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/common/sourceMapOverrides.test.ts b/code/extensions/js-debug/src/test/common/sourceMapOverrides.test.ts new file mode 100644 index 000000000000..d5eef835c88c --- /dev/null +++ b/code/extensions/js-debug/src/test/common/sourceMapOverrides.test.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import * as path from 'path'; +import { Logger } from '../../common/logging/logger'; +import { fixDriveLetter } from '../../common/pathUtils'; +import { + defaultPathMappingResolver, + getComputedSourceRoot, +} from '../../common/sourceMaps/sourceMapResolutionUtils'; + +describe('SourceMapOverrides', () => { + describe('getComputedSourceRoot()', () => { + const resolve = (...parts: string[]) => fixDriveLetter(path.resolve(...parts)); + const genPath = resolve('/project/webroot/code/script.js'); + const GEN_URL = 'http://localhost:8080/code/script.js'; + const ABS_SOURCEROOT = resolve('/project/src'); + const WEBROOT = resolve('/project/webroot'); + const PATH_MAPPING = { '/': WEBROOT }; + + it('handles file:/// sourceRoot', async () => { + expect( + await getComputedSourceRoot( + 'file:///' + ABS_SOURCEROOT, + genPath, + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(ABS_SOURCEROOT); + }); + + it('handles /src style sourceRoot', async () => { + expect( + await getComputedSourceRoot( + '/src', + genPath, + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(resolve('/project/webroot/src')); + }); + + it('handles /src style without matching pathMapping', async () => { + expect( + await getComputedSourceRoot( + '/foo/bar', + genPath, + {}, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal('/foo/bar'); + }); + + it('handles c:/src style without matching pathMapping', async () => { + expect( + await getComputedSourceRoot( + 'c:\\foo\\bar', + genPath, + {}, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal('c:\\foo\\bar'); + }); + + it('handles ../../src style sourceRoot', async () => { + expect( + await getComputedSourceRoot( + '../../src', + genPath, + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(ABS_SOURCEROOT); + }); + + it('handles src style sourceRoot', async () => { + expect( + await getComputedSourceRoot( + 'src', + genPath, + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(resolve('/project/webroot/code/src')); + }); + + it('handles runtime script not on disk', async () => { + expect( + await getComputedSourceRoot( + '../src', + GEN_URL, + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(resolve('/project/webroot/src')); + }); + + it('when no sourceRoot specified and runtime script is on disk, uses the runtime script dirname', async () => { + expect( + await getComputedSourceRoot( + '', + genPath, + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(resolve('/project/webroot/code')); + }); + + it('when no sourceRoot specified and runtime script is not on disk, uses the runtime script dirname', async () => { + expect( + await getComputedSourceRoot( + '', + GEN_URL, + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(resolve('/project/webroot/code')); + }); + + it('no crash on debugadapter:// urls', async () => { + expect( + await getComputedSourceRoot( + '', + 'eval://123', + PATH_MAPPING, + defaultPathMappingResolver, + Logger.null, + ), + ).to.equal(resolve(WEBROOT)); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/common/sourceMapRepository.test.ts b/code/extensions/js-debug/src/test/common/sourceMapRepository.test.ts new file mode 100644 index 000000000000..5bef9aa4c33b --- /dev/null +++ b/code/extensions/js-debug/src/test/common/sourceMapRepository.test.ts @@ -0,0 +1,194 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { join } from 'path'; +import { FileGlobList } from '../../common/fileGlobList'; +import { Logger } from '../../common/logging/logger'; +import { fixDriveLetter, fixDriveLetterAndSlashes } from '../../common/pathUtils'; +import { ISearchStrategy } from '../../common/sourceMaps/sourceMapRepository'; +import { TurboSearchStrategy } from '../../common/sourceMaps/turboSearchStrategy'; +import { absolutePathToFileUrl } from '../../common/urlUtils'; +import { createFileTree } from '../createFileTree'; +import { testFixturesDir, testFixturesDirName, workspaceFolder } from '../test'; + +describe('ISourceMapRepository', () => { + [{ name: 'TurboSearchStrategy', create: () => new TurboSearchStrategy(Logger.null) }].forEach( + tcase => + describe(tcase.name, () => { + let r: ISearchStrategy; + beforeEach(() => { + r = tcase.create(); + createFileTree(testFixturesDir, { + 'a.js': '//# sourceMappingURL=a.js.map', + 'a.js.map': 'content1', + 'c.js': 'no.sourcemap.here', + nested: { + 'd.js': '//# sourceMappingURL=d.js.map', + 'd.js.map': 'content2', + }, + node_modules: { + 'e.js': '//# sourceMappingURL=e.js.map', + 'e.js.map': 'content3', + }, + defaultSearchExcluded: { + 'f.js': '//# sourceMappingURL=f.js.map', + 'f.js.map': 'content3', + }, + }); + }); + + const gatherFileList = (rootPath: string, firstIncludeSegment: string) => + new FileGlobList({ + rootPath, + patterns: [`${firstIncludeSegment}/**/*.js`, '!**/node_modules/**'], + }); + + const gatherSm = async (list: FileGlobList) => { + type TReturn = { + sourceMapUrl: string; + compiledPath: string; + }; + const result = await r.streamChildrenWithSourcemaps({ + files: list, + processMap: async m => { + const { cacheKey, ...rest } = m; + expect(cacheKey).to.be.within(Date.now() - 60 * 1000, Date.now() + 1000); + rest.compiledPath = fixDriveLetterAndSlashes(rest.compiledPath); + return rest; + }, + onProcessedMap: r => r, + }); + return result.values.sort((a, b) => a.compiledPath.length - b.compiledPath.length); + }; + const gatherSmNames = async (list: FileGlobList) => { + const result = await gatherSm(list); + return result.map(r => fixDriveLetterAndSlashes(r.compiledPath)); + }; + + const gatherAll = (list: FileGlobList) => { + return r.streamAllChildren(list, m => m).then(r => r.sort()); + }; + + it('no-ops for non-existent directories', async () => { + expect(await gatherSm(gatherFileList(__dirname, 'does-not-exist'))).to.be.empty; + }); + + it('discovers source maps and applies negated globs', async () => { + expect( + await gatherSm(gatherFileList(workspaceFolder, testFixturesDirName)), + ).to.deep.equal([ + { + compiledPath: fixDriveLetter(join(testFixturesDir, 'a.js')), + sourceMapUrl: absolutePathToFileUrl(join(testFixturesDir, 'a.js.map')), + }, + { + compiledPath: fixDriveLetter(join(testFixturesDir, 'nested', 'd.js')), + sourceMapUrl: absolutePathToFileUrl(join(testFixturesDir, 'nested', 'd.js.map')), + }, + { + compiledPath: fixDriveLetter( + join(testFixturesDir, 'defaultSearchExcluded', 'f.js'), + ), + sourceMapUrl: absolutePathToFileUrl( + join(testFixturesDir, 'defaultSearchExcluded', 'f.js.map'), + ), + }, + ]); + }); + + it('applies second patterns (vscode#168635)', async () => { + createFileTree(testFixturesDir, { + rootPath: { + 'd.js': '//# sourceMappingURL=d.js.map', + 'd.js.map': 'content2', + }, + otherFolder: { + 'f.js': '//# sourceMappingURL=f.js.map', + 'f.js.map': 'content3', + }, + }); + + expect( + await gatherSmNames( + new FileGlobList({ + patterns: ['rootPath/*.js', 'otherFolder/*.js'], + rootPath: testFixturesDir, + }), + ), + ).to.deep.equal([ + fixDriveLetter(join(testFixturesDir, 'rootPath', 'd.js')), + fixDriveLetter(join(testFixturesDir, 'otherFolder', 'f.js')), + ]); + }); + + it('globs for a single file', async () => { + expect( + await gatherSmNames( + new FileGlobList({ + patterns: ['nested/d.js'], + rootPath: testFixturesDir, + }), + ), + ).to.deep.equal([fixDriveLetter(join(testFixturesDir, 'nested', 'd.js'))]); + }); + + it('applies negated globs outside rootPath (#1479)', async () => { + // also tests https://github.com/microsoft/vscode/issues/104889#issuecomment-993722692 + const nodeModules = { + 'e.js': '//# sourceMappingURL=e.js.map', + 'e.js.map': 'content3', + }; + createFileTree(testFixturesDir, { + rootPath: { + 'd.js': '//# sourceMappingURL=d.js.map', + 'd.js.map': 'content2', + node_modules: nodeModules, + }, + otherFolder: { + 'f.js': '//# sourceMappingURL=f.js.map', + 'f.js.map': 'content3', + node_modules: nodeModules, + }, + }); + + expect( + await gatherSmNames( + new FileGlobList({ + patterns: ['**/*.js', '../otherFolder/**/*.js', '!**/node_modules/**'], + rootPath: join(testFixturesDir, 'rootPath'), + }), + ), + ).to.deep.equal([ + fixDriveLetter(join(testFixturesDir, 'rootPath', 'd.js')), + fixDriveLetter(join(testFixturesDir, 'otherFolder', 'f.js')), + ]); + }); + + it('streams all children', async () => { + expect( + await gatherAll(gatherFileList(workspaceFolder, testFixturesDirName)), + ).to.deep.equal([ + fixDriveLetter(join(testFixturesDir, 'a.js')), + fixDriveLetter(join(testFixturesDir, 'c.js')), + fixDriveLetter(join(testFixturesDir, 'defaultSearchExcluded', 'f.js')), + fixDriveLetter(join(testFixturesDir, 'nested', 'd.js')), + ]); + }); + + it('greps inside node_modules explicitly', async () => { + expect( + await gatherSm(gatherFileList(join(testFixturesDir, 'node_modules'), '.')), + ).to.deep.equal([ + { + compiledPath: fixDriveLetter(join(testFixturesDir, 'node_modules', 'e.js')), + sourceMapUrl: absolutePathToFileUrl( + join(testFixturesDir, 'node_modules', 'e.js.map'), + ), + }, + ]); + }); + }), + ); +}); diff --git a/code/extensions/js-debug/src/test/completion/completion.test.ts b/code/extensions/js-debug/src/test/completion/completion.test.ts new file mode 100644 index 000000000000..049fdc6d9677 --- /dev/null +++ b/code/extensions/js-debug/src/test/completion/completion.test.ts @@ -0,0 +1,382 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import Dap from '../../dap/api'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('completion', () => { + const tcases: [string, Dap.CompletionItem[]][] = [ + ['ar|', [{ + label: 'arr', + sortText: '~~arr', + type: 'variable', + detail: 'Array', + start: 0, + length: 2, + }]], + ['ar|.length', [{ + label: 'arr', + sortText: '~~arr', + type: 'variable', + detail: 'Array', + start: 0, + length: 2, + }]], + [ + 'arr.|', + [ + { + label: '[index]', + text: '[index]', + type: 'property', + sortText: '~~[', + length: 1, + selectionLength: 5, + selectionStart: 1, + start: 3, + }, + { + label: 'length', + detail: '3', + sortText: '~~length', + type: 'property', + }, + { + label: 'at', + detail: 'fn(?)', + sortText: '~~~at', + type: 'method', + }, + ], + ], + [ + 'arr.len|', + [ + { + label: '[index]', + text: '[index]', + type: 'property', + sortText: '~~[', + length: 1, + selectionLength: 5, + selectionStart: 1, + start: 3, + }, + { + label: 'length', + detail: '3', + sortText: '~~length', + type: 'property', + }, + ], + ], + ['arr[|', []], + [ + 'arr[0].|', + [ + { + label: 'length', + detail: '0', + sortText: '~~length', + type: 'property', + }, + { + label: 'anchor', + detail: 'fn(?)', + sortText: '~~~anchor', + type: 'method', + }, + { + label: 'at', + detail: 'fn(?)', + sortText: '~~~at', + type: 'method', + }, + ], + ], + ['arr[2].|', []], + [ + 'obj.|', + [ + { + label: 'bar', + detail: '42', + sortText: '~~bar', + type: 'property', + }, + { + label: 'baz', + detail: 'fn()', + sortText: '~~baz', + type: 'method', + }, + { + label: 'foo', + detail: 'string', + sortText: '~~foo', + type: 'property', + }, + ], + ], + ['ob|', [{ + label: 'obj', + sortText: '~~obj', + type: 'variable', + detail: 'Object', + start: 0, + length: 2, + }]], + [ + 'arr[myStr|', + [{ + label: 'myString', + sortText: '~~myString', + type: 'variable', + detail: 'string', + start: 4, + length: 5, + }], + ], + ['const replVar = 42; replV|', [{ + label: 'replVar', + sortText: 'replVar', + type: 'variable', + start: 20, + length: 5, + }]], + [ + 'MyCoolCl|', + [{ + label: 'MyCoolClass', + sortText: '~~MyCoolClass', + type: 'class', + detail: 'fn()', + start: 0, + length: 8, + }], + ], + ['Strin|', [{ + label: 'String', + sortText: '~~String', + type: 'class', + detail: 'fn(?)', + start: 0, + length: 5, + }]], + [ + 'myNeatFun|', + [ + { + label: 'myNeatFunction', + sortText: '~~myNeatFunction', + type: 'function', + detail: 'fn()', + start: 0, + length: 9, + }, + ], + ], + [ + 'new Array(42).|', + [ + { + label: '[index]', + text: '[index]', + type: 'property', + sortText: '~~[', + length: 1, + selectionLength: 5, + selectionStart: 1, + start: 13, + }, + { + label: 'length', + detail: '42', + sortText: '~~length', + type: 'property', + }, + { + label: 'at', + detail: 'fn(?)', + sortText: '~~~at', + type: 'method', + }, + ], + ], + [ + 'poison.|', + [ + { label: 'bar', sortText: '~~bar', type: 'property', detail: 'true' }, + { label: 'foo', sortText: '~~foo', type: 'property' }, + { label: 'constructor', sortText: '~~~constructor', type: 'class', detail: 'fn(?)' }, + ], + ], + [ + 'hasPrivate.|', + [ + { label: 'c', sortText: '~~c', type: 'property', detail: '3' }, + { label: '_a', sortText: '~~{a', type: 'property', detail: '1' }, + { label: '__b', sortText: '~~{{b', type: 'property', detail: '2' }, + ], + ], + [ + 'complexProp.|', + [ + { + label: 'complex prop', + text: '["complex prop"]', + start: 11, + length: 1, + type: 'property', + sortText: '~~complex prop', + detail: 'true', + }, + { label: 'constructor', sortText: '~~~constructor', type: 'class', detail: 'fn(?)' }, + { + label: 'hasOwnProperty', + sortText: '~~~hasOwnProperty', + type: 'method', + detail: 'fn(?)', + }, + ], + ], + ['$returnV|', []], + ]; + + itIntegrates('completion', async ({ r }) => { + const p = await r.launchAndLoad(` + + `); + + for (const [completion, expected] of tcases) { + const index = completion.indexOf('|'); + const actual = await p.dap.completions({ + text: completion.slice(0, index) + completion.slice(index + 1), + column: index + 1, + }); + + expect(actual.targets.slice(0, 3)).to.deep.equal( + expected, + `bad result evaluating ${completion}`, + ); + } + }); + + itIntegrates('completes in scope (vscode#153651)', async ({ r }) => { + const p = await r.launch(` + + `); + + const untilStopped = p.dap.once('stopped'); + p.load(); + + const frame = ( + await p.dap.stackTrace({ + threadId: (await untilStopped).threadId!, + }) + ).stackFrames[0]; + + const actual = await p.dap.completions({ + text: 'helloW', + column: 7, + frameId: frame.id, + }); + + expect(actual.targets).to.deep.equal([ + { + label: 'helloWorld', + type: 'property', + start: 0, + length: 6, + }, + ]); + }); + + itIntegrates('$returnValue', async ({ r }) => { + const getFrameId = async () => + ( + await p.dap.stackTrace({ + threadId: threadId!, + }) + ).stackFrames[0].id; + + const p = await r.launchAndLoad(` + + `); + + p.dap.evaluate({ expression: 'foo() ' }); + + const { threadId } = await p.dap.once('stopped'); + await p.dap.next({ threadId: threadId! }); // step past debugger; + await p.dap.once('stopped'); + + // no returnValue when not in a returned context + const a1 = await p.dap.completions({ + text: '$returnValu', + column: 11, + frameId: await getFrameId(), + }); + expect(a1.targets).to.not.containSubset([ + { + sortText: '~$returnValue', + }, + ]); + + await p.dap.next({ threadId: threadId! }); // step past return; + await p.dap.once('stopped'); + + // returnValue is available + const frameId = await getFrameId(); + const a2 = await p.dap.completions({ + text: '$returnValu', + column: 11, + frameId, + }); + + expect(a2.targets).to.containSubset([ + { + label: '$returnValue', + type: 'variable', + sortText: '~$returnValue', + }, + ]); + + // returnValue can be completed on + const a3 = await p.dap.completions({ + text: '$returnValue.', + column: 14, + frameId, + }); + + expect(a3.targets).to.containSubset([ + { + label: 'a', + sortText: '~~a', + type: 'property', + }, + ]); + }); +}); diff --git a/code/extensions/js-debug/src/test/console/console-api-format-format-string.txt b/code/extensions/js-debug/src/test/console/console-api-format-format-string.txt new file mode 100644 index 000000000000..23d088397e0a --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-api-format-format-string.txt @@ -0,0 +1,27 @@ +Evaluating: 'console.log('Log')' +stdout> Log + +Evaluating: 'console.info('Info')' +stdout> Info + +Evaluating: 'console.warn('Warn')' +stderr> Warn + +Evaluating: 'console.error('Error')' +stderr> Error + +Evaluating: 'console.assert(false, 'Assert')' +stderr> Assert + +Evaluating: 'console.assert(false)' +stderr> Assertion failed + +Evaluating: 'console.trace('Trace')' +stdout> Trace + +Evaluating: 'console.count('Counter')' +stdout> Counter: 1 + +Evaluating: 'console.count('Counter')' +stdout> Counter: 2 + diff --git a/code/extensions/js-debug/src/test/console/console-api-format-string.txt b/code/extensions/js-debug/src/test/console/console-api-format-string.txt new file mode 100644 index 000000000000..376672ffce47 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-api-format-string.txt @@ -0,0 +1,69 @@ +Evaluating: 'console.table(peopleObject)' +stdout> +> ╭┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄╮ +┊ ┊ 0 ┊ 1 ┊ +├┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┤ +┊ one ┊ John ┊ Smith ┊ +┊ two ┊ Jane ┊ Doe ┊ +┊ three ┊ Emily ┊ Jones ┊ +╰┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄╯: {one: Array(2), two: Array(2), three: Array(2)} + +Evaluating: 'console.table(peopleObject2)' +stdout> +> ╭┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄╮ +┊ ┊ name ┊ last ┊ +├┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┤ +┊ one ┊ John ┊ Smith ┊ +┊ two ┊ Jane ┊ Doe ┊ +┊ three ┊ Emily ┊ Jones ┊ +╰┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄╯: {one: {…}, two: {…}, three: {…}} + +Evaluating: 'console.table(peopleLongHeader)' +stdout> +> ╭┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄╮ +┊ ┊ first name ┊ last name ┊ +├┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┤ +┊ one ┊ John ┊ Smith ┊ +┊ two ┊ Jane ┊ Doe ┊ +┊ three ┊ Emily ┊ Jones ┊ +╰┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┄┄┄┄╯: {one: {…}, two: {…}, three: {…}} + +Evaluating: 'console.table(peopleArray)' +stdout> +> ╭┄┄┄┬┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄╮ +┊ ┊ 0 ┊ 1 ┊ +├┄┄┄┼┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┤ +┊ 0 ┊ John ┊ Smith ┊ +┊ 1 ┊ Jane ┊ Doe ┊ +┊ 2 ┊ Emily ┊ Jones ┊ +╰┄┄┄┴┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄╯: (3) [Array(2), Array(2), Array(2)] + +Evaluating: 'console.table(trimEmptyColumn)' +stdout> +> ╭┄┄┄┬┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄╮ +┊ ┊ 0 ┊ 1 ┊ +├┄┄┄┼┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┤ +┊ 0 ┊ John ┊ Smith ┊ +┊ 1 ┊ Jane ┊ Doe ┊ +┊ 2 ┊ Emily ┊ Jones ┊ +╰┄┄┄┴┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄╯: (3) [Array(3), Array(3), Array(3)] + +Evaluating: 'console.table(cellOverflow)' +stdout> +> ╭┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ +┊ ┊ 0 ┊ 1 ┊ +├┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┤ +┊ one ┊ John ┊ Smith ┊ +┊ two ┊ Jane Jane Jane Jane Jane Jane Jane Jane Jane Jane …ane Jane … ┊ Doe Doe Doe Doe Doe Doe Doe DoeDoe DoeDoe Do… ┊ +┊ th… ┊ Emily ┊ Jones ┊ +╰┄┄┄┄┄┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯: {one: Array(2), two: Array(2), three: Array(2)} + +Evaluating: 'console.table(longTableOverflow)' +stdout> +> ╭┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄┄┬┄┄┄┄… +┊ ┊ 0 ┊ 1 ┊ 2 ┊ 3 ┊ 4 ┊ 5 ┊ 6 ┊ 7 ┊ 8 ┊ 9 ┊ 10 ┊ 11 ┊ 12 ┊ 13 ┊ 14 ┊ 15 ┊ 16 ┊ 17 ┊ 18 … +├┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄┄┼┄┄┄┄… +┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 ┊ 0 … +┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 ┊ 1 … +╰┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄┄┴┄┄┄┄…: (2) [Array(1000), Array(1000)] + diff --git a/code/extensions/js-debug/src/test/console/console-format-adds-error-traces-if-they-do-not-exist.txt b/code/extensions/js-debug/src/test/console/console-format-adds-error-traces-if-they-do-not-exist.txt new file mode 100644 index 000000000000..cdfada582a96 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-adds-error-traces-if-they-do-not-exist.txt @@ -0,0 +1,27 @@ +Evaluating: 'setTimeout(() => { throw "asdf" }, 0) ' +stderr> Uncaught Error asdf + at (/VM:1:20) + --- setTimeout --- + at (/VM:1:1) +stderr> +> Uncaught Error asdf + at (/VM:1:20) + --- setTimeout --- + at (/VM:1:1) +stderr> + @ /VM:1:20 +◀ setTimeout ▶ + @ /VM:1 + +{ + category : stderr + column : 20 + line : 1 + output : Uncaught Error asdf at (/VM:1:20) --- setTimeout --- at (/VM:1:1) + source : { + name : /VM + path : /VM + sourceReference : + } + variablesReference : +} diff --git a/code/extensions/js-debug/src/test/console/console-format-ansi-colorization.txt b/code/extensions/js-debug/src/test/console/console-format-ansi-colorization.txt new file mode 100644 index 000000000000..7a3395095d52 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-ansi-colorization.txt @@ -0,0 +1,11 @@ +stdout> This is a red message +In context watch: +result: '\x1b[31mThis is a red message\x1b[0m' +> result: {x: '\x1b[31mThis is a red message\x1b[0m'} + x: '\x1b[31mThis is a red message\x1b[0m' + > [[Prototype]]: Object +In context variables: +result: '\x1b[31mThis is a red message\x1b[0m' +> result: {x: '\x1b[31mThis is a red message\x1b[0m'} + x: '\x1b[31mThis is a red message\x1b[0m' + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/console/console-format-applies-skipfiles-to-logged-stacks.txt b/code/extensions/js-debug/src/test/console/console-format-applies-skipfiles-to-logged-stacks.txt new file mode 100644 index 000000000000..c79699d6729e --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-applies-skipfiles-to-logged-stacks.txt @@ -0,0 +1,2 @@ +logged hello world + at dont-ignore-me.js:1:1 diff --git a/code/extensions/js-debug/src/test/console/console-format-array.txt b/code/extensions/js-debug/src/test/console/console-format-array.txt new file mode 100644 index 000000000000..77b616f44e02 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-array.txt @@ -0,0 +1,44 @@ +Evaluating: 'console.log(a0)' +stdout> (0) [] +stdout> > (0) [] + +Evaluating: 'console.log(a1)' +stdout> (1) […] +stdout> > (1) […] + +Evaluating: 'console.log(a2)' +stdout> (5) […] +stdout> > (5) […] + +Evaluating: 'console.log(a3)' +stdout> (3) […, 2, 3] +stdout> > (3) […, 2, 3] + +Evaluating: 'console.log(a4)' +stdout> (15) […] +stdout> > (15) […] + +Evaluating: 'console.log(a5)' +stdout> (15) […, 8, …] +stdout> > (15) […, 8, …] + +Evaluating: 'console.log(a6)' +stdout> (15) [0, …, 10, …] +stdout> > (15) [0, …, 10, …] + +Evaluating: 'console.log(a7)' +stdout> (15) […, 4, …, index0: 0, index1: 1, index2: 2, index3: 3, index4: 4, …] +stdout> > (15) […, 4, …, index0: 0, index1: 1, index2: 2, index3: 3, index4: 4, …] + +Evaluating: 'console.log(a8)' +stdout> (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +stdout> > (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +Evaluating: 'console.log(a9)' +stdout> (11) […, 1, 2, 3, 4, …, 6, 7, 8, 9, …, foo: 'bar'] +stdout> > (11) […, 1, 2, 3, 4, …, 6, 7, 8, 9, …, foo: 'bar'] + +Evaluating: 'console.log(a10)' +stdout> Array +stdout> > Array + diff --git a/code/extensions/js-debug/src/test/console/console-format-class.txt b/code/extensions/js-debug/src/test/console/console-format-class.txt new file mode 100644 index 000000000000..77b616f44e02 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-class.txt @@ -0,0 +1,44 @@ +Evaluating: 'console.log(a0)' +stdout> (0) [] +stdout> > (0) [] + +Evaluating: 'console.log(a1)' +stdout> (1) […] +stdout> > (1) […] + +Evaluating: 'console.log(a2)' +stdout> (5) […] +stdout> > (5) […] + +Evaluating: 'console.log(a3)' +stdout> (3) […, 2, 3] +stdout> > (3) […, 2, 3] + +Evaluating: 'console.log(a4)' +stdout> (15) […] +stdout> > (15) […] + +Evaluating: 'console.log(a5)' +stdout> (15) […, 8, …] +stdout> > (15) […, 8, …] + +Evaluating: 'console.log(a6)' +stdout> (15) [0, …, 10, …] +stdout> > (15) [0, …, 10, …] + +Evaluating: 'console.log(a7)' +stdout> (15) […, 4, …, index0: 0, index1: 1, index2: 2, index3: 3, index4: 4, …] +stdout> > (15) […, 4, …, index0: 0, index1: 1, index2: 2, index3: 3, index4: 4, …] + +Evaluating: 'console.log(a8)' +stdout> (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +stdout> > (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +Evaluating: 'console.log(a9)' +stdout> (11) […, 1, 2, 3, 4, …, 6, 7, 8, 9, …, foo: 'bar'] +stdout> > (11) […, 1, 2, 3, 4, …, 6, 7, 8, 9, …, foo: 'bar'] + +Evaluating: 'console.log(a10)' +stdout> Array +stdout> > Array + diff --git a/code/extensions/js-debug/src/test/console/console-format-collections.txt b/code/extensions/js-debug/src/test/console/console-format-collections.txt new file mode 100644 index 000000000000..8b76b1098ea0 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-collections.txt @@ -0,0 +1,80 @@ +Evaluating: 'console.log(nodelist)' +stdout> HTMLCollection(1) [select#sel, sel: select#sel] +stdout> > HTMLCollection(1) [select#sel, sel: select#sel] + +Evaluating: 'console.log([nodelist])' +stdout> (1) [HTMLCollection(1)] +stdout> > (1) [HTMLCollection(1)] + +Evaluating: 'console.log(htmlcollection)' +stdout> HTMLCollection(0) [] +stdout> > HTMLCollection(0) [] + +Evaluating: 'console.log([htmlcollection])' +stdout> (1) [HTMLCollection(0)] +stdout> > (1) [HTMLCollection(0)] + +Evaluating: 'console.log(options)' +stdout> HTMLOptionsCollection(2) [option, option, selectedIndex: 0] +stdout> > HTMLOptionsCollection(2) [option, option, selectedIndex: 0] + +Evaluating: 'console.log([options])' +stdout> (1) [HTMLOptionsCollection(2)] +stdout> > (1) [HTMLOptionsCollection(2)] + +Evaluating: 'console.log(all)' +stdout> HTMLAllCollection(11) [html, head, body, div.c1.c2.c3, form#f, select#sel, option, option, input, input, script, f: form#f, sel: select#sel, x: HTMLCollection(2)] +stdout> > HTMLAllCollection(11) [html, head, body, div.c1.c2.c3, form#f, select#sel, option, option, input, input, script, f: form#f, sel: select#sel, x: HTMLCollection(2)] + +Evaluating: 'console.log([all])' +stdout> (1) [HTMLAllCollection(11)] +stdout> > (1) [HTMLAllCollection(11)] + +Evaluating: 'console.log(formControls)' +stdout> HTMLFormControlsCollection(3) [select#sel, input, input, sel: select#sel, x: RadioNodeList(2)] +stdout> > HTMLFormControlsCollection(3) [select#sel, input, input, sel: select#sel, x: RadioNodeList(2)] + +Evaluating: 'console.log([formControls])' +stdout> (1) [HTMLFormControlsCollection(3)] +stdout> > (1) [HTMLFormControlsCollection(3)] + +Evaluating: 'console.log(radioNodeList)' +stdout> RadioNodeList(2) [input, input, value: ''] +stdout> > RadioNodeList(2) [input, input, value: ''] + +Evaluating: 'console.log([radioNodeList])' +stdout> (1) [RadioNodeList(2)] +stdout> > (1) [RadioNodeList(2)] + +Evaluating: 'console.log(arrayX)' +stdout> (2) [1, Array(2)] +stdout> > (2) [1, Array(2)] + +Evaluating: 'console.log([arrayX])' +stdout> (1) [Array(2)] +stdout> > (1) [Array(2)] + +Evaluating: 'console.log(nonArray)' +stdout> NonArrayWithLength {keys: Array(0)} +stdout> > NonArrayWithLength {keys: Array(0)} + +Evaluating: 'console.log([nonArray])' +stdout> (1) [NonArrayWithLength] +stdout> > (1) [NonArrayWithLength] + +Evaluating: 'console.log(generateArguments(1, "2"))' +stdout> Arguments(2) [1, '2', callee: ƒ, Symbol(Symbol.iterator): ƒ] +stdout> > Arguments(2) [1, '2', callee: ƒ, Symbol(Symbol.iterator): ƒ] + +Evaluating: 'console.log([generateArguments(1, "2")])' +stdout> (1) [Arguments(2)] +stdout> > (1) [Arguments(2)] + +Evaluating: 'console.log(div.classList)' +stdout> DOMTokenList(3) ['c1', 'c2', 'c3', value: 'c1 c2 c3'] +stdout> > DOMTokenList(3) ['c1', 'c2', 'c3', value: 'c1 c2 c3'] + +Evaluating: 'console.log([div.classList])' +stdout> (1) [DOMTokenList(3)] +stdout> > (1) [DOMTokenList(3)] + diff --git a/code/extensions/js-debug/src/test/console/console-format-colors.txt b/code/extensions/js-debug/src/test/console/console-format-colors.txt new file mode 100644 index 000000000000..4c4e76b824cc --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-colors.txt @@ -0,0 +1,21 @@ +Evaluating: 'console.log('%cColors are awesome.', 'color: blue;')' +stdout> Colors are awesome. + +Evaluating: 'console.log('%cColors are awesome.', 'background-color: red;')' +stdout> Colors are awesome. + +Evaluating: 'console.log('%cColors are awesome.', 'background-color: red;', 'Do not apply to trailing params')' +stdout> Colors are awesome. Do not apply to trailing params + +Evaluating: 'console.log('%cColors %care %cawesome.', 'color: red', 'color:green', 'color:blue')' +stdout> Colors are awesome. + +Evaluating: 'console.log('%cBold text.', 'font-weight: bold')' +stdout> Bold text. + +Evaluating: 'console.log('%cItalic text.', 'font-style: italic')' +stdout> Italic text. + +Evaluating: 'console.log('%cUnderline text.', 'text-decoration: underline')' +stdout> Underline text. + diff --git a/code/extensions/js-debug/src/test/console/console-format-custom-symbol.txt b/code/extensions/js-debug/src/test/console/console-format-custom-symbol.txt new file mode 100644 index 000000000000..f162da66b268 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-custom-symbol.txt @@ -0,0 +1,3 @@ +> result: hello a + > prop: hello b + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/console/console-format-custom-tostring.txt b/code/extensions/js-debug/src/test/console/console-format-custom-tostring.txt new file mode 100644 index 000000000000..f162da66b268 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-custom-tostring.txt @@ -0,0 +1,3 @@ +> result: hello a + > prop: hello b + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/console/console-format-error-traces-in-source-maps.txt b/code/extensions/js-debug/src/test/console/console-format-error-traces-in-source-maps.txt new file mode 100644 index 000000000000..5080dd3c231c --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-error-traces-in-source-maps.txt @@ -0,0 +1,16 @@ +Evaluating: 'try { throwError() } catch (e) { console.error(e) }' +stderr> Error + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at :1:7 {stack: 'Error + at throwError (http://localhost:800…erify/bundle.js:23:11) + at :1:7'} + +stderr> +> Error + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at :1:7 {stack: 'Error + at throwError (http://localhost:800…erify/bundle.js:23:11) + at :1:7'} +stderr> > arg0: Error\n at throwError (http://localhost:8001/browserify/bundle.js:23:11)\n at :1:7 {stack: 'Error\n at throwError (http://localhost:800…erify/bundle.js:23:11)\n at :1:7'} +stderr> @ /VM:1:42 + diff --git a/code/extensions/js-debug/src/test/console/console-format-es6-2.txt b/code/extensions/js-debug/src/test/console/console-format-es6-2.txt new file mode 100644 index 000000000000..fd15c3abaf21 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-es6-2.txt @@ -0,0 +1,64 @@ +Evaluating: 'console.log(map2.keys())' +stdout> MapIterator {41, {foo: 1}} +stdout> > MapIterator {41, {foo: 1}} + +Evaluating: 'console.log([map2.keys()])' +stdout> (1) [MapIterator] +stdout> > (1) [MapIterator] + +Evaluating: 'console.log(map2.values())' +stdout> MapIterator {42, {foo: 2}} +stdout> > MapIterator {42, {foo: 2}} + +Evaluating: 'console.log([map2.values()])' +stdout> (1) [MapIterator] +stdout> > (1) [MapIterator] + +Evaluating: 'console.log(map2.entries())' +stdout> MapIterator {41 => 42, {foo: 1} => {foo: 2}} +stdout> > MapIterator {41 => 42, {foo: 1} => {foo: 2}} + +Evaluating: 'console.log([map2.entries()])' +stdout> (1) [MapIterator] +stdout> > (1) [MapIterator] + +Evaluating: 'console.log(set2.keys())' +stdout> SetIterator {41, {foo: 1}} +stdout> > SetIterator {41, {foo: 1}} + +Evaluating: 'console.log([set2.keys()])' +stdout> (1) [SetIterator] +stdout> > (1) [SetIterator] + +Evaluating: 'console.log(set2.values())' +stdout> SetIterator {41, {foo: 1}} +stdout> > SetIterator {41, {foo: 1}} + +Evaluating: 'console.log([set2.values()])' +stdout> (1) [SetIterator] +stdout> > (1) [SetIterator] + +Evaluating: 'console.log(set2.entries())' +stdout> SetIterator {41 => 41, {foo: 1} => {foo: 1}} +stdout> > SetIterator {41 => 41, {foo: 1} => {foo: 1}} + +Evaluating: 'console.log([set2.entries()])' +stdout> (1) [SetIterator] +stdout> > (1) [SetIterator] + +Evaluating: 'console.log(iter1)' +stdout> MapIterator {{foo: 2}} +stdout> > MapIterator {{foo: 2}} + +Evaluating: 'console.log([iter1])' +stdout> (1) [MapIterator] +stdout> > (1) [MapIterator] + +Evaluating: 'console.log(iter2)' +stdout> SetIterator {{foo: 1}} +stdout> > SetIterator {{foo: 1}} + +Evaluating: 'console.log([iter2])' +stdout> (1) [SetIterator] +stdout> > (1) [SetIterator] + diff --git a/code/extensions/js-debug/src/test/console/console-format-es6.txt b/code/extensions/js-debug/src/test/console/console-format-es6.txt new file mode 100644 index 000000000000..1c4a3a579c27 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-es6.txt @@ -0,0 +1,126 @@ +Evaluating: 'console.log(p)' +stdout> Promise {[[PromiseState]]: 'rejected', [[PromiseResult]]: -0} +stdout> > Promise {[[PromiseState]]: 'rejected', [[PromiseResult]]: -0} + +Evaluating: 'console.log([p])' +stdout> (1) [Promise] +stdout> > (1) [Promise] + +Evaluating: 'console.log(p2)' +stdout> Promise {[[PromiseState]]: 'fulfilled', [[PromiseResult]]: 1} +stdout> > Promise {[[PromiseState]]: 'fulfilled', [[PromiseResult]]: 1} + +Evaluating: 'console.log([p2])' +stdout> (1) [Promise] +stdout> > (1) [Promise] + +Evaluating: 'console.log(p3)' +stdout> Promise {[[PromiseState]]: 'pending', [[PromiseResult]]: undefined} +stdout> > Promise {[[PromiseState]]: 'pending', [[PromiseResult]]: undefined} + +Evaluating: 'console.log([p3])' +stdout> (1) [Promise] +stdout> > (1) [Promise] + +Evaluating: 'console.log(smb1)' +stdout> Symbol() + +Evaluating: 'console.log([smb1])' +stdout> (1) [Symbol()] +stdout> > (1) [Symbol()] + +Evaluating: 'console.log(smb2)' +stdout> Symbol(a) + +Evaluating: 'console.log([smb2])' +stdout> (1) [Symbol(a)] +stdout> > (1) [Symbol(a)] + +Evaluating: 'console.log(obj)' +stdout> {getter: , a: Symbol(), Symbol(a): 2} +stdout> > {getter: , a: Symbol(), Symbol(a): 2} + +Evaluating: 'console.log([obj])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(map)' +stdout> Map(1) {size: 1, {getter: , …} => {foo: 1}} +stdout> > Map(1) {size: 1, {getter: , …} => {foo: 1}} + +Evaluating: 'console.log([map])' +stdout> (1) [Map(1)] +stdout> > (1) [Map(1)] + +Evaluating: 'console.log(weakMap)' +stdout> WeakMap {{getter: , …} => {foo: 1}} +stdout> > WeakMap {{getter: , …} => {foo: 1}} + +Evaluating: 'console.log([weakMap])' +stdout> (1) [WeakMap] +stdout> > (1) [WeakMap] + +Evaluating: 'console.log(set)' +stdout> Set(1) {size: 1, {getter: , …}} +stdout> > Set(1) {size: 1, {getter: , …}} + +Evaluating: 'console.log([set])' +stdout> (1) [Set(1)] +stdout> > (1) [Set(1)] + +Evaluating: 'console.log(weakSet)' +stdout> WeakSet {{getter: , …}} +stdout> > WeakSet {{getter: , …}} + +Evaluating: 'console.log([weakSet])' +stdout> (1) [WeakSet] +stdout> > (1) [WeakSet] + +Evaluating: 'console.log(mapMap0)' +stdout> Map(1) {size: 1, Map(0) {…} => WeakMap} +stdout> > Map(1) {size: 1, Map(0) {…} => WeakMap} + +Evaluating: 'console.log([mapMap0])' +stdout> (1) [Map(1)] +stdout> > (1) [Map(1)] + +Evaluating: 'console.log(mapMap)' +stdout> Map(1) {size: 1, Map(1) {…} => WeakMap {…}} +stdout> > Map(1) {size: 1, Map(1) {…} => WeakMap {…}} + +Evaluating: 'console.log([mapMap])' +stdout> (1) [Map(1)] +stdout> > (1) [Map(1)] + +Evaluating: 'console.log(setSet0)' +stdout> Set(1) {size: 1, WeakSet} +stdout> > Set(1) {size: 1, WeakSet} + +Evaluating: 'console.log([setSet0])' +stdout> (1) [Set(1)] +stdout> > (1) [Set(1)] + +Evaluating: 'console.log(setSet)' +stdout> Set(1) {size: 1, WeakSet {…}} +stdout> > Set(1) {size: 1, WeakSet {…}} + +Evaluating: 'console.log([setSet])' +stdout> (1) [Set(1)] +stdout> > (1) [Set(1)] + +Evaluating: 'console.log(bigmap)' +stdout> Map(6) {size: 6, from str => to str , undefined => undefined, null => null, 42 => 42, {foo: 'from'} => {foo: 'to'}, …} +stdout> > Map(6) {size: 6, from str => to str , undefined => undefined, null => null, 42 => 42, {foo: 'from'} => {foo: 'to'}, …} + +Evaluating: 'console.log([bigmap])' +stdout> (1) [Map(6)] +stdout> > (1) [Map(6)] + +Evaluating: 'console.log(generator)' +stdout> genFunction {[[GeneratorState]]: 'suspended'} +stdout> > genFunction {[[GeneratorState]]: 'suspended'} + +Evaluating: 'console.log([generator])' +stdout> (1) [genFunction] +stdout> > (1) [genFunction] + diff --git a/code/extensions/js-debug/src/test/console/console-format-ext-handling.txt b/code/extensions/js-debug/src/test/console/console-format-ext-handling.txt new file mode 100644 index 000000000000..8f85a15dd91b --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-ext-handling.txt @@ -0,0 +1 @@ +"helloworldnew line\nasdfnow ext\nthis should be bulkedwith this!\nWaiting for the debugger to disconnect...\ntrailing\n" diff --git a/code/extensions/js-debug/src/test/console/console-format-groups.txt b/code/extensions/js-debug/src/test/console/console-format-groups.txt new file mode 100644 index 000000000000..cbb249d1d417 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-groups.txt @@ -0,0 +1,39 @@ +Evaluating: 'console.log('outer')' +stdout> outer + +Evaluating: 'console.group()' +# group: start +stdout> console.group + +Evaluating: 'console.log('in anonymous')' +stdout> in anonymous + +Evaluating: 'console.groupCollapsed('named')' +# group: startCollapsed +stdout> named + +Evaluating: 'console.log('in named')' +stdout> in named + +Evaluating: 'console.group({ complex: true })' +# group: start +stdout> {complex: true} +stdout> > {complex: true} +stdout> complex: true +stdout> > [[Prototype]]: Object + +Evaluating: 'console.log('in complex')' +stdout> in complex + +Evaluating: 'console.groupEnd()' +# group: end + +Evaluating: 'console.groupEnd()' +# group: end + +Evaluating: 'console.log('back in anonymous')' +stdout> back in anonymous + +Evaluating: 'console.groupEnd()' +# group: end + diff --git a/code/extensions/js-debug/src/test/console/console-format-nodes.txt b/code/extensions/js-debug/src/test/console/console-format-nodes.txt new file mode 100644 index 000000000000..8fb54b146624 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-nodes.txt @@ -0,0 +1,7 @@ +> result: 
...
 + 0: '\n Content\n ' + > 1: ...

 + 0: 'Paragaph' + 2: '\n More content\n ' + > 3:  + 4: '\n ' diff --git a/code/extensions/js-debug/src/test/console/console-format-popular-types.txt b/code/extensions/js-debug/src/test/console/console-format-popular-types.txt new file mode 100644 index 000000000000..bddc48be40fc --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-popular-types.txt @@ -0,0 +1,384 @@ +Evaluating: 'console.log(regex1)' +stdout> /^url\(\s*(?:(?:"(?:[^\\\"]|(?:\\[\da-f]{1,6}\s?|\.))*"|'(?:[^\\\']|(?:\\[\da-f]{1,6}\s?|\.))*')|(?:[!#$%&*-~\w]|(?:\\[\da-f]{1,6}\s?|\.))*)\s*\)/i + +Evaluating: 'console.log([regex1])' +stdout> (1) [/^url\(\s*(?:(?:"(?:[^\\\"]|(?:\\[\da-f]{1,6}\s?|\…?:[!#$%&*-~\w]|(?:\\[\da-f]{1,6}\s?|\.))*)\s*\)/i] +stdout> > (1) [/^url\(\s*(?:(?:"(?:[^\\\"]|(?:\\[\da-f]{1,6}\s?|\…?:[!#$%&*-~\w]|(?:\\[\da-f]{1,6}\s?|\.))*)\s*\)/i] + +Evaluating: 'console.log(regex2)' +stdout> /foo\\bar\sbaz/i + +Evaluating: 'console.log([regex2])' +stdout> (1) [/foo\\bar\sbaz/i] +stdout> > (1) [/foo\\bar\sbaz/i] + +Evaluating: 'console.log(str)' +stdout> test + +Evaluating: 'console.log([str])' +stdout> (1) ['test'] +stdout> > (1) ['test'] + +Evaluating: 'console.log(str2)' +stdout> test named "test" + +Evaluating: 'console.log([str2])' +stdout> (1) ['test named "test"'] +stdout> > (1) ['test named "test"'] + +Evaluating: 'console.log(error)' +stdout> Error + at console-format:7:23 {stack: 'Error + at console-format:7:23'} + +stdout> +> Error + at console-format:7:23 {stack: 'Error + at console-format:7:23'} + +Evaluating: 'console.log([error])' +stdout> (1) [Error + at console-format:7:23] +stdout> +> (1) [Error + at console-format:7:23] + +Evaluating: 'console.log(errorWithMessage)' +stdout> Error: my error message + at console-format:8:34 {stack: 'Error: my error message + at console-format:8:34', message: 'my error message'} + +stdout> +> Error: my error message + at console-format:8:34 {stack: 'Error: my error message + at console-format:8:34', message: 'my error message'} + +Evaluating: 'console.log([errorWithMessage])' +stdout> (1) [Error: my error message + at console-format:8:34] +stdout> +> (1) [Error: my error message + at console-format:8:34] + +Evaluating: 'console.log(errorWithMultilineMessage)' +stdout> Error: my multiline +error message + at console-format:9:43 {stack: 'Error: my multiline +error message + at console-format:9:43', message: 'my multiline +error message'} + +stdout> +> Error: my multiline +error message + at console-format:9:43 {stack: 'Error: my multiline +error message + at console-format:9:43', message: 'my multiline +error message'} + +Evaluating: 'console.log([errorWithMultilineMessage])' +stdout> (1) [Error: my multiline +error message + at console-format:9:43] +stdout> +> (1) [Error: my multiline +error message + at console-format:9:43] + +Evaluating: 'console.log(func)' +stdout> ƒ () { return 1; } +stdout> > ƒ () { return 1; } + +Evaluating: 'console.log([func])' +stdout> (1) [ƒ] +stdout> > (1) [ƒ] + +Evaluating: 'console.log(multilinefunc)' +stdout> ƒ () { + return 2; + } +stdout> +> ƒ () { + return 2; + } + +Evaluating: 'console.log([multilinefunc])' +stdout> (1) [ƒ] +stdout> > (1) [ƒ] + +Evaluating: 'console.log(num)' +stdout> 0.12 + +Evaluating: 'console.log([num])' +stdout> (1) [0.12] +stdout> > (1) [0.12] + +Evaluating: 'console.log(null)' +stdout> null + +Evaluating: 'console.log([null])' +stdout> (1) [null] +stdout> > (1) [null] + +Evaluating: 'console.log(undefined)' +stdout> undefined + +Evaluating: 'console.log([undefined])' +stdout> (1) [undefined] +stdout> > (1) [undefined] + +Evaluating: 'console.log(NaN)' +stdout> NaN + +Evaluating: 'console.log([NaN])' +stdout> (1) [NaN] +stdout> > (1) [NaN] + +Evaluating: 'console.log(Number.POSITIVE_INFINITY)' +stdout> Infinity + +Evaluating: 'console.log([Number.POSITIVE_INFINITY])' +stdout> (1) [Infinity] +stdout> > (1) [Infinity] + +Evaluating: 'console.log(Number.NEGATIVE_INFINITY)' +stdout> -Infinity + +Evaluating: 'console.log([Number.NEGATIVE_INFINITY])' +stdout> (1) [-Infinity] +stdout> > (1) [-Infinity] + +Evaluating: 'console.log({})' +stdout> {} +stdout> > {} + +Evaluating: 'console.log([{}])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log([function() {}])' +stdout> (1) [ƒ] +stdout> > (1) [ƒ] + +Evaluating: 'console.log([[function() {}]])' +stdout> (1) [Array(1)] +stdout> > (1) [Array(1)] + +Evaluating: 'console.log(objectWithNonEnumerables)' +stdout> {enumerableProp: 4, __underscoreEnumerableProp__: 5, __underscoreNonEnumerableProp: 2, abc: 3, getFoo: ƒ, …} +stdout> > {enumerableProp: 4, __underscoreEnumerableProp__: 5, __underscoreNonEnumerableProp: 2, abc: 3, getFoo: ƒ, …} + +Evaluating: 'console.log([objectWithNonEnumerables])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(negZero)' +stdout> -0 + +Evaluating: 'console.log([negZero])' +stdout> (1) [-0] +stdout> > (1) [-0] + +Evaluating: 'console.log(Object.create(null))' +stdout> {} +stdout> > {} + +Evaluating: 'console.log([Object.create(null)])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(Object)' +stdout> ƒ Object() +stdout> > ƒ Object() + +Evaluating: 'console.log([Object])' +stdout> (1) [ƒ] +stdout> > (1) [ƒ] + +Evaluating: 'console.log(Object.prototype)' +stdout> {__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, …} +stdout> > {__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, …} + +Evaluating: 'console.log([Object.prototype])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(new Number(42))' +stdout> Number (42) +stdout> > Number (42) + +Evaluating: 'console.log([new Number(42)])' +stdout> (1) [Number] +stdout> > (1) [Number] + +Evaluating: 'console.log(new String("abc"))' +stdout> String ('abc') +stdout> > String ('abc') + +Evaluating: 'console.log([new String("abc")])' +stdout> (1) [String] +stdout> > (1) [String] + +Evaluating: 'console.log(arrayLikeFunction)' +stdout> ƒ ( /**/ foo/**/, /*/**/bar, + /**/baz) {} +stdout> +> ƒ ( /**/ foo/**/, /*/**/bar, + /**/baz) {} + +Evaluating: 'console.log([arrayLikeFunction])' +stdout> (1) [ƒ] +stdout> > (1) [ƒ] + +Evaluating: 'console.log(new Uint16Array(["1", "2", "3"]))' +stdout> Uint16Array(3) [1, 2, 3, buffer: ArrayBuffer(6), byteLength: 6, byteOffset: 0, length: 3, Symbol(Symbol.toStringTag): 'Uint16Array'] +stdout> > Uint16Array(3) [1, 2, 3, buffer: ArrayBuffer(6), byteLength: 6, byteOffset: 0, length: 3, Symbol(Symbol.toStringTag): 'Uint16Array'] + +Evaluating: 'console.log([new Uint16Array(["1", "2", "3"])])' +stdout> (1) [Uint16Array(3)] +stdout> > (1) [Uint16Array(3)] + +Evaluating: 'console.log(tinyTypedArray)' +stdout> Uint8Array(1) [3, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array'] +stdout> > Uint8Array(1) [3, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array'] + +Evaluating: 'console.log([tinyTypedArray])' +stdout> (1) [Uint8Array(1)] +stdout> > (1) [Uint8Array(1)] + +Evaluating: 'console.log(smallTypedArray)' +stdout> Uint8Array(400) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …] +stdout> > Uint8Array(400) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …] + +Evaluating: 'console.log([smallTypedArray])' +stdout> (1) [Uint8Array(400)] +stdout> > (1) [Uint8Array(400)] + +Evaluating: 'console.log(bigTypedArray)' +stdout> Uint8Array(400000000) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …] +stdout> > Uint8Array(400000000) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …] + +Evaluating: 'console.log([bigTypedArray])' +stdout> (1) [Uint8Array(400000000)] +stdout> > (1) [Uint8Array(400000000)] + +Evaluating: 'console.log(throwingLengthGetter)' +stdout> {length: } +stdout> > {length: } + +Evaluating: 'console.log([throwingLengthGetter])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(domException())' +stdout> NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. {stack: '', code: 8, name: 'NotFoundError', message: "Failed to execute 'removeChild' on 'Node': T…e to be removed is not a child of this node."} + +stdout> +> NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. {stack: '', code: 8, name: 'NotFoundError', message: "Failed to execute 'removeChild' on 'Node': T…e to be removed is not a child of this node."} + +Evaluating: 'console.log([domException()])' +stdout> (1) [NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of …] +stdout> > (1) [NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of …] + +Evaluating: 'console.log(bigArray)' +stdout> (200) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …] +stdout> > (200) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …] + +Evaluating: 'console.log([bigArray])' +stdout> (1) [Array(200)] +stdout> > (1) [Array(200)] + +Evaluating: 'console.log(boxedNumberWithProps)' +stdout> Number (42) +stdout> > Number (42) + +Evaluating: 'console.log([boxedNumberWithProps])' +stdout> (1) [Number] +stdout> > (1) [Number] + +Evaluating: 'console.log(boxedStringWithProps)' +stdout> String ('abc') +stdout> > String ('abc') + +Evaluating: 'console.log([boxedStringWithProps])' +stdout> (1) [String] +stdout> > (1) [String] + +Evaluating: 'console.log(false)' +stdout> false + +Evaluating: 'console.log([false])' +stdout> (1) [false] +stdout> > (1) [false] + +Evaluating: 'console.log(true)' +stdout> true + +Evaluating: 'console.log([true])' +stdout> (1) [true] +stdout> > (1) [true] + +Evaluating: 'console.log(node)' +stdout> p#p +stdout> > 

 + +Evaluating: 'console.log([node])' +stdout> (1) [p#p] +stdout> > (1) [p#p] + +Evaluating: 'console.log(new Boolean(true))' +stdout> Boolean (true) +stdout> > Boolean (true) + +Evaluating: 'console.log([new Boolean(true)])' +stdout> (1) [Boolean] +stdout> > (1) [Boolean] + +Evaluating: 'console.log(new Set([1, 2, 3, 4]))' +stdout> Set(4) {size: 4, 1, 2, 3, 4} +stdout> > Set(4) {size: 4, 1, 2, 3, 4} + +Evaluating: 'console.log([new Set([1, 2, 3, 4])])' +stdout> (1) [Set(4)] +stdout> > (1) [Set(4)] + +Evaluating: 'console.log(new Set([1, 2, 3, 4, 5, 6, 7, 8]))' +stdout> Set(8) {size: 8, 1, 2, 3, 4, 5, …} +stdout> > Set(8) {size: 8, 1, 2, 3, 4, 5, …} + +Evaluating: 'console.log([new Set([1, 2, 3, 4, 5, 6, 7, 8])])' +stdout> (1) [Set(8)] +stdout> > (1) [Set(8)] + +Evaluating: 'console.log(new class { toString() { return "custom to string" } })' +stdout> {} +stdout> > custom to string + +Evaluating: 'console.log([new class { toString() { return "custom to string" } }])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(new class { toString() { return "long custom to string".repeat(500) } })' +stdout> {} +stdout> > long custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to stringlong custom to s… + +Evaluating: 'console.log([new class { toString() { return "long custom to string".repeat(500) } }])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(new class { [Symbol.for("debug.description")]() { return "some custom repr" } })' +stdout> {} +stdout> > some custom repr + +Evaluating: 'console.log([new class { [Symbol.for("debug.description")]() { return "some custom repr" } }])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + +Evaluating: 'console.log(new class { [Symbol.for("nodejs.util.inspect.custom")](depth) { return "some node repr, depth: " + depth } })' +stdout> {} +stdout> > some node repr, depth: 2 + +Evaluating: 'console.log([new class { [Symbol.for("nodejs.util.inspect.custom")](depth) { return "some node repr, depth: " + depth } }])' +stdout> (1) [{…}] +stdout> > (1) [{…}] + diff --git a/code/extensions/js-debug/src/test/console/console-format-string-format.txt b/code/extensions/js-debug/src/test/console/console-format-string-format.txt new file mode 100644 index 000000000000..07d76251e456 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-string-format.txt @@ -0,0 +1,31 @@ +stdout> single' quote\ +stdout> double" quote\ +stdout> bacl` quote\ +stdout> mixed' " ` quote\ +stdout> {a: "single' quote\\", b: 'double" quote\\', c: 'bacl` quote\\', d: 'mixed\' " ` quote\\'} +stdout> > {a: "single' quote\\", b: 'double" quote\\', c: 'bacl` quote\\', d: 'mixed\' " ` quote\\'} +stdout> a: "single' quote\\" +stdout> b: 'double" quote\\' +stdout> c: 'bacl` quote\\' +stdout> d: 'mixed\' " ` quote\\' +stdout> > [[Prototype]]: Object +> result: {a: "single' quote\\", b: 'double" quote\\', c: 'bacl` quote\\', d: 'mixed\' " ` quote\\'} + a: "single' quote\\" + b: 'double" quote\\' + c: 'bacl` quote\\' + d: 'mixed\' " ` quote\\' + > [[Prototype]]: Object +result: "single' quote\\" +result: 'double" quote\\' +result: 'bacl` quote\\' +result: 'mixed\' " ` quote\\' +> result: {a: "single' quote\\", b: 'double" quote\\', c: 'bacl` quote\\', d: 'mixed\' " ` quote\\'} + a: "single' quote\\" + b: 'double" quote\\' + c: 'bacl` quote\\' + d: 'mixed\' " ` quote\\' + > [[Prototype]]: Object +result: "single' quote\\" +result: 'double" quote\\' +result: 'bacl` quote\\' +result: 'mixed\' " ` quote\\' diff --git a/code/extensions/js-debug/src/test/console/console-format-string.txt b/code/extensions/js-debug/src/test/console/console-format-string.txt new file mode 100644 index 000000000000..a4d0d0b6bafb --- /dev/null +++ b/code/extensions/js-debug/src/test/console/console-format-string.txt @@ -0,0 +1,45 @@ +Evaluating: 'console.log(array)' +stdout> (10) ['test', 'test2', …, 'test4', …, foo: {…}] +stdout> > (10) ['test', 'test2', …, 'test4', …, foo: {…}] +stdout> 0: 'test' +stdout> 1: 'test2' +stdout> 4: 'test4' +stdout> > foo: {} +stdout> length: 10 +stdout> > [[Prototype]]: Array(0) +stdout> > [[Prototype]]: Object + +Evaluating: 'console.log("hello world".repeat(10000))' +stdout> hello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello…worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello worldhello world… + +Evaluating: 'console.log("%o", array)' +stdout> (10) ['test', 'test2', …, 'test4', …, foo: {…}] +stdout> > (10) ['test', 'test2', …, 'test4', …, foo: {…}] +stdout> > arg1: (10) ['test', 'test2', …, 'test4', …, foo: {…}] + +Evaluating: 'console.log("%O", array)' +stdout> (10) ['test', 'test2', …, 'test4', …, foo: {…}] +stdout> > (10) ['test', 'test2', …, 'test4', …, foo: {…}] +stdout> > arg1: (10) ['test', 'test2', …, 'test4', …, foo: {…}] + +Evaluating: 'console.log("Test for zero \"%f\" in formatter", 0)' +stdout> Test for zero "0" in formatter + +Evaluating: 'console.log("%% self-escape1", "dummy")' +stdout> % self-escape1 dummy + +Evaluating: 'console.log("%%s self-escape2", "dummy")' +stdout> %s self-escape2 dummy + +Evaluating: 'console.log("%%ss self-escape3", "dummy")' +stdout> %ss self-escape3 dummy + +Evaluating: 'console.log("%%s%s%%s self-escape4", "dummy")' +stdout> %sdummy%s self-escape4 + +Evaluating: 'console.log("%%%%% self-escape5", "dummy")' +stdout> %%% self-escape5 dummy + +Evaluating: 'console.log("%%%s self-escape6", "dummy");' +stdout> %dummy self-escape6 + diff --git a/code/extensions/js-debug/src/test/console/consoleAPITest.ts b/code/extensions/js-debug/src/test/console/consoleAPITest.ts new file mode 100644 index 000000000000..68890ec7efaf --- /dev/null +++ b/code/extensions/js-debug/src/test/console/consoleAPITest.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { itIntegrates } from '../testIntegrationUtils'; + +describe('console api', () => { + describe('format', () => { + itIntegrates('format string', async ({ r }) => { + const p = await r.launchAndLoad(`blank`); + await p.logger.evaluateAndLog([ + `console.log('Log')`, + `console.info('Info')`, + `console.warn('Warn')`, + `console.error('Error')`, + `console.assert(false, 'Assert')`, + `console.assert(false)`, + `console.trace('Trace')`, + `console.count('Counter')`, + `console.count('Counter')`, + ]); + p.assertLog(); + }); + }); + + itIntegrates('format string', async ({ r }) => { + const p = await r.launchAndLoad(``); + await p.logger.evaluateAndLog( + [ + `console.table(peopleObject)`, + `console.table(peopleObject2)`, + `console.table(peopleLongHeader)`, + `console.table(peopleArray)`, + `console.table(trimEmptyColumn)`, + `console.table(cellOverflow)`, + `console.table(longTableOverflow)`, + ], + { depth: 0 }, + ); + p.assertLog(); + }); +}); diff --git a/code/extensions/js-debug/src/test/console/consoleFormatTest.ts b/code/extensions/js-debug/src/test/console/consoleFormatTest.ts new file mode 100644 index 000000000000..f7a9fd5e3f19 --- /dev/null +++ b/code/extensions/js-debug/src/test/console/consoleFormatTest.ts @@ -0,0 +1,581 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { OutputSource } from '../../configuration'; +import { createFileTree } from '../createFileTree'; +import { testFixturesDir } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('console format', () => { + itIntegrates('string', async ({ r }) => { + const p = await r.launchAndLoad(` + `); + await p.logger.evaluateAndLog([ + `console.log(array)`, + `console.log("hello world".repeat(10000))`, + `console.log("%o", array)`, + `console.log("%O", array)`, + `console.log("Test for zero \\"%f\\" in formatter", 0)`, + `console.log("%% self-escape1", "dummy")`, + `console.log("%%s self-escape2", "dummy")`, + `console.log("%%ss self-escape3", "dummy")`, + `console.log("%%s%s%%s self-escape4", "dummy")`, + `console.log("%%%%% self-escape5", "dummy")`, + `console.log("%%%s self-escape6", "dummy");`, + ]); + p.assertLog(); + }); + + itIntegrates('string format', async ({ r }) => { + const handle = await r.launchUrl('stringFormats.html'); + handle.load(); + + const obj = await handle.dap.once('output'); + for (let i = 0; i < 4; i++) { + // xa - xd + await handle.logger.logOutput(await handle.dap.once('output')); + } + await handle.logger.logOutput(obj); + + for (const context of ['hover', 'repl'] as const) { + await handle.logger.evaluateAndLog('obj', { depth: 1 }, context); + await handle.logger.evaluateAndLog('xa', { depth: 1 }, context); + await handle.logger.evaluateAndLog('xb', { depth: 1 }, context); + await handle.logger.evaluateAndLog('xc', { depth: 1 }, context); + await handle.logger.evaluateAndLog('xd', { depth: 1 }, context); + } + + handle.assertLog(); + }); + + itIntegrates('popular types', async ({ r }) => { + const p = await r.launchAndLoad(` +

+ `); + const variables = [ + 'regex1', + 'regex2', + 'str', + 'str2', + 'error', + 'errorWithMessage', + 'errorWithMultilineMessage', + 'func', + 'multilinefunc', + 'num', + 'null', + 'undefined', + 'NaN', + 'Number.POSITIVE_INFINITY', + 'Number.NEGATIVE_INFINITY', + '{}', + '[function() {}]', + 'objectWithNonEnumerables', + 'negZero', + 'Object.create(null)', + 'Object', + 'Object.prototype', + 'new Number(42)', + 'new String("abc")', + 'arrayLikeFunction', + 'new Uint16Array(["1", "2", "3"])', + 'tinyTypedArray', + 'smallTypedArray', + 'bigTypedArray', + 'throwingLengthGetter', + 'domException()', + 'bigArray', + 'boxedNumberWithProps', + 'boxedStringWithProps', + 'false', + 'true', + 'node', + 'new Boolean(true)', + 'new Set([1, 2, 3, 4])', + 'new Set([1, 2, 3, 4, 5, 6, 7, 8])', + 'new class { toString() { return "custom to string" } }', + 'new class { toString() { return "long custom to string".repeat(500) } }', + 'new class { [Symbol.for("debug.description")]() { return "some custom repr" } }', + 'new class { [Symbol.for("nodejs.util.inspect.custom")](depth) { return "some node repr, depth: " + depth } }', + ]; + const expressions = variables.map(v => [`console.log(${v})`, `console.log([${v}])`]); + await p.logger.evaluateAndLog(([] as string[]).concat(...expressions), { depth: 0 }); + p.assertLog(); + }); + + itIntegrates('custom toString', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + new class A { + prop = new class B { + toString() { return "hello b" } + } + toString() { return "hello a" } + } + `); + p.assertLog(); + }); + + itIntegrates('custom symbol', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + new class A { + prop = new class B { + [Symbol.for("debug.description")]() { return "hello b" } + }; + [Symbol.for("debug.description")]() { return "hello a" } + } + `); + p.assertLog(); + }); + + itIntegrates('collections', async ({ r }) => { + const p = await r.launchAndLoad(` + + `); + + const variables = [ + 'nodelist', + 'htmlcollection', + 'options', + 'all', + 'formControls', + 'radioNodeList', + 'arrayX', + 'nonArray', + 'generateArguments(1, "2")', + 'div.classList', + ]; + const expressions = variables.map(v => [`console.log(${v})`, `console.log([${v}])`]); + await p.logger.evaluateAndLog(([] as string[]).concat(...expressions), { depth: 0 }); + p.assertLog(); + }); + + itIntegrates('es6', async ({ r }) => { + const p = await r.launchAndLoad(` + `); + + const variables = [ + 'p', + 'p2', + 'p3', + 'smb1', + 'smb2', + 'obj', + 'map', + 'weakMap', + 'set', + 'weakSet', + 'mapMap0', + 'mapMap', + 'setSet0', + 'setSet', + 'bigmap', + 'generator', + ]; + const expressions = variables.map(v => [`console.log(${v})`, `console.log([${v}])`]); + await p.logger.evaluateAndLog(([] as string[]).concat(...expressions), { depth: 0 }); + p.assertLog(); + }); + + itIntegrates('es6-2', async ({ r }) => { + const p = await r.launchAndLoad(` + `); + + const variables = [ + 'map2.keys()', + 'map2.values()', + 'map2.entries()', + 'set2.keys()', + 'set2.values()', + 'set2.entries()', + 'iter1', + 'iter2', + ]; + const expressions = variables.map(v => [`console.log(${v})`, `console.log([${v}])`]); + await p.logger.evaluateAndLog(([] as string[]).concat(...expressions), { depth: 0 }); + p.assertLog(); + }); + + itIntegrates('array', async ({ r }) => { + const p = await r.launchAndLoad(` + `); + + const expressions = new Array(11).fill(0).map((a, b) => `console.log(a${b})`); + await p.logger.evaluateAndLog(expressions, { depth: 0 }); + p.assertLog(); + }); + + itIntegrates('class', async ({ r }) => { + const p = await r.launchAndLoad(` + `); + + const expressions = new Array(11).fill(0).map((a, b) => `console.log(a${b})`); + await p.logger.evaluateAndLog(expressions, { depth: 0 }); + p.assertLog(); + }); + + itIntegrates('groups', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog([ + `console.log('outer')`, + `console.group()`, + `console.log('in anonymous')`, + `console.groupCollapsed('named')`, + `console.log('in named')`, + `console.group({ complex: true })`, + `console.log('in complex')`, + `console.groupEnd()`, + `console.groupEnd()`, + `console.log('back in anonymous')`, + `console.groupEnd()`, + ]); + p.assertLog(); + }); + + itIntegrates('colors', async ({ r }) => { + const p = await r.launchAndLoad(`blank`); + + await p.logger.evaluateAndLog( + [ + `console.log('%cColors are awesome.', 'color: blue;')`, + `console.log('%cColors are awesome.', 'background-color: red;')`, + `console.log('%cColors are awesome.', 'background-color: red;', 'Do not apply to trailing params')`, + `console.log('%cColors %care %cawesome.', 'color: red', 'color:green', 'color:blue')`, + `console.log('%cBold text.', 'font-weight: bold')`, + `console.log('%cItalic text.', 'font-style: italic')`, + `console.log('%cUnderline text.', 'text-decoration: underline')`, + ], + { depth: 0 }, + ); + p.assertLog(); + }); + + itIntegrates('nodes', async ({ r }) => { + const p = await r.launchAndLoad(` +
+ Content +

Paragaph

+ More content +
+
+ `); + + await p.logger.evaluateAndLog('document.getElementById("main")', { + depth: 3, + omitProperties: ['Node Attributes', '[[Prototype]]'], + }); + p.assertLog(); + }); + + itIntegrates('error traces in source maps', async ({ r }) => { + const handle = await r.launchUrlAndLoad('browserify/browserify.html'); + await handle.logger.evaluateAndLog(['try { throwError() } catch (e) { console.error(e) }']); + handle.assertLog(); + }); + + itIntegrates('adds error traces if they do not exist', async ({ r }) => { + const handle = await r.launchUrlAndLoad('browserify/browserify.html'); + const output = handle.dap.once('output'); + await handle.logger.evaluateAndLog(['setTimeout(() => { throw "asdf" }, 0) ']); + handle.log(await output); + handle.assertLog(); + }); + + itIntegrates('applies skipfiles to logged stacks', async ({ r }) => { + const handle = await r.launchAndLoad( + ` + + `, + { skipFiles: ['**/ignore-me.js'] }, + ); + + const evaluation = handle.dap.evaluate({ + expression: 'doLog("hello world");\n//# sourceURL=dont-ignore-me.js', + context: 'watch', + }); + const output = await handle.dap.once('output'); + await evaluation; + handle.log( + `logged ${output.output} at ${output.source?.name}:${output.line}:${output.column}`, + ); + handle.assertLog(); + }); + + itIntegrates('EXT handling', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': [ + ` + process.stdout.write('hello'); + debugger; + process.stdout.write('world'); + debugger; + process.stdout.write('new line\\r\\nasdf'); + debugger; + process.stdout.write('now ext\\u0003this should be bulked'); + debugger; + process.stdout.write('with this!\\u0003trailing'); + `, + ], + }); + const handle = await r.runScript('test.js', { outputCapture: OutputSource.Stdio }); + let output = ''; + r.rootDap().on('output', o => { + output += o.output; + }); + + // use debugger statements to sync chunks of output + handle.dap.on('stopped', ev => { + handle.dap.continue({ threadId: ev.threadId! }); + }); + + await handle.load(); + await r.rootDap().once('terminated'); + r.log(JSON.stringify(output)); + handle.assertLog(); + }); + + itIntegrates('ANSI colorization', async ({ r }) => { + const p = await r.launchAndLoad(` + `); + + p.dap.evaluate({ expression: 'console.log("\\x1b[31mThis is a red message\\x1b[0m")' }); + p.logger.logOutput(await p.dap.once('output')); + + for (const context of ['watch', 'variables'] as const) { + p.log(`In context ${context}:`); + await p.logger.logEvaluateResult( + await p.dap.evaluate({ + expression: `"\\x1b[31mThis is a red message\\x1b[0m"`, + context, + }), + ); + await p.logger.logEvaluateResult( + await p.dap.evaluate({ + expression: `({x:"\\x1b[31mThis is a red message\\x1b[0m"})`, + context, + }), + ); + } + + p.assertLog(); + }); +}); diff --git a/code/extensions/js-debug/src/test/createFileTree.ts b/code/extensions/js-debug/src/test/createFileTree.ts new file mode 100644 index 000000000000..38b178c92960 --- /dev/null +++ b/code/extensions/js-debug/src/test/createFileTree.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +import { randomBytes } from 'crypto'; +import * as fs from 'fs'; +import { EOL, tmpdir } from 'os'; +import * as path from 'path'; +import { join } from 'path'; +import { IFileTree } from './test'; + +export const getTestDir = () => join(tmpdir(), 'js-debug-test-' + randomBytes(6).toString('hex')); + +/** + * Creates a file tree at the given location. Primarily useful for creating + * fixtures in unit tests. + */ +export function createFileTree(rootDir: string, tree: IFileTree) { + fs.mkdirSync(rootDir, { recursive: true }); + + for (const key of Object.keys(tree)) { + const value = tree[key]; + const targetPath = path.join(rootDir, key); + + let write: Buffer; + if (typeof value === 'string') { + write = Buffer.from(value); + } else if (value instanceof Buffer) { + write = value; + } else if (value instanceof Array) { + write = Buffer.from(value.join(EOL)); + } else { + createFileTree(targetPath, value); + continue; + } + + fs.mkdirSync(path.dirname(targetPath), { recursive: true }); + fs.writeFileSync(targetPath, write); + } +} diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-cd.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-cd.txt new file mode 100644 index 000000000000..6fb9e6bb5b4a --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-cd.txt @@ -0,0 +1,39 @@ +"|": [ + [0] : { + label : cd top + length : 0 + start : 0 + } +] +"cd|": [ + [0] : { + label : cd top + length : 2 + start : 0 + } +] +"cd |": [ + [0] : { + label : cd top + length : 3 + start : 0 + } +] +"cd t|": [ + [0] : { + label : cd top + length : 4 + start : 0 + } +] +"cd h|": [ +] +"c|d": [ + [0] : { + label : cd top + length : 2 + start : 0 + } +] +"co|": [ +] diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-copy-via-evaluate-context.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-copy-via-evaluate-context.txt new file mode 100644 index 000000000000..39e88576dd61 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-copy-via-evaluate-context.txt @@ -0,0 +1,35 @@ +{ + result : 123 + type : string + variablesReference : +} +{ + result : null + type : string + variablesReference : +} +{ + result : { "foo": "bar", "baz": { "a": [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "b": 123 } } + type : string + variablesReference : +} +{ + result : function hello() { return "world" } + type : string + variablesReference : +} +{ + result : { "foo": true, "recurse": "[Circular ~]" } + type : string + variablesReference : +} +{ + result : "1267650600228229401496703205376" + type : string + variablesReference : +} +{ + result :
hi
+ type : string + variablesReference : +} diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-copy-via-function.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-copy-via-function.txt new file mode 100644 index 000000000000..a47daf17ae27 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-copy-via-function.txt @@ -0,0 +1,24 @@ +{ + text : hello +} +{ + text : 123n +} +{ + text : NaN +} +{ + text : { "foo": "bar", "baz": { "a": [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "b": 123 } } +} +{ + text : function hello() { return "world" } +} +{ + text : { "foo": true, "recurse": "[Circular ~]" } +} +{ + text : 1267650600228229401496703205376n +} +{ + text :
hi
+} diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-default.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-default.txt new file mode 100644 index 000000000000..1dd05b9dcb97 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-default.txt @@ -0,0 +1,135 @@ +result: 42 + +result: 'foo' + +result: 1234567890n + +: Uncaught Error: foo + +: Uncaught Object + +: Uncaught 42 + +result: 3 + +: Uncaught ReferenceError: baz is not defined + +result: '\x1b[2m' + +> result: Uint8Array(3) [1, 2, 3, buffer: ArrayBuffer(3), byteLength: 3, byteOffset: 0, length: 3, Symbol(Symbol.toStringTag): 'Uint8Array'] + 0: 1 + 1: 2 + 2: 3 + > buffer: (...) + > byteLength: (...) + > byteOffset: (...) + > length: (...) + Symbol(Symbol.toStringTag): undefined + > [[Prototype]]: TypedArray + > [[Prototype]]: Object + +> result: ArrayBuffer(3) {byteLength: 3, maxByteLength: 3, resizable: false, detached: false} + byteLength: 3 + detached: false + maxByteLength: 3 + resizable: false + [[ArrayBufferByteLength]]: 3 + > [[Int8Array]]: Int8Array(3) + > [[Prototype]]: ArrayBuffer + > [[Uint8Array]]: Uint8Array(3) + +> result: Proxy(Object) {a: 1} + > [[Handler]]: Object + [[IsRevoked]]: false + > [[Target]]: Object + +> result: {} + > foo: write-only + > arguments: (...) + > caller: (...) + length: 1 + name: 'set foo' + [[FunctionLocation]]: @ /VM:1 + > [[Prototype]]: ƒ () + > [[Scopes]]: Scopes[1] + > [[Prototype]]: Object + > foo: write-only + > [[Prototype]]: Object + +> result: {} + > foo: (...) + foo: 42 + > [[Prototype]]: Object + > foo: (...) + > [[Prototype]]: Object + +> result: {} + > foo: (...) + > arguments: (...) + > caller: (...) + length: 0 + name: 'get foo' + [[FunctionLocation]]: @ /VM:1 + > [[Prototype]]: ƒ () + > [[Scopes]]: Scopes[1] + > [[Prototype]]: Object + > foo: (...) + > [[Prototype]]: Object + +> result: {} + > Symbol(Symbol.toStringTag): (...) + Symbol(Symbol.toStringTag): 42 + > [[Prototype]]: Object + > Symbol(Symbol.toStringTag): (...) + > [[Prototype]]: Object + +Evaluating#1: setTimeout(() => { throw new Error('bar')}, 0) +stderr> Uncaught Error Error: bar + at (localhost꞉8001/eval1.js:1:26) + --- setTimeout --- + at (localhost꞉8001/eval1.js:1:1) +stderr> +> Uncaught Error Error: bar + at (localhost꞉8001/eval1.js:1:26) + --- setTimeout --- + at (localhost꞉8001/eval1.js:1:1) +stderr> + @ localhost꞉8001/eval1.js:1:26 +◀ setTimeout ▶ + @ localhost꞉8001/eval1.js:1 + +stderr> Uncaught Error Error: baz + at (/VM:1:26) + --- setTimeout --- + at (/VM:1:1) +stderr> +> Uncaught Error Error: baz + at (/VM:1:26) + --- setTimeout --- + at (/VM:1:1) +stderr> + @ /VM:1:26 +◀ setTimeout ▶ + @ /VM:1 + +: Uncaught Error: error1 + +: Uncaught Object + +stderr> Uncaught Error Error: error2 + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at (/VM:1:27) + --- setTimeout --- + at (/VM:1:1) +stderr> +> Uncaught Error Error: error2 + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at (/VM:1:27) + --- setTimeout --- + at (/VM:1:1) +stderr> +throwError @ ${workspaceFolder}/web/browserify/module1.ts:6:9 + @ /VM:1:27 +◀ setTimeout ▶ + @ /VM:1 + diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-inspect.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-inspect.txt new file mode 100644 index 000000000000..6f849930bdfa --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-inspect.txt @@ -0,0 +1,9 @@ +{ + column : 13 + line : 1 + source : { + name : test.js + path : test.js + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-output-slots-2.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-output-slots-2.txt new file mode 100644 index 000000000000..09eb71e9c904 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-output-slots-2.txt @@ -0,0 +1,10 @@ +result: +stdout> ↳ 1 +stdout> 2 +stderr> Uncaught Object +stderr> > Uncaught Object +stderr> > arg0: {foo: 3} +stderr> + @ /VM:5:9 +◀ setTimeout ▶ + @ /VM:3:7 diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-output-slots.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-output-slots.txt new file mode 100644 index 000000000000..46dc23d992a7 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-output-slots.txt @@ -0,0 +1,3 @@ +result: +stdout> 1 +stdout> ↳ 2 diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-queryobjects.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-queryobjects.txt new file mode 100644 index 000000000000..fc2d202e0488 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-queryobjects.txt @@ -0,0 +1,6 @@ +stdout> > (2) [Foo, Foo] +stdout> > 0: Foo {value: 1} +stdout> > 1: Foo {value: 2} +stdout> length: 2 +stdout> > [[Prototype]]: Array(0) +stdout> > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-repl.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-repl.txt new file mode 100644 index 000000000000..75934d6e9e55 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-repl.txt @@ -0,0 +1,91 @@ +result: 42 + +result: 'foo' + +result: 1234567890n + +: Uncaught Error Error: foo + at (repl:1:7) + + +: Uncaught Object Object + at (repl:1:1) + + +: Uncaught Error 42 + at (repl:1:1) + + +> result: {foo: 3} + foo: 3 + > [[Prototype]]: Object + +: Uncaught ReferenceError ReferenceError: baz is not defined + at (repl:1:1) + + +> result: Map(1) {size: 1, hello => ƒ ()} + > 0: {"hello" => function() { return 'world' }} + size: 1 + > [[Prototype]]: Map + +result: 42 +stderr> Uncaught Error Error: bar + at (repl:1:26) + --- setTimeout --- + at (repl:1:1) +stderr> +> Uncaught Error Error: bar + at (repl:1:26) + --- setTimeout --- + at (repl:1:1) +stderr> + @ repl:1:26 +◀ setTimeout ▶ + @ repl:1 + +result: 42 +stderr> Uncaught Error Error: baz + at (repl:1:26) + --- setTimeout --- + at (repl:1:1) +stderr> +> Uncaught Error Error: baz + at (repl:1:26) + --- setTimeout --- + at (repl:1:1) +stderr> + @ repl:1:26 +◀ setTimeout ▶ + @ repl:1 + +: Uncaught Error Error: error1 + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at (repl:1:8) + + +: Uncaught Object Object + at throwValue (${workspaceFolder}/web/browserify/module1.ts:9:3) + at (repl:1:8) + + +result: 42 +stderr> Uncaught Error Error: error2 + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at (repl:1:27) + --- setTimeout --- + at (repl:1:1) +stderr> +> Uncaught Error Error: error2 + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at (repl:1:27) + --- setTimeout --- + at (repl:1:1) +stderr> +throwError @ ${workspaceFolder}/web/browserify/module1.ts:6:9 + @ repl:1:27 +◀ setTimeout ▶ + @ repl:1 + +> result: Uint8Array(100000) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …] // type=Uint8Array named=1 indexed=100000 + diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-returnvalue.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-returnvalue.txt new file mode 100644 index 000000000000..2ae1b331a583 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-returnvalue.txt @@ -0,0 +1,11 @@ +return 42: + +result: 42 +return { a: { b: true } }: + +> result: {a: {…}} + > a: {b: true} + > [[Prototype]]: Object +return undefined: + +result: undefined diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-rewritetoplevelawait.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-rewritetoplevelawait.txt new file mode 100644 index 000000000000..9e5f9666d60f --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-rewritetoplevelawait.txt @@ -0,0 +1,105 @@ +------ +0 + +------ +await 0 +(async () => {return (await 0) +})() +------ +async function foo() { await 0; } + +------ +async () => await 0 + +------ +class A { async method() { await 0 } } + +------ +await 0; return 0; + +------ +var a = await 1 +(async () => {void( a = await 1) +})() +------ +let a = await 1 +(async () => {void( a = await 1) +})() +------ +const a = await 1 +(async () => {void( a = await 1) +})() +------ +for (var i = 0; i < 1; ++i) { await i } +(async () => {for (void( i = 0); i < 1; ++i) { await i } +})() +------ +for (let i = 0; i < 1; ++i) { await i } +(async () => {for (let i = 0; i < 1; ++i) { await i } +})() +------ +var {a} = {a:1}, [b] = [1], {c:{d}} = {c:{d: await 1}} +(async () => {void (( {a} = {a:1}),( [b] = [1]),( {c:{d}} = {c:{d: await 1}})) +})() +------ +console.log(`${(await {a:1}).a}`) +(async () => {return (console.log(`${(await {a:1}).a}`)) +})() +------ +await 0;function foo() {} +(async () => {await 0;foo=function foo() {} +})() +------ +await 0;class Foo {} +(async () => {await 0;Foo=class Foo {} +})() +------ +if (await true) { function foo() {} } +(async () => {if (await true) {foo= function foo() {} } +})() +------ +if (await true) { class Foo{} } +(async () => {if (await true) { class Foo{} } +})() +------ +if (await true) { var a = 1; } +(async () => {if (await true) { void( a = 1); } +})() +------ +if (await true) { let a = 1; } +(async () => {if (await true) { let a = 1; } +})() +------ +var a = await 1; let b = 2; const c = 3; +(async () => {void( a = await 1); void( b = 2); void( c = 3); +})() +------ +let o = await 1, p +(async () => {void (( o = await 1),( p=undefined)) +})() +------ +for await (const number of asyncRandomNumbers()) {} +(async () => {for await (const number of asyncRandomNumbers()) {} +})() +------ +[...(await fetch('url', { method: 'HEAD' })).headers.entries()] +(async () => {return ([...(await fetch('url', { method: 'HEAD' })).headers.entries()]) +})() +------ +await 1 +//hello +(async () => {return (await 1) +//hello +})() +------ +var {a = await new Promise(resolve => resolve({a:123}))} = {a : 3} +(async () => {void( {a = await new Promise(resolve => resolve({a:123}))} = {a : 3}) +})() +------ +await 1; for (var a of [1,2,3]); +(async () => {await 1; for (var a of [1,2,3]); +})() +------ +for (let j = 0; j < 5; ++j) { await j; } +(async () => {for (let j = 0; j < 5; ++j) { await j; } +})() diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-selected-context.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-selected-context.txt new file mode 100644 index 000000000000..278db68b37b8 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-selected-context.txt @@ -0,0 +1,9 @@ +--- Evaluating in page +Pausing... +Paused +result: false +Resumed +--- Evaluating in worker +Paused +result: true +Resumed diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-shadowed-variables.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-shadowed-variables.txt new file mode 100644 index 000000000000..e58d6030cd24 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-shadowed-variables.txt @@ -0,0 +1,15 @@ +result: 3 +line 1: +result: 3 +line 2: +result: 1 +line 3: +result: 1 +line 4: +result: 2 +line 5: +result: 2 +line 6: +result: 3 +line 7: +result: 3 diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-supports-bigint-map-keys-1277.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-supports-bigint-map-keys-1277.txt new file mode 100644 index 000000000000..acf5b82ba008 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-supports-bigint-map-keys-1277.txt @@ -0,0 +1,5 @@ +> result: Map(2) {size: 2, 1n => one, 2n => two} + > 0: {1n => "one"} + > 1: {2n => "two"} + size: 2 + > [[Prototype]]: Map diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-supports-location-lookup.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-supports-location-lookup.txt new file mode 100644 index 000000000000..ccd9efc00f7f --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-supports-location-lookup.txt @@ -0,0 +1,23 @@ + +> result: ƒ printArr(arr) { + for (const num of arr) { + console.log(plusTwo(num)); + } +} + > arguments: (...) + > caller: (...) + length: 1 + name: 'printArr' + > prototype: {} + [[FunctionLocation]]: @ ${workspaceFolder}/web/basic.ts:5 + > [[Prototype]]: ƒ () + > [[Scopes]]: Scopes[1] +{ + column : 18 + line : 5 + source : { + name : basic.ts + path : ${workspaceFolder}/web/basic.ts + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-toplevelawait.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-toplevelawait.txt new file mode 100644 index 000000000000..c79908c2cbfd --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-toplevelawait.txt @@ -0,0 +1,85 @@ +Evaluating: 'await Promise.resolve(1)' +result: 1 +Evaluating: '{a:await Promise.resolve(1)}' +> result: {a: 1} +Evaluating: '4' +result: 4 +Evaluating: '$_' +result: 4 +Evaluating: 'let {a,b} = await Promise.resolve({a: 1, b:2}), f = 5;' +result: undefined +Evaluating: 'a' +result: 1 +Evaluating: 'b' +result: 2 +Evaluating: 'let c = await Promise.resolve(2)' +result: undefined +Evaluating: 'c' +result: 2 +Evaluating: 'let d;' +result: undefined +Evaluating: 'd' +result: undefined +Evaluating: 'let [i,{abc:{k}}] = [0,{abc:{k:1}}];' +result: undefined +Evaluating: 'i' +result: 0 +Evaluating: 'k' +result: 1 +Evaluating: 'var l = await Promise.resolve(2);' +result: undefined +Evaluating: 'l' +result: 2 +Evaluating: 'foo(await koo());' +result: 4 +Evaluating: '$_' +result: 4 +Evaluating: 'const m = foo(await koo());' +result: undefined +Evaluating: 'm' +result: 4 +Evaluating: 'const n = foo(await +koo());' +result: undefined +Evaluating: 'n' +result: 4 +Evaluating: '`status: ${(await Promise.resolve({status:200})).status}`' +result: 'status: 200' +Evaluating: 'for (let i = 0; i < 2; ++i) await i' +result: undefined +Evaluating: 'for (let i = 0; i < 2; ++i) { await i }' +result: undefined +Evaluating: 'await 0' +result: 0 +Evaluating: 'await 0;function foo(){}' +> result: ƒ foo() {} +Evaluating: 'foo' +> result: ƒ foo() {} +Evaluating: 'class Foo{}; await 1;' +result: 1 +Evaluating: 'Foo' +> result: class Foo {} +Evaluating: 'await 0;function* gen(){}' +> result: ƒ* gen() {} +Evaluating: 'for (var i = 0; i < 10; ++i) { await i; }' +result: undefined +Evaluating: 'i' +result: 0 +Evaluating: 'for (let j = 0; j < 5; ++j) { await j; }' +result: undefined +Evaluating: 'j' +result: +Evaluating: 'gen' +> result: ƒ* gen() {} +Evaluating: 'await 5; return 42;' +result: +Evaluating: 'let o = await 1, p' +result: undefined +Evaluating: 'p' +result: undefined +Evaluating: 'let q = 1, s = await 2' +result: undefined +Evaluating: 's' +result: 2 +Evaluating: 'await {...{foo: 42}}' +> result: {foo: 42} diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate-valueformatter.txt b/code/extensions/js-debug/src/test/evaluate/evaluate-valueformatter.txt new file mode 100644 index 000000000000..a4b8466ec629 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate-valueformatter.txt @@ -0,0 +1,12 @@ +result: 2a + +result: 15a4f9d339f5e8dd2f8dd19bc149ff5e4f15a1b5 + +result: 68656c6c6f20776f726c64 + +> result: {a: 'hello', b: 42, c: true} + a: 68656c6c6f + b: 2a + c: true + > [[Prototype]]: Object + diff --git a/code/extensions/js-debug/src/test/evaluate/evaluate.ts b/code/extensions/js-debug/src/test/evaluate/evaluate.ts new file mode 100644 index 000000000000..ab743cbb1124 --- /dev/null +++ b/code/extensions/js-debug/src/test/evaluate/evaluate.ts @@ -0,0 +1,573 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { delay } from '../../common/promiseUtil'; +import Dap from '../../dap/api'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('evaluate', () => { + itIntegrates('default', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + + await p.logger.evaluateAndLog(`42`); + p.log(''); + + await p.logger.evaluateAndLog(`'foo'`); + p.log(''); + + await p.logger.evaluateAndLog(`1234567890n`); + p.log(''); + + await p.logger.evaluateAndLog(`throw new Error('foo')`); + p.log(''); + + await p.logger.evaluateAndLog(`throw {foo: 3, bar: 'baz'};`); + p.log(''); + + await p.logger.evaluateAndLog(`throw 42;`); + p.log(''); + + await p.logger.evaluateAndLog(`{foo: 3}`); + p.log(''); + + await p.logger.evaluateAndLog(`baz();`); + p.log(''); + + await p.logger.evaluateAndLog(`'\\x1b[2m'`); + p.log(''); + + await p.logger.evaluateAndLog(`new Uint8Array([1, 2, 3]);`); + p.log(''); + + await p.logger.evaluateAndLog(`new Uint8Array([1, 2, 3]).buffer;`); + p.log(''); + + await p.logger.evaluateAndLog(`new Proxy({ a: 1 }, { get: () => 2 });`); + p.log(''); + + // prototype-free objs just to avoid adding prototype noise to tests: + await p.logger.evaluateAndLog(`Object.create({ set foo(x) {} })`, { depth: 2 }); + p.log(''); + + await p.logger.evaluateAndLog(`Object.create({ get foo() { return 42 } })`, { depth: 2 }); + p.log(''); + + await p.logger.evaluateAndLog(`Object.create({ get foo() { throw 'wat'; } })`, { depth: 2 }); + p.log(''); + + await p.logger.evaluateAndLog(`Object.create({ get [Symbol.toStringTag]() { return 42 } })`, { + depth: 2, + }); + p.log(''); + + p.evaluate(`setTimeout(() => { throw new Error('bar')}, 0)`); + await p.logger.logOutput(await p.dap.once('output')); + p.log(''); + + p.dap.evaluate({ expression: `setTimeout(() => { throw new Error('baz')}, 0)` }); + await p.logger.logOutput(await p.dap.once('output')); + p.log(''); + + await p.addScriptTag('browserify/bundle.js'); + + await p.logger.evaluateAndLog(`window.throwError('error1')`); + p.log(''); + + await p.logger.evaluateAndLog(`window.throwValue({foo: 3, bar: 'baz'})`); + p.log(''); + + p.dap.evaluate({ expression: `setTimeout(() => { window.throwError('error2')}, 0)` }); + await p.logger.logOutput(await p.dap.once('output')); + p.log(''); + + p.assertLog(); + }); + + itIntegrates('supports location lookup', async ({ r }) => { + const p = await r.launchUrlAndLoad('basic.html'); + const fn = await p.logger.evaluateAndLog('printArr'); + expect(fn.valueLocationReference).to.be.greaterThan(0); + const location = await p.dap.locations({ locationReference: fn.valueLocationReference! }); + p.log(location); + p.assertLog(); + }); + + itIntegrates('repl', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + + await p.logger.evaluateAndLog('42', undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`'foo'`, undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`1234567890n`, undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`throw new Error('foo')`, undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`throw {foo: 3, bar: 'baz'};`, undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`throw 42;`, undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`{foo: 3}`, undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`baz();`, undefined, 'repl'); + p.log(''); + + // #490 + await p.logger.evaluateAndLog( + `new Map([['hello', function() { return 'world' }]])`, + undefined, + 'repl', + ); + p.log(''); + + const [, e1] = await Promise.all([ + p.logger.evaluateAndLog( + `setTimeout(() => { throw new Error('bar')}, 0); 42`, + undefined, + 'repl', + ), + p.dap.once('output'), + ]); + await p.logger.logOutput(e1); + p.log(''); + + const [, e2] = await Promise.all([ + p.logger.evaluateAndLog( + `setTimeout(() => { throw new Error('baz')}, 0); 42`, + undefined, + 'repl', + ), + p.dap.once('output'), + ]); + await p.logger.logOutput(e2); + p.log(''); + + await p.addScriptTag('browserify/bundle.js'); + + await p.logger.evaluateAndLog(`window.throwError('error1')`, undefined, 'repl'); + p.log(''); + + await p.logger.evaluateAndLog(`window.throwValue({foo: 3, bar: 'baz'})`, undefined, 'repl'); + p.log(''); + + const [, e3] = await Promise.all([ + p.logger.evaluateAndLog( + `setTimeout(() => { window.throwError('error2')}, 0); 42`, + undefined, + 'repl', + ), + p.dap.once('output'), + ]); + await p.logger.logOutput(e3); + p.log(''); + + // vscode#152643 + await p.logger.evaluateAndLog( + `new Uint8Array(100_000).fill(0)`, + { logInternalInfo: true, depth: 0 }, + 'repl', + ); + p.log(''); + + p.assertLog(); + }); + + itIntegrates('valueFormatter', async ({ r }) => { + const format: Dap.ValueFormat = { hex: true }; + const p = await r.launchUrlAndLoad('index.html'); + + await p.logger.evaluateAndLog(`42`, { format }); + p.log(''); + + await p.logger.evaluateAndLog(`123567891235678912356789123567891235678912356789n`, { + format, + }); + p.log(''); + + await p.logger.evaluateAndLog(`'hello world'`, { format }); + p.log(''); + + await p.logger.evaluateAndLog(`({ a: 'hello', b: 42, c: true })`, { format }); + p.log(''); + + p.assertLog(); + }); + + const copyExpressions: { [expr: string]: string } = { + '123n': '123n', + NaN: 'NaN', + '{foo: "bar", baz: { a: [1, 2, 3, 4, 5, 6, 7, 8, 9], b: 123n, "complex key": true }}': `{ + foo: "bar", + baz: { + a: [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ], + b: 123n, + "complex key": true, + }, +}`, + [ + `{ + double(x) { + return x * 2; + }, + triple: x => { + return x * 3 + } +}` + ]: `{ + double: function(x) { + return x * 2; + }, + triple: x => { + return x * 3 + }, +}`, + 'function hello() { return "world" }': 'function hello() { return "world" }', + '(() => { const n = { foo: true }; n.recurse = n; return n })()': `{ + foo: true, + recurse: [Circular], +}`, + 'new Uint8Array([1, 2, 3])': 'new Uint8Array([1, 2, 3])', + 'new Uint8Array([1, 2, 3]).buffer': 'new Uint8Array([1, 2, 3]).buffer', + 'new Float32Array([1.5, 2.5, 3.5])': 'new Float32Array([1.5, 2.5, 3.5])', + '1n << 100n': '1267650600228229401496703205376n', + 'new Date(1665007127286)': '"2022-10-05T21:58:47.286Z"', + '(() => { const node = document.createElement("div"); node.innerText = "hi"; return node })()': + `
hi
`, + '"hello\\nmu`lti${line}"': '`hello\nmu\\`lti\\${line}`', + }; + + const fnCopyExpressions = { + '"hello"': 'hello', + '"hello\\nmu`lti${line}"': 'hello\nmu`lti${line}', + }; + + itIntegrates('copy via function', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + const mapping = { ...copyExpressions, ...fnCopyExpressions }; + for (const [expression, expected] of Object.entries(mapping)) { + p.dap.evaluate({ expression: `copy(${expression})` }); + const actual = await p.dap.once('copyRequested'); + expect(actual.text).to.equal(expected, expression); + } + }); + + itIntegrates('copy via evaluate context', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + for (const [expression, expected] of Object.entries(copyExpressions)) { + const actual = await p.dap.evaluate({ expression, context: 'clipboard' }); + expect(actual.result).to.equal(expected, expression); + } + }); + + itIntegrates('inspect', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.dap.evaluate({ expression: 'function foo() {}; inspect(foo)\n//# sourceURL=test.js' }); + p.log(await p.dap.once('revealLocationRequested')); + p.assertLog(); + }); + + itIntegrates('queryObjects', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.dap.evaluate({ + expression: ` + class Foo { + constructor(value) { + this.value = value; + } + } + var foo1 = new Foo(1); + var foo2 = new Foo(2); + `, + }); + p.dap.evaluate({ expression: 'queryObjects(Foo)' }); + await p.logger.logOutput(await p.dap.once('output')); + p.assertLog(); + }); + + itIntegrates('topLevelAwait', async ({ r }) => { + const p = await r.launchAndLoad(` + + `); + + const exprs = [ + 'await Promise.resolve(1)', + '{a:await Promise.resolve(1)}', + '4', + '$_', + 'let {a,b} = await Promise.resolve({a: 1, b:2}), f = 5;', + 'a', + 'b', + 'let c = await Promise.resolve(2)', + 'c', + 'let d;', + 'd', + 'let [i,{abc:{k}}] = [0,{abc:{k:1}}];', + 'i', + 'k', + 'var l = await Promise.resolve(2);', + 'l', + 'foo(await koo());', + '$_', + 'const m = foo(await koo());', + 'm', + 'const n = foo(await\nkoo());', + 'n', + '`status: ${(await Promise.resolve({status:200})).status}`', + 'for (let i = 0; i < 2; ++i) await i', + 'for (let i = 0; i < 2; ++i) { await i }', + 'await 0', + 'await 0;function foo(){}', + 'foo', + 'class Foo{}; await 1;', + 'Foo', + 'await 0;function* gen(){}', + 'for (var i = 0; i < 10; ++i) { await i; }', + 'i', + 'for (let j = 0; j < 5; ++j) { await j; }', + 'j', + 'gen', + 'await 5; return 42;', + 'let o = await 1, p', + 'p', + 'let q = 1, s = await 2', + 's', + 'await {...{foo: 42}}', + ]; + + for (const expression of exprs) { + p.log(`Evaluating: '${expression}'`); + p.logger.logEvaluateResult(await p.dap.evaluate({ expression, context: 'repl' }), { + depth: 0, + }); + } + p.assertLog(); + }); + + itIntegrates('escapes strings', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + for (const context of ['watch', 'hover', 'repl'] as const) { + p.log(`context=${context}`); + await p.logger.evaluateAndLog(JSON.stringify('1\n2\r3\t\\4'), { depth: 0 }, context); + } + + p.assertLog({ + customAssert: str => + expect(str).to.equal( + [ + 'context=watch', + "result: '1\\n2\\r3\\t\\\\4'", + 'context=hover', + "result: '1\\n2\\r3\\t\\\\4'", + 'context=repl', + "\nresult: '1\n2\r3\t\\\\4'", + '', + ].join('\n'), + ), + }); + }); + + itIntegrates.skip('output slots', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + const empty = await p.dap.evaluate({ + expression: 'let i = 0; console.log(++i); ++i', + context: 'repl', + }); + const console = await p.dap.once('output'); + const result = await p.dap.once('output'); + await p.logger.logEvaluateResult(empty); + await p.logger.logOutput(console); + await p.logger.logOutput(result); + p.assertLog(); + }); + + itIntegrates.skip('output slots 2', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + const empty = await p.dap.evaluate({ + expression: ` + let i = 0; + setTimeout(() => { + console.log(++i); + throw {foo: ++i}; + }, 0); + ++i + `, + context: 'repl', + }); + const result = await p.dap.once('output'); + const console = await p.dap.once('output'); + const exception = await p.dap.once('output'); + await p.logger.logEvaluateResult(empty); + await p.logger.logOutput(result); + await p.logger.logOutput(console); + await p.logger.logOutput(exception); + p.assertLog(); + }); + + itIntegrates('selected context', async ({ r }) => { + const p = await r.launchUrlAndLoad('worker.html'); + p.log('--- Evaluating in page'); + p.log('Pausing...'); + p.dap.evaluate({ expression: `window.w.postMessage('pause');`, context: 'repl' }); + const { threadId: pageThreadId } = await p.dap.once('stopped'); + p.log('Paused'); + const { id: pageFrameId } = ( + await p.dap.stackTrace({ + threadId: pageThreadId!, + }) + ).stackFrames[0]; + await p.logger.logEvaluateResult( + await p.dap.evaluate({ expression: 'isWorker', frameId: pageFrameId }), + { depth: 0 }, + ); + p.dap.continue({ threadId: pageThreadId! }); + await p.dap.once('continued'); + p.log('Resumed'); + + p.log('--- Evaluating in worker'); + p.dap.evaluate({ expression: `window.w.postMessage('pauseWorker');`, context: 'repl' }); + const worker = await r.worker(); + const { threadId: workerThreadId } = await worker.dap.once('stopped'); + p.log('Paused'); + const { id: workerFrameId } = ( + await worker.dap.stackTrace({ + threadId: workerThreadId!, + }) + ).stackFrames[0]; + await worker.logger.logEvaluateResult( + await worker.dap.evaluate({ expression: 'isWorker', frameId: workerFrameId }), + { depth: 0 }, + ); + worker.dap.continue({ threadId: workerThreadId! }); + await worker.dap.once('continued'); + p.log('Resumed'); + + p.assertLog(); + }); + + itIntegrates('cd', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + + async function logCompletions(params: Dap.CompletionsParams) { + const completions = await p.dap.completions(params); + const text = params.text.substring(0, params.column - 1) + + '|' + + params.text.substring(params.column - 1); + p.log( + completions.targets.filter(c => c.label.startsWith('cd')), + `"${text}": `, + ); + } + + await delay(50); // todo(connor4312): there's some race here on the first resolution + await logCompletions({ line: 1, column: 1, text: '' }); + await logCompletions({ line: 1, column: 3, text: 'cd' }); + await logCompletions({ line: 1, column: 4, text: 'cd ' }); + await logCompletions({ line: 1, column: 5, text: 'cd t' }); + + await logCompletions({ line: 1, column: 5, text: 'cd h' }); + await logCompletions({ line: 1, column: 2, text: 'cd' }); + await logCompletions({ line: 1, column: 3, text: 'co' }); + p.assertLog(); + }); + + itIntegrates('returnValue', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + + const evaluateAtReturn = async (returnValue: string, expression = '$returnValue') => { + p.dap.evaluate({ + expression: `(function () { + debugger; + return ${returnValue}; + })();`, + }); + + const { threadId } = await p.dap.once('stopped'); + await p.dap.next({ threadId: threadId! }); // step past debugger; + await p.dap.once('stopped'); + await p.dap.next({ threadId: threadId! }); // step past return; + await p.dap.once('stopped'); + + const frameId = ( + await p.dap.stackTrace({ + threadId: threadId!, + }) + ).stackFrames[0].id; + + p.log(`return ${returnValue}:\n`); + await p.logger.evaluateAndLog(expression, { params: { frameId } }); + await p.dap.continue({ threadId: threadId! }); + }; + + await evaluateAtReturn('42'); + await evaluateAtReturn('{ a: { b: true } }'); + await evaluateAtReturn('undefined'); + r.assertLog(); + }); + + itIntegrates('shadowed variables', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + + p.dap.evaluate({ + expression: `(function () { + let foo = 1; + if (true) { + let foo = 2; + if (true) { + let foo = 3; + debugger; + console.log(foo); + } + } + })();`, + }); + const sourcePromise = p.dap.once('loadedSource'); + const { threadId } = await p.dap.once('stopped'); + + const frameId = ( + await p.dap.stackTrace({ + threadId: threadId!, + }) + ).stackFrames[0].id; + const { source } = await sourcePromise; + await p.logger.evaluateAndLog('foo', { params: { frameId } }); + for (let line = 1; line <= 7; line++) { + p.log(`line ${line}:`); + await p.logger.evaluateAndLog('foo', { params: { frameId, line, column: 1, source } }); + } + + r.assertLog(); + }); + + itIntegrates('supports bigint map keys (#1277)', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await p.logger.evaluateAndLog(`new Map([[1n, 'one'], [2n, 'two']])`); + r.assertLog(); + }); +}); diff --git a/code/extensions/js-debug/src/test/extension/editorBrowserConfigurationProvider.test.ts b/code/extensions/js-debug/src/test/extension/editorBrowserConfigurationProvider.test.ts new file mode 100644 index 000000000000..25af8b5ad998 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/editorBrowserConfigurationProvider.test.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { upcastPartial } from '../../common/objUtils'; +import { + editorBrowserAttachConfigDefaults, + editorBrowserLaunchConfigDefaults, +} from '../../configuration'; +import { EditorBrowserDebugConfigurationResolver } from '../../ui/configuration/editorBrowserDebugConfigurationProvider'; +import { testFixturesDir } from '../test'; +import { TestMemento } from '../testMemento'; + +describe('EditorBrowserDebugConfigurationProvider', () => { + let provider: EditorBrowserDebugConfigurationResolver; + const folder: vscode.WorkspaceFolder = { + uri: vscode.Uri.file(testFixturesDir), + name: 'test-dir', + index: 0, + }; + + beforeEach(() => { + provider = new EditorBrowserDebugConfigurationResolver( + upcastPartial({ + logPath: testFixturesDir, + workspaceState: new TestMemento(), + }), + ); + }); + + describe('launch config', () => { + it('returns null for empty config', async () => { + const result = await provider.resolveDebugConfiguration(folder, { + type: '', + name: '', + request: '', + }); + expect(result).to.be.null; + }); + + it('applies launch defaults', async () => { + const result = await provider.resolveDebugConfiguration(folder, { + type: DebugType.EditorBrowser, + name: 'test', + request: 'launch', + url: 'http://localhost:3000', + }); + + expect(result).to.containSubset({ + type: DebugType.EditorBrowser, + request: 'launch', + url: 'http://localhost:3000', + webRoot: editorBrowserLaunchConfigDefaults.webRoot, + disableNetworkCache: editorBrowserLaunchConfigDefaults.disableNetworkCache, + }); + }); + + it('user config overrides defaults', async () => { + const result = await provider.resolveDebugConfiguration(folder, { + type: DebugType.EditorBrowser, + name: 'test', + request: 'launch', + url: 'http://localhost:9000', + webRoot: '/custom/path', + }); + + expect(result).to.containSubset({ + url: 'http://localhost:9000', + webRoot: '/custom/path', + }); + }); + }); + + describe('attach config', () => { + it('applies attach defaults', async () => { + const result = await provider.resolveDebugConfiguration(folder, { + type: DebugType.EditorBrowser, + name: 'test', + request: 'attach', + }); + + expect(result).to.containSubset({ + type: DebugType.EditorBrowser, + request: 'attach', + webRoot: editorBrowserAttachConfigDefaults.webRoot, + disableNetworkCache: editorBrowserAttachConfigDefaults.disableNetworkCache, + }); + }); + + it('user config overrides attach defaults', async () => { + const result = await provider.resolveDebugConfiguration(folder, { + type: DebugType.EditorBrowser, + name: 'test', + request: 'attach', + webRoot: '/my/root', + }); + + expect(result).to.containSubset({ + type: DebugType.EditorBrowser, + request: 'attach', + webRoot: '/my/root', + }); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/extension/editorBrowserIntegration.test.ts b/code/extensions/js-debug/src/test/extension/editorBrowserIntegration.test.ts new file mode 100644 index 000000000000..164fea3911ab --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/editorBrowserIntegration.test.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { createServer, Server } from 'http'; +import type { AddressInfo } from 'net'; +import { SinonSpy, stub } from 'sinon'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { EventEmitter } from '../../common/events'; + +describe('integrated browser debugging', function() { + this.timeout(30_000); + + let server: Server; + let serverUrl: string; + + before(async function() { + // Skip entire suite when the proposed browser API is not available + if (typeof vscode.window.openBrowserTab !== 'function') { + return this.skip(); + } + + await new Promise(resolve => { + server = createServer((_req, res) => { + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(''); + }); + server.listen(0, '127.0.0.1', resolve); + }); + serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; + }); + + afterEach(async () => { + try { + await vscode.debug.stopDebugging(); + } catch { + // no active session to stop + } + }); + + after(async () => { + if (server) { + await new Promise(resolve => server.close(() => resolve())); + } + }); + + /** Waits for a child debug session to start (one with __pendingTargetId). */ + const waitForChildSession = () => + new Promise(resolve => { + const d = vscode.debug.onDidStartDebugSession(s => { + if ('__pendingTargetId' in s.configuration) { + d.dispose(); + resolve(s); + } + }); + }); + + it('launch opens a browser tab visible via the API', async () => { + const tabsBefore = [...vscode.window.browserTabs]; + const sessionStarted = waitForChildSession(); + + await vscode.debug.startDebugging(undefined, { + type: DebugType.EditorBrowser, + request: 'launch', + name: 'Launch Test', + url: serverUrl, + }); + + const session = await sessionStarted; + expect(session).to.exist; + + // The launcher calls openBrowserTab, so a new tab should appear + const tabsAfter = vscode.window.browserTabs; + const newTabs = tabsAfter.filter(t => !tabsBefore.includes(t)); + expect(newTabs).to.have.lengthOf(1, 'expected exactly one new browser tab'); + expect(newTabs[0].url).to.include(serverUrl); + + await vscode.debug.stopDebugging(session); + }); + + it('attach debugs the pre-opened tab without opening another', async () => { + // Open a tab before starting the debug session + const tab = await vscode.window.openBrowserTab(serverUrl, { background: true }); + const tabCountBefore = vscode.window.browserTabs.length; + + // Stub createQuickPick to auto-select the tab matching our URL + let acceptEmitter: EventEmitter; + const originalCreateQuickPick = vscode.window.createQuickPick; + const createQuickPickStub: SinonSpy = stub( + vscode.window, + 'createQuickPick', + ).callsFake(() => { + const picker = originalCreateQuickPick.call(vscode.window); + acceptEmitter = new EventEmitter(); + stub(picker, 'onDidAccept').callsFake(acceptEmitter.event); + + // Once shown, poll for items matching our tab and auto-accept + const origShow = picker.show.bind(picker); + stub(picker, 'show').callsFake(() => { + origShow(); + const interval = setInterval(() => { + const match = picker.items.find( + i => 'detail' in i && typeof i.detail === 'string' && i.detail.includes(serverUrl), + ); + if (match) { + clearInterval(interval); + picker.selectedItems = [match]; + acceptEmitter.fire(); + } + }, 50); + }); + + return picker; + }); + + try { + const sessionStarted = waitForChildSession(); + + await vscode.debug.startDebugging(undefined, { + type: DebugType.EditorBrowser, + request: 'attach', + name: 'Attach Test', + }); + + const session = await sessionStarted; + expect(session).to.exist; + + // Verify no additional browser tabs were opened + expect(vscode.window.browserTabs).to.have.lengthOf( + tabCountBefore, + 'attach should not open a new browser tab', + ); + + // Verify the tab we opened is the one being debugged by checking + // that the debugged URL matches our pre-opened tab + expect(tab.url).to.include(serverUrl); + + await vscode.debug.stopDebugging(session); + } finally { + createQuickPickStub.restore(); + } + }); +}); diff --git a/code/extensions/js-debug/src/test/extension/extensionHostConfigurationProvider.test.ts b/code/extensions/js-debug/src/test/extension/extensionHostConfigurationProvider.test.ts new file mode 100644 index 000000000000..3f8e09b12c90 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/extensionHostConfigurationProvider.test.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { join } from 'path'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { EnvironmentVars } from '../../common/environmentVars'; +import { upcastPartial } from '../../common/objUtils'; +import { ExtensionHostConfigurationResolver } from '../../ui/configuration/extensionHostConfigurationResolver'; +import { createFileTree } from '../createFileTree'; +import { testFixturesDir } from '../test'; +import { TestMemento } from '../testMemento'; + +describe('ExtensionHostConfigurationProvider', () => { + let provider: ExtensionHostConfigurationResolver; + const folder = (name: string): vscode.WorkspaceFolder => ({ + uri: vscode.Uri.file(join(testFixturesDir, name)), + name: 'test-dir', + index: 0, + }); + + const emptyRequest = { + type: DebugType.ExtensionHost, + name: '', + request: '', + args: ['--extensionDevelopmentPath=${workspaceFolder}'], + }; + + beforeEach(() => { + provider = new ExtensionHostConfigurationResolver( + upcastPartial({ + logPath: testFixturesDir, + workspaceState: new TestMemento(), + }), + ); + EnvironmentVars.platform = 'linux'; + }); + + describe('web worker debugging', () => { + beforeEach(() => + createFileTree(testFixturesDir, { + 'withWeb/package.json': JSON.stringify({ extensionKind: ['web'] }), + 'withoutWeb/package.json': JSON.stringify({}), + 'withEntrypoint1/package.json': JSON.stringify({ main: './foo/j/x.js' }), + 'withEntrypoint2/nested/package.json': JSON.stringify({ main: './bar/j/x.js' }), + }) + ); + + it('does not enable if no args', async () => { + const result = await provider.resolveDebugConfiguration(folder('withWeb'), { + ...emptyRequest, + args: [], + }); + expect(result?.debugWebWorkerHost).to.be.false; + }); + + it('does not enable if wrong type', async () => { + const result = await provider.resolveDebugConfiguration(folder('withoutWeb'), emptyRequest); + expect(result?.debugWebWorkerHost).to.be.false; + }); + + it('does not enable if enoent folder', async () => { + const result = await provider.resolveDebugConfiguration( + folder('doesNotExist'), + emptyRequest, + ); + expect(result?.debugWebWorkerHost).to.be.false; + }); + + it('does not override existing option', async () => { + const result = await provider.resolveDebugConfiguration(folder('doesNotExist'), { + ...emptyRequest, + debugWebWorkerHost: true, + }); + expect(result?.debugWebWorkerHost).to.be.true; + }); + + it('enables if all good', async () => { + const result = await provider.resolveDebugConfiguration(folder('withWeb'), emptyRequest); + expect(result?.debugWebWorkerHost).to.be.true; + }); + + it('guesses outfiles 1', async () => { + const result = await provider.resolveDebugConfiguration( + folder('withEntrypoint1'), + emptyRequest, + ); + expect(result?.outFiles).to.deep.equal(['${workspaceFolder}/foo/**/*.js']); + }); + + it('guesses outfiles 2', async () => { + const result = await provider.resolveDebugConfiguration(folder('withEntrypoint2'), { + type: DebugType.ExtensionHost, + name: '', + request: '', + args: ['--extensionDevelopmentPath=${workspaceFolder}/nested'], + }); + expect(result?.outFiles).to.deep.equal(['${workspaceFolder}/nested/bar/**/*.js']); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/extension/nodeConfigurationProvider.test.ts b/code/extensions/js-debug/src/test/extension/nodeConfigurationProvider.test.ts new file mode 100644 index 000000000000..76b42172add0 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/nodeConfigurationProvider.test.ts @@ -0,0 +1,511 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { promises as fsPromises } from 'fs'; +import { join } from 'path'; +import { SinonStub, stub } from 'sinon'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { EnvironmentVars } from '../../common/environmentVars'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { upcastPartial } from '../../common/objUtils'; +import { INodeLaunchConfiguration } from '../../configuration'; +import { NodeConfigurationResolver } from '../../ui/configuration/nodeDebugConfigurationResolver'; +import { createFileTree } from '../createFileTree'; +import { testFixturesDir } from '../test'; +import { TestMemento } from '../testMemento'; + +describe('NodeDebugConfigurationProvider', () => { + let provider: NodeConfigurationResolver; + let nvmResolver: { resolveNvmVersionPath: SinonStub }; + const folder: vscode.WorkspaceFolder = { + uri: vscode.Uri.file(testFixturesDir), + name: 'test-dir', + index: 0, + }; + + beforeEach(() => { + nvmResolver = { resolveNvmVersionPath: stub() }; + provider = new NodeConfigurationResolver( + upcastPartial({ + logPath: testFixturesDir, + workspaceState: new TestMemento(), + }), + nvmResolver, + new LocalFsUtils(fsPromises), + ); + EnvironmentVars.platform = 'linux'; + }); + + afterEach(() => { + EnvironmentVars.platform = process.platform; + }); + + describe('logging resolution', () => { + const emptyRequest = { + type: 'node', + name: '', + request: '', + }; + + beforeEach(() => { + createFileTree(testFixturesDir, { + 'hello.js': '', + 'package.json': JSON.stringify({ main: 'hello.js' }), + }); + }); + + it('does not log by default', async () => { + const result = await provider.resolveDebugConfiguration(folder, emptyRequest); + expect(result!.trace).to.deep.equal({ + stdio: false, + logFile: null, + }); + }); + + it('applies defaults with trace=true', async () => { + const result = await provider.resolveDebugConfiguration(folder, { + ...emptyRequest, + trace: true, + }); + expect(result!.trace).to.containSubset({ + stdio: true, + }); + }); + }); + + describe('launch config from context', () => { + const emptyRequest = { + type: '', + name: '', + request: '', + }; + + it('loads the program from a package.json main if available', async () => { + createFileTree(testFixturesDir, { + 'hello.js': '', + 'package.json': JSON.stringify({ main: 'hello.js' }), + }); + + const result = (await provider.resolveDebugConfiguration(folder, emptyRequest))!; + result.cwd = result.cwd!.toLowerCase(); + + expect(result).to.containSubset({ + type: DebugType.Node, + cwd: testFixturesDir.toLowerCase(), + name: 'Launch Program', + program: join('${workspaceFolder}', 'hello.js'), + request: 'launch', + }); + }); + + it('loads the program from a package.json start script if available', async () => { + createFileTree(testFixturesDir, { + 'hello.js': '', + 'package.json': JSON.stringify({ scripts: { start: 'node hello.js' } }), + }); + + const result = (await provider.resolveDebugConfiguration(folder, emptyRequest))!; + result.cwd = result.cwd!.toLowerCase(); + + expect(result).to.containSubset({ + type: DebugType.Node, + cwd: testFixturesDir.toLowerCase(), + name: 'Launch Program', + program: join('${workspaceFolder}', 'hello.js'), + request: 'launch', + }); + }); + + it('configures mern starters', async () => { + createFileTree(testFixturesDir, { + 'hello.js': '', + 'package.json': JSON.stringify({ name: 'mern-starter' }), + }); + + const result = await provider.resolveDebugConfiguration(folder, emptyRequest); + + expect(result).to.containSubset({ + runtimeExecutable: 'nodemon', + program: '${workspaceFolder}/index.js', + restart: true, + env: { BABEL_DISABLE_CACHE: '1', NODE_ENV: 'development' }, + }); + }); + + it('loads a common entrypoint if available', async () => { + createFileTree(testFixturesDir, { + 'main.js': '', + }); + + const result = (await provider.resolveDebugConfiguration(folder, emptyRequest))!; + result.cwd = result.cwd!.toLowerCase(); + + expect(result).to.containSubset({ + type: DebugType.Node, + cwd: testFixturesDir.toLowerCase(), + name: 'Launch Program', + program: join('${workspaceFolder}', 'main.js'), + request: 'launch', + }); + }); + + it('attempts to load the active text editor', async () => { + createFileTree(testFixturesDir, { 'hello.js': '' }); + const doc = await vscode.workspace.openTextDocument(join(testFixturesDir, 'hello.js')); + await vscode.window.showTextDocument(doc); + + try { + const result = await provider.resolveDebugConfiguration(folder, emptyRequest); + expect(result).to.containSubset({ + program: join('${workspaceFolder}', 'hello.js'), + }); + } finally { + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + } + }); + + it('applies tsconfig settings automatically', async () => { + createFileTree(testFixturesDir, { + out: { 'hello.js': '' }, + src: { 'hello.ts': '' }, + 'package.json': JSON.stringify({ main: 'out/hello.js' }), + 'tsconfig.json': JSON.stringify({ compilerOptions: { outDir: 'out' } }), + }); + + const doc = await vscode.workspace.openTextDocument( + join(testFixturesDir, 'src', 'hello.ts'), + ); + await vscode.window.showTextDocument(doc); + try { + const result = await provider.resolveDebugConfiguration(folder, emptyRequest); + expect(result).to.containSubset({ + program: join('${workspaceFolder}', 'out', 'hello.js'), + preLaunchTask: 'tsc: build - tsconfig.json', + outFiles: ['${workspaceFolder}/out/**/*.js'], + }); + } finally { + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + } + }); + }); + + it('attempts to resolve nvm', async () => { + createFileTree(testFixturesDir, { + 'my.env': 'A=bar\nB="quoted"\n"C"="more quoted"\n\nD=overridden\n', + 'hello.js': '', + }); + + nvmResolver.resolveNvmVersionPath.resolves({ + directory: '/my/node/location', + binary: 'node64', + }); + const result = await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + runtimeVersion: '3.1.4', + env: { hello: 'world', PATH: '/usr/bin' }, + }); + + expect(result).to.containSubset({ + runtimeExecutable: 'node64', + env: { + hello: 'world', + PATH: '/my/node/location:/usr/bin', + }, + }); + }); + + describe('inspect flags', () => { + it('demaps', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + runtimeArgs: ['-a', '--inspect-brk', '--b'], + })) as INodeLaunchConfiguration; + + expect(result.runtimeArgs).to.deep.equal(['-a', '--b']); + expect(result.stopOnEntry).to.be.true; + }); + + it('does not overwrite existing stop on entry', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + stopOnEntry: 'hello.js', + runtimeArgs: ['-a', '--inspect-brk', '--b'], + })) as INodeLaunchConfiguration; + + expect(result.runtimeArgs).to.deep.equal(['-a', '--b']); + expect(result.stopOnEntry).to.equal('hello.js'); + }); + + it('assigns a random simple attach port', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + attachSimplePort: 0, + })) as INodeLaunchConfiguration; + + expect(result.continueOnAttach).to.be.true; + expect(result.attachSimplePort).to.be.greaterThan(0); + expect(result.runtimeArgs).to.deep.equal([`--inspect-brk=${result.attachSimplePort}`]); + expect(result.continueOnAttach).to.equal(true); + }); + + it('merged picked port with existing runtime args', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + attachSimplePort: 0, + runtimeArgs: ['--nolazy'], + })) as INodeLaunchConfiguration; + + expect(result.runtimeArgs).to.deep.equal([ + '--nolazy', + `--inspect-brk=${result.attachSimplePort}`, + ]); + }); + + it('keeps a static attach port', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + attachSimplePort: 9229, + runtimeArgs: ['--inspect-brk'], + })) as INodeLaunchConfiguration; + + expect(result.continueOnAttach).to.be.true; + expect(result.attachSimplePort).to.be.greaterThan(0); + expect(result.runtimeArgs).to.deep.equal(['--inspect-brk']); + expect(result.continueOnAttach).to.equal(true); + }); + + it('adjusts stopOnEntry to continueOnArray', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + attachSimplePort: 0, + stopOnEntry: true, + })) as INodeLaunchConfiguration; + + expect(result.continueOnAttach).to.be.false; + expect(result.stopOnEntry).to.be.false; + }); + }); + + describe('outFiles', () => { + it('does not modify outfiles with no package.json', async () => { + createFileTree(testFixturesDir, { + 'hello.js': '', + }); + + const result = await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + }); + + expect(result?.outFiles).to.deep.equal([ + '${workspaceFolder}/**/*.(m|c|)js', + '!**/node_modules/**', + ]); + }); + + it('preserves outFiles if package.json is in the same folder', async () => { + createFileTree(testFixturesDir, { + 'hello.js': '', + 'package.json': '{}', + }); + + const result = await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + }); + + expect(result?.outFiles).to.deep.equal([ + '${workspaceFolder}/**/*.(m|c|)js', + '!**/node_modules/**', + ]); + }); + + it('gets the nearest nested package.json', async () => { + createFileTree(testFixturesDir, { + 'a/b/c/hello.js': '', + 'a/b/package.json': '{}', + 'a/package.json': '{}', + }); + + const result = await provider.resolveDebugConfiguration( + { + uri: vscode.Uri.file(join(testFixturesDir, 'b')), + name: 'test-dir', + index: 0, + }, + { + type: DebugType.Node, + name: '', + request: 'launch', + program: '../a/b/c/hello.js', + }, + ); + + expect(result?.outFiles).to.deep.equal([ + '${workspaceFolder}/**/*.(m|c|)js', + '!**/node_modules/**', + '${workspaceFolder}/../a/b/**/*.js', + '!${workspaceFolder}/../a/b/**/node_modules/**', + ]); + }); + + it('does not resolve in node_modules', async () => { + createFileTree(testFixturesDir, { + 'a/node_modules/c/hello.js': '', + 'a/node_modules/c/package.json': '{}', + 'a/package.json': '{}', + }); + + const result = await provider.resolveDebugConfiguration( + { + uri: vscode.Uri.file(join(testFixturesDir, 'b')), + name: 'test-dir', + index: 0, + }, + { + type: DebugType.Node, + name: '', + request: 'launch', + program: '../a/node_modules/c/hello.js', + }, + ); + + expect(result?.outFiles).to.deep.equal([ + '${workspaceFolder}/**/*.(m|c|)js', + '!**/node_modules/**', + '${workspaceFolder}/../a/**/*.js', + '!${workspaceFolder}/../a/**/node_modules/**', + ]); + }); + + it('does not resolve outside the workspace folder', async () => { + createFileTree(testFixturesDir, { + 'a/b/c/hello.js': '', + 'package.json': '{}', + }); + + const result = await provider.resolveDebugConfiguration( + { + uri: vscode.Uri.file(join(testFixturesDir, 'a')), + name: 'test-dir', + index: 0, + }, + { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'b/c/hello.js', + }, + ); + + expect(result?.outFiles).to.deep.equal([ + '${workspaceFolder}/**/*.(m|c|)js', + '!**/node_modules/**', + ]); + }); + }); + + describe('deno', () => { + it('fills in default deno options', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + runtimeExecutable: 'deno', + })) as INodeLaunchConfiguration; + + const port = result.attachSimplePort!; + expect(port).to.be.a('number'); + expect(result.runtimeArgs).to.deep.equal([ + 'run', + `--inspect-brk=127.0.0.1:${port}`, + '--allow-all', + ]); + expect(result.continueOnAttach).to.be.true; + }); + + it('allows manual application', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + runtimeExecutable: 'deno', + runtimeArgs: ['run', '--inspect-brk=9229'], + attachSimplePort: 9229, + })) as INodeLaunchConfiguration; + + expect(result.attachSimplePort).to.equal(9229); + expect(result.runtimeArgs).to.deep.equal(['run', `--inspect-brk=9229`]); + }); + + it('allows partial run args', async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + runtimeExecutable: 'deno', + runtimeArgs: ['--some-arg'], + })) as INodeLaunchConfiguration; + + const port = result.attachSimplePort!; + expect(port).to.be.a('number'); + expect(result.runtimeArgs).to.deep.equal([ + 'run', + `--inspect-brk=127.0.0.1:${port}`, + '--allow-all', + '--some-arg', + ]); + }); + + it("doesn't duplicate --allow-all", async () => { + const result = (await provider.resolveDebugConfiguration(folder, { + type: DebugType.Node, + name: '', + request: 'launch', + program: 'hello.js', + runtimeExecutable: 'deno', + runtimeArgs: ['--allow-all --some-arg'], + })) as INodeLaunchConfiguration; + + const port = result.attachSimplePort!; + expect(port).to.be.a('number'); + expect(result.runtimeArgs).to.deep.equal([ + 'run', + `--inspect-brk=127.0.0.1:${port}`, + '--allow-all', + '--some-arg', + ]); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/extension/pickAttach.test.ts b/code/extensions/js-debug/src/test/extension/pickAttach.test.ts new file mode 100644 index 000000000000..c7ce9dd45be9 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/pickAttach.test.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { ChildProcess, spawn } from 'child_process'; +import { promises as fsPromises } from 'fs'; +import { tmpdir } from 'os'; +import * as path from 'path'; +import { createSandbox, SinonSandbox } from 'sinon'; +import * as vscode from 'vscode'; +import { Commands, DebugType } from '../../common/contributionUtils'; +import { findOpenPort } from '../../common/findOpenPort'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { delay } from '../../common/promiseUtil'; +import { StreamSplitter } from '../../common/streamSplitter'; +import { nodeAttachConfigDefaults } from '../../configuration'; +import { resolveProcessId } from '../../ui/processPicker'; +import { createFileTree } from '../createFileTree'; +import { removePrivatePrefix } from '../test'; +import { eventuallyOk } from '../testIntegrationUtils'; + +describe('pick and attach', () => { + const testDir = path.join(tmpdir(), 'js-debug-pick-and-attach'); + let child: ChildProcess; + let sandbox: SinonSandbox; + let port: number; + let attached = false; + + beforeEach(() => (sandbox = createSandbox())); + afterEach(async () => { + sandbox?.restore(); + child?.kill(); + + after(async () => { + await fsPromises.rm(testDir, { recursive: true, force: true }); + }); + }); + + if (process.platform !== 'win32') { + // perform these in a separate test dir so that we don't have an extra + // package.json from the test workspace + it('infers the working directory', async () => { + createFileTree(testDir, { + 'foo.js': 'setInterval(() => {}, 1000)', + }); + + child = spawn('node', ['foo.js'], { cwd: testDir }); + const config = { ...nodeAttachConfigDefaults, processId: `${child.pid}:1234` }; + await resolveProcessId(new LocalFsUtils(fsPromises), config, true); + expect(removePrivatePrefix(config.cwd!)).to.equal(testDir); + }); + + it('adjusts to the package root', async () => { + createFileTree(testDir, { + 'package.json': '{}', + 'nested/foo.js': 'setInterval(() => {}, 1000)', + }); + + child = spawn('node', ['foo.js'], { cwd: path.join(testDir, 'nested') }); + const config = { ...nodeAttachConfigDefaults, processId: `${child.pid}:1234` }; + await resolveProcessId(new LocalFsUtils(fsPromises), config, true); + expect(removePrivatePrefix(config.cwd!)).to.equal(testDir); + }); + + it('limits inference to workspace root', async () => { + createFileTree(testDir, { + 'package.json': '{}', + 'nested/foo.js': 'setInterval(() => {}, 1000)', + }); + + const getWorkspaceFolder = sandbox.stub(vscode.workspace, 'getWorkspaceFolder'); + getWorkspaceFolder.returns({ + name: 'nested', + index: 1, + uri: vscode.Uri.file(path.join(testDir, 'nested')), + }); + + child = spawn('node', ['foo.js'], { cwd: path.join(testDir, 'nested') }); + const config = { ...nodeAttachConfigDefaults, processId: `${child.pid}:1234` }; + await resolveProcessId(new LocalFsUtils(fsPromises), config, true); + expect(removePrivatePrefix(config.cwd!)).to.equal(path.join(testDir, 'nested')); + }); + } + + describe('', () => { + beforeEach(async () => { + port = await findOpenPort(); + child = spawn('node', ['--inspect-brk', `--inspect-port=${port}`], { stdio: 'pipe' }); + child.on('error', console.error); + child + .stderr!.pipe(new StreamSplitter('\n')) + .on( + 'data', + ( + line: string, + ) => (attached = attached || line.toString().includes('Debugger attached')), + ); + }); + + it('end to end', async function() { + this.timeout(30 * 1000); + + const createQuickPick = sandbox.spy(vscode.window, 'createQuickPick'); + vscode.commands.executeCommand(Commands.AttachProcess); + + await delay(2000); + const picker = await eventuallyOk(() => { + expect(createQuickPick.called).to.be.true; + return createQuickPick.getCall(0).returnValue; + }, 10 * 1000); + + await delay(2000); + const item = await eventuallyOk(() => { + const i = picker.items.find(item => (item as any).pidAndPort === `${child.pid}:${port}`); + if (!i) { + throw new Error('expected quickpick to have item'); + } + return i; + }, 10 * 1000); + + picker.selectedItems = [item]; + await delay(2000); + await vscode.commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + await delay(2000); + await eventuallyOk( + () => expect(attached).to.equal(true, 'expected to have attached'), + 10 * 1000, + ); + }); + + it('works without a defined workspace', async () => { + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'attach', + name: 'attach', + processId: `${child.pid}:${port}`, + }); + + await eventuallyOk( + () => expect(attached).to.equal(true, 'expected to have attached'), + 10 * 1000, + ); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-continues-if-was-paused-on-start-with-debugger-domain.txt b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-continues-if-was-paused-on-start-with-debugger-domain.txt new file mode 100644 index 000000000000..6b319c0f5e3f --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-continues-if-was-paused-on-start-with-debugger-domain.txt @@ -0,0 +1,34 @@ +[ + [0] : { + column : 17 + id : + line : 21 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } + verified : true + } +] +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +{ + allThreadsContinued : false + threadId : +} +{ + category : stdout + column : 11 + line : 23 + output : hello + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-continues-if-was-paused-on-start.txt b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-continues-if-was-paused-on-start.txt new file mode 100644 index 000000000000..6b319c0f5e3f --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-continues-if-was-paused-on-start.txt @@ -0,0 +1,34 @@ +[ + [0] : { + column : 17 + id : + line : 21 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } + verified : true + } +] +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +{ + allThreadsContinued : false + threadId : +} +{ + category : stdout + column : 11 + line : 23 + output : hello + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-does-not-unverify-target-breakpoint.txt b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-does-not-unverify-target-breakpoint.txt new file mode 100644 index 000000000000..fa69cc0d4bf5 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-does-not-unverify-target-breakpoint.txt @@ -0,0 +1,46 @@ +[ + [0] : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + [1] : { + column : 3 + id : 1 + line : 17 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } +] +{ + breakpoint : { + id : 0 + message : Unbound breakpoint + verified : false + } + reason : changed +} +{ + breakpoint : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + reason : changed +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-runs-until-a-breakpoint-is-hit.txt b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-runs-until-a-breakpoint-is-hit.txt new file mode 100644 index 000000000000..638ace07c842 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-runs-until-a-breakpoint-is-hit.txt @@ -0,0 +1,24 @@ +paused event{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +finished profile{ + label : + running : false +} +reenabled breakpoint{ + breakpoint : { + column : 1 + id : 0 + line : 20 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + reason : changed +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-unverifies-and-reverifies.txt b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-unverifies-and-reverifies.txt new file mode 100644 index 000000000000..92e7fbfb7b64 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-cpu-profiling-breakpoints-unverifies-and-reverifies.txt @@ -0,0 +1,35 @@ +[ + [0] : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } +] +{ + breakpoint : { + id : 0 + message : Unbound breakpoint + verified : false + } + reason : changed +} +{ + breakpoint : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + reason : changed +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-continues-if-was-paused-on-start-with-debugger-domain.txt b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-continues-if-was-paused-on-start-with-debugger-domain.txt new file mode 100644 index 000000000000..6b319c0f5e3f --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-continues-if-was-paused-on-start-with-debugger-domain.txt @@ -0,0 +1,34 @@ +[ + [0] : { + column : 17 + id : + line : 21 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } + verified : true + } +] +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +{ + allThreadsContinued : false + threadId : +} +{ + category : stdout + column : 11 + line : 23 + output : hello + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-continues-if-was-paused-on-start.txt b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-continues-if-was-paused-on-start.txt new file mode 100644 index 000000000000..6b319c0f5e3f --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-continues-if-was-paused-on-start.txt @@ -0,0 +1,34 @@ +[ + [0] : { + column : 17 + id : + line : 21 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } + verified : true + } +] +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +{ + allThreadsContinued : false + threadId : +} +{ + category : stdout + column : 11 + line : 23 + output : hello + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-does-not-unverify-target-breakpoint.txt b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-does-not-unverify-target-breakpoint.txt new file mode 100644 index 000000000000..fa69cc0d4bf5 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-does-not-unverify-target-breakpoint.txt @@ -0,0 +1,46 @@ +[ + [0] : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + [1] : { + column : 3 + id : 1 + line : 17 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } +] +{ + breakpoint : { + id : 0 + message : Unbound breakpoint + verified : false + } + reason : changed +} +{ + breakpoint : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + reason : changed +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-runs-until-a-breakpoint-is-hit.txt b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-runs-until-a-breakpoint-is-hit.txt new file mode 100644 index 000000000000..638ace07c842 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-runs-until-a-breakpoint-is-hit.txt @@ -0,0 +1,24 @@ +paused event{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +finished profile{ + label : + running : false +} +reenabled breakpoint{ + breakpoint : { + column : 1 + id : 0 + line : 20 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + reason : changed +} diff --git a/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-unverifies-and-reverifies.txt b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-unverifies-and-reverifies.txt new file mode 100644 index 000000000000..92e7fbfb7b64 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling-heap-profiling-breakpoints-unverifies-and-reverifies.txt @@ -0,0 +1,35 @@ +[ + [0] : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } +] +{ + breakpoint : { + id : 0 + message : Unbound breakpoint + verified : false + } + reason : changed +} +{ + breakpoint : { + column : 16 + id : 0 + line : 6 + source : { + name : simpleNode/profilePlayground.js + path : ${workspaceFolder}/simpleNode/profilePlayground.js + sourceReference : 0 + } + verified : true + } + reason : changed +} diff --git a/code/extensions/js-debug/src/test/extension/profiling.test.ts b/code/extensions/js-debug/src/test/extension/profiling.test.ts new file mode 100644 index 000000000000..bb3d286c5b18 --- /dev/null +++ b/code/extensions/js-debug/src/test/extension/profiling.test.ts @@ -0,0 +1,773 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { promises as fs } from 'fs'; +import { join } from 'path'; +import { SinonSpy, stub } from 'sinon'; +import * as vscode from 'vscode'; +import { Commands, DebugType, runCommand } from '../../common/contributionUtils'; +import { DisposableList } from '../../common/disposable'; +import { EventEmitter } from '../../common/events'; +import { delay } from '../../common/promiseUtil'; +import Dap from '../../dap/api'; +import { createFileTree } from '../createFileTree'; +import { ITestHandle, NodeTestHandle, testFixturesDir, testWorkspace } from '../test'; +import { eventuallyOk, itIntegrates } from '../testIntegrationUtils'; + +describe('profiling', () => { + const cwd = join(testWorkspace, 'simpleNode'); + const script = join(cwd, 'profilePlayground.js'); + let createQuickPick: SinonSpy; + let acceptQuickPick: EventEmitter; + + const assertValidOutputFile = async (file: string) => { + const contents = await fs.readFile(file, 'utf-8'); + expect(() => JSON.parse(contents)).to.not.throw( + undefined, + 'expected to be valid JSON: ' + contents, + ); + }; + + const getFrameId = async (threadId: number, handle: ITestHandle) => + (await handle.dap.stackTrace({ threadId })).stackFrames[0].id; + + beforeEach(() => { + const original = vscode.window.createQuickPick; + createQuickPick = stub(vscode.window, 'createQuickPick').callsFake(() => { + const picker = original(); + acceptQuickPick = new EventEmitter(); + stub(picker, 'onDidAccept').callsFake(acceptQuickPick.event); + return picker; + }); + }); + + afterEach(() => { + createQuickPick.restore(); + }); + + describe('console', () => { + itIntegrates('works', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ['console.profile("some-profile");', 'console.profileEnd("some-profile");'], + }); + const handle = await r.runScript('test.js', { + __workspaceFolder: testFixturesDir, + }); + + handle.load(); + await handle.dap.once('terminated'); + await eventuallyOk(() => + assertValidOutputFile(join(testFixturesDir, 'some-profile.cpuprofile')) + ); + }); + }); + + const setBreakpointsAndWaitForVerification = async ( + handle: NodeTestHandle, + bps: Dap.SetBreakpointsParams, + ) => { + const r = await handle.dap.setBreakpoints(bps); + + return await Promise.all( + r.breakpoints.map(async bp => { + if (bp.verified) { + return bp; + } + + return ( + await handle.dap.once( + 'breakpoint', + ev => ev.breakpoint.id === bp.id && ev.breakpoint.verified, + ) + ).breakpoint; + }), + ); + }; + + describe('cpu-profiling', () => { + itIntegrates('cpu sanity test', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + handle.load(); + const startedEvent = handle.dap.once('profileStarted'); + await handle.dap.startProfile({ type: 'cpu' }); + await delay(300); + await handle.dap.stopProfile({}); + await assertValidOutputFile((await startedEvent).file); + }); + + describe('breakpoints', () => { + itIntegrates('continues if was paused on start', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [{ line: 21, column: 1 }], + }), + ); + + handle.log(await handle.dap.once('stopped')); + const continued = handle.dap.once('continued'); + const output = handle.dap.once('output'); + await handle.dap.startProfile({ type: 'cpu' }); + handle.log(await continued); + handle.log(await output); // make sure it *actually* continued + handle.assertLog(); + }); + + itIntegrates('continues if was paused on start with debugger domain', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + const stopped = handle.dap.once('stopped'); + handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [{ line: 21, column: 1 }], + }), + ); + + handle.log(await stopped); + const continued = handle.dap.once('continued'); + const output = handle.dap.once('output'); + await handle.dap.startProfile({ type: 'cpu', stopAtBreakpoint: [-1] }); + handle.log(await continued); + handle.log(await output); // make sure it *actually* continued + handle.assertLog(); + }); + + itIntegrates('unverifies and reverifies', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [{ line: 6, column: 1 }], + }), + undefined, + [], + ); + + await delay(0); + + const logfn = stub().callsFake(data => handle.log(data, undefined, [])); + handle.dap.on('breakpoint', logfn); + + await handle.dap.startProfile({ type: 'cpu' }); + await eventuallyOk(() => expect(logfn.callCount).to.gte(1), 2000); + await handle.dap.stopProfile({}); + await eventuallyOk(() => expect(logfn.callCount).to.gte(2), 2000); + handle.assertLog(); + }); + + itIntegrates('does not unverify target breakpoint', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + const breakpoints = handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [ + { line: 6, column: 1 }, + { line: 17, column: 1 }, + ], + }), + undefined, + [], + ); + + await delay(0); + + const logfn = stub().callsFake(data => handle.log(data, undefined, [])); + handle.dap.on('breakpoint', logfn); + + await handle.dap.startProfile({ + type: 'cpu', + stopAtBreakpoint: [breakpoints[1].id], + }); + await eventuallyOk(() => expect(logfn.callCount).to.gte(1)); + await handle.dap.stopProfile({}); + await eventuallyOk(() => expect(logfn.callCount).to.gte(2)); + handle.assertLog(); + }); + + itIntegrates('runs until a breakpoint is hit', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + const { breakpoints } = await handle.dap.setBreakpoints({ + source: { path: script }, + breakpoints: [ + { line: 20, column: 1 }, // entry bp to let us set the timeout + { line: 17, column: 1 }, // inside the "noop" function + ], + }); + + await handle.load(); + + const startedEvent = handle.dap.once('profileStarted'); + + // Wait for the pause, and call noop after a second which will hit the BP + const stopped = await handle.dap.once('stopped'); + await handle.dap.evaluate({ + expression: 'setTimeout(noop, 1000)', + context: 'repl', + frameId: await getFrameId(stopped.threadId!, handle), + }); + + // Start a profile + await handle.dap.startProfile({ + type: 'cpu', + stopAtBreakpoint: [breakpoints[1].id!], + }); + + // We should hit the breakpoint, stop the profile, and re-verify the first breakpoint. + const paused = handle.dap.once('stopped'); + const profileFinished = handle.dap.once('profilerStateUpdate'); + const breakpointReenabled = handle.dap.once( + 'breakpoint', + evt => evt.breakpoint.id === breakpoints[0].id && evt.breakpoint.verified, + ); + handle.log(await paused, 'paused event'); + handle.log(await profileFinished, 'finished profile'); + handle.log(await breakpointReenabled, 'reenabled breakpoint', []); + await assertValidOutputFile((await startedEvent).file); + handle.assertLog(); + }); + }); + + describe('ui', () => { + afterEach(async () => { + await vscode.debug.stopDebugging(); + }); + + const pickTermination = async (session: vscode.DebugSession, labelRe: RegExp) => { + vscode.commands.executeCommand(Commands.StartProfile, session.id); + + // we skip this step while "cpu" is the only profile: + const typePicker = await eventuallyOk(() => { + expect(createQuickPick.callCount).to.equal(1); + const picker: vscode.QuickPick = + createQuickPick.getCall(0).returnValue; + expect(picker.items).to.not.be.empty; + return picker; + }, 2000); + + typePicker.selectedItems = typePicker.items.filter(i => /CPU/i.test(i.label)); + acceptQuickPick.fire(); + + const terminationPicker = await eventuallyOk(() => { + expect(createQuickPick.callCount).to.equal(2); + const picker: vscode.QuickPick = + createQuickPick.getCall(0).returnValue; + expect(picker.items).to.not.be.empty; + return picker; + }, 2000); + + terminationPicker.selectedItems = terminationPicker.items.filter(i => + labelRe.test(i.label) + ); + acceptQuickPick.fire(); + }; + + // todo: renable after 1.49, fails in CI right now + it.skip('allows picking breakpoints', async () => { + vscode.debug.addBreakpoints([ + new vscode.SourceBreakpoint( + new vscode.Location(vscode.Uri.file(script), new vscode.Position(19, 0)), + ), + new vscode.SourceBreakpoint( + new vscode.Location(vscode.Uri.file(script), new vscode.Position(5, 0)), + ), + new vscode.SourceBreakpoint( + new vscode.Location(vscode.Uri.file(script + '.foo'), new vscode.Position(0, 0)), + ), + ]); + + after(() => { + vscode.debug.removeBreakpoints(vscode.debug.breakpoints); + }); + + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await pickTermination(session, /breakpoint/i); + + const breakpointPicker = await eventuallyOk(() => { + expect(createQuickPick.callCount).to.equal(2); + const picker: vscode.QuickPick = + createQuickPick.getCall(1).returnValue; + expect(picker.items.length).to.be.greaterThan(0, 'expected to have picker items'); + return picker; + }, 5000); + + expect(breakpointPicker.items).to.containSubset([ + { + description: 'for (let i = 0; i < 10; i++) {', + label: 'testWorkspace/simpleNode/profilePlayground.js:6:16', + }, + { + description: 'setInterval(() => {', + label: 'testWorkspace/simpleNode/profilePlayground.js:20:1', + }, + ]); + + breakpointPicker.dispose(); + }); + + it('sets substate correctly', async () => { + const disposable = new DisposableList(); + disposable.push( + vscode.commands.registerCommand('js-debug.test.callback', () => undefined), + ); + + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await runCommand(vscode.commands, Commands.StartProfile, { + sessionId: session.id, + type: 'cpu', + termination: { type: 'manual' }, + }); + + await eventuallyOk(() => expect(session.name).to.contain('Profiling'), 2000); + await runCommand(vscode.commands, Commands.StopProfile, session.id); + await eventuallyOk(() => expect(session.name).to.not.contain('Profiling'), 2000); + disposable.dispose(); + }); + + it('works with pure command API', async () => { + const callback = stub(); + const disposable = new DisposableList(); + disposable.push(vscode.commands.registerCommand('js-debug.test.callback', callback)); + + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await runCommand(vscode.commands, Commands.StartProfile, { + sessionId: session.id, + type: 'cpu', + termination: { type: 'manual' }, + onCompleteCommand: 'js-debug.test.callback', + }); + + await delay(1000); + + await runCommand(vscode.commands, Commands.StopProfile, session.id); + + const args = await eventuallyOk(() => { + expect(callback.called).to.be.true; + return callback.getCall(0).args[0]; + }, 2000); + + expect(() => JSON.parse(args.contents)).to.not.throw; + expect(args.basename).to.match(/\.cpuprofile$/); + disposable.dispose(); + }); + + it('profiles from launch', async function() { + this.timeout(20 * 1000); // 20 seconds timeout + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + profileStartup: true, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await eventuallyOk(() => expect(session.name).to.contain('Profiling'), 2000); + await runCommand(vscode.commands, Commands.StopProfile, session.id); + await eventuallyOk(() => expect(session.name).to.not.contain('Profiling'), 2000); + }); + }); + }); + + describe('heap-profiling', () => { + itIntegrates('heap sanity test', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + handle.load(); + const startedEvent = handle.dap.once('profileStarted'); + await handle.dap.startProfile({ type: 'heap' }); + await delay(300); + await handle.dap.stopProfile({}); + await assertValidOutputFile((await startedEvent).file); + }); + + describe('breakpoints', () => { + itIntegrates('continues if was paused on start', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + const stopped = handle.dap.once('stopped'); + handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [{ line: 21, column: 1 }], + }), + ); + + handle.log(await stopped); + const continued = handle.dap.once('continued'); + const output = handle.dap.once('output'); + await handle.dap.startProfile({ type: 'heap' }); + handle.log(await continued); + handle.log(await output); // make sure it *actually* continued + handle.assertLog(); + }); + + itIntegrates('continues if was paused on start with debugger domain', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + const stopped = handle.dap.once('stopped'); + handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [{ line: 21, column: 1 }], + }), + ); + + handle.log(await stopped); + const continued = handle.dap.once('continued'); + const output = handle.dap.once('output'); + await handle.dap.startProfile({ type: 'heap', stopAtBreakpoint: [-1] }); + handle.log(await continued); + handle.log(await output); // make sure it *actually* continued + handle.assertLog(); + }); + + itIntegrates('unverifies and reverifies', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [{ line: 6, column: 1 }], + }), + undefined, + [], + ); + + await delay(0); + + const logfn = stub().callsFake(data => handle.log(data, undefined, [])); + handle.dap.on('breakpoint', logfn); + + await handle.dap.startProfile({ type: 'heap' }); + await eventuallyOk(() => expect(logfn.callCount).to.gte(1), 2000); + await handle.dap.stopProfile({}); + await eventuallyOk(() => expect(logfn.callCount).to.gte(2), 2000); + handle.assertLog(); + }); + + itIntegrates('does not unverify target breakpoint', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + await handle.load(); + const breakpoints = handle.log( + await setBreakpointsAndWaitForVerification(handle, { + source: { path: script }, + breakpoints: [ + { line: 6, column: 1 }, + { line: 17, column: 1 }, + ], + }), + undefined, + [], + ); + + await delay(0); + + const logfn = stub().callsFake(data => handle.log(data, undefined, [])); + handle.dap.on('breakpoint', logfn); + + await handle.dap.startProfile({ + type: 'heap', + stopAtBreakpoint: [breakpoints[1].id], + }); + await eventuallyOk(() => expect(logfn.callCount).to.gte(1)); + await handle.dap.stopProfile({}); + await eventuallyOk(() => expect(logfn.callCount).to.gte(2)); + handle.assertLog(); + }); + + itIntegrates('runs until a breakpoint is hit', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(script); + const { breakpoints } = await handle.dap.setBreakpoints({ + source: { path: script }, + breakpoints: [ + { line: 20, column: 1 }, // entry bp to let us set the timeout + { line: 17, column: 1 }, // inside the "noop" function + ], + }); + + await handle.load(); + + const startedEvent = handle.dap.once('profileStarted'); + + // Wait for the pause, and call noop after a second which will hit the BP + const stopped = await handle.dap.once('stopped'); + await handle.dap.evaluate({ + expression: 'setTimeout(noop, 1000)', + context: 'repl', + frameId: await getFrameId(stopped.threadId!, handle), + }); + + // Start a profile + await handle.dap.startProfile({ + type: 'heap', + stopAtBreakpoint: [breakpoints[1].id!], + }); + + // We should hit the breakpoint, stop the profile, and re-verify the first breakpoint. + const paused = handle.dap.once('stopped'); + const profileFinished = handle.dap.once('profilerStateUpdate'); + const breakpointReenabled = handle.dap.once( + 'breakpoint', + evt => evt.breakpoint.id === breakpoints[0].id && evt.breakpoint.verified, + ); + handle.log(await paused, 'paused event'); + handle.log(await profileFinished, 'finished profile'); + handle.log(await breakpointReenabled, 'reenabled breakpoint', []); + await assertValidOutputFile((await startedEvent).file); + handle.assertLog(); + }); + }); + + describe('ui', () => { + afterEach(async () => { + await vscode.debug.stopDebugging(); + }); + + const pickTermination = async (session: vscode.DebugSession, labelRe: RegExp) => { + vscode.commands.executeCommand(Commands.StartProfile, session.id); + + // we skip this step while "heap" is the only profile: + const typePicker = await eventuallyOk(() => { + expect(createQuickPick.callCount).to.equal(1); + const picker: vscode.QuickPick = + createQuickPick.getCall(0).returnValue; + expect(picker.items).to.not.be.empty; + return picker; + }, 2000); + + typePicker.selectedItems = typePicker.items.filter(i => /heap/i.test(i.label)); + acceptQuickPick.fire(); + + const terminationPicker = await eventuallyOk(() => { + expect(createQuickPick.callCount).to.equal(2); + const picker: vscode.QuickPick = + createQuickPick.getCall(0).returnValue; + expect(picker.items).to.not.be.empty; + return picker; + }, 2000); + + terminationPicker.selectedItems = terminationPicker.items.filter(i => + labelRe.test(i.label) + ); + acceptQuickPick.fire(); + }; + + // todo: renable after 1.49, fails in CI right now + it.skip('allows picking breakpoints', async () => { + vscode.debug.addBreakpoints([ + new vscode.SourceBreakpoint( + new vscode.Location(vscode.Uri.file(script), new vscode.Position(19, 0)), + ), + new vscode.SourceBreakpoint( + new vscode.Location(vscode.Uri.file(script), new vscode.Position(5, 0)), + ), + new vscode.SourceBreakpoint( + new vscode.Location(vscode.Uri.file(script + '.foo'), new vscode.Position(0, 0)), + ), + ]); + + after(() => { + vscode.debug.removeBreakpoints(vscode.debug.breakpoints); + }); + + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await pickTermination(session, /breakpoint/i); + + const breakpointPicker = await eventuallyOk(() => { + expect(createQuickPick.callCount).to.equal(2); + const picker: vscode.QuickPick = + createQuickPick.getCall(1).returnValue; + expect(picker.items.length).to.be.greaterThan(0, 'expected to have picker items'); + return picker; + }, 5000); + + expect(breakpointPicker.items).to.containSubset([ + { + description: 'for (let i = 0; i < 10; i++) {', + label: 'testWorkspace/simpleNode/profilePlayground.js:6:16', + }, + { + description: 'setInterval(() => {', + label: 'testWorkspace/simpleNode/profilePlayground.js:20:1', + }, + ]); + + breakpointPicker.dispose(); + }); + + it('sets substate correctly', async () => { + const disposable = new DisposableList(); + disposable.push( + vscode.commands.registerCommand('js-debug.test.callback', () => undefined), + ); + + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await runCommand(vscode.commands, Commands.StartProfile, { + sessionId: session.id, + type: 'cpu', + termination: { type: 'manual' }, + }); + + await eventuallyOk(() => expect(session.name).to.contain('Profiling'), 2000); + await runCommand(vscode.commands, Commands.StopProfile, session.id); + await eventuallyOk(() => expect(session.name).to.not.contain('Profiling'), 2000); + disposable.dispose(); + }); + + it('works with pure command API', async () => { + const callback = stub(); + const disposable = new DisposableList(); + disposable.push(vscode.commands.registerCommand('js-debug.test.callback', callback)); + + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await runCommand(vscode.commands, Commands.StartProfile, { + sessionId: session.id, + type: 'heap', + termination: { type: 'manual' }, + onCompleteCommand: 'js-debug.test.callback', + }); + + await delay(1000); + + await runCommand(vscode.commands, Commands.StopProfile, session.id); + + const args = await eventuallyOk(() => { + expect(callback.called).to.be.true; + return callback.getCall(0).args[0]; + }, 2000); + + expect(() => JSON.parse(args.contents)).to.not.throw; + expect(args.basename).to.match(/\.heapprofile$/); + disposable.dispose(); + }); + + it('profiles from launch', async function() { + this.timeout(20 * 1000); // 20 seconds timeout + vscode.debug.startDebugging(undefined, { + type: DebugType.Node, + request: 'launch', + name: 'test', + program: script, + profileStartup: true, + }); + + const session = await new Promise(resolve => + vscode.debug.onDidStartDebugSession(s => + '__pendingTargetId' in s.configuration ? resolve(s) : undefined + ) + ); + + await eventuallyOk(() => expect(session.name).to.contain('Profiling'), 2000); + await runCommand(vscode.commands, Commands.StopProfile, session.id); + await eventuallyOk(() => expect(session.name).to.not.contain('Profiling'), 2000); + }); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/framework/react-hit-breakpoint.txt b/code/extensions/js-debug/src/test/framework/react-hit-breakpoint.txt new file mode 100644 index 000000000000..b1722cbd2a13 --- /dev/null +++ b/code/extensions/js-debug/src/test/framework/react-hit-breakpoint.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +App @ ${fixturesDir}/react-test/src/App.tsx:6:1 diff --git a/code/extensions/js-debug/src/test/framework/react-js-hit-breakpoint.txt b/code/extensions/js-debug/src/test/framework/react-js-hit-breakpoint.txt new file mode 100644 index 000000000000..9f91b407ca0c --- /dev/null +++ b/code/extensions/js-debug/src/test/framework/react-js-hit-breakpoint.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +App @ ${fixturesDir}/react-test/src/App.js:6:1 diff --git a/code/extensions/js-debug/src/test/framework/reactTest.ts b/code/extensions/js-debug/src/test/framework/reactTest.ts new file mode 100644 index 000000000000..5620f43e382a --- /dev/null +++ b/code/extensions/js-debug/src/test/framework/reactTest.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as cp from 'child_process'; +import * as fs from 'fs'; +import { join } from 'path'; +import { Logger } from '../../common/logging/logger'; +import { getDeferred } from '../../common/promiseUtil'; +import Dap from '../../dap/api'; +import { killTree } from '../../targets/node/killTree'; +import { ITestHandle, testFixturesDir } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('react', () => { + async function waitForPause(p: ITestHandle, cb?: (threadId: string) => Promise) { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + if (cb) await cb(threadId); + return p.dap.continue({ threadId }); + } + + const projectName = 'react-test'; + let projectFolder: string; + let devServerProc: cp.ChildProcessWithoutNullStreams | undefined; + + afterEach(() => { + if (devServerProc) { + console.log('Killing ' + devServerProc.pid); + killTree(devServerProc.pid!, Logger.null); + } + }); + + describe('TS', () => { + beforeEach(async function() { + this.timeout(60000 * 4); + projectFolder = join(testFixturesDir, projectName); + await setupCRA(projectName, testFixturesDir, ['--template', 'cra-template-typescript']); + devServerProc = await startDevServer(projectFolder); + }); + + itIntegrates('hit breakpoint', async ({ r }) => { + // Breakpoint in inline script set before launch. + const p = await r._launch('http://localhost:3000', { + webRoot: projectFolder, + __workspaceFolder: projectFolder, + rootPath: projectFolder, + }); + const source: Dap.Source = { + path: join(projectFolder, 'src/App.tsx'), + }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 6, column: 0 }] }); + p.load(); + await waitForPause(p); + p.assertLog({ substring: true }); + }); + }); + + describe('JS', () => { + beforeEach(async function() { + this.timeout(60000 * 4); + projectFolder = join(testFixturesDir, projectName); + await setupCRA(projectName, testFixturesDir); + devServerProc = await startDevServer(projectFolder); + }); + + itIntegrates('hit breakpoint', async ({ r }) => { + // Breakpoint in inline script set before launch. + const p = await r._launch('http://localhost:3000', { + webRoot: projectFolder, + __workspaceFolder: projectFolder, + rootPath: projectFolder, + }); + const source: Dap.Source = { + path: join(projectFolder, 'src/App.js'), + }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 6, column: 0 }] }); + p.load(); + await waitForPause(p); + p.assertLog({ substring: true }); + }); + }); +}); + +async function setupCRA(projectName: string, cwd: string, args: string[] = []): Promise { + console.log('Setting up CRA in ' + cwd); + fs.mkdirSync(cwd, { recursive: true }); + const setupProc = cp.spawn('npx', ['create-react-app', ...args, projectName], { + cwd, + stdio: 'pipe', + env: process.env, + }); + setupProc.stdout.on('data', d => console.log(d.toString().replace(/\r?\n$/, ''))); + setupProc.stderr.on('data', d => console.error(d.toString().replace(/\r?\n$/, ''))); + + const done = getDeferred(); + setupProc.once('exit', () => { + done.resolve(undefined); + }); + await done.promise; +} + +async function startDevServer(projectFolder: string): Promise { + const devServerListening = getDeferred(); + const devServerProc = cp.spawn('npm', ['run-script', 'start'], { + env: { ...process.env, BROWSER: 'none', SKIP_PREFLIGHT_CHECK: 'true' }, + cwd: projectFolder, + stdio: 'pipe', + }); + const timer = setTimeout(() => { + console.log('Did not get recognized dev server output, continuing'); + devServerListening.resolve(undefined); + }, 10000); + devServerProc.stdout.on('data', d => { + d = d.toString(); + if (d.includes('You can now view')) { + console.log('Detected CRA dev server started'); + devServerListening.resolve(undefined); + } else if (d.includes('Something is already')) { + devServerListening.reject(new Error('Failed to start the dev server: ' + d)); + } + + console.log(d.toString().replace(/\r?\n$/, '')); + }); + devServerProc.stderr.on('data', d => console.error(d.toString().replace(/\r?\n$/, ''))); + await devServerListening.promise; + clearTimeout(timer); + + return devServerProc; +} diff --git a/code/extensions/js-debug/src/test/goldenText.ts b/code/extensions/js-debug/src/test/goldenText.ts new file mode 100644 index 000000000000..b2f22842dfd0 --- /dev/null +++ b/code/extensions/js-debug/src/test/goldenText.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import * as fs from 'fs'; +import * as path from 'path'; +import { forceForwardSlashes } from '../common/pathUtils'; +import { escapeRegexSpecialChars } from '../common/stringUtils'; +import * as urlUtils from '../common/urlUtils'; +import { testFixturesDir } from './test'; + +const kStabilizeNames = ['id', 'threadId', 'sourceReference', 'variablesReference']; +const kOmitNames = new Set(['hitBreakpointIds']); + +const trimLineWhitespace = (str: string) => + str + .split('\n') + .map(l => l.trimRight()) + .join('\n') + .replace(/\\r\\n/g, '\\n'); + +export const removeNodeInternalsStackLines = (s: string) => + s.replace(/^.*.*\r?\n/gm, '').replace(/^.*@ internal\/.*\r?\n/gm, ''); + +export class GoldenText { + _results: string[]; + _testName: string; + _hasNonAssertedLogs: boolean; + _workspaceFolder: string; + + constructor(testName: string, private readonly testFile: string, workspaceFolder: string) { + this._results = []; + this._testName = testName; + this._hasNonAssertedLogs = false; + this._workspaceFolder = urlUtils.platformPathToPreferredCase(workspaceFolder); + } + + hasNonAssertedLogs() { + return this._hasNonAssertedLogs; + } + + getOutput(): string { + return trimLineWhitespace(this._results.join('\n') + '\n'); + } + + /** + * This method _must_ be called from the test file. + * The output file will go next to the file from which this is called. + */ + assertLog( + options: { + substring?: boolean; + process?: (s: string) => string; + customAssert?: (expected: string) => any; + } = {}, + ) { + let output = this.getOutput(); + this._hasNonAssertedLogs = false; + + if (options.customAssert) { + options.customAssert(output); + return; + } + + if (options.process) { + output = options.process(output); + } + + const goldenFilePath = this.findGoldenFilePath(); + if (!fs.existsSync(goldenFilePath)) { + console.log(`----- Missing expectations file, writing a new one`); + fs.writeFileSync(goldenFilePath, output, { encoding: 'utf-8' }); + } else if (process.env.RESET_RESULTS) { + fs.writeFileSync(goldenFilePath, output, { encoding: 'utf-8' }); + } else { + const expectations = trimLineWhitespace(fs.readFileSync(goldenFilePath).toString('utf-8')); + + try { + if (options.substring) { + expect(output).to.contain(expectations); + } else { + expect(output).to.equal(expectations); + } + } catch (err) { + fs.writeFileSync(goldenFilePath + '.actual', output, { encoding: 'utf-8' }); + throw err; + } + } + } + + private findGoldenFilePath() { + const testFilePath = this.testFile; + if (!testFilePath) { + throw new Error('GoldenText failed to get filename!'); + } + + const fileFriendlyTestName = this._testName + .trim() + .toLowerCase() + .replace(/\s/g, '-') + .replace(/[^-0-9a-zа-яё]/gi, ''); + + const testFileBase = path.resolve(path.dirname(testFilePath), fileFriendlyTestName); + const platformPath = testFileBase + `.${process.platform}.txt`; + if (fs.existsSync(platformPath)) { + return platformPath; + } + + return testFileBase + '.txt'; + } + + _sanitize(value: string): string { + // replaces path like C:/testDir/foo/bar.js -> ${testDir}/foo/bar.js + const replacePath = (needle: string, replacement: string) => { + // Escape special chars, force paths to use forward slashes + const safeStr = escapeRegexSpecialChars(forceForwardSlashes(needle), '/'); + // Create an re that allows for any slash delimiter, and looks at the rest of the line + const re = new RegExp(safeStr.replace(/\//g, '[\\\\/]') + '(.*)', 'gi'); + + // Replace it with the ${replacementString} and a forward-slashed version + // of the rest of the line. + value = value.replace( + re, + (_match, trailing) => replacement + forceForwardSlashes(trailing), + ); + }; + + value = String(value); + replacePath(this._workspaceFolder, '${workspaceFolder}'); + replacePath(this._workspaceFolder.replace(/\\/g, '\\\\'), '${workspaceFolder}'); // string escaping on windows + replacePath(testFixturesDir, '${fixturesDir}'); + value = value.replace(/testWorkspace/g, '${workspaceFolder}'); + value = value.replace('/private${fixturesDir}', '${fixturesDir}'); // for osx + + // Don't compare blackboxed code, as this is subject to change between + // runtime/Node.js versions. + value = value + .split('\n') + .filter(line => !line.includes('hidden: blackboxed')) + .join('\n'); + + return value + .replace(/VM\d+/g, 'VM') + .replace(/logpoint-.*?\.cdp/g, 'logpoint-.cdp') + .replace(/\r\n/g, '\n') + .replace(/@\ .*vscode-pwa(\/|\\)/g, '@ ') + .replace(/data:text\/html;base64,[a-zA-Z0-9+/]*=?/g, ''); + } + + log(item: any, title?: string, stabilizeNames?: string[]): any { + this._hasNonAssertedLogs = true; + if (typeof item === 'object') return this._logObject(item, title, stabilizeNames); + this._results.push((title || '') + this._sanitize(item)); + return item; + } + + _logObject(object: Record, title?: string, stabilizeNames?: string[]): any { + stabilizeNames = stabilizeNames || kStabilizeNames; + const lines: string[] = []; + + const dumpValue = (value: any, prefix: string, prefixWithName: string) => { + if (typeof value === 'object' && value !== null) { + if (value instanceof Array) dumpItems(value, prefix, prefixWithName); + else dumpProperties(value, prefix, prefixWithName); + } else { + lines.push(prefixWithName + this._sanitize(value).replace(/\n/g, ' ')); + } + }; + + function dumpProperties(object: any, prefix: string, firstLinePrefix: string) { + prefix = prefix || ''; + firstLinePrefix = firstLinePrefix || prefix; + lines.push(firstLinePrefix + '{'); + + const propertyNames = Object.keys(object); + propertyNames.sort(); + for (let i = 0; i < propertyNames.length; ++i) { + const name = propertyNames[i]; + if (!object.hasOwnProperty(name) || kOmitNames.has(name)) continue; + const prefixWithName = ' ' + prefix + name + ' : '; + let value = object[name]; + if (stabilizeNames && stabilizeNames.includes(name)) value = `<${typeof value}>`; + dumpValue(value, ' ' + prefix, prefixWithName); + } + lines.push(prefix + '}'); + } + + function dumpItems(object: any, prefix: string, firstLinePrefix: string) { + prefix = prefix || ''; + firstLinePrefix = firstLinePrefix || prefix; + lines.push(firstLinePrefix + '['); + for (let i = 0; i < object.length; ++i) { + dumpValue(object[i], ' ' + prefix, ' ' + prefix + '[' + i + '] : '); + } + lines.push(prefix + ']'); + } + + dumpValue(object, '', title || ''); + this._results.push(...lines); + return object; + } +} diff --git a/code/extensions/js-debug/src/test/infra/infra-initialize.txt b/code/extensions/js-debug/src/test/infra/infra-initialize.txt new file mode 100644 index 000000000000..6720da753f73 --- /dev/null +++ b/code/extensions/js-debug/src/test/infra/infra-initialize.txt @@ -0,0 +1,62 @@ +{ + additionalModuleColumns : [ + ] + completionTriggerCharacters : [ + [0] : . + [1] : [ + [2] : " + [3] : ' + ] + exceptionBreakpointFilters : [ + [0] : { + conditionDescription : error.name == "MyError" + default : false + description : Breaks on all throw errors, even if they're caught later. + filter : all + label : Caught Exceptions + supportsCondition : true + } + [1] : { + conditionDescription : error.name == "MyError" + default : false + description : Breaks only on errors or promise rejections that are not handled. + filter : uncaught + label : Uncaught Exceptions + supportsCondition : true + } + ] + supportTerminateDebuggee : true + supportedChecksumAlgorithms : [ + ] + supportsANSIStyling : true + supportsBreakpointLocationsRequest : true + supportsClipboardContext : true + supportsCompletionsRequest : true + supportsConditionalBreakpoints : true + supportsConfigurationDoneRequest : true + supportsDebuggerProperties : false + supportsDelayedStackTraceLoading : true + supportsEvaluateForHovers : true + supportsEvaluationOptions : false + supportsExceptionFilterOptions : true + supportsExceptionInfoRequest : true + supportsExceptionOptions : false + supportsFunctionBreakpoints : false + supportsGotoTargetsRequest : false + supportsHitConditionalBreakpoints : true + supportsLoadedSourcesRequest : true + supportsLogPoints : true + supportsModulesRequest : false + supportsReadMemoryRequest : true + supportsRestartFrame : true + supportsRestartRequest : true + supportsSetExpression : true + supportsSetSymbolOptions : false + supportsSetVariable : true + supportsStepBack : false + supportsStepInTargetsRequest : true + supportsTerminateRequest : false + supportsTerminateThreadsRequest : false + supportsValueFormattingOptions : true + supportsWriteMemoryRequest : true +} diff --git a/code/extensions/js-debug/src/test/infra/infra.ts b/code/extensions/js-debug/src/test/infra/infra.ts new file mode 100644 index 000000000000..232f0f40b87b --- /dev/null +++ b/code/extensions/js-debug/src/test/infra/infra.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { itIntegrates } from '../testIntegrationUtils'; + +describe('infra', () => { + itIntegrates('initialize', async ({ r }) => { + r.log(await r.initialize); + r.assertLog(); + }); + + it('imports win32 app container tokens', async () => { + if (process.platform === 'win32') { + await import('@vscode/win32-app-container-tokens'); // should not fail + } + }); +}); diff --git a/code/extensions/js-debug/src/test/logger.ts b/code/extensions/js-debug/src/test/logger.ts new file mode 100644 index 000000000000..5f7737215224 --- /dev/null +++ b/code/extensions/js-debug/src/test/logger.ts @@ -0,0 +1,249 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Dap from '../dap/api'; +import { Log } from './test'; + +interface ILogOptions { + depth?: number; + format?: Dap.ValueFormat; + params?: Partial; + logInternalInfo?: boolean; + omitProperties?: string[]; +} + +const kOmitProperties = ['[[ArrayBufferData]]']; + +/** + * Runs the 'walker' function over the tree of variables, iterating in a depth- + * first-search until walker returns false. + */ +export const walkVariables = async ( + dap: Dap.TestApi, + variable: Dap.Variable, + walker: (v: Dap.Variable, depth: number) => Promise | boolean, + depth = 0, + format?: Dap.ValueFormat, +): Promise => { + if (!(await walker(variable, depth))) { + return; + } + + if (variable.variablesReference === undefined) { + return; + } + + const hasHints = typeof variable.namedVariables === 'number' + || typeof variable.indexedVariables === 'number'; + if (hasHints) { + if (variable.namedVariables) { + const named = await dap.variables({ + variablesReference: variable.variablesReference, + filter: 'named', + format, + }); + for (const variable of named.variables) { + await walkVariables(dap, variable, walker, depth + 1, format); + } + } + if (variable.indexedVariables) { + const indexed = await dap.variables({ + variablesReference: variable.variablesReference, + filter: 'indexed', + start: 0, + count: variable.indexedVariables, + format, + }); + for (const variable of indexed.variables) { + await walkVariables(dap, variable, walker, depth + 1); + } + } + } else { + const all = await dap.variables({ + variablesReference: variable.variablesReference, + format, + }); + for (const variable of all.variables) { + await walkVariables(dap, variable, walker, depth + 1, format); + } + } +}; + +export class Logger { + private _dap: Dap.TestApi; + private _log: Log; + + constructor(dap: Dap.TestApi, log: Log) { + this._dap = dap; + this._log = log; + } + + logAsConsole(text: string) { + if (!text) return; + if (text.endsWith('\n')) text = text.substring(0, text.length - 1); + this._log(text); + } + + public logVariable( + rootVariable: Dap.Variable, + options: ILogOptions = {}, + baseIndent: string = '', + ): Promise { + return walkVariables( + this._dap, + rootVariable, + (variable, depth) => { + if ( + kOmitProperties.includes(variable.name) || options.omitProperties?.includes(variable.name) + ) { + return false; + } + + const name = variable.name ? `${variable.name}: ` : ''; + let value = (variable.presentationHint?.lazy ? '(...)' : variable.value) || ''; + if (value.endsWith('\n')) value = value.substring(0, value.length - 1); + const type = variable.type ? `type=${variable.type}` : ''; + const namedCount = variable.namedVariables ? ` named=${variable.namedVariables}` : ''; + const indexedCount = variable.indexedVariables + ? ` indexed=${variable.indexedVariables}` + : ''; + const indent = baseIndent + ' '.repeat(depth); + + const expanded = variable.variablesReference ? '> ' : ''; + let suffix = options.logInternalInfo ? `${type}${namedCount}${indexedCount}` : ''; + if (suffix) suffix = ' // ' + suffix; + let line = `${expanded}${name}${value}`; + if (line) { + if (line.includes('\n')) line = '\n' + line; + this.logAsConsole(`${indent}${line}${suffix}`); + } + + return depth < (options.depth ?? 1); + }, + undefined, + options.format, + ); + } + + async logOutput(params: Dap.OutputEventParams, options?: ILogOptions) { + if (params.group) { + this.logAsConsole(`# group: ${params.group}`); + } + + const prefix = `${params.category}> `; + if (params.output) { + this.logAsConsole(`${prefix}${params.output}`); + } + + if (params.variablesReference) { + const result = await this._dap.variables({ variablesReference: params.variablesReference }); + for (const variable of result.variables) await this.logVariable(variable, options, prefix); + } + } + + async logEvaluateResult( + result: Dap.EvaluateResult, + options?: ILogOptions, + ): Promise { + const variable = { ...result, name: 'result', value: result.result }; + await this.logVariable(variable, options); + return variable; + } + + async logStackTrace(threadId: number, withScopes = 0) { + const initial = await this._dap.stackTrace({ threadId }); + const stack = initial.stackFrames; + let totalFrames = initial.totalFrames || stack.length; + while (stack.length < totalFrames) { + const response = await this._dap.stackTrace({ + threadId, + startFrame: stack.length, + levels: Math.min(20, totalFrames - stack.length), + }); + stack.push(...response.stackFrames); + if (response.totalFrames) totalFrames = Math.min(totalFrames, response.totalFrames); + } + let emptyLine = withScopes > 0; + for (const frame of stack) { + if (emptyLine) this._log(''); + if (frame.presentationHint === 'label') { + this._log(`----${frame.name}----`); + emptyLine = false; + continue; + } + const origin = frame.source && frame.source.presentationHint === 'deemphasize' + ? ` ` + : ''; + this._log( + `${frame.name} @ ${ + frame.source ? frame.source.path! : 'unknown' + }:${frame.line}:${frame.column}${origin}`, + ); + if (withScopes-- <= 0) continue; + const scopes = await this._dap.scopes({ frameId: frame.id }); + if (typeof scopes === 'string') { + this._log(` scope error: ${scopes}`); + } else { + for (let i = 0; i < scopes.scopes.length; i++) { + const scope = scopes.scopes[i]; + if (scope.expensive) { + this._log(` scope #${i}: ${scope.name} [expensive]`); + continue; + } + await this.logVariable( + { + name: 'scope #' + i, + value: scope.name, + variablesReference: scope.variablesReference, + namedVariables: scope.namedVariables, + indexedVariables: scope.indexedVariables, + }, + {}, + ' ', + ); + } + } + } + + return stack; + } + + evaluateAndLog( + expression: string, + options?: ILogOptions, + context?: 'watch' | 'repl' | 'hover' | 'clipboard', + ): Promise; + evaluateAndLog( + expressions: string[], + options?: ILogOptions, + context?: 'watch' | 'repl' | 'hover' | 'clipboard', + ): Promise; + async evaluateAndLog( + expressions: string[] | string, + options: ILogOptions = {}, + context?: 'watch' | 'repl' | 'hover' | 'clipboard', + ): Promise { + if (typeof expressions === 'string') { + const result = await this._dap.evaluate({ + expression: expressions, + context, + ...options.params, + format: options.format, + }); + if (typeof result === 'string') { + this._log(`: ${result}`); + return { name: 'result', value: result, variablesReference: 0 }; + } + return await this.logEvaluateResult(result, options); + } + + for (const expression of expressions) { + this._log(`Evaluating: '${expression}'`); + const evaluation = this._dap.evaluate({ expression, context }); + await this.logOutput(await this._dap.once('output'), options); + await evaluation; + this._log(``); + } + } +} diff --git a/code/extensions/js-debug/src/test/node/lease-file.test.ts b/code/extensions/js-debug/src/test/node/lease-file.test.ts new file mode 100644 index 000000000000..047900f76027 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/lease-file.test.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { delay } from '../../common/promiseUtil'; +import { LeaseFile } from '../../targets/node/lease-file'; + +describe('node lease file', () => { + let file: LeaseFile; + beforeEach(() => (file = new LeaseFile())); + afterEach(() => file.dispose()); + + it('says the lease is not valid for missing files', async () => { + expect(LeaseFile.isValid('does-not-exist.txt')).to.be.false; + }); + + it('says the lease is not valid if too far in the past', async () => { + await file.touch(() => Date.now() - 5000); + expect(LeaseFile.isValid(file.path)).to.be.false; + }); + + it('says the lease is valid if recent', async () => { + await file.touch(() => Date.now()); + expect(LeaseFile.isValid(file.path)).to.be.true; + }); + + it('truncates and updates on touches', async () => { + await file.touch(() => Date.now() - 5000); + await file.touch(() => Date.now()); + expect(LeaseFile.isValid(file.path)).to.be.true; + }); + + it('disposes the file', async () => { + await file.touch(() => Date.now()); + await file.dispose(); + expect(LeaseFile.isValid(file.path)).to.be.false; + }); + + it('disposes the touch loop', async () => { + await file.startTouchLoop(); + await file.dispose(); + await delay(1200); + expect(LeaseFile.isValid(file.path)).to.be.false; + }); +}); diff --git a/code/extensions/js-debug/src/test/node/node-runtime-adjusts-to-compiles-file-if-it-exists.txt b/code/extensions/js-debug/src/test/node/node-runtime-adjusts-to-compiles-file-if-it-exists.txt new file mode 100644 index 000000000000..2ccf943e4b3f --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-adjusts-to-compiles-file-if-it-exists.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + @ ${workspaceFolder}/web/basic.ts:21:1 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-children-of-child-processes.txt b/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-children-of-child-processes.txt new file mode 100644 index 000000000000..69ccbbd46e2d --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-children-of-child-processes.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +global.foo @ ${fixturesDir}/child.js:1:19 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-to-cluster-processes.txt b/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-to-cluster-processes.txt new file mode 100644 index 000000000000..289747864508 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-to-cluster-processes.txt @@ -0,0 +1,6 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} diff --git a/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-to-existing-processes.txt b/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-to-existing-processes.txt new file mode 100644 index 000000000000..e2c53b89b1eb --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-attaching-attaches-to-existing-processes.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${fixturesDir}/test.js:1:21 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-attaching-restarts-if-requested.txt b/code/extensions/js-debug/src/test/node/node-runtime-attaching-restarts-if-requested.txt new file mode 100644 index 000000000000..906a87aa8d59 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-attaching-restarts-if-requested.txt @@ -0,0 +1,13 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${fixturesDir}/test.js:1:21 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-attaching-retries-attachment.txt b/code/extensions/js-debug/src/test/node/node-runtime-attaching-retries-attachment.txt new file mode 100644 index 000000000000..e2c53b89b1eb --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-attaching-retries-attachment.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${fixturesDir}/test.js:1:21 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-chakracore-string-value.txt b/code/extensions/js-debug/src/test/node/node-runtime-chakracore-string-value.txt new file mode 100644 index 000000000000..511d806d4738 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-chakracore-string-value.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +> result: 'hello world!' \ No newline at end of file diff --git a/code/extensions/js-debug/src/test/node/node-runtime-child-processes-debugs.txt b/code/extensions/js-debug/src/test/node/node-runtime-child-processes-debugs.txt new file mode 100644 index 000000000000..4d11cfcde0a3 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-child-processes-debugs.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +result: 'It works!' diff --git a/code/extensions/js-debug/src/test/node/node-runtime-debugs-child-processes.txt b/code/extensions/js-debug/src/test/node/node-runtime-debugs-child-processes.txt new file mode 100644 index 000000000000..d3702eb79f4e --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-debugs-child-processes.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +result: 'It works!' diff --git a/code/extensions/js-debug/src/test/node/node-runtime-debugs-worker-threads.txt b/code/extensions/js-debug/src/test/node/node-runtime-debugs-worker-threads.txt new file mode 100644 index 000000000000..35e6c862a3a7 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-debugs-worker-threads.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + @ ${fixturesDir}/test.js:6:5 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-inspect-flag-handling-does-not-break-with-inspect-flag.txt b/code/extensions/js-debug/src/test/node/node-runtime-inspect-flag-handling-does-not-break-with-inspect-flag.txt new file mode 100644 index 000000000000..d88a1dcf35f4 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-inspect-flag-handling-does-not-break-with-inspect-flag.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${fixturesDir}/test.js:2:1 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-inspect-flag-handling-treats-inspect-brk-as-stoponentry.txt b/code/extensions/js-debug/src/test/node/node-runtime-inspect-flag-handling-treats-inspect-brk-as-stoponentry.txt new file mode 100644 index 000000000000..c29ad17a0c6a --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-inspect-flag-handling-treats-inspect-brk-as-stoponentry.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : entry + threadId : +} + @ ${fixturesDir}/test.js:1:1 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-reads-the-envfile.txt b/code/extensions/js-debug/src/test/node/node-runtime-reads-the-envfile.txt new file mode 100644 index 000000000000..9c62478c34f0 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-reads-the-envfile.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +result: '{"a":"foo","b":"overwritten","c":"inherited"}' diff --git a/code/extensions/js-debug/src/test/node/node-runtime-scripts-with-http-urls.txt b/code/extensions/js-debug/src/test/node/node-runtime-scripts-with-http-urls.txt new file mode 100644 index 000000000000..be267769c20c --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-scripts-with-http-urls.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/urlSourcemap/index.ts:3:1 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-sets-arguments.txt b/code/extensions/js-debug/src/test/node/node-runtime-sets-arguments.txt new file mode 100644 index 000000000000..3da7ab43e26f --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-sets-arguments.txt @@ -0,0 +1,13 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +> result: (3) ['--some', 'very fancy', '--arguments'] + 0: '--some' + 1: 'very fancy' + 2: '--arguments' + length: 3 + > [[Prototype]]: Array(0) + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/node/node-runtime-sets-environment-variables.txt b/code/extensions/js-debug/src/test/node/node-runtime-sets-environment-variables.txt new file mode 100644 index 000000000000..50f509996b7d --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-sets-environment-variables.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +result: 'world' diff --git a/code/extensions/js-debug/src/test/node/node-runtime-sets-sourcemapoverrides-from-the-cwd.txt b/code/extensions/js-debug/src/test/node/node-runtime-sets-sourcemapoverrides-from-the-cwd.txt new file mode 100644 index 000000000000..a00e73292cd0 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-sets-sourcemapoverrides-from-the-cwd.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +./index.ts @ ${workspaceFolder}/simpleNode/simpleWebpack.ts:1:1 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-sets-the-cwd.txt b/code/extensions/js-debug/src/test/node/node-runtime-sets-the-cwd.txt new file mode 100644 index 000000000000..2b00ab66085e --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-sets-the-cwd.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +result: '${workspaceFolder}' diff --git a/code/extensions/js-debug/src/test/node/node-runtime-simple-script.txt b/code/extensions/js-debug/src/test/node/node-runtime-simple-script.txt new file mode 100644 index 000000000000..d88a1dcf35f4 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-simple-script.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${fixturesDir}/test.js:2:1 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-allows-inspect-brk-in-npm-scripts.txt b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-allows-inspect-brk-in-npm-scripts.txt new file mode 100644 index 000000000000..2b723d401022 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-allows-inspect-brk-in-npm-scripts.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +stdout> NODE_OPTIONS= undefined diff --git a/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-allows-simple-port-attachment.txt b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-allows-simple-port-attachment.txt new file mode 100644 index 000000000000..2b723d401022 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-allows-simple-port-attachment.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +stdout> NODE_OPTIONS= undefined diff --git a/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-terminates-when-inspector-closed.txt b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-terminates-when-inspector-closed.txt new file mode 100644 index 000000000000..2da2dbdb4011 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-terminates-when-inspector-closed.txt @@ -0,0 +1,8 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +{ +} diff --git a/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-terminates-when-process-killed.txt b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-terminates-when-process-killed.txt new file mode 100644 index 000000000000..2da2dbdb4011 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-simpleportattach-terminates-when-process-killed.txt @@ -0,0 +1,8 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +{ +} diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-caught.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-caught.txt new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-caught.txt @@ -0,0 +1 @@ + diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-caughtinusercode.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-caughtinusercode.txt new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-caughtinusercode.txt @@ -0,0 +1 @@ + diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-rethrown.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-rethrown.txt new file mode 100644 index 000000000000..024791bcb4e2 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-rethrown.txt @@ -0,0 +1,2 @@ +exports.rethrown @ ${workspaceFolder}/simpleNode/skippedScript.js:17:5 + @ ${workspaceFolder}/simpleNode/skipFiles.js:15:23 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-uncaught.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-uncaught.txt new file mode 100644 index 000000000000..39bb85648de3 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-with-delay-uncaught.txt @@ -0,0 +1,2 @@ +exports.uncaught @ ${workspaceFolder}/simpleNode/skippedScript.js:2:3 + @ ${workspaceFolder}/simpleNode/skipFiles.js:15:23 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-caught.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-caught.txt new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-caught.txt @@ -0,0 +1 @@ + diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-caughtinusercode.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-caughtinusercode.txt new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-caughtinusercode.txt @@ -0,0 +1 @@ + diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-rethrown.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-rethrown.txt new file mode 100644 index 000000000000..024791bcb4e2 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-rethrown.txt @@ -0,0 +1,2 @@ +exports.rethrown @ ${workspaceFolder}/simpleNode/skippedScript.js:17:5 + @ ${workspaceFolder}/simpleNode/skipFiles.js:15:23 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-uncaught.txt b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-uncaught.txt new file mode 100644 index 000000000000..39bb85648de3 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-skipfiles-without-delay-uncaught.txt @@ -0,0 +1,2 @@ +exports.uncaught @ ${workspaceFolder}/simpleNode/skippedScript.js:2:3 + @ ${workspaceFolder}/simpleNode/skipFiles.js:15:23 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-launches-and-infers-entry-from-args.txt b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-launches-and-infers-entry-from-args.txt new file mode 100644 index 000000000000..7241909fbcd0 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-launches-and-infers-entry-from-args.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : entry + threadId : +} + @ ${fixturesDir}/test.js:1:9 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-sets-an-explicit-stop-on-entry-point.txt b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-sets-an-explicit-stop-on-entry-point.txt new file mode 100644 index 000000000000..7241909fbcd0 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-sets-an-explicit-stop-on-entry-point.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : entry + threadId : +} + @ ${fixturesDir}/test.js:1:9 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-stops-with-a-breakpoint-elsewhere-515.txt b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-stops-with-a-breakpoint-elsewhere-515.txt new file mode 100644 index 000000000000..7241909fbcd0 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-stops-with-a-breakpoint-elsewhere-515.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : entry + threadId : +} + @ ${fixturesDir}/test.js:1:9 diff --git a/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-stops-with-a-program-provided.txt b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-stops-with-a-program-provided.txt new file mode 100644 index 000000000000..7241909fbcd0 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime-stoponentry-stops-with-a-program-provided.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : entry + threadId : +} + @ ${fixturesDir}/test.js:1:9 diff --git a/code/extensions/js-debug/src/test/node/node-runtime.test.ts b/code/extensions/js-debug/src/test/node/node-runtime.test.ts new file mode 100644 index 000000000000..b00a6d317940 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-runtime.test.ts @@ -0,0 +1,767 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { ChildProcess, spawn } from 'child_process'; +import { promises as fsPromises } from 'fs'; +import { dirname, join } from 'path'; +import { stub } from 'sinon'; +import { EnvironmentVars } from '../../common/environmentVars'; +import { findOpenPort } from '../../common/findOpenPort'; +import { once } from '../../common/objUtils'; +import { findInPath } from '../../common/pathUtils'; +import { delay } from '../../common/promiseUtil'; +import { StreamSplitter } from '../../common/streamSplitter'; +import { + INodeLaunchConfiguration, + nodeLaunchConfigDefaults, + OutputSource, +} from '../../configuration'; +import Dap from '../../dap/api'; +import { TerminalProgramLauncher } from '../../targets/node/terminalProgramLauncher'; +import { createFileTree } from '../createFileTree'; +import { ITestHandle, NodeTestHandle, testFixturesDir, testWorkspace } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('node runtime', () => { + async function waitForPause(p: ITestHandle) { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + return p.dap.continue({ threadId }); + } + + async function evaluate(handle: NodeTestHandle, expression: string) { + handle.load(); + const { threadId } = handle.log(await handle.dap.once('stopped')); + const stack = await handle.dap.stackTrace({ threadId }); + await handle.logger.evaluateAndLog(expression, { + params: { + frameId: stack.stackFrames[0].id, + }, + }); + + handle.assertLog(); + } + + function assertSkipFiles(expectedStacktrace: string) { + const stackframes = expectedStacktrace.trim().split('\n'); + expect(stackframes.length).to.be.greaterThan(0); + expect(stackframes[0]).to.not.contain(''); + for (let n = 1; n < stackframes.length; n++) { + expect(stackframes[n]).to.contain(''); + } + } + + describe('skipFiles', () => { + itIntegrates('skipFiles skip node internals', async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'simpleNode'); + const handle = await r.runScript(join(cwd, 'index.js'), { + skipFiles: ['/**'], + }); + await handle.dap.setBreakpoints({ + source: { path: join(cwd, 'index.js') }, + breakpoints: [{ line: 1, column: 1 }], + }); + + handle.load(); + const stoppedParams = await handle.dap.once('stopped'); + await delay(200); // need to pause test to let debouncer update scripts + await handle.logger.logStackTrace(stoppedParams.threadId!); + handle.assertLog({ customAssert: assertSkipFiles }); + }); + + for ( + const [name, useDelay] of [ + ['with delay', true], + ['without delay', false], + ] as const + ) { + describe(name, () => { + for (const fn of ['caughtInUserCode', 'uncaught', 'caught', 'rethrown']) { + itIntegrates(fn, async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'simpleNode'); + const handle = await r.runScript(join(cwd, 'skipFiles.js'), { + args: [useDelay ? '1000' : '0', fn], + skipFiles: ['**/skippedScript.js'], + }); + + await handle.dap.setExceptionBreakpoints({ + filters: ['all', 'uncaught'], + }); + + handle.dap.on('output', o => handle.logger.logOutput(o)); + handle.dap.on('stopped', async o => { + await handle.logger.logStackTrace(o.threadId!); + await handle.dap.continue({ threadId: o.threadId! }); + }); + + handle.load(); + + await handle.dap.once('terminated'); + handle.assertLog({ substring: true }); + }); + } + }); + } + }); + + itIntegrates('simple script', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': ['console.log("hello world");', 'debugger;'] }); + const handle = await r.runScript('test.js'); + handle.load(); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + itIntegrates('chakracore string value', async ({ r }) => { + if (process.platform !== 'win32') { + return; + } + + createFileTree(testFixturesDir, { + 'test.js': ['const message = "hello world!";', 'console.log(message);', 'debugger;'], + }); + const chakracore = join(testWorkspace, 'chakracore', 'ChakraCore.Debugger.Sample.exe'); + const port = await findOpenPort(); + const handle = await r.runScript('test.js', { + runtimeExecutable: chakracore, + runtimeArgs: ['--inspect-brk', '--port', `${port}`], + attachSimplePort: port, + continueOnAttach: true, + }); + + handle.load(); + const { threadId } = handle.log(await handle.dap.once('stopped')); + handle.dap.continue({ threadId }); + await handle.dap.once('stopped'); + + const stack = await handle.dap.stackTrace({ threadId }); + await handle.logger.evaluateAndLog('message', { + params: { + frameId: stack.stackFrames[0].id, + }, + }); + + handle.assertLog({ substring: true }); + }); + + itIntegrates('exits with child process launcher', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': '' }); + const handle = await r.runScript('test.js', { console: 'internalConsole' }); + handle.load(); + await handle.dap.once('terminated'); + }); + + if (process.env.ONLY_MINSPEC !== 'true') { + // not available on node 8 + itIntegrates('debugs worker threads', async ({ r }) => { + // note: __filename is broken up in the below script to + // avoid the esbuild plugin that replaces them in tests 🙈 + createFileTree(testFixturesDir, { + 'test.js': [ + 'const { Worker, isMainThread, workerData } = require("worker_threads");', + 'if (isMainThread) {', + ' new Worker(__f' + 'ilename, { workerData: { greet: "world" } });', + '} else {', + ' setInterval(() => {', + ' console.log("hello " + workerData.greet);', + ' }, 100);', + '}', + ], + }); + + const handle = await r.runScript('test.js'); + handle.load(); + + const worker = await r.worker(); + await worker.dap.setBreakpoints({ + source: { path: join(testFixturesDir, 'test.js') }, + breakpoints: [{ line: 6, column: 1 }], + }); + + worker.load(); + + await waitForPause(worker); + handle.assertLog({ substring: true }); + }); + } + + itIntegrates('exits with integrated terminal launcher', async ({ r }) => { + // We don't actually attach the DAP fully through vscode, so stub about + // the launch request. We just want to test that the lifecycle of a detached + // process is handled correctly. + const launch = stub(TerminalProgramLauncher.prototype, 'sendLaunchRequest'); + after(() => launch.restore()); + + let receivedRequest: Dap.RunInTerminalParams | undefined; + launch.callsFake((request: Dap.RunInTerminalParams) => { + receivedRequest = request; + spawn(request.args[0], request.args.slice(1), { + cwd: request.cwd, + env: { ...process.env, ...request.env }, + }); + + return Promise.resolve({}); + }); + + createFileTree(testFixturesDir, { 'test.js': '' }); + const handle = await r.runScript('test.js', { + console: 'integratedTerminal', + cwd: testFixturesDir, + env: { myEnv: 'foo' }, + }); + handle.load(); + await handle.dap.once('terminated'); + expect(receivedRequest).to.containSubset({ + title: 'Test Case', + kind: 'integrated', + cwd: testFixturesDir, + env: { myEnv: 'foo' }, + }); + }); + + itIntegrates('adjusts to compiles file if it exists', async ({ r }) => { + await r.initialize; + + const handle = await r.runScript(join(testWorkspace, 'web/basic.ts')); + await handle.dap.setBreakpoints({ + source: { path: handle.workspacePath('web/basic.ts') }, + breakpoints: [{ line: 21, column: 0 }], + }); + handle.load(); + + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + describe('inspect flag handling', () => { + itIntegrates('does not break with inspect flag', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ['console.log("hello world");', 'debugger;'], + }); + const handle = await r.runScript('test.js', { + runtimeArgs: ['--inspect'], + }); + handle.load(); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + itIntegrates('treats inspect-brk as stopOnEntry', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': ['console.log("hello world");'] }); + const handle = await r.runScript('test.js', { + cwd: testFixturesDir, + runtimeArgs: ['--inspect-brk'], + }); + handle.load(); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + }); + + describe('stopOnEntry', () => { + beforeEach(() => + createFileTree(testFixturesDir, { + 'test.js': ['let i = 0;', 'i++;', 'i++;'], + 'bar.js': 'require("./test")', + }) + ); + + itIntegrates('stops with a breakpoint elsewhere (#515)', async ({ r }) => { + const handle = await r.runScript('test.js', { + cwd: testFixturesDir, + stopOnEntry: true, + }); + + await handle.dap.setBreakpoints({ + source: { path: join(testFixturesDir, 'test.js') }, + breakpoints: [{ line: 3, column: 1 }], + }); + + handle.load(); + await waitForPause(handle); + r.assertLog({ substring: true }); + }); + + itIntegrates('stops with a program provided', async ({ r }) => { + const handle = await r.runScript('test.js', { + cwd: testFixturesDir, + stopOnEntry: true, + }); + + handle.load(); + await waitForPause(handle); + r.assertLog({ substring: true }); + }); + + itIntegrates('launches and infers entry from args', async ({ r }) => { + const handle = await r.runScript('test.js', { + cwd: testFixturesDir, + args: ['--max-old-space-size=1024', 'test.js', '--not-a-file'], + program: undefined, + stopOnEntry: true, + }); + + handle.load(); + await waitForPause(handle); + r.assertLog({ substring: true }); + }); + + itIntegrates('sets an explicit stop on entry point', async ({ r }) => { + const handle = await r.runScript('bar.js', { + cwd: testFixturesDir, + stopOnEntry: join(testFixturesDir, 'test.js'), + }); + + handle.load(); + await waitForPause(handle); + r.assertLog({ substring: true }); + }); + }); + + describe('attaching', () => { + let child: ChildProcess | undefined; + + afterEach(() => { + if (child) { + child.kill(); + } + }); + + itIntegrates('attaches to existing processes', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ['setInterval(() => { debugger; }, 500)'], + }); + + const port = await findOpenPort(); + child = spawn('node', [`--inspect=${port}`, join(testFixturesDir, 'test')]); + await delay(500); // give it a moment to boot + const handle = await r.attachNode(child.pid!, { port }); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + // todo(connor4312): I'm having a really hard time getting this to pass. I + // think there might be funky with out test setup, works fine running manually. + itIntegrates.skip('continueOnAttach', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ['console.log("");', 'debugger;'], + }); + + const port = await findOpenPort(); + child = spawn('node', [`--inspect-brk=${port}`, join(testFixturesDir, 'test')]); + const handle = await r.attachNode(child.pid!, { continueOnAttach: true, port }); + await waitForPause(handle); // pauses on 2nd line, not 1st + handle.assertLog({ substring: true }); + }); + + itIntegrates('retries attachment', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ['setInterval(() => { debugger; }, 500)'], + }); + + const port = await findOpenPort(); + const handleProm = r.attachNode(0, { port }); + await delay(500); // give it a moment to start trying to attach + child = spawn('node', [`--inspect=${port}`, join(testFixturesDir, 'test')]); + const handle = await handleProm; + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + itIntegrates('attaches children of child processes', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ` + const { spawn } = require('child_process'); + setInterval(() => spawn('node', ['child'], { cwd: __dir${''}name }), 500); + `, + 'child.js': '(function foo() { debugger; })();', + }); + + const port = await findOpenPort(); + child = spawn('node', [`--inspect=${port}`, join(testFixturesDir, 'test')]); + await delay(500); // give it a moment to boot + const handle = await r.attachNode(child.pid!, { port }); + handle.load(); + + const worker = await r.worker(); + worker.load(); + + await waitForPause(worker); + worker.assertLog({ substring: true }); + }); + + itIntegrates('attaches to cluster processes', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ` + const cluster = require('cluster'); + if (cluster.isMaster) { + cluster.fork(); + } else { + setInterval(() => { debugger; }, 500); + } + `, + }); + + const port = await findOpenPort(); + child = spawn('node', [`--inspect=${port}`, join(testFixturesDir, 'test')]); + await delay(500); // give it a moment to boot + const handle = await r.attachNode(child.pid!, { port }); + handle.load(); + + const worker = await r.worker(); + worker.load(); + + await waitForPause(worker); + worker.assertLog({ substring: true }); + }); + + itIntegrates('restarts if requested', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ['setInterval(() => { debugger; }, 100)'], + }); + + const port = await findOpenPort(); + child = spawn('node', [`--inspect=${port}`, join(testFixturesDir, 'test')]); + const handle = await r.attachNode(0, { port, restart: true }); + + handle.log(await handle.dap.once('stopped')); + await handle.dap.evaluate({ expression: 'process.exit(0)' }); + + child = spawn('node', [`--inspect=${port}`, join(testFixturesDir, 'test')]); + const reconnect = await r.waitForTopLevel(); + reconnect.load(); + + await waitForPause(reconnect); + handle.assertLog({ substring: true }); + }); + + itIntegrates('does not restart if killed', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': ['setInterval(() => { debugger; }, 100)'], + }); + + const port = await findOpenPort(); + child = spawn('node', [`--inspect=${port}`, join(testFixturesDir, 'test')], { + stdio: 'pipe', + }); + const lines: string[] = []; + child.stderr?.pipe(new StreamSplitter('\n')).on( + 'data', + line => lines.push(line.toString()), + ); + + const handle = await r.attachNode(0, { port, restart: true }); + await handle.dap.once('stopped'); + await handle.dap.disconnect({}); + await r.rootDap().disconnect({}); + + await delay(1000); + expect(lines.filter(l => l.includes('Debugger attached'))).to.have.lengthOf(1); + }); + }); + + describe('child processes', () => { + beforeEach(() => + createFileTree(testFixturesDir, { + 'test.js': ` + const cp = require('child_process'); + const path = require('path'); + cp.fork(path.join(__dir${''}name, 'child.js')); + `, + 'child.js': ` + const foo = 'It works!'; + debugger; + `, + }) + ); + + itIntegrates('debugs', async ({ r }) => { + const handle = await r.runScript('test.js'); + handle.load(); + + const worker = await r.worker(); + worker.load(); + + const { threadId } = worker.log(await worker.dap.once('stopped')); + const stack = await worker.dap.stackTrace({ threadId }); + await worker.logger.evaluateAndLog('foo', { + params: { + frameId: stack.stackFrames[0].id, + }, + }); + + worker.assertLog(); + }); + + itIntegrates('does not debug if auto attach off', async ({ r }) => { + const handle = await r.runScript('test.js', { autoAttachChildProcesses: false }); + handle.load(); + + const result = await Promise.race([ + r.worker(), + new Promise(r => setTimeout(() => r('ok'), 1000)), + ]); + + expect(result).to.equal('ok'); + }); + }); + + itIntegrates('sets arguments', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': 'debugger' }); + const handle = await r.runScript('test.js', { + args: ['--some', 'very fancy', '--arguments'], + }); + + await evaluate(handle, 'process.argv.slice(2)'); + }); + + itIntegrates('sets the cwd', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': 'debugger' }); + const handle = await r.runScript('test.js', { + cwd: testWorkspace, + }); + + await evaluate(handle, 'process.cwd()'); + }); + + itIntegrates('sets sourceMapOverrides from the cwd', async ({ r }) => { + const handle = await r.runScript(join(testWorkspace, 'simpleNode', 'simpleWebpack.js'), { + cwd: join(testWorkspace, 'simpleNode'), + }); + + handle.load(); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + itIntegrates('sets environment variables', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': 'debugger' }); + const handle = await r.runScript('test.js', { + env: { + HELLO: 'world', + }, + }); + + await evaluate(handle, 'process.env.HELLO'); + }); + + itIntegrates('sets environment variables', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': 'debugger' }); + const handle = await r.runScript('test.js', { + env: { + HELLO: 'world', + }, + }); + + await evaluate(handle, 'process.env.HELLO'); + }); + + itIntegrates('reads the envfile', async ({ r }) => { + createFileTree(testFixturesDir, { + 'test.js': 'debugger;', + vars: ['A=foo', 'B=bar'], + }); + + EnvironmentVars.processEnv.forget(); + const previousC = process.env.C; + process.env.C = 'inherited'; + + const handle = await r.runScript('test.js', { + envFile: join(testFixturesDir, 'vars'), + env: { + B: 'overwritten', + }, + }); + + await evaluate( + handle, + 'JSON.stringify({ a: process.env.A, b: process.env.B, c: process.env.C })', + ); + + process.env.C = previousC; + EnvironmentVars.processEnv.forget(); + }); + + itIntegrates('writes errors if runtime executable not found', async ({ r }) => { + await r.initialize; + const result = await r.rootDap().launch({ + ...nodeLaunchConfigDefaults, + cwd: dirname(testFixturesDir), + program: join(testFixturesDir, 'test.js'), + rootPath: testWorkspace, + runtimeExecutable: 'does-not-exist', + __workspaceFolder: testFixturesDir, + } as INodeLaunchConfiguration); + + expect(result).to.include('Can\'t find Node.js binary "does-not-exist"'); + }); + + itIntegrates('scripts with http urls', async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'web', 'urlSourcemap'); + const handle = await r.runScript(join(cwd, 'index.js'), { + cwd: testWorkspace, + skipFiles: ['/**'], + sourceMapPathOverrides: { 'http://localhost:8001/*': `${testWorkspace}/web/*` }, + }); + handle.load(); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + itIntegrates('gets performance information', async ({ r }) => { + createFileTree(testFixturesDir, { 'test.js': 'setInterval(() => {}, 1000)' }); + const handle = await r.runScript('test.js'); + await handle.load(); + const res = await handle.dap.getPerformance({}); + expect(res.error).to.be.undefined; + expect(res.metrics).to.not.be.empty; + }); + + describe('simplePortAttach', () => { + const npm = once(async () => { + const npmPath = await findInPath(fsPromises, 'npm', process.env); + if (!npmPath) { + throw new Error('npm not on path'); + } + + return npmPath; + }); + + itIntegrates('allows inspect-brk in npm scripts', async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'simpleNode'); + const handle = await r.runScript('', { + program: undefined, + cwd, + runtimeExecutable: await npm(), + runtimeArgs: ['run', 'startWithBrk'], + port: 29204, + }); + + const optionsOut = handle.dap.once('output', o => o.output.includes('NODE_OPTIONS')); + handle.load(); + const { threadId } = handle.log(await handle.dap.once('stopped')); + handle.dap.continue({ threadId }); + handle.logger.logOutput(await optionsOut); + handle.assertLog({ substring: true }); + }); + + itIntegrates('uses bootloader for normal npm scripts', async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'simpleNode'); + r.onSessionCreated(t => t.load()); + const handle = await r.runScript('', { + program: undefined, + cwd, + runtimeExecutable: await npm(), + runtimeArgs: ['run', 'startWithoutBrk'], + port: 29204, + }); + handle.load(); + + const worker = await r.worker(); + const optionsOut = worker.dap.once('output', o => o.output.includes('NODE_OPTIONS')); + handle.logger.logOutput(await optionsOut); + handle.assertLog({ customAssert: l => expect(l).to.contain('NODE_OPTIONS= --require') }); + }); + + itIntegrates('allows simple port attachment', async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'simpleNode'); + const port = await findOpenPort(); + const handle = await r.runScript(join(cwd, 'logNodeOptions'), { + runtimeArgs: [`--inspect-brk=${port}`], + attachSimplePort: port, + }); + handle.load(); + + const optionsOut = handle.dap.once('output', o => o.output.includes('NODE_OPTIONS')); + const { threadId } = handle.log(await handle.dap.once('stopped')); + handle.dap.continue({ threadId }); + handle.logger.logOutput(await optionsOut); + handle.assertLog({ substring: true }); + }); + + itIntegrates('terminates when inspector closed', async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'simpleNode'); + const port = await findOpenPort(); + const handle = await r.runScript(join(cwd, 'debuggerStmt'), { + runtimeArgs: [`--inspect-brk=${port}`], + attachSimplePort: port, + }); + handle.load(); + + const { threadId } = handle.log(await handle.dap.once('stopped')); + const stack = await handle.dap.stackTrace({ threadId }); + handle.dap.evaluate({ + expression: 'require("inspector").close()', + frameId: stack.stackFrames[0].id, + }); + handle.log(await handle.dap.once('terminated')); + handle.assertLog({ substring: true }); + }); + + itIntegrates('terminates when process killed', async ({ r }) => { + await r.initialize; + const cwd = join(testWorkspace, 'simpleNode'); + const port = await findOpenPort(); + const handle = await r.runScript(join(cwd, 'debuggerStmt'), { + runtimeArgs: [`--inspect-brk=${port}`], + attachSimplePort: port, + }); + handle.load(); + + handle.log(await handle.dap.once('stopped')); + handle.dap.evaluate({ expression: 'process.exit(1)' }); + handle.log(await handle.dap.once('terminated')); + handle.assertLog({ substring: true }); + }); + }); + + describe('etx', () => { + itIntegrates('stdio without etx', async ({ r }) => { + await r.initialize; + + createFileTree(testFixturesDir, { + 'test.js': ['process.stdout.write("hello world!");', 'debugger;'], + }); + const handle = await r.runScript('test.js', { outputCapture: OutputSource.Stdio }); + handle.load(); + let logs = ''; + r.rootDap().on('output', p => (logs += p.output)); + await handle.dap.once('stopped'); + expect(logs).to.deep.equal('hello world!'); + }); + + itIntegrates('stdio with etx', async ({ r }) => { + await r.initialize; + + createFileTree(testFixturesDir, { + 'test.js': [ + 'process.stdout.write("etx\u0003start");', + 'process.stdout.write("well defined\u0003");', + 'process.stdout.write("chunks\u0003\u0003now!\u0003");', + ], + }); + const handle = await r.runScript('test.js', { outputCapture: OutputSource.Stdio }); + handle.load(); + const logs: string[] = []; + r.rootDap().on('output', p => logs.push(p.output)); + await handle.dap.once('terminated'); + + expect(logs.slice(0, 5)).to.deep.equal([ + 'etx\n', + 'startwell defined\n', + 'chunks\n', + '\n', + 'now!\n', + ]); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/node/node-source-path-resolver.test.ts b/code/extensions/js-debug/src/test/node/node-source-path-resolver.test.ts new file mode 100644 index 000000000000..469c3d93d70b --- /dev/null +++ b/code/extensions/js-debug/src/test/node/node-source-path-resolver.test.ts @@ -0,0 +1,307 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { promises as fsPromises } from 'fs'; +import { join, resolve } from 'path'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { Logger } from '../../common/logging/logger'; +import { fixDriveLetter } from '../../common/pathUtils'; +import { resetCaseSensitivePaths, setCaseSensitivePaths } from '../../common/urlUtils'; +import { NodeSourcePathResolver } from '../../targets/node/nodeSourcePathResolver'; + +const fsUtils = new LocalFsUtils(fsPromises); + +describe('node source path resolver', () => { + describe('url to path', () => { + const defaultOptions = { + workspaceFolder: 'file:///', + resolveSourceMapLocations: null, + basePath: __dirname, + remoteRoot: null, + localRoot: null, + sourceMapOverrides: { 'webpack:///*': `${__dirname}/*` }, + }; + + it('resolves absolute', async () => { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + defaultOptions, + Logger.null, + ); + expect(await r.urlToAbsolutePath({ url: 'file:///src/index.js' })).to.equal(fixDriveLetter( + resolve('/src/index.js'), + )); + }); + + it('escapes regex parts segments', async () => { + if (process.platform === 'win32') { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + { + ...defaultOptions, + workspaceFolder: 'C:\\some\\workspa*ce\\folder', + basePath: 'C:\\some\\workspa*ce\\folder', + resolveSourceMapLocations: [ + 'C:\\some\\workspa*ce\\folder/**', + 'C:\\some\\workspa*ce\\folder/../**', + 'C:\\some\\workspa*ce\\folder/../foo/**', + ], + }, + Logger.null, + ); + expect((r as unknown as Record).resolvePatterns).to.deep.equal([ + 'C:/some/workspa\\*ce/folder/**', + 'C:/some/workspa\\*ce/**', + 'C:/some/workspa\\*ce/foo/**', + ]); + } + }); + + it('fixes regex escape issue #1554', async () => { + if (process.platform === 'win32') { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + { + ...defaultOptions, + workspaceFolder: 'C:\\Users\\Segev\\prj\\swimm\\ide\\extensions\\vscode', + basePath: 'C:\\Users\\Segev\\prj\\swimm\\ide\\extensions\\vscode', + resolveSourceMapLocations: [ + 'C:\\Users\\Segev\\prj\\swimm\\ide\\extensions\\vscode/**', + 'C:\\Users\\Segev\\prj\\swimm\\ide\\extensions\\vscode/../../../packages/shared/dist/**', + 'C:\\Users\\Segev\\prj\\swimm\\ide\\extensions\\vscode/../../../packages/swimmagic/dist/**', + 'C:\\Users\\Segev\\prj\\swimm\\ide\\extensions\\vscode/../../../packages/editor/dist/**', + 'C:\\Users\\Segev\\prj\\swimm\\ide\\extensions\\vscode/../../server/dist/**', + '!**/node_modules/**', + ], + }, + Logger.null, + ); + expect( + r.shouldResolveSourceMap({ + compiledPath: 'c:\\Users\\Segev\\prj\\swimm\\ide\\server\\dist\\app.js', + sourceMapUrl: 'file:///c:/Users/Segev/prj/swimm/ide/server/dist/app.js.map', + }), + ).to.be.true; + } + }); + + it('avoids bad matches in relative rebasd #2091', async () => { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + { + ...defaultOptions, + workspaceFolder: 'C:\\some\\workspa*ce\\folder', + basePath: 'C:\\some\\workspa*ce\\folder', + resolveSourceMapLocations: [ + '**/*o*/**', + ], + }, + Logger.null, + ); + expect( + r.shouldResolveSourceMap({ + compiledPath: 'external:///app.js', + sourceMapUrl: 'external:///app.js.map', + }), + ).to.be.false; + }); + + it('resolves unc paths', async () => { + if (process.platform !== 'win32') { + return; + } + + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + defaultOptions, + Logger.null, + ); + expect( + await r.urlToAbsolutePath({ + url: 'file:////mac/Home/Github/js-debug-demos/node/main.js', + }), + ).to.equal(resolve('\\\\mac\\Home\\Github\\js-debug-demos\\node\\main.js')); + }); + + it('normalizes roots (win -> posix) ', async () => { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + { + ...defaultOptions, + remoteRoot: 'C:\\Source', + localRoot: '/dev/src', + }, + Logger.null, + ); + + expect(await r.urlToAbsolutePath({ url: 'file:///c:/source/foo/bar.js' })).to.equal( + '/dev/src/foo/bar.js', + ); + }); + + it('normalizes roots (posix -> win) ', async () => { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + { + ...defaultOptions, + remoteRoot: '/dev/src', + localRoot: 'C:\\Source', + }, + Logger.null, + ); + + expect(await r.urlToAbsolutePath({ url: 'file:///dev/src/foo/bar.js' })).to.equal( + 'c:\\Source\\foo\\bar.js', + ); + }); + + it('places relative paths in node_internals', async () => { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + defaultOptions, + Logger.null, + ); + + expect( + await r.urlToAbsolutePath({ + url: 'internal.js', + }), + ).to.equal('/internal.js'); + }); + + it('applies source map overrides', async () => { + const r = new NodeSourcePathResolver( + fsUtils, + undefined, + defaultOptions, + Logger.null, + ); + + expect( + await r.urlToAbsolutePath({ + url: 'webpack:///hello.js', + map: { sourceRoot: '', metadata: { compiledPath: 'hello.js' } } as any, + }), + ).to.equal(fixDriveLetter(join(__dirname, 'hello.js'))); + }); + + it('loads local node internals (#823)', async () => { + const r = new NodeSourcePathResolver(fsUtils, undefined, defaultOptions, Logger.null); + + expect(await r.urlToAbsolutePath({ url: 'node:url' })).to.equal( + join(__dirname, 'lib/url.js'), + ); + expect(await r.urlToAbsolutePath({ url: 'node:internal/url.js' })).to.equal( + join(__dirname, 'lib/internal/url.js'), + ); + }); + + it('applies rebase to file URIs (#2122)', async () => { + const resolver = new NodeSourcePathResolver(fsUtils, undefined, { + ...defaultOptions, + localRoot: '/biz', + remoteRoot: '/foo', + }, Logger.null); + + const result = await resolver.urlToAbsolutePath({ + url: 'file:///foo/bar/baz.ts', + map: { + metadata: { + sourceMapUrl: 'file:///foo/bar/baz/my.map.js', + compiledPath: 'file:///foo/bar/baz/my.map.js', + }, + sourceRoot: '', + } as any, + }); + + expect(result).to.equal('/biz/bar/baz.ts'); + }); + + describe('source map filtering', () => { + const testTable = { + 'matches paths': { + locs: ['/foo/bar/**', '!**/node_modules/**'], + map: 'file:///foo/bar/baz/my.map.js', + ok: true, + }, + 'is case sensitive on unix': { + locs: ['/foo/BAR/**', '!**/node_modules/**'], + map: 'file:////bar/my.map.js', + ok: false, + caseSensitive: true, + }, + 'does not match paths outside of locations': { + locs: ['/foo/bar/**', '!**/node_modules/**'], + map: 'file:////bar/my.map.js', + ok: false, + }, + 'applies negations': { + locs: ['/foo/bar/**', '!**/node_modules/**'], + map: 'file:///foo/bar/node_modules/my.map.js', + ok: false, + }, + 'matches win32 paths, case insensitive': { + locs: ['c:\\foo\\BAR\\**', '!**\\node_modules\\**'], + map: 'file:///c:/foo/bar/BAZ/my.map.js', + ok: true, + caseSensitive: false, + }, + 'applies win32 negations': { + locs: ['c:\\foo\\bar\\**', '!**\\node_modules\\**'], + map: 'file:///c:/foo/bar/node_modules/my.map.js', + ok: false, + }, + 'works for http urls, case insensitive': { + locs: ['https://EXAMPLE.com/**'], + map: 'https://example.COM/my.map.js', + ok: true, + }, + }; + + afterEach(() => resetCaseSensitivePaths()); + + for (const key of Object.keys(testTable)) { + const tcase = testTable[key]; + const { locs, map, ok } = tcase; + const caseSensitive = 'caseSensitive' in tcase && tcase.caseSensitive; + + it(key, async () => { + setCaseSensitivePaths(caseSensitive); + + const resolver = new NodeSourcePathResolver( + fsUtils, + undefined, + { + ...defaultOptions, + resolveSourceMapLocations: locs, + }, + Logger.null, + ); + + const result = await resolver.urlToAbsolutePath({ + url: 'webpack:///hello.js', + map: { metadata: { sourceMapUrl: map, compiledPath: map }, sourceRoot: '' } as any, + }); + + if (ok) { + expect(result && fixDriveLetter(result)).to.equal( + fixDriveLetter(join(__dirname, 'hello.js')), + ); + } else { + expect(result).to.be.undefined; + } + }); + } + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/node/process-tree.test.ts b/code/extensions/js-debug/src/test/node/process-tree.test.ts new file mode 100644 index 000000000000..ee6313f29028 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/process-tree.test.ts @@ -0,0 +1,119 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { EventEmitter } from 'events'; +import { promises as fsPromises } from 'fs'; +import { stub } from 'sinon'; +import { ReadableStreamBuffer } from 'stream-buffers'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { DarwinProcessTree } from '../../ui/processTree/darwinProcessTree'; +import { PosixProcessTree } from '../../ui/processTree/posixProcessTree'; +import { IProcess, IProcessTree, processTree } from '../../ui/processTree/processTree'; + +const fakeChildProcess = (stdoutData: string) => { + const ee: any = new EventEmitter(); + ee.stderr = new ReadableStreamBuffer(); + ee.stderr.stop(); + ee.stdout = new ReadableStreamBuffer({ frequency: 2, chunkSize: 32 }); + ee.stdout.put(stdoutData); + ee.stdout.stop(); + ee.stdout.once('end', () => setTimeout(() => ee.emit('close', 0), 1)); + + return ee; +}; + +const assertParses = async (tree: IProcessTree, input: string, expected: IProcess[]) => { + const stubbed = stub(tree, 'createProcess' as any).returns(fakeChildProcess(input)); + try { + const result = await tree.lookup((entry, acc) => [...acc, entry], []); + expect(result).to.deep.equal(expected); + } finally { + stubbed.restore(); + } +}; + +// These tests are a handful of samples taken by manually running the process +// tree on given platforms. +describe('process tree', () => { + it('gives some output for the current platform', async () => { + // sanity check + const data = await processTree.lookup((entry, acc) => [...acc, entry], []); + expect(data.length).to.be.greaterThan(0); + expect(data[0].pid).to.be.a('number'); + expect(data[0].ppid).to.be.a('number'); + expect(data[0].command).to.match(/./); // not empty string + }); + + if (process.platform !== 'win32') { + it('gets the working directory', async () => { + const currentWd = await processTree.getWorkingDirectory(process.pid); + expect(currentWd).to.equal(process.cwd()); + }); + } + + it('works for darwin', async () => { + await assertParses( + new DarwinProcessTree(new LocalFsUtils(fsPromises)), + ' PID PPID BINARY COMMAND\n 380 1 /usr/sbin/cfpref /usr/sbin/cfprefsd agent\n 381 1 /usr/libexec/Use /usr/libexec/UserEventAgent (Aqua)\n 383 1 /usr/sbin/distno /usr/sbin/distnoted agent\n 384 1 /usr/libexec/USB /usr/libexec/USBAgent\n 387 1 /System/Library/ /System/Library/Frameworks/CoreTelephony.framework/Support/CommCenter -L\n 389 1 /usr/libexec/lsd /usr/libexec/lsd\n 390 1 /usr/libexec/tru /usr/libexec/trustd --agent\n 391 1 /usr/libexec/sec /usr/libexec/secd\n 392 2 /System/Library/ /System/Library/PrivateFrameworks/CloudKitDaemon.framework/Support/cloudd', + [ + { pid: 380, ppid: 1, command: 'usr/sbin/cfpref', args: '/usr/sbin/cfprefsd agent' }, + { + pid: 381, + ppid: 1, + command: 'usr/libexec/Use', + args: '/usr/libexec/UserEventAgent (Aqua)', + }, + { pid: 383, ppid: 1, command: 'usr/sbin/distno', args: '/usr/sbin/distnoted agent' }, + { pid: 384, ppid: 1, command: 'usr/libexec/USB', args: '/usr/libexec/USBAgent' }, + { + pid: 387, + ppid: 1, + command: 'System/Library/', + args: '/System/Library/Frameworks/CoreTelephony.framework/Support/CommCenter -L', + }, + { pid: 389, ppid: 1, command: 'usr/libexec/lsd', args: '/usr/libexec/lsd' }, + { pid: 390, ppid: 1, command: 'usr/libexec/tru', args: '/usr/libexec/trustd --agent' }, + { pid: 391, ppid: 1, command: 'usr/libexec/sec', args: '/usr/libexec/secd' }, + { + pid: 392, + ppid: 2, + command: 'System/Library/', + args: '/System/Library/PrivateFrameworks/CloudKitDaemon.framework/Support/cloudd', + }, + ], + ); + }); + + it('works for posix', async () => { + await assertParses( + new PosixProcessTree(new LocalFsUtils(fsPromises)), + ' PID PPID BINARY COMMAND\n 351 1 systemd /lib/systemd/systemd --user\n 352 351 (sd-pam) (sd-pam)\n 540 1 sh sh /home/connor/.vscode-server-insiders/bin/bbf00d8ea6aa7e825ca3393364d746fe401d3299/server.sh --host=127.0.0.1 --enable-remote-auto-shutdown --port=0\n 548 540 node /home/connor/.vscode-server-insiders/bin/bbf00d8ea6aa7e825ca3393364d746fe401d3299/node /home/connor/.vscode-server-insiders/bin/bbf00d8ea6aa7e825ca3393364d746fe401d3299/out/vs/server/main.js --host=127.0.0.1 --enable-remote-auto-shutdown --port=0\n 6557 6434 sshd sshd: connor@notty\n 6558 6557 bash bash\n 7281 7199 sshd sshd: connor@pts/0\n 7282 7281 bash -bash\n 9880 99219 bash /bin/bash', + [ + { pid: 351, ppid: 1, command: '/lib/systemd/systemd', args: '--user' }, + { pid: 352, ppid: 351, command: '(sd-pam)', args: '' }, + { + pid: 540, + ppid: 1, + command: 'sh', + args: + '/home/connor/.vscode-server-insiders/bin/bbf00d8ea6aa7e825ca3393364d746fe401d3299/server.sh --host=127.0.0.1 --enable-remote-auto-shutdown --port=0', + }, + { + pid: 548, + ppid: 540, + command: + '/home/connor/.vscode-server-insiders/bin/bbf00d8ea6aa7e825ca3393364d746fe401d3299/node', + args: + '/home/connor/.vscode-server-insiders/bin/bbf00d8ea6aa7e825ca3393364d746fe401d3299/out/vs/server/main.js --host=127.0.0.1 --enable-remote-auto-shutdown --port=0', + }, + { pid: 6557, ppid: 6434, command: 'sshd:', args: 'connor@notty' }, + { pid: 6558, ppid: 6557, command: 'bash', args: '' }, + { pid: 7281, ppid: 7199, command: 'sshd:', args: 'connor@pts/0' }, + { pid: 7282, ppid: 7281, command: '-bash', args: '' }, + { pid: 9880, ppid: 99219, command: '/bin/bash', args: '' }, + ], + ); + }); +}); diff --git a/code/extensions/js-debug/src/test/node/runtimeVersion.test.ts b/code/extensions/js-debug/src/test/node/runtimeVersion.test.ts new file mode 100644 index 000000000000..9db842794469 --- /dev/null +++ b/code/extensions/js-debug/src/test/node/runtimeVersion.test.ts @@ -0,0 +1,282 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { promises as fsPromises } from 'fs'; +import * as path from 'path'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { ProtocolError } from '../../dap/protocolError'; +import { INvmResolver, NvmResolver } from '../../targets/node/nvmResolver'; +import { createFileTree } from '../createFileTree'; +import { testFixturesDir, testWorkspace } from '../test'; + +const fsUtils = new LocalFsUtils(fsPromises); + +describe('runtimeVersion', () => { + let resolver: INvmResolver; + + it('fails if no nvm/s present', async () => { + resolver = new NvmResolver(fsUtils, {}, 'x64', 'linux', testWorkspace); + await expect(resolver.resolveNvmVersionPath('13')).to.eventually.be.rejectedWith( + ProtocolError, + /requires Node.js version manager/, + ); + }); + + describe('fall throughs', () => { + beforeEach(() => { + createFileTree(testFixturesDir, { + 'nvs/node/13.12.0/x64/bin/node': '', + 'nvs/node/13.11.0/x86/bin/node': '', + 'nvm/versions/node/v13.11.0/bin/node': '', + 'fnm/node-versions/v13.10.0/installation/bin/node': '', + }); + + resolver = new NvmResolver( + fsUtils, + { + NVS_HOME: path.join(testFixturesDir, 'nvs'), + NVM_DIR: path.join(testFixturesDir, 'nvm'), + FNM_DIR: path.join(testFixturesDir, 'fnm'), + }, + 'x64', + 'linux', + testWorkspace, + ); + }); + + it('attempts multiple lookup to get the right version', async () => { + const { directory: a } = await resolver.resolveNvmVersionPath('13.12'); + expect(a).to.equal(path.join(testFixturesDir, 'nvs/node/13.12.0/x64/bin')); + + const { directory: b } = await resolver.resolveNvmVersionPath('13.11'); + expect(b).to.equal(path.join(testFixturesDir, 'nvm/versions/node/v13.11.0/bin')); + + const { directory: c } = await resolver.resolveNvmVersionPath('13.10'); + expect(c).to.equal( + path.join(testFixturesDir, 'fnm/node-versions/v13.10.0/installation/bin'), + ); + + await expect(resolver.resolveNvmVersionPath('14')).to.eventually.be.rejectedWith( + ProtocolError, + /not installed using version manager nvs\/nvm\/fnm/, + ); + }); + + it('requires nvs for a specific architecture', async () => { + resolver = new NvmResolver( + fsUtils, + { NVM_DIR: path.join(testFixturesDir, 'nvm') }, + 'x64', + 'linux', + testWorkspace, + ); + await expect(resolver.resolveNvmVersionPath('13.11/x64')).to.eventually.be.rejectedWith( + ProtocolError, + /architecture requires 'nvs' to be installed/, + ); + }); + + it('does not fall through if requesting a specific nvs architecture', async () => { + await expect(resolver.resolveNvmVersionPath('13.11/x64')).to.eventually.be.rejectedWith( + ProtocolError, + /not installed/, + ); + }); + }); + + describe('nvs support', () => { + beforeEach(() => { + createFileTree(testFixturesDir, { + 'node/13.12.0/x64/bin/node': '', + 'node/13.4.0/x86/bin/node': '', + 'node/13.3.0/x64/bin/node': '', + 'node/13.3.1/x64/bin/node64.exe': '', + 'node/13.invalid/x64/bin/node': '', + }); + + resolver = new NvmResolver( + fsUtils, + { NVS_HOME: testFixturesDir }, + 'x64', + 'linux', + testWorkspace, + ); + }); + + it('gets an exact match', async () => { + const { directory, binary } = await resolver.resolveNvmVersionPath('13.3.0'); + expect(directory).to.equal(path.join(testFixturesDir, 'node/13.3.0/x64/bin')); + expect(binary).to.equal('node'); + }); + + it('resolves node64', async () => { + const { directory, binary } = await resolver.resolveNvmVersionPath('13.3.1'); + expect(directory).to.equal(path.join(testFixturesDir, 'node/13.3.1/x64/bin')); + expect(binary).to.equal('node64'); + }); + + it('gets the best matching version', async () => { + const { directory } = await resolver.resolveNvmVersionPath('13'); + expect(directory).to.equal(path.join(testFixturesDir, 'node/13.12.0/x64/bin')); + }); + + it('throws if no version match', async () => { + await expect(resolver.resolveNvmVersionPath('14')).to.eventually.be.rejectedWith( + ProtocolError, + /not installed/, + ); + }); + + it('throws on none for specific architecture', async () => { + await expect(resolver.resolveNvmVersionPath('13.4.0')).to.eventually.be.rejectedWith( + ProtocolError, + /not installed/, + ); + }); + + it('gets a specific architecture', async () => { + const { directory } = await resolver.resolveNvmVersionPath('13/x86'); + expect(directory).to.equal(path.join(testFixturesDir, 'node/13.4.0/x86/bin')); + }); + + it('omits the bin directory on windows', async () => { + resolver = new NvmResolver( + fsUtils, + { NVS_HOME: testFixturesDir }, + 'x64', + 'win32', + testWorkspace, + ); + const { directory } = await resolver.resolveNvmVersionPath('13.3.0'); + expect(directory).to.equal(path.join(testFixturesDir, 'node/13.3.0/x64')); + }); + }); + + describe('nvm windows', () => { + beforeEach(() => { + createFileTree(testFixturesDir, { + 'v13.12.0/node.exe': '', + 'v13.3.0/node.exe': '', + 'v13.3.1/node64.exe': '', + 'v13.invalid/node.exe': '', + }); + + resolver = new NvmResolver( + fsUtils, + { NVM_HOME: testFixturesDir }, + 'x64', + 'win32', + testWorkspace, + ); + }); + + it('gets an exact match', async () => { + const { directory, binary } = await resolver.resolveNvmVersionPath('13.3.0'); + expect(directory).to.equal(path.join(testFixturesDir, 'v13.3.0')); + expect(binary).to.equal('node'); + }); + + it('resolves node64', async () => { + const { directory, binary } = await resolver.resolveNvmVersionPath('13.3.1'); + expect(directory).to.equal(path.join(testFixturesDir, 'v13.3.1')); + expect(binary).to.equal('node64'); + }); + + it('gets the best matching version', async () => { + const { directory } = await resolver.resolveNvmVersionPath('13'); + expect(directory).to.equal(path.join(testFixturesDir, 'v13.12.0')); + }); + + it('throws if no version match', async () => { + await expect(resolver.resolveNvmVersionPath('14')).to.eventually.be.rejectedWith( + ProtocolError, + /not installed/, + ); + }); + }); + + describe('nvm osx', () => { + beforeEach(() => { + createFileTree(testFixturesDir, { + 'versions/node/v13.12.0/bin/node': '', + 'versions/node/v13.3.0/bin/node': '', + 'versions/node/v13.3.1/bin/node64': '', + 'versions/node/v13.invalid/bin/node': '', + }); + + resolver = new NvmResolver( + fsUtils, + { NVM_DIR: testFixturesDir }, + 'x64', + 'linux', + testWorkspace, + ); + }); + + it('gets an exact match', async () => { + const { directory, binary } = await resolver.resolveNvmVersionPath('13.3.0'); + expect(directory).to.equal(path.join(testFixturesDir, 'versions/node/v13.3.0/bin')); + expect(binary).to.equal('node'); + }); + + it('resolves node64', async () => { + const { directory, binary } = await resolver.resolveNvmVersionPath('13.3.1'); + expect(directory).to.equal(path.join(testFixturesDir, 'versions/node/v13.3.1/bin')); + expect(binary).to.equal('node64'); + }); + + it('gets the best matching version', async () => { + const { directory } = await resolver.resolveNvmVersionPath('13'); + expect(directory).to.equal(path.join(testFixturesDir, 'versions/node/v13.12.0/bin')); + }); + + it('throws if no version match', async () => { + await expect(resolver.resolveNvmVersionPath('14')).to.eventually.be.rejectedWith( + ProtocolError, + /not installed/, + ); + }); + }); + + describe('fnm', () => { + beforeEach(() => { + createFileTree(testFixturesDir, { + 'node-versions/v13.12.0/installation/bin/node': '', + 'node-versions/v13.3.0/installation/bin/node': '', + 'node-versions/v13.invalid/installation/bin/node': '', + }); + + resolver = new NvmResolver( + fsUtils, + { FNM_DIR: testFixturesDir }, + 'x64', + 'linux', + testWorkspace, + ); + }); + + it('gets an exact match', async () => { + const { directory, binary } = await resolver.resolveNvmVersionPath('13.3.0'); + expect(directory).to.equal( + path.join(testFixturesDir, 'node-versions/v13.3.0/installation/bin'), + ); + expect(binary).to.equal('node'); + }); + + it('gets the best matching version', async () => { + const { directory } = await resolver.resolveNvmVersionPath('13'); + expect(directory).to.equal( + path.join(testFixturesDir, 'node-versions/v13.12.0/installation/bin'), + ); + }); + + it('throws if no version match', async () => { + await expect(resolver.resolveNvmVersionPath('14')).to.eventually.be.rejectedWith( + ProtocolError, + /not installed/, + ); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/reporters/goldenTextReporter.ts b/code/extensions/js-debug/src/test/reporters/goldenTextReporter.ts new file mode 100644 index 000000000000..5249e2e9975a --- /dev/null +++ b/code/extensions/js-debug/src/test/reporters/goldenTextReporter.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as mocha from 'mocha'; +import { IGoldenReporterTextTest } from './goldenTextReporterUtils'; + +class GoldenTextReporter extends mocha.reporters.Spec { + static alwaysDumpGoldenText = process.env['DUMP_GOLDEN_TEXT']; + + constructor(runner: any) { + super(runner); + + runner.on('pass', (test: IGoldenReporterTextTest) => { + if (GoldenTextReporter.alwaysDumpGoldenText) { + return this.dumpGoldenText(test); + } + }); + + runner.on('fail', (test: IGoldenReporterTextTest) => { + return this.dumpGoldenText(test); + }); + } + + private async dumpGoldenText(test: IGoldenReporterTextTest): Promise { + if (!(test instanceof mocha.Test)) return; + + if (test.goldenText && test.goldenText.hasNonAssertedLogs()) { + console.error('=== Golden Text ==='); + console.error(test.goldenText.getOutput()); + } + } +} + +// Must be default export +export = GoldenTextReporter; diff --git a/code/extensions/js-debug/src/test/reporters/goldenTextReporterUtils.ts b/code/extensions/js-debug/src/test/reporters/goldenTextReporterUtils.ts new file mode 100644 index 000000000000..764aa7a1f787 --- /dev/null +++ b/code/extensions/js-debug/src/test/reporters/goldenTextReporterUtils.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as mocha from 'mocha'; +import { GoldenText } from '../goldenText'; + +export interface IGoldenReporterTextTest extends mocha.Runnable { + goldenText: GoldenText; +} diff --git a/code/extensions/js-debug/src/test/reporters/logReporterUtils.ts b/code/extensions/js-debug/src/test/reporters/logReporterUtils.ts new file mode 100644 index 000000000000..6e23639b2957 --- /dev/null +++ b/code/extensions/js-debug/src/test/reporters/logReporterUtils.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import mocha from 'mocha'; +import os from 'os'; +import path from 'path'; + +export interface TestWithLogfile extends mocha.Test { + logPath?: string; +} + +export function getLogFileForTest(testTitlePath: string) { + return path.join(os.tmpdir(), `${testTitlePath.replace(/[^a-z0-9]/gi, '-')}.json`); +} diff --git a/code/extensions/js-debug/src/test/reporters/logTestReporter.ts b/code/extensions/js-debug/src/test/reporters/logTestReporter.ts new file mode 100644 index 000000000000..10d6aa3df2a3 --- /dev/null +++ b/code/extensions/js-debug/src/test/reporters/logTestReporter.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as mocha from 'mocha'; +import { getLogFileForTest as getLogPathForTest, TestWithLogfile } from './logReporterUtils'; + +class LoggingReporter extends mocha.reporters.Spec { + static alwaysDumpLogs = process.env['DUMP_LOGS']; + + constructor(runner: any) { + super(runner); + + runner.on('pass', (test: TestWithLogfile) => { + if (LoggingReporter.alwaysDumpLogs) { + return this.dumpLogs(test); + } + }); + + runner.on('fail', (test: TestWithLogfile) => { + return this.dumpLogs(test); + }); + } + + private async dumpLogs(test: mocha.Runnable): Promise { + if (!(test instanceof mocha.Test)) return; + + const logPath = getLogPathForTest(test.fullTitle()); + console.log(`##vso[build.uploadlog]${logPath}`); + } +} + +// Must be default export +export = LoggingReporter; diff --git a/code/extensions/js-debug/src/test/resourceProvider/resourceProvider.test.ts b/code/extensions/js-debug/src/test/resourceProvider/resourceProvider.test.ts new file mode 100644 index 000000000000..67c43096204a --- /dev/null +++ b/code/extensions/js-debug/src/test/resourceProvider/resourceProvider.test.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { promises as fs } from 'fs'; +import { BasicResourceProvider } from '../../adapter/resourceProvider/basicResourceProvider'; +import { ITestHandle } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('resourceProvider', () => { + async function waitForPause(p: ITestHandle, cb?: (threadId: string) => Promise) { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + if (cb) await cb(threadId); + return p.dap.continue({ threadId }); + } + + itIntegrates('applies cookies', async ({ r }) => { + // Breakpoint in source mapped script set before launch. + // Note: this only works in Chrome 76 or later and Node 12 or later, since it relies + // on 'pause before executing script with source map' functionality in CDP. + const p = await r.launchUrl('cookies/home'); + p.load(); + await waitForPause(p); + p.assertLog(); + }); + + itIntegrates('follows redirects', async ({ r }) => { + const p = await r.launchUrl('redirect-test/home'); + p.load(); + p.log(await p.waitForSource('module1.ts')); + p.assertLog(); + }); + + it('decodes base64 data uris', async () => { + const rp = new BasicResourceProvider(fs); + expect(await rp.fetch('data:text/plain;base64,SGVsbG8gd29ybGQh')).to.deep.equal({ + ok: true, + statusCode: 200, + body: 'Hello world!', + url: 'data:text/plain;base64,SGVsbG8gd29ybGQh', + }); + }); + + it('decodes utf8 data uris (#662)', async () => { + const rp = new BasicResourceProvider(fs); + expect(await rp.fetch('data:text/plain;utf-8,Hello%20world!')).to.deep.equal({ + ok: true, + statusCode: 200, + body: 'Hello world!', + url: 'data:text/plain;utf-8,Hello%20world!', + }); + }); + + it('fetches remote url', async () => { + const rp = new BasicResourceProvider(fs); + expect(await rp.fetch('http://localhost:8001/greet')).to.deep.equal({ + ok: true, + statusCode: 200, + body: 'Hello world!', + url: 'http://localhost:8001/greet', + }); + }); + + it('follows redirects (unit)', async () => { + const rp = new BasicResourceProvider(fs); + expect(await rp.fetch('http://localhost:8001/redirect-to-greet')).to.deep.equal({ + ok: true, + statusCode: 200, + body: 'Hello world!', + url: 'http://localhost:8001/redirect-to-greet', + }); + }); + + it('applies request options', async () => { + const rp = new BasicResourceProvider(fs, { + provideOptions: opts => { + opts.headers = { cool: 'true' }; + }, + }); + + const res = await rp.fetch('http://localhost:8001/view-headers'); + expect(JSON.parse(res.body || 'no content')).to.containSubset({ cool: 'true' }); + }); +}); diff --git a/code/extensions/js-debug/src/test/resourceProvider/resourceprovider-applies-cookies.txt b/code/extensions/js-debug/src/test/resourceProvider/resourceprovider-applies-cookies.txt new file mode 100644 index 000000000000..331020d50cc4 --- /dev/null +++ b/code/extensions/js-debug/src/test/resourceProvider/resourceprovider-applies-cookies.txt @@ -0,0 +1,12 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +foo @ ${workspaceFolder}/web/cookies/module1.ts:3:3 +Object.bar @ ${workspaceFolder}/web/cookies/module2.ts:3:3 +3../module1 @ ${workspaceFolder}/web/cookies/pause.ts:4:4 +Window.o @ ${workspaceFolder}/node_modules/browser-pack/_prelude.js:1:1 +Window.r @ ${workspaceFolder}/node_modules/browser-pack/_prelude.js:1:1 + @ ${workspaceFolder}/node_modules/browser-pack/_prelude.js:1:1 diff --git a/code/extensions/js-debug/src/test/resourceProvider/resourceprovider-follows-redirects.txt b/code/extensions/js-debug/src/test/resourceProvider/resourceprovider-follows-redirects.txt new file mode 100644 index 000000000000..39bdd01c022a --- /dev/null +++ b/code/extensions/js-debug/src/test/resourceProvider/resourceprovider-follows-redirects.txt @@ -0,0 +1,8 @@ +{ + reason : new + source : { + name : redirect-test/module1.ts + path : ${workspaceFolder}/web/redirect-test/module1.ts + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/runTest.js b/code/extensions/js-debug/src/test/runTest.js new file mode 100644 index 000000000000..f3d7863d43d9 --- /dev/null +++ b/code/extensions/js-debug/src/test/runTest.js @@ -0,0 +1,40 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +const { runTests } = require('@vscode/test-electron'); +const minimist = require('minimist'); +const path = require('path'); + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../dist'); + + // The path to the extension test script + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(extensionDevelopmentPath, 'src/testRunner'); + + process.env.PWA_TEST_OPTIONS = JSON.stringify(minimist(process.argv.slice(2))); + + // Download VS Code, unzip it and run the integration test + const basedir = path.resolve(__dirname, '../..'); + + await runTests({ + extensionDevelopmentPath, + extensionTestsPath, + launchArgs: [ + basedir, + '--disable-extension=ms-vscode.js-debug', + '--disable-user-env-probe', + '--disable-workspace-trust', + ], + }); + } catch (err) { + console.error('Failed to run tests', err); + process.exit(1); + } +} + +main(); diff --git a/code/extensions/js-debug/src/test/sources/pretty-print-sources-base.txt b/code/extensions/js-debug/src/test/sources/pretty-print-sources-base.txt new file mode 100644 index 000000000000..5b361e4742f2 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/pretty-print-sources-base.txt @@ -0,0 +1,26 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + @ ${workspaceFolder}/web/pretty/ugly.js:5:1 +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + @ ${workspaceFolder}/web/pretty/ugly.js-pretty.js:11:1 +{ + allThreadsContinued : false + threadId : +} +{ + reason : new + source : { + name : pretty/ugly.js-pretty.js + path : ${workspaceFolder}/web/pretty/ugly.js-pretty.js + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/sources/pretty-print-sources-bps.txt b/code/extensions/js-debug/src/test/sources/pretty-print-sources-bps.txt new file mode 100644 index 000000000000..cf8845af5208 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/pretty-print-sources-bps.txt @@ -0,0 +1,129 @@ +{ + breakpoint : { + column : 1 + id : + line : 5 + source : { + name : localhost꞉8001/pretty/ugly.js + path : ${workspaceFolder}/web/pretty/ugly.js + sourceReference : + } + verified : true + } + reason : changed +} +{ + breakpoint : { + column : 1 + id : + line : 9 + source : { + name : localhost꞉8001/pretty/ugly.js + path : ${workspaceFolder}/web/pretty/ugly.js + sourceReference : + } + verified : true + } + reason : changed +} +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +{ + breakpoint : { + column : 1 + id : + line : 11 + source : { + name : pretty/ugly.js-pretty.js + path : ${workspaceFolder}/web/pretty/ugly.js-pretty.js + sourceReference : + } + verified : true + } + reason : changed +} +{ + breakpoint : { + column : 1 + id : + line : 21 + source : { + name : pretty/ugly.js-pretty.js + path : ${workspaceFolder}/web/pretty/ugly.js-pretty.js + sourceReference : + } + verified : true + } + reason : changed +} +{ + breakpoints : [ + [0] : { + column : 1 + id : + line : 17 + source : { + name : pretty/ugly.js-pretty.js + path : ${workspaceFolder}/web/pretty/ugly.js-pretty.js + sourceReference : + } + verified : true + } + [1] : { + column : 1 + id : + line : 11 + source : { + name : pretty/ugly.js-pretty.js + path : ${workspaceFolder}/web/pretty/ugly.js-pretty.js + sourceReference : + } + verified : true + } + [2] : { + column : 1 + id : + line : 21 + source : { + name : pretty/ugly.js-pretty.js + path : ${workspaceFolder}/web/pretty/ugly.js-pretty.js + sourceReference : + } + verified : true + } + ] +} + +continue +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : + } +] + @ ${workspaceFolder}/web/pretty/ugly.js-pretty.js:17:1 + +continue +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : + } +] + @ ${workspaceFolder}/web/pretty/ugly.js-pretty.js:21:1 diff --git a/code/extensions/js-debug/src/test/sources/pretty-print-sources-eval-sources-929.txt b/code/extensions/js-debug/src/test/sources/pretty-print-sources-eval-sources-929.txt new file mode 100644 index 000000000000..865d8832c9e3 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/pretty-print-sources-eval-sources-929.txt @@ -0,0 +1,27 @@ +Evaluating#1: let n=1;for(let l=0;l +} + @ /VM:1:48 +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/46947589.js-pretty.js:4:3 +{ + allThreadsContinued : false + threadId : +} +{ + reason : new + source : { + name : 46947589.js-pretty.js + path : ${workspaceFolder}/web/46947589.js-pretty.js + sourceReference : + } +} diff --git a/code/extensions/js-debug/src/test/sources/pretty-print-sources-steps-in-pretty.txt b/code/extensions/js-debug/src/test/sources/pretty-print-sources-steps-in-pretty.txt new file mode 100644 index 000000000000..cfbc38ca38ad --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/pretty-print-sources-steps-in-pretty.txt @@ -0,0 +1,66 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + +step +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ ${workspaceFolder}/web/pretty/ugly.js-pretty.js:12:1 + +step +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ ${workspaceFolder}/web/pretty/ugly.js-pretty.js:13:1 + +step +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ ${workspaceFolder}/web/pretty/ugly.js-pretty.js:14:1 + +step +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ ${workspaceFolder}/web/pretty/ugly.js-pretty.js:15:1 diff --git a/code/extensions/js-debug/src/test/sources/pretty-print.test.ts b/code/extensions/js-debug/src/test/sources/pretty-print.test.ts new file mode 100644 index 000000000000..aec19c94b611 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/pretty-print.test.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Dap from '../../dap/api'; +import { ITestHandle } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('pretty print sources', () => { + async function waitAndStayPaused(p: ITestHandle) { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + return () => p.dap.continue({ threadId }); + } + + const testPrettyPrints = async (p: ITestHandle, source: Dap.Source) => { + const res = p.dap.prettyPrintSource({ source }); + + const gotSource = p.dap.once('loadedSource'); + const continued = p.dap.once('continued'); + const stopped = await waitAndStayPaused(p); + + p.log(await continued); + p.log(await gotSource); + await res; + stopped(); + }; + + itIntegrates('base', async function({ r }) { + const p = await r.launchUrl('pretty/pretty.html'); + const source = { path: p.workspacePath('web/pretty/ugly.js') }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 5, column: 1 }] }); + p.load(); + + await waitAndStayPaused(p); + await testPrettyPrints(p, source); + p.assertLog(); + }); + + itIntegrates('steps in pretty', async ({ r }) => { + const p = await r.launchUrl('pretty/pretty.html'); + const source = { path: p.workspacePath('web/pretty/ugly.js') }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 5, column: 1 }] }); + p.load(); + + await p.dap.once('stopped'); + p.dap.prettyPrintSource({ source }); + + const { threadId } = p.log(await p.dap.once('stopped')); + + for (let i = 0; i < 4; i++) { + p.log('\nstep'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + } + + p.assertLog(); + }); + + itIntegrates('bps', async ({ r }) => { + const p = await r.launchUrl('pretty/pretty.html'); + const source = { path: p.workspacePath('web/pretty/ugly.js') }; + await p.dap.setBreakpoints({ + source, + breakpoints: [ + { line: 5, column: 1 }, + { line: 9, column: 1 }, + ], + }); + p.load(); + + const stopped = p.dap.once('stopped'); + for (let i = 0; i < 2; i++) { + p.log(await p.dap.once('breakpoint')); + } + const { threadId } = p.log(await stopped); + p.dap.prettyPrintSource({ source }); + const pretty = p.dap.once('loadedSource'); + + // should adjust all BPs to new file + const bp1 = p.log(await p.dap.once('breakpoint')).breakpoint; + const bp2 = p.log(await p.dap.once('breakpoint')).breakpoint; + + // should set a breakpoint in pretty source: + p.log( + await p.dap.setBreakpoints({ + source: (await pretty).source, + breakpoints: [ + { line: 17, column: 1 }, + { line: bp1.line, column: bp1.column }, + { line: bp2.line, column: bp2.column }, + ], + }), + ); + + // should hit breakpoints: + for (let i = 0; i < 2; i++) { + p.log('\ncontinue'); + p.dap.continue({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + } + + p.assertLog(); + }); + + itIntegrates('eval sources (#929)', async function({ r }) { + const p = await r.launchUrlAndLoad('index.html'); + await p.load(); + + const sourceEvt = p.waitForSource(); + const evaled = p.evaluate('let n=1;for(let l=0;l +} +sayHello @ ${workspaceFolder}/web/was-nested/greet.js:2:1 +./src/index.js @ ${workspaceFolder}/web/webpack/relative-paths.js:3:9 +Window.__webpack_require__ @ ${workspaceFolder}/web/webpack/webpack:/webpack/bootstrap:19:1 + @ ${workspaceFolder}/web/webpack/webpack:/webpack/bootstrap:83:1 + @ ${workspaceFolder}/web/webpack/relative-paths.bundle.js:87:10 +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +sayGoodbye @ ${workspaceFolder}/web/webpack/farewell.js:2:1 +./src/index.js @ ${workspaceFolder}/web/webpack/relative-paths.js:4:11 +Window.__webpack_require__ @ ${workspaceFolder}/web/webpack/webpack:/webpack/bootstrap:19:1 + @ ${workspaceFolder}/web/webpack/webpack:/webpack/bootstrap:83:1 + @ ${workspaceFolder}/web/webpack/relative-paths.bundle.js:87:10 diff --git a/code/extensions/js-debug/src/test/sources/sources-allows-shebang-in-node-code.txt b/code/extensions/js-debug/src/test/sources/sources-allows-shebang-in-node-code.txt new file mode 100644 index 000000000000..594ebc6970c3 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-allows-shebang-in-node-code.txt @@ -0,0 +1,16 @@ +{ + reason : new + source : { + name : ${fixturesDir}/shebang-lf + path : ${fixturesDir}/shebang-lf + sourceReference : 0 + } +} +{ + reason : new + source : { + name : ${fixturesDir}/shebang-crlf + path : ${fixturesDir}/shebang-crlf + sourceReference : 0 + } +} diff --git a/code/extensions/js-debug/src/test/sources/sources-applies-sourcemap-path-mappings-to-sourceurls-vscode204784.txt b/code/extensions/js-debug/src/test/sources/sources-applies-sourcemap-path-mappings-to-sourceurls-vscode204784.txt new file mode 100644 index 000000000000..be807e4f0d47 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-applies-sourcemap-path-mappings-to-sourceurls-vscode204784.txt @@ -0,0 +1,8 @@ +{ + reason : new + source : { + name : vscode-204784/src/original.js + path : ${workspaceFolder}/web/vscode-204784/src/original.js + sourceReference : 0 + } +} diff --git a/code/extensions/js-debug/src/test/sources/sources-basic-source-map.txt b/code/extensions/js-debug/src/test/sources/sources-basic-source-map.txt new file mode 100644 index 000000000000..5531bbb47ade --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-basic-source-map.txt @@ -0,0 +1,63 @@ + +Source event for +{ + reason : new + source : { + name : browserify/index.ts + path : ${workspaceFolder}/web/browserify/index.ts + sourceReference : + } +} +import * as m1 from './module1'; +import * as m2 from './module2'; + +window['throwError'] = m1.throwError; +window['throwValue'] = m1.throwValue; +window['pause'] = m1.foo; +window['callBack'] = m2.bar; +window['logSome'] = function logSome() { + console.log(m1.kModule1 + m2.kModule2); +} + +--------- + +Source event for +{ + reason : new + source : { + name : browserify/module1.ts + path : ${workspaceFolder}/web/browserify/module1.ts + sourceReference : + } +} +export const kModule1 = 1; +export function foo() { + debugger; +} +export function throwError(s) { + throw new Error(s); +} +export function throwValue(v) { + throw v; +} + +--------- + +Source event for +{ + reason : new + source : { + name : browserify/module2.ts + path : ${workspaceFolder}/web/browserify/module2.ts + sourceReference : + } +} +export const kModule2 = 2; +export function bar(callback) { + callback(); +} +export function pause() { + debugger; +} + +--------- diff --git a/code/extensions/js-debug/src/test/sources/sources-basic-sources.txt b/code/extensions/js-debug/src/test/sources/sources-basic-sources.txt new file mode 100644 index 000000000000..fab2755d8c77 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-basic-sources.txt @@ -0,0 +1,90 @@ + +Source event for inline +{ + reason : new + source : { + name : localhost꞉8001/inlinescript.html꞉2:11 + path : ${workspaceFolder}/web/inlinescript.html + sourceReference : + } +} + + + console.log('inline script'); + +--------- + +Source event for empty.js +{ + reason : new + source : { + name : localhost꞉8001/empty.js + path : ${workspaceFolder}/web/empty.js + sourceReference : + } +} +"111111111111111111111111111111111111111111111111111" + +--------- +Evaluating#1: 17 + +Source event for does not exist +{ + reason : new + source : { + name : localhost꞉8001/doesnotexist.js + path : localhost꞉8001/doesnotexist.js + sourceReference : + } +} +17 +//# sourceURL=http://localhost:8001/doesnotexist.js +--------- + +Source event for dir/helloworld +{ + reason : new + source : { + name : localhost꞉8001/dir/helloworld.js + path : ${workspaceFolder}/web/dir/helloworld.js + sourceReference : + } +} +console.log('Hello, world!'); + +--------- + +Loaded sources: { + sources : [ + [0] : { + name : localhost꞉8001/inlinescript.html꞉2:11 + path : ${workspaceFolder}/web/inlinescript.html + sourceReference : + } + [1] : { + name : /VM + path : /VM + sourceReference : + } + [2] : { + name : localhost꞉8001/empty.js + path : ${workspaceFolder}/web/empty.js + sourceReference : + } + [3] : { + name : localhost꞉8001/doesnotexist.js + path : localhost꞉8001/doesnotexist.js + sourceReference : + } + [4] : { + name : /VM + path : /VM + sourceReference : + } + [5] : { + name : localhost꞉8001/dir/helloworld.js + path : ${workspaceFolder}/web/dir/helloworld.js + sourceReference : + } + ] +} diff --git a/code/extensions/js-debug/src/test/sources/sources-lazily-announces-eval.txt b/code/extensions/js-debug/src/test/sources/sources-lazily-announces-eval.txt new file mode 100644 index 000000000000..bdc5c24b3036 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-lazily-announces-eval.txt @@ -0,0 +1,41 @@ +Evaluating#1: 42 +Evaluating#2: window.hello = m => console.log(m) +Evaluating#3: hello() + +Source event for eval +{ + reason : new + source : { + name : /VM + path : /VM + sourceReference : + } +} +window.hello = m => console.log(m) + +--------- + +Loaded sources: { + sources : [ + [0] : { + name : localhost꞉8001/inlinescript.html꞉2:11 + path : ${workspaceFolder}/web/inlinescript.html + sourceReference : + } + [1] : { + name : /VM + path : /VM + sourceReference : + } + [2] : { + name : /VM + path : /VM + sourceReference : + } + [3] : { + name : localhost꞉8001/eval3.js + path : localhost꞉8001/eval3.js + sourceReference : + } + ] +} diff --git a/code/extensions/js-debug/src/test/sources/sources-removes-any-query-from-node-paths-529.txt b/code/extensions/js-debug/src/test/sources/sources-removes-any-query-from-node-paths-529.txt new file mode 100644 index 000000000000..51d33603713e --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-removes-any-query-from-node-paths-529.txt @@ -0,0 +1,8 @@ +{ + reason : new + source : { + name : simpleNode/simpleWebpackWithQuery.ts + path : ${workspaceFolder}/simpleNode/simpleWebpackWithQuery.ts + sourceReference : 0 + } +} diff --git a/code/extensions/js-debug/src/test/sources/sources-sourcemap-error-handling-logs-initial-parse-errors.txt b/code/extensions/js-debug/src/test/sources/sources-sourcemap-error-handling-logs-initial-parse-errors.txt new file mode 100644 index 000000000000..e8cf0d377d6a --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-sourcemap-error-handling-logs-initial-parse-errors.txt @@ -0,0 +1,3 @@ +Evaluating#1: //# sourceMappingURL=data:application/json;charset=utf-8;base64,ZGV2cw== + +stderr> Could not read source map for http://localhost:8001/eval1.js: Unexpected token 'd', "devs" is not valid JSON diff --git a/code/extensions/js-debug/src/test/sources/sources-sourcemap-error-handling-logs-not-found-errors.txt b/code/extensions/js-debug/src/test/sources/sources-sourcemap-error-handling-logs-not-found-errors.txt new file mode 100644 index 000000000000..bfcaf80a43f9 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-sourcemap-error-handling-logs-not-found-errors.txt @@ -0,0 +1,13 @@ +Evaluating#1: //# sourceMappingURL=does-not-exist.js.map + +stderr> Could not read source map for http://localhost:8001/eval1.js: Unexpected 404 response from http://localhost:8001/does-not-exist.js.map: + + + +Error + + +
Cannot GET /does-not-exist.js.map
+ + + diff --git a/code/extensions/js-debug/src/test/sources/sources-supports-nested-sourcemaps-1390.txt b/code/extensions/js-debug/src/test/sources/sources-supports-nested-sourcemaps-1390.txt new file mode 100644 index 000000000000..b4701a61f507 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-supports-nested-sourcemaps-1390.txt @@ -0,0 +1,7 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +Module.add @ ${workspaceFolder}/nestedSourceMaps/b/lib.js:2:1 diff --git a/code/extensions/js-debug/src/test/sources/sources-updated-content.txt b/code/extensions/js-debug/src/test/sources/sources-updated-content.txt new file mode 100644 index 000000000000..465dced86c7d --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-updated-content.txt @@ -0,0 +1,34 @@ + +Source event for test.js +{ + reason : new + source : { + name : localhost꞉8001/test.js + path : localhost꞉8001/test.js + sourceReference : + } +} +content1//# sourceURL=test.js +--------- + +Source event for test.js updated +{ + reason : new + source : { + name : localhost꞉8001/test.js + path : localhost꞉8001/test.js + sourceReference : + } +} +content2//# sourceURL=test.js +--------- + +Loaded sources: { + sources : [ + [0] : { + name : localhost꞉8001/test.js + path : localhost꞉8001/test.js + sourceReference : + } + ] +} diff --git a/code/extensions/js-debug/src/test/sources/sources-url-and-hash.txt b/code/extensions/js-debug/src/test/sources/sources-url-and-hash.txt new file mode 100644 index 000000000000..d40a0479b238 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-url-and-hash.txt @@ -0,0 +1,81 @@ + +Source event for foo.js +{ + reason : new + source : { + name : localhost꞉8001/foo.js + path : localhost꞉8001/foo.js + sourceReference : + } +} +a +//# sourceURL=foo.js +--------- + +Source event for foo.js +{ + reason : new + source : { + name : localhost꞉8001/foo.js + path : localhost꞉8001/foo.js + sourceReference : + } +} +b +//# sourceURL=foo.js +--------- + +Source event for empty.js +{ + reason : new + source : { + name : localhost꞉8001/empty.js + path : ${workspaceFolder}/web/empty.js + sourceReference : + } +} +"111111111111111111111111111111111111111111111111111" + +--------- + +Source event for empty2.js +{ + reason : new + source : { + name : localhost꞉8001/empty2.js + path : localhost꞉8001/empty2.js + sourceReference : + } +} + +--------- + +Loaded sources: { + sources : [ + [0] : { + name : localhost꞉8001/foo.js + path : localhost꞉8001/foo.js + sourceReference : + } + [1] : { + name : /VM + path : /VM + sourceReference : + } + [2] : { + name : localhost꞉8001/empty.js + path : ${workspaceFolder}/web/empty.js + sourceReference : + } + [3] : { + name : /VM + path : /VM + sourceReference : + } + [4] : { + name : localhost꞉8001/empty2.js + path : localhost꞉8001/empty2.js + sourceReference : + } + ] +} diff --git a/code/extensions/js-debug/src/test/sources/sources-waiting-for-source-map-failure.txt b/code/extensions/js-debug/src/test/sources/sources-waiting-for-source-map-failure.txt new file mode 100644 index 000000000000..1a2d2788bc0d --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-waiting-for-source-map-failure.txt @@ -0,0 +1,7 @@ +stderr> Uncaught Error: error2 +stderr> > Uncaught Error: error2 +stderr> +throwError @ ${workspaceFolder}/web/browserify/bundle.js:23:11 + @ /VM:1:27 +◀ setTimeout ▶ + @ /VM:1 diff --git a/code/extensions/js-debug/src/test/sources/sources-waiting-for-source-map.txt b/code/extensions/js-debug/src/test/sources/sources-waiting-for-source-map.txt new file mode 100644 index 000000000000..ff58846ddbfd --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-waiting-for-source-map.txt @@ -0,0 +1,16 @@ +stderr> Uncaught Error Error: error2 + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at (/VM:1:27) + --- setTimeout --- + at (/VM:1:1) +stderr> +> Uncaught Error Error: error2 + at throwError (${workspaceFolder}/web/browserify/module1.ts:6:9) + at (/VM:1:27) + --- setTimeout --- + at (/VM:1:1) +stderr> +throwError @ ${workspaceFolder}/web/browserify/module1.ts:6:9 + @ /VM:1:27 +◀ setTimeout ▶ + @ /VM:1 diff --git a/code/extensions/js-debug/src/test/sources/sources-works-with-relative-webpack-sourcemaps-479.txt b/code/extensions/js-debug/src/test/sources/sources-works-with-relative-webpack-sourcemaps-479.txt new file mode 100644 index 000000000000..121c04ccc156 --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sources-works-with-relative-webpack-sourcemaps-479.txt @@ -0,0 +1,22 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +sayHello @ ${workspaceFolder}/greet.js:2:1 +./src/index.js @ ${workspaceFolder}/web/webpack/relative-paths.js:3:9 +Window.__webpack_require__ @ ${workspaceFolder}/web/webpack/bootstrap:19:1 + @ ${workspaceFolder}/web/webpack/bootstrap:83:1 + @ ${workspaceFolder}/web/webpack/relative-paths.bundle.js:87:10 +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +sayGoodbye @ ${workspaceFolder}/web/webpack/farewell.js:2:1 +./src/index.js @ ${workspaceFolder}/web/webpack/relative-paths.js:4:11 +Window.__webpack_require__ @ ${workspaceFolder}/web/webpack/bootstrap:19:1 + @ ${workspaceFolder}/web/webpack/bootstrap:83:1 + @ ${workspaceFolder}/web/webpack/relative-paths.bundle.js:87:10 diff --git a/code/extensions/js-debug/src/test/sources/sourcesTest.ts b/code/extensions/js-debug/src/test/sources/sourcesTest.ts new file mode 100644 index 000000000000..14c0f2a4593d --- /dev/null +++ b/code/extensions/js-debug/src/test/sources/sourcesTest.ts @@ -0,0 +1,269 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { join } from 'path'; +import Dap from '../../dap/api'; +import { createFileTree } from '../createFileTree'; +import { ITestHandle, testFixturesDir, testWorkspace } from '../test'; +import { itIntegrates, waitForPause } from '../testIntegrationUtils'; + +describe('sources', () => { + async function dumpSource(p: ITestHandle, event: Dap.LoadedSourceEventParams, name: string) { + p.log('\nSource event for ' + name); + p.log(event); + const content = await p.dap.source({ + sourceReference: event.source.sourceReference!, + source: { + path: event.source.path, + sourceReference: event.source.sourceReference, + }, + }); + p.log(content.content); + p.log('---------'); + } + + itIntegrates('basic sources', async ({ r }) => { + const p = await r.launchUrl('inlinescript.html'); + p.load(); + await dumpSource(p, await p.waitForSource('inline'), 'inline'); + p.addScriptTag('empty.js'); + await dumpSource(p, await p.waitForSource('empty.js'), 'empty.js'); + p.evaluate('17', 'doesnotexist.js'); + await dumpSource(p, await p.waitForSource('doesnotexist'), 'does not exist'); + p.addScriptTag('dir/helloworld.js'); + await dumpSource(p, await p.waitForSource('helloworld'), 'dir/helloworld'); + + p.log(await p.dap.loadedSources({}), '\nLoaded sources: '); + p.assertLog(); + }); + + itIntegrates('lazily announces eval', async ({ r }) => { + const p = await r.launchUrlAndLoad('inlinescript.html'); + + const src = p.waitForSource('eval'); + p.evaluate('42', ''); // sohuld never be announced + p.evaluate('window.hello = m => console.log(m)', ''); // announced when its source is in the console + + p.evaluate('hello()'); + + await dumpSource(p, await src, 'eval'); + p.log(await p.dap.loadedSources({}), '\nLoaded sources: '); + p.assertLog(); + }); + + itIntegrates('updated content', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.cdp.Runtime.evaluate({ expression: 'content1//# sourceURL=test.js' }); + await dumpSource(p, await p.waitForSource('test'), 'test.js'); + p.cdp.Runtime.evaluate({ expression: 'content2//# sourceURL=test.js' }); + await dumpSource(p, await p.waitForSource('test'), 'test.js updated'); + p.log(await p.dap.loadedSources({}), '\nLoaded sources: '); + p.assertLog(); + }); + + itIntegrates('basic source map', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.addScriptTag('browserify/bundle.js'); + const sources = await Promise.all([ + p.waitForSource('index.ts'), + p.waitForSource('module1.ts'), + p.waitForSource('module2.ts'), + ]); + for (const source of sources) await dumpSource(p, source, ''); + p.assertLog(); + }); + + itIntegrates('waiting for source map', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await p.addScriptTag('browserify/bundle.js'); + p.dap.evaluate({ expression: `setTimeout(() => { window.throwError('error2')}, 0)` }); + await p.logger.logOutput(await p.dap.once('output')); + p.assertLog(); + }); + + itIntegrates.skip('waiting for source map failure', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.adapter.sourceContainer.setSourceMapTimeouts({ + load: 2000, + resolveLocation: 0, + output: 0, + sourceMapMinPause: 0, + sourceMapCumulativePause: 0, + }); + await p.addScriptTag('browserify/bundle.js'); + p.dap.evaluate({ expression: `setTimeout(() => { window.throwError('error2')}, 0)` }); + await p.logger.logOutput(await p.dap.once('output')); + p.assertLog(); + }); + + itIntegrates('supports nested sourcemaps (#1390)', async ({ r }) => { + await r.initialize; + + const cwd = join(testWorkspace, 'nestedSourceMaps'); + const handle = await r.runScript(join(cwd, 'a/main.bundle.js'), { cwd }); + await handle.dap.setBreakpoints({ + source: { path: join(testWorkspace, 'nestedSourceMaps', 'b', 'lib.js') }, + breakpoints: [{ line: 2, column: 1 }], + }); + + handle.load(); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + + itIntegrates('works with relative webpack sourcemaps (#479)', async ({ r }) => { + const p = await r.launchUrl('webpack/relative-paths.html'); + + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('greet.js') }, + breakpoints: [{ line: 2, column: 1 }], + }); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/webpack/farewell.js') }, + breakpoints: [{ line: 2, column: 1 }], + }); + p.load(); + + await waitForPause(p); // greet + await waitForPause(p); // farewell + p.assertLog(); + }); + + itIntegrates('allows overrides for relative webpack paths (#479)', async ({ r }) => { + const p = await r.launchUrl('webpack/relative-paths.html', { + sourceMapPathOverrides: { + 'webpack:///./*': '${webRoot}/*', + 'webpack:///../*': '${webRoot}/was-nested/*', + }, + }); + + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/was-nested/greet.js') }, + breakpoints: [{ line: 2, column: 1 }], + }); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/webpack/farewell.js') }, + breakpoints: [{ line: 2, column: 1 }], + }); + p.load(); + + await waitForPause(p); // greet + await waitForPause(p); // farewell + p.assertLog(); + }); + + itIntegrates('url and hash', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + + p.cdp.Runtime.evaluate({ expression: 'a\n//# sourceURL=foo.js' }); + await dumpSource(p, await p.waitForSource('foo.js'), 'foo.js'); + + // Same url, different content => different source. + p.cdp.Runtime.evaluate({ expression: 'b\n//# sourceURL=foo.js' }); + await dumpSource(p, await p.waitForSource('foo.js'), 'foo.js'); + + // Same url, same content => same sources. + await p.cdp.Runtime.evaluate({ expression: 'a\n//# sourceURL=foo.js' }); + await p.cdp.Runtime.evaluate({ expression: 'b\n//# sourceURL=foo.js' }); + + // Content matches => maps to file. + p.addScriptTag('empty.js'); + await dumpSource(p, await p.waitForSource('empty.js'), 'empty.js'); + + // Content does not match => debugger script. + const path = p.workspacePath('web/empty2.js'); + p.adapter.sourceContainer.setFileContentOverrideForTest(path, '123'); + p.addScriptTag('empty2.js'); + await dumpSource(p, await p.waitForSource('empty2.js'), 'empty2.js'); + + p.log(await p.dap.loadedSources({}), '\nLoaded sources: '); + p.assertLog(); + }); + + itIntegrates('allows module wrapper in node code', async ({ r }) => { + const handle = await r.runScript(join(testWorkspace, 'moduleWrapper', 'index.js')); + handle.load(); + const src = await handle.waitForSource('moduleWrapper/test.js'); + expect(src.source.sourceReference).to.equal(0); + }); + + itIntegrates('verifies content when enableContentValidation=true', async ({ r }) => { + const handle = await r.runScript(join(testWorkspace, 'moduleWrapper', 'customWrapper.js')); + handle.load(); + const src = await handle.waitForSource('moduleWrapper/test.js'); + expect(src.source.sourceReference).to.be.greaterThan(0); + }); + + itIntegrates('does not verify content when enableContentValidation=false', async ({ r }) => { + const handle = await r.runScript(join(testWorkspace, 'moduleWrapper', 'customWrapper.js'), { + enableContentValidation: false, + }); + handle.load(); + const src = await handle.waitForSource('moduleWrapper/test.js'); + expect(src.source.sourceReference).to.equal(0); + }); + + itIntegrates('allows shebang in node code', async ({ r }) => { + createFileTree(testFixturesDir, { + index: 'require("./shebang-lf"); require("./shebang-crlf"); debugger;', + 'shebang-lf': '#!/bin/node\nconsole.log("hello world")', + 'shebang-crlf': '#!/bin/node\r\nconsole.log("hello world")', + }); + + const handle = await r.runScript(join(testFixturesDir, 'index')); + handle.load(); + + const lf = handle.waitForSource('shebang-lf'); + const crlf = handle.waitForSource('shebang-crlf'); + handle.log(await lf, undefined, []); + handle.log(await crlf, undefined, []); + handle.assertLog(); + }); + + itIntegrates('applies sourcemap path mappings to sourceURLs (vscode#204784)', async ({ r }) => { + const handle = await r.launchUrl('vscode-204784/index.html', { + sourceMapPathOverrides: { 'mapped://*': '${workspaceFolder}/web/vscode-204784/*' }, + resolveSourceMapLocations: ['${workspaceFolder}/web/vscode-204784/**'], + }); + + handle.load(); + const src = await handle.waitForSource('original'); + handle.log(src, undefined, []); + handle.assertLog(); + }); + + itIntegrates('removes any query from node paths (#529)', async ({ r }) => { + const handle = await r.runScript( + join(testWorkspace, 'simpleNode', 'simpleWebpackWithQuery.js'), + { + cwd: join(testWorkspace, 'simpleNode'), + }, + ); + + handle.load(); + handle.log(await handle.waitForSource('simpleWebpackWithQuery.ts'), undefined, []); + handle.assertLog(); + }); + + describe('sourcemap error handling', () => { + itIntegrates('logs initial parse errors', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + const output = p.dap.once('output', o => o.category === 'stderr'); + await p.evaluate( + '//# sourceMappingURL=data:application/json;charset=utf-8;base64,ZGV2cw==\n', + ); + await p.logger.logOutput(await output); + p.assertLog(); + }); + + itIntegrates('logs not found errors', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + const output = p.dap.once('output', o => o.category === 'stderr'); + await p.evaluate('//# sourceMappingURL=does-not-exist.js.map\n'); + await p.logger.logOutput(await output); + p.assertLog(); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/stacks/stacks-anonymous-initial-script.txt b/code/extensions/js-debug/src/test/stacks/stacks-anonymous-initial-script.txt new file mode 100644 index 000000000000..5383612b7ff4 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-anonymous-initial-script.txt @@ -0,0 +1 @@ + @ /VM:1:9 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-anonymous-scopes.txt b/code/extensions/js-debug/src/test/stacks/stacks-anonymous-scopes.txt new file mode 100644 index 000000000000..547cbbef4c8f --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-anonymous-scopes.txt @@ -0,0 +1,33 @@ + +Window.paused @ /VM:4:9 + > scope #0: Local: paused + > this: Window + y: 'paused' + scope #1: Global [expensive] + +Window.chained @ /VM:11:23 + > scope #0: Local: chained + > this: Window + x: 'x1' + > scope #1: Closure (chain) + n: 1 + scope #2: Global [expensive] + +Window.chained @ /VM:11:23 + > scope #0: Local: chained + > this: Window + x: 'x2' + > scope #1: Closure (chain) + n: 2 + scope #2: Global [expensive] + +Window.chained @ /VM:11:23 + > scope #0: Local: chained + > this: Window + x: 'x3' + > scope #1: Closure (chain) + n: 3 + scope #2: Global [expensive] + + @ /VM:14:15 + scope #0: Global [expensive] diff --git a/code/extensions/js-debug/src/test/stacks/stacks-async-disables.txt b/code/extensions/js-debug/src/test/stacks/stacks-async-disables.txt new file mode 100644 index 000000000000..3362bb1914c1 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-async-disables.txt @@ -0,0 +1,12 @@ + +Window.foo @ /VM:4:11 + > scope #0: Local: foo + n: 0 + > this: Window + scope #1: Global [expensive] + +Window.bar @ /VM:13:15 + > scope #0: Local: bar + n: 0 + > this: Window + scope #1: Global [expensive] diff --git a/code/extensions/js-debug/src/test/stacks/stacks-async.txt b/code/extensions/js-debug/src/test/stacks/stacks-async.txt new file mode 100644 index 000000000000..f65d52b754b1 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-async.txt @@ -0,0 +1,24 @@ + +Window.foo @ /VM:4:11 + > scope #0: Local: foo + n: 0 + > this: Window + scope #1: Global [expensive] + +Window.bar @ /VM:13:15 + > scope #0: Local: bar + n: 0 + > this: Window + scope #1: Global [expensive] + +----await---- + @ /VM:8:11 + scope error: Variables not available in async stacks +----setTimeout---- +foo @ /VM:7:9 + scope error: Variables not available in async stacks +bar @ /VM:13:15 + scope error: Variables not available in async stacks +----await---- + @ /VM:15:7 + scope error: Variables not available in async stacks diff --git a/code/extensions/js-debug/src/test/stacks/stacks-cross-target.txt b/code/extensions/js-debug/src/test/stacks/stacks-cross-target.txt new file mode 100644 index 000000000000..801e3f05b10e --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-cross-target.txt @@ -0,0 +1,13 @@ + + @ ${workspaceFolder}/web/worker.html:6:5 + > scope #0: Local + > e: MessageEvent {isTrusted: true, data: 'pause', origin: '', lastEventId: '', source: null, …} + this: undefined + scope #1: Global [expensive] + +----postMessage---- + @ ${workspaceFolder}/web/worker.js:6:5 + scope error: Variables not available in async stacks +----Worker.postMessage---- + @ /VM:1:10 + scope error: Variables not available in async stacks diff --git a/code/extensions/js-debug/src/test/stacks/stacks-eval-in-anonymous.txt b/code/extensions/js-debug/src/test/stacks/stacks-eval-in-anonymous.txt new file mode 100644 index 000000000000..af2442b014c2 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-eval-in-anonymous.txt @@ -0,0 +1 @@ + @ eval.js:3:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-return-value.txt b/code/extensions/js-debug/src/test/stacks/stacks-return-value.txt new file mode 100644 index 000000000000..7bf84b4470c2 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-return-value.txt @@ -0,0 +1,9 @@ + +Window.foo @ /VM:4:19 + > scope #0: Local: foo + Return value: 42 + > this: Window + scope #1: Global [expensive] + + @ /VM:6:7 + scope #0: Global [expensive] diff --git a/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-handles-special-chars-in-stack-203408.txt b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-handles-special-chars-in-stack-203408.txt new file mode 100644 index 000000000000..6209f962045e --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-handles-special-chars-in-stack-203408.txt @@ -0,0 +1,11 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${fixturesDir}/test.js:1:74 +exports.foo @ ${fixturesDir}/nested/a.js:1:23 + @ ${fixturesDir}/test.js:1:62 +exports.foo @ ${fixturesDir}/@nested/a.js:1:23 + @ ${fixturesDir}/test.js:1:27 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-multiple-authored-ts-to-js.txt b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-multiple-authored-ts-to-js.txt new file mode 100644 index 000000000000..ffb4f9062e2b --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-multiple-authored-ts-to-js.txt @@ -0,0 +1,9 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ /VM:1:25 +Window.bar @ ${workspaceFolder}/web/browserify/module2.ts:3:3 + @ /VM:1:8 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-single-authored-js.txt b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-single-authored-js.txt new file mode 100644 index 000000000000..f3ae60ab863d --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-single-authored-js.txt @@ -0,0 +1,3 @@ +Window.bar @ ${workspaceFolder}/web/script.js:6:3 +Window.foo @ ${workspaceFolder}/web/script.js:2:3 + @ ${workspaceFolder}/web/script.js:9:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-single-compiled-js.txt b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-single-compiled-js.txt new file mode 100644 index 000000000000..fe5e8bb420ab --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-single-compiled-js.txt @@ -0,0 +1,4 @@ +plusTwo @ ${workspaceFolder}/web/basic.js:3:5 +printArr @ ${workspaceFolder}/web/basic.js:7:21 +abcdef @ ${workspaceFolder}/web/basic.js:17:5 + @ ${workspaceFolder}/web/basic.js:19:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-toggle-authored-ts.txt b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-toggle-authored-ts.txt new file mode 100644 index 000000000000..b04494cdfc00 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-toggle-authored-ts.txt @@ -0,0 +1,5 @@ + @ ${workspaceFolder}/web/basic.ts:21:1 +----send toggle skipfile status request---- + @ ${workspaceFolder}/web/basic.ts:21:1 +----send (un)toggle skipfile status request---- + @ ${workspaceFolder}/web/basic.ts:21:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-works-with-absolute-paths-470.txt b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-works-with-absolute-paths-470.txt new file mode 100644 index 000000000000..fe5e8bb420ab --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-skipfiles-works-with-absolute-paths-470.txt @@ -0,0 +1,4 @@ +plusTwo @ ${workspaceFolder}/web/basic.js:3:5 +printArr @ ${workspaceFolder}/web/basic.js:7:21 +abcdef @ ${workspaceFolder}/web/basic.js:17:5 + @ ${workspaceFolder}/web/basic.js:19:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-smart-step-manual-breakpoints.txt b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-smart-step-manual-breakpoints.txt new file mode 100644 index 000000000000..f81e3c51a7f7 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-smart-step-manual-breakpoints.txt @@ -0,0 +1,3 @@ +foo @ ${workspaceFolder}/web/smartStep/exceptionBp.js:9:3 +bar @ ${workspaceFolder}/web/smartStep/exceptionBp.js:3:5 + @ ${workspaceFolder}/web/smartStep/exceptionBp.js:5:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-smart-step-on-exception-breakpoints.txt b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-smart-step-on-exception-breakpoints.txt new file mode 100644 index 000000000000..d9f90c159c01 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-smart-step-on-exception-breakpoints.txt @@ -0,0 +1,3 @@ +foo @ ${workspaceFolder}/web/smartStep/exceptionBp.js:9:3 +bar @ ${workspaceFolder}/web/smartStep/exceptionBp.ts:4:3 + @ ${workspaceFolder}/web/smartStep/exceptionBp.ts:7:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-step-in-sources-missing-maps.txt b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-step-in-sources-missing-maps.txt new file mode 100644 index 000000000000..b7336cbd6b27 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-does-not-step-in-sources-missing-maps.txt @@ -0,0 +1,11 @@ +Evaluating#1: debugger; doCallback(() => { + console.log("hi"); + }); +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +Window.doCallback @ ${workspaceFolder}/web/smartStep/missingMap.js:2:5 + @ localhost꞉8001/eval1.js:1:11 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-smartstep-remembers-step-direction-in.txt b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-remembers-step-direction-in.txt new file mode 100644 index 000000000000..667a8c71bfe0 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-remembers-step-direction-in.txt @@ -0,0 +1,12 @@ +Evaluating#1: doCall(() => { mapped1(); mapped2(); }) +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic291cmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic291cmNlLnRzIl0sIm1hcHBpbmdzIjoiIn0= +mapped1 @ ${workspaceFolder}/web/smartStep/directional.ts:2:3 + @ localhost꞉8001/eval1.js:1:16 +doCall @ ${workspaceFolder}/web/smartStep/directional.ts:10:3 + @ localhost꞉8001/eval1.js:1:1 + +# stepping in +mapped2 @ ${workspaceFolder}/web/smartStep/directional.ts:6:3 + @ localhost꞉8001/eval1.js:1:27 +doCall @ ${workspaceFolder}/web/smartStep/directional.ts:10:3 + @ localhost꞉8001/eval1.js:1:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-smartstep-remembers-step-direction-out.txt b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-remembers-step-direction-out.txt new file mode 100644 index 000000000000..a5148659b4e0 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-remembers-step-direction-out.txt @@ -0,0 +1,10 @@ +Evaluating#1: doCall(() => { mapped1(); mapped2(); }) +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic291cmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic291cmNlLnRzIl0sIm1hcHBpbmdzIjoiIn0= +mapped1 @ ${workspaceFolder}/web/smartStep/directional.ts:2:3 + @ localhost꞉8001/eval1.js:1:16 +doCall @ ${workspaceFolder}/web/smartStep/directional.ts:10:3 + @ localhost꞉8001/eval1.js:1:1 + +# stepping out +doCall @ ${workspaceFolder}/web/smartStep/directional.ts:11:1 + @ localhost꞉8001/eval1.js:1:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-smartstep-simple-stepping.txt b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-simple-stepping.txt new file mode 100644 index 000000000000..003e04c3ae61 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-smartstep-simple-stepping.txt @@ -0,0 +1,27 @@ + @ ${workspaceFolder}/web/smartStep/async.ts:8:13 + @ ${workspaceFolder}/web/smartStep/async.js:7:71 +Window.__awaiter @ ${workspaceFolder}/web/smartStep/async.js:3:12 +Window.main @ ${workspaceFolder}/web/smartStep/async.js:17:12 + @ ${workspaceFolder}/web/smartStep/async.ts:11:1 + +# step in 1x + @ ${workspaceFolder}/web/smartStep/async.ts:2:11 + @ ${workspaceFolder}/web/smartStep/async.js:7:71 +Window.__awaiter @ ${workspaceFolder}/web/smartStep/async.js:3:12 +Window.foo @ ${workspaceFolder}/web/smartStep/async.js:11:12 + @ ${workspaceFolder}/web/smartStep/async.ts:8:19 + @ ${workspaceFolder}/web/smartStep/async.js:7:71 +Window.__awaiter @ ${workspaceFolder}/web/smartStep/async.js:3:12 +Window.main @ ${workspaceFolder}/web/smartStep/async.js:17:12 + @ ${workspaceFolder}/web/smartStep/async.ts:11:1 + +# step in 2x + @ ${workspaceFolder}/web/smartStep/async.ts:3:11 + @ ${workspaceFolder}/web/smartStep/async.js:7:71 +Window.__awaiter @ ${workspaceFolder}/web/smartStep/async.js:3:12 +Window.foo @ ${workspaceFolder}/web/smartStep/async.js:11:12 + @ ${workspaceFolder}/web/smartStep/async.ts:8:19 + @ ${workspaceFolder}/web/smartStep/async.js:7:71 +Window.__awaiter @ ${workspaceFolder}/web/smartStep/async.js:3:12 +Window.main @ ${workspaceFolder}/web/smartStep/async.js:17:12 + @ ${workspaceFolder}/web/smartStep/async.ts:11:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-smartstep.txt b/code/extensions/js-debug/src/test/stacks/stacks-smartstep.txt new file mode 100644 index 000000000000..50418e8d9efb --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-smartstep.txt @@ -0,0 +1,14 @@ + @ ${workspaceFolder}/web/async-ts/test.ts:8:13 + @ ${workspaceFolder}/web/async-ts/test.js:7:71 +__awaiter @ ${workspaceFolder}/web/async-ts/test.js:3:12 +main @ ${workspaceFolder}/web/async-ts/test.js:17:12 + @ ${workspaceFolder}/web/async-ts/test.ts:11:1 + @ ${workspaceFolder}/web/async-ts/test.ts:2:11 + @ ${workspaceFolder}/web/async-ts/test.js:7:71 +__awaiter @ ${workspaceFolder}/web/async-ts/test.js:3:12 +foo @ ${workspaceFolder}/web/async-ts/test.js:11:12 + @ ${workspaceFolder}/web/async-ts/test.ts:8:19 + @ ${workspaceFolder}/web/async-ts/test.js:7:71 +__awaiter @ ${workspaceFolder}/web/async-ts/test.js:3:12 +main @ ${workspaceFolder}/web/async-ts/test.js:17:12 + @ ${workspaceFolder}/web/async-ts/test.ts:11:1 diff --git a/code/extensions/js-debug/src/test/stacks/stacks-source-map.txt b/code/extensions/js-debug/src/test/stacks/stacks-source-map.txt new file mode 100644 index 000000000000..de97bef2a858 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-source-map.txt @@ -0,0 +1,58 @@ + +foo @ ${workspaceFolder}/web/browserify/module1.ts:3:3 + > scope #0: Local: foo + this: undefined + scope #1: Global [expensive] + +Object.bar @ ${workspaceFolder}/web/browserify/module2.ts:3:3 + > scope #0: Local: bar + +> callback: ƒ foo() { + debugger; +} + > this: Object + scope #1: Global [expensive] + +3../module1 @ ${workspaceFolder}/web/browserify/pause.ts:4:4 + > scope #0: Local: 3../module1 + > exports: {__esModule: true} + > m1: {__esModule: true, kModule1: 1, foo: ƒ, throwError: ƒ, throwValue: ƒ} + > m2: {__esModule: true, kModule2: 2, bar: ƒ, pause: ƒ} + > module: {exports: {…}} + > require: ƒ (r){var n=e[i][1][r];return o(n||r)} + > this: Object + scope #1: Global [expensive] + +Window.o @ ${workspaceFolder}/node_modules/browser-pack/_prelude.js:1:1 + > scope #0: Local: o + a: undefined + c: undefined + f: undefined + i: 3 + > p: {exports: {…}} + > this: Window + > scope #1: Closure (r) + > e: {1: Array(2), 2: Array(2), 3: Array(2)} + > n: {1: {…}, 2: {…}, 3: {…}} + > o: ƒ o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports} + > t: (1) [3] + u: false + > scope #2: Closure + > r: ƒ r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i scope #0: Local: r + > e: {1: Array(2), 2: Array(2), 3: Array(2)} + i: 0 + > n: {1: {…}, 2: {…}, 3: {…}} + > o: ƒ o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports} + > t: (1) [3] + > this: Window + u: false + > scope #1: Closure + > r: ƒ r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i @ ${workspaceFolder}/node_modules/browser-pack/_prelude.js:1:1 + scope #0: Global [expensive] diff --git a/code/extensions/js-debug/src/test/stacks/stacks-uses-custom-descriptions-in-frame-names.txt b/code/extensions/js-debug/src/test/stacks/stacks-uses-custom-descriptions-in-frame-names.txt new file mode 100644 index 000000000000..824b8d221943 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacks-uses-custom-descriptions-in-frame-names.txt @@ -0,0 +1,3 @@ +Custom.method2 @ /VM:4:13 +Foo.method1 @ /VM:14:30 + @ /VM:18:19 diff --git a/code/extensions/js-debug/src/test/stacks/stacksTest.ts b/code/extensions/js-debug/src/test/stacks/stacksTest.ts new file mode 100644 index 000000000000..c4927d1916e7 --- /dev/null +++ b/code/extensions/js-debug/src/test/stacks/stacksTest.ts @@ -0,0 +1,397 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { delay } from '../../common/promiseUtil'; +import { Dap } from '../../dap/api'; +import { createFileTree } from '../createFileTree'; +import { testFixturesDir, TestP, testWorkspace } from '../test'; +import { itIntegrates, waitForPause } from '../testIntegrationUtils'; + +describe('stacks', () => { + async function dumpStackAndContinue(p: TestP, scopes: boolean) { + const event = await p.dap.once('stopped'); + await p.logger.logStackTrace(event.threadId!, scopes ? Infinity : 0); + await p.dap.continue({ threadId: event.threadId! }); + } + + itIntegrates('eval in anonymous', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ expression: '\n\ndebugger;\n//# sourceURL=eval.js' }); + await dumpStackAndContinue(p, false); + p.assertLog(); + }); + + itIntegrates('anonymous initial script', async ({ r }) => { + const p = await r.launch(''); + p.load(); + await dumpStackAndContinue(p, false); + p.assertLog(); + }); + + itIntegrates('anonymous scopes', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + function paused() { + let y = 'paused'; + debugger; + } + function chain(n) { + if (!n) + return paused; + return function chained() { + let x = 'x' + n; + chain(n - 1)(); + }; + } + chain(3)(); + `, + }); + await dumpStackAndContinue(p, true); + p.assertLog(); + }); + + itIntegrates('async', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + function foo(n) { + if (!n) { + debugger; + return; + } + setTimeout(() => { + bar(n - 1); + }, 0); + } + async function bar(n) { + await Promise.resolve(15); + await foo(n); + } + bar(1); + `, + }); + await dumpStackAndContinue(p, true); + p.assertLog(); + }); + + itIntegrates('async disables', async ({ r }) => { + const p = await r.launchAndLoad('blank', { showAsyncStacks: false }); + p.cdp.Runtime.evaluate({ + expression: ` + function foo(n) { + if (!n) { + debugger; + return; + } + setTimeout(() => { + bar(n - 1); + }, 0); + } + async function bar(n) { + await Promise.resolve(15); + await foo(n); + } + bar(1); + `, + }); + await dumpStackAndContinue(p, true); + p.assertLog(); + }); + + itIntegrates('cross target', async ({ r }) => { + const p = await r.launchUrlAndLoad('worker.html'); + p.cdp.Runtime.evaluate({ expression: `window.w.postMessage('pause')` }); + await dumpStackAndContinue(p, true); + p.assertLog(); + }); + + itIntegrates('source map', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.addScriptTag('browserify/pause.js'); + await dumpStackAndContinue(p, true); + p.assertLog(); + }); + + describe('smartStep', () => { + const emptySourceMapContents = Buffer.from( + JSON.stringify({ + version: 3, + file: 'source.js', + sourceRoot: '', + sources: ['source.ts'], + mappings: '', + }), + ).toString('base64'); + + const emptySourceMap = `//# sourceMappingURL=data:application/json;charset=utf-8;base64,` + + emptySourceMapContents; + + itIntegrates('simple stepping', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.addScriptTag('smartStep/async.js'); + const { threadId } = await p.dap.once('stopped'); + await p.dap.next({ threadId: threadId! }); + + await p.dap.once('stopped'); + await p.logger.logStackTrace(threadId!); + + p.log('\n# step in 1x'); + p.dap.stepIn({ threadId: threadId! }); + await p.dap.once('stopped'); + await p.logger.logStackTrace(threadId!); + + p.log('\n# step in 2x'); + p.dap.stepIn({ threadId: threadId! }); + await dumpStackAndContinue(p, false); + p.assertLog(); + }); + + itIntegrates('remembers step direction out', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await p.addScriptTag('smartStep/directional.js'); + await p.waitForSource('directional.ts'); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/smartStep/directional.ts') }, + breakpoints: [{ line: 2, column: 0 }], + }); + + const result = p.evaluate(`doCall(() => { mapped1(); mapped2(); })\n${emptySourceMap}`); + const { threadId } = await p.dap.once('stopped'); + await p.logger.logStackTrace(threadId!); + await p.dap.stepOut({ threadId: threadId! }); + p.logger.logAsConsole('\n# stepping out\n'); + + await p.dap.once('stopped'); + await p.logger.logStackTrace(threadId!); + await p.dap.continue({ threadId: threadId! }); + await result; + p.assertLog(); + }); + + itIntegrates('remembers step direction in', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await p.addScriptTag('smartStep/directional.js'); + await p.waitForSource('directional.ts'); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/smartStep/directional.ts') }, + breakpoints: [{ line: 2, column: 0 }], + }); + + const result = p.evaluate(`doCall(() => { mapped1(); mapped2(); })\n${emptySourceMap}`); + const { threadId } = await p.dap.once('stopped'); + await p.logger.logStackTrace(threadId!); + + for (let i = 0; i < 2; i++) { + await p.dap.stepIn({ threadId: threadId! }); + await p.dap.once('stopped'); + } + + p.logger.logAsConsole('\n# stepping in\n'); + await p.logger.logStackTrace(threadId!); + await p.dap.continue({ threadId: threadId! }); + await result; + p.assertLog(); + }); + + itIntegrates('does not smart step on exception breakpoints', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await p.dap.setExceptionBreakpoints({ filters: ['uncaught', 'all'] }); + p.addScriptTag('smartStep/exceptionBp.js'); + await dumpStackAndContinue(p, false); + p.assertLog(); + }); + + itIntegrates('does not smart step manual breakpoints', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/smartStep/exceptionBp.js') }, + breakpoints: [{ line: 9, column: 0 }], + }); + p.addScriptTag('smartStep/exceptionBp.js'); + await dumpStackAndContinue(p, false); + p.assertLog(); + }); + + itIntegrates('does not step in sources missing maps', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await p.addScriptTag('smartStep/missingMap.js'); + const evaluated = p.evaluate(`debugger; doCallback(() => { + console.log("hi"); + });`); + + let threadId = (await p.dap.once('stopped')).threadId!; + await p.dap.stepIn({ threadId }); + + threadId = (await p.dap.once('stopped')).threadId!; + await p.dap.stepIn({ threadId }); + + await waitForPause(p); + await evaluated; + p.assertLog(); + }); + }); + + itIntegrates('return value', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + function foo() { + debugger; + return 42; + } + foo(); + `, + }); + const { threadId } = await p.dap.once('stopped'); // debugger + p.dap.next({ threadId: threadId! }); + await p.dap.once('stopped'); // return 42 + p.dap.next({ threadId: threadId! }); + await dumpStackAndContinue(p, true); // exit point + p.assertLog(); + }); + + describe('skipFiles', () => { + async function waitForPausedThenDelayStackTrace(p: TestP, scopes: boolean) { + const event = await p.dap.once('stopped'); + await delay(200); // need to pause test to let debouncer update scripts + await p.logger.logStackTrace(event.threadId!, scopes ? Infinity : 0); + return event; + } + + itIntegrates('single authored js', async ({ r }) => { + const p = await r.launchUrl('script.html', { skipFiles: ['**/script.js'] }); + const source: Dap.Source = { + path: p.workspacePath('web/script.js'), + }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 6, column: 0 }] }); + p.load(); + await waitForPausedThenDelayStackTrace(p, false); + p.assertLog(); + }); + + itIntegrates('single compiled js', async ({ r }) => { + const p = await r.launchUrlAndLoad('basic.html', { skipFiles: ['**/basic.js'] }); + const source: Dap.Source = { + path: p.workspacePath('web/basic.js'), + }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 3, column: 0 }] }); + p.load(); + await waitForPausedThenDelayStackTrace(p, false); + p.assertLog(); + }); + + itIntegrates('multiple authored ts to js', async ({ r }) => { + const p = await r.launchUrlAndLoad('browserify/browserify.html', { + skipFiles: ['**/module*.ts'], + }); + const evaluate = p.dap.evaluate({ + expression: 'window.callBack(() => { debugger });\nconsole.log("out");', + }); + + await waitForPause(p); + await evaluate; + p.assertLog(); + }); + + itIntegrates('works with absolute paths (#470)', async ({ r }) => { + const p = await r.launchUrl('basic.html', { + skipFiles: [`${testWorkspace}/web/basic.js`], + }); + const source: Dap.Source = { + path: p.workspacePath('web/basic.js'), + }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 3, column: 0 }] }); + p.load(); + await waitForPausedThenDelayStackTrace(p, false); + p.assertLog(); + }); + + itIntegrates('toggle authored ts', async ({ r }) => { + const p = await r.launchUrl('basic.html'); + const path = p.workspacePath('web/basic.ts'); + const source: Dap.Source = { + path: path, + }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 21, column: 0 }] }); + p.load(); + + const event = await p.dap.once('stopped'); + await delay(500); // need to pause test to let debouncer update scripts + await p.logger.logStackTrace(event.threadId!); + + p.log('----send toggle skipfile status request----'); + await p.dap.toggleSkipFileStatus({ resource: path }); + await p.logger.logStackTrace(event.threadId!); + + p.log('----send (un)toggle skipfile status request----'); + await p.dap.toggleSkipFileStatus({ resource: path }); + await p.logger.logStackTrace(event.threadId!); + + p.assertLog(); + }); + + itIntegrates('handles special chars in stack (#203408)', async ({ r }) => { + createFileTree(testFixturesDir, { + 'nested/a.js': 'exports.foo = (fn) => fn()', + '@nested/a.js': 'exports.foo = (fn) => fn()', + 'test.js': [ + 'require("./@nested/a.js").foo(() => require("./nested/a.js").foo(() => { debugger }))', + ], + }); + const handle = await r.runScript('test.js', { + skipFiles: ['**/a.js', '!**/@nested/**'], + }); + handle.load(); + await waitForPause(handle); + handle.assertLog({ substring: true }); + }); + }); + + itIntegrates('uses custom descriptions in frame names', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + class Bar { + method2() { + debugger; + } + + toString() { + return 'Custom'; + } + } + + class Foo { + method1() { + return new Bar().method2(); + } + } + + new Foo().method1(); + `, + }); + + await dumpStackAndContinue(p, false); + p.assertLog(); + }); + + itIntegrates('shows sourcemapped stack during shutdown', async ({ r }) => { + createFileTree(testFixturesDir, { + 'input.js': [ + "console.log(new Error('asdf'));", + "throw new Error('asdf');", + '//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbnB1dC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7QUFDL0IsTUFBTSxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyJ9', + ], + 'input.ts': ["console.log(new Error('asdf'));", "throw new Error('asdf');"], + }); + const handle = await r.runScript('input.js'); + handle.load(); + const output1 = (await handle.dap.once('output'))?.output; + expect(output1).to.contain('input.ts'); + const output2 = (await handle.dap.once('output'))?.output; + expect(output2).to.contain('input.ts'); + }); +}); diff --git a/code/extensions/js-debug/src/test/test.ts b/code/extensions/js-debug/src/test/test.ts new file mode 100644 index 000000000000..aaeefd25107b --- /dev/null +++ b/code/extensions/js-debug/src/test/test.ts @@ -0,0 +1,644 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { promises as fs } from 'fs'; +import * as gulp from 'gulp'; +import { tmpdir } from 'os'; +import * as path from 'path'; +import * as stream from 'stream'; +import { ExtensionContext } from 'vscode'; +import { DebugAdapter } from '../adapter/debugAdapter'; +import { Binder } from '../binder'; +import Cdp from '../cdp/api'; +import CdpConnection from '../cdp/connection'; +import { DebugType } from '../common/contributionUtils'; +import { EventEmitter } from '../common/events'; +import { ILogger } from '../common/logging'; +import { upcastPartial } from '../common/objUtils'; +import { forceForwardSlashes } from '../common/pathUtils'; +import * as utils from '../common/urlUtils'; +import { + AnyChromiumLaunchConfiguration, + chromeLaunchConfigDefaults, + INodeAttachConfiguration, + INodeLaunchConfiguration, + nodeAttachConfigDefaults, + nodeLaunchConfigDefaults, +} from '../configuration'; +import Dap from '../dap/api'; +import DapConnection from '../dap/connection'; +import { StreamDapTransport } from '../dap/transport'; +import { createGlobalContainer, createTopLevelSessionContainer } from '../ioc'; +import { BrowserLauncher } from '../targets/browser/browserLauncher'; +import { BrowserTarget } from '../targets/browser/browserTargets'; +import { TargetOrigin } from '../targets/targetOrigin'; +import { ITarget } from '../targets/targets'; +import { GoldenText } from './goldenText'; +import { Logger } from './logger'; +import { getLogFileForTest } from './reporters/logReporterUtils'; + +export const kStabilizeNames = ['id', 'threadId', 'sourceReference', 'variablesReference']; + +export const workspaceFolder = path.join(__dirname, '..', '..'); +export const testWorkspace = path.join(workspaceFolder, 'testWorkspace'); +export const testSources = path.join(workspaceFolder, 'src'); +export const testFixturesDirName = '.dynamic-testWorkspace'; +export const testFixturesDir = path.join(workspaceFolder, testFixturesDirName); + +/** + * Replaces the `/private` folder prefix, which OS X likes to add for the + * user's tmpdir while require('os').tmpdir() returns the path without + * the prefix, which causes mismatch. + */ +export const removePrivatePrefix = (folder: string) => + process.platform === 'darwin' ? folder.replace(/^\/private/, '') : folder; + +class Stream extends stream.Duplex { + _write(chunk: any, encoding: BufferEncoding, callback: (err?: Error) => void): void { + Promise.resolve() + .then() + .then() + .then() + .then() + .then() + .then() + .then() + .then() + .then() + .then() + .then(() => { + this.push(chunk, encoding); + callback(); + }); + } + + _read(size: number) { + // no-op + } +} + +export type Log = (value: any, title?: string, stabilizeNames?: string[]) => typeof value; + +export type AssertLog = GoldenText['assertLog']; + +class Session { + readonly dap: Dap.TestApi; + readonly adapterConnection: DapConnection; + + constructor(logger: ILogger) { + const testToAdapter = new Stream(); + const adapterToTest = new Stream(); + + this.adapterConnection = new DapConnection( + new StreamDapTransport(testToAdapter, adapterToTest, logger), + logger, + ); + const testConnection = new DapConnection( + new StreamDapTransport(adapterToTest, testToAdapter, logger), + logger, + ); + this.dap = testConnection.createTestApi(); + } + + async _init(): Promise { + const [r] = await Promise.all([ + this.dap.initialize({ + clientID: 'pwa-test', + adapterID: 'pwa', + linesStartAt1: true, + columnsStartAt1: true, + pathFormat: 'path', + supportsVariablePaging: true, + supportsANSIStyling: true, + }), + this.dap.once('initialized'), + ]); + return r; + } +} + +/** + * Test handle for a Chrome or Node debug sessions/ + */ +export interface ITestHandle { + readonly cdp: Cdp.Api; + readonly adapter: DebugAdapter; + readonly logger: Logger; + readonly dap: Dap.TestApi; + readonly log: Log; + readonly assertLog: AssertLog; + + load(): Promise; + _init( + adapter: DebugAdapter, + target: ITarget, + launcher: BrowserLauncher, + ): Promise; +} + +export class TestP implements ITestHandle { + readonly dap: Dap.TestApi; + readonly logger: Logger; + readonly log: Log; + readonly assertLog: AssertLog; + + _session: Session; + _adapter?: DebugAdapter; + private _root: TestRoot; + private _evaluateCounter = 0; + private _connection: CdpConnection | undefined; + private _cdp: Cdp.Api | undefined; + private _target: ITarget; + + constructor(root: TestRoot, target: ITarget) { + this._root = root; + this._target = target; + this.log = root.log; + this.assertLog = root.assertLog; + this._session = new Session(root.logger); + this.dap = this._session.dap; + this.logger = new Logger(this.dap, this.log); + } + + get cdp(): Cdp.Api { + return this._cdp!; + } + + get adapter(): DebugAdapter { + return this._adapter!; + } + + async evaluate(expression: string, sourceUrl?: string): Promise { + ++this._evaluateCounter; + this.log(`Evaluating#${this._evaluateCounter}: ${expression}`); + if (sourceUrl === undefined) sourceUrl = `//# sourceURL=eval${this._evaluateCounter}.js`; + else if (sourceUrl) sourceUrl = `//# sourceURL=${this.completeUrl(sourceUrl)}`; + return this._cdp!.Runtime.evaluate({ expression: expression + `\n${sourceUrl}` }).then( + result => { + if (!result) { + this.log(expression, 'Error evaluating'); + debugger; + throw new Error('Error evaluating "' + expression + '"'); + } else if (result.exceptionDetails) { + this.log(result.exceptionDetails, 'Error evaluating'); + debugger; + throw new Error('Error evaluating "' + expression + '"'); + } + return result; + }, + ); + } + + async addScriptTag(relativePath: string): Promise { + await this._cdp!.Runtime.evaluate({ + expression: ` + new Promise(f => { + var script = document.createElement('script'); + script.src = '${this._root.completeUrl(relativePath)}'; + script.onload = () => f(undefined); + document.head.appendChild(script); + }) + `, + awaitPromise: true, + }); + } + + waitForSource(filter?: string): Promise { + return this.dap.once('loadedSource', event => { + return filter === undefined || (event.source.path || '').indexOf(filter) !== -1; + }); + } + + completeUrl(relativePath: string): string { + return this._root.completeUrl(relativePath); + } + + workspacePath(relative: string): string { + return this._root.workspacePath(relative); + } + + async _init( + adapter: DebugAdapter, + _target: ITarget, + launcher: BrowserLauncher, + ) { + adapter.breakpointManager.setPredictorDisabledForTest(true); + adapter.sourceContainer.setSourceMapTimeouts({ + load: 0, + resolveLocation: 2000, + sourceMapMinPause: 1000, + output: 3000, + sourceMapCumulativePause: 10000, + }); + this._adapter = adapter; + + this._root._browserLauncher = launcher; + this._connection = this._root._browserLauncher?.connectionForTest()!; + const result = await this._connection.rootSession().Target.attachToBrowserTarget({}); + const testSession = this._connection.createSession(result!.sessionId); + const { sessionId } = (await testSession.Target.attachToTarget({ + targetId: this._target instanceof BrowserTarget ? this._target.targetId : this._target.id(), + flatten: true, + }))!; + this._cdp = this._connection.createSession(sessionId); + await this._session._init(); + if (this._target.parent()) { + this.dap.configurationDone({}); + this.dap.attach({}); + } + + return false; + } + + async load() { + await this.dap.configurationDone({}); + await this.dap.attach({}); + this._cdp!.Page.enable({}); + this._cdp!.Page.navigate({ url: this._root._launchUrl! }); + await new Promise(f => this._cdp!.Page.on('frameStoppedLoading', f)); + await this._cdp!.Page.disable({}); + } +} + +export class NodeTestHandle implements ITestHandle { + readonly dap: Dap.TestApi; + readonly logger: Logger; + readonly log: Log; + readonly assertLog: AssertLog; + + _session: Session; + _adapter?: DebugAdapter; + private _root: TestRoot; + private _cdp: Cdp.Api | undefined; + private _target: ITarget; + + constructor(root: TestRoot, target: ITarget) { + this._root = root; + this._target = target; + this.log = root.log; + this.assertLog = root.assertLog; + this._session = new Session(root.logger); + this.dap = this._session.dap; + this.logger = new Logger(this.dap, this.log); + } + + get cdp(): Cdp.Api { + return this._cdp!; + } + + get adapter(): DebugAdapter { + return this._adapter!; + } + + waitForSource(filter?: string): Promise { + return this.dap.once('loadedSource', event => { + return filter === undefined + || forceForwardSlashes(event.source.path || '').includes(filter); + }); + } + + workspacePath(relative: string): string { + return this._root.workspacePath(relative); + } + + async _init(adapter: DebugAdapter, target: ITarget) { + this._adapter = adapter; + await this._session._init(); + if (this._target.parent()) { + this.dap.configurationDone({}); + this.dap.attach({}); + } + + return true; + } + + async load() { + await this.dap.configurationDone({}); + } +} + +export class TestRoot { + readonly initialize: Promise; + readonly log: Log; + readonly assertLog: AssertLog; + + private _targetToP = new Map(); + private _root: Session; + private _workspaceRoot: string; + private _webRoot: string | undefined; + _launchUrl: string | undefined; + private _args: string[]; + + private _worker: Promise; + private _workerCallback: (session: ITestHandle) => void; + private _launchCallback: (session: ITestHandle) => void; + + _browserLauncher: BrowserLauncher | undefined; + readonly binder: Binder; + + private _onSessionCreatedEmitter = new EventEmitter(); + readonly onSessionCreated = this._onSessionCreatedEmitter.event; + public readonly logger: ILogger; + + constructor(goldenText: GoldenText, private _testTitlePath: string) { + this._args = ['--headless']; + this.log = goldenText.log.bind(goldenText); + this.assertLog = goldenText.assertLog.bind(goldenText); + this._workspaceRoot = utils.platformPathToPreferredCase(testWorkspace); + this._webRoot = path.join(this._workspaceRoot, 'web'); + + const storagePath = path.join(__dirname, '..', '..'); + // todo: make a more proper mock here + const workspaceState = new Map(); + const services = createTopLevelSessionContainer( + createGlobalContainer({ + storagePath, + isVsCode: true, + context: upcastPartial({ + workspaceState: { + keys: () => [], + get(key: string, defaultValue?: T) { + return workspaceState.get(key) ?? defaultValue; + }, + update(key: string, value: unknown) { + workspaceState.set(key, value); + return Promise.resolve(); + }, + }, + }), + }), + ); + + this.logger = services.get(ILogger); + this._root = new Session(this.logger); + const dap = this._root.adapterConnection.dap(); + dap.on('initialize', async () => { + dap.initialized({}); + return DebugAdapter.capabilities(); + }); + dap.on('configurationDone', async () => { + return {}; + }); + + this.binder = new Binder(this, this._root.adapterConnection, services, new TargetOrigin('0')); + + this.initialize = this._root._init(); + + this._launchCallback = () => {}; + this._workerCallback = () => {}; + this._worker = new Promise(f => (this._workerCallback = f)); + } + + public async acquireDap(target: ITarget): Promise { + const p = target.type() === 'page' + ? new TestP(this, target) + : new NodeTestHandle(this, target); + this._targetToP.set(target, p); + return p._session.adapterConnection; + } + + async initAdapter( + adapter: DebugAdapter, + target: ITarget, + launcher: BrowserLauncher, + ): Promise { + const p = this._targetToP.get(target); + if (!p) { + return true; + } + + const boot = await p._init(adapter, target, launcher); + if (target.parent()) this._workerCallback(p); + else this._launchCallback(p); + this._onSessionCreatedEmitter.fire(p); + return boot; + } + + releaseDap(target: ITarget) { + this._targetToP.delete(target); + } + + setArgs(args: string[]) { + this._args = args; + } + + worker(): Promise { + return this._worker; + } + + /** + * Returns the root session DAP connection. + */ + rootDap() { + return this._root.dap; + } + + async waitForTopLevel() { + const result = await new Promise(f => (this._launchCallback = f)); + return result as TestP; + } + + async _launch( + url: string, + options: Partial = {}, + ): Promise { + await this.initialize; + this._launchUrl = url; + + // playwright does not expose the executable path for the headless shell + const exe = require('playwright-core/lib/server').registry.findExecutable( + 'chromium-headless-shell', + ); + + const tmpLogPath = getLogFileForTest(this._testTitlePath); + this._root.dap.launch({ + ...chromeLaunchConfigDefaults, + url, + runtimeArgs: this._args, + webRoot: this._webRoot, + rootPath: this._workspaceRoot, + skipNavigateForTest: true, + trace: { logFile: tmpLogPath }, + runtimeExecutable: exe.executablePathOrDie(), + outFiles: [`${this._workspaceRoot}/**/*.js`, '!**/node_modules/**'], + __workspaceFolder: this._workspaceRoot, + cleanUp: 'wholeBrowser', // We want the tests to clean up chrome afterwards + ...options, + } as AnyChromiumLaunchConfiguration); + + const result = await new Promise(f => (this._launchCallback = f)); + return result as TestP; + } + + async runScript( + filename: string, + options: Partial = {}, + ): Promise { + await this.initialize; + this._launchUrl = path.isAbsolute(filename) ? filename : path.join(testFixturesDir, filename); + + const tmpLogPath = getLogFileForTest(this._testTitlePath); + this._root.dap.launch({ + type: DebugType.Node, + request: 'launch', + name: 'Test Case', + cwd: path.dirname(testFixturesDir), + program: this._launchUrl, + rootPath: this._workspaceRoot, + trace: { logFile: tmpLogPath }, + runtimeVersion: process.env.JSDBG_USE_NODE_VERSION, + outFiles: [`${this._workspaceRoot}/**/*.js`, '!**/node_modules/**'], + resolveSourceMapLocations: ['**', '!**/node_modules/**'], + __workspaceFolder: this._workspaceRoot, + ...options, + } as INodeLaunchConfiguration); + const result = await new Promise(f => (this._launchCallback = f)); + return result as NodeTestHandle; + } + + /** + * Runs a script in a separate workspace (i.e. a different 'remoteRoot') + * from the original file, by copying the containing folder of the file + * into a temporary directory. + */ + async runScriptAsRemote( + filename: string, + options: Partial = {}, + ): Promise { + await this.initialize; + + filename = path.isAbsolute(filename) ? filename : path.join(testFixturesDir, filename); + let tmpPath = path.join(tmpdir(), 'js-debug-test'); + if (process.platform === 'darwin' && tmpPath.startsWith('/var/folders')) { + // on OSX, tmpdir is 'virtually' inside /private. os.tmpdir() omits the + // private prefix, but Chrome sees it, so make sure it matches here. + tmpPath = `/private/${tmpPath}`; + } + after(async () => { + await fs.rm(tmpPath, { recursive: true, force: true }); + }); + + await new Promise((resolve, reject) => + gulp + .src('**/*.*', { cwd: path.dirname(filename) }) + .pipe(gulp.dest(tmpPath)) + .on('end', resolve) + .on('error', reject) + ); + + this._root.dap.launch({ + ...nodeLaunchConfigDefaults, + cwd: path.dirname(testFixturesDir), + program: path.join(tmpPath, path.basename(filename)), + localRoot: path.dirname(filename), + remoteRoot: tmpPath, + trace: { logFile: getLogFileForTest(this._testTitlePath) }, + outFiles: [], + resolveSourceMapLocations: ['**', '!**/node_modules/**'], + env: { + NODE_PATH: [ + process.env.NODE_PATH, + path.resolve(path.dirname(filename), 'node_modules'), + path.resolve(workspaceFolder, 'node_modules'), + ] + .filter(Boolean) + .join(process.platform === 'win32' ? ';' : ':'), + }, + __workspaceFolder: this._workspaceRoot, + ...options, + } as INodeLaunchConfiguration); + + const result = await new Promise(f => (this._launchCallback = f)); + return result as NodeTestHandle; + } + + async attachNode( + processId: number, + options: Partial = {}, + ): Promise { + await this.initialize; + this._launchUrl = `process${processId}`; + this._root.dap.launch({ + ...nodeAttachConfigDefaults, + trace: { logFile: getLogFileForTest(this._testTitlePath) }, + processId: `inspector${processId}`, + __workspaceFolder: this._workspaceRoot, + ...options, + } as INodeAttachConfiguration); + const result = await new Promise(f => (this._launchCallback = f)); + await result.load(); + return result as NodeTestHandle; + } + + async launch( + content: string, + options: Partial = {}, + ): Promise { + const url = 'data:text/html;base64,' + Buffer.from(content).toString('base64'); + return this._launch(url, options); + } + + async launchAndLoad( + content: string, + options: Partial = {}, + ): Promise { + const url = 'data:text/html;base64,' + Buffer.from(content).toString('base64'); + const p = await this._launch(url, options); + await p.load(); + return p; + } + + public buildUrl(url: string) { + return utils.completeUrl('http://localhost:8001/', url) || url; + } + + async launchUrl( + url: string, + options: Partial = {}, + ): Promise { + return await this._launch(this.buildUrl(url), options); + } + + async launchUrlAndLoad( + url: string, + options: Partial = {}, + ): Promise { + const p = await this._launch(this.buildUrl(url), options); + await p.load(); + return p; + } + + async disconnect(): Promise { + return new Promise(cb => { + this.initialize.then(() => { + const connection = this._browserLauncher?.connectionForTest(); + if (connection) { + const disposable = connection.onDisconnected(() => { + cb(); + disposable.dispose(); + }); + } else { + cb(); + } + this._root.dap.disconnect({}); + this.binder.dispose(); + }); + }); + } + + completeUrl(relativePath: string): string { + return utils.completeUrl(this._launchUrl, relativePath) || ''; + } + + workspacePath(relative: string): string { + return path.join(this._workspaceRoot, relative); + } +} + +/** + * Recursive structure that lists folders/files and describes their contents. + */ +export interface IFileTree { + [directoryOrFile: string]: string | string[] | Buffer | IFileTree; +} diff --git a/code/extensions/js-debug/src/test/testHooks.ts b/code/extensions/js-debug/src/test/testHooks.ts new file mode 100644 index 000000000000..f12b86624364 --- /dev/null +++ b/code/extensions/js-debug/src/test/testHooks.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import 'reflect-metadata'; +import { use } from 'chai'; + +use(require('chai-subset')); +use(require('chai-as-promised')); diff --git a/code/extensions/js-debug/src/test/testIntegrationUtils.ts b/code/extensions/js-debug/src/test/testIntegrationUtils.ts new file mode 100644 index 000000000000..48abc9c9bc46 --- /dev/null +++ b/code/extensions/js-debug/src/test/testIntegrationUtils.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as child_process from 'child_process'; +import { promises as fs } from 'fs'; +import { ExclusiveTestFunction, TestFunction } from 'mocha'; +import * as path from 'path'; +import { delay } from '../common/promiseUtil'; +import { GoldenText } from './goldenText'; +import { IGoldenReporterTextTest } from './reporters/goldenTextReporterUtils'; +import { ITestHandle, testFixturesDir, TestRoot, testWorkspace } from './test'; + +process.env['DA_TEST_DISABLE_TELEMETRY'] = 'true'; + +let servers: child_process.ChildProcess[]; + +before(async () => { + servers = [ + child_process.fork(path.join(__dirname, 'testServer.js'), ['8001'], { stdio: 'pipe' }), + child_process.fork(path.join(__dirname, 'testServer.js'), ['8002'], { stdio: 'pipe' }), + ]; + + await Promise.all( + servers.map(server => { + return new Promise((resolve, reject) => { + let error = ''; + server.stderr?.on('data', data => (error += data.toString())); + server.stdout?.on('data', data => (error += data.toString())); + server.once('error', reject); + server.once('close', code => reject(new Error(`Exited with ${code}, stderr=${error}`))); + server.once('message', resolve); + }); + }), + ); +}); + +after(async () => { + servers.forEach(server => server.kill()); + servers = []; +}); + +interface IIntegrationState { + context: Mocha.Context & { test: Mocha.Runnable }; + golden: GoldenText; + r: TestRoot; +} + +const itIntegratesBasic = ( + test: string, + fn: (s: IIntegrationState) => Promise | void, + testFunction: TestFunction | ExclusiveTestFunction = it, +) => + testFunction(test, async function() { + if (!this.test?.file) { + throw new Error(`Could not find file for test`); + } + + const golden = new GoldenText( + this.test!.titlePath().join(' '), + this.test?.file!, + testWorkspace, + ); + const root = new TestRoot(golden, this.test!.fullTitle()); + await root.initialize; + + try { + (this.test as IGoldenReporterTextTest).goldenText = golden; + + await fn({ golden, r: root, context: this as Mocha.Context & { test: Mocha.Runnable } }); + } finally { + try { + await root.disconnect(); + } catch (e) { + console.warn('Error disconnecting test root:', e); + } + } + + if (golden.hasNonAssertedLogs()) { + throw new Error( + `Whoa, test "${test}" has some logs that it did not assert!\n\n${golden.getOutput()}`, + ); + } + }); + +itIntegratesBasic.only = (test: string, fn: (s: IIntegrationState) => Promise | void) => + itIntegratesBasic(test, fn, it.only); +itIntegratesBasic.skip = (test: string, fn: (s: IIntegrationState) => Promise | void) => + itIntegratesBasic(test, fn, it.skip); +export const itIntegrates = itIntegratesBasic; + +export const eventuallyOk = async ( + fn: () => Promise | T, + timeout = 1000, + wait = 10, +): Promise => { + const deadline = Date.now() + timeout; + while (true) { + try { + return await fn(); + } catch (e) { + if (Date.now() + wait > deadline) { + throw e; + } + + await delay(wait); + } + } +}; + +afterEach(async () => { + // Retry to avoid flaking with EINVAL/EBUSY if files are written out during deletion + for (let retries = 10; retries >= 0; retries--) { + try { + await fs.rm(testFixturesDir, { recursive: true, force: true }); + return; + } catch (e) { + if (retries === 0) { + throw e; + } + await delay(100); + } + } +}); + +export async function waitForPause(p: ITestHandle, cb?: (threadId: number) => Promise) { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + await cb?.(threadId); + return p.dap.continue({ threadId }); +} diff --git a/code/extensions/js-debug/src/test/testMemento.ts b/code/extensions/js-debug/src/test/testMemento.ts new file mode 100644 index 000000000000..8e96988ad456 --- /dev/null +++ b/code/extensions/js-debug/src/test/testMemento.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Memento } from 'vscode'; + +export class TestMemento implements Memento { + private readonly data = new Map(); + + keys(): readonly string[] { + return [...this.data.keys()]; + } + + get(key: string): T | undefined; + get(key: string, defaultValue: T): T; + get(key: any, defaultValue?: any): T | T | undefined { + return this.data.has(key) ? this.data.get(key) : defaultValue; + } + + update(key: string, value: any): Thenable { + this.data.set(key, value); + return Promise.resolve(); + } +} diff --git a/code/extensions/js-debug/src/test/testRunner.ts b/code/extensions/js-debug/src/test/testRunner.ts new file mode 100644 index 000000000000..971a817831ee --- /dev/null +++ b/code/extensions/js-debug/src/test/testRunner.ts @@ -0,0 +1,116 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +// @ts-ignore +import allTests from '**/*.test.ts'; +import Mocha from 'mocha'; +import { join } from 'path'; +import LoggingReporter from './reporters/logTestReporter'; +import './testHooks'; + +function setupCoverage() { + const NYC = require('nyc'); + const nyc = new NYC({ + cwd: join(__dirname, '..', '..', '..'), + exclude: ['**/test/**', '.vscode-test/**'], + reporter: ['text', 'html'], + all: true, + instrument: true, + hookRequire: true, + hookRunInContext: true, + hookRunInThisContext: true, + }); + + nyc.reset(); + nyc.wrap(); + + return nyc; +} + +export async function run(): Promise { + const nyc = process.env.COVERAGE ? setupCoverage() : null; + + const mochaOpts: Mocha.MochaOptions = { + timeout: 10 * 1000, + color: true, + ...JSON.parse(process.env.PWA_TEST_OPTIONS || '{}'), + }; + + if (process.env.ONLY_MINSPEC === 'true') { + mochaOpts.grep = 'node runtime'; // may eventually want a more dynamic system + } + + const grep = mochaOpts.grep || (mochaOpts as Record).g; + if (grep) { + mochaOpts.grep = new RegExp(String(grep), 'i'); + } + + mochaOpts.reporter = LoggingReporter; + if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { + mochaOpts.reporterOptions = { + reporterEnabled: `mocha-junit-reporter`, + mochaJunitReporterReporterOptions: { + testsuitesTitle: `tests ${process.platform}`, + mochaFile: join( + process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, + `test-results/TEST-${process.platform}-test-results.xml`, + ), + }, + }; + } + + const runner = new Mocha(mochaOpts); + const addFile = async (file: string, doImport: () => Promise) => { + runner.suite.emit(Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE, globalThis, file, runner); + const m = await doImport(); + runner.suite.emit(Mocha.Suite.constants.EVENT_FILE_REQUIRE, m, file, runner); + runner.suite.emit(Mocha.Suite.constants.EVENT_FILE_POST_REQUIRE, globalThis, file, runner); + }; + + // todo: retry failing tests https://github.com/microsoft/vscode-pwa/issues/28 + if (process.env.RETRY_TESTS) { + runner.retries(Number(process.env.RETRY_TESTS)); + } + + const rel = (f: string) => join(__dirname, `${f}.ts`); + if (process.env.FRAMEWORK_TESTS) { + await addFile(rel('framework/reactTest'), () => import('./framework/reactTest')); + } else { + await addFile(rel('testIntegrationUtils'), () => import('./testIntegrationUtils')); + await addFile(rel('infra/infra'), () => import('./infra/infra')); + await addFile( + rel('breakpoints/breakpointsTest'), + () => import('./breakpoints/breakpointsTest'), + ); + await addFile(rel('browser/framesTest'), () => import('./browser/framesTest')); + await addFile( + rel('browser/blazorSourcePathResolverTest'), + () => import('./browser/blazorSourcePathResolverTest'), + ); + await addFile(rel('evaluate/evaluate'), () => import('./evaluate/evaluate')); + await addFile(rel('sources/sourcesTest'), () => import('./sources/sourcesTest')); + await addFile(rel('stacks/stacksTest'), () => import('./stacks/stacksTest')); + await addFile(rel('threads/threadsTest'), () => import('./threads/threadsTest')); + await addFile(rel('variables/variablesTest'), () => import('./variables/variablesTest')); + await addFile(rel('console/consoleFormatTest'), () => import('./console/consoleFormatTest')); + await addFile(rel('console/consoleAPITest'), () => import('./console/consoleAPITest')); + + for (const [path, imp] of allTests) { + await addFile(rel(path), imp); + } + } + + try { + await new Promise((resolve, reject) => + runner.run(failures => + failures ? reject(new Error(`${failures} tests failed`)) : resolve(undefined) + ) + ); + } finally { + if (nyc) { + nyc.writeCoverageFile(); + await nyc.report(); + } + } +} diff --git a/code/extensions/js-debug/src/test/testServer.js b/code/extensions/js-debug/src/test/testServer.js new file mode 100644 index 000000000000..c33c5733f58c --- /dev/null +++ b/code/extensions/js-debug/src/test/testServer.js @@ -0,0 +1,57 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +// @ts-check + +const express = require('express'); +const path = require('path'); + +const port = +process.argv[2]; +const app = express(); +const webRoot = path.join(__dirname, '..', '..', 'testWorkspace', 'web'); +app.get('/cookies/home', (req, res) => { + res.header('Set-Cookie', 'authed=true'); + res.sendFile(path.join(webRoot, 'browserify/pause.html')); +}); + +app.get('/unique-refresh', (req, res) => { + res.send(``); +}); + +app.get('/redirect-test/home', (req, res) => { + res.header('Set-Cookie', 'authed=true'); + res.sendFile(path.join(webRoot, 'browserify/pause.html')); +}); + +app.get('/redirect-test/:resource', (req, res) => { + if (req.params.resource.endsWith('.map')) { + res.redirect(`/browserify/${req.params.resource}`); + } else { + res.sendFile(path.join(webRoot, `browserify/${req.params.resource}`)); + } +}); + +app.use( + '/cookies', + (req, res, next) => { + if (!req.headers.cookie?.includes('authed=true')) { + res.status(403).send('access denied'); + } else { + next(); + } + }, + express.static(path.join(webRoot, 'browserify')), +); + +app.get('/redirect-to-greet', (_req, res) => res.redirect('/greet')); + +app.get('/view-headers', (req, res) => res.json(req.headers)); + +app.get('/greet', (_req, res) => res.send('Hello world!')); + +app.use('/', express.static(path.join(webRoot))); + +app.listen(port, () => { + process.send('ready'); +}); diff --git a/code/extensions/js-debug/src/test/threads/threads-not-paused.txt b/code/extensions/js-debug/src/test/threads/threads-not-paused.txt new file mode 100644 index 000000000000..93e4eb004766 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-not-paused.txt @@ -0,0 +1,8 @@ +{ + threads : [ + [0] : { + id : + name : localhost:8001/index.html + } + ] +} diff --git a/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-cases.txt b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-cases.txt new file mode 100644 index 000000000000..992cc986b69a --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-cases.txt @@ -0,0 +1,93 @@ +Not pausing on exceptions +Evaluating#1: setTimeout(() => { throw new Error('hello'); }) +Evaluating#2: setTimeout(() => { try { throw new Error('hello'); } catch (e) {}}) +Pausing on uncaught exceptions +Evaluating#3: setTimeout(() => { try { throw new Error('hello'); } catch (e) {}}) +Evaluating#4: setTimeout(() => { throw new Error('hello'); }) +{ + allThreadsStopped : false + description : Paused on exception + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at eval4.js:1:26 + } + exceptionId : Error: hello +} +{ + allThreadsContinued : false +} +Pausing on uncaught rejections +Evaluating#5: new Promise((res, rej) => rej(new Error('oh no!'))) +{ + allThreadsStopped : false + description : Paused on promise rejection + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at eval5.js:1:31 at new Promise () at eval5.js:1:1 + } + exceptionId : Error: oh no! +} +{ + allThreadsContinued : false +} +Pausing on caught exceptions +Evaluating#6: setTimeout(() => { throw new Error('hello'); }) +{ + allThreadsStopped : false + description : Paused on exception + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at eval6.js:1:26 + } + exceptionId : Error: hello +} +{ + allThreadsContinued : false +} +Evaluating#7: setTimeout(() => { try { throw new Error('hello'); } catch (e) {}}) +{ + allThreadsStopped : false + description : Paused on exception + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at eval7.js:1:32 + } + exceptionId : Error: hello +} +{ + allThreadsContinued : false +} +Pausing on caught rejections +Evaluating#8: new Promise((res, rej) => rej(new Error('oh no!'))).catch(err => {}) +{ + allThreadsStopped : false + description : Paused on promise rejection + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at eval8.js:1:31 at new Promise () at eval8.js:1:1 + } + exceptionId : Error: oh no! +} +{ + allThreadsContinued : false +} diff --git a/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-configuration.txt b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-configuration.txt new file mode 100644 index 000000000000..48bba01dfa90 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-configuration.txt @@ -0,0 +1,16 @@ +{ + allThreadsStopped : false + description : Paused on exception + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at :7:17 + } + exceptionId : Error: this error is uncaught +} +{ + allThreadsContinued : false +} diff --git a/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-deals-with-syntax-errors-in-conditional-exception-bps.txt b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-deals-with-syntax-errors-in-conditional-exception-bps.txt new file mode 100644 index 000000000000..59d232a1d8e6 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-deals-with-syntax-errors-in-conditional-exception-bps.txt @@ -0,0 +1 @@ +stderr> Syntax error setting breakpoint with condition "!!(error.message.includes(\"bye)" on line 0: Invalid or unexpected token diff --git a/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-does-not-pause-on-exceptions-in-internals.txt b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-does-not-pause-on-exceptions-in-internals.txt new file mode 100644 index 000000000000..50e53e77252d --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-does-not-pause-on-exceptions-in-internals.txt @@ -0,0 +1 @@ +Evaluating#1: console.log({ [Symbol.for('debug.description')]() { throw 'oops'; } }) diff --git a/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-pauses-on-conditional-exceptions.txt b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-pauses-on-conditional-exceptions.txt new file mode 100644 index 000000000000..e8a054acbf9e --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-pause-on-exceptions-pauses-on-conditional-exceptions.txt @@ -0,0 +1,39 @@ +Pausing on caught exceptions +Evaluating#1: setTimeout(() => { try { throw new Error('hello'); } catch (e) {} }) +Evaluating#2: setTimeout(() => { try { throw new Error('goodbye'); } catch (e) {} }) +{ + allThreadsStopped : false + description : Paused on exception + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at eval2.js:1:32 + } + exceptionId : Error: goodbye +} +{ + allThreadsContinued : false +} +Pausing on uncaught exceptions +Evaluating#3: setTimeout(() => { throw new Error('hello'); }) +Evaluating#4: setTimeout(() => { try { throw new Error('goodbye1'); } catch (e) {} }) +Evaluating#5: setTimeout(() => { throw new Error('goodbye2'); }) +{ + allThreadsStopped : false + description : Paused on exception + reason : exception + threadId : +} +{ + breakMode : all + details : { + stackTrace : at eval5.js:1:26 + } + exceptionId : Error: goodbye2 +} +{ + allThreadsContinued : false +} diff --git a/code/extensions/js-debug/src/test/threads/threads-paused.txt b/code/extensions/js-debug/src/test/threads/threads-paused.txt new file mode 100644 index 000000000000..93e4eb004766 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-paused.txt @@ -0,0 +1,8 @@ +{ + threads : [ + [0] : { + id : + name : localhost:8001/index.html + } + ] +} diff --git a/code/extensions/js-debug/src/test/threads/threads-stepping-basic.txt b/code/extensions/js-debug/src/test/threads/threads-stepping-basic.txt new file mode 100644 index 000000000000..99da81465147 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-stepping-basic.txt @@ -0,0 +1,90 @@ +Evaluating#1: + function bar() { + return 2; + } + function foo() { + debugger; + bar(); + bar(); + } + foo(); + +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +Window.foo @ localhost꞉8001/eval1.js:6:11 + @ localhost꞉8001/eval1.js:10:9 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] +Window.foo @ localhost꞉8001/eval1.js:7:11 + @ localhost꞉8001/eval1.js:10:9 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] +Window.foo @ localhost꞉8001/eval1.js:8:11 + @ localhost꞉8001/eval1.js:10:9 + +step in +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] +Window.bar @ localhost꞉8001/eval1.js:3:11 +Window.foo @ localhost꞉8001/eval1.js:8:11 + @ localhost꞉8001/eval1.js:10:9 + +step out +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] +Window.foo @ localhost꞉8001/eval1.js:9:9 + @ localhost꞉8001/eval1.js:10:9 + +resume +{ + allThreadsContinued : false + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor-source-map-predicted.txt b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor-source-map-predicted.txt new file mode 100644 index 000000000000..bdbd5e90f309 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor-source-map-predicted.txt @@ -0,0 +1,43 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ localhost꞉8001/test.js:1:1 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ localhost꞉8001/test.js:2:1 + +step in +{ + allThreadsContinued : false + threadId : +} +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/workerSourceMap.ts:1:1 +----Worker Created---- + @ localhost꞉8001/test.js:2:12 + +resume +{ + allThreadsContinued : false + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor-source-map.txt b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor-source-map.txt new file mode 100644 index 000000000000..bdbd5e90f309 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor-source-map.txt @@ -0,0 +1,43 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ localhost꞉8001/test.js:1:1 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ localhost꞉8001/test.js:2:1 + +step in +{ + allThreadsContinued : false + threadId : +} +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/workerSourceMap.ts:1:1 +----Worker Created---- + @ localhost꞉8001/test.js:2:12 + +resume +{ + allThreadsContinued : false + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor.txt b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor.txt new file mode 100644 index 000000000000..08eb4afac265 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-constructor.txt @@ -0,0 +1,43 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ localhost꞉8001/test.js:2:9 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ localhost꞉8001/test.js:3:9 + +step in +{ + allThreadsContinued : false + threadId : +} +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/worker.js:1:1 +----Worker Created---- + @ localhost꞉8001/test.js:3:20 + +resume +{ + allThreadsContinued : false + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-skip-over-tasks.txt b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-skip-over-tasks.txt new file mode 100644 index 000000000000..21303ee69066 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-skip-over-tasks.txt @@ -0,0 +1,43 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ localhost꞉8001/test.js:3:9 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ localhost꞉8001/test.js:4:9 + +step in +{ + allThreadsContinued : false + threadId : +} +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} + @ localhost꞉8001/test.js:5:19 +----Promise.then---- + @ localhost꞉8001/test.js:4:11 + +resume +{ + allThreadsContinued : false + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-source-map.txt b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-source-map.txt new file mode 100644 index 000000000000..fb3d756e7187 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread-source-map.txt @@ -0,0 +1,43 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ localhost꞉8001/test.js:3:9 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ localhost꞉8001/test.js:4:9 + +step in +{ + allThreadsContinued : false + threadId : +} +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/workerSourceMap.ts:4:3 +----Worker.postMessage---- + @ localhost꞉8001/test.js:4:18 + +resume +{ + allThreadsContinued : false + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread.txt b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread.txt new file mode 100644 index 000000000000..09390d288e7b --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threads-stepping-cross-thread.txt @@ -0,0 +1,43 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ localhost꞉8001/test.js:1:1 + +step over +[ + [0] : { + allThreadsContinued : false + threadId : + } + [1] : { + allThreadsStopped : false + description : Paused + reason : step + threadId : + } +] + @ localhost꞉8001/test.js:2:1 + +step in +{ + allThreadsContinued : false + threadId : +} +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/worker.js:4:3 +----Worker.postMessage---- + @ localhost꞉8001/test.js:2:10 + +resume +{ + allThreadsContinued : false + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threadsTest-startup-tests-events.txt b/code/extensions/js-debug/src/test/threads/threadsTest-startup-tests-events.txt new file mode 100644 index 000000000000..b54868087d9d --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threadsTest-startup-tests-events.txt @@ -0,0 +1,28 @@ +Initializing +Launching +Thread started: { + reason : started + threadId : +} +Requesting threads: { + threads : [ + [0] : { + id : + name : 📄 blank + } + ] +} +Launched +Requesting threads: { + threads : [ + [0] : { + id : + name : 📄 blank + } + ] +} +Disconnecting +Thread exited: { + reason : exited + threadId : +} diff --git a/code/extensions/js-debug/src/test/threads/threadsTest.ts b/code/extensions/js-debug/src/test/threads/threadsTest.ts new file mode 100644 index 000000000000..e4856ee8ff51 --- /dev/null +++ b/code/extensions/js-debug/src/test/threads/threadsTest.ts @@ -0,0 +1,340 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { delay } from '../../common/promiseUtil'; +import { TestP, TestRoot } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('threads', () => { + itIntegrates('paused', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.cdp.Runtime.evaluate({ expression: 'debugger;' }); + await p.dap.once('stopped'); + p.log(await p.dap.threads({})); + p.assertLog(); + }); + + itIntegrates('not paused', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.log(await p.dap.threads({})); + p.assertLog(); + }); + + describe('stepping', () => { + itIntegrates('basic', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + p.evaluate(` + function bar() { + return 2; + } + function foo() { + debugger; + bar(); + bar(); + } + foo(); + `); + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + + p.log('\nstep over'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep over'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep in'); + p.dap.stepIn({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep out'); + p.dap.stepOut({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nresume'); + p.dap.continue({ threadId }); + p.log(await p.dap.once('continued')); + p.assertLog(); + }); + + itIntegrates('cross thread', async ({ r }) => { + const p = await r.launchUrlAndLoad('worker.html'); + + p.cdp.Runtime.evaluate({ + expression: `debugger;\nwindow.w.postMessage('message')\n//# sourceURL=test.js`, + }); + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + + p.log('\nstep over'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep in'); + p.dap.stepIn({ threadId }); + p.log(await p.dap.once('continued')); + const worker = await r.worker(); + const { threadId: secondThreadId } = p.log(await worker.dap.once('stopped')); + await worker.logger.logStackTrace(secondThreadId); + + p.log('\nresume'); + worker.dap.continue({ threadId: secondThreadId }); + p.log(await worker.dap.once('continued')); + p.assertLog(); + }); + + itIntegrates('cross thread constructor', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + + p.cdp.Runtime.evaluate({ + expression: ` + debugger; + window.w = new Worker('worker.js');\n//# sourceURL=test.js`, + }); + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + + p.log('\nstep over'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep in'); + p.dap.stepIn({ threadId }); + p.log(await p.dap.once('continued')); + const worker = await r.worker(); + const { threadId: secondThreadId } = p.log(await worker.dap.once('stopped')); + await worker.logger.logStackTrace(secondThreadId); + + p.log('\nresume'); + worker.dap.continue({ threadId: secondThreadId }); + p.log(await worker.dap.once('continued')); + p.assertLog(); + }); + + itIntegrates('cross thread skip over tasks', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + + p.cdp.Runtime.evaluate({ + expression: ` + window.p = new Promise(f => window.cb = f); + debugger; + p.then(() => { + var a = 1; // should stop here + }); + window.w = new Worker('worker.js'); + window.w.postMessage('hey'); + window.w.addEventListener('message', () => window.cb()); + \n//# sourceURL=test.js`, + }); + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + + p.log('\nstep over'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep in'); + p.dap.stepIn({ threadId }); + p.log(await p.dap.once('continued')); + const { threadId: secondThreadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(secondThreadId); + + p.log('\nresume'); + p.dap.continue({ threadId: secondThreadId }); + p.log(await p.dap.once('continued')); + p.assertLog(); + }); + + const runCrossThreadTest = async (r: TestRoot, p: TestP) => { + p.cdp.Runtime.evaluate({ + expression: + `debugger;\nwindow.w = new Worker('workerSourceMap.js');\n//# sourceURL=test.js`, + }); + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + + p.log('\nstep over'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep in'); + p.dap.stepIn({ threadId }); + p.log(await p.dap.once('continued')); + const worker = await r.worker(); + const { threadId: secondThreadId } = p.log(await worker.dap.once('stopped')); + await worker.logger.logStackTrace(secondThreadId); + + p.log('\nresume'); + worker.dap.continue({ threadId: secondThreadId }); + p.log(await worker.dap.once('continued')); + p.assertLog(); + }; + + itIntegrates('cross thread constructor source map', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + await runCrossThreadTest(r, p); + }); + + itIntegrates('cross thread constructor source map predicted', async ({ r }) => { + // the call path is different when an instrumentation breakpoint is present, + // set a breakpoint to ensure with add the instrumentation bp as well. + const p = await r.launchUrlAndLoad('index.html'); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/does-not-exist.html') }, + breakpoints: [{ line: 1, column: 1 }], + }); + await runCrossThreadTest(r, p); + }); + + itIntegrates('cross thread source map', async ({ r }) => { + const p = await r.launchUrlAndLoad('index.html'); + + p.cdp.Runtime.evaluate({ + expression: ` + window.w = new Worker('workerSourceMap.js'); + debugger; + window.w.postMessage('hey');\n//# sourceURL=test.js`, + }); + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + + p.log('\nstep over'); + p.dap.next({ threadId }); + p.log(await Promise.all([p.dap.once('continued'), p.dap.once('stopped')])); + await p.logger.logStackTrace(threadId); + + p.log('\nstep in'); + p.dap.stepIn({ threadId }); + p.log(await p.dap.once('continued')); + const worker = await r.worker(); + const { threadId: secondThreadId } = p.log(await worker.dap.once('stopped')); + await worker.logger.logStackTrace(secondThreadId); + + p.log('\nresume'); + worker.dap.continue({ threadId: secondThreadId }); + p.log(await worker.dap.once('continued')); + p.assertLog(); + }); + }); + + describe('pause on exceptions', () => { + async function waitForPauseOnException(p: TestP) { + const event = p.log(await p.dap.once('stopped')); + p.log(await p.dap.exceptionInfo({ threadId: event.threadId })); + p.log(await p.dap.continue({ threadId: event.threadId })); + } + + itIntegrates('cases', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + + p.log('Not pausing on exceptions'); + await p.dap.setExceptionBreakpoints({ filters: [] }); + await p.evaluate(`setTimeout(() => { throw new Error('hello'); })`); + await p.evaluate(`setTimeout(() => { try { throw new Error('hello'); } catch (e) {}})`); + + p.log('Pausing on uncaught exceptions'); + await p.dap.setExceptionBreakpoints({ filters: ['uncaught'] }); + await p.evaluate(`setTimeout(() => { try { throw new Error('hello'); } catch (e) {}})`); + p.evaluate(`setTimeout(() => { throw new Error('hello'); })`); + await waitForPauseOnException(p); + + p.log('Pausing on uncaught rejections'); + p.evaluate(`new Promise((res, rej) => rej(new Error('oh no!')))`); + await waitForPauseOnException(p); + + p.log('Pausing on caught exceptions'); + await p.dap.setExceptionBreakpoints({ filters: ['all'] }); + p.evaluate(`setTimeout(() => { throw new Error('hello'); })`); + await waitForPauseOnException(p); + p.evaluate(`setTimeout(() => { try { throw new Error('hello'); } catch (e) {}})`); + await waitForPauseOnException(p); + + p.log('Pausing on caught rejections'); + p.evaluate(`new Promise((res, rej) => rej(new Error('oh no!'))).catch(err => {})`); + await waitForPauseOnException(p); + + p.assertLog(); + }); + + itIntegrates('does not pause on exceptions in internals', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + + await p.dap.setExceptionBreakpoints({ filters: ['all'] }); + const e = p.evaluate( + `console.log({ [Symbol.for('debug.description')]() { throw 'oops'; } })`, + ); + + await Promise.race([ + p.dap.once('stopped').then(() => { + throw new Error('should not stop'); + }), + delay(1000), + ]); + + await e; + p.assertLog(); + }); + + itIntegrates('pauses on conditional exceptions', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + + p.log('Pausing on caught exceptions'); + await p.dap.setExceptionBreakpoints({ + filters: ['all'], + filterOptions: [{ filterId: 'all', condition: 'error.message.includes("bye")' }], + }); + p.evaluate(`setTimeout(() => { try { throw new Error('hello'); } catch (e) {} })`); + p.evaluate(`setTimeout(() => { try { throw new Error('goodbye'); } catch (e) {} })`); + await waitForPauseOnException(p); + + p.log('Pausing on uncaught exceptions'); + await p.dap.setExceptionBreakpoints({ + filters: ['uncaught'], + filterOptions: [{ filterId: 'uncaught', condition: 'error.message.includes("bye")' }], + }); + p.evaluate(`setTimeout(() => { throw new Error('hello'); })`); + p.evaluate(`setTimeout(() => { try { throw new Error('goodbye1'); } catch (e) {} })`); + p.evaluate(`setTimeout(() => { throw new Error('goodbye2'); })`); + await waitForPauseOnException(p); + + p.assertLog(); + }); + + itIntegrates('deals with syntax errors in conditional exception bps', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.dap.setExceptionBreakpoints({ + filters: ['all'], + filterOptions: [{ filterId: 'all', condition: 'error.message.includes("bye' }], + }); + await p.logger.logOutput(await p.dap.once('output')); + p.assertLog(); + }); + + itIntegrates('configuration', async ({ r }) => { + const p = await r.launch(` + + `); + await p.dap.setExceptionBreakpoints({ filters: ['uncaught'] }); + p.load(); + await waitForPauseOnException(p); + p.assertLog(); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/variables/variables-basic-basic-object.txt b/code/extensions/js-debug/src/test/variables/variables-basic-basic-object.txt new file mode 100644 index 000000000000..582ffeb39974 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-basic-basic-object.txt @@ -0,0 +1,3 @@ +> result: {a: 1} + a: 1 + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-basic-clear-console.txt b/code/extensions/js-debug/src/test/variables/variables-basic-clear-console.txt new file mode 100644 index 000000000000..bc76455fef51 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-basic-clear-console.txt @@ -0,0 +1,4 @@ +stdout> Hello world +console>  +stdout> Hello world +console>  diff --git a/code/extensions/js-debug/src/test/variables/variables-basic-simple-log.txt b/code/extensions/js-debug/src/test/variables/variables-basic-simple-log.txt new file mode 100644 index 000000000000..0da33c53a3cc --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-basic-simple-log.txt @@ -0,0 +1 @@ +stdout> Hello world diff --git a/code/extensions/js-debug/src/test/variables/variables-map-variable-without-preview-1824.txt b/code/extensions/js-debug/src/test/variables/variables-map-variable-without-preview-1824.txt new file mode 100644 index 000000000000..880a256f69a4 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-map-variable-without-preview-1824.txt @@ -0,0 +1,3 @@ +> result: A {#bar: Map(1)} + > #bar: Map(1) + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-multiple-threads-worker.txt b/code/extensions/js-debug/src/test/variables/variables-multiple-threads-worker.txt new file mode 100644 index 000000000000..3c148c01691f --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-multiple-threads-worker.txt @@ -0,0 +1,23 @@ +stderr> {foo: {…}} +stderr> > {foo: {…}} +stderr> > arg0: {foo: {…}} +stderr> + @ ${workspaceFolder}/web/worker.html:4:11 +◀ postMessage ▶ + @ ${workspaceFolder}/web/worker.js:10:5 +◀ Worker.postMessage ▶ + @ ${workspaceFolder}/web/worker.html:8:10 +stderr> {foo: {…}} +stderr> > {foo: {…}} +stderr> > arg0: {foo: {…}} +stderr> + @ ${workspaceFolder}/web/worker.js:1:9 +◀ Worker Created ▶ + @ ${workspaceFolder}/web/worker.html:2:12 +stderr> {foo: {…}} +stderr> > {foo: {…}} +stderr> > arg0: {foo: {…}} +stderr> + @ ${workspaceFolder}/web/worker.js:4:11 +◀ Worker.postMessage ▶ + @ ${workspaceFolder}/web/worker.html:8:10 diff --git a/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-shows-errors.txt b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-shows-errors.txt new file mode 100644 index 000000000000..411cd7b61a8d --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-shows-errors.txt @@ -0,0 +1,3 @@ +> result: Error: oh no! (couldn't describe: object) + > getter: (...) + > [[Prototype]]: Foo diff --git a/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-function-declaration.txt b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-function-declaration.txt new file mode 100644 index 000000000000..b48f0a3644da --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-function-declaration.txt @@ -0,0 +1,3 @@ +> result: using function: Instance of bar + > getter: (...) + > [[Prototype]]: Foo diff --git a/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-statement-syntax.txt b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-statement-syntax.txt new file mode 100644 index 000000000000..471f37509376 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-statement-syntax.txt @@ -0,0 +1,3 @@ +> result: using statement: Instance of bar + > getter: (...) + > [[Prototype]]: Foo diff --git a/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-statement-with-return-syntax.txt b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-statement-with-return-syntax.txt new file mode 100644 index 000000000000..efd666a920b2 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-using-statement-with-return-syntax.txt @@ -0,0 +1,3 @@ +> result: using statement return: Instance of bar + > getter: (...) + > [[Prototype]]: Foo diff --git a/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-with-arrays.txt b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-with-arrays.txt new file mode 100644 index 000000000000..956b4f809a05 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-customdescriptiongenerator-with-arrays.txt @@ -0,0 +1,8 @@ +> result: (4) [Bar, Foo, 5, 'test'] + > 0: Instance of bar + > 1: Foo + 2: 5 + 3: 'test' + length: 4 + > [[Prototype]]: Array(0) + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-object-custompropertiesgenerator-works-with-custompropertiesgenerator-method.txt b/code/extensions/js-debug/src/test/variables/variables-object-custompropertiesgenerator-works-with-custompropertiesgenerator-method.txt new file mode 100644 index 000000000000..02f5dd1d7b19 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-custompropertiesgenerator-works-with-custompropertiesgenerator-method.txt @@ -0,0 +1,6 @@ +> result: Bar {realProp: 'cc3'} + customProp1: 'aa1' + customProp2: 'bb2' + > getter: (...) + realProp: 'cc3' + > [[Prototype]]: Foo diff --git a/code/extensions/js-debug/src/test/variables/variables-object-deep-accessor.txt b/code/extensions/js-debug/src/test/variables/variables-object-deep-accessor.txt new file mode 100644 index 000000000000..e7de1079a527 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-deep-accessor.txt @@ -0,0 +1,3 @@ +> result: Bar + > getter: (...) + > [[Prototype]]: Foo diff --git a/code/extensions/js-debug/src/test/variables/variables-object-get-set.txt b/code/extensions/js-debug/src/test/variables/variables-object-get-set.txt new file mode 100644 index 000000000000..cd031e4b7a09 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-get-set.txt @@ -0,0 +1,5 @@ +> result: {getter: , accessor: } + > accessor: (...) + > getter: (...) + > setter: write-only + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-object-private-props.txt b/code/extensions/js-debug/src/test/variables/variables-object-private-props.txt new file mode 100644 index 000000000000..0b160d00dfd2 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-private-props.txt @@ -0,0 +1,3 @@ +> result: A {#foo: 'bar'} + #foo: 'bar' + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-object-shows-errors-while-generating-properties.txt b/code/extensions/js-debug/src/test/variables/variables-object-shows-errors-while-generating-properties.txt new file mode 100644 index 000000000000..8f992344e876 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-shows-errors-while-generating-properties.txt @@ -0,0 +1,8 @@ +> result: Bar {realProp: 'cc3'} + +Error: Some error while generating properties + at Bar. (:3:47) + at Bar._generatedCode (:4:6) + > getter: (...) + realProp: 'cc3' + > [[Prototype]]: Foo diff --git a/code/extensions/js-debug/src/test/variables/variables-object-simple-array.txt b/code/extensions/js-debug/src/test/variables/variables-object-simple-array.txt new file mode 100644 index 000000000000..66da1850f0be --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-simple-array.txt @@ -0,0 +1,8 @@ +> result: (3) [1, 2, 3, foo: 1] // type=Array + 0: 1 // type=number + 1: 2 // type=number + 2: 3 // type=number + foo: 1 // type=number + length: 3 // type=number + > [[Prototype]]: Array(0) // type=Array + > [[Prototype]]: Object // type=Object diff --git a/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-handles-errors-gracefully.txt b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-handles-errors-gracefully.txt new file mode 100644 index 000000000000..b9e068fcf29d --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-handles-errors-gracefully.txt @@ -0,0 +1,4 @@ +> result: BrokenClass {prop1: 'value1', prop2: 'value2'} + prop1: 'value1' + prop2: 'value2' + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-replaces-properties-with-custom-object.txt b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-replaces-properties-with-custom-object.txt new file mode 100644 index 000000000000..f32420a4d2b3 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-replaces-properties-with-custom-object.txt @@ -0,0 +1,5 @@ +> result: Observable {_value: 'test value', _observers: Set(3), _scheduler: {…}, _isDisposed: false} + subscriberCount: 3 + value: 'test value' + > ...: Observable {_value: 'test value', _observers: Set(3), _scheduler: {…}, _isDisposed: false} + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-shows-escape-hatch-for-original-object.txt b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-shows-escape-hatch-for-original-object.txt new file mode 100644 index 000000000000..125a007fa00a --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-shows-escape-hatch-for-original-object.txt @@ -0,0 +1,4 @@ +> result: MyClass {internal1: 'hidden1', internal2: 'hidden2', internal3: 'hidden3'} // type=MyClass + public: 'visible' // type=string + > ...: MyClass {internal1: 'hidden1', internal2: 'hidden2', internal3: 'hidden3'} // type=MyClass + > [[Prototype]]: Object // type=Object diff --git a/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-works-with-symbolfordebugdescription-together.txt b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-works-with-symbolfordebugdescription-together.txt new file mode 100644 index 000000000000..2f9508d906ba --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-object-symbolfordebugproperties-works-with-symbolfordebugdescription-together.txt @@ -0,0 +1,5 @@ +> result: Counter(42) + count: 42 + listenerCount: 2 + > ...: Counter {_count: 42, _listeners: Array(2)} + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variables-readmemorywritememory.txt b/code/extensions/js-debug/src/test/variables/variables-readmemorywritememory.txt new file mode 100644 index 000000000000..1eb7f79dc441 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-readmemorywritememory.txt @@ -0,0 +1,66 @@ +$memA [0, 20]{ + address : 0 + data : AAAAAAAAAAAAAAAAAAAAAAAAAAA= + unreadableBytes : 0 +} +$memA [5, 10]{ + address : 0 + data : AAAAAAAAAAAAAA== + unreadableBytes : 0 +} +$memB [0, 20]{ + address : 0 + data : AAECAwQFBgcICQoL + unreadableBytes : 8 +} +$memB [5, 10]{ + address : 0 + data : BQYHCAkKCw== + unreadableBytes : 3 +} +$memC [0, 20]{ + address : 0 + data : AAECAwQFBgcICQoL + unreadableBytes : 8 +} +$memC [5, 10]{ + address : 0 + data : BQYHCAkKCw== + unreadableBytes : 3 +} +$memD [0, 20]{ + address : 0 + data : AAECAwQFBgcICQoL + unreadableBytes : 8 +} +$memD [5, 10]{ + address : 0 + data : BQYHCAkKCw== + unreadableBytes : 3 +} +$memE [0, 20]{ + address : 0 + data : AwQFBgcICQo= + unreadableBytes : 12 +} +$memE [5, 10]{ + address : 0 + data : CAkK + unreadableBytes : 7 +} +write{ + bytesWritten : 5 +} +read outcome{ + address : 0 + data : AGhlbGxvBgcICQ== + unreadableBytes : 0 +} +write with offset{ + bytesWritten : 7 +} +read outcome{ + address : 0 + data : AGhlbGhlbGxvdw== + unreadableBytes : 0 +} diff --git a/code/extensions/js-debug/src/test/variables/variables-setvariable-basic.txt b/code/extensions/js-debug/src/test/variables/variables-setvariable-basic.txt new file mode 100644 index 000000000000..ba6187bb7272 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-setvariable-basic.txt @@ -0,0 +1,15 @@ +> result: {foo: 42} + foo: 42 + > [[Prototype]]: Object + +Setting "foo" to "{bar: 17}" +> : Object + bar: 17 + > [[Prototype]]: Object + +Original +> result: {foo: 42} + > foo: {bar: 17} + > [[Prototype]]: Object + +setVariable failure: ReferenceError: baz is not defined diff --git a/code/extensions/js-debug/src/test/variables/variables-setvariable-evaluatename.txt b/code/extensions/js-debug/src/test/variables/variables-setvariable-evaluatename.txt new file mode 100644 index 000000000000..00b471a5f554 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-setvariable-evaluatename.txt @@ -0,0 +1,40 @@ +stopped: { + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +undefined + a + b + b[0] + b[1] + b[2] + b[3] + b.prop + b.length + b["[[Prototype]]"] + b["[[Prototype]]"] + c + c[Symbol.for("debug.properties")]()[2] + c[Symbol.for("debug.properties")]().a + c[Symbol.for("debug.properties")]()["c c"] + c + c._b + c.$a + c[42] + c.c + c["d d"] + c.e + c.e.nested + c.e.nested[0] + c.e.nested[0].obj + c.e.nested[0]["[[Prototype]]"] + c.e.nested.length + c.e.nested["[[Prototype]]"] + c.e.nested["[[Prototype]]"] + c.e["[[Prototype]]"] + c["Symbol(wut)"] + c["[[Prototype]]"] + c["[[Prototype]]"] + this diff --git a/code/extensions/js-debug/src/test/variables/variables-setvariable-name-mapping.txt b/code/extensions/js-debug/src/test/variables/variables-setvariable-name-mapping.txt new file mode 100644 index 000000000000..53c35124be81 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-setvariable-name-mapping.txt @@ -0,0 +1,25 @@ + +Window.c @ ${workspaceFolder}/web/minified/index.js:13:5 + > scope #0: Local: c + arg1: 2 + arg2: 3 + > this: Window + scope #1: Global [expensive] + +Window.test @ ${workspaceFolder}/web/minified/index.js:6:5 + > scope #0: Block: test + inner1: 2 + inner2: 3 + > this: Window + > scope #1: Local: test + > hitDebugger: ƒ c(n,t){debugger} + inner1: 1 + scope #2: Global [expensive] + + @ /VM:1:1 + scope #0: Global [expensive] + +Preserves eval sourceURL (#1259): +Uncaught ReferenceError ReferenceError: thenSomethingInvalid is not defined + at eval (repl:2:1) + diff --git a/code/extensions/js-debug/src/test/variables/variables-setvariable-scope.txt b/code/extensions/js-debug/src/test/variables/variables-setvariable-scope.txt new file mode 100644 index 000000000000..ca217e575efe --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-setvariable-scope.txt @@ -0,0 +1,19 @@ +stopped: { + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} +> scope: Local: foo + > this: Window + y: 'value of y' + z: 'value of z' + +Setting "y" to "z" +: 'value of z' + +Original +> scope: Local: foo + > this: Window + y: 'value of y' + z: 'value of z' diff --git a/code/extensions/js-debug/src/test/variables/variables-setvariable-setexpression.txt b/code/extensions/js-debug/src/test/variables/variables-setvariable-setexpression.txt new file mode 100644 index 000000000000..d374cf04bbe8 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-setvariable-setexpression.txt @@ -0,0 +1,15 @@ + +setExpression a: { + type : number + value : 42 + variablesReference : +} + +setExpression a: { + type : string + value : 'hello world' + variablesReference : +} + + Vars: +stdout> 42 hello world diff --git a/code/extensions/js-debug/src/test/variables/variables-web-tags.txt b/code/extensions/js-debug/src/test/variables/variables-web-tags.txt new file mode 100644 index 000000000000..d0a6ed6d27d1 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variables-web-tags.txt @@ -0,0 +1,7 @@ +> result: HTMLCollection(2) [meta, title, foo: meta] + > 0:  + > 1: ... + > length: (...) + > foo:  + > [[Prototype]]: HTMLCollection + > [[Prototype]]: Object diff --git a/code/extensions/js-debug/src/test/variables/variablesTest.ts b/code/extensions/js-debug/src/test/variables/variablesTest.ts new file mode 100644 index 000000000000..a8c27002d1d8 --- /dev/null +++ b/code/extensions/js-debug/src/test/variables/variablesTest.ts @@ -0,0 +1,627 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import Dap from '../../dap/api'; +import { Logger, walkVariables } from '../logger'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('variables', () => { + describe('basic', () => { + itIntegrates('basic object', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog('({a: 1})'); + p.assertLog(); + }); + + itIntegrates('simple log', async ({ r }) => { + const p = await r.launch(` + `); + p.load(); + await p.logger.logOutput(await p.dap.once('output')); + p.assertLog(); + }); + + itIntegrates('clear console', async ({ r }) => { + let complete: () => void; + const result = new Promise(f => (complete = f)); + let chain = Promise.resolve(); + const p = await r.launch(` + `); + p.load(); + p.dap.on('output', async params => { + chain = chain.then(async () => { + if (params.category === 'stderr') complete(); + else await p.logger.logOutput(params); + }); + }); + + await result; + p.assertLog(); + }); + }); + + describe('object', () => { + itIntegrates('simple array', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog('var a = [1, 2, 3]; a.foo = 1; a', { logInternalInfo: true }); + p.assertLog(); + }); + + itIntegrates.skip('large array', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog('var a = new Array(110); a.fill(1); a', { + logInternalInfo: true, + }); + p.assertLog(); + }); + + itIntegrates('get set', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + const a = {}; + Object.defineProperty(a, 'getter', { get: () => {} }); + Object.defineProperty(a, 'setter', { set: () => {} }); + Object.defineProperty(a, 'accessor', { get: () => {}, set: () => {} }); + a;`); + p.assertLog(); + }); + + itIntegrates('private props', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + class A { #foo = 'bar' } + new A();`); + p.assertLog(); + }); + + itIntegrates('deep accessor', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { } + new Bar();`); + p.assertLog(); + }); + + describe('customDescriptionGenerator', () => { + itIntegrates('using function declaration', async ({ r }) => { + const p = await r.launchAndLoad('blank', { + customDescriptionGenerator: + 'function (def) { if (this.customDescription) return "using function: " + this.customDescription(); else return def }', + }); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { customDescription() { return 'Instance of bar'} } + new Bar();`); + p.assertLog(); + }); + + itIntegrates('shows errors', async ({ r }) => { + const p = await r.launchAndLoad('blank', { + customDescriptionGenerator: + 'function (def) { if (this.customDescription) throw new Error("oh no!"); else return def }', + }); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { customDescription() { return 'Instance of bar'} } + new Bar();`); + p.assertLog(); + }); + + itIntegrates('using statement syntax', async ({ r }) => { + const p = await r.launchAndLoad('blank', { + customDescriptionGenerator: + 'const hasCustomDescription = this.customDescription; "using statement: " + (hasCustomDescription ? this.customDescription() : defaultValue)', + }); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { customDescription() { return 'Instance of bar'} } + new Bar();`); + p.assertLog(); + }); + + itIntegrates('using statement with return syntax', async ({ r }) => { + const p = await r.launchAndLoad('blank', { + customDescriptionGenerator: + 'const hasCustomDescription = this.customDescription; if (hasCustomDescription) { return "using statement return: " + this.customDescription() } else return defaultValue', + }); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { customDescription() { return 'Instance of bar'} } + new Bar();`); + p.assertLog(); + }); + + itIntegrates('with arrays', async ({ r }) => { + const p = await r.launchAndLoad('blank', { + customDescriptionGenerator: `function (def) { + return this.customDescription + ? this.customDescription() + : (Array.isArray(this) + ? "I'm an array" + : def); }`, + }); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { customDescription() { return 'Instance of bar'} } + [new Bar(), new Foo(), 5, "test"];`); + p.assertLog(); + }); + }); + + describe('customPropertiesGenerator', () => { + itIntegrates('works with customPropertiesGenerator method ', async ({ r }) => { + const p = await r.launchAndLoad('blank', { + customPropertiesGenerator: + 'function () { if (this.customPropertiesGenerator) return this.customPropertiesGenerator(); else return this; }', + }); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { + constructor() { + super(); + this.realProp = 'cc3'; + } + + customPropertiesGenerator() { + const properties = Object.create(this.__proto__); + return Object.assign(properties, this, { customProp1: 'aa1', customProp2: 'bb2' }); + } + } + new Bar();`); + p.assertLog(); + }); + }); + + itIntegrates('shows errors while generating properties', async ({ r }) => { + const p = await r.launchAndLoad('blank', { + customPropertiesGenerator: + 'function () { if (this.customPropertiesGenerator) throw new Error("Some error while generating properties"); else return this; }', + }); + await p.logger.evaluateAndLog(` + class Foo { get getter() {} } + class Bar extends Foo { + constructor() { + super(); + this.realProp = 'cc3'; + } + + customPropertiesGenerator() { + const properties = Object.create(this.__proto__); + return Object.assign(properties, this, { customProp1: 'aa1', customProp2: 'bb2' }); + } + } + new Bar();`); + p.assertLog(); + }); + + describe('Symbol.for("debug.properties")', () => { + itIntegrates('replaces properties with custom object', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + class Observable { + constructor(value) { + this._value = value; + this._observers = new Set(['observer1', 'observer2', 'observer3']); + this._scheduler = { name: 'scheduler', details: 'complex object' }; + this._isDisposed = false; + } + + [Symbol.for('debug.properties')]() { + return { + value: this._value, + subscriberCount: this._observers.size + }; + } + } + new Observable('test value'); + `); + p.assertLog(); + }); + + itIntegrates('shows escape hatch for original object', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog( + ` + class MyClass { + constructor() { + this.internal1 = 'hidden1'; + this.internal2 = 'hidden2'; + this.internal3 = 'hidden3'; + } + + [Symbol.for('debug.properties')]() { + return { public: 'visible' }; + } + } + new MyClass(); + `, + { logInternalInfo: true }, + ); + p.assertLog(); + }); + + itIntegrates('works with Symbol.for("debug.description") together', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + class Counter { + constructor() { + this._count = 42; + this._listeners = ['listener1', 'listener2']; + } + + [Symbol.for('debug.description')]() { + return \`Counter(\${this._count})\`; + } + + [Symbol.for('debug.properties')]() { + return { + count: this._count, + listenerCount: this._listeners.length + }; + } + } + new Counter(); + `); + p.assertLog(); + }); + + itIntegrates('handles errors gracefully', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + class BrokenClass { + constructor() { + this.prop1 = 'value1'; + this.prop2 = 'value2'; + } + + [Symbol.for('debug.properties')]() { + throw new Error('Something went wrong'); + } + } + new BrokenClass(); + `); + p.assertLog(); + }); + }); + }); + + describe('web', () => { + itIntegrates('tags', async ({ r }) => { + const p = await r.launchAndLoad(` + + Title + `); + await p.logger.evaluateAndLog('document.head.children'); + p.assertLog(); + }); + }); + + describe('multiple threads', () => { + itIntegrates('worker', async ({ r }) => { + const p = await r.launchUrlAndLoad('worker.html'); + const outputs: { output: Dap.OutputEventParams; logger: Logger }[] = []; + await Promise.all([ + (async () => outputs.push({ output: await p.dap.once('output'), logger: p.logger }))(), + (async () => { + const worker = await r.worker(); + outputs.push({ output: await worker.dap.once('output'), logger: worker.logger }); + outputs.push({ output: await worker.dap.once('output'), logger: worker.logger }); + })(), + ]); + + outputs.sort((a, b) => { + const aName = a?.output?.source?.name; + const bName = b?.output?.source?.name; + return aName && bName ? aName.localeCompare(bName) : 0; + }); + for (const { output, logger } of outputs) await logger.logOutput(output); + p.assertLog(); + }); + }); + + describe('setVariable', () => { + itIntegrates('basic', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + const v = await p.logger.evaluateAndLog(`window.x = ({foo: 42}); x`); + + p.log(`\nSetting "foo" to "{bar: 17}"`); + const response = await p.dap.setVariable({ + variablesReference: v.variablesReference, + name: 'foo', + value: '{bar: 17}', + }); + + const v2: Dap.Variable = { + ...response, + variablesReference: response.variablesReference || 0, + name: '', + }; + await p.logger.logVariable(v2); + + p.log(`\nOriginal`); + await p.logger.logVariable(v); + + p.log( + await p.dap.setVariable({ + variablesReference: v.variablesReference, + name: 'foo', + value: 'baz', + }), + '\nsetVariable failure: ', + ); + p.assertLog(); + }); + + itIntegrates('setExpression', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + (function foo() { + let a = { b: 1 } + let c = 'e'; + debugger; + console.log(a.b, c); + })() + `, + }); + + const paused = await p.dap.once('stopped'); + const stack = await p.dap.stackTrace({ threadId: paused.threadId! }); + + p.log( + await p.dap.setExpression({ + expression: 'a.b', + value: '42', + frameId: stack.stackFrames[0].id, + }), + '\nsetExpression a: ', + ); + + p.log( + await p.dap.setExpression({ + expression: 'c', + value: '"hello " + "world"', + frameId: stack.stackFrames[0].id, + }), + '\nsetExpression a: ', + ); + + p.dap.continue({ threadId: paused.threadId! }); + p.log('\n Vars:'); + await p.logger.logOutput(await p.dap.once('output')); + p.assertLog(); + }); + + itIntegrates('scope', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + (function foo() { + let y = 'value of y'; + let z = 'value of z'; + debugger; + })() + `, + }); + + const paused = p.log(await p.dap.once('stopped'), 'stopped: '); + const stack = await p.dap.stackTrace({ threadId: paused.threadId }); + const scopes = await p.dap.scopes({ frameId: stack.stackFrames[0].id }); + const scope = scopes.scopes[0]; + const v: Dap.Variable = { + name: 'scope', + value: scope.name, + variablesReference: scope.variablesReference, + namedVariables: scope.namedVariables, + indexedVariables: scope.indexedVariables, + }; + + await p.logger.logVariable(v); + + p.log(`\nSetting "y" to "z"`); + const response = await p.dap.setVariable({ + variablesReference: v.variablesReference, + name: 'y', + value: `z`, + }); + + const v2: Dap.Variable = { + ...response, + variablesReference: response.variablesReference || 0, + name: '', + }; + await p.logger.logVariable(v2); + + p.log(`\nOriginal`); + await p.logger.logVariable(v); + + p.assertLog(); + }); + + itIntegrates('name mapping', async ({ r }) => { + const p = await r.launchUrlAndLoad('minified/index.html'); + p.cdp.Runtime.evaluate({ expression: `test()` }); + const event = await p.dap.once('stopped'); + const stacks = await p.logger.logStackTrace(event.threadId!, Infinity); + + p.log('\nPreserves eval sourceURL (#1259):'); // https://github.com/microsoft/vscode-js-debug/issues/1259#issuecomment-1442584596 + p.log( + await p.dap.evaluate({ + expression: 'arg1; thenSomethingInvalid()', + context: 'repl', + frameId: stacks[0].id, + }), + ); + + await p.dap.continue({ threadId: event.threadId! }); + + p.assertLog(); + }); + + itIntegrates('evaluateName', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + (function foo() { + let a = 'some string'; + let b = [1, 2, 3, 4]; + b.prop = ''; + let c = { $a: 1, _b: 2, c: 3, 'd d': 4, [42]: 5, + e: { nested: [{ obj: true }]}, [Symbol('wut')]: 'wut', + [Symbol.for('debug.properties')]: () => ({ a: 1, 2: 3, 'c c': 4 }) }; + debugger; + })(); + `, + }); + + const paused = p.log(await p.dap.once('stopped'), 'stopped: '); + const stack = await p.dap.stackTrace({ threadId: paused.threadId }); + const scopes = await p.dap.scopes({ frameId: stack.stackFrames[0].id }); + const scope = scopes.scopes[0]; + const v: Dap.Variable = { + name: 'scope', + value: scope.name, + variablesReference: scope.variablesReference, + namedVariables: scope.namedVariables, + indexedVariables: scope.indexedVariables, + }; + + await walkVariables(p.dap, v, (variable, depth) => { + if (depth > 10) { + return false; + } + p.log(' '.repeat(depth) + variable.evaluateName); + return ( + !variable.name.startsWith('__') + && !variable.name.startsWith('[[') + && variable.name !== 'this' + ); + }); + + p.assertLog(); + }); + }); + + itIntegrates('map variable without preview (#1824)', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + await p.logger.evaluateAndLog(` + class A { #bar = new Map([[1, 2]]) } + new A();`); + p.assertLog(); + }); + + itIntegrates('readMemory/writeMemory', async ({ r }) => { + const p = await r.launchAndLoad('blank'); + p.cdp.Runtime.evaluate({ + expression: ` + (function foo() { + let $memA = new WebAssembly.Memory({ initial: 1, maximum: 1 }); + let $memB = $memA.buffer.slice(0, 12); + let $memC = new Uint8Array($memB); + let $memD = new DataView($memB); + let $memE = new Uint8Array($memB, 3, 8); + + for (let i = 0; i < $memC.length; i++) { + $memC[i] = i; + } + + debugger; + console.log($memA, $memB, $memC, $memD); + })() + `, + }); + + const paused = await p.dap.once('stopped'); + const stack = await p.dap.stackTrace({ threadId: paused.threadId! }); + + const scopes = await p.dap.scopes({ frameId: stack.stackFrames[0].id }); + const scope = scopes.scopes[0]; + const v: Dap.Variable = { + name: 'scope', + value: scope.name, + variablesReference: scope.variablesReference, + namedVariables: scope.namedVariables, + indexedVariables: scope.indexedVariables, + }; + + let memB: Dap.Variable; + let memE: Dap.Variable; + + await walkVariables(p.dap, v, async (variable, depth) => { + if (!variable.name.startsWith('$mem')) { + return depth < 2; + } + + if (variable.name === '$memB') { + memB = variable; + } else if (variable.name === '$memE') { + memE = variable; + } + + expect(variable).to.have.property('memoryReference'); + + const memory1 = await p.dap.readMemory({ + count: 20, + memoryReference: variable.memoryReference!, + offset: 0, + }); + + p.log(memory1, `${variable.name} [0, 20]`); + + const memory2 = await p.dap.readMemory({ + count: 10, + memoryReference: variable.memoryReference!, + offset: 5, + }); + + p.log(memory2, `${variable.name} [5, 10]`); + + return false; + }); + + const written = await p.dap.writeMemory({ + memoryReference: memB!.memoryReference!, + data: Buffer.from('hello').toString('base64'), + offset: 1, + }); + + p.log(written, 'write'); + + const memory3 = await p.dap.readMemory({ + count: 10, + memoryReference: memB!.memoryReference!, + }); + + p.log(memory3, 'read outcome'); + + const written2 = await p.dap.writeMemory({ + memoryReference: memE!.memoryReference!, + data: Buffer.from('helloworld').toString('base64'), + offset: 1, + }); + + p.log(written2, 'write with offset'); + + const memory4 = await p.dap.readMemory({ + count: 10, + memoryReference: memB!.memoryReference!, + }); + + p.log(memory4, 'read outcome'); + + p.assertLog(); + }); +}); diff --git a/code/extensions/js-debug/src/test/wasm/wasm.test.ts b/code/extensions/js-debug/src/test/wasm/wasm.test.ts new file mode 100644 index 000000000000..dca6fb88fd74 --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/wasm.test.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import Dap from '../../dap/api'; +import { TestRoot } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('webassembly', () => { + itIntegrates('basic stepping and breakpoints', async ({ r }) => { + const p = await r.launchUrl('wasm/hello.html'); + await p.dap.setBreakpoints({ + source: { + path: p.workspacePath('web/wasm/hello.html'), + }, + breakpoints: [{ line: 14 }], + }); + + p.load(); + + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.dap.stepIn({ threadId }); + } + + { + const { threadId } = p.log(await p.dap.once('stopped'), 'stopped event'); + const stacktrace = await p.logger.logStackTrace(threadId); + const content = await p.dap.source({ + sourceReference: stacktrace[0].source!.sourceReference!, + source: stacktrace[0].source, + }); + + p.log(content.mimeType, 'source mime type'); + p.log(content.content, 'source content'); + + await p.dap.setBreakpoints({ + source: stacktrace[0].source!, + breakpoints: [{ line: 10 }], + }); + + await p.dap.continue({ threadId }); + } + + { + const { threadId } = p.log(await p.dap.once('stopped'), 'breakpoint stopped event'); + await p.logger.logStackTrace(threadId); + } + + p.assertLog(); + }); + + describe('dwarf', () => { + const prepare = async ( + r: TestRoot, + context: Mocha.Context, + file: string, + bp: Dap.SetBreakpointsParams, + ) => { + // starting the dwarf debugger can be pretty slow, I observed up to 40 + // seconds in one case :( + // context.timeout(120_000); + + const p = await r.launchUrlAndLoad(`dwarf/${file}.html`); + bp.source.path = p.workspacePath(bp.source.path!); + await p.dap.setBreakpoints(bp); + + await p.dap.once('breakpoint', bp => bp.breakpoint.verified); + p.cdp.Page.reload({}); + // wait for the reload to start: + await p.dap.once('loadedSource', e => e.reason === 'removed'); + return p; + }; + + itIntegrates('can break immediately', async ({ r }) => { + const p = await r.launchUrl(`dwarf/fibonacci.html`); + + // Note: we need the extra stop because we depend on the eval running to add the instrumentation for wasm + // https://github.com/microsoft/vscode-js-debug/blob/16b601cb24260e1a58c2c09c9456ccb5bbef0013/src/adapter/threads.ts#L939 + + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/dwarf/fibonacci.html') }, + breakpoints: [{ line: 1221 }], + }); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/dwarf/fibonacci.c') }, + breakpoints: [{ line: 6 }], + }); + p.load(); + + const prePause = await p.dap.once('stopped'); + await p.dap.continue({ threadId: prePause.threadId! }); + + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId, 2); + + r.assertLog(); + }); + + itIntegrates('scopes and variables', async ({ r, context }) => { + const p = await prepare(r, context, 'fibonacci', { + source: { path: 'web/dwarf/fibonacci.c' }, + breakpoints: [{ line: 6 }], + }); + + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId, 2); + + r.assertLog(); + }); + + itIntegrates('basic stepping', async ({ r, context }) => { + const p = await prepare(r, context, 'fibonacci', { + source: { path: 'web/dwarf/fibonacci.c' }, + breakpoints: [{ line: 6 }], + }); + + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.dap.setBreakpoints({ + source: { path: p.workspacePath('web/dwarf/fibonacci.c') }, + breakpoints: [], + }); + + p.dap.next({ threadId }); + } + + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + p.dap.stepOut({ threadId }); + } + + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + } + + r.assertLog(); + }); + + itIntegrates('inline breakpoints set at all call sites', async ({ r, context }) => { + const p = await prepare(r, context, 'diverse-inlining', { + source: { + path: 'web/dwarf/diverse-inlining.h', + }, + breakpoints: [{ line: 2 }], + }); + + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + p.dap.continue({ threadId }); + } + + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId, 2); + p.dap.continue({ threadId }); + } + + r.assertLog(); + }); + + itIntegrates('inline function stepping 1', async ({ r, context }) => { + const p = await prepare(r, context, 'diverse-inlining', { + source: { + path: 'web/dwarf/diverse-inlining-main.c', + }, + breakpoints: [{ line: 7 }], + }); + + const steps = [ + // stopped at `argc = foo(argc);` + 'stepIn', + // stopped at `INLINE static int` + 'stepOut', + // stopped at `argc = foo(argc);`, + 'next', + + // stopped at `argc = bar(argc);` + 'stepIn', + // stopped at `int bar(int x) {` + 'stepIn', + // stopped at `x = x + 1;` + 'stepIn', + ] as const; + + for (const step of steps) { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + p.dap[step]({ threadId }); + p.log(`---- ${step} ----`); + } + + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + + r.assertLog(); + }); + + itIntegrates('inline function stepping 2', async ({ r, context }) => { + const p = await prepare(r, context, 'diverse-inlining', { + source: { + path: 'web/dwarf/diverse-inlining-extern.c', + }, + breakpoints: [{ line: 5 }], + }); + + // stopped at return foo() + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + p.dap.next({ threadId }); + } + + // should be back in main, stepped over inline range + { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + } + + r.assertLog(); + }); + + itIntegrates('does lldb evaluation', async ({ r, context }) => { + const p = await prepare(r, context, 'fibonacci', { + source: { path: 'web/dwarf/fibonacci.c' }, + breakpoints: [{ line: 6 }], + }); + + const { threadId } = p.log(await p.dap.once('stopped')); + const { id: frameId } = (await p.dap.stackTrace({ threadId })).stackFrames[0]; + + await p.logger.evaluateAndLog(`n`, { params: { frameId } }); + await p.logger.evaluateAndLog(`a`, { params: { frameId } }); + await p.logger.evaluateAndLog(`a + n * 2`, { params: { frameId } }); + + r.assertLog(); + }); + + itIntegrates('does lldb evaluation for structs', async ({ r, context }) => { + const p = await prepare(r, context, 'c-with-struct', { + source: { path: 'web/dwarf/c-with-struct.c' }, + breakpoints: [{ line: 11 }], + }); + + const { threadId } = p.log(await p.dap.once('stopped')); + const { id: frameId } = (await p.dap.stackTrace({ threadId })).stackFrames[0]; + + await p.logger.evaluateAndLog('(data_t*)data', { params: { frameId }, depth: 3 }); + + r.assertLog(); + }); + }); +}); diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-basic-stepping-and-breakpoints.txt b/code/extensions/js-debug/src/test/wasm/webassembly-basic-stepping-and-breakpoints.txt new file mode 100644 index 000000000000..87ccd8a50e77 --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-basic-stepping-and-breakpoints.txt @@ -0,0 +1,44 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +stopped event{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +Window.$fac @ localhost꞉8001/wasm/hello.wat:3:1 + @ ${workspaceFolder}/web/wasm/hello.html:14:19 +----Promise.then---- + @ ${workspaceFolder}/web/wasm/hello.html:12:59 +source mime typetext/wat +source content(module + (func $fac (;0;) (export "fac") (param $var0 f64) (result f64) + local.get $var0 + f64.const 1 + f64.lt + if (result f64) + f64.const 1 + else + local.get $var0 + local.get $var0 + f64.const 1 + f64.sub + call $fac + f64.mul + end + ) +) +breakpoint stopped event{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +Window.$fac @ localhost꞉8001/wasm/hello.wat:10:1 + @ ${workspaceFolder}/web/wasm/hello.html:14:19 +----Promise.then---- + @ ${workspaceFolder}/web/wasm/hello.html:12:59 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-basic-stepping.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-basic-stepping.txt new file mode 100644 index 000000000000..6be4cdfaa2b4 --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-basic-stepping.txt @@ -0,0 +1,55 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +fib @ ${workspaceFolder}/web/dwarf/fibonacci.c:7:9 +main @ ${workspaceFolder}/web/dwarf/fibonacci.c:14:11 +Window.$main @ localhost꞉8001/dwarf/fibonacci.wat:230:1 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/fibonacci.js:1580:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/fibonacci.js:1630:23 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1641:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/fibonacci.js:1637:5 +runCaller @ ${workspaceFolder}/web/dwarf/fibonacci.js:1565:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/fibonacci.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/fibonacci.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/fibonacci.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/fibonacci.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/fibonacci.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/fibonacci.js:897:3 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1253:19 +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +main @ ${workspaceFolder}/web/dwarf/fibonacci.c:14:11 +Window.$main @ localhost꞉8001/dwarf/fibonacci.wat:230:1 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/fibonacci.js:1580:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/fibonacci.js:1630:23 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1641:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/fibonacci.js:1637:5 +runCaller @ ${workspaceFolder}/web/dwarf/fibonacci.js:1565:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/fibonacci.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/fibonacci.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/fibonacci.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/fibonacci.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/fibonacci.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/fibonacci.js:897:3 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1253:19 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-can-break-immediately.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-can-break-immediately.txt new file mode 100644 index 000000000000..c30ba5f359c5 --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-can-break-immediately.txt @@ -0,0 +1,42 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + +fib @ ${workspaceFolder}/web/dwarf/fibonacci.c:6:9 + > scope #0: Locals + a: 0 + b: 0 + c: 1 + i: 1 + scope #1: Parameters [expensive] + +main @ ${workspaceFolder}/web/dwarf/fibonacci.c:14:11 + > scope #0: Locals + a: 0 + b: 0 + +Window.$main @ localhost꞉8001/dwarf/fibonacci.wat:230:1 + + @ ${workspaceFolder}/web/dwarf/fibonacci.js:723:14 + +Window.callMain @ ${workspaceFolder}/web/dwarf/fibonacci.js:1580:15 + +Window.doRun @ ${workspaceFolder}/web/dwarf/fibonacci.js:1630:23 + + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1641:7 + +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/fibonacci.js:1637:5 +runCaller @ ${workspaceFolder}/web/dwarf/fibonacci.js:1565:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/fibonacci.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/fibonacci.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/fibonacci.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/fibonacci.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/fibonacci.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/fibonacci.js:897:3 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1253:19 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-does-lldb-evaluation-for-structs.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-does-lldb-evaluation-for-structs.txt new file mode 100644 index 000000000000..59bd3b219d1d --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-does-lldb-evaluation-for-structs.txt @@ -0,0 +1,23 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +> result: data_t * + > 0x10408: data_t + > id: char[12] + 0: 'H' + 1: 'e' + 2: 'l' + 3: 'l' + 4: 'o' + 5: ' ' + 6: 'w' + 7: 'o' + 8: 'r' + 9: 'l' + 10: 'd' + 11: '\0' + x: 12 + y: 34 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-does-lldb-evaluation.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-does-lldb-evaluation.txt new file mode 100644 index 000000000000..8dc6bf1b88e4 --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-does-lldb-evaluation.txt @@ -0,0 +1,9 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +result: 9 +result: 0 +result: 18 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-breakpoints-set-at-all-call-sites.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-breakpoints-set-at-all-call-sites.txt new file mode 100644 index 000000000000..673319c1610e --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-breakpoints-set-at-all-call-sites.txt @@ -0,0 +1,59 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +foo @ ${workspaceFolder}/web/dwarf/diverse-inlining.h:2:7 +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:7:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + +foo @ ${workspaceFolder}/web/dwarf/diverse-inlining.h:2:7 + scope #0: Parameters [expensive] + +bar @ ${workspaceFolder}/web/dwarf/diverse-inlining-extern.c:5:10 + scope #0: Parameters [expensive] + +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:8:10 + + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 + +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 + +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 + +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-function-stepping-1.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-function-stepping-1.txt new file mode 100644 index 000000000000..727194ee5e6a --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-function-stepping-1.txt @@ -0,0 +1,172 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:7:14 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +---- stepIn ---- +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +foo @ ${workspaceFolder}/web/dwarf/diverse-inlining.h:1:0 +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:7:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +---- stepOut ---- +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:7:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +---- next ---- +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:8:14 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +---- stepIn ---- +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +bar @ ${workspaceFolder}/web/dwarf/diverse-inlining-extern.c:4:0 +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:8:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +---- stepIn ---- +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +bar @ ${workspaceFolder}/web/dwarf/diverse-inlining-extern.c:5:14 +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:8:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +---- stepIn ---- +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +foo @ ${workspaceFolder}/web/dwarf/diverse-inlining.h:2:7 +bar @ ${workspaceFolder}/web/dwarf/diverse-inlining-extern.c:5:10 +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:8:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-function-stepping-2.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-function-stepping-2.txt new file mode 100644 index 000000000000..c4023ec2396f --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-inline-function-stepping-2.txt @@ -0,0 +1,47 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +bar @ ${workspaceFolder}/web/dwarf/diverse-inlining-extern.c:5:14 +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:8:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 +{ + allThreadsStopped : false + description : Paused + reason : step + threadId : +} +__main_argc_argv @ ${workspaceFolder}/web/dwarf/diverse-inlining-main.c:8:10 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:723:14 +Window.callMain @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1626:15 +Window.doRun @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1676:23 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1687:7 +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1683:5 +runCaller @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1603:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:897:3 + @ ${workspaceFolder}/web/dwarf/diverse-inlining.js:1292:19 diff --git a/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-scopes-and-variables.txt b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-scopes-and-variables.txt new file mode 100644 index 000000000000..c30ba5f359c5 --- /dev/null +++ b/code/extensions/js-debug/src/test/wasm/webassembly-dwarf-scopes-and-variables.txt @@ -0,0 +1,42 @@ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + +fib @ ${workspaceFolder}/web/dwarf/fibonacci.c:6:9 + > scope #0: Locals + a: 0 + b: 0 + c: 1 + i: 1 + scope #1: Parameters [expensive] + +main @ ${workspaceFolder}/web/dwarf/fibonacci.c:14:11 + > scope #0: Locals + a: 0 + b: 0 + +Window.$main @ localhost꞉8001/dwarf/fibonacci.wat:230:1 + + @ ${workspaceFolder}/web/dwarf/fibonacci.js:723:14 + +Window.callMain @ ${workspaceFolder}/web/dwarf/fibonacci.js:1580:15 + +Window.doRun @ ${workspaceFolder}/web/dwarf/fibonacci.js:1630:23 + + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1641:7 + +----setTimeout---- +run @ ${workspaceFolder}/web/dwarf/fibonacci.js:1637:5 +runCaller @ ${workspaceFolder}/web/dwarf/fibonacci.js:1565:19 +removeRunDependency @ ${workspaceFolder}/web/dwarf/fibonacci.js:641:7 +receiveInstance @ ${workspaceFolder}/web/dwarf/fibonacci.js:860:5 +receiveInstantiationResult @ ${workspaceFolder}/web/dwarf/fibonacci.js:878:5 +----Promise.then---- + @ ${workspaceFolder}/web/dwarf/fibonacci.js:813:21 +----Promise.then---- +instantiateAsync @ ${workspaceFolder}/web/dwarf/fibonacci.js:805:62 +createWasm @ ${workspaceFolder}/web/dwarf/fibonacci.js:897:3 + @ ${workspaceFolder}/web/dwarf/fibonacci.js:1253:19 diff --git a/code/extensions/js-debug/src/test/webview/webview-breakpoints-launched-script.txt b/code/extensions/js-debug/src/test/webview/webview-breakpoints-launched-script.txt new file mode 100644 index 000000000000..7e68970de748 --- /dev/null +++ b/code/extensions/js-debug/src/test/webview/webview-breakpoints-launched-script.txt @@ -0,0 +1,16 @@ +{ + allThreadsStopped : false + description : Paused on debugger statement + reason : pause + threadId : +} + @ ${workspaceFolder}/web/script.js:10:1 +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} +bar @ ${workspaceFolder}/web/script.js:6:3 +foo @ ${workspaceFolder}/web/script.js:2:3 + @ ${workspaceFolder}/web/script.js:11:1 diff --git a/code/extensions/js-debug/src/test/webview/webview.breakpoints.test.win.ts b/code/extensions/js-debug/src/test/webview/webview.breakpoints.test.win.ts new file mode 100644 index 000000000000..53ec70cec475 --- /dev/null +++ b/code/extensions/js-debug/src/test/webview/webview.breakpoints.test.win.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { DebugType } from '../../common/contributionUtils'; +import Dap from '../../dap/api'; +import { ITestHandle } from '../test'; +import { itIntegrates } from '../testIntegrationUtils'; + +describe('webview breakpoints', () => { + async function waitForPause(p: ITestHandle, cb?: (threadId: string) => Promise) { + const { threadId } = p.log(await p.dap.once('stopped')); + await p.logger.logStackTrace(threadId); + if (cb) await cb(threadId); + return p.dap.continue({ threadId }); + } + + itIntegrates('launched script', async ({ r, context }) => { + context.timeout(30 * 1000); + + // Breakpoint in separate script set after launch. + const p = await r.launchUrl('script.html', { + type: DebugType.Edge, + runtimeExecutable: r.workspacePath('webview/win/WebView2Sample.exe'), + useWebView: true, + // WebView2Sample.exe launches about:blank + urlFilter: 'about:blank', + }); + p.load(); + await waitForPause(p, async () => { + const source: Dap.Source = { + path: p.workspacePath('web/script.js'), + }; + await p.dap.setBreakpoints({ source, breakpoints: [{ line: 6 }] }); + }); + await waitForPause(p); + p.assertLog(); + }); +}); diff --git a/code/extensions/js-debug/src/typings/acorn-loose.d.ts b/code/extensions/js-debug/src/typings/acorn-loose.d.ts new file mode 100644 index 000000000000..4603965b88d0 --- /dev/null +++ b/code/extensions/js-debug/src/typings/acorn-loose.d.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +declare module 'acorn-loose' { + import acorn from 'acorn'; + import { Node } from 'estree'; + + export function isDummy(node: acorn.Node | Node): boolean; + + export = acorn; +} diff --git a/code/extensions/js-debug/src/typings/acorn-walk.d.ts b/code/extensions/js-debug/src/typings/acorn-walk.d.ts new file mode 100644 index 000000000000..505da4e6d3e6 --- /dev/null +++ b/code/extensions/js-debug/src/typings/acorn-walk.d.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +declare module 'acorn-walk' { + import { Node } from 'estree'; + + export type Visitors = { + [T in Node['type']]?: (node: Node & { type: T }, ...args: Args) => void; + }; + + export type BaseVisitor = Required void]>>; + + /** + * Type for an acorn/estree node. estree provides better typing for + * generators, but its definitions are slightly incompatible with acorn's + * (acorn generates a valid estree, it's just the typings which mismatch.) + */ + interface EstreeNode { + type: string; + } + + export function simple( + node: EstreeNode, + visitors: Visitors, + base?: BaseVisitor, + state?: State, + ): void; + + export function ancestor( + node: EstreeNode, + visitors: Visitors<[Node[]]>, + base?: BaseVisitor, + state?: State, + ): void; + + export function recursive( + node: EstreeNode, + state: State, + visitors: Visitors<[Node[], State, (node: Node, state: State) => void]>, + base?: BaseVisitor, + ): void; + + export function full( + node: EstreeNode, + callback: (node: Node, state: State, type: Node['type']) => void, + base?: BaseVisitor, + state?: State, + ): void; + + export function fullAncestor( + node: EstreeNode, + callback: (node: Node, state: State, ancestors: Node[], type: Node['type']) => void, + base?: BaseVisitor, + state?: State, + ): void; +} diff --git a/code/extensions/js-debug/src/typings/astring.d.ts b/code/extensions/js-debug/src/typings/astring.d.ts new file mode 100644 index 000000000000..427ce6f86393 --- /dev/null +++ b/code/extensions/js-debug/src/typings/astring.d.ts @@ -0,0 +1,82 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +declare module 'astring' { + import { Node } from 'estree'; + import { Writable } from 'stream'; + + /** + * State shape passed to generator code. + */ + export interface State { + output: string; + write(code: string): void; + writeComments: boolean; + indent: string; + lineEnd: string; + indentLevel: number; + } + + /** + * State shape if a sourceMap is specified. + */ + export interface StateWithSourceMap { + line: number; + column: number; + lineEndSize: number; + mapping: Mapping; + } + + /** + * Options for generating code in astring. + */ + export interface Options { + /** + * If present, source mappings will be written to the generator. + */ + sourceMap?: { + file?: string; + addMapping(mapping: { + original: { line: number; column: number }; + generated: { line: number; column: number }; + source: string | undefined; + }); + }; + /** + * String to use for indentation, defaults to " ". + */ + indent?: string; + /** + * String to use for line endings, defaults to "\n" + */ + lineEnd?: string; + /** + * Indent level to start from, defaults to "0" + */ + startingIndentLevel?: number; + /** + * Generate comments, defaults to false. + */ + comments?: boolean; + /** + * Output stream to write the render code to, defaults to null. + */ + output?: Writable | null; + /** + * Custom code generator logic. + */ + generator?: { [T in Node['type']]: (node: Node & { type: T }, state: State) => void }; + } + + /** + * Type for an acorn/estree node. estree provides better typing for + * generators, but its definitions are slightly incompatible with acorn's + * (acorn generates a valid estree, it's just the typings which mismatch.) + */ + interface EstreeNode { + type: string; + } + + export function generate(node: EstreeNode, options?: Options): string; +} diff --git a/code/extensions/js-debug/src/typings/json.d.ts b/code/extensions/js-debug/src/typings/json.d.ts new file mode 100644 index 000000000000..8cb293272ce7 --- /dev/null +++ b/code/extensions/js-debug/src/typings/json.d.ts @@ -0,0 +1 @@ +declare module '*.json'; diff --git a/code/extensions/js-debug/src/typings/object.d.ts b/code/extensions/js-debug/src/typings/object.d.ts new file mode 100644 index 000000000000..91bcaaf2ac21 --- /dev/null +++ b/code/extensions/js-debug/src/typings/object.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +// Improve the default typing of Object.keys(o: T) to be keyof T (without the symbols) + +interface ObjectConstructor { + keys(o: T): WithoutSymbols[]; + fromEntries(map: ReadonlyMap): { [key: string]: V }; +} + +// eslint-disable-next-line +declare var Object: ObjectConstructor; + +/** Return the strings that form S and ignore the symbols */ +type WithoutSymbols = S extends string ? S : never; diff --git a/code/extensions/js-debug/src/typings/typedArrays.d.ts b/code/extensions/js-debug/src/typings/typedArrays.d.ts new file mode 100644 index 000000000000..65357d74d497 --- /dev/null +++ b/code/extensions/js-debug/src/typings/typedArrays.d.ts @@ -0,0 +1,16 @@ +type TypedArray = + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array + | BigUint64Array + | Int8Array + | Int32Array + | BigInt64Array + | Float32Array + | Float64Array; + +interface TypedArrayConstructor { + new(): TypedArray; + new(values: ArrayBuffer): TypedArray; +} diff --git a/code/extensions/js-debug/src/typings/vscode-js-debug.d.ts b/code/extensions/js-debug/src/typings/vscode-js-debug.d.ts new file mode 100644 index 000000000000..f1da2c6aec0b --- /dev/null +++ b/code/extensions/js-debug/src/typings/vscode-js-debug.d.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +declare module '@vscode/js-debug' { + import * as vscode from 'vscode'; + + /** @see {IExports.registerDebugTerminalOptionsProvider} */ + export interface IDebugTerminalOptionsProvider { + /** + * Called when the user creates a JavaScript Debug Terminal. It's called + * with the options js-debug wants to use to create the terminal. It should + * modify and return the options to use in the terminal. + * + * In order to avoid conflicting with existing logic, participants should + * try to modify options in a additive way. For example prefer appending + * to rather than reading and overwriting `options.env.PATH`. + */ + provideTerminalOptions(options: vscode.TerminalOptions): vscode.ProviderResult; + } + + /** + * Defines the exports of the `js-debug` extension. Once you have this typings + * file, these can be acquired in your extension using the following code: + * + * ``` + * const jsDebugExt = vscode.extensions.getExtension('ms-vscode.js-debug-nightly') + * || vscode.extensions.getExtension('ms-vscode.js-debug'); + * await jsDebugExt.activate() + * const jsDebug: import('@vscode/js-debug').IExports = jsDebug.exports; + * ``` + */ + export interface IExports { + /** + * Registers a participant used when the user creates a JavaScript Debug Terminal. + */ + registerDebugTerminalOptionsProvider(provider: IDebugTerminalOptionsProvider): vscode.Disposable; + } +} diff --git a/code/extensions/js-debug/src/typings/vscode.d.ts b/code/extensions/js-debug/src/typings/vscode.d.ts new file mode 100644 index 000000000000..c9ecff2b0b98 --- /dev/null +++ b/code/extensions/js-debug/src/typings/vscode.d.ts @@ -0,0 +1,20056 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + /** + * The version of the editor. + */ + export const version: string; + + /** + * Represents a reference to a command. Provides a title which + * will be used to represent a command in the UI and, optionally, + * an array of arguments which will be passed to the command handler + * function when invoked. + */ + export interface Command { + /** + * Title of the command, like `save`. + */ + title: string; + + /** + * The identifier of the actual command handler. + * @see {@link commands.registerCommand} + */ + command: string; + + /** + * A tooltip for the command, when represented in the UI. + */ + tooltip?: string; + + /** + * Arguments that the command handler should be + * invoked with. + */ + arguments?: any[]; + } + + /** + * Represents a line of text, such as a line of source code. + * + * TextLine objects are __immutable__. When a {@link TextDocument document} changes, + * previously retrieved lines will not represent the latest state. + */ + export interface TextLine { + + /** + * The zero-based line number. + */ + readonly lineNumber: number; + + /** + * The text of this line without the line separator characters. + */ + readonly text: string; + + /** + * The range this line covers without the line separator characters. + */ + readonly range: Range; + + /** + * The range this line covers with the line separator characters. + */ + readonly rangeIncludingLineBreak: Range; + + /** + * The offset of the first character which is not a whitespace character as defined + * by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned. + */ + readonly firstNonWhitespaceCharacterIndex: number; + + /** + * Whether this line is whitespace only, shorthand + * for {@link TextLine.firstNonWhitespaceCharacterIndex} === {@link TextLine.text TextLine.text.length}. + */ + readonly isEmptyOrWhitespace: boolean; + } + + /** + * Represents a text document, such as a source file. Text documents have + * {@link TextLine lines} and knowledge about an underlying resource like a file. + */ + export interface TextDocument { + + /** + * The associated uri for this document. + * + * *Note* that most documents use the `file`-scheme, which means they are files on disk. However, **not** all documents are + * saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk. + * + * @see {@link FileSystemProvider} + * @see {@link TextDocumentContentProvider} + */ + readonly uri: Uri; + + /** + * The file system path of the associated resource. Shorthand + * notation for {@link TextDocument.uri TextDocument.uri.fsPath}. Independent of the uri scheme. + */ + readonly fileName: string; + + /** + * Is this document representing an untitled file which has never been saved yet. *Note* that + * this does not mean the document will be saved to disk, use {@linkcode Uri.scheme} + * to figure out where a document will be {@link FileSystemProvider saved}, e.g. `file`, `ftp` etc. + */ + readonly isUntitled: boolean; + + /** + * The identifier of the language associated with this document. + */ + readonly languageId: string; + + /** + * The version number of this document (it will strictly increase after each + * change, including undo/redo). + */ + readonly version: number; + + /** + * `true` if there are unpersisted changes. + */ + readonly isDirty: boolean; + + /** + * `true` if the document has been closed. A closed document isn't synchronized anymore + * and won't be re-used when the same resource is opened again. + */ + readonly isClosed: boolean; + + /** + * Save the underlying file. + * + * @returns A promise that will resolve to `true` when the file + * has been saved. If the save failed, will return `false`. + */ + save(): Thenable; + + /** + * The {@link EndOfLine end of line} sequence that is predominately + * used in this document. + */ + readonly eol: EndOfLine; + + /** + * The number of lines in this document. + */ + readonly lineCount: number; + + /** + * Returns a text line denoted by the line number. Note + * that the returned object is *not* live and changes to the + * document are not reflected. + * + * @param line A line number in `[0, lineCount)`. + * @returns A {@link TextLine line}. + */ + lineAt(line: number): TextLine; + + /** + * Returns a text line denoted by the position. Note + * that the returned object is *not* live and changes to the + * document are not reflected. + * + * The position will be {@link TextDocument.validatePosition adjusted}. + * + * @see {@link TextDocument.lineAt} + * + * @param position A position. + * @returns A {@link TextLine line}. + */ + lineAt(position: Position): TextLine; + + /** + * Converts the position to a zero-based offset. + * + * The position will be {@link TextDocument.validatePosition adjusted}. + * + * @param position A position. + * @returns A valid zero-based offset. + */ + offsetAt(position: Position): number; + + /** + * Converts a zero-based offset to a position. + * + * @param offset A zero-based offset. + * @returns A valid {@link Position}. + */ + positionAt(offset: number): Position; + + /** + * Get the text of this document. A substring can be retrieved by providing + * a range. The range will be {@link TextDocument.validateRange adjusted}. + * + * @param range Include only the text included by the range. + * @returns The text inside the provided range or the entire text. + */ + getText(range?: Range): string; + + /** + * Get a word-range at the given position. By default words are defined by + * common separators, like space, -, _, etc. In addition, per language custom + * [word definitions] can be defined. It + * is also possible to provide a custom regular expression. + * + * * *Note 1:* A custom regular expression must not match the empty string and + * if it does, it will be ignored. + * * *Note 2:* A custom regular expression will fail to match multiline strings + * and in the name of speed regular expressions should not match words with + * spaces. Use {@linkcode TextLine.text} for more complex, non-wordy, scenarios. + * + * The position will be {@link TextDocument.validatePosition adjusted}. + * + * @param position A position. + * @param regex Optional regular expression that describes what a word is. + * @returns A range spanning a word, or `undefined`. + */ + getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined; + + /** + * Ensure a range is completely contained in this document. + * + * @param range A range. + * @returns The given range or a new, adjusted range. + */ + validateRange(range: Range): Range; + + /** + * Ensure a position is contained in the range of this document. + * + * @param position A position. + * @returns The given position or a new, adjusted position. + */ + validatePosition(position: Position): Position; + } + + /** + * Represents a line and character position, such as + * the position of the cursor. + * + * Position objects are __immutable__. Use the {@link Position.with with} or + * {@link Position.translate translate} methods to derive new positions + * from an existing position. + */ + export class Position { + + /** + * The zero-based line value. + */ + readonly line: number; + + /** + * The zero-based character value. + */ + readonly character: number; + + /** + * @param line A zero-based line value. + * @param character A zero-based character value. + */ + constructor(line: number, character: number); + + /** + * Check if this position is before `other`. + * + * @param other A position. + * @returns `true` if position is on a smaller line + * or on the same line on a smaller character. + */ + isBefore(other: Position): boolean; + + /** + * Check if this position is before or equal to `other`. + * + * @param other A position. + * @returns `true` if position is on a smaller line + * or on the same line on a smaller or equal character. + */ + isBeforeOrEqual(other: Position): boolean; + + /** + * Check if this position is after `other`. + * + * @param other A position. + * @returns `true` if position is on a greater line + * or on the same line on a greater character. + */ + isAfter(other: Position): boolean; + + /** + * Check if this position is after or equal to `other`. + * + * @param other A position. + * @returns `true` if position is on a greater line + * or on the same line on a greater or equal character. + */ + isAfterOrEqual(other: Position): boolean; + + /** + * Check if this position is equal to `other`. + * + * @param other A position. + * @returns `true` if the line and character of the given position are equal to + * the line and character of this position. + */ + isEqual(other: Position): boolean; + + /** + * Compare this to `other`. + * + * @param other A position. + * @returns A number smaller than zero if this position is before the given position, + * a number greater than zero if this position is after the given position, or zero when + * this and the given position are equal. + */ + compareTo(other: Position): number; + + /** + * Create a new position relative to this position. + * + * @param lineDelta Delta value for the line value, default is `0`. + * @param characterDelta Delta value for the character value, default is `0`. + * @returns A position which line and character is the sum of the current line and + * character and the corresponding deltas. + */ + translate(lineDelta?: number, characterDelta?: number): Position; + + /** + * Derived a new position relative to this position. + * + * @param change An object that describes a delta to this position. + * @returns A position that reflects the given delta. Will return `this` position if the change + * is not changing anything. + */ + translate(change: { + /** + * Delta value for the line value, default is `0`. + */ + lineDelta?: number; + /** + * Delta value for the character value, default is `0`. + */ + characterDelta?: number; + }): Position; + + /** + * Create a new position derived from this position. + * + * @param line Value that should be used as line value, default is the {@link Position.line existing value} + * @param character Value that should be used as character value, default is the {@link Position.character existing value} + * @returns A position where line and character are replaced by the given values. + */ + with(line?: number, character?: number): Position; + + /** + * Derived a new position from this position. + * + * @param change An object that describes a change to this position. + * @returns A position that reflects the given change. Will return `this` position if the change + * is not changing anything. + */ + with(change: { + /** + * New line value, defaults the line value of `this`. + */ + line?: number; + /** + * New character value, defaults the character value of `this`. + */ + character?: number; + }): Position; + } + + /** + * A range represents an ordered pair of two positions. + * It is guaranteed that {@link Range.start start}.isBeforeOrEqual({@link Range.end end}) + * + * Range objects are __immutable__. Use the {@link Range.with with}, + * {@link Range.intersection intersection}, or {@link Range.union union} methods + * to derive new ranges from an existing range. + */ + export class Range { + + /** + * The start position. It is before or equal to {@link Range.end end}. + */ + readonly start: Position; + + /** + * The end position. It is after or equal to {@link Range.start start}. + */ + readonly end: Position; + + /** + * Create a new range from two positions. If `start` is not + * before or equal to `end`, the values will be swapped. + * + * @param start A position. + * @param end A position. + */ + constructor(start: Position, end: Position); + + /** + * Create a new range from number coordinates. It is a shorter equivalent of + * using `new Range(new Position(startLine, startCharacter), new Position(endLine, endCharacter))` + * + * @param startLine A zero-based line value. + * @param startCharacter A zero-based character value. + * @param endLine A zero-based line value. + * @param endCharacter A zero-based character value. + */ + constructor(startLine: number, startCharacter: number, endLine: number, endCharacter: number); + + /** + * `true` if `start` and `end` are equal. + */ + isEmpty: boolean; + + /** + * `true` if `start.line` and `end.line` are equal. + */ + isSingleLine: boolean; + + /** + * Check if a position or a range is contained in this range. + * + * @param positionOrRange A position or a range. + * @returns `true` if the position or range is inside or equal + * to this range. + */ + contains(positionOrRange: Position | Range): boolean; + + /** + * Check if `other` equals this range. + * + * @param other A range. + * @returns `true` when start and end are {@link Position.isEqual equal} to + * start and end of this range. + */ + isEqual(other: Range): boolean; + + /** + * Intersect `range` with this range and returns a new range or `undefined` + * if the ranges have no overlap. + * + * @param range A range. + * @returns A range of the greater start and smaller end positions. Will + * return undefined when there is no overlap. + */ + intersection(range: Range): Range | undefined; + + /** + * Compute the union of `other` with this range. + * + * @param other A range. + * @returns A range of smaller start position and the greater end position. + */ + union(other: Range): Range; + + /** + * Derived a new range from this range. + * + * @param start A position that should be used as start. The default value is the {@link Range.start current start}. + * @param end A position that should be used as end. The default value is the {@link Range.end current end}. + * @returns A range derived from this range with the given start and end position. + * If start and end are not different `this` range will be returned. + */ + with(start?: Position, end?: Position): Range; + + /** + * Derived a new range from this range. + * + * @param change An object that describes a change to this range. + * @returns A range that reflects the given change. Will return `this` range if the change + * is not changing anything. + */ + with(change: { + /** + * New start position, defaults to {@link Range.start current start} + */ + start?: Position; + /** + * New end position, defaults to {@link Range.end current end} + */ + end?: Position; + }): Range; + } + + /** + * Represents a text selection in an editor. + */ + export class Selection extends Range { + + /** + * The position at which the selection starts. + * This position might be before or after {@link Selection.active active}. + */ + anchor: Position; + + /** + * The position of the cursor. + * This position might be before or after {@link Selection.anchor anchor}. + */ + active: Position; + + /** + * Create a selection from two positions. + * + * @param anchor A position. + * @param active A position. + */ + constructor(anchor: Position, active: Position); + + /** + * Create a selection from four coordinates. + * + * @param anchorLine A zero-based line value. + * @param anchorCharacter A zero-based character value. + * @param activeLine A zero-based line value. + * @param activeCharacter A zero-based character value. + */ + constructor(anchorLine: number, anchorCharacter: number, activeLine: number, activeCharacter: number); + + /** + * A selection is reversed if its {@link Selection.anchor anchor} is the {@link Selection.end end} position. + */ + isReversed: boolean; + } + + /** + * Represents sources that can cause {@link window.onDidChangeTextEditorSelection selection change events}. + */ + export enum TextEditorSelectionChangeKind { + /** + * Selection changed due to typing in the editor. + */ + Keyboard = 1, + /** + * Selection change due to clicking in the editor. + */ + Mouse = 2, + /** + * Selection changed because a command ran. + */ + Command = 3 + } + + /** + * Represents an event describing the change in a {@link TextEditor.selections text editor's selections}. + */ + export interface TextEditorSelectionChangeEvent { + /** + * The {@link TextEditor text editor} for which the selections have changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.selections text editor's selections}. + */ + readonly selections: readonly Selection[]; + /** + * The {@link TextEditorSelectionChangeKind change kind} which has triggered this + * event. Can be `undefined`. + */ + readonly kind: TextEditorSelectionChangeKind | undefined; + } + + /** + * Represents an event describing the change in a {@link TextEditor.visibleRanges text editor's visible ranges}. + */ + export interface TextEditorVisibleRangesChangeEvent { + /** + * The {@link TextEditor text editor} for which the visible ranges have changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.visibleRanges text editor's visible ranges}. + */ + readonly visibleRanges: readonly Range[]; + } + + /** + * Represents an event describing the change in a {@link TextEditor.options text editor's options}. + */ + export interface TextEditorOptionsChangeEvent { + /** + * The {@link TextEditor text editor} for which the options have changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.options text editor's options}. + */ + readonly options: TextEditorOptions; + } + + /** + * Represents an event describing the change of a {@link TextEditor.viewColumn text editor's view column}. + */ + export interface TextEditorViewColumnChangeEvent { + /** + * The {@link TextEditor text editor} for which the view column has changed. + */ + readonly textEditor: TextEditor; + /** + * The new value for the {@link TextEditor.viewColumn text editor's view column}. + */ + readonly viewColumn: ViewColumn; + } + + /** + * Rendering style of the cursor. + */ + export enum TextEditorCursorStyle { + /** + * Render the cursor as a vertical thick line. + */ + Line = 1, + /** + * Render the cursor as a block filled. + */ + Block = 2, + /** + * Render the cursor as a thick horizontal line. + */ + Underline = 3, + /** + * Render the cursor as a vertical thin line. + */ + LineThin = 4, + /** + * Render the cursor as a block outlined. + */ + BlockOutline = 5, + /** + * Render the cursor as a thin horizontal line. + */ + UnderlineThin = 6 + } + + /** + * Rendering style of the line numbers. + */ + export enum TextEditorLineNumbersStyle { + /** + * Do not render the line numbers. + */ + Off = 0, + /** + * Render the line numbers. + */ + On = 1, + /** + * Render the line numbers with values relative to the primary cursor location. + */ + Relative = 2, + /** + * Render the line numbers on every 10th line number. + */ + Interval = 3, + } + + /** + * Represents a {@link TextEditor text editor}'s {@link TextEditor.options options}. + */ + export interface TextEditorOptions { + + /** + * The size in spaces a tab takes. This is used for two purposes: + * - the rendering width of a tab character; + * - the number of spaces to insert when {@link TextEditorOptions.insertSpaces insertSpaces} is true + * and `indentSize` is set to `"tabSize"`. + * + * When getting a text editor's options, this property will always be a number (resolved). + * When setting a text editor's options, this property is optional and it can be a number or `"auto"`. + */ + tabSize?: number | string; + + /** + * The number of spaces to insert when {@link TextEditorOptions.insertSpaces insertSpaces} is true. + * + * When getting a text editor's options, this property will always be a number (resolved). + * When setting a text editor's options, this property is optional and it can be a number or `"tabSize"`. + */ + indentSize?: number | string; + + /** + * When pressing Tab insert {@link TextEditorOptions.tabSize n} spaces. + * When getting a text editor's options, this property will always be a boolean (resolved). + * When setting a text editor's options, this property is optional and it can be a boolean or `"auto"`. + */ + insertSpaces?: boolean | string; + + /** + * The rendering style of the cursor in this editor. + * When getting a text editor's options, this property will always be present. + * When setting a text editor's options, this property is optional. + */ + cursorStyle?: TextEditorCursorStyle; + + /** + * Render relative line numbers w.r.t. the current line number. + * When getting a text editor's options, this property will always be present. + * When setting a text editor's options, this property is optional. + */ + lineNumbers?: TextEditorLineNumbersStyle; + } + + /** + * Represents a handle to a set of decorations + * sharing the same {@link DecorationRenderOptions styling options} in a {@link TextEditor text editor}. + * + * To get an instance of a `TextEditorDecorationType` use + * {@link window.createTextEditorDecorationType createTextEditorDecorationType}. + */ + export interface TextEditorDecorationType { + + /** + * Internal representation of the handle. + */ + readonly key: string; + + /** + * Remove this decoration type and all decorations on all text editors using it. + */ + dispose(): void; + } + + /** + * Represents different {@link TextEditor.revealRange reveal} strategies in a text editor. + */ + export enum TextEditorRevealType { + /** + * The range will be revealed with as little scrolling as possible. + */ + Default = 0, + /** + * The range will always be revealed in the center of the viewport. + */ + InCenter = 1, + /** + * If the range is outside the viewport, it will be revealed in the center of the viewport. + * Otherwise, it will be revealed with as little scrolling as possible. + */ + InCenterIfOutsideViewport = 2, + /** + * The range will always be revealed at the top of the viewport. + */ + AtTop = 3 + } + + /** + * Represents different positions for rendering a decoration in an {@link DecorationRenderOptions.overviewRulerLane overview ruler}. + * The overview ruler supports three lanes. + */ + export enum OverviewRulerLane { + /** + * The left lane of the overview ruler. + */ + Left = 1, + /** + * The center lane of the overview ruler. + */ + Center = 2, + /** + * The right lane of the overview ruler. + */ + Right = 4, + /** + * All lanes of the overview ruler. + */ + Full = 7 + } + + /** + * Describes the behavior of decorations when typing/editing at their edges. + */ + export enum DecorationRangeBehavior { + /** + * The decoration's range will widen when edits occur at the start or end. + */ + OpenOpen = 0, + /** + * The decoration's range will not widen when edits occur at the start or end. + */ + ClosedClosed = 1, + /** + * The decoration's range will widen when edits occur at the start, but not at the end. + */ + OpenClosed = 2, + /** + * The decoration's range will widen when edits occur at the end, but not at the start. + */ + ClosedOpen = 3 + } + + /** + * Represents options to configure the behavior of showing a {@link TextDocument document} in an {@link TextEditor editor}. + */ + export interface TextDocumentShowOptions { + /** + * An optional view column in which the {@link TextEditor editor} should be shown. + * The default is the {@link ViewColumn.Active active}. Columns that do not exist + * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. + * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently + * active one. + */ + viewColumn?: ViewColumn; + + /** + * An optional flag that when `true` will stop the {@link TextEditor editor} from taking focus. + */ + preserveFocus?: boolean; + + /** + * An optional flag that controls if an {@link TextEditor editor}-tab shows as preview. Preview tabs will + * be replaced and reused until set to stay - either explicitly or through editing. + * + * *Note* that the flag is ignored if a user has disabled preview editors in settings. + */ + preview?: boolean; + + /** + * An optional selection to apply for the document in the {@link TextEditor editor}. + */ + selection?: Range; + } + + /** + * Represents an event describing the change in a {@link NotebookEditor.selections notebook editor's selections}. + */ + export interface NotebookEditorSelectionChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the selections have changed. + */ + readonly notebookEditor: NotebookEditor; + + /** + * The new value for the {@link NotebookEditor.selections notebook editor's selections}. + */ + readonly selections: readonly NotebookRange[]; + } + + /** + * Represents an event describing the change in a {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. + */ + export interface NotebookEditorVisibleRangesChangeEvent { + /** + * The {@link NotebookEditor notebook editor} for which the visible ranges have changed. + */ + readonly notebookEditor: NotebookEditor; + + /** + * The new value for the {@link NotebookEditor.visibleRanges notebook editor's visibleRanges}. + */ + readonly visibleRanges: readonly NotebookRange[]; + } + + /** + * Represents options to configure the behavior of showing a {@link NotebookDocument notebook document} in an {@link NotebookEditor notebook editor}. + */ + export interface NotebookDocumentShowOptions { + /** + * An optional view column in which the {@link NotebookEditor notebook editor} should be shown. + * The default is the {@link ViewColumn.Active active}. Columns that do not exist + * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. + * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently + * active one. + */ + readonly viewColumn?: ViewColumn; + + /** + * An optional flag that when `true` will stop the {@link NotebookEditor notebook editor} from taking focus. + */ + readonly preserveFocus?: boolean; + + /** + * An optional flag that controls if an {@link NotebookEditor notebook editor}-tab shows as preview. Preview tabs will + * be replaced and reused until set to stay - either explicitly or through editing. The default behaviour depends + * on the `workbench.editor.enablePreview`-setting. + */ + readonly preview?: boolean; + + /** + * An optional selection to apply for the document in the {@link NotebookEditor notebook editor}. + */ + readonly selections?: readonly NotebookRange[]; + } + + /** + * A reference to one of the workbench colors as defined in https://code.visualstudio.com/api/references/theme-color. + * Using a theme color is preferred over a custom color as it gives theme authors and users the possibility to change the color. + */ + export class ThemeColor { + + /** + * The id of this color. + */ + readonly id: string; + + /** + * Creates a reference to a theme color. + * @param id of the color. The available colors are listed in https://code.visualstudio.com/api/references/theme-color. + */ + constructor(id: string); + } + + /** + * A reference to a named icon. Currently, {@link ThemeIcon.File File}, {@link ThemeIcon.Folder Folder}, + * and [ThemeIcon ids](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing) are supported. + * Using a theme icon is preferred over a custom icon as it gives product theme authors the possibility to change the icons. + * + * *Note* that theme icons can also be rendered inside labels and descriptions. Places that support theme icons spell this out + * and they use the `$()`-syntax, for instance `quickPick.label = "Hello World $(globe)"`. + */ + export class ThemeIcon { + /** + * Reference to an icon representing a file. The icon is taken from the current file icon theme or a placeholder icon is used. + */ + static readonly File: ThemeIcon; + + /** + * Reference to an icon representing a folder. The icon is taken from the current file icon theme or a placeholder icon is used. + */ + static readonly Folder: ThemeIcon; + + /** + * The id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. + */ + readonly id: string; + + /** + * The optional ThemeColor of the icon. The color is currently only used in {@link TreeItem}. + */ + readonly color?: ThemeColor | undefined; + + /** + * Creates a reference to a theme icon. + * @param id id of the icon. The available icons are listed in https://code.visualstudio.com/api/references/icons-in-labels#icon-listing. + * @param color optional `ThemeColor` for the icon. The color is currently only used in {@link TreeItem}. + */ + constructor(id: string, color?: ThemeColor); + } + + /** + * Represents an icon in the UI. This is either an uri, separate uris for the light- and dark-themes, + * or a {@link ThemeIcon theme icon}. + */ + export type IconPath = Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; + + /** + * Represents theme specific rendering styles for a {@link TextEditorDecorationType text editor decoration}. + */ + export interface ThemableDecorationRenderOptions { + /** + * Background color of the decoration. Use rgba() and define transparent background colors to play well with other decorations. + * Alternatively a color from the color registry can be {@link ThemeColor referenced}. + */ + backgroundColor?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + outline?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'outline' for setting one or more of the individual outline properties. + */ + outlineColor?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'outline' for setting one or more of the individual outline properties. + */ + outlineStyle?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'outline' for setting one or more of the individual outline properties. + */ + outlineWidth?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + border?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderColor?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderRadius?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderSpacing?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderStyle?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + * Better use 'border' for setting one or more of the individual border properties. + */ + borderWidth?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + fontStyle?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + fontWeight?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + textDecoration?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + cursor?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + color?: string | ThemeColor; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + opacity?: string; + + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + letterSpacing?: string; + + /** + * An **absolute path** or an URI to an image to be rendered in the gutter. + */ + gutterIconPath?: string | Uri; + + /** + * Specifies the size of the gutter icon. + * Available values are 'auto', 'contain', 'cover' and any percentage value. + * For further information: https://msdn.microsoft.com/en-us/library/jj127316(v=vs.85).aspx + */ + gutterIconSize?: string; + + /** + * The color of the decoration in the overview ruler. Use rgba() and define transparent colors to play well with other decorations. + */ + overviewRulerColor?: string | ThemeColor; + + /** + * Defines the rendering options of the attachment that is inserted before the decorated text. + */ + before?: ThemableDecorationAttachmentRenderOptions; + + /** + * Defines the rendering options of the attachment that is inserted after the decorated text. + */ + after?: ThemableDecorationAttachmentRenderOptions; + } + + /** + * Represents theme specific rendering styles for {@link ThemableDecorationRenderOptions.before before} and + * {@link ThemableDecorationRenderOptions.after after} the content of text decorations. + */ + export interface ThemableDecorationAttachmentRenderOptions { + /** + * Defines a text content that is shown in the attachment. Either an icon or a text can be shown, but not both. + */ + contentText?: string; + /** + * An **absolute path** or an URI to an image to be rendered in the attachment. Either an icon + * or a text can be shown, but not both. + */ + contentIconPath?: string | Uri; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + border?: string; + /** + * CSS styling property that will be applied to text enclosed by a decoration. + */ + borderColor?: string | ThemeColor; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontStyle?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + fontWeight?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + textDecoration?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + color?: string | ThemeColor; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + backgroundColor?: string | ThemeColor; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + margin?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + width?: string; + /** + * CSS styling property that will be applied to the decoration attachment. + */ + height?: string; + } + + /** + * Represents rendering styles for a {@link TextEditorDecorationType text editor decoration}. + */ + export interface DecorationRenderOptions extends ThemableDecorationRenderOptions { + /** + * Should the decoration be rendered also on the whitespace after the line text. + * Defaults to `false`. + */ + isWholeLine?: boolean; + + /** + * Customize the growing behavior of the decoration when edits occur at the edges of the decoration's range. + * Defaults to `DecorationRangeBehavior.OpenOpen`. + */ + rangeBehavior?: DecorationRangeBehavior; + + /** + * The position in the overview ruler where the decoration should be rendered. + */ + overviewRulerLane?: OverviewRulerLane; + + /** + * Overwrite options for light themes. + */ + light?: ThemableDecorationRenderOptions; + + /** + * Overwrite options for dark themes. + */ + dark?: ThemableDecorationRenderOptions; + } + + /** + * Represents options for a specific decoration in a {@link TextEditorDecorationType decoration set}. + */ + export interface DecorationOptions { + + /** + * Range to which this decoration is applied. The range must not be empty. + */ + range: Range; + + /** + * A message that should be rendered when hovering over the decoration. + */ + hoverMessage?: MarkdownString | MarkedString | Array; + + /** + * Render options applied to the current decoration. For performance reasons, keep the + * number of decoration specific options small, and use decoration types wherever possible. + */ + renderOptions?: DecorationInstanceRenderOptions; + } + + /** + * Represents themable render options for decoration instances. + */ + export interface ThemableDecorationInstanceRenderOptions { + /** + * Defines the rendering options of the attachment that is inserted before the decorated text. + */ + before?: ThemableDecorationAttachmentRenderOptions; + + /** + * Defines the rendering options of the attachment that is inserted after the decorated text. + */ + after?: ThemableDecorationAttachmentRenderOptions; + } + + /** + * Represents render options for decoration instances. See {@link DecorationOptions.renderOptions}. + */ + export interface DecorationInstanceRenderOptions extends ThemableDecorationInstanceRenderOptions { + /** + * Overwrite options for light themes. + */ + light?: ThemableDecorationInstanceRenderOptions; + + /** + * Overwrite options for dark themes. + */ + dark?: ThemableDecorationInstanceRenderOptions; + } + + /** + * Represents an editor that is attached to a {@link TextDocument document}. + */ + export interface TextEditor { + + /** + * The document associated with this text editor. The document will be the same for the entire lifetime of this text editor. + */ + readonly document: TextDocument; + + /** + * The primary selection on this text editor. Shorthand for `TextEditor.selections[0]`. + */ + selection: Selection; + + /** + * The selections in this text editor. The primary selection is always at index 0. + */ + selections: readonly Selection[]; + + /** + * The current visible ranges in the editor (vertically). + * This accounts only for vertical scrolling, and not for horizontal scrolling. + */ + readonly visibleRanges: readonly Range[]; + + /** + * Text editor options. + */ + options: TextEditorOptions; + + /** + * The column in which this editor shows. Will be `undefined` in case this + * isn't one of the main editors, e.g. an embedded editor, or when the editor + * column is larger than three. + */ + readonly viewColumn: ViewColumn | undefined; + + /** + * Perform an edit on the document associated with this text editor. + * + * The given callback-function is invoked with an {@link TextEditorEdit edit-builder} which must + * be used to make edits. Note that the edit-builder is only valid while the + * callback executes. + * + * @param callback A function which can create edits using an {@link TextEditorEdit edit-builder}. + * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. + * @returns A promise that resolves with a value indicating if the edits could be applied. + */ + edit(callback: (editBuilder: TextEditorEdit) => void, options?: { + /** + * Add undo stop before making the edits. + */ + readonly undoStopBefore: boolean; + /** + * Add undo stop after making the edits. + */ + readonly undoStopAfter: boolean; + }): Thenable; + + /** + * Insert a {@link SnippetString snippet} and put the editor into snippet mode. "Snippet mode" + * means the editor adds placeholders and additional cursors so that the user can complete + * or accept the snippet. + * + * @param snippet The snippet to insert in this edit. + * @param location Position or range at which to insert the snippet, defaults to the current editor selection or selections. + * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. + * @returns A promise that resolves with a value indicating if the snippet could be inserted. Note that the promise does not signal + * that the snippet is completely filled-in or accepted. + */ + insertSnippet(snippet: SnippetString, location?: Position | Range | readonly Position[] | readonly Range[], options?: { + /** + * Add undo stop before making the edits. + */ + readonly undoStopBefore: boolean; + /** + * Add undo stop after making the edits. + */ + readonly undoStopAfter: boolean; + }): Thenable; + + /** + * Adds a set of decorations to the text editor. If a set of decorations already exists with + * the given {@link TextEditorDecorationType decoration type}, they will be replaced. If + * `rangesOrOptions` is empty, the existing decorations with the given {@link TextEditorDecorationType decoration type} + * will be removed. + * + * @see {@link window.createTextEditorDecorationType createTextEditorDecorationType}. + * + * @param decorationType A decoration type. + * @param rangesOrOptions Either {@link Range ranges} or more detailed {@link DecorationOptions options}. + */ + setDecorations(decorationType: TextEditorDecorationType, rangesOrOptions: readonly Range[] | readonly DecorationOptions[]): void; + + /** + * Scroll as indicated by `revealType` in order to reveal the given range. + * + * @param range A range. + * @param revealType The scrolling strategy for revealing `range`. + */ + revealRange(range: Range, revealType?: TextEditorRevealType): void; + + /** + * Show the text editor. + * + * @deprecated Use {@link window.showTextDocument} instead. + * + * @param column The {@link ViewColumn column} in which to show this editor. + * This method shows unexpected behavior and will be removed in the next major update. + */ + show(column?: ViewColumn): void; + + /** + * Hide the text editor. + * + * @deprecated Use the command `workbench.action.closeActiveEditor` instead. + * This method shows unexpected behavior and will be removed in the next major update. + */ + hide(): void; + } + + /** + * Represents an end of line character sequence in a {@link TextDocument document}. + */ + export enum EndOfLine { + /** + * The line feed `\n` character. + */ + LF = 1, + /** + * The carriage return line feed `\r\n` sequence. + */ + CRLF = 2 + } + + /** + * A complex edit that will be applied in one transaction on a TextEditor. + * This holds a description of the edits and if the edits are valid (i.e. no overlapping regions, document was not changed in the meantime, etc.) + * they can be applied on a {@link TextDocument document} associated with a {@link TextEditor text editor}. + */ + export interface TextEditorEdit { + /** + * Replace a certain text region with a new value. + * You can use `\r\n` or `\n` in `value` and they will be normalized to the current {@link TextDocument document}. + * + * @param location The range this operation should remove. + * @param value The new text this operation should insert after removing `location`. + */ + replace(location: Position | Range | Selection, value: string): void; + + /** + * Insert text at a location. + * You can use `\r\n` or `\n` in `value` and they will be normalized to the current {@link TextDocument document}. + * Although the equivalent text edit can be made with {@link TextEditorEdit.replace replace}, `insert` will produce a different resulting selection (it will get moved). + * + * @param location The position where the new text should be inserted. + * @param value The new text this operation should insert. + */ + insert(location: Position, value: string): void; + + /** + * Delete a certain text region. + * + * @param location The range this operation should remove. + */ + delete(location: Range | Selection): void; + + /** + * Set the end of line sequence. + * + * @param endOfLine The new end of line for the {@link TextDocument document}. + */ + setEndOfLine(endOfLine: EndOfLine): void; + } + + /** + * A universal resource identifier representing either a file on disk + * or another resource, like untitled resources. + */ + export class Uri { + + /** + * Create an URI from a string, e.g. `http://www.example.com/some/path`, + * `file:///usr/home`, or `scheme:with/path`. + * + * *Note* that for a while uris without a `scheme` were accepted. That is not correct + * as all uris should have a scheme. To avoid breakage of existing code the optional + * `strict`-argument has been added. We *strongly* advise to use it, e.g. `Uri.parse('my:uri', true)` + * + * @see {@link Uri.toString} + * @param value The string value of an Uri. + * @param strict Throw an error when `value` is empty or when no `scheme` can be parsed. + * @returns A new Uri instance. + */ + static parse(value: string, strict?: boolean): Uri; + + /** + * Create an URI from a file system path. The {@link Uri.scheme scheme} + * will be `file`. + * + * The *difference* between {@link Uri.parse} and {@link Uri.file} is that the latter treats the argument + * as path, not as stringified-uri. E.g. `Uri.file(path)` is *not* the same as + * `Uri.parse('file://' + path)` because the path might contain characters that are + * interpreted (# and ?). See the following sample: + * ```ts + * const good = URI.file('/coding/c#/project1'); + * good.scheme === 'file'; + * good.path === '/coding/c#/project1'; + * good.fragment === ''; + * + * const bad = URI.parse('file://' + '/coding/c#/project1'); + * bad.scheme === 'file'; + * bad.path === '/coding/c'; // path is now broken + * bad.fragment === '/project1'; + * ``` + * + * @param path A file system or UNC path. + * @returns A new Uri instance. + */ + static file(path: string): Uri; + + /** + * Create a new uri which path is the result of joining + * the path of the base uri with the provided path segments. + * + * - Note 1: `joinPath` only affects the path component + * and all other components (scheme, authority, query, and fragment) are + * left as they are. + * - Note 2: The base uri must have a path; an error is thrown otherwise. + * + * The path segments are normalized in the following ways: + * - sequences of path separators (`/` or `\`) are replaced with a single separator + * - for `file`-uris on windows, the backslash-character (`\`) is considered a path-separator + * - the `..`-segment denotes the parent segment, the `.` denotes the current segment + * - paths have a root which always remains, for instance on windows drive-letters are roots + * so that is true: `joinPath(Uri.file('file:///c:/root'), '../../other').fsPath === 'c:/other'` + * + * @param base An uri. Must have a path. + * @param pathSegments One more more path fragments + * @returns A new uri which path is joined with the given fragments + */ + static joinPath(base: Uri, ...pathSegments: string[]): Uri; + + /** + * Create an URI from its component parts + * + * @see {@link Uri.toString} + * @param components The component parts of an Uri. + * @returns A new Uri instance. + */ + static from(components: { + /** + * The scheme of the uri + */ + readonly scheme: string; + /** + * The authority of the uri + */ + readonly authority?: string; + /** + * The path of the uri + */ + readonly path?: string; + /** + * The query string of the uri + */ + readonly query?: string; + /** + * The fragment identifier of the uri + */ + readonly fragment?: string; + }): Uri; + + /** + * Use the `file` and `parse` factory functions to create new `Uri` objects. + */ + private constructor(scheme: string, authority: string, path: string, query: string, fragment: string); + + /** + * Scheme is the `http` part of `http://www.example.com/some/path?query#fragment`. + * The part before the first colon. + */ + readonly scheme: string; + + /** + * Authority is the `www.example.com` part of `http://www.example.com/some/path?query#fragment`. + * The part between the first double slashes and the next slash. + */ + readonly authority: string; + + /** + * Path is the `/some/path` part of `http://www.example.com/some/path?query#fragment`. + */ + readonly path: string; + + /** + * Query is the `query` part of `http://www.example.com/some/path?query#fragment`. + */ + readonly query: string; + + /** + * Fragment is the `fragment` part of `http://www.example.com/some/path?query#fragment`. + */ + readonly fragment: string; + + /** + * The string representing the corresponding file system path of this Uri. + * + * Will handle UNC paths and normalize windows drive letters to lower-case. Also + * uses the platform specific path separator. + * + * * Will *not* validate the path for invalid characters and semantics. + * * Will *not* look at the scheme of this Uri. + * * The resulting string shall *not* be used for display purposes but + * for disk operations, like `readFile` et al. + * + * The *difference* to the {@linkcode Uri.path path}-property is the use of the platform specific + * path separator and the handling of UNC paths. The sample below outlines the difference: + * ```ts + * const u = URI.parse('file://server/c$/folder/file.txt') + * u.authority === 'server' + * u.path === '/c$/folder/file.txt' + * u.fsPath === '\\server\c$\folder\file.txt' + * ``` + */ + readonly fsPath: string; + + /** + * Derive a new Uri from this Uri. + * + * ```ts + * let file = Uri.parse('before:some/file/path'); + * let other = file.with({ scheme: 'after' }); + * assert.ok(other.toString() === 'after:some/file/path'); + * ``` + * + * @param change An object that describes a change to this Uri. To unset components use `null` or + * the empty string. + * @returns A new Uri that reflects the given change. Will return `this` Uri if the change + * is not changing anything. + */ + with(change: { + /** + * The new scheme, defaults to this Uri's scheme. + */ + scheme?: string; + /** + * The new authority, defaults to this Uri's authority. + */ + authority?: string; + /** + * The new path, defaults to this Uri's path. + */ + path?: string; + /** + * The new query, defaults to this Uri's query. + */ + query?: string; + /** + * The new fragment, defaults to this Uri's fragment. + */ + fragment?: string; + }): Uri; + + /** + * Returns a string representation of this Uri. The representation and normalization + * of a URI depends on the scheme. + * + * * The resulting string can be safely used with {@link Uri.parse}. + * * The resulting string shall *not* be used for display purposes. + * + * *Note* that the implementation will encode _aggressive_ which often leads to unexpected, + * but not incorrect, results. For instance, colons are encoded to `%3A` which might be unexpected + * in file-uri. Also `&` and `=` will be encoded which might be unexpected for http-uris. For stability + * reasons this cannot be changed anymore. If you suffer from too aggressive encoding you should use + * the `skipEncoding`-argument: `uri.toString(true)`. + * + * @param skipEncoding Do not percentage-encode the result, defaults to `false`. Note that + * the `#` and `?` characters occurring in the path will always be encoded. + * @returns A string representation of this Uri. + */ + toString(skipEncoding?: boolean): string; + + /** + * Returns a JSON representation of this Uri. + * + * @returns An object. + */ + toJSON(): any; + } + + /** + * A cancellation token is passed to an asynchronous or long running + * operation to request cancellation, like cancelling a request + * for completion items because the user continued to type. + * + * To get an instance of a `CancellationToken` use a + * {@link CancellationTokenSource}. + */ + export interface CancellationToken { + + /** + * Is `true` when the token has been cancelled, `false` otherwise. + */ + isCancellationRequested: boolean; + + /** + * An {@link Event} which fires upon cancellation. + */ + onCancellationRequested: Event; + } + + /** + * A cancellation source creates and controls a {@link CancellationToken cancellation token}. + */ + export class CancellationTokenSource { + + /** + * The cancellation token of this source. + */ + token: CancellationToken; + + /** + * Signal cancellation on the token. + */ + cancel(): void; + + /** + * Dispose object and free resources. + */ + dispose(): void; + } + + /** + * An error type that should be used to signal cancellation of an operation. + * + * This type can be used in response to a {@link CancellationToken cancellation token} + * being cancelled or when an operation is being cancelled by the + * executor of that operation. + */ + export class CancellationError extends Error { + + /** + * Creates a new cancellation error. + */ + constructor(); + } + + /** + * Represents a type which can release resources, such + * as event listening or a timer. + */ + export class Disposable { + + /** + * Combine many disposable-likes into one. You can use this method when having objects with + * a dispose function which aren't instances of `Disposable`. + * + * @param disposableLikes Objects that have at least a `dispose`-function member. Note that asynchronous + * dispose-functions aren't awaited. + * @returns Returns a new disposable which, upon dispose, will + * dispose all provided disposables. + */ + static from(...disposableLikes: { + /** + * Function to clean up resources. + */ + dispose: () => any; + }[]): Disposable; + + /** + * Creates a new disposable that calls the provided function + * on dispose. + * + * *Note* that an asynchronous function is not awaited. + * + * @param callOnDispose Function that disposes something. + */ + constructor(callOnDispose: () => any); + + /** + * Dispose this object. + */ + dispose(): any; + } + + /** + * Represents a typed event. + * + * A function that represents an event to which you subscribe by calling it with + * a listener function as argument. + * + * @example + * item.onDidChange(function(event) { console.log("Event happened: " + event); }); + */ + export interface Event { + + /** + * A function that represents an event to which you subscribe by calling it with + * a listener function as argument. + * + * @param listener The listener function will be called when the event happens. + * @param thisArgs The `this`-argument which will be used when calling the event listener. + * @param disposables An array to which a {@link Disposable} will be added. + * @returns A disposable which unsubscribes the event listener. + */ + (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable; + } + + /** + * An event emitter can be used to create and manage an {@link Event} for others + * to subscribe to. One emitter always owns one event. + * + * Use this class if you want to provide event from within your extension, for instance + * inside a {@link TextDocumentContentProvider} or when providing + * API to other extensions. + */ + export class EventEmitter { + + /** + * The event listeners can subscribe to. + */ + event: Event; + + /** + * Notify all subscribers of the {@link EventEmitter.event event}. Failure + * of one or more listener will not fail this function call. + * + * @param data The event object. + */ + fire(data: T): void; + + /** + * Dispose this object and free resources. + */ + dispose(): void; + } + + /** + * A file system watcher notifies about changes to files and folders + * on disk or from other {@link FileSystemProvider FileSystemProviders}. + * + * To get an instance of a `FileSystemWatcher` use + * {@link workspace.createFileSystemWatcher createFileSystemWatcher}. + */ + export interface FileSystemWatcher extends Disposable { + + /** + * true if this file system watcher has been created such that + * it ignores creation file system events. + */ + readonly ignoreCreateEvents: boolean; + + /** + * true if this file system watcher has been created such that + * it ignores change file system events. + */ + readonly ignoreChangeEvents: boolean; + + /** + * true if this file system watcher has been created such that + * it ignores delete file system events. + */ + readonly ignoreDeleteEvents: boolean; + + /** + * An event which fires on file/folder creation. + */ + readonly onDidCreate: Event; + + /** + * An event which fires on file/folder change. + */ + readonly onDidChange: Event; + + /** + * An event which fires on file/folder deletion. + */ + readonly onDidDelete: Event; + } + + /** + * A text document content provider allows to add readonly documents + * to the editor, such as source from a dll or generated html from md. + * + * Content providers are {@link workspace.registerTextDocumentContentProvider registered} + * for a {@link Uri.scheme uri-scheme}. When a uri with that scheme is to + * be {@link workspace.openTextDocument loaded} the content provider is + * asked. + */ + export interface TextDocumentContentProvider { + + /** + * An event to signal a resource has changed. + */ + onDidChange?: Event; + + /** + * Provide textual content for a given uri. + * + * The editor will use the returned string-content to create a readonly + * {@link TextDocument document}. Resources allocated should be released when + * the corresponding document has been {@link workspace.onDidCloseTextDocument closed}. + * + * **Note**: The contents of the created {@link TextDocument document} might not be + * identical to the provided text due to end-of-line-sequence normalization. + * + * @param uri An uri which scheme matches the scheme this provider was {@link workspace.registerTextDocumentContentProvider registered} for. + * @param token A cancellation token. + * @returns A string or a thenable that resolves to such. + */ + provideTextDocumentContent(uri: Uri, token: CancellationToken): ProviderResult; + } + + /** + * The kind of {@link QuickPickItem quick pick item}. + */ + export enum QuickPickItemKind { + /** + * When a {@link QuickPickItem} has a kind of {@link Separator}, the item is just a visual separator and does not represent a real item. + * The only property that applies is {@link QuickPickItem.label label }. All other properties on {@link QuickPickItem} will be ignored and have no effect. + */ + Separator = -1, + /** + * The default {@link QuickPickItem.kind} is an item that can be selected in the quick pick. + */ + Default = 0, + } + + /** + * Represents an item that can be selected from + * a list of items. + */ + export interface QuickPickItem { + + /** + * A human-readable string which is rendered prominent. Supports rendering of {@link ThemeIcon theme icons} via + * the `$()`-syntax. + */ + label: string; + + /** + * The kind of QuickPickItem that will determine how this item is rendered in the quick pick. When not specified, + * the default is {@link QuickPickItemKind.Default}. + */ + kind?: QuickPickItemKind; + + /** + * The icon path or {@link ThemeIcon} for the QuickPickItem. + */ + iconPath?: IconPath; + + /** + * A human-readable string which is rendered less prominent in the same line. Supports rendering of + * {@link ThemeIcon theme icons} via the `$()`-syntax. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + description?: string; + + /** + * A human-readable string which is rendered less prominent in a separate line. Supports rendering of + * {@link ThemeIcon theme icons} via the `$()`-syntax. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + detail?: string; + + /** + * Optional flag indicating if this item is picked initially. This is only honored when using + * the {@link window.showQuickPick showQuickPick()} API. To do the same thing with + * the {@link window.createQuickPick createQuickPick()} API, simply set the {@link QuickPick.selectedItems} + * to the items you want picked initially. + * (*Note:* This is only honored when the picker allows multiple selections.) + * + * @see {@link QuickPickOptions.canPickMany} + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + picked?: boolean; + + /** + * Always show this item. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + alwaysShow?: boolean; + + /** + * Optional buttons that will be rendered on this particular item. These buttons will trigger + * an {@link QuickPickItemButtonEvent} when clicked. Buttons are only rendered when using a quickpick + * created by the {@link window.createQuickPick createQuickPick()} API. Buttons are not rendered when using + * the {@link window.showQuickPick showQuickPick()} API. + * + * Note: this property is ignored when {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Separator} + */ + buttons?: readonly QuickInputButton[]; + } + + /** + * Options to configure the behavior of the quick pick UI. + */ + export interface QuickPickOptions { + + /** + * An optional string that represents the title of the quick pick. + */ + title?: string; + + /** + * An optional flag to include the description when filtering the picks. + */ + matchOnDescription?: boolean; + + /** + * An optional flag to include the detail when filtering the picks. + */ + matchOnDetail?: boolean; + + /** + * An optional string to show as placeholder in the input box to guide the user what to pick on. + */ + placeHolder?: string; + + /** + * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut?: boolean; + + /** + * An optional flag to make the picker accept multiple selections, if true the result is an array of picks. + */ + canPickMany?: boolean; + + /** + * An optional function that is invoked whenever an item is selected. + */ + onDidSelectItem?(item: QuickPickItem | string): any; + } + + /** + * Options to configure the behaviour of the {@link WorkspaceFolder workspace folder} pick UI. + */ + export interface WorkspaceFolderPickOptions { + + /** + * An optional string to show as placeholder in the input box to guide the user what to pick on. + */ + placeHolder?: string; + + /** + * Set to `true` to keep the picker open when focus moves to another part of the editor or to another window. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut?: boolean; + } + + /** + * Options to configure the behaviour of a file open dialog. + * + * * Note 1: On Windows and Linux, a file dialog cannot be both a file selector and a folder selector, so if you + * set both `canSelectFiles` and `canSelectFolders` to `true` on these platforms, a folder selector will be shown. + * * Note 2: Explicitly setting `canSelectFiles` and `canSelectFolders` to `false` is futile + * and the editor then silently adjusts the options to select files. + */ + export interface OpenDialogOptions { + /** + * The resource the dialog shows when opened. + */ + defaultUri?: Uri; + + /** + * A human-readable string for the open button. + */ + openLabel?: string; + + /** + * Allow to select files, defaults to `true`. + */ + canSelectFiles?: boolean; + + /** + * Allow to select folders, defaults to `false`. + */ + canSelectFolders?: boolean; + + /** + * Allow to select many files or folders. + */ + canSelectMany?: boolean; + + /** + * A set of file filters that are used by the dialog. Each entry is a human-readable label, + * like "TypeScript", and an array of extensions, for example: + * ```ts + * { + * 'Images': ['png', 'jpg'], + * 'TypeScript': ['ts', 'tsx'] + * } + * ``` + */ + filters?: { [name: string]: string[] }; + + /** + * Dialog title. + * + * This parameter might be ignored, as not all operating systems display a title on open dialogs + * (for example, macOS). + */ + title?: string; + } + + /** + * Options to configure the behaviour of a file save dialog. + */ + export interface SaveDialogOptions { + /** + * The resource the dialog shows when opened. + */ + defaultUri?: Uri; + + /** + * A human-readable string for the save button. + */ + saveLabel?: string; + + /** + * A set of file filters that are used by the dialog. Each entry is a human-readable label, + * like "TypeScript", and an array of extensions, for example: + * ```ts + * { + * 'Images': ['png', 'jpg'], + * 'TypeScript': ['ts', 'tsx'] + * } + * ``` + */ + filters?: { [name: string]: string[] }; + + /** + * Dialog title. + * + * This parameter might be ignored, as not all operating systems display a title on save dialogs + * (for example, macOS). + */ + title?: string; + } + + /** + * Represents an action that is shown with an information, warning, or + * error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * @see {@link window.showWarningMessage showWarningMessage} + * @see {@link window.showErrorMessage showErrorMessage} + */ + export interface MessageItem { + + /** + * A short title like 'Retry', 'Open Log' etc. + */ + title: string; + + /** + * A hint for modal dialogs that the item should be triggered + * when the user cancels the dialog (e.g. by pressing the ESC + * key). + * + * Note: this option is ignored for non-modal messages. + */ + isCloseAffordance?: boolean; + } + + /** + * Options to configure the behavior of the message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * @see {@link window.showWarningMessage showWarningMessage} + * @see {@link window.showErrorMessage showErrorMessage} + */ + export interface MessageOptions { + + /** + * Indicates that this message should be modal. + */ + modal?: boolean; + + /** + * Human-readable detail message that is rendered less prominent. _Note_ that detail + * is only shown for {@link MessageOptions.modal modal} messages. + */ + detail?: string; + } + + /** + * Impacts the behavior and appearance of the validation message. + */ + /** + * The severity level for input box validation. + */ + export enum InputBoxValidationSeverity { + /** + * Informational severity level. + */ + Info = 1, + /** + * Warning severity level. + */ + Warning = 2, + /** + * Error severity level. + */ + Error = 3 + } + + /** + * Object to configure the behavior of the validation message. + */ + export interface InputBoxValidationMessage { + /** + * The validation message to display. + */ + readonly message: string; + + /** + * The severity of the validation message. + * NOTE: When using `InputBoxValidationSeverity.Error`, the user will not be allowed to accept (hit ENTER) the input. + * `Info` and `Warning` will still allow the InputBox to accept the input. + */ + readonly severity: InputBoxValidationSeverity; + } + + /** + * Options to configure the behavior of the input box UI. + */ + export interface InputBoxOptions { + + /** + * An optional string that represents the title of the input box. + */ + title?: string; + + /** + * The value to pre-fill in the input box. + */ + value?: string; + + /** + * Selection of the pre-filled {@linkcode InputBoxOptions.value value}. Defined as tuple of two number where the + * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole + * pre-filled value will be selected, when empty (start equals end) only the cursor will be set, + * otherwise the defined range will be selected. + */ + valueSelection?: [number, number]; + + /** + * The text to display underneath the input box. + */ + prompt?: string; + + /** + * An optional string to show as placeholder in the input box to guide the user what to type. + */ + placeHolder?: string; + + /** + * Controls if a password input is shown. Password input hides the typed text. + */ + password?: boolean; + + /** + * Set to `true` to keep the input box open when focus moves to another part of the editor or to another window. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut?: boolean; + + /** + * An optional function that will be called to validate input and to give a hint + * to the user. + * + * @param value The current value of the input box. + * @returns Either a human-readable string which is presented as an error message or an {@link InputBoxValidationMessage} + * which can provide a specific message severity. Return `undefined`, `null`, or the empty string when 'value' is valid. + */ + validateInput?(value: string): string | InputBoxValidationMessage | undefined | null | + Thenable; + } + + /** + * A relative pattern is a helper to construct glob patterns that are matched + * relatively to a base file path. The base path can either be an absolute file + * path as string or uri or a {@link WorkspaceFolder workspace folder}, which is the + * preferred way of creating the relative pattern. + */ + export class RelativePattern { + + /** + * A base file path to which this pattern will be matched against relatively. The + * file path must be absolute, should not have any trailing path separators and + * not include any relative segments (`.` or `..`). + */ + baseUri: Uri; + + /** + * A base file path to which this pattern will be matched against relatively. + * + * This matches the `fsPath` value of {@link RelativePattern.baseUri}. + * + * *Note:* updating this value will update {@link RelativePattern.baseUri} to + * be a uri with `file` scheme. + * + * @deprecated This property is deprecated, please use {@link RelativePattern.baseUri} instead. + */ + base: string; + + /** + * A file glob pattern like `*.{ts,js}` that will be matched on file paths + * relative to the base path. + * + * Example: Given a base of `/home/work/folder` and a file path of `/home/work/folder/index.js`, + * the file glob pattern will match on `index.js`. + */ + pattern: string; + + /** + * Creates a new relative pattern object with a base file path and pattern to match. This pattern + * will be matched on file paths relative to the base. + * + * Example: + * ```ts + * const folder = vscode.workspace.workspaceFolders?.[0]; + * if (folder) { + * + * // Match any TypeScript file in the root of this workspace folder + * const pattern1 = new vscode.RelativePattern(folder, '*.ts'); + * + * // Match any TypeScript file in `someFolder` inside this workspace folder + * const pattern2 = new vscode.RelativePattern(folder, 'someFolder/*.ts'); + * } + * ``` + * + * @param base A base to which this pattern will be matched against relatively. It is recommended + * to pass in a {@link WorkspaceFolder workspace folder} if the pattern should match inside the workspace. + * Otherwise, a uri or string should only be used if the pattern is for a file path outside the workspace. + * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on paths relative to the base. + */ + constructor(base: WorkspaceFolder | Uri | string, pattern: string); + } + + /** + * A file glob pattern to match file paths against. This can either be a glob pattern string + * (like `**​/*.{ts,js}` or `*.{ts,js}`) or a {@link RelativePattern relative pattern}. + * + * Glob patterns can have the following syntax: + * * `*` to match zero or more characters in a path segment + * * `?` to match on one character in a path segment + * * `**` to match any number of path segments, including none + * * `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * * `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * * `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + * + * Note: a backslash (`\`) is not valid within a glob pattern. If you have an existing file + * path to match against, consider to use the {@link RelativePattern relative pattern} support + * that takes care of converting any backslash into slash. Otherwise, make sure to convert + * any backslash to slash when creating the glob pattern. + */ + export type GlobPattern = string | RelativePattern; + + /** + * A document filter denotes a document by different properties like + * the {@link TextDocument.languageId language}, the {@link Uri.scheme scheme} of + * its resource, or a glob-pattern that is applied to the {@link TextDocument.fileName path}. + * + * @example A language filter that applies to typescript files on disk + * { language: 'typescript', scheme: 'file' } + * + * @example A language filter that applies to all package.json paths + * { language: 'json', pattern: '**​/package.json' } + */ + export interface DocumentFilter { + + /** + * A language id, like `typescript`. + */ + readonly language?: string; + + /** + * The {@link NotebookDocument.notebookType type} of a notebook, like `jupyter-notebook`. This allows + * to narrow down on the type of a notebook that a {@link NotebookCell.document cell document} belongs to. + * + * *Note* that setting the `notebookType`-property changes how `scheme` and `pattern` are interpreted. When set + * they are evaluated against the {@link NotebookDocument.uri notebook uri}, not the document uri. + * + * @example Match python document inside jupyter notebook that aren't stored yet (`untitled`) + * { language: 'python', notebookType: 'jupyter-notebook', scheme: 'untitled' } + */ + readonly notebookType?: string; + + /** + * A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. + */ + readonly scheme?: string; + + /** + * A {@link GlobPattern glob pattern} that is matched on the absolute path of the document. Use a {@link RelativePattern relative pattern} + * to filter documents to a {@link WorkspaceFolder workspace folder}. + */ + readonly pattern?: GlobPattern; + } + + /** + * A language selector is the combination of one or many language identifiers + * and {@link DocumentFilter language filters}. + * + * *Note* that a document selector that is just a language identifier selects *all* + * documents, even those that are not saved on disk. Only use such selectors when + * a feature works without further context, e.g. without the need to resolve related + * 'files'. + * + * @example + * let sel:DocumentSelector = { scheme: 'file', language: 'typescript' }; + */ + export type DocumentSelector = DocumentFilter | string | ReadonlyArray; + + /** + * A provider result represents the values a provider, like the {@linkcode HoverProvider}, + * may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves + * to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a + * thenable. + * + * The snippets below are all valid implementations of the {@linkcode HoverProvider}: + * + * ```ts + * let a: HoverProvider = { + * provideHover(doc, pos, token): ProviderResult { + * return new Hover('Hello World'); + * } + * } + * + * let b: HoverProvider = { + * provideHover(doc, pos, token): ProviderResult { + * return new Promise(resolve => { + * resolve(new Hover('Hello World')); + * }); + * } + * } + * + * let c: HoverProvider = { + * provideHover(doc, pos, token): ProviderResult { + * return; // undefined + * } + * } + * ``` + */ + export type ProviderResult = T | undefined | null | Thenable; + + /** + * Kind of a code action. + * + * Kinds are a hierarchical list of identifiers separated by `.`, e.g. `"refactor.extract.function"`. + * + * Code action kinds are used by the editor for UI elements such as the refactoring context menu. Users + * can also trigger code actions with a specific kind with the `editor.action.codeAction` command. + */ + export class CodeActionKind { + /** + * Empty kind. + */ + static readonly Empty: CodeActionKind; + + /** + * Base kind for quickfix actions: `quickfix`. + * + * Quick fix actions address a problem in the code and are shown in the normal code action context menu. + */ + static readonly QuickFix: CodeActionKind; + + /** + * Base kind for refactoring actions: `refactor` + * + * Refactoring actions are shown in the refactoring context menu. + */ + static readonly Refactor: CodeActionKind; + + /** + * Base kind for refactoring extraction actions: `refactor.extract` + * + * Example extract actions: + * + * - Extract method + * - Extract function + * - Extract variable + * - Extract interface from class + * - ... + */ + static readonly RefactorExtract: CodeActionKind; + + /** + * Base kind for refactoring inline actions: `refactor.inline` + * + * Example inline actions: + * + * - Inline function + * - Inline variable + * - Inline constant + * - ... + */ + static readonly RefactorInline: CodeActionKind; + + /** + * Base kind for refactoring move actions: `refactor.move` + * + * Example move actions: + * + * - Move a function to a new file + * - Move a property between classes + * - Move method to base class + * - ... + */ + static readonly RefactorMove: CodeActionKind; + + /** + * Base kind for refactoring rewrite actions: `refactor.rewrite` + * + * Example rewrite actions: + * + * - Convert JavaScript function to class + * - Add or remove parameter + * - Encapsulate field + * - Make method static + * - ... + */ + static readonly RefactorRewrite: CodeActionKind; + + /** + * Base kind for source actions: `source` + * + * Source code actions apply to the entire file. They must be explicitly requested and will not show in the + * normal [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) menu. Source actions + * can be run on save using `editor.codeActionsOnSave` and are also shown in the `source` context menu. + */ + static readonly Source: CodeActionKind; + + /** + * Base kind for an organize imports source action: `source.organizeImports`. + */ + static readonly SourceOrganizeImports: CodeActionKind; + + /** + * Base kind for auto-fix source actions: `source.fixAll`. + * + * Fix all actions automatically fix errors that have a clear fix that do not require user input. + * They should not suppress errors or perform unsafe fixes such as generating new types or classes. + */ + static readonly SourceFixAll: CodeActionKind; + + /** + * Base kind for all code actions applying to the entire notebook's scope. CodeActionKinds using + * this should always begin with `notebook.` + * + * This requires that new CodeActions be created for it and contributed via extensions. + * Pre-existing kinds can not just have the new `notebook.` prefix added to them, as the functionality + * is unique to the full-notebook scope. + * + * Notebook CodeActionKinds can be initialized as either of the following (both resulting in `notebook.source.xyz`): + * - `const newKind = CodeActionKind.Notebook.append(CodeActionKind.Source.append('xyz').value)` + * - `const newKind = CodeActionKind.Notebook.append('source.xyz')` + * + * Example Kinds/Actions: + * - `notebook.source.organizeImports` (might move all imports to a new top cell) + * - `notebook.source.normalizeVariableNames` (might rename all variables to a standardized casing format) + */ + static readonly Notebook: CodeActionKind; + + /** + * Private constructor, use static `CodeActionKind.XYZ` to derive from an existing code action kind. + * + * @param value The value of the kind, such as `refactor.extract.function`. + */ + private constructor(value: string); + + /** + * String value of the kind, e.g. `"refactor.extract.function"`. + */ + readonly value: string; + + /** + * Create a new kind by appending a more specific selector to the current kind. + * + * Does not modify the current kind. + */ + append(parts: string): CodeActionKind; + + /** + * Checks if this code action kind intersects `other`. + * + * The kind `"refactor.extract"` for example intersects `refactor`, `"refactor.extract"` and `"refactor.extract.function"`, + * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"`. + * + * @param other Kind to check. + */ + intersects(other: CodeActionKind): boolean; + + /** + * Checks if `other` is a sub-kind of this `CodeActionKind`. + * + * The kind `"refactor.extract"` for example contains `"refactor.extract"` and ``"refactor.extract.function"`, + * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"` or `refactor`. + * + * @param other Kind to check. + */ + contains(other: CodeActionKind): boolean; + } + + /** + * The reason why code actions were requested. + */ + export enum CodeActionTriggerKind { + /** + * Code actions were explicitly requested by the user or by an extension. + */ + Invoke = 1, + + /** + * Code actions were requested automatically. + * + * This typically happens when current selection in a file changes, but can + * also be triggered when file content changes. + */ + Automatic = 2, + } + + /** + * Contains additional diagnostic information about the context in which + * a {@link CodeActionProvider.provideCodeActions code action} is run. + */ + export interface CodeActionContext { + /** + * The reason why code actions were requested. + */ + readonly triggerKind: CodeActionTriggerKind; + + /** + * An array of diagnostics. + */ + readonly diagnostics: readonly Diagnostic[]; + + /** + * Requested kind of actions to return. + * + * Actions not of this kind are filtered out before being shown by the [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action). + */ + readonly only: CodeActionKind | undefined; + } + + /** + * A code action represents a change that can be performed in code, e.g. to fix a problem or + * to refactor code. + * + * A CodeAction must set either {@linkcode CodeAction.edit edit} and/or a {@linkcode CodeAction.command command}. If both are supplied, the `edit` is applied first, then the command is executed. + */ + export class CodeAction { + + /** + * A short, human-readable, title for this code action. + */ + title: string; + + /** + * A {@link WorkspaceEdit workspace edit} this code action performs. + */ + edit?: WorkspaceEdit; + + /** + * {@link Diagnostic Diagnostics} that this code action resolves. + */ + diagnostics?: Diagnostic[]; + + /** + * A {@link Command} this code action executes. + * + * If this command throws an exception, the editor displays the exception message to users in the editor at the + * current cursor position. + */ + command?: Command; + + /** + * {@link CodeActionKind Kind} of the code action. + * + * Used to filter code actions. + */ + kind?: CodeActionKind; + + /** + * Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted + * by keybindings. + * + * A quick fix should be marked preferred if it properly addresses the underlying error. + * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. + */ + isPreferred?: boolean; + + /** + * Marks that the code action cannot currently be applied. + * + * - Disabled code actions are not shown in automatic [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) + * code action menu. + * + * - Disabled actions are shown as faded out in the code action menu when the user request a more specific type + * of code action, such as refactorings. + * + * - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions) + * that auto applies a code action and only a disabled code actions are returned, the editor will show the user an + * error message with `reason` in the editor. + */ + disabled?: { + /** + * Human readable description of why the code action is currently disabled. + * + * This is displayed in the code actions UI. + */ + readonly reason: string; + }; + + /** + * Creates a new code action. + * + * A code action must have at least a {@link CodeAction.title title} and {@link CodeAction.edit edits} + * and/or a {@link CodeAction.command command}. + * + * @param title The title of the code action. + * @param kind The kind of the code action. + */ + constructor(title: string, kind?: CodeActionKind); + } + + /** + * Provides contextual actions for code. Code actions typically either fix problems or beautify/refactor code. + * + * Code actions are surfaced to users in a few different ways: + * + * - The [lightbulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature, which shows + * a list of code actions at the current cursor position. The lightbulb's list of actions includes both quick fixes + * and refactorings. + * - As commands that users can run, such as `Refactor`. Users can run these from the command palette or with keybindings. + * - As source actions, such `Organize Imports`. + * - {@link CodeActionKind.QuickFix Quick fixes} are shown in the problems view. + * - Change applied on save by the `editor.codeActionsOnSave` setting. + */ + export interface CodeActionProvider { + /** + * Get code actions for a given range in a document. + * + * Only return code actions that are relevant to user for the requested range. Also keep in mind how the + * returned code actions will appear in the UI. The lightbulb widget and `Refactor` commands for instance show + * returned code actions as a list, so do not return a large number of code actions that will overwhelm the user. + * + * @param document The document in which the command was invoked. + * @param range The selector or range for which the command was invoked. This will always be a + * {@link Selection selection} if the actions are being requested in the currently active editor. + * @param context Provides additional information about what code actions are being requested. You can use this + * to see what specific type of code actions are being requested by the editor in order to return more relevant + * actions and avoid returning irrelevant code actions that the editor will discard. + * @param token A cancellation token. + * + * @returns An array of code actions, such as quick fixes or refactorings. The lack of a result can be signaled + * by returning `undefined`, `null`, or an empty array. + * + * We also support returning `Command` for legacy reasons, however all new extensions should return + * `CodeAction` object instead. + */ + provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult>; + + /** + * Given a code action fill in its {@linkcode CodeAction.edit edit}-property. Changes to + * all other properties, like title, are ignored. A code action that has an edit + * will not be resolved. + * + * *Note* that a code action provider that returns commands, not code actions, cannot successfully + * implement this function. Returning commands is deprecated and instead code actions should be + * returned. + * + * @param codeAction A code action. + * @param token A cancellation token. + * @returns The resolved code action or a thenable that resolves to such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveCodeAction?(codeAction: T, token: CancellationToken): ProviderResult; + } + + /** + * Metadata about the type of code actions that a {@link CodeActionProvider} provides. + */ + export interface CodeActionProviderMetadata { + /** + * List of {@link CodeActionKind CodeActionKinds} that a {@link CodeActionProvider} may return. + * + * This list is used to determine if a given `CodeActionProvider` should be invoked or not. + * To avoid unnecessary computation, every `CodeActionProvider` should list use `providedCodeActionKinds`. The + * list of kinds may either be generic, such as `[CodeActionKind.Refactor]`, or list out every kind provided, + * such as `[CodeActionKind.Refactor.Extract.append('function'), CodeActionKind.Refactor.Extract.append('constant'), ...]`. + */ + readonly providedCodeActionKinds?: readonly CodeActionKind[]; + + /** + * Static documentation for a class of code actions. + * + * Documentation from the provider is shown in the code actions menu if either: + * + * - Code actions of `kind` are requested by the editor. In this case, the editor will show the documentation that + * most closely matches the requested code action kind. For example, if a provider has documentation for + * both `Refactor` and `RefactorExtract`, when the user requests code actions for `RefactorExtract`, + * the editor will use the documentation for `RefactorExtract` instead of the documentation for `Refactor`. + * + * - Any code actions of `kind` are returned by the provider. + * + * At most one documentation entry will be shown per provider. + */ + readonly documentation?: ReadonlyArray<{ + /** + * The kind of the code action being documented. + * + * If the kind is generic, such as `CodeActionKind.Refactor`, the documentation will be shown whenever any + * refactorings are returned. If the kind if more specific, such as `CodeActionKind.RefactorExtract`, the + * documentation will only be shown when extract refactoring code actions are returned. + */ + readonly kind: CodeActionKind; + + /** + * Command that displays the documentation to the user. + * + * This can display the documentation directly in the editor or open a website using {@linkcode env.openExternal}; + * + * The title of this documentation code action is taken from {@linkcode Command.title} + */ + readonly command: Command; + }>; + } + + /** + * A code lens represents a {@link Command} that should be shown along with + * source text, like the number of references, a way to run tests, etc. + * + * A code lens is _unresolved_ when no command is associated to it. For performance + * reasons the creation of a code lens and resolving should be done to two stages. + * + * @see {@link CodeLensProvider.provideCodeLenses} + * @see {@link CodeLensProvider.resolveCodeLens} + */ + export class CodeLens { + + /** + * The range in which this code lens is valid. Should only span a single line. + */ + range: Range; + + /** + * The command this code lens represents. + */ + command?: Command; + + /** + * `true` when there is a command associated. + */ + readonly isResolved: boolean; + + /** + * Creates a new code lens object. + * + * @param range The range to which this code lens applies. + * @param command The command associated to this code lens. + */ + constructor(range: Range, command?: Command); + } + + /** + * A code lens provider adds {@link Command commands} to source text. The commands will be shown + * as dedicated horizontal lines in between the source text. + */ + export interface CodeLensProvider { + + /** + * An optional event to signal that the code lenses from this provider have changed. + */ + onDidChangeCodeLenses?: Event; + + /** + * Compute a list of {@link CodeLens lenses}. This call should return as fast as possible and if + * computing the commands is expensive implementors should only return code lens objects with the + * range set and implement {@link CodeLensProvider.resolveCodeLens resolve}. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @returns An array of code lenses or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * This function will be called for each visible code lens, usually when scrolling and after + * calls to {@link CodeLensProvider.provideCodeLenses compute}-lenses. + * + * @param codeLens Code lens that must be resolved. + * @param token A cancellation token. + * @returns The given, resolved code lens or thenable that resolves to such. + */ + resolveCodeLens?(codeLens: T, token: CancellationToken): ProviderResult; + } + + /** + * Information about where a symbol is defined. + * + * Provides additional metadata over normal {@link Location} definitions, including the range of + * the defining symbol + */ + export type DefinitionLink = LocationLink; + + /** + * The definition of a symbol represented as one or many {@link Location locations}. + * For most programming languages there is only one location at which a symbol is + * defined. + */ + export type Definition = Location | Location[]; + + /** + * The definition provider interface defines the contract between extensions and + * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) + * and peek definition features. + */ + export interface DefinitionProvider { + + /** + * Provide the definition of the symbol at the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns A definition or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * The implementation provider interface defines the contract between extensions and + * the go to implementation feature. + */ + export interface ImplementationProvider { + + /** + * Provide the implementations of the symbol at the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns A definition or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * The type definition provider defines the contract between extensions and + * the go to type definition feature. + */ + export interface TypeDefinitionProvider { + + /** + * Provide the type definition of the symbol at the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns A definition or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * The declaration of a symbol representation as one or many {@link Location locations} + * or {@link LocationLink location links}. + */ + export type Declaration = Location | Location[] | LocationLink[]; + + /** + * The declaration provider interface defines the contract between extensions and + * the go to declaration feature. + */ + export interface DeclarationProvider { + + /** + * Provide the declaration of the symbol at the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns A declaration or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideDeclaration(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * Human-readable text that supports formatting via the [markdown syntax](https://commonmark.org). + * + * Rendering of {@link ThemeIcon theme icons} via the `$()`-syntax is supported + * when the {@linkcode supportThemeIcons} is set to `true`. + * + * Rendering of embedded html is supported when {@linkcode supportHtml} is set to `true`. + */ + export class MarkdownString { + + /** + * The markdown string. + */ + value: string; + + /** + * Indicates that this markdown string is from a trusted source. Only *trusted* + * markdown supports links that execute commands, e.g. `[Run it](command:myCommandId)`. + * + * Defaults to `false` (commands are disabled). + */ + isTrusted?: boolean | { + /** + * A set of commend ids that are allowed to be executed by this markdown string. + */ + readonly enabledCommands: readonly string[]; + }; + + /** + * Indicates that this markdown string can contain {@link ThemeIcon ThemeIcons}, e.g. `$(zap)`. + */ + supportThemeIcons?: boolean; + + /** + * Indicates that this markdown string can contain raw html tags. Defaults to `false`. + * + * When `supportHtml` is false, the markdown renderer will strip out any raw html tags + * that appear in the markdown text. This means you can only use markdown syntax for rendering. + * + * When `supportHtml` is true, the markdown render will also allow a safe subset of html tags + * and attributes to be rendered. See https://github.com/microsoft/vscode/blob/6d2920473c6f13759c978dd89104c4270a83422d/src/vs/base/browser/markdownRenderer.ts#L296 + * for a list of all supported tags and attributes. + */ + supportHtml?: boolean; + + /** + * Uri that relative paths are resolved relative to. + * + * If the `baseUri` ends with `/`, it is considered a directory and relative paths in the markdown are resolved relative to that directory: + * + * ```ts + * const md = new vscode.MarkdownString(`[link](./file.js)`); + * md.baseUri = vscode.Uri.file('/path/to/dir/'); + * // Here 'link' in the rendered markdown resolves to '/path/to/dir/file.js' + * ``` + * + * If the `baseUri` is a file, relative paths in the markdown are resolved relative to the parent dir of that file: + * + * ```ts + * const md = new vscode.MarkdownString(`[link](./file.js)`); + * md.baseUri = vscode.Uri.file('/path/to/otherFile.js'); + * // Here 'link' in the rendered markdown resolves to '/path/to/file.js' + * ``` + */ + baseUri?: Uri; + + /** + * Creates a new markdown string with the given value. + * + * @param value Optional, initial value. + * @param supportThemeIcons Optional, Specifies whether {@link ThemeIcon ThemeIcons} are supported within the {@linkcode MarkdownString}. + */ + constructor(value?: string, supportThemeIcons?: boolean); + + /** + * Appends and escapes the given string to this markdown string. + * @param value Plain text. + */ + appendText(value: string): MarkdownString; + + /** + * Appends the given string 'as is' to this markdown string. When {@linkcode MarkdownString.supportThemeIcons supportThemeIcons} is `true`, {@link ThemeIcon ThemeIcons} in the `value` will be iconified. + * @param value Markdown string. + */ + appendMarkdown(value: string): MarkdownString; + + /** + * Appends the given string as codeblock using the provided language. + * @param value A code snippet. + * @param language An optional {@link languages.getLanguages language identifier}. + */ + appendCodeblock(value: string, language?: string): MarkdownString; + } + + /** + * MarkedString can be used to render human-readable text. It is either a markdown string + * or a code-block that provides a language and a code snippet. Note that + * markdown strings will be sanitized - that means html will be escaped. + * + * @deprecated This type is deprecated, please use {@linkcode MarkdownString} instead. + */ + export type MarkedString = string | { + /** + * The language of a markdown code block + * @deprecated please use {@linkcode MarkdownString} instead + */ + language: string; + /** + * The code snippet of a markdown code block. + * @deprecated please use {@linkcode MarkdownString} instead + */ + value: string; + }; + + /** + * A hover represents additional information for a symbol or word. Hovers are + * rendered in a tooltip-like widget. + */ + export class Hover { + + /** + * The contents of this hover. + */ + contents: Array; + + /** + * The range to which this hover applies. When missing, the + * editor will use the range at the current position or the + * current position itself. + */ + range?: Range; + + /** + * Creates a new hover object. + * + * @param contents The contents of the hover. + * @param range The range to which the hover applies. + */ + constructor(contents: MarkdownString | MarkedString | Array, range?: Range); + } + + /** + * The hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ + export interface HoverProvider { + + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns A hover or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * An EvaluatableExpression represents an expression in a document that can be evaluated by an active debugger or runtime. + * The result of this evaluation is shown in a tooltip-like widget. + * If only a range is specified, the expression will be extracted from the underlying document. + * An optional expression can be used to override the extracted expression. + * In this case the range is still used to highlight the range in the document. + */ + export class EvaluatableExpression { + + /* + * The range is used to extract the evaluatable expression from the underlying document and to highlight it. + */ + readonly range: Range; + + /* + * If specified the expression overrides the extracted expression. + */ + readonly expression?: string | undefined; + + /** + * Creates a new evaluatable expression object. + * + * @param range The range in the underlying document from which the evaluatable expression is extracted. + * @param expression If specified overrides the extracted expression. + */ + constructor(range: Range, expression?: string); + } + + /** + * The evaluatable expression provider interface defines the contract between extensions and + * the debug hover. In this contract the provider returns an evaluatable expression for a given position + * in a document and the editor evaluates this expression in the active debug session and shows the result in a debug hover. + */ + export interface EvaluatableExpressionProvider { + + /** + * Provide an evaluatable expression for the given document and position. + * The editor will evaluate this expression in the active debug session and will show the result in the debug hover. + * The expression can be implicitly specified by the range in the underlying document or by explicitly returning an expression. + * + * @param document The document for which the debug hover is about to appear. + * @param position The line and character position in the document where the debug hover is about to appear. + * @param token A cancellation token. + * @returns An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideEvaluatableExpression(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * Provide inline value as text. + */ + export class InlineValueText { + /** + * The document range for which the inline value applies. + */ + readonly range: Range; + /** + * The text of the inline value. + */ + readonly text: string; + /** + * Creates a new InlineValueText object. + * + * @param range The document line where to show the inline value. + * @param text The value to be shown for the line. + */ + constructor(range: Range, text: string); + } + + /** + * Provide inline value through a variable lookup. + * If only a range is specified, the variable name will be extracted from the underlying document. + * An optional variable name can be used to override the extracted name. + */ + export class InlineValueVariableLookup { + /** + * The document range for which the inline value applies. + * The range is used to extract the variable name from the underlying document. + */ + readonly range: Range; + /** + * If specified the name of the variable to look up. + */ + readonly variableName?: string | undefined; + /** + * How to perform the lookup. + */ + readonly caseSensitiveLookup: boolean; + /** + * Creates a new InlineValueVariableLookup object. + * + * @param range The document line where to show the inline value. + * @param variableName The name of the variable to look up. + * @param caseSensitiveLookup How to perform the lookup. If missing lookup is case sensitive. + */ + constructor(range: Range, variableName?: string, caseSensitiveLookup?: boolean); + } + + /** + * Provide an inline value through an expression evaluation. + * If only a range is specified, the expression will be extracted from the underlying document. + * An optional expression can be used to override the extracted expression. + */ + export class InlineValueEvaluatableExpression { + /** + * The document range for which the inline value applies. + * The range is used to extract the evaluatable expression from the underlying document. + */ + readonly range: Range; + /** + * If specified the expression overrides the extracted expression. + */ + readonly expression?: string | undefined; + /** + * Creates a new InlineValueEvaluatableExpression object. + * + * @param range The range in the underlying document from which the evaluatable expression is extracted. + * @param expression If specified overrides the extracted expression. + */ + constructor(range: Range, expression?: string); + } + + /** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + */ + export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression; + + /** + * A value-object that contains contextual information when requesting inline values from a InlineValuesProvider. + */ + export interface InlineValueContext { + + /** + * The stack frame (as a DAP Id) where the execution has stopped. + */ + readonly frameId: number; + + /** + * The document range where execution has stopped. + * Typically the end position of the range denotes the line where the inline values are shown. + */ + readonly stoppedLocation: Range; + } + + /** + * The inline values provider interface defines the contract between extensions and the editor's debugger inline values feature. + * In this contract the provider returns inline value information for a given document range + * and the editor shows this information in the editor at the end of lines. + */ + export interface InlineValuesProvider { + + /** + * An optional event to signal that inline values have changed. + * @see {@link EventEmitter} + */ + onDidChangeInlineValues?: Event | undefined; + + /** + * Provide "inline value" information for a given document and range. + * The editor calls this method whenever debugging stops in the given document. + * The returned inline values information is rendered in the editor at the end of lines. + * + * @param document The document for which the inline values information is needed. + * @param viewPort The visible document range for which inline values should be computed. + * @param context A bag containing contextual information like the current location. + * @param token A cancellation token. + * @returns An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; + } + + /** + * A document highlight kind. + */ + export enum DocumentHighlightKind { + + /** + * A textual occurrence. + */ + Text = 0, + + /** + * Read-access of a symbol, like reading a variable. + */ + Read = 1, + + /** + * Write-access of a symbol, like writing to a variable. + */ + Write = 2 + } + + /** + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ + export class DocumentHighlight { + + /** + * The range this highlight applies to. + */ + range: Range; + + /** + * The highlight kind, default is {@link DocumentHighlightKind.Text text}. + */ + kind?: DocumentHighlightKind; + + /** + * Creates a new document highlight object. + * + * @param range The range the highlight applies to. + * @param kind The highlight kind, default is {@link DocumentHighlightKind.Text text}. + */ + constructor(range: Range, kind?: DocumentHighlightKind); + } + + /** + * The document highlight provider interface defines the contract between extensions and + * the word-highlight-feature. + */ + export interface DocumentHighlightProvider { + + /** + * Provide a set of document highlights, like all occurrences of a variable or + * all exit-points of a function. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns An array of document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentHighlights(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * A symbol kind. + */ + export enum SymbolKind { + /** + * The `File` symbol kind. + */ + File = 0, + /** + * The `Module` symbol kind. + */ + Module = 1, + /** + * The `Namespace` symbol kind. + */ + Namespace = 2, + /** + * The `Package` symbol kind. + */ + Package = 3, + /** + * The `Class` symbol kind. + */ + Class = 4, + /** + * The `Method` symbol kind. + */ + Method = 5, + /** + * The `Property` symbol kind. + */ + Property = 6, + /** + * The `Field` symbol kind. + */ + Field = 7, + /** + * The `Constructor` symbol kind. + */ + Constructor = 8, + /** + * The `Enum` symbol kind. + */ + Enum = 9, + /** + * The `Interface` symbol kind. + */ + Interface = 10, + /** + * The `Function` symbol kind. + */ + Function = 11, + /** + * The `Variable` symbol kind. + */ + Variable = 12, + /** + * The `Constant` symbol kind. + */ + Constant = 13, + /** + * The `String` symbol kind. + */ + String = 14, + /** + * The `Number` symbol kind. + */ + Number = 15, + /** + * The `Boolean` symbol kind. + */ + Boolean = 16, + /** + * The `Array` symbol kind. + */ + Array = 17, + /** + * The `Object` symbol kind. + */ + Object = 18, + /** + * The `Key` symbol kind. + */ + Key = 19, + /** + * The `Null` symbol kind. + */ + Null = 20, + /** + * The `EnumMember` symbol kind. + */ + EnumMember = 21, + /** + * The `Struct` symbol kind. + */ + Struct = 22, + /** + * The `Event` symbol kind. + */ + Event = 23, + /** + * The `Operator` symbol kind. + */ + Operator = 24, + /** + * The `TypeParameter` symbol kind. + */ + TypeParameter = 25 + } + + /** + * Symbol tags are extra annotations that tweak the rendering of a symbol. + */ + export enum SymbolTag { + + /** + * Render a symbol as obsolete, usually using a strike-out. + */ + Deprecated = 1 + } + + /** + * Represents information about programming constructs like variables, classes, + * interfaces etc. + */ + export class SymbolInformation { + + /** + * The name of this symbol. + */ + name: string; + + /** + * The name of the symbol containing this symbol. + */ + containerName: string; + + /** + * The kind of this symbol. + */ + kind: SymbolKind; + + /** + * Tags for this symbol. + */ + tags?: readonly SymbolTag[]; + + /** + * The location of this symbol. + */ + location: Location; + + /** + * Creates a new symbol information object. + * + * @param name The name of the symbol. + * @param kind The kind of the symbol. + * @param containerName The name of the symbol containing the symbol. + * @param location The location of the symbol. + */ + constructor(name: string, kind: SymbolKind, containerName: string, location: Location); + + /** + * Creates a new symbol information object. + * + * @deprecated Please use the constructor taking a {@link Location} object. + * + * @param name The name of the symbol. + * @param kind The kind of the symbol. + * @param range The range of the location of the symbol. + * @param uri The resource of the location of symbol, defaults to the current document. + * @param containerName The name of the symbol containing the symbol. + */ + constructor(name: string, kind: SymbolKind, range: Range, uri?: Uri, containerName?: string); + } + + /** + * Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document + * symbols can be hierarchical and they have two ranges: one that encloses its definition and one that points to + * its most interesting range, e.g. the range of an identifier. + */ + export class DocumentSymbol { + + /** + * The name of this symbol. + */ + name: string; + + /** + * More detail for this symbol, e.g. the signature of a function. + */ + detail: string; + + /** + * The kind of this symbol. + */ + kind: SymbolKind; + + /** + * Tags for this symbol. + */ + tags?: readonly SymbolTag[]; + + /** + * The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. + */ + range: Range; + + /** + * The range that should be selected and reveal when this symbol is being picked, e.g. the name of a function. + * Must be contained by the {@linkcode DocumentSymbol.range range}. + */ + selectionRange: Range; + + /** + * Children of this symbol, e.g. properties of a class. + */ + children: DocumentSymbol[]; + + /** + * Creates a new document symbol. + * + * @param name The name of the symbol. + * @param detail Details for the symbol. + * @param kind The kind of the symbol. + * @param range The full range of the symbol. + * @param selectionRange The range that should be reveal. + */ + constructor(name: string, detail: string, kind: SymbolKind, range: Range, selectionRange: Range); + } + + /** + * The document symbol provider interface defines the contract between extensions and + * the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature. + */ + export interface DocumentSymbolProvider { + + /** + * Provide symbol information for the given document. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @returns An array of document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult; + } + + /** + * Metadata about a document symbol provider. + */ + export interface DocumentSymbolProviderMetadata { + /** + * A human-readable string that is shown when multiple outlines trees show for one document. + */ + label?: string; + } + + /** + * The workspace symbol provider interface defines the contract between extensions and + * the [symbol search](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name)-feature. + */ + export interface WorkspaceSymbolProvider { + + /** + * Project-wide search for a symbol matching the given query string. + * + * The `query`-parameter should be interpreted in a *relaxed way* as the editor will apply its own highlighting + * and scoring on the results. A good rule of thumb is to match case-insensitive and to simply check that the + * characters of *query* appear in their order in a candidate symbol. Don't use prefix, substring, or similar + * strict matching. + * + * To improve performance implementors can implement `resolveWorkspaceSymbol` and then provide symbols with partial + * {@link SymbolInformation.location location}-objects, without a `range` defined. The editor will then call + * `resolveWorkspaceSymbol` for selected symbols only, e.g. when opening a workspace symbol. + * + * @param query A query string, can be the empty string in which case all symbols should be returned. + * @param token A cancellation token. + * @returns An array of document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideWorkspaceSymbols(query: string, token: CancellationToken): ProviderResult; + + /** + * Given a symbol fill in its {@link SymbolInformation.location location}. This method is called whenever a symbol + * is selected in the UI. Providers can implement this method and return incomplete symbols from + * {@linkcode WorkspaceSymbolProvider.provideWorkspaceSymbols provideWorkspaceSymbols} which often helps to improve + * performance. + * + * @param symbol The symbol that is to be resolved. Guaranteed to be an instance of an object returned from an + * earlier call to `provideWorkspaceSymbols`. + * @param token A cancellation token. + * @returns The resolved symbol or a thenable that resolves to that. When no result is returned, + * the given `symbol` is used. + */ + resolveWorkspaceSymbol?(symbol: T, token: CancellationToken): ProviderResult; + } + + /** + * Value-object that contains additional information when + * requesting references. + */ + export interface ReferenceContext { + + /** + * Include the declaration of the current symbol. + */ + readonly includeDeclaration: boolean; + } + + /** + * The reference provider interface defines the contract between extensions and + * the [find references](https://code.visualstudio.com/docs/editor/editingevolved#_peek)-feature. + */ + export interface ReferenceProvider { + + /** + * Provide a set of project-wide references for the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param context Additional information about the references request. + * @param token A cancellation token. + * + * @returns An array of locations or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult; + } + + /** + * A text edit represents edits that should be applied + * to a document. + */ + export class TextEdit { + + /** + * Utility to create a replace edit. + * + * @param range A range. + * @param newText A string. + * @returns A new text edit object. + */ + static replace(range: Range, newText: string): TextEdit; + + /** + * Utility to create an insert edit. + * + * @param position A position, will become an empty range. + * @param newText A string. + * @returns A new text edit object. + */ + static insert(position: Position, newText: string): TextEdit; + + /** + * Utility to create a delete edit. + * + * @param range A range. + * @returns A new text edit object. + */ + static delete(range: Range): TextEdit; + + /** + * Utility to create an eol-edit. + * + * @param eol An eol-sequence + * @returns A new text edit object. + */ + static setEndOfLine(eol: EndOfLine): TextEdit; + + /** + * The range this edit applies to. + */ + range: Range; + + /** + * The string this edit will insert. + */ + newText: string; + + /** + * The eol-sequence used in the document. + * + * *Note* that the eol-sequence will be applied to the + * whole document. + */ + newEol?: EndOfLine; + + /** + * Create a new TextEdit. + * + * @param range A range. + * @param newText A string. + */ + constructor(range: Range, newText: string); + } + + /** + * A snippet edit represents an interactive edit that is performed by + * the editor. + * + * *Note* that a snippet edit can always be performed as a normal {@link TextEdit text edit}. + * This will happen when no matching editor is open or when a {@link WorkspaceEdit workspace edit} + * contains snippet edits for multiple files. In that case only those that match the active editor + * will be performed as snippet edits and the others as normal text edits. + */ + export class SnippetTextEdit { + + /** + * Utility to create a replace snippet edit. + * + * @param range A range. + * @param snippet A snippet string. + * @returns A new snippet edit object. + */ + static replace(range: Range, snippet: SnippetString): SnippetTextEdit; + + /** + * Utility to create an insert snippet edit. + * + * @param position A position, will become an empty range. + * @param snippet A snippet string. + * @returns A new snippet edit object. + */ + static insert(position: Position, snippet: SnippetString): SnippetTextEdit; + + /** + * The range this edit applies to. + */ + range: Range; + + /** + * The {@link SnippetString snippet} this edit will perform. + */ + snippet: SnippetString; + + /** + * Create a new snippet edit. + * + * @param range A range. + * @param snippet A snippet string. + */ + constructor(range: Range, snippet: SnippetString); + } + + /** + * A notebook edit represents edits that should be applied to the contents of a notebook. + */ + export class NotebookEdit { + + /** + * Utility to create a edit that replaces cells in a notebook. + * + * @param range The range of cells to replace + * @param newCells The new notebook cells. + */ + static replaceCells(range: NotebookRange, newCells: NotebookCellData[]): NotebookEdit; + + /** + * Utility to create an edit that replaces cells in a notebook. + * + * @param index The index to insert cells at. + * @param newCells The new notebook cells. + */ + static insertCells(index: number, newCells: NotebookCellData[]): NotebookEdit; + + /** + * Utility to create an edit that deletes cells in a notebook. + * + * @param range The range of cells to delete. + */ + static deleteCells(range: NotebookRange): NotebookEdit; + + /** + * Utility to create an edit that update a cell's metadata. + * + * @param index The index of the cell to update. + * @param newCellMetadata The new metadata for the cell. + */ + static updateCellMetadata(index: number, newCellMetadata: { [key: string]: any }): NotebookEdit; + + /** + * Utility to create an edit that updates the notebook's metadata. + * + * @param newNotebookMetadata The new metadata for the notebook. + */ + static updateNotebookMetadata(newNotebookMetadata: { [key: string]: any }): NotebookEdit; + + /** + * Range of the cells being edited. May be empty. + */ + range: NotebookRange; + + /** + * New cells being inserted. May be empty. + */ + newCells: NotebookCellData[]; + + /** + * Optional new metadata for the cells. + */ + newCellMetadata?: { [key: string]: any }; + + /** + * Optional new metadata for the notebook. + */ + newNotebookMetadata?: { [key: string]: any }; + + /** + * Create a new notebook edit. + * + * @param range A notebook range. + * @param newCells An array of new cell data. + */ + constructor(range: NotebookRange, newCells: NotebookCellData[]); + } + + /** + * Additional data for entries of a workspace edit. Supports to label entries and marks entries + * as needing confirmation by the user. The editor groups edits with equal labels into tree nodes, + * for instance all edits labelled with "Changes in Strings" would be a tree node. + */ + export interface WorkspaceEditEntryMetadata { + + /** + * A flag which indicates that user confirmation is needed. + */ + needsConfirmation: boolean; + + /** + * A human-readable string which is rendered prominent. + */ + label: string; + + /** + * A human-readable string which is rendered less prominent on the same line. + */ + description?: string; + + /** + * The icon path or {@link ThemeIcon} for the edit. + */ + iconPath?: IconPath; + } + + /** + * Additional data about a workspace edit. + */ + export interface WorkspaceEditMetadata { + /** + * Signal to the editor that this edit is a refactoring. + */ + isRefactoring?: boolean; + } + + /** + * A workspace edit is a collection of textual and files changes for + * multiple resources and documents. + * + * Use the {@link workspace.applyEdit applyEdit}-function to apply a workspace edit. + */ + export class WorkspaceEdit { + + /** + * The number of affected resources of textual or resource changes. + */ + readonly size: number; + + /** + * Replace the given range with given text for the given resource. + * + * @param uri A resource identifier. + * @param range A range. + * @param newText A string. + * @param metadata Optional metadata for the entry. + */ + replace(uri: Uri, range: Range, newText: string, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Insert the given text at the given position. + * + * @param uri A resource identifier. + * @param position A position. + * @param newText A string. + * @param metadata Optional metadata for the entry. + */ + insert(uri: Uri, position: Position, newText: string, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Delete the text at the given range. + * + * @param uri A resource identifier. + * @param range A range. + * @param metadata Optional metadata for the entry. + */ + delete(uri: Uri, range: Range, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Check if a text edit for a resource exists. + * + * @param uri A resource identifier. + * @returns `true` if the given resource will be touched by this edit. + */ + has(uri: Uri): boolean; + + /** + * Set (and replace) text edits or snippet edits for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: ReadonlyArray): void; + + /** + * Set (and replace) text edits or snippet edits with metadata for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata | undefined]>): void; + + /** + * Set (and replace) notebook edits for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: readonly NotebookEdit[]): void; + + /** + * Set (and replace) notebook edits with metadata for a resource. + * + * @param uri A resource identifier. + * @param edits An array of edits. + */ + set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata | undefined]>): void; + + /** + * Get the text edits for a resource. + * + * @param uri A resource identifier. + * @returns An array of text edits. + */ + get(uri: Uri): TextEdit[]; + + /** + * Create a regular file. + * + * @param uri Uri of the new file. + * @param options Defines if an existing file should be overwritten or be + * ignored. When `overwrite` and `ignoreIfExists` are both set `overwrite` wins. + * When both are unset and when the file already exists then the edit cannot + * be applied successfully. The `content`-property allows to set the initial contents + * the file is being created with. + * @param metadata Optional metadata for the entry. + */ + createFile(uri: Uri, options?: { + /** + * Overwrite existing file. Overwrite wins over `ignoreIfExists` + */ + readonly overwrite?: boolean; + /** + * Do nothing if a file with `uri` exists already. + */ + readonly ignoreIfExists?: boolean; + /** + * The initial contents of the new file. + * + * If creating a file from a {@link DocumentDropEditProvider drop operation}, you can + * pass in a {@link DataTransferFile} to improve performance by avoiding extra data copying. + */ + readonly contents?: Uint8Array | DataTransferFile; + }, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Delete a file or folder. + * + * @param uri The uri of the file that is to be deleted. + * @param metadata Optional metadata for the entry. + */ + deleteFile(uri: Uri, options?: { + /** + * Delete the content recursively if a folder is denoted. + */ + readonly recursive?: boolean; + /** + * Do nothing if a file with `uri` exists already. + */ + readonly ignoreIfNotExists?: boolean; + }, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Rename a file or folder. + * + * @param oldUri The existing file. + * @param newUri The new location. + * @param options Defines if existing files should be overwritten or be + * ignored. When overwrite and ignoreIfExists are both set overwrite wins. + * @param metadata Optional metadata for the entry. + */ + renameFile(oldUri: Uri, newUri: Uri, options?: { + /** + * Overwrite existing file. Overwrite wins over `ignoreIfExists` + */ + readonly overwrite?: boolean; + /** + * Do nothing if a file with `uri` exists already. + */ + readonly ignoreIfExists?: boolean; + }, metadata?: WorkspaceEditEntryMetadata): void; + + /** + * Get all text edits grouped by resource. + * + * @returns A shallow copy of `[Uri, TextEdit[]]`-tuples. + */ + entries(): [Uri, TextEdit[]][]; + } + + /** + * A snippet string is a template which allows to insert text + * and to control the editor cursor when insertion happens. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Variables are defined with `$name` and + * `${name:default value}`. Also see + * [the full snippet syntax](https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets). + */ + export class SnippetString { + + /** + * The snippet string. + */ + value: string; + + /** + * Create a new snippet string. + * + * @param value A snippet string. + */ + constructor(value?: string); + + /** + * Builder-function that appends the given string to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param string A value to append 'as given'. The string will be escaped. + * @returns This snippet string. + */ + appendText(string: string): SnippetString; + + /** + * Builder-function that appends a tabstop (`$1`, `$2` etc) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param number The number of this tabstop, defaults to an auto-increment + * value starting at 1. + * @returns This snippet string. + */ + appendTabstop(number?: number): SnippetString; + + /** + * Builder-function that appends a placeholder (`${1:value}`) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param value The value of this placeholder - either a string or a function + * with which a nested snippet can be created. + * @param number The number of this tabstop, defaults to an auto-increment + * value starting at 1. + * @returns This snippet string. + */ + appendPlaceholder(value: string | ((snippet: SnippetString) => any), number?: number): SnippetString; + + /** + * Builder-function that appends a choice (`${1|a,b,c|}`) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param values The values for choices - the array of strings + * @param number The number of this tabstop, defaults to an auto-increment + * value starting at 1. + * @returns This snippet string. + */ + appendChoice(values: readonly string[], number?: number): SnippetString; + + /** + * Builder-function that appends a variable (`${VAR}`) to + * the {@linkcode SnippetString.value value} of this snippet string. + * + * @param name The name of the variable - excluding the `$`. + * @param defaultValue The default value which is used when the variable name cannot + * be resolved - either a string or a function with which a nested snippet can be created. + * @returns This snippet string. + */ + appendVariable(name: string, defaultValue: string | ((snippet: SnippetString) => any)): SnippetString; + } + + /** + * The rename provider interface defines the contract between extensions and + * the [rename](https://code.visualstudio.com/docs/editor/editingevolved#_rename-symbol)-feature. + */ + export interface RenameProvider { + + /** + * Provide an edit that describes changes that have to be made to one + * or many resources to rename a symbol to a different name. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param newName The new name of the symbol. If the given name is not valid, the provider must return a rejected promise. + * @param token A cancellation token. + * @returns A workspace edit or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): ProviderResult; + + /** + * Optional function for resolving and validating a position *before* running rename. The result can + * be a range or a range and a placeholder text. The placeholder text should be the identifier of the symbol + * which is being renamed - when omitted the text in the returned range is used. + * + * *Note:* This function should throw an error or return a rejected thenable when the provided location + * doesn't allow for a rename. + * + * @param document The document in which rename will be invoked. + * @param position The position at which rename will be invoked. + * @param token A cancellation token. + * @returns The range or range and placeholder text of the identifier that is to be renamed. The lack of a result can signaled by returning `undefined` or `null`. + */ + prepareRename?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * A semantic tokens legend contains the needed information to decipher + * the integer encoded representation of semantic tokens. + */ + export class SemanticTokensLegend { + /** + * The possible token types. + */ + readonly tokenTypes: string[]; + /** + * The possible token modifiers. + */ + readonly tokenModifiers: string[]; + + /** + * Creates a semantic tokens legend. + * + * @param tokenTypes An array of token types. + * @param tokenModifiers An array of token modifiers. + */ + constructor(tokenTypes: string[], tokenModifiers?: string[]); + } + + /** + * A semantic tokens builder can help with creating a `SemanticTokens` instance + * which contains delta encoded semantic tokens. + */ + export class SemanticTokensBuilder { + + /** + * Creates a semantic tokens builder. + * + * @param legend A semantic tokens legend. + */ + constructor(legend?: SemanticTokensLegend); + + /** + * Add another token. + * + * @param line The token start line number (absolute value). + * @param char The token start character (absolute value). + * @param length The token length in characters. + * @param tokenType The encoded token type. + * @param tokenModifiers The encoded token modifiers. + */ + push(line: number, char: number, length: number, tokenType: number, tokenModifiers?: number): void; + + /** + * Add another token. Use only when providing a legend. + * + * @param range The range of the token. Must be single-line. + * @param tokenType The token type. + * @param tokenModifiers The token modifiers. + */ + push(range: Range, tokenType: string, tokenModifiers?: readonly string[]): void; + + /** + * Finish and create a `SemanticTokens` instance. + */ + build(resultId?: string): SemanticTokens; + } + + /** + * Represents semantic tokens, either in a range or in an entire document. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens} for an explanation of the format. + * @see {@link SemanticTokensBuilder} for a helper to create an instance. + */ + export class SemanticTokens { + /** + * The result id of the tokens. + * + * This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented). + */ + readonly resultId: string | undefined; + /** + * The actual tokens data. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens} for an explanation of the format. + */ + readonly data: Uint32Array; + + /** + * Create new semantic tokens. + * + * @param data Token data. + * @param resultId Result identifier. + */ + constructor(data: Uint32Array, resultId?: string); + } + + /** + * Represents edits to semantic tokens. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits provideDocumentSemanticTokensEdits} for an explanation of the format. + */ + export class SemanticTokensEdits { + /** + * The result id of the tokens. + * + * This is the id that will be passed to `DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits` (if implemented). + */ + readonly resultId: string | undefined; + /** + * The edits to the tokens data. + * All edits refer to the initial data state. + */ + readonly edits: SemanticTokensEdit[]; + + /** + * Create new semantic tokens edits. + * + * @param edits An array of semantic token edits + * @param resultId Result identifier. + */ + constructor(edits: SemanticTokensEdit[], resultId?: string); + } + + /** + * Represents an edit to semantic tokens. + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokensEdits provideDocumentSemanticTokensEdits} for an explanation of the format. + */ + export class SemanticTokensEdit { + /** + * The start offset of the edit. + */ + readonly start: number; + /** + * The count of elements to remove. + */ + readonly deleteCount: number; + /** + * The elements to insert. + */ + readonly data: Uint32Array | undefined; + + /** + * Create a semantic token edit. + * + * @param start Start offset + * @param deleteCount Number of elements to remove. + * @param data Elements to insert + */ + constructor(start: number, deleteCount: number, data?: Uint32Array); + } + + /** + * The document semantic tokens provider interface defines the contract between extensions and + * semantic tokens. + */ + export interface DocumentSemanticTokensProvider { + /** + * An optional event to signal that the semantic tokens from this provider have changed. + */ + onDidChangeSemanticTokens?: Event; + + /** + * Tokens in a file are represented as an array of integers. The position of each token is expressed relative to + * the token before it, because most tokens remain stable relative to each other when edits are made in a file. + * + * --- + * In short, each token takes 5 integers to represent, so a specific token `i` in the file consists of the following array indices: + * - at index `5*i` - `deltaLine`: token line number, relative to the previous token + * - at index `5*i+1` - `deltaStart`: token start character, relative to the previous token (relative to 0 or the previous token's start if they are on the same line) + * - at index `5*i+2` - `length`: the length of the token. A token cannot be multiline. + * - at index `5*i+3` - `tokenType`: will be looked up in `SemanticTokensLegend.tokenTypes`. We currently ask that `tokenType` < 65536. + * - at index `5*i+4` - `tokenModifiers`: each set bit will be looked up in `SemanticTokensLegend.tokenModifiers` + * + * --- + * ### How to encode tokens + * + * Here is an example for encoding a file with 3 tokens in a uint32 array: + * ``` + * { line: 2, startChar: 5, length: 3, tokenType: "property", tokenModifiers: ["private", "static"] }, + * { line: 2, startChar: 10, length: 4, tokenType: "type", tokenModifiers: [] }, + * { line: 5, startChar: 2, length: 7, tokenType: "class", tokenModifiers: [] } + * ``` + * + * 1. First of all, a legend must be devised. This legend must be provided up-front and capture all possible token types. + * For this example, we will choose the following legend which must be passed in when registering the provider: + * ``` + * tokenTypes: ['property', 'type', 'class'], + * tokenModifiers: ['private', 'static'] + * ``` + * + * 2. The first transformation step is to encode `tokenType` and `tokenModifiers` as integers using the legend. Token types are looked + * up by index, so a `tokenType` value of `1` means `tokenTypes[1]`. Multiple token modifiers can be set by using bit flags, + * so a `tokenModifier` value of `3` is first viewed as binary `0b00000011`, which means `[tokenModifiers[0], tokenModifiers[1]]` because + * bits 0 and 1 are set. Using this legend, the tokens now are: + * ``` + * { line: 2, startChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, + * { line: 2, startChar: 10, length: 4, tokenType: 1, tokenModifiers: 0 }, + * { line: 5, startChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } + * ``` + * + * 3. The next step is to represent each token relative to the previous token in the file. In this case, the second token + * is on the same line as the first token, so the `startChar` of the second token is made relative to the `startChar` + * of the first token, so it will be `10 - 5`. The third token is on a different line than the second token, so the + * `startChar` of the third token will not be altered: + * ``` + * { deltaLine: 2, deltaStartChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, + * { deltaLine: 0, deltaStartChar: 5, length: 4, tokenType: 1, tokenModifiers: 0 }, + * { deltaLine: 3, deltaStartChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } + * ``` + * + * 4. Finally, the last step is to inline each of the 5 fields for a token in a single array, which is a memory friendly representation: + * ``` + * // 1st token, 2nd token, 3rd token + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * ``` + * + * @see {@link SemanticTokensBuilder} for a helper to encode tokens as integers. + * *NOTE*: When doing edits, it is possible that multiple edits occur until the editor decides to invoke the semantic tokens provider. + * *NOTE*: If the provider cannot temporarily compute semantic tokens, it can indicate this by throwing an error with the message 'Busy'. + */ + provideDocumentSemanticTokens(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * Instead of always returning all the tokens in a file, it is possible for a `DocumentSemanticTokensProvider` to implement + * this method (`provideDocumentSemanticTokensEdits`) and then return incremental updates to the previously provided semantic tokens. + * + * --- + * ### How tokens change when the document changes + * + * Suppose that `provideDocumentSemanticTokens` has previously returned the following semantic tokens: + * ``` + * // 1st token, 2nd token, 3rd token + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * ``` + * + * Also suppose that after some edits, the new semantic tokens in a file are: + * ``` + * // 1st token, 2nd token, 3rd token + * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * ``` + * It is possible to express these new tokens in terms of an edit applied to the previous tokens: + * ``` + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] // old tokens + * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] // new tokens + * + * edit: { start: 0, deleteCount: 1, data: [3] } // replace integer at offset 0 with 3 + * ``` + * + * *NOTE*: If the provider cannot compute `SemanticTokensEdits`, it can "give up" and return all the tokens in the document again. + * *NOTE*: All edits in `SemanticTokensEdits` contain indices in the old integers array, so they all refer to the previous result state. + */ + provideDocumentSemanticTokensEdits?(document: TextDocument, previousResultId: string, token: CancellationToken): ProviderResult; + } + + /** + * The document range semantic tokens provider interface defines the contract between extensions and + * semantic tokens. + */ + export interface DocumentRangeSemanticTokensProvider { + /** + * @see {@link DocumentSemanticTokensProvider.provideDocumentSemanticTokens provideDocumentSemanticTokens}. + */ + provideDocumentRangeSemanticTokens(document: TextDocument, range: Range, token: CancellationToken): ProviderResult; + } + + /** + * Value-object describing what options formatting should use. + */ + export interface FormattingOptions { + + /** + * Size of a tab in spaces. + */ + tabSize: number; + + /** + * Prefer spaces over tabs. + */ + insertSpaces: boolean; + + /** + * Signature for further properties. + */ + [key: string]: boolean | number | string; + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ + export interface DocumentFormattingEditProvider { + + /** + * Provide formatting edits for a whole document. + * + * @param document The document in which the command was invoked. + * @param options Options controlling formatting. + * @param token A cancellation token. + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult; + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ + export interface DocumentRangeFormattingEditProvider { + + /** + * Provide formatting edits for a range in a document. + * + * The given range is a hint and providers can decide to format a smaller + * or larger range. Often this is done by adjusting the start and end + * of the range to full syntax nodes. + * + * @param document The document in which the command was invoked. + * @param range The range which should be formatted. + * @param options Options controlling formatting. + * @param token A cancellation token. + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult; + + + /** + * Provide formatting edits for multiple ranges in a document. + * + * This function is optional but allows a formatter to perform faster when formatting only modified ranges or when + * formatting a large number of selections. + * + * The given ranges are hints and providers can decide to format a smaller + * or larger range. Often this is done by adjusting the start and end + * of the range to full syntax nodes. + * + * @param document The document in which the command was invoked. + * @param ranges The ranges which should be formatted. + * @param options Options controlling formatting. + * @param token A cancellation token. + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentRangesFormattingEdits?(document: TextDocument, ranges: Range[], options: FormattingOptions, token: CancellationToken): ProviderResult; + } + + /** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ + export interface OnTypeFormattingEditProvider { + + /** + * Provide formatting edits after a character has been typed. + * + * The given position and character should hint to the provider + * what range the position to expand to, like find the matching `{` + * when `}` has been entered. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param ch The character that has been typed. + * @param options Options controlling formatting. + * @param token A cancellation token. + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): ProviderResult; + } + + /** + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ + export class ParameterInformation { + + /** + * The label of this signature. + * + * Either a string or inclusive start and exclusive end offsets within its containing + * {@link SignatureInformation.label signature label}. *Note*: A label of type string must be + * a substring of its containing signature information's {@link SignatureInformation.label label}. + */ + label: string | [number, number]; + + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string | MarkdownString; + + /** + * Creates a new parameter information object. + * + * @param label A label string or inclusive start and exclusive end offsets within its containing signature label. + * @param documentation A doc string. + */ + constructor(label: string | [number, number], documentation?: string | MarkdownString); + } + + /** + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ + export class SignatureInformation { + + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string; + + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string | MarkdownString; + + /** + * The parameters of this signature. + */ + parameters: ParameterInformation[]; + + /** + * The index of the active parameter. + * + * If provided, this is used in place of {@linkcode SignatureHelp.activeParameter}. + */ + activeParameter?: number; + + /** + * Creates a new signature information object. + * + * @param label A label string. + * @param documentation A doc string. + */ + constructor(label: string, documentation?: string | MarkdownString); + } + + /** + * Signature help represents the signature of something + * callable. There can be multiple signatures but only one + * active and only one active parameter. + */ + export class SignatureHelp { + + /** + * One or more signatures. + */ + signatures: SignatureInformation[]; + + /** + * The active signature. + */ + activeSignature: number; + + /** + * The active parameter of the active signature. + */ + activeParameter: number; + } + + /** + * How a {@linkcode SignatureHelpProvider} was triggered. + */ + export enum SignatureHelpTriggerKind { + /** + * Signature help was invoked manually by the user or by a command. + */ + Invoke = 1, + + /** + * Signature help was triggered by a trigger character. + */ + TriggerCharacter = 2, + + /** + * Signature help was triggered by the cursor moving or by the document content changing. + */ + ContentChange = 3, + } + + /** + * Additional information about the context in which a + * {@linkcode SignatureHelpProvider.provideSignatureHelp SignatureHelpProvider} was triggered. + */ + export interface SignatureHelpContext { + /** + * Action that caused signature help to be triggered. + */ + readonly triggerKind: SignatureHelpTriggerKind; + + /** + * Character that caused signature help to be triggered. + * + * This is `undefined` when signature help is not triggered by typing, such as when manually invoking + * signature help or when moving the cursor. + */ + readonly triggerCharacter: string | undefined; + + /** + * `true` if signature help was already showing when it was triggered. + * + * Retriggers occur when the signature help is already active and can be caused by actions such as + * typing a trigger character, a cursor move, or document content changes. + */ + readonly isRetrigger: boolean; + + /** + * The currently active {@linkcode SignatureHelp}. + * + * The `activeSignatureHelp` has its {@linkcode SignatureHelp.activeSignature activeSignature} field updated based on + * the user arrowing through available signatures. + */ + readonly activeSignatureHelp: SignatureHelp | undefined; + } + + /** + * The signature help provider interface defines the contract between extensions and + * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ + export interface SignatureHelpProvider { + + /** + * Provide help for the signature at the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @param context Information about how signature help was triggered. + * + * @returns Signature help or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; + } + + /** + * Metadata about a registered {@linkcode SignatureHelpProvider}. + */ + export interface SignatureHelpProviderMetadata { + /** + * List of characters that trigger signature help. + */ + readonly triggerCharacters: readonly string[]; + + /** + * List of characters that re-trigger signature help. + * + * These trigger characters are only active when signature help is already showing. All trigger characters + * are also counted as re-trigger characters. + */ + readonly retriggerCharacters: readonly string[]; + } + + /** + * A structured label for a {@link CompletionItem completion item}. + */ + export interface CompletionItemLabel { + + /** + * The label of this completion item. + * + * By default this is also the text that is inserted when this completion is selected. + */ + label: string; + + /** + * An optional string which is rendered less prominently directly after {@link CompletionItemLabel.label label}, + * without any spacing. Should be used for function signatures or type annotations. + */ + detail?: string; + + /** + * An optional string which is rendered less prominently after {@link CompletionItemLabel.detail}. Should be used + * for fully qualified names or file path. + */ + description?: string; + } + + /** + * Completion item kinds. + */ + export enum CompletionItemKind { + /** + * The `Text` completion item kind. + */ + Text = 0, + /** + * The `Method` completion item kind. + */ + Method = 1, + /** + * The `Function` completion item kind. + */ + Function = 2, + /** + * The `Constructor` completion item kind. + */ + Constructor = 3, + /** + * The `Field` completion item kind. + */ + Field = 4, + /** + * The `Variable` completion item kind. + */ + Variable = 5, + /** + * The `Class` completion item kind. + */ + Class = 6, + /** + * The `Interface` completion item kind. + */ + Interface = 7, + /** + * The `Module` completion item kind. + */ + Module = 8, + /** + * The `Property` completion item kind. + */ + Property = 9, + /** + * The `Unit` completion item kind. + */ + Unit = 10, + /** + * The `Value` completion item kind. + */ + Value = 11, + /** + * The `Enum` completion item kind. + */ + Enum = 12, + /** + * The `Keyword` completion item kind. + */ + Keyword = 13, + /** + * The `Snippet` completion item kind. + */ + Snippet = 14, + /** + * The `Color` completion item kind. + */ + Color = 15, + /** + * The `Reference` completion item kind. + */ + Reference = 17, + /** + * The `File` completion item kind. + */ + File = 16, + /** + * The `Folder` completion item kind. + */ + Folder = 18, + /** + * The `EnumMember` completion item kind. + */ + EnumMember = 19, + /** + * The `Constant` completion item kind. + */ + Constant = 20, + /** + * The `Struct` completion item kind. + */ + Struct = 21, + /** + * The `Event` completion item kind. + */ + Event = 22, + /** + * The `Operator` completion item kind. + */ + Operator = 23, + /** + * The `TypeParameter` completion item kind. + */ + TypeParameter = 24, + /** + * The `User` completion item kind. + */ + User = 25, + /** + * The `Issue` completion item kind. + */ + Issue = 26, + } + + /** + * Completion item tags are extra annotations that tweak the rendering of a completion + * item. + */ + export enum CompletionItemTag { + /** + * Render a completion as obsolete, usually using a strike-out. + */ + Deprecated = 1 + } + + /** + * A completion item represents a text snippet that is proposed to complete text that is being typed. + * + * It is sufficient to create a completion item from just a {@link CompletionItem.label label}. In that + * case the completion item will replace the {@link TextDocument.getWordRangeAtPosition word} + * until the cursor with the given label or {@link CompletionItem.insertText insertText}. Otherwise the + * given {@link CompletionItem.textEdit edit} is used. + * + * When selecting a completion item in the editor its defined or synthesized text edit will be applied + * to *all* cursors/selections whereas {@link CompletionItem.additionalTextEdits additionalTextEdits} will be + * applied as provided. + * + * @see {@link CompletionItemProvider.provideCompletionItems} + * @see {@link CompletionItemProvider.resolveCompletionItem} + */ + export class CompletionItem { + + /** + * The label of this completion item. By default + * this is also the text that is inserted when selecting + * this completion. + */ + label: string | CompletionItemLabel; + + /** + * The kind of this completion item. Based on the kind + * an icon is chosen by the editor. + */ + kind?: CompletionItemKind; + + /** + * Tags for this completion item. + */ + tags?: readonly CompletionItemTag[]; + + /** + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + detail?: string; + + /** + * A human-readable string that represents a doc-comment. + */ + documentation?: string | MarkdownString; + + /** + * A string that should be used when comparing this item + * with other items. When `falsy` the {@link CompletionItem.label label} + * is used. + * + * Note that `sortText` is only used for the initial ordering of completion + * items. When having a leading word (prefix) ordering is based on how + * well completions match that prefix and the initial ordering is only used + * when completions match equally well. The prefix is defined by the + * {@linkcode CompletionItem.range range}-property and can therefore be different + * for each completion. + */ + sortText?: string; + + /** + * A string that should be used when filtering a set of + * completion items. When `falsy` the {@link CompletionItem.label label} + * is used. + * + * Note that the filter text is matched against the leading word (prefix) which is defined + * by the {@linkcode CompletionItem.range range}-property. + */ + filterText?: string; + + /** + * Select this item when showing. *Note* that only one completion item can be selected and + * that the editor decides which item that is. The rule is that the *first* item of those + * that match best is selected. + */ + preselect?: boolean; + + /** + * A string or snippet that should be inserted in a document when selecting + * this completion. When `falsy` the {@link CompletionItem.label label} + * is used. + */ + insertText?: string | SnippetString; + + /** + * A range or a insert and replace range selecting the text that should be replaced by this completion item. + * + * When omitted, the range of the {@link TextDocument.getWordRangeAtPosition current word} is used as replace-range + * and as insert-range the start of the {@link TextDocument.getWordRangeAtPosition current word} to the + * current position is used. + * + * *Note 1:* A range must be a {@link Range.isSingleLine single line} and it must + * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. + * *Note 2:* A insert range must be a prefix of a replace range, that means it must be contained and starting at the same position. + */ + range?: Range | { + /** + * The range that should be used when insert-accepting a completion. Must be a prefix of `replaceRange`. + */ + inserting: Range; + /** + * The range that should be used when replace-accepting a completion. + */ + replacing: Range; + }; + + /** + * An optional set of characters that when pressed while this completion is active will accept it first and + * then type that character. *Note* that all commit characters should have `length=1` and that superfluous + * characters will be ignored. + */ + commitCharacters?: string[]; + + /** + * Keep whitespace of the {@link CompletionItem.insertText insertText} as is. By default, the editor adjusts leading + * whitespace of new lines so that they match the indentation of the line for which the item is accepted - setting + * this to `true` will prevent that. + */ + keepWhitespace?: boolean; + + /** + * @deprecated Use `CompletionItem.insertText` and `CompletionItem.range` instead. + * + * An {@link TextEdit edit} which is applied to a document when selecting + * this completion. When an edit is provided the value of + * {@link CompletionItem.insertText insertText} is ignored. + * + * The {@link Range} of the edit must be single-line and on the same + * line completions were {@link CompletionItemProvider.provideCompletionItems requested} at. + */ + textEdit?: TextEdit; + + /** + * An optional array of additional {@link TextEdit text edits} that are applied when + * selecting this completion. Edits must not overlap with the main {@link CompletionItem.textEdit edit} + * nor with themselves. + */ + additionalTextEdits?: TextEdit[]; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. *Note* that + * additional modifications to the current document should be described with the + * {@link CompletionItem.additionalTextEdits additionalTextEdits}-property. + */ + command?: Command; + + /** + * Creates a new completion item. + * + * Completion items must have at least a {@link CompletionItem.label label} which then + * will be used as insert text as well as for sorting and filtering. + * + * @param label The label of the completion. + * @param kind The {@link CompletionItemKind kind} of the completion. + */ + constructor(label: string | CompletionItemLabel, kind?: CompletionItemKind); + } + + /** + * Represents a collection of {@link CompletionItem completion items} to be presented + * in the editor. + */ + export class CompletionList { + + /** + * This list is not complete. Further typing should result in recomputing + * this list. + */ + isIncomplete?: boolean; + + /** + * The completion items. + */ + items: T[]; + + /** + * Creates a new completion list. + * + * @param items The completion items. + * @param isIncomplete The list is not complete. + */ + constructor(items?: T[], isIncomplete?: boolean); + } + + /** + * How a {@link CompletionItemProvider completion provider} was triggered + */ + export enum CompletionTriggerKind { + /** + * Completion was triggered normally. + */ + Invoke = 0, + /** + * Completion was triggered by a trigger character. + */ + TriggerCharacter = 1, + /** + * Completion was re-triggered as current completion list is incomplete + */ + TriggerForIncompleteCompletions = 2 + } + + /** + * Contains additional information about the context in which + * {@link CompletionItemProvider.provideCompletionItems completion provider} is triggered. + */ + export interface CompletionContext { + /** + * How the completion was triggered. + */ + readonly triggerKind: CompletionTriggerKind; + + /** + * Character that triggered the completion item provider. + * + * `undefined` if the provider was not triggered by a character. + * + * The trigger character is already in the document when the completion provider is triggered. + */ + readonly triggerCharacter: string | undefined; + } + + /** + * The completion item provider interface defines the contract between extensions and + * [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). + * + * Providers can delay the computation of the {@linkcode CompletionItem.detail detail} + * and {@linkcode CompletionItem.documentation documentation} properties by implementing the + * {@linkcode CompletionItemProvider.resolveCompletionItem resolveCompletionItem}-function. However, properties that + * are needed for the initial sorting and filtering, like `sortText`, `filterText`, `insertText`, and `range`, must + * not be changed during resolve. + * + * Providers are asked for completions either explicitly by a user gesture or -depending on the configuration- + * implicitly when typing words or trigger characters. + */ + export interface CompletionItemProvider { + + /** + * Provide completion items for the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @param context How the completion was triggered. + * + * @returns An array of completions, a {@link CompletionList completion list}, or a thenable that resolves to either. + * The lack of a result can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult>; + + /** + * Given a completion item fill in more data, like {@link CompletionItem.documentation doc-comment} + * or {@link CompletionItem.detail details}. + * + * The editor will only resolve a completion item once. + * + * *Note* that this function is called when completion items are already showing in the UI or when an item has been + * selected for insertion. Because of that, no property that changes the presentation (label, sorting, filtering etc) + * or the (primary) insert behaviour ({@link CompletionItem.insertText insertText}) can be changed. + * + * This function may fill in {@link CompletionItem.additionalTextEdits additionalTextEdits}. However, that means an item might be + * inserted *before* resolving is done and in that case the editor will do a best effort to still apply those additional + * text edits. + * + * @param item A completion item currently active in the UI. + * @param token A cancellation token. + * @returns The resolved completion item or a thenable that resolves to of such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveCompletionItem?(item: T, token: CancellationToken): ProviderResult; + } + + + /** + * The inline completion item provider interface defines the contract between extensions and + * the inline completion feature. + * + * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. + */ + export interface InlineCompletionItemProvider { + + /** + * Provides inline completion items for the given position and document. + * If inline completions are enabled, this method will be called whenever the user stopped typing. + * It will also be called when the user explicitly triggers inline completions or explicitly asks for the next or previous inline completion. + * In that case, all available inline completions should be returned. + * `context.triggerKind` can be used to distinguish between these scenarios. + * + * @param document The document inline completions are requested for. + * @param position The position inline completions are requested for. + * @param context A context object with additional information. + * @param token A cancellation token. + * @returns An array of completion items or a thenable that resolves to an array of completion items. + */ + provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; + } + + /** + * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented + * in the editor. + */ + export class InlineCompletionList { + /** + * The inline completion items. + */ + items: InlineCompletionItem[]; + + /** + * Creates a new list of inline completion items. + */ + constructor(items: InlineCompletionItem[]); + } + + /** + * Provides information about the context in which an inline completion was requested. + */ + export interface InlineCompletionContext { + /** + * Describes how the inline completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; + + /** + * Provides information about the currently selected item in the autocomplete widget if it is visible. + * + * If set, provided inline completions must extend the text of the selected item + * and use the same range, otherwise they are not shown as preview. + * As an example, if the document text is `console.` and the selected item is `.log` replacing the `.` in the document, + * the inline completion must also replace `.` and start with `.log`, for example `.log()`. + * + * Inline completion providers are requested again whenever the selected item changes. + */ + readonly selectedCompletionInfo: SelectedCompletionInfo | undefined; + } + + /** + * Describes the currently selected completion item. + */ + export interface SelectedCompletionInfo { + /** + * The range that will be replaced if this completion item is accepted. + */ + readonly range: Range; + + /** + * The text the range will be replaced with if this completion is accepted. + */ + readonly text: string; + } + + /** + * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. + */ + export enum InlineCompletionTriggerKind { + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Invoke = 0, + + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 1, + } + + /** + * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + * + * @see {@link InlineCompletionItemProvider.provideInlineCompletionItems} + */ + export class InlineCompletionItem { + /** + * The text to replace the range with. Must be set. + * Is used both for the preview and the accept operation. + */ + insertText: string | SnippetString; + + /** + * A text that is used to decide if this inline completion should be shown. When `falsy` + * the {@link InlineCompletionItem.insertText} is used. + * + * An inline completion is shown if the text to replace is a prefix of the filter text. + */ + filterText?: string; + + /** + * The range to replace. + * Must begin and end on the same line. + * + * Prefer replacements over insertions to provide a better experience when the user deletes typed text. + */ + range?: Range; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command; + + /** + * Creates a new inline completion item. + * + * @param insertText The text to replace the range with. + * @param range The range to replace. If not set, the word at the requested position will be used. + * @param command An optional {@link Command} that is executed *after* inserting this completion. + */ + constructor(insertText: string | SnippetString, range?: Range, command?: Command); + } + + /** + * A document link is a range in a text document that links to an internal or external resource, like another + * text document or a web site. + */ + export class DocumentLink { + + /** + * The range this link applies to. + */ + range: Range; + + /** + * The uri this link points to. + */ + target?: Uri; + + /** + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on how to + * trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, + * user settings, and localization. + */ + tooltip?: string; + + /** + * Creates a new document link. + * + * @param range The range the document link applies to. Must not be empty. + * @param target The uri the document link points to. + */ + constructor(range: Range, target?: Uri); + } + + /** + * The document link provider defines the contract between extensions and feature of showing + * links in the editor. + */ + export interface DocumentLinkProvider { + + /** + * Provide links for the given document. Note that the editor ships with a default provider that detects + * `http(s)` and `file` links. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @returns An array of {@link DocumentLink document links} or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentLinks(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * Given a link fill in its {@link DocumentLink.target target}. This method is called when an incomplete + * link is selected in the UI. Providers can implement this method and return incomplete links + * (without target) from the {@linkcode DocumentLinkProvider.provideDocumentLinks provideDocumentLinks} method which + * often helps to improve performance. + * + * @param link The link that is to be resolved. + * @param token A cancellation token. + */ + resolveDocumentLink?(link: T, token: CancellationToken): ProviderResult; + } + + /** + * Represents a color in RGBA space. + */ + export class Color { + + /** + * The red component of this color in the range `[0-1]`. + */ + readonly red: number; + + /** + * The green component of this color in the range `[0-1]`. + */ + readonly green: number; + + /** + * The blue component of this color in the range `[0-1]`. + */ + readonly blue: number; + + /** + * The alpha component of this color in the range `[0-1]`. + */ + readonly alpha: number; + + /** + * Creates a new color instance. + * + * @param red The red component. + * @param green The green component. + * @param blue The blue component. + * @param alpha The alpha component. + */ + constructor(red: number, green: number, blue: number, alpha: number); + } + + /** + * Represents a color range from a document. + */ + export class ColorInformation { + + /** + * The range in the document where this color appears. + */ + range: Range; + + /** + * The actual color value for this color range. + */ + color: Color; + + /** + * Creates a new color range. + * + * @param range The range the color appears in. Must not be empty. + * @param color The value of the color. + */ + constructor(range: Range, color: Color); + } + + /** + * A color presentation object describes how a {@linkcode Color} should be represented as text and what + * edits are required to refer to it from source code. + * + * For some languages one color can have multiple presentations, e.g. css can represent the color red with + * the constant `Red`, the hex-value `#ff0000`, or in rgba and hsla forms. In csharp other representations + * apply, e.g. `System.Drawing.Color.Red`. + */ + export class ColorPresentation { + + /** + * The label of this color presentation. It will be shown on the color + * picker header. By default this is also the text that is inserted when selecting + * this color presentation. + */ + label: string; + + /** + * An {@link TextEdit edit} which is applied to a document when selecting + * this presentation for the color. When `falsy` the {@link ColorPresentation.label label} + * is used. + */ + textEdit?: TextEdit; + + /** + * An optional array of additional {@link TextEdit text edits} that are applied when + * selecting this color presentation. Edits must not overlap with the main {@link ColorPresentation.textEdit edit} nor with themselves. + */ + additionalTextEdits?: TextEdit[]; + + /** + * Creates a new color presentation. + * + * @param label The label of this color presentation. + */ + constructor(label: string); + } + + /** + * The document color provider defines the contract between extensions and feature of + * picking and modifying colors in the editor. + */ + export interface DocumentColorProvider { + + /** + * Provide colors for the given document. + * + * @param document The document in which the command was invoked. + * @param token A cancellation token. + * @returns An array of {@link ColorInformation color information} or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; + + /** + * Provide {@link ColorPresentation representations} for a color. + * + * @param color The color to show and insert. + * @param context A context object with additional information + * @param token A cancellation token. + * @returns An array of color presentations or a thenable that resolves to such. The lack of a result + * can be signaled by returning `undefined`, `null`, or an empty array. + */ + provideColorPresentations(color: Color, context: { + /** + * The text document that contains the color + */ + readonly document: TextDocument; + /** + * The range in the document where the color is located. + */ + readonly range: Range; + }, token: CancellationToken): ProviderResult; + } + + /** + * Inlay hint kinds. + * + * The kind of an inline hint defines its appearance, e.g the corresponding foreground and background colors are being + * used. + */ + export enum InlayHintKind { + /** + * An inlay hint that for a type annotation. + */ + Type = 1, + /** + * An inlay hint that is for a parameter. + */ + Parameter = 2, + } + + /** + * An inlay hint label part allows for interactive and composite labels of inlay hints. + */ + export class InlayHintLabelPart { + + /** + * The value of this label part. + */ + value: string; + + /** + * The tooltip text when you hover over this label part. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + tooltip?: string | MarkdownString | undefined; + + /** + * An optional {@link Location source code location} that represents this label + * part. + * + * The editor will use this location for the hover and for code navigation features: This + * part will become a clickable link that resolves to the definition of the symbol at the + * given location (not necessarily the location itself), it shows the hover that shows at + * the given location, and it shows a context menu with further code navigation commands. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + location?: Location | undefined; + + /** + * An optional command for this label part. + * + * The editor renders parts with commands as clickable links. The command is added to the context menu + * when a label part defines {@link InlayHintLabelPart.location location} and {@link InlayHintLabelPart.command command} . + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + command?: Command | undefined; + + /** + * Creates a new inlay hint label part. + * + * @param value The value of the part. + */ + constructor(value: string); + } + + /** + * Inlay hint information. + */ + export class InlayHint { + + /** + * The position of this hint. + */ + position: Position; + + /** + * The label of this hint. A human readable string or an array of {@link InlayHintLabelPart label parts}. + * + * *Note* that neither the string nor the label part can be empty. + */ + label: string | InlayHintLabelPart[]; + + /** + * The tooltip text when you hover over this item. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + tooltip?: string | MarkdownString | undefined; + + /** + * The kind of this hint. The inlay hint kind defines the appearance of this inlay hint. + */ + kind?: InlayHintKind; + + /** + * Optional {@link TextEdit text edits} that are performed when accepting this inlay hint. The default + * gesture for accepting an inlay hint is the double click. + * + * *Note* that edits are expected to change the document so that the inlay hint (or its nearest variant) is + * now part of the document and the inlay hint itself is now obsolete. + * + * *Note* that this property can be set late during + * {@link InlayHintsProvider.resolveInlayHint resolving} of inlay hints. + */ + textEdits?: TextEdit[]; + + /** + * Render padding before the hint. Padding will use the editor's background color, + * not the background color of the hint itself. That means padding can be used to visually + * align/separate an inlay hint. + */ + paddingLeft?: boolean; + + /** + * Render padding after the hint. Padding will use the editor's background color, + * not the background color of the hint itself. That means padding can be used to visually + * align/separate an inlay hint. + */ + paddingRight?: boolean; + + /** + * Creates a new inlay hint. + * + * @param position The position of the hint. + * @param label The label of the hint. + * @param kind The {@link InlayHintKind kind} of the hint. + */ + constructor(position: Position, label: string | InlayHintLabelPart[], kind?: InlayHintKind); + } + + /** + * The inlay hints provider interface defines the contract between extensions and + * the inlay hints feature. + */ + export interface InlayHintsProvider { + + /** + * An optional event to signal that inlay hints from this provider have changed. + */ + onDidChangeInlayHints?: Event; + + /** + * Provide inlay hints for the given range and document. + * + * *Note* that inlay hints that are not {@link Range.contains contained} by the given range are ignored. + * + * @param document The document in which the command was invoked. + * @param range The range for which inlay hints should be computed. + * @param token A cancellation token. + * @returns An array of inlay hints or a thenable that resolves to such. + */ + provideInlayHints(document: TextDocument, range: Range, token: CancellationToken): ProviderResult; + + /** + * Given an inlay hint fill in {@link InlayHint.tooltip tooltip}, {@link InlayHint.textEdits text edits}, + * or complete label {@link InlayHintLabelPart parts}. + * + * *Note* that the editor will resolve an inlay hint at most once. + * + * @param hint An inlay hint. + * @param token A cancellation token. + * @returns The resolved inlay hint or a thenable that resolves to such. It is OK to return the given `item`. When no result is returned, the given `item` will be used. + */ + resolveInlayHint?(hint: T, token: CancellationToken): ProviderResult; + } + + /** + * A line based folding range. To be valid, start and end line must be bigger than zero and smaller than the number of lines in the document. + * Invalid ranges will be ignored. + */ + export class FoldingRange { + + /** + * The zero-based start line of the range to fold. The folded area starts after the line's last character. + * To be valid, the end must be zero or larger and smaller than the number of lines in the document. + */ + start: number; + + /** + * The zero-based end line of the range to fold. The folded area ends with the line's last character. + * To be valid, the end must be zero or larger and smaller than the number of lines in the document. + */ + end: number; + + /** + * Describes the {@link FoldingRangeKind Kind} of the folding range such as {@link FoldingRangeKind.Comment Comment} or + * {@link FoldingRangeKind.Region Region}. The kind is used to categorize folding ranges and used by commands + * like 'Fold all comments'. See + * {@link FoldingRangeKind} for an enumeration of all kinds. + * If not set, the range is originated from a syntax element. + */ + kind?: FoldingRangeKind; + + /** + * Creates a new folding range. + * + * @param start The start line of the folded range. + * @param end The end line of the folded range. + * @param kind The kind of the folding range. + */ + constructor(start: number, end: number, kind?: FoldingRangeKind); + } + + /** + * An enumeration of specific folding range kinds. The kind is an optional field of a {@link FoldingRange} + * and is used to distinguish specific folding ranges such as ranges originated from comments. The kind is used by commands like + * `Fold all comments` or `Fold all regions`. + * If the kind is not set on the range, the range originated from a syntax element other than comments, imports or region markers. + */ + export enum FoldingRangeKind { + /** + * Kind for folding range representing a comment. + */ + Comment = 1, + /** + * Kind for folding range representing a import. + */ + Imports = 2, + /** + * Kind for folding range representing regions originating from folding markers like `#region` and `#endregion`. + */ + Region = 3 + } + + /** + * Folding context (for future use) + */ + export interface FoldingContext { + } + + /** + * The folding range provider interface defines the contract between extensions and + * [Folding](https://code.visualstudio.com/docs/editor/codebasics#_folding) in the editor. + */ + export interface FoldingRangeProvider { + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChangeFoldingRanges?: Event; + + /** + * Returns a list of folding ranges or null and undefined if the provider + * does not want to participate or was cancelled. + * @param document The document in which the command was invoked. + * @param context Additional context information (for future use) + * @param token A cancellation token. + */ + provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken): ProviderResult; + } + + /** + * A selection range represents a part of a selection hierarchy. A selection range + * may have a parent selection range that contains it. + */ + export class SelectionRange { + + /** + * The {@link Range} of this selection range. + */ + range: Range; + + /** + * The parent selection range containing this range. + */ + parent?: SelectionRange; + + /** + * Creates a new selection range. + * + * @param range The range of the selection range. + * @param parent The parent of the selection range. + */ + constructor(range: Range, parent?: SelectionRange); + } + + /** + * The selection range provider interface defines the contract between extensions and the "Expand and Shrink Selection" feature. + */ + export interface SelectionRangeProvider { + /** + * Provide selection ranges for the given positions. + * + * Selection ranges should be computed individually and independent for each position. The editor will merge + * and deduplicate ranges but providers must return hierarchies of selection ranges so that a range + * is {@link Range.contains contained} by its parent. + * + * @param document The document in which the command was invoked. + * @param positions The positions at which the command was invoked. + * @param token A cancellation token. + * @returns Selection ranges or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideSelectionRanges(document: TextDocument, positions: readonly Position[], token: CancellationToken): ProviderResult; + } + + /** + * Represents programming constructs like functions or constructors in the context + * of call hierarchy. + */ + export class CallHierarchyItem { + /** + * The name of this item. + */ + name: string; + + /** + * The kind of this item. + */ + kind: SymbolKind; + + /** + * Tags for this item. + */ + tags?: readonly SymbolTag[]; + + /** + * More detail for this item, e.g. the signature of a function. + */ + detail?: string; + + /** + * The resource identifier of this item. + */ + uri: Uri; + + /** + * The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. + */ + range: Range; + + /** + * The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function. + * Must be contained by the {@linkcode CallHierarchyItem.range range}. + */ + selectionRange: Range; + + /** + * Creates a new call hierarchy item. + */ + constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); + } + + /** + * Represents an incoming call, e.g. a caller of a method or constructor. + */ + export class CallHierarchyIncomingCall { + + /** + * The item that makes the call. + */ + from: CallHierarchyItem; + + /** + * The range at which at which the calls appears. This is relative to the caller + * denoted by {@linkcode CallHierarchyIncomingCall.from this.from}. + */ + fromRanges: Range[]; + + /** + * Create a new call object. + * + * @param item The item making the call. + * @param fromRanges The ranges at which the calls appear. + */ + constructor(item: CallHierarchyItem, fromRanges: Range[]); + } + + /** + * Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc. + */ + export class CallHierarchyOutgoingCall { + + /** + * The item that is called. + */ + to: CallHierarchyItem; + + /** + * The range at which this item is called. This is the range relative to the caller, e.g the item + * passed to {@linkcode CallHierarchyProvider.provideCallHierarchyOutgoingCalls provideCallHierarchyOutgoingCalls} + * and not {@linkcode CallHierarchyOutgoingCall.to this.to}. + */ + fromRanges: Range[]; + + /** + * Create a new call object. + * + * @param item The item being called + * @param fromRanges The ranges at which the calls appear. + */ + constructor(item: CallHierarchyItem, fromRanges: Range[]); + } + + /** + * The call hierarchy provider interface describes the contract between extensions + * and the call hierarchy feature which allows to browse calls and caller of function, + * methods, constructor etc. + */ + export interface CallHierarchyProvider { + + /** + * Bootstraps call hierarchy by returning the item that is denoted by the given document + * and position. This item will be used as entry into the call graph. Providers should + * return `undefined` or `null` when there is no item at the given location. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns One or multiple call hierarchy items or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + prepareCallHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + + /** + * Provide all incoming calls for an item, e.g all callers for a method. In graph terms this describes directed + * and annotated edges inside the call graph, e.g the given item is the starting node and the result is the nodes + * that can be reached. + * + * @param item The hierarchy item for which incoming calls should be computed. + * @param token A cancellation token. + * @returns A set of incoming calls or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideCallHierarchyIncomingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; + + /** + * Provide all outgoing calls for an item, e.g call calls to functions, methods, or constructors from the given item. In + * graph terms this describes directed and annotated edges inside the call graph, e.g the given item is the starting + * node and the result is the nodes that can be reached. + * + * @param item The hierarchy item for which outgoing calls should be computed. + * @param token A cancellation token. + * @returns A set of outgoing calls or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideCallHierarchyOutgoingCalls(item: CallHierarchyItem, token: CancellationToken): ProviderResult; + } + + /** + * Represents an item of a type hierarchy, like a class or an interface. + */ + export class TypeHierarchyItem { + /** + * The name of this item. + */ + name: string; + + /** + * The kind of this item. + */ + kind: SymbolKind; + + /** + * Tags for this item. + */ + tags?: ReadonlyArray; + + /** + * More detail for this item, e.g. the signature of a function. + */ + detail?: string; + + /** + * The resource identifier of this item. + */ + uri: Uri; + + /** + * The range enclosing this symbol not including leading/trailing whitespace + * but everything else, e.g. comments and code. + */ + range: Range; + + /** + * The range that should be selected and revealed when this symbol is being + * picked, e.g. the name of a class. Must be contained by the {@link TypeHierarchyItem.range range}-property. + */ + selectionRange: Range; + + /** + * Creates a new type hierarchy item. + * + * @param kind The kind of the item. + * @param name The name of the item. + * @param detail The details of the item. + * @param uri The Uri of the item. + * @param range The whole range of the item. + * @param selectionRange The selection range of the item. + */ + constructor(kind: SymbolKind, name: string, detail: string, uri: Uri, range: Range, selectionRange: Range); + } + + /** + * The type hierarchy provider interface describes the contract between extensions + * and the type hierarchy feature. + */ + export interface TypeHierarchyProvider { + + /** + * Bootstraps type hierarchy by returning the item that is denoted by the given document + * and position. This item will be used as entry into the type graph. Providers should + * return `undefined` or `null` when there is no item at the given location. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @returns One or multiple type hierarchy items or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty array. + */ + prepareTypeHierarchy(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + + /** + * Provide all supertypes for an item, e.g all types from which a type is derived/inherited. In graph terms this describes directed + * and annotated edges inside the type graph, e.g the given item is the starting node and the result is the nodes + * that can be reached. + * + * @param item The hierarchy item for which super types should be computed. + * @param token A cancellation token. + * @returns A set of direct supertypes or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideTypeHierarchySupertypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult; + + /** + * Provide all subtypes for an item, e.g all types which are derived/inherited from the given item. In + * graph terms this describes directed and annotated edges inside the type graph, e.g the given item is the starting + * node and the result is the nodes that can be reached. + * + * @param item The hierarchy item for which subtypes should be computed. + * @param token A cancellation token. + * @returns A set of direct subtypes or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideTypeHierarchySubtypes(item: TypeHierarchyItem, token: CancellationToken): ProviderResult; + } + + /** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid range contents. + */ + export class LinkedEditingRanges { + /** + * Create a new linked editing ranges object. + * + * @param ranges A list of ranges that can be edited together + * @param wordPattern An optional word pattern that describes valid contents for the given ranges + */ + constructor(ranges: Range[], wordPattern?: RegExp); + + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap. + */ + readonly ranges: Range[]; + + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + readonly wordPattern: RegExp | undefined; + } + + /** + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. + */ + export interface LinkedEditingRangeProvider { + /** + * For a given position in a document, returns the range of the symbol at the position and all ranges + * that have the same content. A change to one of the ranges can be applied to all other ranges if the new content + * is valid. An optional word pattern can be returned with the result to describe valid contents. + * If no result-specific word pattern is provided, the word pattern from the language configuration is used. + * + * @param document The document in which the provider was invoked. + * @param position The position at which the provider was invoked. + * @param token A cancellation token. + * @returns A list of ranges that can be edited together + */ + provideLinkedEditingRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + + /** + * An edit operation applied {@link DocumentDropEditProvider on drop}. + */ + export class DocumentDropEdit { + /** + * The text or snippet to insert at the drop location. + */ + insertText: string | SnippetString; + + /** + * An optional additional edit to apply on drop. + */ + additionalEdit?: WorkspaceEdit; + + /** + * @param insertText The text or snippet to insert at the drop location. + */ + constructor(insertText: string | SnippetString); + } + + /** + * Provider which handles dropping of resources into a text editor. + * + * This allows users to drag and drop resources (including resources from external apps) into the editor. While dragging + * and dropping files, users can hold down `shift` to drop the file into the editor instead of opening it. + * Requires `editor.dropIntoEditor.enabled` to be on. + */ + export interface DocumentDropEditProvider { + /** + * Provide edits which inserts the content being dragged and dropped into the document. + * + * @param document The document in which the drop occurred. + * @param position The position in the document where the drop occurred. + * @param dataTransfer A {@link DataTransfer} object that holds data about what is being dragged and dropped. + * @param token A cancellation token. + * + * @returns A {@link DocumentDropEdit} or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideDocumentDropEdits(document: TextDocument, position: Position, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; + } + + /** + * A tuple of two characters, like a pair of + * opening and closing brackets. + */ + export type CharacterPair = [string, string]; + + /** + * Describes how comments for a language work. + */ + export interface CommentRule { + + /** + * The line comment token, like `// this is a comment` + */ + lineComment?: string; + + /** + * The block comment character pair, like `/* block comment */` + */ + blockComment?: CharacterPair; + } + + /** + * Describes indentation rules for a language. + */ + export interface IndentationRule { + /** + * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). + */ + decreaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then all the lines after it should be indented once (until another rule matches). + */ + increaseIndentPattern: RegExp; + /** + * If a line matches this pattern, then **only the next line** after it should be indented once. + */ + indentNextLinePattern?: RegExp; + /** + * If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules. + */ + unIndentedLinePattern?: RegExp; + } + + /** + * Describes what to do with the indentation when pressing Enter. + */ + export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3 + } + + /** + * Describes what to do when pressing Enter. + */ + export interface EnterAction { + /** + * Describe what to do with the indentation. + */ + indentAction: IndentAction; + /** + * Describes text to be appended after the new line and after the indentation. + */ + appendText?: string; + /** + * Describes the number of characters to remove from the new line's indentation. + */ + removeText?: number; + } + + /** + * Describes a rule to be evaluated when pressing Enter. + */ + export interface OnEnterRule { + /** + * This rule will only execute if the text before the cursor matches this regular expression. + */ + beforeText: RegExp; + /** + * This rule will only execute if the text after the cursor matches this regular expression. + */ + afterText?: RegExp; + /** + * This rule will only execute if the text above the current line matches this regular expression. + */ + previousLineText?: RegExp; + /** + * The action to execute. + */ + action: EnterAction; + } + + /** + * Enumeration of commonly encountered syntax token types. + */ + export enum SyntaxTokenType { + /** + * Everything except tokens that are part of comments, string literals and regular expressions. + */ + Other = 0, + /** + * A comment. + */ + Comment = 1, + /** + * A string literal. + */ + String = 2, + /** + * A regular expression. + */ + RegEx = 3 + } + + /** + * Describes pairs of strings where the close string will be automatically inserted when typing the opening string. + */ + export interface AutoClosingPair { + /** + * The string that will trigger the automatic insertion of the closing string. + */ + open: string; + /** + * The closing string that will be automatically inserted when typing the opening string. + */ + close: string; + /** + * A set of tokens where the pair should not be auto closed. + */ + notIn?: SyntaxTokenType[]; + } + + /** + * The language configuration interfaces defines the contract between extensions + * and various editor features, like automatic bracket insertion, automatic indentation etc. + */ + export interface LanguageConfiguration { + /** + * The language's comment settings. + */ + comments?: CommentRule; + /** + * The language's brackets. + * This configuration implicitly affects pressing Enter around these brackets. + */ + brackets?: CharacterPair[]; + /** + * The language's word definition. + * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable + * to provide a word definition that uses exclusion of known separators. + * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): + * ``` + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + * ``` + */ + wordPattern?: RegExp; + /** + * The language's indentation settings. + */ + indentationRules?: IndentationRule; + /** + * The language's rules to be evaluated when pressing Enter. + */ + onEnterRules?: OnEnterRule[]; + /** + * The language's auto closing pairs. + */ + autoClosingPairs?: AutoClosingPair[]; + + /** + * **Deprecated** Do not use. + * + * @deprecated Will be replaced by a better API soon. + */ + __electricCharacterSupport?: { + /** + * This property is deprecated and will be **ignored** from + * the editor. + * @deprecated + */ + brackets?: any; + /** + * This property is deprecated and not fully supported anymore by + * the editor (scope and lineStart are ignored). + * Use the autoClosingPairs property in the language configuration file instead. + * @deprecated + */ + docComment?: { + /** + * @deprecated + */ + scope: string; + /** + * @deprecated + */ + open: string; + /** + * @deprecated + */ + lineStart: string; + /** + * @deprecated + */ + close?: string; + }; + }; + + /** + * **Deprecated** Do not use. + * + * @deprecated * Use the autoClosingPairs property in the language configuration file instead. + */ + __characterPairSupport?: { + /** + * @deprecated + */ + autoClosingPairs: { + /** + * @deprecated + */ + open: string; + /** + * @deprecated + */ + close: string; + /** + * @deprecated + */ + notIn?: string[]; + }[]; + }; + } + + /** + * The configuration target + */ + export enum ConfigurationTarget { + /** + * Global configuration + */ + Global = 1, + + /** + * Workspace configuration + */ + Workspace = 2, + + /** + * Workspace folder configuration + */ + WorkspaceFolder = 3 + } + + /** + * Represents the configuration. It is a merged view of + * + * - *Default Settings* + * - *Global (User) Settings* + * - *Workspace settings* + * - *Workspace Folder settings* - From one of the {@link workspace.workspaceFolders Workspace Folders} under which requested resource belongs to. + * - *Language settings* - Settings defined under requested language. + * + * The *effective* value (returned by {@linkcode WorkspaceConfiguration.get get}) is computed by overriding or merging the values in the following order: + * + * 1. `defaultValue` (if defined in `package.json` otherwise derived from the value's type) + * 1. `globalValue` (if defined) + * 1. `workspaceValue` (if defined) + * 1. `workspaceFolderValue` (if defined) + * 1. `defaultLanguageValue` (if defined) + * 1. `globalLanguageValue` (if defined) + * 1. `workspaceLanguageValue` (if defined) + * 1. `workspaceFolderLanguageValue` (if defined) + * + * **Note:** Only `object` value types are merged and all other value types are overridden. + * + * Example 1: Overriding + * + * ```ts + * defaultValue = 'on'; + * globalValue = 'relative' + * workspaceFolderValue = 'off' + * value = 'off' + * ``` + * + * Example 2: Language Values + * + * ```ts + * defaultValue = 'on'; + * globalValue = 'relative' + * workspaceFolderValue = 'off' + * globalLanguageValue = 'on' + * value = 'on' + * ``` + * + * Example 3: Object Values + * + * ```ts + * defaultValue = { "a": 1, "b": 2 }; + * globalValue = { "b": 3, "c": 4 }; + * value = { "a": 1, "b": 3, "c": 4 }; + * ``` + * + * *Note:* Workspace and Workspace Folder configurations contains `launch` and `tasks` settings. Their basename will be + * part of the section identifier. The following snippets shows how to retrieve all configurations + * from `launch.json`: + * + * ```ts + * // launch.json configuration + * const config = workspace.getConfiguration('launch', vscode.workspace.workspaceFolders[0].uri); + * + * // retrieve values + * const values = config.get('configurations'); + * ``` + * + * Refer to [Settings](https://code.visualstudio.com/docs/getstarted/settings) for more information. + */ + export interface WorkspaceConfiguration { + + /** + * Return a value from this configuration. + * + * @param section Configuration name, supports _dotted_ names. + * @returns The value `section` denotes or `undefined`. + */ + get(section: string): T | undefined; + + /** + * Return a value from this configuration. + * + * @param section Configuration name, supports _dotted_ names. + * @param defaultValue A value should be returned when no value could be found, is `undefined`. + * @returns The value `section` denotes or the default. + */ + get(section: string, defaultValue: T): T; + + /** + * Check if this configuration has a certain value. + * + * @param section Configuration name, supports _dotted_ names. + * @returns `true` if the section doesn't resolve to `undefined`. + */ + has(section: string): boolean; + + /** + * Retrieve all information about a configuration setting. A configuration value + * often consists of a *default* value, a global or installation-wide value, + * a workspace-specific value, folder-specific value + * and language-specific values (if {@link WorkspaceConfiguration} is scoped to a language). + * + * Also provides all language ids under which the given configuration setting is defined. + * + * *Note:* The configuration name must denote a leaf in the configuration tree + * (`editor.fontSize` vs `editor`) otherwise no result is returned. + * + * @param section Configuration name, supports _dotted_ names. + * @returns Information about a configuration setting or `undefined`. + */ + inspect(section: string): { + + /** + * The fully qualified key of the configuration value + */ + key: string; + + /** + * The default value which is used when no other value is defined + */ + defaultValue?: T; + + /** + * The global or installation-wide value. + */ + globalValue?: T; + + /** + * The workspace-specific value. + */ + workspaceValue?: T; + + /** + * The workspace-folder-specific value. + */ + workspaceFolderValue?: T; + + /** + * Language specific default value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ + defaultLanguageValue?: T; + + /** + * Language specific global value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ + globalLanguageValue?: T; + + /** + * Language specific workspace value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ + workspaceLanguageValue?: T; + + /** + * Language specific workspace-folder value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ + workspaceFolderLanguageValue?: T; + + /** + * All language identifiers for which this configuration is defined. + */ + languageIds?: string[]; + + } | undefined; + + /** + * Update a configuration value. The updated configuration values are persisted. + * + * A value can be changed in + * + * - {@link ConfigurationTarget.Global Global settings}: Changes the value for all instances of the editor. + * - {@link ConfigurationTarget.Workspace Workspace settings}: Changes the value for current workspace, if available. + * - {@link ConfigurationTarget.WorkspaceFolder Workspace folder settings}: Changes the value for settings from one of the {@link workspace.workspaceFolders Workspace Folders} under which the requested resource belongs to. + * - Language settings: Changes the value for the requested languageId. + * + * *Note:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` + * + * @param section Configuration name, supports _dotted_ names. + * @param value The new value. + * @param configurationTarget The {@link ConfigurationTarget configuration target} or a boolean value. + * - If `true` updates {@link ConfigurationTarget.Global Global settings}. + * - If `false` updates {@link ConfigurationTarget.Workspace Workspace settings}. + * - If `undefined` or `null` updates to {@link ConfigurationTarget.WorkspaceFolder Workspace folder settings} if configuration is resource specific, + * otherwise to {@link ConfigurationTarget.Workspace Workspace settings}. + * @param overrideInLanguage Whether to update the value in the scope of requested languageId or not. + * - If `true` updates the value under the requested languageId. + * - If `undefined` updates the value under the requested languageId only if the configuration is defined for the language. + * @throws error while updating + * - configuration which is not registered. + * - window configuration to workspace folder + * - configuration to workspace or workspace folder when no workspace is opened. + * - configuration to workspace folder when there is no workspace folder settings. + * - configuration to workspace folder when {@link WorkspaceConfiguration} is not scoped to a resource. + */ + update(section: string, value: any, configurationTarget?: ConfigurationTarget | boolean | null, overrideInLanguage?: boolean): Thenable; + + /** + * Readable dictionary that backs this configuration. + */ + readonly [key: string]: any; + } + + /** + * Represents a location inside a resource, such as a line + * inside a text file. + */ + export class Location { + + /** + * The resource identifier of this location. + */ + uri: Uri; + + /** + * The document range of this location. + */ + range: Range; + + /** + * Creates a new location object. + * + * @param uri The resource identifier. + * @param rangeOrPosition The range or position. Positions will be converted to an empty range. + */ + constructor(uri: Uri, rangeOrPosition: Range | Position); + } + + /** + * Represents the connection of two locations. Provides additional metadata over normal {@link Location locations}, + * including an origin range. + */ + export interface LocationLink { + /** + * Span of the origin of this link. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + originSelectionRange?: Range; + + /** + * The target resource identifier of this link. + */ + targetUri: Uri; + + /** + * The full target range of this link. + */ + targetRange: Range; + + /** + * The span of this link. + */ + targetSelectionRange?: Range; + } + + /** + * The event that is fired when diagnostics change. + */ + export interface DiagnosticChangeEvent { + + /** + * An array of resources for which diagnostics have changed. + */ + readonly uris: readonly Uri[]; + } + + /** + * Represents the severity of diagnostics. + */ + export enum DiagnosticSeverity { + + /** + * Something not allowed by the rules of a language or other means. + */ + Error = 0, + + /** + * Something suspicious but allowed. + */ + Warning = 1, + + /** + * Something to inform about but not a problem. + */ + Information = 2, + + /** + * Something to hint to a better way of doing it, like proposing + * a refactoring. + */ + Hint = 3 + } + + /** + * Represents a related message and source code location for a diagnostic. This should be + * used to point to code locations that cause or related to a diagnostics, e.g. when duplicating + * a symbol in a scope. + */ + export class DiagnosticRelatedInformation { + + /** + * The location of this related diagnostic information. + */ + location: Location; + + /** + * The message of this related diagnostic information. + */ + message: string; + + /** + * Creates a new related diagnostic information object. + * + * @param location The location. + * @param message The message. + */ + constructor(location: Location, message: string); + } + + /** + * Additional metadata about the type of a diagnostic. + */ + export enum DiagnosticTag { + /** + * Unused or unnecessary code. + * + * Diagnostics with this tag are rendered faded out. The amount of fading + * is controlled by the `"editorUnnecessaryCode.opacity"` theme color. For + * example, `"editorUnnecessaryCode.opacity": "#000000c0"` will render the + * code with 75% opacity. For high contrast themes, use the + * `"editorUnnecessaryCode.border"` theme color to underline unnecessary code + * instead of fading it out. + */ + Unnecessary = 1, + + /** + * Deprecated or obsolete code. + * + * Diagnostics with this tag are rendered with a strike through. + */ + Deprecated = 2, + } + + /** + * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects + * are only valid in the scope of a file. + */ + export class Diagnostic { + + /** + * The range to which this diagnostic applies. + */ + range: Range; + + /** + * The human-readable message. + */ + message: string; + + /** + * The severity, default is {@link DiagnosticSeverity.Error error}. + */ + severity: DiagnosticSeverity; + + /** + * A human-readable string describing the source of this + * diagnostic, e.g. 'typescript' or 'super lint'. + */ + source?: string; + + /** + * A code or identifier for this diagnostic. + * Should be used for later processing, e.g. when providing {@link CodeActionContext code actions}. + */ + code?: string | number | { + /** + * A code or identifier for this diagnostic. + * Should be used for later processing, e.g. when providing {@link CodeActionContext code actions}. + */ + value: string | number; + + /** + * A target URI to open with more information about the diagnostic error. + */ + target: Uri; + }; + + /** + * An array of related diagnostic information, e.g. when symbol-names within + * a scope collide all definitions can be marked via this property. + */ + relatedInformation?: DiagnosticRelatedInformation[]; + + /** + * Additional metadata about the diagnostic. + */ + tags?: DiagnosticTag[]; + + /** + * Creates a new diagnostic object. + * + * @param range The range to which this diagnostic applies. + * @param message The human-readable message. + * @param severity The severity, default is {@link DiagnosticSeverity.Error error}. + */ + constructor(range: Range, message: string, severity?: DiagnosticSeverity); + } + + /** + * A diagnostics collection is a container that manages a set of + * {@link Diagnostic diagnostics}. Diagnostics are always scopes to a + * diagnostics collection and a resource. + * + * To get an instance of a `DiagnosticCollection` use + * {@link languages.createDiagnosticCollection createDiagnosticCollection}. + */ + export interface DiagnosticCollection extends Iterable<[uri: Uri, diagnostics: readonly Diagnostic[]]> { + + /** + * The name of this diagnostic collection, for instance `typescript`. Every diagnostic + * from this collection will be associated with this name. Also, the task framework uses this + * name when defining [problem matchers](https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher). + */ + readonly name: string; + + /** + * Assign diagnostics for given resource. Will replace + * existing diagnostics for that resource. + * + * @param uri A resource identifier. + * @param diagnostics Array of diagnostics or `undefined` + */ + set(uri: Uri, diagnostics: readonly Diagnostic[] | undefined): void; + + /** + * Replace diagnostics for multiple resources in this collection. + * + * _Note_ that multiple tuples of the same uri will be merged, e.g + * `[[file1, [d1]], [file1, [d2]]]` is equivalent to `[[file1, [d1, d2]]]`. + * If a diagnostics item is `undefined` as in `[file1, undefined]` + * all previous but not subsequent diagnostics are removed. + * + * @param entries An array of tuples, like `[[file1, [d1, d2]], [file2, [d3, d4, d5]]]`, or `undefined`. + */ + set(entries: ReadonlyArray<[Uri, readonly Diagnostic[] | undefined]>): void; + + /** + * Remove all diagnostics from this collection that belong + * to the provided `uri`. The same as `#set(uri, undefined)`. + * + * @param uri A resource identifier. + */ + delete(uri: Uri): void; + + /** + * Remove all diagnostics from this collection. The same + * as calling `#set(undefined)`; + */ + clear(): void; + + /** + * Iterate over each entry in this collection. + * + * @param callback Function to execute for each entry. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callback: (uri: Uri, diagnostics: readonly Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void; + + /** + * Get the diagnostics for a given resource. *Note* that you cannot + * modify the diagnostics-array returned from this call. + * + * @param uri A resource identifier. + * @returns An immutable array of {@link Diagnostic diagnostics} or `undefined`. + */ + get(uri: Uri): readonly Diagnostic[] | undefined; + + /** + * Check if this collection contains diagnostics for a + * given resource. + * + * @param uri A resource identifier. + * @returns `true` if this collection has diagnostic for the given resource. + */ + has(uri: Uri): boolean; + + /** + * Dispose and free associated resources. Calls + * {@link DiagnosticCollection.clear clear}. + */ + dispose(): void; + } + + /** + * Represents the severity of a language status item. + */ + /** + * Represents the severity level of a language status. + */ + export enum LanguageStatusSeverity { + /** + * Informational severity level. + */ + Information = 0, + /** + * Warning severity level. + */ + Warning = 1, + /** + * Error severity level. + */ + Error = 2 + } + + /** + * A language status item is the preferred way to present language status reports for the active text editors, + * such as selected linter or notifying about a configuration problem. + */ + export interface LanguageStatusItem { + + /** + * The identifier of this item. + */ + readonly id: string; + + /** + * The short name of this item, like 'Java Language Status', etc. + */ + name: string | undefined; + + /** + * A {@link DocumentSelector selector} that defines for what editors + * this item shows. + */ + selector: DocumentSelector; + + /** + * The severity of this item. + * + * Defaults to {@link LanguageStatusSeverity.Information information}. You can use this property to + * signal to users that there is a problem that needs attention, like a missing executable or an + * invalid configuration. + */ + severity: LanguageStatusSeverity; + + /** + * The text to show for the entry. You can embed icons in the text by leveraging the syntax: + * + * `My text $(icon-name) contains icons like $(icon-name) this one.` + * + * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. + * `light-bulb`, `thumbsup`, `zap` etc. + */ + text: string; + + /** + * Optional, human-readable details for this item. + */ + detail?: string; + + /** + * Controls whether the item is shown as "busy". Defaults to `false`. + */ + busy: boolean; + + /** + * A {@linkcode Command command} for this item. + */ + command: Command | undefined; + + /** + * Accessibility information used when a screen reader interacts with this item + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * Denotes a location of an editor in the window. Editors can be arranged in a grid + * and each column represents one editor location in that grid by counting the editors + * in order of their appearance. + */ + export enum ViewColumn { + /** + * A *symbolic* editor column representing the currently active column. This value + * can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value + * of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Active`. + */ + Active = -1, + /** + * A *symbolic* editor column representing the column to the side of the active one. This value + * can be used when opening editors, but the *resolved* {@link TextEditor.viewColumn viewColumn}-value + * of editors will always be `One`, `Two`, `Three`,... or `undefined` but never `Beside`. + */ + Beside = -2, + /** + * The first editor column. + */ + One = 1, + /** + * The second editor column. + */ + Two = 2, + /** + * The third editor column. + */ + Three = 3, + /** + * The fourth editor column. + */ + Four = 4, + /** + * The fifth editor column. + */ + Five = 5, + /** + * The sixth editor column. + */ + Six = 6, + /** + * The seventh editor column. + */ + Seven = 7, + /** + * The eighth editor column. + */ + Eight = 8, + /** + * The ninth editor column. + */ + Nine = 9 + } + + /** + * An output channel is a container for readonly textual information. + * + * To get an instance of an `OutputChannel` use + * {@link window.createOutputChannel createOutputChannel}. + */ + export interface OutputChannel { + + /** + * The human-readable name of this output channel. + */ + readonly name: string; + + /** + * Append the given value to the channel. + * + * @param value A string, falsy values will not be printed. + */ + append(value: string): void; + + /** + * Append the given value and a line feed character + * to the channel. + * + * @param value A string, falsy values will be printed. + */ + appendLine(value: string): void; + + /** + * Replaces all output from the channel with the given value. + * + * @param value A string, falsy values will not be printed. + */ + replace(value: string): void; + + /** + * Removes all output from the channel. + */ + clear(): void; + + /** + * Reveal this channel in the UI. + * + * @param preserveFocus When `true` the channel will not take focus. + */ + show(preserveFocus?: boolean): void; + + /** + * Reveal this channel in the UI. + * + * @deprecated Use the overload with just one parameter (`show(preserveFocus?: boolean): void`). + * + * @param column This argument is **deprecated** and will be ignored. + * @param preserveFocus When `true` the channel will not take focus. + */ + show(column?: ViewColumn, preserveFocus?: boolean): void; + + /** + * Hide this channel from the UI. + */ + hide(): void; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * A channel for containing log output. + * + * To get an instance of a `LogOutputChannel` use + * {@link window.createOutputChannel createOutputChannel}. + */ + export interface LogOutputChannel extends OutputChannel { + + /** + * The current log level of the channel. Defaults to {@link env.logLevel editor log level}. + */ + readonly logLevel: LogLevel; + + /** + * An {@link Event} which fires when the log level of the channel changes. + */ + readonly onDidChangeLogLevel: Event; + + /** + * Outputs the given trace message to the channel. Use this method to log verbose information. + * + * The message is only logged if the channel is configured to display {@link LogLevel.Trace trace} log level. + * + * @param message trace message to log + */ + trace(message: string, ...args: any[]): void; + + /** + * Outputs the given debug message to the channel. + * + * The message is only logged if the channel is configured to display {@link LogLevel.Debug debug} log level or lower. + * + * @param message debug message to log + */ + debug(message: string, ...args: any[]): void; + + /** + * Outputs the given information message to the channel. + * + * The message is only logged if the channel is configured to display {@link LogLevel.Info info} log level or lower. + * + * @param message info message to log + */ + info(message: string, ...args: any[]): void; + + /** + * Outputs the given warning message to the channel. + * + * The message is only logged if the channel is configured to display {@link LogLevel.Warning warning} log level or lower. + * + * @param message warning message to log + */ + warn(message: string, ...args: any[]): void; + + /** + * Outputs the given error or error message to the channel. + * + * The message is only logged if the channel is configured to display {@link LogLevel.Error error} log level or lower. + * + * @param error Error or error message to log + */ + error(error: string | Error, ...args: any[]): void; + } + + /** + * Accessibility information which controls screen reader behavior. + */ + export interface AccessibilityInformation { + /** + * Label to be read out by a screen reader once the item has focus. + */ + readonly label: string; + + /** + * Role of the widget which defines how a screen reader interacts with it. + * The role should be set in special cases when for example a tree-like element behaves like a checkbox. + * If role is not specified the editor will pick the appropriate role automatically. + * More about aria roles can be found here https://w3c.github.io/aria/#widget_roles + */ + readonly role?: string; + } + + /** + * Represents the alignment of status bar items. + */ + export enum StatusBarAlignment { + + /** + * Aligned to the left side. + */ + Left = 1, + + /** + * Aligned to the right side. + */ + Right = 2 + } + + /** + * A status bar item is a status bar contribution that can + * show text and icons and run a command on click. + */ + export interface StatusBarItem { + + /** + * The identifier of this item. + * + * *Note*: if no identifier was provided by the {@linkcode window.createStatusBarItem} + * method, the identifier will match the {@link Extension.id extension identifier}. + */ + readonly id: string; + + /** + * The alignment of this item. + */ + readonly alignment: StatusBarAlignment; + + /** + * The priority of this item. Higher value means the item should + * be shown more to the left. + */ + readonly priority: number | undefined; + + /** + * The name of the entry, like 'Python Language Indicator', 'Git Status' etc. + * Try to keep the length of the name short, yet descriptive enough that + * users can understand what the status bar item is about. + */ + name: string | undefined; + + /** + * The text to show for the entry. You can embed icons in the text by leveraging the syntax: + * + * `My text $(icon-name) contains icons like $(icon-name) this one.` + * + * Where the icon-name is taken from the ThemeIcon [icon set](https://code.visualstudio.com/api/references/icons-in-labels#icon-listing), e.g. + * `light-bulb`, `thumbsup`, `zap` etc. + */ + text: string; + + /** + * The tooltip text when you hover over this entry. + */ + tooltip: string | MarkdownString | undefined; + + /** + * The foreground color for this entry. + */ + color: string | ThemeColor | undefined; + + /** + * The background color for this entry. + * + * *Note*: only the following colors are supported: + * * `new ThemeColor('statusBarItem.errorBackground')` + * * `new ThemeColor('statusBarItem.warningBackground')` + * + * More background colors may be supported in the future. + * + * *Note*: when a background color is set, the statusbar may override + * the `color` choice to ensure the entry is readable in all themes. + */ + backgroundColor: ThemeColor | undefined; + + /** + * {@linkcode Command} or identifier of a command to run on click. + * + * The command must be {@link commands.getCommands known}. + * + * Note that if this is a {@linkcode Command} object, only the {@linkcode Command.command command} and {@linkcode Command.arguments arguments} + * are used by the editor. + */ + command: string | Command | undefined; + + /** + * Accessibility information used when a screen reader interacts with this StatusBar item + */ + accessibilityInformation: AccessibilityInformation | undefined; + + /** + * Shows the entry in the status bar. + */ + show(): void; + + /** + * Hide the entry in the status bar. + */ + hide(): void; + + /** + * Dispose and free associated resources. Call + * {@link StatusBarItem.hide hide}. + */ + dispose(): void; + } + + /** + * Defines a generalized way of reporting progress updates. + */ + export interface Progress { + + /** + * Report a progress update. + * @param value A progress item, like a message and/or an + * report on how much work finished + */ + report(value: T): void; + } + + /** + * An individual terminal instance within the integrated terminal. + */ + export interface Terminal { + + /** + * The name of the terminal. + */ + readonly name: string; + + /** + * The process ID of the shell process. + */ + readonly processId: Thenable; + + /** + * The object used to initialize the terminal, this is useful for example to detecting the + * shell type of when the terminal was not launched by this extension or for detecting what + * folder the shell was launched in. + */ + readonly creationOptions: Readonly; + + /** + * The exit status of the terminal, this will be undefined while the terminal is active. + * + * **Example:** Show a notification with the exit code when the terminal exits with a + * non-zero exit code. + * ```typescript + * window.onDidCloseTerminal(t => { + * if (t.exitStatus && t.exitStatus.code) { + * vscode.window.showInformationMessage(`Exit code: ${t.exitStatus.code}`); + * } + * }); + * ``` + */ + readonly exitStatus: TerminalExitStatus | undefined; + + /** + * The current state of the {@link Terminal}. + */ + readonly state: TerminalState; + + /** + * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered + * features for the terminal. This will always be `undefined` immediately after the terminal + * is created. Listen to {@link window.onDidChangeTerminalShellIntegration} to be notified + * when shell integration is activated for a terminal. + * + * Note that this object may remain undefined if shell integration never activates. For + * example Command Prompt does not support shell integration and a user's shell setup could + * conflict with the automatic shell integration activation. + */ + readonly shellIntegration: TerminalShellIntegration | undefined; + + /** + * Send text to the terminal. The text is written to the stdin of the underlying pty process + * (shell) of the terminal. + * + * @param text The text to send. + * @param shouldExecute Indicates that the text being sent should be executed rather than just inserted in the terminal. + * The character(s) added are `\n` or `\r\n`, depending on the platform. This defaults to `true`. + */ + sendText(text: string, shouldExecute?: boolean): void; + + /** + * Show the terminal panel and reveal this terminal in the UI. + * + * @param preserveFocus When `true` the terminal will not take focus. + */ + show(preserveFocus?: boolean): void; + + /** + * Hide the terminal panel if this terminal is currently showing. + */ + hide(): void; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * The location of the terminal. + */ + export enum TerminalLocation { + /** + * In the terminal view + */ + Panel = 1, + /** + * In the editor area + */ + Editor = 2, + } + + /** + * Assumes a {@link TerminalLocation} of editor and allows specifying a {@link ViewColumn} and + * {@link TerminalEditorLocationOptions.preserveFocus preserveFocus } property + */ + export interface TerminalEditorLocationOptions { + /** + * A view column in which the {@link Terminal terminal} should be shown in the editor area. + * The default is the {@link ViewColumn.Active active}. Columns that do not exist + * will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. + * Use {@linkcode ViewColumn.Beside} to open the editor to the side of the currently + * active one. + */ + viewColumn: ViewColumn; + /** + * An optional flag that when `true` will stop the {@link Terminal} from taking focus. + */ + preserveFocus?: boolean; + } + + /** + * Uses the parent {@link Terminal}'s location for the terminal + */ + export interface TerminalSplitLocationOptions { + /** + * The parent terminal to split this terminal beside. This works whether the parent terminal + * is in the panel or the editor area. + */ + parentTerminal: Terminal; + } + + /** + * Represents the state of a {@link Terminal}. + */ + export interface TerminalState { + /** + * Whether the {@link Terminal} has been interacted with. Interaction means that the + * terminal has sent data to the process which depending on the terminal's _mode_. By + * default input is sent when a key is pressed or when a command or extension sends text, + * but based on the terminal's mode it can also happen on: + * + * - a pointer click event + * - a pointer scroll event + * - a pointer move event + * - terminal focus in/out + * + * For more information on events that can send data see "DEC Private Mode Set (DECSET)" on + * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html + */ + readonly isInteractedWith: boolean; + } + + /** + * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. + */ + export interface TerminalShellIntegration { + /** + * The current working directory of the terminal. This {@link Uri} may represent a file on + * another machine (eg. ssh into another machine). This requires the shell integration to + * support working directory reporting. + */ + readonly cwd: Uri | undefined; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * @param commandLine The command line to execute, this is the exact text that will be sent + * to the terminal. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidChangeTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const execution = shellIntegration.executeCommand('echo "Hello world"'); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * console.log(`Command exited with code ${event.exitCode}`); + * } + * }); + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const execution = shellIntegration.executeCommand({ commandLine }); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * console.log(`Command exited with code ${event.exitCode}`); + * } + * }); + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + */ + executeCommand(commandLine: string): TerminalShellExecution; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) + * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to + * verify whether it was successful. + * + * @param executable A command to run. + * @param args Arguments to launch the executable with. The arguments will be escaped such + * that they are interpreted as single arguments when the argument both contains whitespace + * and does not include any single quote, double quote or backtick characters. + * + * Note that this escaping is not intended to be a security measure, be careful when passing + * untrusted data to this API as strings like `$(...)` can often be used in shells to + * execute code within a string. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidChangeTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const command = shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const command = term.shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + */ + executeCommand(executable: string, args: string[]): TerminalShellExecution; + } + + /** + * A command that was executed in a terminal. + */ + export interface TerminalShellExecution { + /** + * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence} + * of this value depends on the specific shell's shell integration implementation. This + * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is + * fired. + * + * @example + * // Log the details of the command line on start and end + * window.onDidStartTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command started\n${summarizeCommandLine(commandLine)}`); + * }); + * window.onDidEndTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command ended\n${summarizeCommandLine(commandLine)}`); + * }); + * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { + * return [ + * ` Command line: ${command.commandLine.value}`, + * ` Confidence: ${command.commandLine.confidence}`, + * ` Trusted: ${command.commandLine.isTrusted} + * ].join('\n'); + * } + */ + readonly commandLine: TerminalShellExecutionCommandLine; + + /** + * The working directory that was reported by the shell when this command executed. This + * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This + * requires the shell integration to support working directory reporting. + */ + readonly cwd: Uri | undefined; + + /** + * Creates a stream of raw data (including escape sequences) that is written to the + * terminal. This will only include data that was written after `read` was called for + * the first time, ie. you must call `read` immediately after the command is executed via + * {@link TerminalShellIntegration.executeCommand} or + * {@link window.onDidStartTerminalShellExecution} to not miss any data. + * + * @example + * // Log all data written to the terminal for a command + * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); + * const stream = command.read(); + * for await (const data of stream) { + * console.log(data); + * } + */ + read(): AsyncIterable; + } + + /** + * A command line that was executed in a terminal. + */ + export interface TerminalShellExecutionCommandLine { + /** + * The full command line that was executed, including both the command and its arguments. + */ + readonly value: string; + + /** + * Whether the command line value came from a trusted source and is therefore safe to + * execute without user additional confirmation, such as a notification that asks "Do you + * want to execute (command)?". This verification is likely only needed if you are going to + * execute the command again. + * + * This is `true` only when the command line was reported explicitly by the shell + * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}) + * and it used a nonce for verification. + */ + readonly isTrusted: boolean; + + /** + * The confidence of the command line value which is determined by how the value was + * obtained. This depends upon the implementation of the shell integration script. + */ + readonly confidence: TerminalShellExecutionCommandLineConfidence; + } + + /** + * The confidence of a {@link TerminalShellExecutionCommandLine} value. + */ + enum TerminalShellExecutionCommandLineConfidence { + /** + * The command line value confidence is low. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. Additionally one + * of the following conditions will be met: + * + * - The command started on the very left-most column which is unusual, or + * - The command is multi-line which is more difficult to accurately detect due to line + * continuation characters and right prompts. + * - Command line markers were not reported by the shell integration script. + */ + Low = 0, + + /** + * The command line value confidence is medium. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. The command is + * single-line and does not start on the very left-most column (which is unusual). + */ + Medium = 1, + + /** + * The command line value confidence is high. This means that the value was explicitly sent + * from the shell integration script or the command was executed via the + * {@link TerminalShellIntegration.executeCommand} API. + */ + High = 2 + } + + /** + * An event signalling that a terminal's shell integration has changed. + */ + export interface TerminalShellIntegrationChangeEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + } + + /** + * An event signalling that an execution has started in a terminal. + */ + export interface TerminalShellExecutionStartEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + */ + readonly execution: TerminalShellExecution; + } + + /** + * An event signalling that an execution has ended in a terminal. + */ + export interface TerminalShellExecutionEndEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + */ + readonly execution: TerminalShellExecution; + + /** + * The exit code reported by the shell. + * + * Note that `undefined` means the shell either did not report an exit code (ie. the shell + * integration script is misbehaving) or the shell reported a command started before the command + * finished (eg. a sub-shell was opened). Generally this should not happen, depending on the use + * case, it may be best to treat this as a failure. + * + * @example + * const execution = shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * if (event.exitCode === undefined) { + * console.log('Command finished but exit code is unknown'); + * } else if (event.exitCode === 0) { + * console.log('Command succeeded'); + * } else { + * console.log('Command failed'); + * } + * } + * }); + */ + readonly exitCode: number | undefined; + } + + /** + * Provides information on a line in a terminal in order to provide links for it. + */ + export interface TerminalLinkContext { + /** + * This is the text from the unwrapped line in the terminal. + */ + line: string; + + /** + * The terminal the link belongs to. + */ + terminal: Terminal; + } + + /** + * A provider that enables detection and handling of links within terminals. + */ + export interface TerminalLinkProvider { + /** + * Provide terminal links for the given context. Note that this can be called multiple times + * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) + * that could have problems when asynchronous usage may overlap. + * @param context Information about what links are being provided for. + * @param token A cancellation token. + * @returns A list of terminal links for the given line. + */ + provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult; + + /** + * Handle an activated terminal link. + * @param link The link to handle. + */ + handleTerminalLink(link: T): ProviderResult; + } + + /** + * A link on a terminal line. + */ + export class TerminalLink { + /** + * The start index of the link on {@link TerminalLinkContext.line}. + */ + startIndex: number; + + /** + * The length of the link on {@link TerminalLinkContext.line}. + */ + length: number; + + /** + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on + * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary + * depending on OS, user settings, and localization. + */ + tooltip?: string; + + /** + * Creates a new terminal link. + * @param startIndex The start index of the link on {@link TerminalLinkContext.line}. + * @param length The length of the link on {@link TerminalLinkContext.line}. + * @param tooltip The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on + * how to trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary + * depending on OS, user settings, and localization. + */ + constructor(startIndex: number, length: number, tooltip?: string); + } + + /** + * Provides a terminal profile for the contributed terminal profile when launched via the UI or + * command. + */ + export interface TerminalProfileProvider { + /** + * Provide the terminal profile. + * @param token A cancellation token that indicates the result is no longer needed. + * @returns The terminal profile. + */ + provideTerminalProfile(token: CancellationToken): ProviderResult; + } + + /** + * A terminal profile defines how a terminal will be launched. + */ + export class TerminalProfile { + /** + * The options that the terminal will launch with. + */ + options: TerminalOptions | ExtensionTerminalOptions; + + /** + * Creates a new terminal profile. + * @param options The options that the terminal will launch with. + */ + constructor(options: TerminalOptions | ExtensionTerminalOptions); + } + + /** + * A file decoration represents metadata that can be rendered with a file. + */ + export class FileDecoration { + + /** + * A very short string that represents this decoration. + */ + badge?: string; + + /** + * A human-readable tooltip for this decoration. + */ + tooltip?: string; + + /** + * The color of this decoration. + */ + color?: ThemeColor; + + /** + * A flag expressing that this decoration should be + * propagated to its parents. + */ + propagate?: boolean; + + /** + * Creates a new decoration. + * + * @param badge A letter that represents the decoration. + * @param tooltip The tooltip of the decoration. + * @param color The color of the decoration. + */ + constructor(badge?: string, tooltip?: string, color?: ThemeColor); + } + + /** + * The decoration provider interfaces defines the contract between extensions and + * file decorations. + */ + export interface FileDecorationProvider { + + /** + * An optional event to signal that decorations for one or many files have changed. + * + * *Note* that this event should be used to propagate information about children. + * + * @see {@link EventEmitter} + */ + onDidChangeFileDecorations?: Event; + + /** + * Provide decorations for a given uri. + * + * *Note* that this function is only called when a file gets rendered in the UI. + * This means a decoration from a descendent that propagates upwards must be signaled + * to the editor via the {@link FileDecorationProvider.onDidChangeFileDecorations onDidChangeFileDecorations}-event. + * + * @param uri The uri of the file to provide a decoration for. + * @param token A cancellation token. + * @returns A decoration or a thenable that resolves to such. + */ + provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; + } + + + /** + * In a remote window the extension kind describes if an extension + * runs where the UI (window) runs or if an extension runs remotely. + */ + export enum ExtensionKind { + + /** + * Extension runs where the UI runs. + */ + UI = 1, + + /** + * Extension runs where the remote extension host runs. + */ + Workspace = 2 + } + + /** + * Represents an extension. + * + * To get an instance of an `Extension` use {@link extensions.getExtension getExtension}. + */ + export interface Extension { + + /** + * The canonical extension identifier in the form of: `publisher.name`. + */ + readonly id: string; + + /** + * The uri of the directory containing the extension. + */ + readonly extensionUri: Uri; + + /** + * The absolute file path of the directory containing this extension. Shorthand + * notation for {@link Extension.extensionUri Extension.extensionUri.fsPath} (independent of the uri scheme). + */ + readonly extensionPath: string; + + /** + * `true` if the extension has been activated. + */ + readonly isActive: boolean; + + /** + * The parsed contents of the extension's package.json. + */ + readonly packageJSON: any; + + /** + * The extension kind describes if an extension runs where the UI runs + * or if an extension runs where the remote extension host runs. The extension kind + * is defined in the `package.json`-file of extensions but can also be refined + * via the `remote.extensionKind`-setting. When no remote extension host exists, + * the value is {@linkcode ExtensionKind.UI}. + */ + extensionKind: ExtensionKind; + + /** + * The public API exported by this extension (return value of `activate`). + * It is an invalid action to access this field before this extension has been activated. + */ + readonly exports: T; + + /** + * Activates this extension and returns its public API. + * + * @returns A promise that will resolve when this extension has been activated. + */ + activate(): Thenable; + } + + /** + * The ExtensionMode is provided on the `ExtensionContext` and indicates the + * mode the specific extension is running in. + */ + export enum ExtensionMode { + /** + * The extension is installed normally (for example, from the marketplace + * or VSIX) in the editor. + */ + Production = 1, + + /** + * The extension is running from an `--extensionDevelopmentPath` provided + * when launching the editor. + */ + Development = 2, + + /** + * The extension is running from an `--extensionTestsPath` and + * the extension host is running unit tests. + */ + Test = 3, + } + + /** + * An extension context is a collection of utilities private to an + * extension. + * + * An instance of an `ExtensionContext` is provided as the first + * parameter to the `activate`-call of an extension. + */ + export interface ExtensionContext { + + /** + * An array to which disposables can be added. When this + * extension is deactivated the disposables will be disposed. + * + * *Note* that asynchronous dispose-functions aren't awaited. + */ + readonly subscriptions: { + /** + * Function to clean up resources. + */ + dispose(): any; + }[]; + + /** + * A memento object that stores state in the context + * of the currently opened {@link workspace.workspaceFolders workspace}. + */ + readonly workspaceState: Memento; + + /** + * A memento object that stores state independent + * of the current opened {@link workspace.workspaceFolders workspace}. + */ + readonly globalState: Memento & { + /** + * Set the keys whose values should be synchronized across devices when synchronizing user-data + * like configuration, extensions, and mementos. + * + * Note that this function defines the whole set of keys whose values are synchronized: + * - calling it with an empty array stops synchronization for this memento + * - calling it with a non-empty array replaces all keys whose values are synchronized + * + * For any given set of keys this function needs to be called only once but there is no harm in + * repeatedly calling it. + * + * @param keys The set of keys whose values are synced. + */ + setKeysForSync(keys: readonly string[]): void; + }; + + /** + * A secret storage object that stores state independent + * of the current opened {@link workspace.workspaceFolders workspace}. + */ + readonly secrets: SecretStorage; + + /** + * The uri of the directory containing the extension. + */ + readonly extensionUri: Uri; + + /** + * The absolute file path of the directory containing the extension. Shorthand + * notation for {@link TextDocument.uri ExtensionContext.extensionUri.fsPath} (independent of the uri scheme). + */ + readonly extensionPath: string; + + /** + * Gets the extension's global environment variable collection for this workspace, enabling changes to be + * applied to terminal environment variables. + */ + readonly environmentVariableCollection: GlobalEnvironmentVariableCollection; + + /** + * Get the absolute path of a resource contained in the extension. + * + * *Note* that an absolute uri can be constructed via {@linkcode Uri.joinPath} and + * {@linkcode ExtensionContext.extensionUri extensionUri}, e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);` + * + * @param relativePath A relative path to a resource contained in the extension. + * @returns The absolute path of the resource. + */ + asAbsolutePath(relativePath: string): string; + + /** + * The uri of a workspace specific directory in which the extension + * can store private state. The directory might not exist and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * The value is `undefined` when no workspace nor folder has been opened. + * + * Use {@linkcode ExtensionContext.workspaceState workspaceState} or + * {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from + * an uri. + */ + readonly storageUri: Uri | undefined; + + /** + * An absolute file path of a workspace specific directory in which the extension + * can store private state. The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use {@linkcode ExtensionContext.workspaceState workspaceState} or + * {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @deprecated Use {@link ExtensionContext.storageUri storageUri} instead. + */ + readonly storagePath: string | undefined; + + /** + * The uri of a directory in which the extension can store global state. + * The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from + * an uri. + */ + readonly globalStorageUri: Uri; + + /** + * An absolute file path in which the extension can store global state. + * The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use {@linkcode ExtensionContext.globalState globalState} to store key value data. + * + * @deprecated Use {@link ExtensionContext.globalStorageUri globalStorageUri} instead. + */ + readonly globalStoragePath: string; + + /** + * The uri of a directory in which the extension can create log files. + * The directory might not exist on disk and creation is up to the extension. However, + * the parent directory is guaranteed to be existent. + * + * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from + * an uri. + */ + readonly logUri: Uri; + + /** + * An absolute file path of a directory in which the extension can create log files. + * The directory might not exist on disk and creation is up to the extension. However, + * the parent directory is guaranteed to be existent. + * + * @deprecated Use {@link ExtensionContext.logUri logUri} instead. + */ + readonly logPath: string; + + /** + * The mode the extension is running in. See {@link ExtensionMode} + * for possible values and scenarios. + */ + readonly extensionMode: ExtensionMode; + + /** + * The current `Extension` instance. + */ + readonly extension: Extension; + + /** + * An object that keeps information about how this extension can use language models. + * + * @see {@link LanguageModelChat.sendRequest} + */ + readonly languageModelAccessInformation: LanguageModelAccessInformation; + } + + /** + * A memento represents a storage utility. It can store and retrieve + * values. + */ + export interface Memento { + + /** + * Returns the stored keys. + * + * @returns The stored keys. + */ + keys(): readonly string[]; + + /** + * Return a value. + * + * @param key A string. + * @returns The stored value or `undefined`. + */ + get(key: string): T | undefined; + + /** + * Return a value. + * + * @param key A string. + * @param defaultValue A value that should be returned when there is no + * value (`undefined`) with the given key. + * @returns The stored value or the defaultValue. + */ + get(key: string, defaultValue: T): T; + + /** + * Store a value. The value must be JSON-stringifyable. + * + * *Note* that using `undefined` as value removes the key from the underlying + * storage. + * + * @param key A string. + * @param value A value. MUST not contain cyclic references. + */ + update(key: string, value: any): Thenable; + } + + /** + * The event data that is fired when a secret is added or removed. + */ + export interface SecretStorageChangeEvent { + /** + * The key of the secret that has changed. + */ + readonly key: string; + } + + /** + * Represents a storage utility for secrets (or any information that is sensitive) + * that will be stored encrypted. The implementation of the secret storage will + * be different on each platform and the secrets will not be synced across + * machines. + */ + export interface SecretStorage { + /** + * Retrieve a secret that was stored with key. Returns undefined if there + * is no password matching that key. + * @param key The key the secret was stored under. + * @returns The stored value or `undefined`. + */ + get(key: string): Thenable; + + /** + * Store a secret under a given key. + * @param key The key to store the secret under. + * @param value The secret. + */ + store(key: string, value: string): Thenable; + + /** + * Remove a secret from storage. + * @param key The key the secret was stored under. + */ + delete(key: string): Thenable; + + /** + * Fires when a secret is stored or deleted. + */ + onDidChange: Event; + } + + /** + * Represents a color theme kind. + */ + export enum ColorThemeKind { + /** + * A light color theme. + */ + Light = 1, + /** + * A dark color theme. + */ + Dark = 2, + /** + * A dark high contrast color theme. + */ + HighContrast = 3, + /** + * A light high contrast color theme. + */ + HighContrastLight = 4 + } + + /** + * Represents a color theme. + */ + export interface ColorTheme { + + /** + * The kind of this color theme: light, dark, high contrast dark and high contrast light. + */ + readonly kind: ColorThemeKind; + } + + /** + * Controls the behaviour of the terminal's visibility. + */ + export enum TaskRevealKind { + /** + * Always brings the terminal to front if the task is executed. + */ + Always = 1, + + /** + * Only brings the terminal to front if a problem is detected executing the task + * (e.g. the task couldn't be started because). + */ + Silent = 2, + + /** + * The terminal never comes to front when the task is executed. + */ + Never = 3 + } + + /** + * Controls how the task channel is used between tasks + */ + export enum TaskPanelKind { + + /** + * Shares a panel with other tasks. This is the default. + */ + Shared = 1, + + /** + * Uses a dedicated panel for this tasks. The panel is not + * shared with other tasks. + */ + Dedicated = 2, + + /** + * Creates a new panel whenever this task is executed. + */ + New = 3 + } + + /** + * Controls how the task is presented in the UI. + */ + export interface TaskPresentationOptions { + /** + * Controls whether the task output is reveal in the user interface. + * Defaults to `RevealKind.Always`. + */ + reveal?: TaskRevealKind; + + /** + * Controls whether the command associated with the task is echoed + * in the user interface. + */ + echo?: boolean; + + /** + * Controls whether the panel showing the task output is taking focus. + */ + focus?: boolean; + + /** + * Controls if the task panel is used for this task only (dedicated), + * shared between tasks (shared) or if a new panel is created on + * every task execution (new). Defaults to `TaskInstanceKind.Shared` + */ + panel?: TaskPanelKind; + + /** + * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message. + */ + showReuseMessage?: boolean; + + /** + * Controls whether the terminal is cleared before executing the task. + */ + clear?: boolean; + + /** + * Controls whether the terminal is closed after executing the task. + */ + close?: boolean; + } + + /** + * A grouping for tasks. The editor by default supports the + * 'Clean', 'Build', 'RebuildAll' and 'Test' group. + */ + export class TaskGroup { + + /** + * The clean task group; + */ + static Clean: TaskGroup; + + /** + * The build task group; + */ + static Build: TaskGroup; + + /** + * The rebuild all task group; + */ + static Rebuild: TaskGroup; + + /** + * The test all task group; + */ + static Test: TaskGroup; + + /** + * Whether the task that is part of this group is the default for the group. + * This property cannot be set through API, and is controlled by a user's task configurations. + */ + readonly isDefault: boolean | undefined; + + /** + * The ID of the task group. Is one of TaskGroup.Clean.id, TaskGroup.Build.id, TaskGroup.Rebuild.id, or TaskGroup.Test.id. + */ + readonly id: string; + + /** + * Private constructor + * + * @param id Identifier of a task group. + * @param label The human-readable name of a task group. + */ + private constructor(id: string, label: string); + } + + /** + * A structure that defines a task kind in the system. + * The value must be JSON-stringifyable. + */ + export interface TaskDefinition { + /** + * The task definition describing the task provided by an extension. + * Usually a task provider defines more properties to identify + * a task. They need to be defined in the package.json of the + * extension under the 'taskDefinitions' extension point. The npm + * task definition for example looks like this + * ```typescript + * interface NpmTaskDefinition extends TaskDefinition { + * script: string; + * } + * ``` + * + * Note that type identifier starting with a '$' are reserved for internal + * usages and shouldn't be used by extensions. + */ + readonly type: string; + + /** + * Additional attributes of a concrete task definition. + */ + [name: string]: any; + } + + /** + * Options for a process execution + */ + export interface ProcessExecutionOptions { + /** + * The current working directory of the executed program or shell. + * If omitted the tools current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + } + + /** + * The execution of a task happens as an external process + * without shell interaction. + */ + export class ProcessExecution { + + /** + * Creates a process execution. + * + * @param process The process to start. + * @param options Optional options for the started process. + */ + constructor(process: string, options?: ProcessExecutionOptions); + + /** + * Creates a process execution. + * + * @param process The process to start. + * @param args Arguments to be passed to the process. + * @param options Optional options for the started process. + */ + constructor(process: string, args: string[], options?: ProcessExecutionOptions); + + /** + * The process to be executed. + */ + process: string; + + /** + * The arguments passed to the process. Defaults to an empty array. + */ + args: string[]; + + /** + * The process options used when the process is executed. + * Defaults to undefined. + */ + options?: ProcessExecutionOptions; + } + + /** + * The shell quoting options. + */ + export interface ShellQuotingOptions { + + /** + * The character used to do character escaping. If a string is provided only spaces + * are escaped. If a `{ escapeChar, charsToEscape }` literal is provide all characters + * in `charsToEscape` are escaped using the `escapeChar`. + */ + escape?: string | { + /** + * The escape character. + */ + escapeChar: string; + /** + * The characters to escape. + */ + charsToEscape: string; + }; + + /** + * The character used for strong quoting. The string's length must be 1. + */ + strong?: string; + + /** + * The character used for weak quoting. The string's length must be 1. + */ + weak?: string; + } + + /** + * Options for a shell execution + */ + export interface ShellExecutionOptions { + /** + * The shell executable. + */ + executable?: string; + + /** + * The arguments to be passed to the shell executable used to run the task. Most shells + * require special arguments to execute a command. For example `bash` requires the `-c` + * argument to execute a command, `PowerShell` requires `-Command` and `cmd` requires both + * `/d` and `/c`. + */ + shellArgs?: string[]; + + /** + * The shell quotes supported by this shell. + */ + shellQuoting?: ShellQuotingOptions; + + /** + * The current working directory of the executed shell. + * If omitted the tools current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + } + + /** + * Defines how an argument should be quoted if it contains + * spaces or unsupported characters. + */ + export enum ShellQuoting { + + /** + * Character escaping should be used. This for example + * uses \ on bash and ` on PowerShell. + */ + Escape = 1, + + /** + * Strong string quoting should be used. This for example + * uses " for Windows cmd and ' for bash and PowerShell. + * Strong quoting treats arguments as literal strings. + * Under PowerShell echo 'The value is $(2 * 3)' will + * print `The value is $(2 * 3)` + */ + Strong = 2, + + /** + * Weak string quoting should be used. This for example + * uses " for Windows cmd, bash and PowerShell. Weak quoting + * still performs some kind of evaluation inside the quoted + * string. Under PowerShell echo "The value is $(2 * 3)" + * will print `The value is 6` + */ + Weak = 3 + } + + /** + * A string that will be quoted depending on the used shell. + */ + export interface ShellQuotedString { + /** + * The actual string value. + */ + value: string; + + /** + * The quoting style to use. + */ + quoting: ShellQuoting; + } + + /** + * Represents a task execution that happens inside a shell. + */ + export class ShellExecution { + /** + * Creates a shell execution with a full command line. + * + * @param commandLine The command line to execute. + * @param options Optional options for the started the shell. + */ + constructor(commandLine: string, options?: ShellExecutionOptions); + + /** + * Creates a shell execution with a command and arguments. For the real execution the editor will + * construct a command line from the command and the arguments. This is subject to interpretation + * especially when it comes to quoting. If full control over the command line is needed please + * use the constructor that creates a `ShellExecution` with the full command line. + * + * @param command The command to execute. + * @param args The command arguments. + * @param options Optional options for the started the shell. + */ + constructor(command: string | ShellQuotedString, args: Array, options?: ShellExecutionOptions); + + /** + * The shell command line. Is `undefined` if created with a command and arguments. + */ + commandLine: string | undefined; + + /** + * The shell command. Is `undefined` if created with a full command line. + */ + command: string | ShellQuotedString; + + /** + * The shell args. Is `undefined` if created with a full command line. + */ + args: Array; + + /** + * The shell options used when the command line is executed in a shell. + * Defaults to undefined. + */ + options?: ShellExecutionOptions; + } + + /** + * Class used to execute an extension callback as a task. + */ + export class CustomExecution { + /** + * Constructs a CustomExecution task object. The callback will be executed when the task is run, at which point the + * extension should return the Pseudoterminal it will "run in". The task should wait to do further execution until + * {@link Pseudoterminal.open} is called. Task cancellation should be handled using + * {@link Pseudoterminal.close}. When the task is complete fire + * {@link Pseudoterminal.onDidClose}. + * @param callback The callback that will be called when the task is started by a user. Any ${} style variables that + * were in the task definition will be resolved and passed into the callback as `resolvedDefinition`. + */ + constructor(callback: (resolvedDefinition: TaskDefinition) => Thenable); + } + + /** + * The scope of a task. + */ + export enum TaskScope { + /** + * The task is a global task. Global tasks are currently not supported. + */ + Global = 1, + + /** + * The task is a workspace task + */ + Workspace = 2 + } + + /** + * Run options for a task. + */ + export interface RunOptions { + /** + * Controls whether task variables are re-evaluated on rerun. + */ + reevaluateOnRerun?: boolean; + } + + /** + * A task to execute + */ + export class Task { + + /** + * Creates a new task. + * + * @param taskDefinition The task definition as defined in the taskDefinitions extension point. + * @param scope Specifies the task's scope. It is either a global or a workspace task or a task for a specific workspace folder. Global tasks are currently not supported. + * @param name The task's name. Is presented in the user interface. + * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. + * @param execution The process or shell execution. + * @param problemMatchers the names of problem matchers to use, like '$tsc' + * or '$eslint'. Problem matchers can be contributed by an extension using + * the `problemMatchers` extension point. + */ + constructor(taskDefinition: TaskDefinition, scope: WorkspaceFolder | TaskScope.Global | TaskScope.Workspace, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]); + + /** + * Creates a new task. + * + * @deprecated Use the new constructors that allow specifying a scope for the task. + * + * @param taskDefinition The task definition as defined in the taskDefinitions extension point. + * @param name The task's name. Is presented in the user interface. + * @param source The task's source (e.g. 'gulp', 'npm', ...). Is presented in the user interface. + * @param execution The process or shell execution. + * @param problemMatchers the names of problem matchers to use, like '$tsc' + * or '$eslint'. Problem matchers can be contributed by an extension using + * the `problemMatchers` extension point. + */ + constructor(taskDefinition: TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); + + /** + * The task's definition. + */ + definition: TaskDefinition; + + /** + * The task's scope. + */ + readonly scope: TaskScope.Global | TaskScope.Workspace | WorkspaceFolder | undefined; + + /** + * The task's name + */ + name: string; + + /** + * A human-readable string which is rendered less prominently on a separate line in places + * where the task's name is displayed. Supports rendering of {@link ThemeIcon theme icons} + * via the `$()`-syntax. + */ + detail?: string; + + /** + * The task's execution engine + */ + execution?: ProcessExecution | ShellExecution | CustomExecution; + + /** + * Whether the task is a background task or not. + */ + isBackground: boolean; + + /** + * A human-readable string describing the source of this shell task, e.g. 'gulp' + * or 'npm'. Supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax. + */ + source: string; + + /** + * The task group this tasks belongs to. See TaskGroup + * for a predefined set of available groups. + * Defaults to undefined meaning that the task doesn't + * belong to any special group. + */ + group?: TaskGroup; + + /** + * The presentation options. Defaults to an empty literal. + */ + presentationOptions: TaskPresentationOptions; + + /** + * The problem matchers attached to the task. Defaults to an empty + * array. + */ + problemMatchers: string[]; + + /** + * Run options for the task + */ + runOptions: RunOptions; + } + + /** + * A task provider allows to add tasks to the task service. + * A task provider is registered via {@link tasks.registerTaskProvider}. + */ + export interface TaskProvider { + /** + * Provides tasks. + * @param token A cancellation token. + * @returns an array of tasks + */ + provideTasks(token: CancellationToken): ProviderResult; + + /** + * Resolves a task that has no {@linkcode Task.execution execution} set. Tasks are + * often created from information found in the `tasks.json`-file. Such tasks miss + * the information on how to execute them and a task provider must fill in + * the missing information in the `resolveTask`-method. This method will not be + * called for tasks returned from the above `provideTasks` method since those + * tasks are always fully resolved. A valid default implementation for the + * `resolveTask` method is to return `undefined`. + * + * Note that when filling in the properties of `task`, you _must_ be sure to + * use the exact same `TaskDefinition` and not create a new one. Other properties + * may be changed. + * + * @param task The task to resolve. + * @param token A cancellation token. + * @returns The resolved task + */ + resolveTask(task: T, token: CancellationToken): ProviderResult; + } + + /** + * An object representing an executed Task. It can be used + * to terminate a task. + * + * This interface is not intended to be implemented. + */ + export interface TaskExecution { + /** + * The task that got started. + */ + task: Task; + + /** + * Terminates the task execution. + */ + terminate(): void; + } + + /** + * An event signaling the start of a task execution. + * + * This interface is not intended to be implemented. + */ + interface TaskStartEvent { + /** + * The task item representing the task that got started. + */ + readonly execution: TaskExecution; + } + + /** + * An event signaling the end of an executed task. + * + * This interface is not intended to be implemented. + */ + interface TaskEndEvent { + /** + * The task item representing the task that finished. + */ + readonly execution: TaskExecution; + } + + /** + * An event signaling the start of a process execution + * triggered through a task + */ + export interface TaskProcessStartEvent { + + /** + * The task execution for which the process got started. + */ + readonly execution: TaskExecution; + + /** + * The underlying process id. + */ + readonly processId: number; + } + + /** + * An event signaling the end of a process execution + * triggered through a task + */ + export interface TaskProcessEndEvent { + + /** + * The task execution for which the process got started. + */ + readonly execution: TaskExecution; + + /** + * The process's exit code. Will be `undefined` when the task is terminated. + */ + readonly exitCode: number | undefined; + } + + /** + * A task filter denotes tasks by their version and types + */ + export interface TaskFilter { + /** + * The task version as used in the tasks.json file. + * The string support the package.json semver notation. + */ + version?: string; + + /** + * The task type to return; + */ + type?: string; + } + + /** + * Namespace for tasks functionality. + */ + export namespace tasks { + + /** + * Register a task provider. + * + * @param type The task kind type this provider is registered for. + * @param provider A task provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; + + /** + * Fetches all tasks available in the systems. This includes tasks + * from `tasks.json` files as well as tasks from task providers + * contributed through extensions. + * + * @param filter Optional filter to select tasks of a certain type or version. + * @returns A thenable that resolves to an array of tasks. + */ + export function fetchTasks(filter?: TaskFilter): Thenable; + + /** + * Executes a task that is managed by the editor. The returned + * task execution can be used to terminate the task. + * + * @throws When running a ShellExecution or a ProcessExecution + * task in an environment where a new process cannot be started. + * In such an environment, only CustomExecution tasks can be run. + * + * @param task the task to execute + * @returns A thenable that resolves to a task execution. + */ + export function executeTask(task: Task): Thenable; + + /** + * The currently active task executions or an empty array. + */ + export const taskExecutions: readonly TaskExecution[]; + + /** + * Fires when a task starts. + */ + export const onDidStartTask: Event; + + /** + * Fires when a task ends. + */ + export const onDidEndTask: Event; + + /** + * Fires when the underlying process has been started. + * This event will not fire for tasks that don't + * execute an underlying process. + */ + export const onDidStartTaskProcess: Event; + + /** + * Fires when the underlying process has ended. + * This event will not fire for tasks that don't + * execute an underlying process. + */ + export const onDidEndTaskProcess: Event; + } + + /** + * Enumeration of file types. The types `File` and `Directory` can also be + * a symbolic links, in that case use `FileType.File | FileType.SymbolicLink` and + * `FileType.Directory | FileType.SymbolicLink`. + */ + export enum FileType { + /** + * The file type is unknown. + */ + Unknown = 0, + /** + * A regular file. + */ + File = 1, + /** + * A directory. + */ + Directory = 2, + /** + * A symbolic link to a file. + */ + SymbolicLink = 64 + } + + /** + * Permissions of a file. + */ + export enum FilePermission { + /** + * The file is readonly. + * + * *Note:* All `FileStat` from a `FileSystemProvider` that is registered with + * the option `isReadonly: true` will be implicitly handled as if `FilePermission.Readonly` + * is set. As a consequence, it is not possible to have a readonly file system provider + * registered where some `FileStat` are not readonly. + */ + Readonly = 1 + } + + /** + * The `FileStat`-type represents metadata about a file + */ + export interface FileStat { + /** + * The type of the file, e.g. is a regular file, a directory, or symbolic link + * to a file. + * + * *Note:* This value might be a bitmask, e.g. `FileType.File | FileType.SymbolicLink`. + */ + type: FileType; + /** + * The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + */ + ctime: number; + /** + * The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC. + * + * *Note:* If the file changed, it is important to provide an updated `mtime` that advanced + * from the previous value. Otherwise there may be optimizations in place that will not show + * the updated file contents in an editor for example. + */ + mtime: number; + /** + * The size in bytes. + * + * *Note:* If the file changed, it is important to provide an updated `size`. Otherwise there + * may be optimizations in place that will not show the updated file contents in an editor for + * example. + */ + size: number; + /** + * The permissions of the file, e.g. whether the file is readonly. + * + * *Note:* This value might be a bitmask, e.g. `FilePermission.Readonly | FilePermission.Other`. + */ + permissions?: FilePermission; + } + + /** + * A type that filesystem providers should use to signal errors. + * + * This class has factory methods for common error-cases, like `FileNotFound` when + * a file or folder doesn't exist, use them like so: `throw vscode.FileSystemError.FileNotFound(someUri);` + */ + export class FileSystemError extends Error { + + /** + * Create an error to signal that a file or folder wasn't found. + * @param messageOrUri Message or uri. + */ + static FileNotFound(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that a file or folder already exists, e.g. when + * creating but not overwriting a file. + * @param messageOrUri Message or uri. + */ + static FileExists(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that a file is not a folder. + * @param messageOrUri Message or uri. + */ + static FileNotADirectory(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that a file is a folder. + * @param messageOrUri Message or uri. + */ + static FileIsADirectory(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that an operation lacks required permissions. + * @param messageOrUri Message or uri. + */ + static NoPermissions(messageOrUri?: string | Uri): FileSystemError; + + /** + * Create an error to signal that the file system is unavailable or too busy to + * complete a request. + * @param messageOrUri Message or uri. + */ + static Unavailable(messageOrUri?: string | Uri): FileSystemError; + + /** + * Creates a new filesystem error. + * + * @param messageOrUri Message or uri. + */ + constructor(messageOrUri?: string | Uri); + + /** + * A code that identifies this error. + * + * Possible values are names of errors, like {@linkcode FileSystemError.FileNotFound FileNotFound}, + * or `Unknown` for unspecified errors. + */ + readonly code: string; + } + + /** + * Enumeration of file change types. + */ + export enum FileChangeType { + + /** + * The contents or metadata of a file have changed. + */ + Changed = 1, + + /** + * A file has been created. + */ + Created = 2, + + /** + * A file has been deleted. + */ + Deleted = 3, + } + + /** + * The event filesystem providers must use to signal a file change. + */ + export interface FileChangeEvent { + + /** + * The type of change. + */ + readonly type: FileChangeType; + + /** + * The uri of the file that has changed. + */ + readonly uri: Uri; + } + + /** + * The filesystem provider defines what the editor needs to read, write, discover, + * and to manage files and folders. It allows extensions to serve files from remote places, + * like ftp-servers, and to seamlessly integrate those into the editor. + * + * * *Note 1:* The filesystem provider API works with {@link Uri uris} and assumes hierarchical + * paths, e.g. `foo:/my/path` is a child of `foo:/my/` and a parent of `foo:/my/path/deeper`. + * * *Note 2:* There is an activation event `onFileSystem:` that fires when a file + * or folder is being accessed. + * * *Note 3:* The word 'file' is often used to denote all {@link FileType kinds} of files, e.g. + * folders, symbolic links, and regular files. + */ + export interface FileSystemProvider { + + /** + * An event to signal that a resource has been created, changed, or deleted. This + * event should fire for resources that are being {@link FileSystemProvider.watch watched} + * by clients of this provider. + * + * *Note:* It is important that the metadata of the file that changed provides an + * updated `mtime` that advanced from the previous value in the {@link FileStat stat} and a + * correct `size` value. Otherwise there may be optimizations in place that will not show + * the change in an editor for example. + */ + readonly onDidChangeFile: Event; + + /** + * Subscribes to file change events in the file or folder denoted by `uri`. For folders, + * the option `recursive` indicates whether subfolders, sub-subfolders, etc. should + * be watched for file changes as well. With `recursive: false`, only changes to the + * files that are direct children of the folder should trigger an event. + * + * The `excludes` array is used to indicate paths that should be excluded from file + * watching. It is typically derived from the `files.watcherExclude` setting that + * is configurable by the user. Each entry can be be: + * - the absolute path to exclude + * - a relative path to exclude (for example `build/output`) + * - a simple glob pattern (for example `**​/build`, `output/**`) + * + * It is the file system provider's job to call {@linkcode FileSystemProvider.onDidChangeFile onDidChangeFile} + * for every change given these rules. No event should be emitted for files that match any of the provided + * excludes. + * + * @param uri The uri of the file or folder to be watched. + * @param options Configures the watch. + * @returns A disposable that tells the provider to stop watching the `uri`. + */ + watch(uri: Uri, options: { + /** + * When enabled also watch subfolders. + */ + readonly recursive: boolean; + /** + * A list of paths and pattern to exclude from watching. + */ + readonly excludes: readonly string[]; + }): Disposable; + + /** + * Retrieve metadata about a file. + * + * Note that the metadata for symbolic links should be the metadata of the file they refer to. + * Still, the {@link FileType.SymbolicLink SymbolicLink}-type must be used in addition to the actual type, e.g. + * `FileType.SymbolicLink | FileType.Directory`. + * + * @param uri The uri of the file to retrieve metadata about. + * @returns The file metadata about the file. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + */ + stat(uri: Uri): FileStat | Thenable; + + /** + * Retrieve all entries of a {@link FileType.Directory directory}. + * + * @param uri The uri of the folder. + * @returns An array of name/type-tuples or a thenable that resolves to such. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + */ + readDirectory(uri: Uri): [string, FileType][] | Thenable<[string, FileType][]>; + + /** + * Create a new directory (Note, that new files are created via `write`-calls). + * + * @param uri The uri of the new folder. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when the parent of `uri` doesn't exist, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `uri` already exists. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + createDirectory(uri: Uri): void | Thenable; + + /** + * Read the entire contents of a file. + * + * @param uri The uri of the file. + * @returns An array of bytes or a thenable that resolves to such. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + */ + readFile(uri: Uri): Uint8Array | Thenable; + + /** + * Write data to a file, replacing its entire contents. + * + * @param uri The uri of the file. + * @param content The new content of the file. + * @param options Defines if missing files should or must be created. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist and `create` is not set. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when the parent of `uri` doesn't exist and `create` is set, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `uri` already exists, `create` is set but `overwrite` is not set. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + writeFile(uri: Uri, content: Uint8Array, options: { + /** + * Create the file if it does not exist already. + */ + readonly create: boolean; + /** + * Overwrite the file if it does exist. + */ + readonly overwrite: boolean; + }): void | Thenable; + + /** + * Delete a file. + * + * @param uri The resource that is to be deleted. + * @param options Defines if deletion of folders is recursive. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + delete(uri: Uri, options: { + /** + * Delete the content recursively if a folder is denoted. + */ + readonly recursive: boolean; + }): void | Thenable; + + /** + * Rename a file or folder. + * + * @param oldUri The existing file. + * @param newUri The new location. + * @param options Defines if existing files should be overwritten. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `oldUri` doesn't exist. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when parent of `newUri` doesn't exist, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `newUri` exists and when the `overwrite` option is not `true`. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + rename(oldUri: Uri, newUri: Uri, options: { + /** + * Overwrite the file if it does exist. + */ + readonly overwrite: boolean; + }): void | Thenable; + + /** + * Copy files or folders. Implementing this function is optional but it will speedup + * the copy operation. + * + * @param source The existing file. + * @param destination The destination location. + * @param options Defines if existing files should be overwritten. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `source` doesn't exist. + * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when parent of `destination` doesn't exist, e.g. no mkdirp-logic required. + * @throws {@linkcode FileSystemError.FileExists FileExists} when `destination` exists and when the `overwrite` option is not `true`. + * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. + */ + copy?(source: Uri, destination: Uri, options: { + /** + * Overwrite the file if it does exist. + */ + readonly overwrite: boolean; + }): void | Thenable; + } + + /** + * The file system interface exposes the editor's built-in and contributed + * {@link FileSystemProvider file system providers}. It allows extensions to work + * with files from the local disk as well as files from remote places, like the + * remote extension host or ftp-servers. + * + * *Note* that an instance of this interface is available as {@linkcode workspace.fs}. + */ + export interface FileSystem { + + /** + * Retrieve metadata about a file. + * + * @param uri The uri of the file to retrieve metadata about. + * @returns The file metadata about the file. + */ + stat(uri: Uri): Thenable; + + /** + * Retrieve all entries of a {@link FileType.Directory directory}. + * + * @param uri The uri of the folder. + * @returns An array of name/type-tuples or a thenable that resolves to such. + */ + readDirectory(uri: Uri): Thenable<[string, FileType][]>; + + /** + * Create a new directory (Note, that new files are created via `write`-calls). + * + * *Note* that missing directories are created automatically, e.g this call has + * `mkdirp` semantics. + * + * @param uri The uri of the new folder. + */ + createDirectory(uri: Uri): Thenable; + + /** + * Read the entire contents of a file. + * + * @param uri The uri of the file. + * @returns An array of bytes or a thenable that resolves to such. + */ + readFile(uri: Uri): Thenable; + + /** + * Write data to a file, replacing its entire contents. + * + * @param uri The uri of the file. + * @param content The new content of the file. + */ + writeFile(uri: Uri, content: Uint8Array): Thenable; + + /** + * Delete a file. + * + * @param uri The resource that is to be deleted. + * @param options Defines if trash can should be used and if deletion of folders is recursive + */ + delete(uri: Uri, options?: { + /** + * Delete the content recursively if a folder is denoted. + */ + recursive?: boolean; + /** + * Use the os's trashcan instead of permanently deleting files whenever possible. + */ + useTrash?: boolean; + }): Thenable; + + /** + * Rename a file or folder. + * + * @param source The existing file. + * @param target The new location. + * @param options Defines if existing files should be overwritten. + */ + rename(source: Uri, target: Uri, options?: { + /** + * Overwrite the file if it does exist. + */ + overwrite?: boolean; + }): Thenable; + + /** + * Copy files or folders. + * + * @param source The existing file. + * @param target The destination location. + * @param options Defines if existing files should be overwritten. + */ + copy(source: Uri, target: Uri, options?: { + /** + * Overwrite the file if it does exist. + */ + overwrite?: boolean; + }): Thenable; + + /** + * Check if a given file system supports writing files. + * + * Keep in mind that just because a file system supports writing, that does + * not mean that writes will always succeed. There may be permissions issues + * or other errors that prevent writing a file. + * + * @param scheme The scheme of the filesystem, for example `file` or `git`. + * + * @returns `true` if the file system supports writing, `false` if it does not + * support writing (i.e. it is readonly), and `undefined` if the editor does not + * know about the filesystem. + */ + isWritableFileSystem(scheme: string): boolean | undefined; + } + + /** + * Defines a port mapping used for localhost inside the webview. + */ + export interface WebviewPortMapping { + /** + * Localhost port to remap inside the webview. + */ + readonly webviewPort: number; + + /** + * Destination port. The `webviewPort` is resolved to this port. + */ + readonly extensionHostPort: number; + } + + /** + * Content settings for a webview. + */ + export interface WebviewOptions { + /** + * Controls whether scripts are enabled in the webview content or not. + * + * Defaults to false (scripts-disabled). + */ + readonly enableScripts?: boolean; + + /** + * Controls whether forms are enabled in the webview content or not. + * + * Defaults to true if {@link WebviewOptions.enableScripts scripts are enabled}. Otherwise defaults to false. + * Explicitly setting this property to either true or false overrides the default. + */ + readonly enableForms?: boolean; + + /** + * Controls whether command uris are enabled in webview content or not. + * + * Defaults to `false` (command uris are disabled). + * + * If you pass in an array, only the commands in the array are allowed. + */ + readonly enableCommandUris?: boolean | readonly string[]; + + /** + * Root paths from which the webview can load local (filesystem) resources using uris from `asWebviewUri` + * + * Default to the root folders of the current workspace plus the extension's install directory. + * + * Pass in an empty array to disallow access to any local resources. + */ + readonly localResourceRoots?: readonly Uri[]; + + /** + * Mappings of localhost ports used inside the webview. + * + * Port mapping allow webviews to transparently define how localhost ports are resolved. This can be used + * to allow using a static localhost port inside the webview that is resolved to random port that a service is + * running on. + * + * If a webview accesses localhost content, we recommend that you specify port mappings even if + * the `webviewPort` and `extensionHostPort` ports are the same. + * + * *Note* that port mappings only work for `http` or `https` urls. Websocket urls (e.g. `ws://localhost:3000`) + * cannot be mapped to another port. + */ + readonly portMapping?: readonly WebviewPortMapping[]; + } + + /** + * Displays html content, similarly to an iframe. + */ + export interface Webview { + /** + * Content settings for the webview. + */ + options: WebviewOptions; + + /** + * HTML contents of the webview. + * + * This should be a complete, valid html document. Changing this property causes the webview to be reloaded. + * + * Webviews are sandboxed from normal extension process, so all communication with the webview must use + * message passing. To send a message from the extension to the webview, use {@linkcode Webview.postMessage postMessage}. + * To send message from the webview back to an extension, use the `acquireVsCodeApi` function inside the webview + * to get a handle to the editor's api and then call `.postMessage()`: + * + * ```html + * + * ``` + * + * To load a resources from the workspace inside a webview, use the {@linkcode Webview.asWebviewUri asWebviewUri} method + * and ensure the resource's directory is listed in {@linkcode WebviewOptions.localResourceRoots}. + * + * Keep in mind that even though webviews are sandboxed, they still allow running scripts and loading arbitrary content, + * so extensions must follow all standard web security best practices when working with webviews. This includes + * properly sanitizing all untrusted input (including content from the workspace) and + * setting a [content security policy](https://aka.ms/vscode-api-webview-csp). + */ + html: string; + + /** + * Fired when the webview content posts a message. + * + * Webview content can post strings or json serializable objects back to an extension. They cannot + * post `Blob`, `File`, `ImageData` and other DOM specific objects since the extension that receives the + * message does not run in a browser environment. + */ + readonly onDidReceiveMessage: Event; + + /** + * Post a message to the webview content. + * + * Messages are only delivered if the webview is live (either visible or in the + * background with `retainContextWhenHidden`). + * + * @param message Body of the message. This must be a string or other json serializable object. + * + * For older versions of vscode, if an `ArrayBuffer` is included in `message`, + * it will not be serialized properly and will not be received by the webview. + * Similarly any TypedArrays, such as a `Uint8Array`, will be very inefficiently + * serialized and will also not be recreated as a typed array inside the webview. + * + * However if your extension targets vscode 1.57+ in the `engines` field of its + * `package.json`, any `ArrayBuffer` values that appear in `message` will be more + * efficiently transferred to the webview and will also be correctly recreated inside + * of the webview. + * + * @returns A promise that resolves when the message is posted to a webview or when it is + * dropped because the message was not deliverable. + * + * Returns `true` if the message was posted to the webview. Messages can only be posted to + * live webviews (i.e. either visible webviews or hidden webviews that set `retainContextWhenHidden`). + * + * A response of `true` does not mean that the message was actually received by the webview. + * For example, no message listeners may be have been hooked up inside the webview or the webview may + * have been destroyed after the message was posted but before it was received. + * + * If you want confirm that a message as actually received, you can try having your webview posting a + * confirmation message back to your extension. + */ + postMessage(message: any): Thenable; + + /** + * Convert a uri for the local file system to one that can be used inside webviews. + * + * Webviews cannot directly load resources from the workspace or local file system using `file:` uris. The + * `asWebviewUri` function takes a local `file:` uri and converts it into a uri that can be used inside of + * a webview to load the same resource: + * + * ```ts + * webview.html = `` + * ``` + */ + asWebviewUri(localResource: Uri): Uri; + + /** + * Content security policy source for webview resources. + * + * This is the origin that should be used in a content security policy rule: + * + * ```ts + * `img-src https: ${webview.cspSource} ...;` + * ``` + */ + readonly cspSource: string; + } + + /** + * Content settings for a webview panel. + */ + export interface WebviewPanelOptions { + /** + * Controls if the find widget is enabled in the panel. + * + * Defaults to `false`. + */ + readonly enableFindWidget?: boolean; + + /** + * Controls if the webview panel's content (iframe) is kept around even when the panel + * is no longer visible. + * + * Normally the webview panel's html context is created when the panel becomes visible + * and destroyed when it is hidden. Extensions that have complex state + * or UI can set the `retainContextWhenHidden` to make the editor keep the webview + * context around, even when the webview moves to a background tab. When a webview using + * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. + * When the panel becomes visible again, the context is automatically restored + * in the exact same state it was in originally. You cannot send messages to a + * hidden webview, even with `retainContextWhenHidden` enabled. + * + * `retainContextWhenHidden` has a high memory overhead and should only be used if + * your panel's context cannot be quickly saved and restored. + */ + readonly retainContextWhenHidden?: boolean; + } + + /** + * A panel that contains a webview. + */ + interface WebviewPanel { + /** + * Identifies the type of the webview panel, such as `'markdown.preview'`. + */ + readonly viewType: string; + + /** + * Title of the panel shown in UI. + */ + title: string; + + /** + * Icon for the panel shown in UI. + */ + iconPath?: Uri | { + /** + * The icon path for the light theme. + */ + readonly light: Uri; + /** + * The icon path for the dark theme. + */ + readonly dark: Uri; + }; + + /** + * {@linkcode Webview} belonging to the panel. + */ + readonly webview: Webview; + + /** + * Content settings for the webview panel. + */ + readonly options: WebviewPanelOptions; + + /** + * Editor position of the panel. This property is only set if the webview is in + * one of the editor view columns. + */ + readonly viewColumn: ViewColumn | undefined; + + /** + * Whether the panel is active (focused by the user). + */ + readonly active: boolean; + + /** + * Whether the panel is visible. + */ + readonly visible: boolean; + + /** + * Fired when the panel's view state changes. + */ + readonly onDidChangeViewState: Event; + + /** + * Fired when the panel is disposed. + * + * This may be because the user closed the panel or because `.dispose()` was + * called on it. + * + * Trying to use the panel after it has been disposed throws an exception. + */ + readonly onDidDispose: Event; + + /** + * Show the webview panel in a given column. + * + * A webview panel may only show in a single column at a time. If it is already showing, this + * method moves it to a new column. + * + * @param viewColumn View column to show the panel in. Shows in the current `viewColumn` if undefined. + * @param preserveFocus When `true`, the webview will not take focus. + */ + reveal(viewColumn?: ViewColumn, preserveFocus?: boolean): void; + + /** + * Dispose of the webview panel. + * + * This closes the panel if it showing and disposes of the resources owned by the webview. + * Webview panels are also disposed when the user closes the webview panel. Both cases + * fire the `onDispose` event. + */ + dispose(): any; + } + + /** + * Event fired when a webview panel's view state changes. + */ + export interface WebviewPanelOnDidChangeViewStateEvent { + /** + * Webview panel whose view state changed. + */ + readonly webviewPanel: WebviewPanel; + } + + /** + * Restore webview panels that have been persisted when vscode shuts down. + * + * There are two types of webview persistence: + * + * - Persistence within a session. + * - Persistence across sessions (across restarts of the editor). + * + * A `WebviewPanelSerializer` is only required for the second case: persisting a webview across sessions. + * + * Persistence within a session allows a webview to save its state when it becomes hidden + * and restore its content from this state when it becomes visible again. It is powered entirely + * by the webview content itself. To save off a persisted state, call `acquireVsCodeApi().setState()` with + * any json serializable object. To restore the state again, call `getState()` + * + * ```js + * // Within the webview + * const vscode = acquireVsCodeApi(); + * + * // Get existing state + * const oldState = vscode.getState() || { value: 0 }; + * + * // Update state + * setState({ value: oldState.value + 1 }) + * ``` + * + * A `WebviewPanelSerializer` extends this persistence across restarts of the editor. When the editor is shutdown, + * it will save off the state from `setState` of all webviews that have a serializer. When the + * webview first becomes visible after the restart, this state is passed to `deserializeWebviewPanel`. + * The extension can then restore the old `WebviewPanel` from this state. + * + * @param T Type of the webview's state. + */ + interface WebviewPanelSerializer { + /** + * Restore a webview panel from its serialized `state`. + * + * Called when a serialized webview first becomes visible. + * + * @param webviewPanel Webview panel to restore. The serializer should take ownership of this panel. The + * serializer must restore the webview's `.html` and hook up all webview events. + * @param state Persisted state from the webview content. + * + * @returns Thenable indicating that the webview has been fully restored. + */ + deserializeWebviewPanel(webviewPanel: WebviewPanel, state: T): Thenable; + } + + /** + * A webview based view. + */ + export interface WebviewView { + /** + * Identifies the type of the webview view, such as `'hexEditor.dataView'`. + */ + readonly viewType: string; + + /** + * The underlying webview for the view. + */ + readonly webview: Webview; + + /** + * View title displayed in the UI. + * + * The view title is initially taken from the extension `package.json` contribution. + */ + title?: string; + + /** + * Human-readable string which is rendered less prominently in the title. + */ + description?: string; + + /** + * The badge to display for this webview view. + * To remove the badge, set to undefined. + */ + badge?: ViewBadge | undefined; + + /** + * Event fired when the view is disposed. + * + * Views are disposed when they are explicitly hidden by a user (this happens when a user + * right clicks in a view and unchecks the webview view). + * + * Trying to use the view after it has been disposed throws an exception. + */ + readonly onDidDispose: Event; + + /** + * Tracks if the webview is currently visible. + * + * Views are visible when they are on the screen and expanded. + */ + readonly visible: boolean; + + /** + * Event fired when the visibility of the view changes. + * + * Actions that trigger a visibility change: + * + * - The view is collapsed or expanded. + * - The user switches to a different view group in the sidebar or panel. + * + * Note that hiding a view using the context menu instead disposes of the view and fires `onDidDispose`. + */ + readonly onDidChangeVisibility: Event; + + /** + * Reveal the view in the UI. + * + * If the view is collapsed, this will expand it. + * + * @param preserveFocus When `true` the view will not take focus. + */ + show(preserveFocus?: boolean): void; + } + + /** + * Additional information the webview view being resolved. + * + * @param T Type of the webview's state. + */ + interface WebviewViewResolveContext { + /** + * Persisted state from the webview content. + * + * To save resources, the editor normally deallocates webview documents (the iframe content) that are not visible. + * For example, when the user collapse a view or switches to another top level activity in the sidebar, the + * `WebviewView` itself is kept alive but the webview's underlying document is deallocated. It is recreated when + * the view becomes visible again. + * + * You can prevent this behavior by setting `retainContextWhenHidden` in the `WebviewOptions`. However this + * increases resource usage and should be avoided wherever possible. Instead, you can use persisted state to + * save off a webview's state so that it can be quickly recreated as needed. + * + * To save off a persisted state, inside the webview call `acquireVsCodeApi().setState()` with + * any json serializable object. To restore the state again, call `getState()`. For example: + * + * ```js + * // Within the webview + * const vscode = acquireVsCodeApi(); + * + * // Get existing state + * const oldState = vscode.getState() || { value: 0 }; + * + * // Update state + * setState({ value: oldState.value + 1 }) + * ``` + * + * The editor ensures that the persisted state is saved correctly when a webview is hidden and across + * editor restarts. + */ + readonly state: T | undefined; + } + + /** + * Provider for creating `WebviewView` elements. + */ + export interface WebviewViewProvider { + /** + * Resolves a webview view. + * + * `resolveWebviewView` is called when a view first becomes visible. This may happen when the view is + * first loaded or when the user hides and then shows a view again. + * + * @param webviewView Webview view to restore. The provider should take ownership of this view. The + * provider must set the webview's `.html` and hook up all webview events it is interested in. + * @param context Additional metadata about the view being resolved. + * @param token Cancellation token indicating that the view being provided is no longer needed. + * + * @returns Optional thenable indicating that the view has been fully resolved. + */ + resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Thenable | void; + } + + /** + * Provider for text based custom editors. + * + * Text based custom editors use a {@linkcode TextDocument} as their data model. This considerably simplifies + * implementing a custom editor as it allows the editor to handle many common operations such as + * undo and backup. The provider is responsible for synchronizing text changes between the webview and the `TextDocument`. + */ + export interface CustomTextEditorProvider { + + /** + * Resolve a custom editor for a given text resource. + * + * This is called when a user first opens a resource for a `CustomTextEditorProvider`, or if they reopen an + * existing editor using this `CustomTextEditorProvider`. + * + * + * @param document Document for the resource to resolve. + * + * @param webviewPanel The webview panel used to display the editor UI for this resource. + * + * During resolve, the provider must fill in the initial html for the content webview panel and hook up all + * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to + * use later for example in a command. See {@linkcode WebviewPanel} for additional details. + * + * @param token A cancellation token that indicates the result is no longer needed. + * + * @returns Thenable indicating that the custom editor has been resolved. + */ + resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; + } + + /** + * Represents a custom document used by a {@linkcode CustomEditorProvider}. + * + * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is + * managed by the editor. When no more references remain to a `CustomDocument`, it is disposed of. + */ + interface CustomDocument { + /** + * The associated uri for this document. + */ + readonly uri: Uri; + + /** + * Dispose of the custom document. + * + * This is invoked by the editor when there are no more references to a given `CustomDocument` (for example when + * all editors associated with the document have been closed.) + */ + dispose(): void; + } + + /** + * Event triggered by extensions to signal to the editor that an edit has occurred on an {@linkcode CustomDocument}. + * + * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. + */ + interface CustomDocumentEditEvent { + + /** + * The document that the edit is for. + */ + readonly document: T; + + /** + * Undo the edit operation. + * + * This is invoked by the editor when the user undoes this edit. To implement `undo`, your + * extension should restore the document and editor to the state they were in just before this + * edit was added to the editor's internal edit stack by `onDidChangeCustomDocument`. + */ + undo(): Thenable | void; + + /** + * Redo the edit operation. + * + * This is invoked by the editor when the user redoes this edit. To implement `redo`, your + * extension should restore the document and editor to the state they were in just after this + * edit was added to the editor's internal edit stack by `onDidChangeCustomDocument`. + */ + redo(): Thenable | void; + + /** + * Display name describing the edit. + * + * This will be shown to users in the UI for undo/redo operations. + */ + readonly label?: string; + } + + /** + * Event triggered by extensions to signal to the editor that the content of a {@linkcode CustomDocument} + * has changed. + * + * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. + */ + interface CustomDocumentContentChangeEvent { + /** + * The document that the change is for. + */ + readonly document: T; + } + + /** + * A backup for an {@linkcode CustomDocument}. + */ + interface CustomDocumentBackup { + /** + * Unique identifier for the backup. + * + * This id is passed back to your extension in `openCustomDocument` when opening a custom editor from a backup. + */ + readonly id: string; + + /** + * Delete the current backup. + * + * This is called by the editor when it is clear the current backup is no longer needed, such as when a new backup + * is made or when the file is saved. + */ + delete(): void; + } + + /** + * Additional information used to implement {@linkcode CustomDocumentBackup}. + */ + interface CustomDocumentBackupContext { + /** + * Suggested file location to write the new backup. + * + * Note that your extension is free to ignore this and use its own strategy for backup. + * + * If the editor is for a resource from the current workspace, `destination` will point to a file inside + * `ExtensionContext.storagePath`. The parent folder of `destination` may not exist, so make sure to created it + * before writing the backup to this location. + */ + readonly destination: Uri; + } + + /** + * Additional information about the opening custom document. + */ + interface CustomDocumentOpenContext { + /** + * The id of the backup to restore the document from or `undefined` if there is no backup. + * + * If this is provided, your extension should restore the editor from the backup instead of reading the file + * from the user's workspace. + */ + readonly backupId: string | undefined; + + /** + * If the URI is an untitled file, this will be populated with the byte data of that file + * + * If this is provided, your extension should utilize this byte data rather than executing fs APIs on the URI passed in + */ + readonly untitledDocumentData: Uint8Array | undefined; + } + + /** + * Provider for readonly custom editors that use a custom document model. + * + * Custom editors use {@linkcode CustomDocument} as their document model instead of a {@linkcode TextDocument}. + * + * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple + * text based documents, use {@linkcode CustomTextEditorProvider} instead. + * + * @param T Type of the custom document returned by this provider. + */ + export interface CustomReadonlyEditorProvider { + + /** + * Create a new document for a given resource. + * + * `openCustomDocument` is called when the first time an editor for a given resource is opened. The opened + * document is then passed to `resolveCustomEditor` so that the editor can be shown to the user. + * + * Already opened `CustomDocument` are re-used if the user opened additional editors. When all editors for a + * given resource are closed, the `CustomDocument` is disposed of. Opening an editor at this point will + * trigger another call to `openCustomDocument`. + * + * @param uri Uri of the document to open. + * @param openContext Additional information about the opening custom document. + * @param token A cancellation token that indicates the result is no longer needed. + * + * @returns The custom document. + */ + openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable | T; + + /** + * Resolve a custom editor for a given resource. + * + * This is called whenever the user opens a new editor for this `CustomEditorProvider`. + * + * @param document Document for the resource being resolved. + * + * @param webviewPanel The webview panel used to display the editor UI for this resource. + * + * During resolve, the provider must fill in the initial html for the content webview panel and hook up all + * the event listeners on it that it is interested in. The provider can also hold onto the `WebviewPanel` to + * use later for example in a command. See {@linkcode WebviewPanel} for additional details. + * + * @param token A cancellation token that indicates the result is no longer needed. + * + * @returns Optional thenable indicating that the custom editor has been resolved. + */ + resolveCustomEditor(document: T, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; + } + + /** + * Provider for editable custom editors that use a custom document model. + * + * Custom editors use {@linkcode CustomDocument} as their document model instead of a {@linkcode TextDocument}. + * This gives extensions full control over actions such as edit, save, and backup. + * + * You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple + * text based documents, use {@linkcode CustomTextEditorProvider} instead. + * + * @param T Type of the custom document returned by this provider. + */ + export interface CustomEditorProvider extends CustomReadonlyEditorProvider { + /** + * Signal that an edit has occurred inside a custom editor. + * + * This event must be fired by your extension whenever an edit happens in a custom editor. An edit can be + * anything from changing some text, to cropping an image, to reordering a list. Your extension is free to + * define what an edit is and what data is stored on each edit. + * + * Firing `onDidChange` causes the editors to be marked as being dirty. This is cleared when the user either + * saves or reverts the file. + * + * Editors that support undo/redo must fire a `CustomDocumentEditEvent` whenever an edit happens. This allows + * users to undo and redo the edit using the editor's standard keyboard shortcuts. The editor will also mark + * the editor as no longer being dirty if the user undoes all edits to the last saved state. + * + * Editors that support editing but cannot use the editor's standard undo/redo mechanism must fire a `CustomDocumentContentChangeEvent`. + * The only way for a user to clear the dirty state of an editor that does not support undo/redo is to either + * `save` or `revert` the file. + * + * An editor should only ever fire `CustomDocumentEditEvent` events, or only ever fire `CustomDocumentContentChangeEvent` events. + */ + readonly onDidChangeCustomDocument: Event> | Event>; + + /** + * Save a custom document. + * + * This method is invoked by the editor when the user saves a custom editor. This can happen when the user + * triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled. + * + * To implement `save`, the implementer must persist the custom editor. This usually means writing the + * file data for the custom document to disk. After `save` completes, any associated editor instances will + * no longer be marked as dirty. + * + * @param document Document to save. + * @param cancellation Token that signals the save is no longer required (for example, if another save was triggered). + * + * @returns Thenable signaling that saving has completed. + */ + saveCustomDocument(document: T, cancellation: CancellationToken): Thenable; + + /** + * Save a custom document to a different location. + * + * This method is invoked by the editor when the user triggers 'save as' on a custom editor. The implementer must + * persist the custom editor to `destination`. + * + * When the user accepts save as, the current editor is be replaced by an non-dirty editor for the newly saved file. + * + * @param document Document to save. + * @param destination Location to save to. + * @param cancellation Token that signals the save is no longer required. + * + * @returns Thenable signaling that saving has completed. + */ + saveCustomDocumentAs(document: T, destination: Uri, cancellation: CancellationToken): Thenable; + + /** + * Revert a custom document to its last saved state. + * + * This method is invoked by the editor when the user triggers `File: Revert File` in a custom editor. (Note that + * this is only used using the editor's `File: Revert File` command and not on a `git revert` of the file). + * + * To implement `revert`, the implementer must make sure all editor instances (webviews) for `document` + * are displaying the document in the same state is saved in. This usually means reloading the file from the + * workspace. + * + * @param document Document to revert. + * @param cancellation Token that signals the revert is no longer required. + * + * @returns Thenable signaling that the change has completed. + */ + revertCustomDocument(document: T, cancellation: CancellationToken): Thenable; + + /** + * Back up a dirty custom document. + * + * Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in + * its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in + * the `ExtensionContext.storagePath`. When the editor reloads and your custom editor is opened for a resource, + * your extension should first check to see if any backups exist for the resource. If there is a backup, your + * extension should load the file contents from there instead of from the resource in the workspace. + * + * `backup` is triggered approximately one second after the user stops editing the document. If the user + * rapidly edits the document, `backup` will not be invoked until the editing stops. + * + * `backup` is not invoked when `auto save` is enabled (since auto save already persists the resource). + * + * @param document Document to backup. + * @param context Information that can be used to backup the document. + * @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your + * extension to decided how to respond to cancellation. If for example your extension is backing up a large file + * in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather + * than cancelling it to ensure that the editor has some valid backup. + */ + backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable; + } + + /** + * The clipboard provides read and write access to the system's clipboard. + */ + export interface Clipboard { + + /** + * Read the current clipboard contents as text. + * @returns A thenable that resolves to a string. + */ + readText(): Thenable; + + /** + * Writes text into the clipboard. + * @returns A thenable that resolves when writing happened. + */ + writeText(value: string): Thenable; + } + + /** + * Possible kinds of UI that can use extensions. + */ + export enum UIKind { + + /** + * Extensions are accessed from a desktop application. + */ + Desktop = 1, + + /** + * Extensions are accessed from a web browser. + */ + Web = 2 + } + + /** + * Log levels + */ + export enum LogLevel { + + /** + * No messages are logged with this level. + */ + Off = 0, + + /** + * All messages are logged with this level. + */ + Trace = 1, + + /** + * Messages with debug and higher log level are logged with this level. + */ + Debug = 2, + + /** + * Messages with info and higher log level are logged with this level. + */ + Info = 3, + + /** + * Messages with warning and higher log level are logged with this level. + */ + Warning = 4, + + /** + * Only error messages are logged with this level. + */ + Error = 5 + } + + /** + * Namespace describing the environment the editor runs in. + */ + export namespace env { + + /** + * The application name of the editor, like 'VS Code'. + */ + export const appName: string; + + /** + * The application root folder from which the editor is running. + * + * *Note* that the value is the empty string when running in an + * environment that has no representation of an application root folder. + */ + export const appRoot: string; + + /** + * The hosted location of the application + * On desktop this is 'desktop' + * In the web this is the specified embedder i.e. 'github.dev', 'codespaces', or 'web' if the embedder + * does not provide that information + */ + export const appHost: string; + + /** + * The custom uri scheme the editor registers to in the operating system. + */ + export const uriScheme: string; + + /** + * Represents the preferred user-language, like `de-CH`, `fr`, or `en-US`. + */ + export const language: string; + + /** + * The system clipboard. + */ + export const clipboard: Clipboard; + + /** + * A unique identifier for the computer. + */ + export const machineId: string; + + /** + * A unique identifier for the current session. + * Changes each time the editor is started. + */ + export const sessionId: string; + + /** + * Indicates that this is a fresh install of the application. + * `true` if within the first day of installation otherwise `false`. + */ + export const isNewAppInstall: boolean; + + /** + * Indicates whether the users has telemetry enabled. + * Can be observed to determine if the extension should send telemetry. + */ + export const isTelemetryEnabled: boolean; + + /** + * An {@link Event} which fires when the user enabled or disables telemetry. + * `true` if the user has enabled telemetry or `false` if the user has disabled telemetry. + */ + export const onDidChangeTelemetryEnabled: Event; + + /** + * An {@link Event} which fires when the default shell changes. This fires with the new + * shell path. + */ + export const onDidChangeShell: Event; + + /** + * Creates a new {@link TelemetryLogger telemetry logger}. + * + * @param sender The telemetry sender that is used by the telemetry logger. + * @param options Options for the telemetry logger. + * @returns A new telemetry logger + */ + export function createTelemetryLogger(sender: TelemetrySender, options?: TelemetryLoggerOptions): TelemetryLogger; + + /** + * The name of a remote. Defined by extensions, popular samples are `wsl` for the Windows + * Subsystem for Linux or `ssh-remote` for remotes using a secure shell. + * + * *Note* that the value is `undefined` when there is no remote extension host but that the + * value is defined in all extension hosts (local and remote) in case a remote extension host + * exists. Use {@link Extension.extensionKind} to know if + * a specific extension runs remote or not. + */ + export const remoteName: string | undefined; + + /** + * The detected default shell for the extension host, this is overridden by the + * `terminal.integrated.defaultProfile` setting for the extension host's platform. Note that in + * environments that do not support a shell the value is the empty string. + */ + export const shell: string; + + /** + * The UI kind property indicates from which UI extensions + * are accessed from. For example, extensions could be accessed + * from a desktop application or a web browser. + */ + export const uiKind: UIKind; + + /** + * Opens a link externally using the default application. Depending on the + * used scheme this can be: + * * a browser (`http:`, `https:`) + * * a mail client (`mailto:`) + * * VSCode itself (`vscode:` from `vscode.env.uriScheme`) + * + * *Note* that {@linkcode window.showTextDocument showTextDocument} is the right + * way to open a text document inside the editor, not this function. + * + * @param target The uri that should be opened. + * @returns A promise indicating if open was successful. + */ + export function openExternal(target: Uri): Thenable; + + /** + * Resolves a uri to a form that is accessible externally. + * + * #### `http:` or `https:` scheme + * + * Resolves an *external* uri, such as a `http:` or `https:` link, from where the extension is running to a + * uri to the same resource on the client machine. + * + * This is a no-op if the extension is running on the client machine. + * + * If the extension is running remotely, this function automatically establishes a port forwarding tunnel + * from the local machine to `target` on the remote and returns a local uri to the tunnel. The lifetime of + * the port forwarding tunnel is managed by the editor and the tunnel can be closed by the user. + * + * *Note* that uris passed through `openExternal` are automatically resolved and you should not call `asExternalUri` on them. + * + * #### `vscode.env.uriScheme` + * + * Creates a uri that - if opened in a browser (e.g. via `openExternal`) - will result in a registered {@link UriHandler} + * to trigger. + * + * Extensions should not make any assumptions about the resulting uri and should not alter it in any way. + * Rather, extensions can e.g. use this uri in an authentication flow, by adding the uri as callback query + * argument to the server to authenticate to. + * + * *Note* that if the server decides to add additional query parameters to the uri (e.g. a token or secret), it + * will appear in the uri that is passed to the {@link UriHandler}. + * + * **Example** of an authentication flow: + * ```typescript + * vscode.window.registerUriHandler({ + * handleUri(uri: vscode.Uri): vscode.ProviderResult { + * if (uri.path === '/did-authenticate') { + * console.log(uri.toString()); + * } + * } + * }); + * + * const callableUri = await vscode.env.asExternalUri(vscode.Uri.parse(vscode.env.uriScheme + '://my.extension/did-authenticate')); + * await vscode.env.openExternal(callableUri); + * ``` + * + * *Note* that extensions should not cache the result of `asExternalUri` as the resolved uri may become invalid due to + * a system or user action — for example, in remote cases, a user may close a port forwarding tunnel that was opened by + * `asExternalUri`. + * + * #### Any other scheme + * + * Any other scheme will be handled as if the provided URI is a workspace URI. In that case, the method will return + * a URI which, when handled, will make the editor open the workspace. + * + * @returns A uri that can be used on the client machine. + */ + export function asExternalUri(target: Uri): Thenable; + + /** + * The current log level of the editor. + */ + export const logLevel: LogLevel; + + /** + * An {@link Event} which fires when the log level of the editor changes. + */ + export const onDidChangeLogLevel: Event; + } + + /** + * Namespace for dealing with commands. In short, a command is a function with a + * unique identifier. The function is sometimes also called _command handler_. + * + * Commands can be added to the editor using the {@link commands.registerCommand registerCommand} + * and {@link commands.registerTextEditorCommand registerTextEditorCommand} functions. Commands + * can be executed {@link commands.executeCommand manually} or from a UI gesture. Those are: + * + * * palette - Use the `commands`-section in `package.json` to make a command show in + * the [command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette). + * * keybinding - Use the `keybindings`-section in `package.json` to enable + * [keybindings](https://code.visualstudio.com/docs/getstarted/keybindings#_advanced-customization) + * for your extension. + * + * Commands from other extensions and from the editor itself are accessible to an extension. However, + * when invoking an editor command not all argument types are supported. + * + * This is a sample that registers a command handler and adds an entry for that command to the palette. First + * register a command handler with the identifier `extension.sayHello`. + * ```javascript + * commands.registerCommand('extension.sayHello', () => { + * window.showInformationMessage('Hello World!'); + * }); + * ``` + * Second, bind the command identifier to a title under which it will show in the palette (`package.json`). + * ```json + * { + * "contributes": { + * "commands": [{ + * "command": "extension.sayHello", + * "title": "Hello World" + * }] + * } + * } + * ``` + */ + export namespace commands { + + /** + * Registers a command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Registering a command with an existing command identifier twice + * will cause an error. + * + * @param command A unique identifier for the command. + * @param callback A command handler function. + * @param thisArg The `this` context used when invoking the handler function. + * @returns Disposable which unregisters this command on disposal. + */ + export function registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): Disposable; + + /** + * Registers a text editor command that can be invoked via a keyboard shortcut, + * a menu item, an action, or directly. + * + * Text editor commands are different from ordinary {@link commands.registerCommand commands} as + * they only execute when there is an active editor when the command is called. Also, the + * command handler of an editor command has access to the active editor and to an + * {@link TextEditorEdit edit}-builder. Note that the edit-builder is only valid while the + * callback executes. + * + * @param command A unique identifier for the command. + * @param callback A command handler function with access to an {@link TextEditor editor} and an {@link TextEditorEdit edit}. + * @param thisArg The `this` context used when invoking the handler function. + * @returns Disposable which unregisters this command on disposal. + */ + export function registerTextEditorCommand(command: string, callback: (textEditor: TextEditor, edit: TextEditorEdit, ...args: any[]) => void, thisArg?: any): Disposable; + + /** + * Executes the command denoted by the given command identifier. + * + * * *Note 1:* When executing an editor command not all types are allowed to + * be passed as arguments. Allowed are the primitive types `string`, `boolean`, + * `number`, `undefined`, and `null`, as well as {@linkcode Position}, {@linkcode Range}, {@linkcode Uri} and {@linkcode Location}. + * * *Note 2:* There are no restrictions when executing commands that have been contributed + * by extensions. + * + * @param command Identifier of the command to execute. + * @param rest Parameters passed to the command function. + * @returns A thenable that resolves to the returned value of the given command. Returns `undefined` when + * the command handler function doesn't return anything. + */ + export function executeCommand(command: string, ...rest: any[]): Thenable; + + /** + * Retrieve the list of all available commands. Commands starting with an underscore are + * treated as internal commands. + * + * @param filterInternal Set `true` to not see internal commands (starting with an underscore) + * @returns Thenable that resolves to a list of command ids. + */ + export function getCommands(filterInternal?: boolean): Thenable; + } + + /** + * Represents the state of a window. + */ + export interface WindowState { + + /** + * Whether the current window is focused. + */ + readonly focused: boolean; + + /** + * Whether the window has been interacted with recently. This will change + * immediately on activity, or after a short time of user inactivity. + */ + readonly active: boolean; + } + + /** + * A uri handler is responsible for handling system-wide {@link Uri uris}. + * + * @see {@link window.registerUriHandler}. + */ + export interface UriHandler { + + /** + * Handle the provided system-wide {@link Uri}. + * + * @see {@link window.registerUriHandler}. + */ + handleUri(uri: Uri): ProviderResult; + } + + /** + * Namespace for dealing with the current window of the editor. That is visible + * and active editors, as well as, UI elements to show messages, selections, and + * asking for user input. + */ + export namespace window { + + /** + * Represents the grid widget within the main editor area + */ + export const tabGroups: TabGroups; + + /** + * The currently active editor or `undefined`. The active editor is the one + * that currently has focus or, when none has focus, the one that has changed + * input most recently. + */ + export let activeTextEditor: TextEditor | undefined; + + /** + * The currently visible editors or an empty array. + */ + export let visibleTextEditors: readonly TextEditor[]; + + /** + * An {@link Event} which fires when the {@link window.activeTextEditor active editor} + * has changed. *Note* that the event also fires when the active editor changes + * to `undefined`. + */ + export const onDidChangeActiveTextEditor: Event; + + /** + * An {@link Event} which fires when the array of {@link window.visibleTextEditors visible editors} + * has changed. + */ + export const onDidChangeVisibleTextEditors: Event; + + /** + * An {@link Event} which fires when the selection in an editor has changed. + */ + export const onDidChangeTextEditorSelection: Event; + + /** + * An {@link Event} which fires when the visible ranges of an editor has changed. + */ + export const onDidChangeTextEditorVisibleRanges: Event; + + /** + * An {@link Event} which fires when the options of an editor have changed. + */ + export const onDidChangeTextEditorOptions: Event; + + /** + * An {@link Event} which fires when the view column of an editor has changed. + */ + export const onDidChangeTextEditorViewColumn: Event; + + /** + * The currently visible {@link NotebookEditor notebook editors} or an empty array. + */ + export const visibleNotebookEditors: readonly NotebookEditor[]; + + /** + * An {@link Event} which fires when the {@link window.visibleNotebookEditors visible notebook editors} + * has changed. + */ + export const onDidChangeVisibleNotebookEditors: Event; + + /** + * The currently active {@link NotebookEditor notebook editor} or `undefined`. The active editor is the one + * that currently has focus or, when none has focus, the one that has changed + * input most recently. + */ + export const activeNotebookEditor: NotebookEditor | undefined; + + /** + * An {@link Event} which fires when the {@link window.activeNotebookEditor active notebook editor} + * has changed. *Note* that the event also fires when the active editor changes + * to `undefined`. + */ + export const onDidChangeActiveNotebookEditor: Event; + + /** + * An {@link Event} which fires when the {@link NotebookEditor.selections notebook editor selections} + * have changed. + */ + export const onDidChangeNotebookEditorSelection: Event; + + /** + * An {@link Event} which fires when the {@link NotebookEditor.visibleRanges notebook editor visible ranges} + * have changed. + */ + export const onDidChangeNotebookEditorVisibleRanges: Event; + + /** + * The currently opened terminals or an empty array. + */ + export const terminals: readonly Terminal[]; + + /** + * The currently active terminal or `undefined`. The active terminal is the one that + * currently has focus or most recently had focus. + */ + export const activeTerminal: Terminal | undefined; + + /** + * An {@link Event} which fires when the {@link window.activeTerminal active terminal} + * has changed. *Note* that the event also fires when the active terminal changes + * to `undefined`. + */ + export const onDidChangeActiveTerminal: Event; + + /** + * An {@link Event} which fires when a terminal has been created, either through the + * {@link window.createTerminal createTerminal} API or commands. + */ + export const onDidOpenTerminal: Event; + + /** + * An {@link Event} which fires when a terminal is disposed. + */ + export const onDidCloseTerminal: Event; + + /** + * An {@link Event} which fires when a {@link Terminal.state terminal's state} has changed. + */ + export const onDidChangeTerminalState: Event; + + /** + * Fires when shell integration activates or one of its properties changes in a terminal. + */ + export const onDidChangeTerminalShellIntegration: Event; + + /** + * This will be fired when a terminal command is started. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + */ + export const onDidStartTerminalShellExecution: Event; + + /** + * This will be fired when a terminal command is ended. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + */ + export const onDidEndTerminalShellExecution: Event; + + /** + * Represents the current window's state. + */ + export const state: WindowState; + + /** + * An {@link Event} which fires when the focus or activity state of the current window + * changes. The value of the event represents whether the window is focused. + */ + export const onDidChangeWindowState: Event; + + /** + * Show the given document in a text editor. A {@link ViewColumn column} can be provided + * to control where the editor is being shown. Might change the {@link window.activeTextEditor active editor}. + * + * @param document A text document to be shown. + * @param column A view column in which the {@link TextEditor editor} should be shown. The default is the {@link ViewColumn.Active active}. + * Columns that do not exist will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. Use {@linkcode ViewColumn.Beside} + * to open the editor to the side of the currently active one. + * @param preserveFocus When `true` the editor will not take focus. + * @returns A promise that resolves to an {@link TextEditor editor}. + */ + export function showTextDocument(document: TextDocument, column?: ViewColumn, preserveFocus?: boolean): Thenable; + + /** + * Show the given document in a text editor. {@link TextDocumentShowOptions Options} can be provided + * to control options of the editor is being shown. Might change the {@link window.activeTextEditor active editor}. + * + * @param document A text document to be shown. + * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. + * @returns A promise that resolves to an {@link TextEditor editor}. + */ + export function showTextDocument(document: TextDocument, options?: TextDocumentShowOptions): Thenable; + + /** + * A short-hand for `openTextDocument(uri).then(document => showTextDocument(document, options))`. + * + * @see {@link workspace.openTextDocument} + * + * @param uri A resource identifier. + * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. + * @returns A promise that resolves to an {@link TextEditor editor}. + */ + export function showTextDocument(uri: Uri, options?: TextDocumentShowOptions): Thenable; + + /** + * Show the given {@link NotebookDocument} in a {@link NotebookEditor notebook editor}. + * + * @param document A text document to be shown. + * @param options {@link NotebookDocumentShowOptions Editor options} to configure the behavior of showing the {@link NotebookEditor notebook editor}. + * + * @returns A promise that resolves to an {@link NotebookEditor notebook editor}. + */ + export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; + + /** + * Create a TextEditorDecorationType that can be used to add decorations to text editors. + * + * @param options Rendering options for the decoration type. + * @returns A new decoration type instance. + */ + export function createTextEditorDecorationType(options: DecorationRenderOptions): TextEditorDecorationType; + + /** + * Show an information message to users. Optionally provide an array of items which will be presented as + * clickable buttons. + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an information message to users. Optionally provide an array of items which will be presented as + * clickable buttons. + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show an information message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an information message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, ...items: T[]): Thenable; + + /** + * Show a warning message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, ...items: T[]): Thenable; + + /** + * Show an error message. + * + * @see {@link window.showInformationMessage showInformationMessage} + * + * @param message The message to show. + * @param options Configures the behaviour of the message. + * @param items A set of items that will be rendered as actions in the message. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. + */ + export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; + + /** + * Shows a selection list allowing multiple selections. + * + * @param items An array of strings, or a promise that resolves to an array of strings. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @returns A promise that resolves to the selected items or `undefined`. + */ + export function showQuickPick(items: readonly string[] | Thenable, options: QuickPickOptions & { /** literal-type defines return type */canPickMany: true }, token?: CancellationToken): Thenable; + + /** + * Shows a selection list. + * + * @param items An array of strings, or a promise that resolves to an array of strings. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @returns A promise that resolves to the selection or `undefined`. + */ + export function showQuickPick(items: readonly string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; + + /** + * Shows a selection list allowing multiple selections. + * + * @param items An array of items, or a promise that resolves to an array of items. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @returns A promise that resolves to the selected items or `undefined`. + */ + export function showQuickPick(items: readonly T[] | Thenable, options: QuickPickOptions & { /** literal-type defines return type */ canPickMany: true }, token?: CancellationToken): Thenable; + + /** + * Shows a selection list. + * + * @param items An array of items, or a promise that resolves to an array of items. + * @param options Configures the behavior of the selection list. + * @param token A token that can be used to signal cancellation. + * @returns A promise that resolves to the selected item or `undefined`. + */ + export function showQuickPick(items: readonly T[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; + + /** + * Shows a selection list of {@link workspace.workspaceFolders workspace folders} to pick from. + * Returns `undefined` if no folder is open. + * + * @param options Configures the behavior of the workspace folder list. + * @returns A promise that resolves to the workspace folder or `undefined`. + */ + export function showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable; + + /** + * Shows a file open dialog to the user which allows to select a file + * for opening-purposes. + * + * @param options Options that control the dialog. + * @returns A promise that resolves to the selected resources or `undefined`. + */ + export function showOpenDialog(options?: OpenDialogOptions): Thenable; + + /** + * Shows a file save dialog to the user which allows to select a file + * for saving-purposes. + * + * @param options Options that control the dialog. + * @returns A promise that resolves to the selected resource or `undefined`. + */ + export function showSaveDialog(options?: SaveDialogOptions): Thenable; + + /** + * Opens an input box to ask the user for input. + * + * The returned value will be `undefined` if the input box was canceled (e.g. pressing ESC). Otherwise the + * returned value will be the string typed by the user or an empty string if the user did not type + * anything but dismissed the input box with OK. + * + * @param options Configures the behavior of the input box. + * @param token A token that can be used to signal cancellation. + * @returns A promise that resolves to a string the user provided or to `undefined` in case of dismissal. + */ + export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable; + + /** + * Creates a {@link QuickPick} to let the user pick an item from a list + * of items of type T. + * + * Note that in many cases the more convenient {@link window.showQuickPick} + * is easier to use. {@link window.createQuickPick} should be used + * when {@link window.showQuickPick} does not offer the required flexibility. + * + * @returns A new {@link QuickPick}. + */ + export function createQuickPick(): QuickPick; + + /** + * Creates a {@link InputBox} to let the user enter some text input. + * + * Note that in many cases the more convenient {@link window.showInputBox} + * is easier to use. {@link window.createInputBox} should be used + * when {@link window.showInputBox} does not offer the required flexibility. + * + * @returns A new {@link InputBox}. + */ + export function createInputBox(): InputBox; + + /** + * Creates a new {@link OutputChannel output channel} with the given name and language id + * If language id is not provided, then **Log** is used as default language id. + * + * You can access the visible or active output channel as a {@link TextDocument text document} from {@link window.visibleTextEditors visible editors} or {@link window.activeTextEditor active editor} + * and use the language id to contribute language features like syntax coloring, code lens etc., + * + * @param name Human-readable string which will be used to represent the channel in the UI. + * @param languageId The identifier of the language associated with the channel. + * @returns A new output channel. + */ + export function createOutputChannel(name: string, languageId?: string): OutputChannel; + + /** + * Creates a new {@link LogOutputChannel log output channel} with the given name. + * + * @param name Human-readable string which will be used to represent the channel in the UI. + * @param options Options for the log output channel. + * @returns A new log output channel. + */ + export function createOutputChannel(name: string, options: { /** literal-type defines return type */log: true }): LogOutputChannel; + + /** + * Create and show a new webview panel. + * + * @param viewType Identifies the type of the webview panel. + * @param title Title of the panel. + * @param showOptions Where to show the webview in the editor. If preserveFocus is set, the new webview will not take focus. + * @param options Settings for the new panel. + * + * @returns New webview panel. + */ + export function createWebviewPanel(viewType: string, title: string, showOptions: ViewColumn | { + /** + * The view column in which the {@link WebviewPanel} should be shown. + */ + readonly viewColumn: ViewColumn; + /** + * An optional flag that when `true` will stop the panel from taking focus. + */ + readonly preserveFocus?: boolean; + }, options?: WebviewPanelOptions & WebviewOptions): WebviewPanel; + + /** + * Set a message to the status bar. This is a short hand for the more powerful + * status bar {@link window.createStatusBarItem items}. + * + * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. + * @param hideAfterTimeout Timeout in milliseconds after which the message will be disposed. + * @returns A disposable which hides the status bar message. + */ + export function setStatusBarMessage(text: string, hideAfterTimeout: number): Disposable; + + /** + * Set a message to the status bar. This is a short hand for the more powerful + * status bar {@link window.createStatusBarItem items}. + * + * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. + * @param hideWhenDone Thenable on which completion (resolve or reject) the message will be disposed. + * @returns A disposable which hides the status bar message. + */ + export function setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable; + + /** + * Set a message to the status bar. This is a short hand for the more powerful + * status bar {@link window.createStatusBarItem items}. + * + * *Note* that status bar messages stack and that they must be disposed when no + * longer used. + * + * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. + * @returns A disposable which hides the status bar message. + */ + export function setStatusBarMessage(text: string): Disposable; + + /** + * Show progress in the Source Control viewlet while running the given callback and while + * its returned promise isn't resolve or rejected. + * + * @deprecated Use `withProgress` instead. + * + * @param task A callback returning a promise. Progress increments can be reported with + * the provided {@link Progress}-object. + * @returns The thenable the task did return. + */ + export function withScmProgress(task: (progress: Progress) => Thenable): Thenable; + + /** + * Show progress in the editor. Progress is shown while running the given callback + * and while the promise it returned isn't resolved nor rejected. The location at which + * progress should show (and other details) is defined via the passed {@linkcode ProgressOptions}. + * + * @param options A {@linkcode ProgressOptions}-object describing the options to use for showing progress, like its location + * @param task A callback returning a promise. Progress state can be reported with + * the provided {@link Progress}-object. + * + * To report discrete progress, use `increment` to indicate how much work has been completed. Each call with + * a `increment` value will be summed up and reflected as overall progress until 100% is reached (a value of + * e.g. `10` accounts for `10%` of work done). + * Note that currently only `ProgressLocation.Notification` is capable of showing discrete progress. + * + * To monitor if the operation has been cancelled by the user, use the provided {@linkcode CancellationToken}. + * Note that currently only `ProgressLocation.Notification` is supporting to show a cancel button to cancel the + * long running operation. + * + * @returns The thenable the task-callback returned. + */ + export function withProgress(options: ProgressOptions, task: (progress: Progress<{ + /** + * A progress message that represents a chunk of work + */ + message?: string; + /** + * An increment for discrete progress. Increments will be summed up until 100% is reached + */ + increment?: number; + }>, token: CancellationToken) => Thenable): Thenable; + + /** + * Creates a status bar {@link StatusBarItem item}. + * + * @param id The identifier of the item. Must be unique within the extension. + * @param alignment The alignment of the item. + * @param priority The priority of the item. Higher values mean the item should be shown more to the left. + * @returns A new status bar item. + */ + export function createStatusBarItem(id: string, alignment?: StatusBarAlignment, priority?: number): StatusBarItem; + + /** + * Creates a status bar {@link StatusBarItem item}. + * + * @see {@link createStatusBarItem} for creating a status bar item with an identifier. + * @param alignment The alignment of the item. + * @param priority The priority of the item. Higher values mean the item should be shown more to the left. + * @returns A new status bar item. + */ + export function createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem; + + /** + * Creates a {@link Terminal} with a backing shell process. The cwd of the terminal will be the workspace + * directory if it exists. + * + * @param name Optional human-readable string which will be used to represent the terminal in the UI. + * @param shellPath Optional path to a custom shell executable to be used in the terminal. + * @param shellArgs Optional args for the custom shell executable. A string can be used on Windows only which + * allows specifying shell args in + * [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). + * @returns A new Terminal. + * @throws When running in an environment where a new process cannot be started. + */ + export function createTerminal(name?: string, shellPath?: string, shellArgs?: readonly string[] | string): Terminal; + + /** + * Creates a {@link Terminal} with a backing shell process. + * + * @param options A TerminalOptions object describing the characteristics of the new terminal. + * @returns A new Terminal. + * @throws When running in an environment where a new process cannot be started. + */ + export function createTerminal(options: TerminalOptions): Terminal; + + /** + * Creates a {@link Terminal} where an extension controls its input and output. + * + * @param options An {@link ExtensionTerminalOptions} object describing + * the characteristics of the new terminal. + * @returns A new Terminal. + */ + export function createTerminal(options: ExtensionTerminalOptions): Terminal; + + /** + * Register a {@link TreeDataProvider} for the view contributed using the extension point `views`. + * This will allow you to contribute data to the {@link TreeView} and update if the data changes. + * + * **Note:** To get access to the {@link TreeView} and perform operations on it, use {@link window.createTreeView createTreeView}. + * + * @param viewId Id of the view contributed using the extension point `views`. + * @param treeDataProvider A {@link TreeDataProvider} that provides tree data for the view + * @returns A {@link Disposable disposable} that unregisters the {@link TreeDataProvider}. + */ + export function registerTreeDataProvider(viewId: string, treeDataProvider: TreeDataProvider): Disposable; + + /** + * Create a {@link TreeView} for the view contributed using the extension point `views`. + * @param viewId Id of the view contributed using the extension point `views`. + * @param options Options for creating the {@link TreeView} + * @returns a {@link TreeView}. + */ + export function createTreeView(viewId: string, options: TreeViewOptions): TreeView; + + /** + * Registers a {@link UriHandler uri handler} capable of handling system-wide {@link Uri uris}. + * In case there are multiple windows open, the topmost window will handle the uri. + * A uri handler is scoped to the extension it is contributed from; it will only + * be able to handle uris which are directed to the extension itself. A uri must respect + * the following rules: + * + * - The uri-scheme must be `vscode.env.uriScheme`; + * - The uri-authority must be the extension id (e.g. `my.extension`); + * - The uri-path, -query and -fragment parts are arbitrary. + * + * For example, if the `my.extension` extension registers a uri handler, it will only + * be allowed to handle uris with the prefix `product-name://my.extension`. + * + * An extension can only register a single uri handler in its entire activation lifetime. + * + * * *Note:* There is an activation event `onUri` that fires when a uri directed for + * the current extension is about to be handled. + * + * @param handler The uri handler to register for this extension. + * @returns A {@link Disposable disposable} that unregisters the handler. + */ + export function registerUriHandler(handler: UriHandler): Disposable; + + /** + * Registers a webview panel serializer. + * + * Extensions that support reviving should have an `"onWebviewPanel:viewType"` activation event and + * make sure that `registerWebviewPanelSerializer` is called during activation. + * + * Only a single serializer may be registered at a time for a given `viewType`. + * + * @param viewType Type of the webview panel that can be serialized. + * @param serializer Webview serializer. + * @returns A {@link Disposable disposable} that unregisters the serializer. + */ + export function registerWebviewPanelSerializer(viewType: string, serializer: WebviewPanelSerializer): Disposable; + + /** + * Register a new provider for webview views. + * + * @param viewId Unique id of the view. This should match the `id` from the + * `views` contribution in the package.json. + * @param provider Provider for the webview views. + * + * @returns Disposable that unregisters the provider. + */ + export function registerWebviewViewProvider(viewId: string, provider: WebviewViewProvider, options?: { + /** + * Content settings for the webview created for this view. + */ + readonly webviewOptions?: { + /** + * Controls if the webview element itself (iframe) is kept around even when the view + * is no longer visible. + * + * Normally the webview's html context is created when the view becomes visible + * and destroyed when it is hidden. Extensions that have complex state + * or UI can set the `retainContextWhenHidden` to make the editor keep the webview + * context around, even when the webview moves to a background tab. When a webview using + * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. + * When the view becomes visible again, the context is automatically restored + * in the exact same state it was in originally. You cannot send messages to a + * hidden webview, even with `retainContextWhenHidden` enabled. + * + * `retainContextWhenHidden` has a high memory overhead and should only be used if + * your view's context cannot be quickly saved and restored. + */ + readonly retainContextWhenHidden?: boolean; + }; + }): Disposable; + + /** + * Register a provider for custom editors for the `viewType` contributed by the `customEditors` extension point. + * + * When a custom editor is opened, an `onCustomEditor:viewType` activation event is fired. Your extension + * must register a {@linkcode CustomTextEditorProvider}, {@linkcode CustomReadonlyEditorProvider}, + * {@linkcode CustomEditorProvider}for `viewType` as part of activation. + * + * @param viewType Unique identifier for the custom editor provider. This should match the `viewType` from the + * `customEditors` contribution point. + * @param provider Provider that resolves custom editors. + * @param options Options for the provider. + * + * @returns Disposable that unregisters the provider. + */ + export function registerCustomEditorProvider(viewType: string, provider: CustomTextEditorProvider | CustomReadonlyEditorProvider | CustomEditorProvider, options?: { + /** + * Content settings for the webview panels created for this custom editor. + */ + readonly webviewOptions?: WebviewPanelOptions; + + /** + * Only applies to `CustomReadonlyEditorProvider | CustomEditorProvider`. + * + * Indicates that the provider allows multiple editor instances to be open at the same time for + * the same resource. + * + * By default, the editor only allows one editor instance to be open at a time for each resource. If the + * user tries to open a second editor instance for the resource, the first one is instead moved to where + * the second one was to be opened. + * + * When `supportsMultipleEditorsPerDocument` is enabled, users can split and create copies of the custom + * editor. In this case, the custom editor must make sure it can properly synchronize the states of all + * editor instances for a resource so that they are consistent. + */ + readonly supportsMultipleEditorsPerDocument?: boolean; + }): Disposable; + + /** + * Register provider that enables the detection and handling of links within the terminal. + * @param provider The provider that provides the terminal links. + * @returns Disposable that unregisters the provider. + */ + export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; + + /** + * Registers a provider for a contributed terminal profile. + * + * @param id The ID of the contributed terminal profile. + * @param provider The terminal profile provider. + * @returns A {@link Disposable disposable} that unregisters the provider. + */ + export function registerTerminalProfileProvider(id: string, provider: TerminalProfileProvider): Disposable; + /** + * Register a file decoration provider. + * + * @param provider A {@link FileDecorationProvider}. + * @returns A {@link Disposable} that unregisters the provider. + */ + export function registerFileDecorationProvider(provider: FileDecorationProvider): Disposable; + + /** + * The currently active color theme as configured in the settings. The active + * theme can be changed via the `workbench.colorTheme` setting. + */ + export let activeColorTheme: ColorTheme; + + /** + * An {@link Event} which fires when the active color theme is changed or has changes. + */ + export const onDidChangeActiveColorTheme: Event; + } + + /** + * Options for creating a {@link TreeView} + */ + export interface TreeViewOptions { + + /** + * A data provider that provides tree data. + */ + treeDataProvider: TreeDataProvider; + + /** + * Whether to show collapse all action or not. + */ + showCollapseAll?: boolean; + + /** + * Whether the tree supports multi-select. When the tree supports multi-select and a command is executed from the tree, + * the first argument to the command is the tree item that the command was executed on and the second argument is an + * array containing all selected tree items. + */ + canSelectMany?: boolean; + + /** + * An optional interface to implement drag and drop in the tree view. + */ + dragAndDropController?: TreeDragAndDropController; + + /** + * By default, when the children of a tree item have already been fetched, child checkboxes are automatically managed based on the checked state of the parent tree item. + * If the tree item is collapsed by default (meaning that the children haven't yet been fetched) then child checkboxes will not be updated. + * To override this behavior and manage child and parent checkbox state in the extension, set this to `true`. + * + * Examples where {@link TreeViewOptions.manageCheckboxStateManually} is false, the default behavior: + * + * 1. A tree item is checked, then its children are fetched. The children will be checked. + * + * 2. A tree item's parent is checked. The tree item and all of it's siblings will be checked. + * - [ ] Parent + * - [ ] Child 1 + * - [ ] Child 2 + * When the user checks Parent, the tree will look like this: + * - [x] Parent + * - [x] Child 1 + * - [x] Child 2 + * + * 3. A tree item and all of it's siblings are checked. The parent will be checked. + * - [ ] Parent + * - [ ] Child 1 + * - [ ] Child 2 + * When the user checks Child 1 and Child 2, the tree will look like this: + * - [x] Parent + * - [x] Child 1 + * - [x] Child 2 + * + * 4. A tree item is unchecked. The parent will be unchecked. + * - [x] Parent + * - [x] Child 1 + * - [x] Child 2 + * When the user unchecks Child 1, the tree will look like this: + * - [ ] Parent + * - [ ] Child 1 + * - [x] Child 2 + */ + manageCheckboxStateManually?: boolean; + } + + /** + * The event that is fired when an element in the {@link TreeView} is expanded or collapsed + */ + export interface TreeViewExpansionEvent { + + /** + * Element that is expanded or collapsed. + */ + readonly element: T; + + } + + /** + * The event that is fired when there is a change in {@link TreeView.selection tree view's selection} + */ + export interface TreeViewSelectionChangeEvent { + + /** + * Selected elements. + */ + readonly selection: readonly T[]; + + } + + /** + * The event that is fired when there is a change in {@link TreeView.visible tree view's visibility} + */ + export interface TreeViewVisibilityChangeEvent { + + /** + * `true` if the {@link TreeView tree view} is visible otherwise `false`. + */ + readonly visible: boolean; + } + + /** + * A file associated with a {@linkcode DataTransferItem}. + * + * Instances of this type can only be created by the editor and not by extensions. + */ + export interface DataTransferFile { + /** + * The name of the file. + */ + readonly name: string; + + /** + * The full file path of the file. + * + * May be `undefined` on web. + */ + readonly uri?: Uri; + + /** + * The full file contents of the file. + */ + data(): Thenable; + } + + /** + * Encapsulates data transferred during drag and drop operations. + */ + export class DataTransferItem { + /** + * Get a string representation of this item. + * + * If {@linkcode DataTransferItem.value} is an object, this returns the result of json stringifying {@linkcode DataTransferItem.value} value. + */ + asString(): Thenable; + + /** + * Try getting the {@link DataTransferFile file} associated with this data transfer item. + * + * Note that the file object is only valid for the scope of the drag and drop operation. + * + * @returns The file for the data transfer or `undefined` if the item is either not a file or the + * file data cannot be accessed. + */ + asFile(): DataTransferFile | undefined; + + /** + * Custom data stored on this item. + * + * You can use `value` to share data across operations. The original object can be retrieved so long as the extension that + * created the `DataTransferItem` runs in the same extension host. + */ + readonly value: any; + + /** + * @param value Custom data stored on this item. Can be retrieved using {@linkcode DataTransferItem.value}. + */ + constructor(value: any); + } + + /** + * A map containing a mapping of the mime type of the corresponding transferred data. + * + * Drag and drop controllers that implement {@link TreeDragAndDropController.handleDrag `handleDrag`} can add additional mime types to the + * data transfer. These additional mime types will only be included in the `handleDrop` when the the drag was initiated from + * an element in the same drag and drop controller. + */ + export class DataTransfer implements Iterable<[mimeType: string, item: DataTransferItem]> { + /** + * Retrieves the data transfer item for a given mime type. + * + * @param mimeType The mime type to get the data transfer item for, such as `text/plain` or `image/png`. + * Mimes type look ups are case-insensitive. + * + * Special mime types: + * - `text/uri-list` — A string with `toString()`ed Uris separated by `\r\n`. To specify a cursor position in the file, + * set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number. + */ + get(mimeType: string): DataTransferItem | undefined; + + /** + * Sets a mime type to data transfer item mapping. + * + * @param mimeType The mime type to set the data for. Mimes types stored in lower case, with case-insensitive looks up. + * @param value The data transfer item for the given mime type. + */ + set(mimeType: string, value: DataTransferItem): void; + + /** + * Allows iteration through the data transfer items. + * + * @param callbackfn Callback for iteration through the data transfer items. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callbackfn: (item: DataTransferItem, mimeType: string, dataTransfer: DataTransfer) => void, thisArg?: any): void; + + /** + * Get a new iterator with the `[mime, item]` pairs for each element in this data transfer. + */ + [Symbol.iterator](): IterableIterator<[mimeType: string, item: DataTransferItem]>; + } + + /** + * Provides support for drag and drop in `TreeView`. + */ + export interface TreeDragAndDropController { + + /** + * The mime types that the {@link TreeDragAndDropController.handleDrop `handleDrop`} method of this `DragAndDropController` supports. + * This could be well-defined, existing, mime types, and also mime types defined by the extension. + * + * To support drops from trees, you will need to add the mime type of that tree. + * This includes drops from within the same tree. + * The mime type of a tree is recommended to be of the format `application/vnd.code.tree.`. + * + * Use the special `files` mime type to support all types of dropped files {@link DataTransferFile files}, regardless of the file's actual mime type. + * + * To learn the mime type of a dragged item: + * 1. Set up your `DragAndDropController` + * 2. Use the Developer: Set Log Level... command to set the level to "Debug" + * 3. Open the developer tools and drag the item with unknown mime type over your tree. The mime types will be logged to the developer console + * + * Note that mime types that cannot be sent to the extension will be omitted. + */ + readonly dropMimeTypes: readonly string[]; + + /** + * The mime types that the {@link TreeDragAndDropController.handleDrag `handleDrag`} method of this `TreeDragAndDropController` may add to the tree data transfer. + * This could be well-defined, existing, mime types, and also mime types defined by the extension. + * + * The recommended mime type of the tree (`application/vnd.code.tree.`) will be automatically added. + */ + readonly dragMimeTypes: readonly string[]; + + /** + * When the user starts dragging items from this `DragAndDropController`, `handleDrag` will be called. + * Extensions can use `handleDrag` to add their {@link DataTransferItem `DataTransferItem`} items to the drag and drop. + * + * When the items are dropped on **another tree item** in **the same tree**, your `DataTransferItem` objects + * will be preserved. Use the recommended mime type for the tree (`application/vnd.code.tree.`) to add + * tree objects in a data transfer. See the documentation for `DataTransferItem` for how best to take advantage of this. + * + * To add a data transfer item that can be dragged into the editor, use the application specific mime type "text/uri-list". + * The data for "text/uri-list" should be a string with `toString()`ed Uris separated by `\r\n`. To specify a cursor position in the file, + * set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number. + * + * @param source The source items for the drag and drop operation. + * @param dataTransfer The data transfer associated with this drag. + * @param token A cancellation token indicating that drag has been cancelled. + */ + handleDrag?(source: readonly T[], dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; + + /** + * Called when a drag and drop action results in a drop on the tree that this `DragAndDropController` belongs to. + * + * Extensions should fire {@link TreeDataProvider.onDidChangeTreeData onDidChangeTreeData} for any elements that need to be refreshed. + * + * @param target The target tree element that the drop is occurring on. When undefined, the target is the root. + * @param dataTransfer The data transfer items of the source of the drag. + * @param token A cancellation token indicating that the drop has been cancelled. + */ + handleDrop?(target: T | undefined, dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; + } + + /** + * A badge presenting a value for a view + */ + export interface ViewBadge { + + /** + * A label to present in tooltip for the badge. + */ + readonly tooltip: string; + + /** + * The value to present in the badge. + */ + readonly value: number; + } + + /** + * An event describing the change in a tree item's checkbox state. + */ + export interface TreeCheckboxChangeEvent { + /** + * The items that were checked or unchecked. + */ + readonly items: ReadonlyArray<[T, TreeItemCheckboxState]>; + } + + /** + * Represents a Tree view + */ + export interface TreeView extends Disposable { + + /** + * Event that is fired when an element is expanded + */ + readonly onDidExpandElement: Event>; + + /** + * Event that is fired when an element is collapsed + */ + readonly onDidCollapseElement: Event>; + + /** + * Currently selected elements. + */ + readonly selection: readonly T[]; + + /** + * Event that is fired when the {@link TreeView.selection selection} has changed + */ + readonly onDidChangeSelection: Event>; + + /** + * `true` if the {@link TreeView tree view} is visible otherwise `false`. + */ + readonly visible: boolean; + + /** + * Event that is fired when {@link TreeView.visible visibility} has changed + */ + readonly onDidChangeVisibility: Event; + + /** + * An event to signal that an element or root has either been checked or unchecked. + */ + readonly onDidChangeCheckboxState: Event>; + + /** + * An optional human-readable message that will be rendered in the view. + * Setting the message to null, undefined, or empty string will remove the message from the view. + */ + message?: string; + + /** + * The tree view title is initially taken from the extension package.json + * Changes to the title property will be properly reflected in the UI in the title of the view. + */ + title?: string; + + /** + * An optional human-readable description which is rendered less prominently in the title of the view. + * Setting the title description to null, undefined, or empty string will remove the description from the view. + */ + description?: string; + + /** + * The badge to display for this TreeView. + * To remove the badge, set to undefined. + */ + badge?: ViewBadge | undefined; + + /** + * Reveals the given element in the tree view. + * If the tree view is not visible then the tree view is shown and element is revealed. + * + * By default revealed element is selected. + * In order to not to select, set the option `select` to `false`. + * In order to focus, set the option `focus` to `true`. + * In order to expand the revealed element, set the option `expand` to `true`. To expand recursively set `expand` to the number of levels to expand. + * + * * *NOTE:* You can expand only to 3 levels maximum. + * * *NOTE:* The {@link TreeDataProvider} that the `TreeView` {@link window.createTreeView is registered with} with must implement {@link TreeDataProvider.getParent getParent} method to access this API. + */ + reveal(element: T, options?: { + /** + * If true, then the element will be selected. + */ + readonly select?: boolean; + /** + * If true, then the element will be focused. + */ + readonly focus?: boolean; + /** + * If true, then the element will be expanded. If a number is passed, then up to that number of levels of children will be expanded + */ + readonly expand?: boolean | number; + }): Thenable; + } + + /** + * A data provider that provides tree data + */ + export interface TreeDataProvider { + /** + * An optional event to signal that an element or root has changed. + * This will trigger the view to update the changed element/root and its children recursively (if shown). + * To signal that root has changed, do not pass any argument or pass `undefined` or `null`. + */ + onDidChangeTreeData?: Event; + + /** + * Get {@link TreeItem} representation of the `element` + * + * @param element The element for which {@link TreeItem} representation is asked for. + * @returns TreeItem representation of the element. + */ + getTreeItem(element: T): TreeItem | Thenable; + + /** + * Get the children of `element` or root if no element is passed. + * + * @param element The element from which the provider gets children. Can be `undefined`. + * @returns Children of `element` or root if no element is passed. + */ + getChildren(element?: T): ProviderResult; + + /** + * Optional method to return the parent of `element`. + * Return `null` or `undefined` if `element` is a child of root. + * + * **NOTE:** This method should be implemented in order to access {@link TreeView.reveal reveal} API. + * + * @param element The element for which the parent has to be returned. + * @returns Parent of `element`. + */ + getParent?(element: T): ProviderResult; + + /** + * Called on hover to resolve the {@link TreeItem.tooltip TreeItem} property if it is undefined. + * Called on tree item click/open to resolve the {@link TreeItem.command TreeItem} property if it is undefined. + * Only properties that were undefined can be resolved in `resolveTreeItem`. + * Functionality may be expanded later to include being called to resolve other missing + * properties on selection and/or on open. + * + * Will only ever be called once per TreeItem. + * + * onDidChangeTreeData should not be triggered from within resolveTreeItem. + * + * *Note* that this function is called when tree items are already showing in the UI. + * Because of that, no property that changes the presentation (label, description, etc.) + * can be changed. + * + * @param item Undefined properties of `item` should be set then `item` should be returned. + * @param element The object associated with the TreeItem. + * @param token A cancellation token. + * @returns The resolved tree item or a thenable that resolves to such. It is OK to return the given + * `item`. When no result is returned, the given `item` will be used. + */ + resolveTreeItem?(item: TreeItem, element: T, token: CancellationToken): ProviderResult; + } + + /** + * A tree item is an UI element of the tree. Tree items are created by the {@link TreeDataProvider data provider}. + */ + export class TreeItem { + /** + * A human-readable string describing this item. When `falsy`, it is derived from {@link TreeItem.resourceUri resourceUri}. + */ + label?: string | TreeItemLabel; + + /** + * Optional id for the tree item that has to be unique across tree. The id is used to preserve the selection and expansion state of the tree item. + * + * If not provided, an id is generated using the tree item's label. **Note** that when labels change, ids will change and that selection and expansion state cannot be kept stable anymore. + */ + id?: string; + + /** + * The icon path or {@link ThemeIcon} for the tree item. + * When `falsy`, {@link ThemeIcon.Folder Folder Theme Icon} is assigned, if item is collapsible otherwise {@link ThemeIcon.File File Theme Icon}. + * When a file or folder {@link ThemeIcon} is specified, icon is derived from the current file icon theme for the specified theme icon using {@link TreeItem.resourceUri resourceUri} (if provided). + */ + iconPath?: string | IconPath; + + /** + * A human-readable string which is rendered less prominent. + * When `true`, it is derived from {@link TreeItem.resourceUri resourceUri} and when `falsy`, it is not shown. + */ + description?: string | boolean; + + /** + * The {@link Uri} of the resource representing this item. + * + * Will be used to derive the {@link TreeItem.label label}, when it is not provided. + * Will be used to derive the icon from current file icon theme, when {@link TreeItem.iconPath iconPath} has {@link ThemeIcon} value. + */ + resourceUri?: Uri; + + /** + * The tooltip text when you hover over this item. + */ + tooltip?: string | MarkdownString | undefined; + + /** + * The {@link Command} that should be executed when the tree item is selected. + * + * Please use `vscode.open` or `vscode.diff` as command IDs when the tree item is opening + * something in the editor. Using these commands ensures that the resulting editor will + * appear consistent with how other built-in trees open editors. + */ + command?: Command; + + /** + * {@link TreeItemCollapsibleState} of the tree item. + */ + collapsibleState?: TreeItemCollapsibleState; + + /** + * Context value of the tree item. This can be used to contribute item specific actions in the tree. + * For example, a tree item is given a context value as `folder`. When contributing actions to `view/item/context` + * using `menus` extension point, you can specify context value for key `viewItem` in `when` expression like `viewItem == folder`. + * ```json + * "contributes": { + * "menus": { + * "view/item/context": [ + * { + * "command": "extension.deleteFolder", + * "when": "viewItem == folder" + * } + * ] + * } + * } + * ``` + * This will show action `extension.deleteFolder` only for items with `contextValue` is `folder`. + */ + contextValue?: string; + + /** + * Accessibility information used when screen reader interacts with this tree item. + * Generally, a TreeItem has no need to set the `role` of the accessibilityInformation; + * however, there are cases where a TreeItem is not displayed in a tree-like way where setting the `role` may make sense. + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * {@link TreeItemCheckboxState TreeItemCheckboxState} of the tree item. + * {@link TreeDataProvider.onDidChangeTreeData onDidChangeTreeData} should be fired when {@link TreeItem.checkboxState checkboxState} changes. + */ + checkboxState?: TreeItemCheckboxState | { + /** + * The {@link TreeItemCheckboxState} of the tree item + */ + readonly state: TreeItemCheckboxState; + /** + * A tooltip for the checkbox + */ + readonly tooltip?: string; + /** + * Accessibility information used when screen readers interact with this checkbox + */ + readonly accessibilityInformation?: AccessibilityInformation; + }; + + /** + * @param label A human-readable string describing this item + * @param collapsibleState {@link TreeItemCollapsibleState} of the tree item. Default is {@link TreeItemCollapsibleState.None} + */ + constructor(label: string | TreeItemLabel, collapsibleState?: TreeItemCollapsibleState); + + /** + * @param resourceUri The {@link Uri} of the resource representing this item. + * @param collapsibleState {@link TreeItemCollapsibleState} of the tree item. Default is {@link TreeItemCollapsibleState.None} + */ + constructor(resourceUri: Uri, collapsibleState?: TreeItemCollapsibleState); + } + + /** + * Collapsible state of the tree item + */ + export enum TreeItemCollapsibleState { + /** + * Determines an item can be neither collapsed nor expanded. Implies it has no children. + */ + None = 0, + /** + * Determines an item is collapsed + */ + Collapsed = 1, + /** + * Determines an item is expanded + */ + Expanded = 2 + } + + /** + * Label describing the {@link TreeItem Tree item} + */ + export interface TreeItemLabel { + + /** + * A human-readable string describing the {@link TreeItem Tree item}. + */ + label: string; + + /** + * Ranges in the label to highlight. A range is defined as a tuple of two number where the + * first is the inclusive start index and the second the exclusive end index + */ + highlights?: [number, number][]; + } + + /** + * Checkbox state of the tree item + */ + export enum TreeItemCheckboxState { + /** + * Determines an item is unchecked + */ + Unchecked = 0, + /** + * Determines an item is checked + */ + Checked = 1 + } + + /** + * Value-object describing what options a terminal should use. + */ + export interface TerminalOptions { + /** + * A human-readable string which will be used to represent the terminal in the UI. + */ + name?: string; + + /** + * A path to a custom shell executable to be used in the terminal. + */ + shellPath?: string; + + /** + * Args for the custom shell executable. A string can be used on Windows only which allows + * specifying shell args in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). + */ + shellArgs?: string[] | string; + + /** + * A path or Uri for the current working directory to be used for the terminal. + */ + cwd?: string | Uri; + + /** + * Object with environment variables that will be added to the editor process. + */ + env?: { [key: string]: string | null | undefined }; + + /** + * Whether the terminal process environment should be exactly as provided in + * `TerminalOptions.env`. When this is false (default), the environment will be based on the + * window's environment and also apply configured platform settings like + * `terminal.integrated.env.windows` on top. When this is true, the complete environment + * must be provided as nothing will be inherited from the process or any configuration. + */ + strictEnv?: boolean; + + /** + * When enabled the terminal will run the process as normal but not be surfaced to the user + * until `Terminal.show` is called. The typical usage for this is when you need to run + * something that may need interactivity but only want to tell the user about it when + * interaction is needed. Note that the terminals will still be exposed to all extensions + * as normal. The hidden terminals will not be restored when the workspace is next opened. + */ + hideFromUser?: boolean; + + /** + * A message to write to the terminal on first launch, note that this is not sent to the + * process but, rather written directly to the terminal. This supports escape sequences such + * a setting text style. + */ + message?: string; + + /** + * The icon path or {@link ThemeIcon} for the terminal. + */ + iconPath?: IconPath; + + /** + * The icon {@link ThemeColor} for the terminal. + * The `terminal.ansi*` theme keys are + * recommended for the best contrast and consistency across themes. + */ + color?: ThemeColor; + + /** + * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. + */ + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + + /** + * Opt-out of the default terminal persistence on restart and reload. + * This will only take effect when `terminal.integrated.enablePersistentSessions` is enabled. + */ + isTransient?: boolean; + } + + /** + * Value-object describing what options a virtual process terminal should use. + */ + export interface ExtensionTerminalOptions { + /** + * A human-readable string which will be used to represent the terminal in the UI. + */ + name: string; + + /** + * An implementation of {@link Pseudoterminal} that allows an extension to + * control a terminal. + */ + pty: Pseudoterminal; + + /** + * The icon path or {@link ThemeIcon} for the terminal. + */ + iconPath?: IconPath; + + /** + * The icon {@link ThemeColor} for the terminal. + * The standard `terminal.ansi*` theme keys are + * recommended for the best contrast and consistency across themes. + */ + color?: ThemeColor; + + /** + * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. + */ + location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; + + /** + * Opt-out of the default terminal persistence on restart and reload. + * This will only take effect when `terminal.integrated.enablePersistentSessions` is enabled. + */ + isTransient?: boolean; + } + + /** + * Defines the interface of a terminal pty, enabling extensions to control a terminal. + */ + interface Pseudoterminal { + /** + * An event that when fired will write data to the terminal. Unlike + * {@link Terminal.sendText} which sends text to the underlying child + * pseudo-device (the child), this will write the text to parent pseudo-device (the + * _terminal_ itself). + * + * Note writing `\n` will just move the cursor down 1 row, you need to write `\r` as well + * to move the cursor to the left-most cell. + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * **Example:** Write red text to the terminal + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * open: () => writeEmitter.fire('\x1b[31mHello world\x1b[0m'), + * close: () => {} + * }; + * vscode.window.createTerminal({ name: 'My terminal', pty }); + * ``` + * + * **Example:** Move the cursor to the 10th row and 20th column and write an asterisk + * ```typescript + * writeEmitter.fire('\x1b[10;20H*'); + * ``` + */ + onDidWrite: Event; + + /** + * An event that when fired allows overriding the {@link Pseudoterminal.setDimensions dimensions} of the + * terminal. Note that when set, the overridden dimensions will only take effect when they + * are lower than the actual dimensions of the terminal (ie. there will never be a scroll + * bar). Set to `undefined` for the terminal to go back to the regular dimensions (fit to + * the size of the panel). + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * **Example:** Override the dimensions of a terminal to 20 columns and 10 rows + * ```typescript + * const dimensionsEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * onDidOverrideDimensions: dimensionsEmitter.event, + * open: () => { + * dimensionsEmitter.fire({ + * columns: 20, + * rows: 10 + * }); + * }, + * close: () => {} + * }; + * vscode.window.createTerminal({ name: 'My terminal', pty }); + * ``` + */ + onDidOverrideDimensions?: Event; + + /** + * An event that when fired will signal that the pty is closed and dispose of the terminal. + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * A number can be used to provide an exit code for the terminal. Exit codes must be + * positive and a non-zero exit codes signals failure which shows a notification for a + * regular terminal and allows dependent tasks to proceed when used with the + * `CustomExecution` API. + * + * **Example:** Exit the terminal when "y" is pressed, otherwise show a notification. + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const closeEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * onDidClose: closeEmitter.event, + * open: () => writeEmitter.fire('Press y to exit successfully'), + * close: () => {}, + * handleInput: data => { + * if (data !== 'y') { + * vscode.window.showInformationMessage('Something went wrong'); + * } + * closeEmitter.fire(); + * } + * }; + * const terminal = vscode.window.createTerminal({ name: 'Exit example', pty }); + * terminal.show(true); + * ``` + */ + onDidClose?: Event; + + /** + * An event that when fired allows changing the name of the terminal. + * + * Events fired before {@link Pseudoterminal.open} is called will be be ignored. + * + * **Example:** Change the terminal name to "My new terminal". + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const changeNameEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * onDidChangeName: changeNameEmitter.event, + * open: () => changeNameEmitter.fire('My new terminal'), + * close: () => {} + * }; + * vscode.window.createTerminal({ name: 'My terminal', pty }); + * ``` + */ + onDidChangeName?: Event; + + /** + * Implement to handle when the pty is open and ready to start firing events. + * + * @param initialDimensions The dimensions of the terminal, this will be undefined if the + * terminal panel has not been opened before this is called. + */ + open(initialDimensions: TerminalDimensions | undefined): void; + + /** + * Implement to handle when the terminal is closed by an act of the user. + */ + close(): void; + + /** + * Implement to handle incoming keystrokes in the terminal or when an extension calls + * {@link Terminal.sendText}. `data` contains the keystrokes/text serialized into + * their corresponding VT sequence representation. + * + * @param data The incoming data. + * + * **Example:** Echo input in the terminal. The sequence for enter (`\r`) is translated to + * CRLF to go to a new line and move the cursor to the start of the line. + * ```typescript + * const writeEmitter = new vscode.EventEmitter(); + * const pty: vscode.Pseudoterminal = { + * onDidWrite: writeEmitter.event, + * open: () => {}, + * close: () => {}, + * handleInput: data => writeEmitter.fire(data === '\r' ? '\r\n' : data) + * }; + * vscode.window.createTerminal({ name: 'Local echo', pty }); + * ``` + */ + handleInput?(data: string): void; + + /** + * Implement to handle when the number of rows and columns that fit into the terminal panel + * changes, for example when font size changes or when the panel is resized. The initial + * state of a terminal's dimensions should be treated as `undefined` until this is triggered + * as the size of a terminal isn't known until it shows up in the user interface. + * + * When dimensions are overridden by + * {@link Pseudoterminal.onDidOverrideDimensions onDidOverrideDimensions}, `setDimensions` will + * continue to be called with the regular panel dimensions, allowing the extension continue + * to react dimension changes. + * + * @param dimensions The new dimensions. + */ + setDimensions?(dimensions: TerminalDimensions): void; + } + + /** + * Represents the dimensions of a terminal. + */ + export interface TerminalDimensions { + /** + * The number of columns in the terminal. + */ + readonly columns: number; + + /** + * The number of rows in the terminal. + */ + readonly rows: number; + } + + /** + * Represents how a terminal exited. + */ + export interface TerminalExitStatus { + /** + * The exit code that a terminal exited with, it can have the following values: + * - Zero: the terminal process or custom execution succeeded. + * - Non-zero: the terminal process or custom execution failed. + * - `undefined`: the user forcibly closed the terminal or a custom execution exited + * without providing an exit code. + */ + readonly code: number | undefined; + + /** + * The reason that triggered the exit of a terminal. + */ + readonly reason: TerminalExitReason; + } + + /** + * Terminal exit reason kind. + */ + export enum TerminalExitReason { + /** + * Unknown reason. + */ + Unknown = 0, + + /** + * The window closed/reloaded. + */ + Shutdown = 1, + + /** + * The shell process exited. + */ + Process = 2, + + /** + * The user closed the terminal. + */ + User = 3, + + /** + * An extension disposed the terminal. + */ + Extension = 4, + } + + /** + * A type of mutation that can be applied to an environment variable. + */ + export enum EnvironmentVariableMutatorType { + /** + * Replace the variable's existing value. + */ + Replace = 1, + /** + * Append to the end of the variable's existing value. + */ + Append = 2, + /** + * Prepend to the start of the variable's existing value. + */ + Prepend = 3 + } + + /** + * Options applied to the mutator. + */ + export interface EnvironmentVariableMutatorOptions { + /** + * Apply to the environment just before the process is created. Defaults to false. + */ + applyAtProcessCreation?: boolean; + + /** + * Apply to the environment in the shell integration script. Note that this _will not_ apply + * the mutator if shell integration is disabled or not working for some reason. Defaults to + * false. + */ + applyAtShellIntegration?: boolean; + } + + /** + * A type of mutation and its value to be applied to an environment variable. + */ + export interface EnvironmentVariableMutator { + /** + * The type of mutation that will occur to the variable. + */ + readonly type: EnvironmentVariableMutatorType; + + /** + * The value to use for the variable. + */ + readonly value: string; + + /** + * Options applied to the mutator. + */ + readonly options: EnvironmentVariableMutatorOptions; + } + + /** + * A collection of mutations that an extension can apply to a process environment. + */ + export interface EnvironmentVariableCollection extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> { + /** + * Whether the collection should be cached for the workspace and applied to the terminal + * across window reloads. When true the collection will be active immediately such when the + * window reloads. Additionally, this API will return the cached version if it exists. The + * collection will be invalidated when the extension is uninstalled or when the collection + * is cleared. Defaults to true. + */ + persistent: boolean; + + /** + * A description for the environment variable collection, this will be used to describe the + * changes in the UI. + */ + description: string | MarkdownString | undefined; + + /** + * Replace an environment variable with a value. + * + * Note that an extension can only make a single change to any one variable, so this will + * overwrite any previous calls to replace, append or prepend. + * + * @param variable The variable to replace. + * @param value The value to replace the variable with. + * @param options Options applied to the mutator, when no options are provided this will + * default to `{ applyAtProcessCreation: true }`. + */ + replace(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; + + /** + * Append a value to an environment variable. + * + * Note that an extension can only make a single change to any one variable, so this will + * overwrite any previous calls to replace, append or prepend. + * + * @param variable The variable to append to. + * @param value The value to append to the variable. + * @param options Options applied to the mutator, when no options are provided this will + * default to `{ applyAtProcessCreation: true }`. + */ + append(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; + + /** + * Prepend a value to an environment variable. + * + * Note that an extension can only make a single change to any one variable, so this will + * overwrite any previous calls to replace, append or prepend. + * + * @param variable The variable to prepend. + * @param value The value to prepend to the variable. + * @param options Options applied to the mutator, when no options are provided this will + * default to `{ applyAtProcessCreation: true }`. + */ + prepend(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; + + /** + * Gets the mutator that this collection applies to a variable, if any. + * + * @param variable The variable to get the mutator for. + */ + get(variable: string): EnvironmentVariableMutator | undefined; + + /** + * Iterate over each mutator in this collection. + * + * @param callback Function to execute for each entry. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callback: (variable: string, mutator: EnvironmentVariableMutator, collection: EnvironmentVariableCollection) => any, thisArg?: any): void; + + /** + * Deletes this collection's mutator for a variable. + * + * @param variable The variable to delete the mutator for. + */ + delete(variable: string): void; + + /** + * Clears all mutators from this collection. + */ + clear(): void; + } + + /** + * A collection of mutations that an extension can apply to a process environment. Applies to all scopes. + */ + export interface GlobalEnvironmentVariableCollection extends EnvironmentVariableCollection { + /** + * Gets scope-specific environment variable collection for the extension. This enables alterations to + * terminal environment variables solely within the designated scope, and is applied in addition to (and + * after) the global collection. + * + * Each object obtained through this method is isolated and does not impact objects for other scopes, + * including the global collection. + * + * @param scope The scope to which the environment variable collection applies to. + * + * If a scope parameter is omitted, collection applicable to all relevant scopes for that parameter is + * returned. For instance, if the 'workspaceFolder' parameter is not specified, the collection that applies + * across all workspace folders will be returned. + * + * @returns Environment variable collection for the passed in scope. + */ + getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; + } + + /** + * The scope object to which the environment variable collection applies. + */ + export interface EnvironmentVariableScope { + /** + * Any specific workspace folder to get collection for. + */ + workspaceFolder?: WorkspaceFolder; + } + + /** + * A location in the editor at which progress information can be shown. It depends on the + * location how progress is visually represented. + */ + export enum ProgressLocation { + + /** + * Show progress for the source control viewlet, as overlay for the icon and as progress bar + * inside the viewlet (when visible). Neither supports cancellation nor discrete progress nor + * a label to describe the operation. + */ + SourceControl = 1, + + /** + * Show progress in the status bar of the editor. Neither supports cancellation nor discrete progress. + * Supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax in the progress label. + */ + Window = 10, + + /** + * Show progress as notification with an optional cancel button. Supports to show infinite and discrete + * progress but does not support rendering of icons. + */ + Notification = 15 + } + + /** + * Value-object describing where and how progress should show. + */ + export interface ProgressOptions { + + /** + * The location at which progress should show. + */ + location: ProgressLocation | { + /** + * The identifier of a view for which progress should be shown. + */ + viewId: string; + }; + + /** + * A human-readable string which will be used to describe the + * operation. + */ + title?: string; + + /** + * Controls if a cancel button should show to allow the user to + * cancel the long running operation. Note that currently only + * `ProgressLocation.Notification` is supporting to show a cancel + * button. + */ + cancellable?: boolean; + } + + /** + * A light-weight user input UI that is initially not visible. After + * configuring it through its properties the extension can make it + * visible by calling {@link QuickInput.show}. + * + * There are several reasons why this UI might have to be hidden and + * the extension will be notified through {@link QuickInput.onDidHide}. + * (Examples include: an explicit call to {@link QuickInput.hide}, + * the user pressing Esc, some other input UI opening, etc.) + * + * A user pressing Enter or some other gesture implying acceptance + * of the current state does not automatically hide this UI component. + * It is up to the extension to decide whether to accept the user's input + * and if the UI should indeed be hidden through a call to {@link QuickInput.hide}. + * + * When the extension no longer needs this input UI, it should + * {@link QuickInput.dispose} it to allow for freeing up + * any resources associated with it. + * + * See {@link QuickPick} and {@link InputBox} for concrete UIs. + */ + export interface QuickInput { + + /** + * An optional title. + */ + title: string | undefined; + + /** + * An optional current step count. + */ + step: number | undefined; + + /** + * An optional total step count. + */ + totalSteps: number | undefined; + + /** + * If the UI should allow for user input. Defaults to true. + * + * Change this to false, e.g., while validating user input or + * loading data for the next step in user input. + */ + enabled: boolean; + + /** + * If the UI should show a progress indicator. Defaults to false. + * + * Change this to true, e.g., while loading more data or validating + * user input. + */ + busy: boolean; + + /** + * If the UI should stay open even when loosing UI focus. Defaults to false. + * This setting is ignored on iPad and is always false. + */ + ignoreFocusOut: boolean; + + /** + * Makes the input UI visible in its current configuration. Any other input + * UI will first fire an {@link QuickInput.onDidHide} event. + */ + show(): void; + + /** + * Hides this input UI. This will also fire an {@link QuickInput.onDidHide} + * event. + */ + hide(): void; + + /** + * An event signaling when this input UI is hidden. + * + * There are several reasons why this UI might have to be hidden and + * the extension will be notified through {@link QuickInput.onDidHide}. + * (Examples include: an explicit call to {@link QuickInput.hide}, + * the user pressing Esc, some other input UI opening, etc.) + */ + onDidHide: Event; + + /** + * Dispose of this input UI and any associated resources. If it is still + * visible, it is first hidden. After this call the input UI is no longer + * functional and no additional methods or properties on it should be + * accessed. Instead a new input UI should be created. + */ + dispose(): void; + } + + /** + * A concrete {@link QuickInput} to let the user pick an item from a + * list of items of type T. The items can be filtered through a filter text field and + * there is an option {@link QuickPick.canSelectMany canSelectMany} to allow for + * selecting multiple items. + * + * Note that in many cases the more convenient {@link window.showQuickPick} + * is easier to use. {@link window.createQuickPick} should be used + * when {@link window.showQuickPick} does not offer the required flexibility. + */ + export interface QuickPick extends QuickInput { + + /** + * Current value of the filter text. + */ + value: string; + + /** + * Optional placeholder shown in the filter textbox when no filter has been entered. + */ + placeholder: string | undefined; + + /** + * An event signaling when the value of the filter text has changed. + */ + readonly onDidChangeValue: Event; + + /** + * An event signaling when the user indicated acceptance of the selected item(s). + */ + readonly onDidAccept: Event; + + /** + * Buttons for actions in the UI. + */ + buttons: readonly QuickInputButton[]; + + /** + * An event signaling when a top level button (buttons stored in {@link buttons}) was triggered. + * This event does not fire for buttons on a {@link QuickPickItem}. + */ + readonly onDidTriggerButton: Event; + + /** + * An event signaling when a button in a particular {@link QuickPickItem} was triggered. + * This event does not fire for buttons in the title bar. + */ + readonly onDidTriggerItemButton: Event>; + + /** + * Items to pick from. This can be read and updated by the extension. + */ + items: readonly T[]; + + /** + * If multiple items can be selected at the same time. Defaults to false. + */ + canSelectMany: boolean; + + /** + * If the filter text should also be matched against the description of the items. Defaults to false. + */ + matchOnDescription: boolean; + + /** + * If the filter text should also be matched against the detail of the items. Defaults to false. + */ + matchOnDetail: boolean; + + /** + * An optional flag to maintain the scroll position of the quick pick when the quick pick items are updated. Defaults to false. + */ + keepScrollPosition?: boolean; + + /** + * Active items. This can be read and updated by the extension. + */ + activeItems: readonly T[]; + + /** + * An event signaling when the active items have changed. + */ + readonly onDidChangeActive: Event; + + /** + * Selected items. This can be read and updated by the extension. + */ + selectedItems: readonly T[]; + + /** + * An event signaling when the selected items have changed. + */ + readonly onDidChangeSelection: Event; + } + + /** + * A concrete {@link QuickInput} to let the user input a text value. + * + * Note that in many cases the more convenient {@link window.showInputBox} + * is easier to use. {@link window.createInputBox} should be used + * when {@link window.showInputBox} does not offer the required flexibility. + */ + export interface InputBox extends QuickInput { + + /** + * Current input value. + */ + value: string; + + /** + * Selection range in the input value. Defined as tuple of two number where the + * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole + * pre-filled value will be selected, when empty (start equals end) only the cursor will be set, + * otherwise the defined range will be selected. + * + * This property does not get updated when the user types or makes a selection, + * but it can be updated by the extension. + */ + valueSelection: readonly [number, number] | undefined; + + /** + * Optional placeholder shown when no value has been input. + */ + placeholder: string | undefined; + + /** + * If the input value should be hidden. Defaults to false. + */ + password: boolean; + + /** + * An event signaling when the value has changed. + */ + readonly onDidChangeValue: Event; + + /** + * An event signaling when the user indicated acceptance of the input value. + */ + readonly onDidAccept: Event; + + /** + * Buttons for actions in the UI. + */ + buttons: readonly QuickInputButton[]; + + /** + * An event signaling when a button was triggered. + */ + readonly onDidTriggerButton: Event; + + /** + * An optional prompt text providing some ask or explanation to the user. + */ + prompt: string | undefined; + + /** + * An optional validation message indicating a problem with the current input value. + * By returning a string, the InputBox will use a default {@link InputBoxValidationSeverity} of Error. + * Returning undefined clears the validation message. + */ + validationMessage: string | InputBoxValidationMessage | undefined; + } + + /** + * Button for an action in a {@link QuickPick} or {@link InputBox}. + */ + export interface QuickInputButton { + + /** + * Icon for the button. + */ + readonly iconPath: IconPath; + /** + * An optional tooltip. + */ + readonly tooltip?: string | undefined; + } + + /** + * Predefined buttons for {@link QuickPick} and {@link InputBox}. + */ + export class QuickInputButtons { + + /** + * A back button for {@link QuickPick} and {@link InputBox}. + * + * When a navigation 'back' button is needed this one should be used for consistency. + * It comes with a predefined icon, tooltip and location. + */ + static readonly Back: QuickInputButton; + + /** + * @hidden + */ + private constructor(); + } + + /** + * An event signaling when a button in a particular {@link QuickPickItem} was triggered. + * This event does not fire for buttons in the title bar. + */ + export interface QuickPickItemButtonEvent { + /** + * The button that was clicked. + */ + readonly button: QuickInputButton; + /** + * The item that the button belongs to. + */ + readonly item: T; + } + + /** + * An event describing an individual change in the text of a {@link TextDocument document}. + */ + export interface TextDocumentContentChangeEvent { + /** + * The range that got replaced. + */ + readonly range: Range; + /** + * The offset of the range that got replaced. + */ + readonly rangeOffset: number; + /** + * The length of the range that got replaced. + */ + readonly rangeLength: number; + /** + * The new text for the range. + */ + readonly text: string; + } + + /** + * Reasons for why a text document has changed. + */ + export enum TextDocumentChangeReason { + /** The text change is caused by an undo operation. */ + Undo = 1, + + /** The text change is caused by an redo operation. */ + Redo = 2, + } + + /** + * An event describing a transactional {@link TextDocument document} change. + */ + export interface TextDocumentChangeEvent { + + /** + * The affected document. + */ + readonly document: TextDocument; + + /** + * An array of content changes. + */ + readonly contentChanges: readonly TextDocumentContentChangeEvent[]; + + /** + * The reason why the document was changed. + * Is `undefined` if the reason is not known. + */ + readonly reason: TextDocumentChangeReason | undefined; + } + + /** + * Represents reasons why a text document is saved. + */ + export enum TextDocumentSaveReason { + + /** + * Manually triggered, e.g. by the user pressing save, by starting debugging, + * or by an API call. + */ + Manual = 1, + + /** + * Automatic after a delay. + */ + AfterDelay = 2, + + /** + * When the editor lost focus. + */ + FocusOut = 3 + } + + /** + * An event that is fired when a {@link TextDocument document} will be saved. + * + * To make modifications to the document before it is being saved, call the + * {@linkcode TextDocumentWillSaveEvent.waitUntil waitUntil}-function with a thenable + * that resolves to an array of {@link TextEdit text edits}. + */ + export interface TextDocumentWillSaveEvent { + + /** + * The document that will be saved. + */ + readonly document: TextDocument; + + /** + * The reason why save was triggered. + */ + readonly reason: TextDocumentSaveReason; + + /** + * Allows to pause the event loop and to apply {@link TextEdit pre-save-edits}. + * Edits of subsequent calls to this function will be applied in order. The + * edits will be *ignored* if concurrent modifications of the document happened. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillSaveTextDocument(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that resolves to {@link TextEdit pre-save-edits}. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event loop until the provided thenable resolved. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired when files are going to be created. + * + * To make modifications to the workspace before the files are created, + * call the {@linkcode FileWillCreateEvent.waitUntil waitUntil}-function with a + * thenable that resolves to a {@link WorkspaceEdit workspace edit}. + */ + export interface FileWillCreateEvent { + + /** + * A cancellation token. + */ + readonly token: CancellationToken; + + /** + * The files that are going to be created. + */ + readonly files: readonly Uri[]; + + /** + * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired after files are created. + */ + export interface FileCreateEvent { + + /** + * The files that got created. + */ + readonly files: readonly Uri[]; + } + + /** + * An event that is fired when files are going to be deleted. + * + * To make modifications to the workspace before the files are deleted, + * call the {@link FileWillCreateEvent.waitUntil `waitUntil`}-function with a + * thenable that resolves to a {@link WorkspaceEdit workspace edit}. + */ + export interface FileWillDeleteEvent { + + /** + * A cancellation token. + */ + readonly token: CancellationToken; + + /** + * The files that are going to be deleted. + */ + readonly files: readonly Uri[]; + + /** + * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired after files are deleted. + */ + export interface FileDeleteEvent { + + /** + * The files that got deleted. + */ + readonly files: readonly Uri[]; + } + + /** + * An event that is fired when files are going to be renamed. + * + * To make modifications to the workspace before the files are renamed, + * call the {@link FileWillCreateEvent.waitUntil `waitUntil`}-function with a + * thenable that resolves to a {@link WorkspaceEdit workspace edit}. + */ + export interface FileWillRenameEvent { + + /** + * A cancellation token. + */ + readonly token: CancellationToken; + + /** + * The files that are going to be renamed. + */ + readonly files: ReadonlyArray<{ + /** + * The old uri of a file. + */ + readonly oldUri: Uri; + /** + * The new uri of a file. + */ + readonly newUri: Uri; + }>; + + /** + * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillCreateFiles(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event until the provided thenable resolves. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * An event that is fired after files are renamed. + */ + export interface FileRenameEvent { + + /** + * The files that got renamed. + */ + readonly files: ReadonlyArray<{ + /** + * The old uri of a file. + */ + readonly oldUri: Uri; + /** + * The new uri of a file. + */ + readonly newUri: Uri; + }>; + } + + /** + * An event describing a change to the set of {@link workspace.workspaceFolders workspace folders}. + */ + export interface WorkspaceFoldersChangeEvent { + /** + * Added workspace folders. + */ + readonly added: readonly WorkspaceFolder[]; + + /** + * Removed workspace folders. + */ + readonly removed: readonly WorkspaceFolder[]; + } + + /** + * A workspace folder is one of potentially many roots opened by the editor. All workspace folders + * are equal which means there is no notion of an active or primary workspace folder. + */ + export interface WorkspaceFolder { + + /** + * The associated uri for this workspace folder. + * + * *Note:* The {@link Uri}-type was intentionally chosen such that future releases of the editor can support + * workspace folders that are not stored on the local disk, e.g. `ftp://server/workspaces/foo`. + */ + readonly uri: Uri; + + /** + * The name of this workspace folder. Defaults to + * the basename of its {@link Uri.path uri-path} + */ + readonly name: string; + + /** + * The ordinal number of this workspace folder. + */ + readonly index: number; + } + + /** + * Namespace for dealing with the current workspace. A workspace is the collection of one + * or more folders that are opened in an editor window (instance). + * + * It is also possible to open an editor without a workspace. For example, when you open a + * new editor window by selecting a file from your platform's File menu, you will not be + * inside a workspace. In this mode, some of the editor's capabilities are reduced but you can + * still open text files and edit them. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on + * the concept of workspaces. + * + * The workspace offers support for {@link workspace.createFileSystemWatcher listening} to fs + * events and for {@link workspace.findFiles finding} files. Both perform well and run _outside_ + * the editor-process so that they should be always used instead of nodejs-equivalents. + */ + export namespace workspace { + + /** + * A {@link FileSystem file system} instance that allows to interact with local and remote + * files, e.g. `vscode.workspace.fs.readDirectory(someUri)` allows to retrieve all entries + * of a directory or `vscode.workspace.fs.stat(anotherUri)` returns the meta data for a + * file. + */ + export const fs: FileSystem; + + /** + * The uri of the first entry of {@linkcode workspace.workspaceFolders workspaceFolders} + * as `string`. `undefined` if there is no first entry. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information + * on workspaces. + * + * @deprecated Use {@linkcode workspace.workspaceFolders workspaceFolders} instead. + */ + export const rootPath: string | undefined; + + /** + * List of workspace folders (0-N) that are open in the editor. `undefined` when no workspace + * has been opened. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information + * on workspaces. + */ + export const workspaceFolders: readonly WorkspaceFolder[] | undefined; + + /** + * The name of the workspace. `undefined` when no workspace + * has been opened. + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on + * the concept of workspaces. + */ + export const name: string | undefined; + + /** + * The location of the workspace file, for example: + * + * `file:///Users/name/Development/myProject.code-workspace` + * + * or + * + * `untitled:1555503116870` + * + * for a workspace that is untitled and not yet saved. + * + * Depending on the workspace that is opened, the value will be: + * * `undefined` when no workspace is opened + * * the path of the workspace file as `Uri` otherwise. if the workspace + * is untitled, the returned URI will use the `untitled:` scheme + * + * The location can e.g. be used with the `vscode.openFolder` command to + * open the workspace again after it has been closed. + * + * **Example:** + * ```typescript + * vscode.commands.executeCommand('vscode.openFolder', uriOfWorkspace); + * ``` + * + * Refer to https://code.visualstudio.com/docs/editor/workspaces for more information on + * the concept of workspaces. + * + * **Note:** it is not advised to use `workspace.workspaceFile` to write + * configuration data into the file. You can use `workspace.getConfiguration().update()` + * for that purpose which will work both when a single folder is opened as + * well as an untitled or saved workspace. + */ + export const workspaceFile: Uri | undefined; + + /** + * An event that is emitted when a workspace folder is added or removed. + * + * **Note:** this event will not fire if the first workspace folder is added, removed or changed, + * because in that case the currently executing extensions (including the one that listens to this + * event) will be terminated and restarted so that the (deprecated) `rootPath` property is updated + * to point to the first workspace folder. + */ + export const onDidChangeWorkspaceFolders: Event; + + /** + * Returns the {@link WorkspaceFolder workspace folder} that contains a given uri. + * * returns `undefined` when the given uri doesn't match any workspace folder + * * returns the *input* when the given uri is a workspace folder itself + * + * @param uri An uri. + * @returns A workspace folder or `undefined` + */ + export function getWorkspaceFolder(uri: Uri): WorkspaceFolder | undefined; + + /** + * Returns a path that is relative to the workspace folder or folders. + * + * When there are no {@link workspace.workspaceFolders workspace folders} or when the path + * is not contained in them, the input is returned. + * + * @param pathOrUri A path or uri. When a uri is given its {@link Uri.fsPath fsPath} is used. + * @param includeWorkspaceFolder When `true` and when the given path is contained inside a + * workspace folder the name of the workspace is prepended. Defaults to `true` when there are + * multiple workspace folders and `false` otherwise. + * @returns A path relative to the root or the input. + */ + export function asRelativePath(pathOrUri: string | Uri, includeWorkspaceFolder?: boolean): string; + + /** + * This method replaces `deleteCount` {@link workspace.workspaceFolders workspace folders} starting at index `start` + * by an optional set of `workspaceFoldersToAdd` on the `vscode.workspace.workspaceFolders` array. This "splice" + * behavior can be used to add, remove and change workspace folders in a single operation. + * + * **Note:** in some cases calling this method may result in the currently executing extensions (including the + * one that called this method) to be terminated and restarted. For example when the first workspace folder is + * added, removed or changed the (deprecated) `rootPath` property is updated to point to the first workspace + * folder. Another case is when transitioning from an empty or single-folder workspace into a multi-folder + * workspace (see also: https://code.visualstudio.com/docs/editor/workspaces). + * + * Use the {@linkcode onDidChangeWorkspaceFolders onDidChangeWorkspaceFolders()} event to get notified when the + * workspace folders have been updated. + * + * **Example:** adding a new workspace folder at the end of workspace folders + * ```typescript + * workspace.updateWorkspaceFolders(workspace.workspaceFolders ? workspace.workspaceFolders.length : 0, null, { uri: ...}); + * ``` + * + * **Example:** removing the first workspace folder + * ```typescript + * workspace.updateWorkspaceFolders(0, 1); + * ``` + * + * **Example:** replacing an existing workspace folder with a new one + * ```typescript + * workspace.updateWorkspaceFolders(0, 1, { uri: ...}); + * ``` + * + * It is valid to remove an existing workspace folder and add it again with a different name + * to rename that folder. + * + * **Note:** it is not valid to call {@link updateWorkspaceFolders updateWorkspaceFolders()} multiple times + * without waiting for the {@linkcode onDidChangeWorkspaceFolders onDidChangeWorkspaceFolders()} to fire. + * + * @param start the zero-based location in the list of currently opened {@link WorkspaceFolder workspace folders} + * from which to start deleting workspace folders. + * @param deleteCount the optional number of workspace folders to remove. + * @param workspaceFoldersToAdd the optional variable set of workspace folders to add in place of the deleted ones. + * Each workspace is identified with a mandatory URI and an optional name. + * @returns true if the operation was successfully started and false otherwise if arguments were used that would result + * in invalid workspace folder state (e.g. 2 folders with the same URI). + */ + export function updateWorkspaceFolders(start: number, deleteCount: number | undefined | null, ...workspaceFoldersToAdd: { + /** + * The uri of a workspace folder that's to be added. + */ + readonly uri: Uri; + /** + * The name of a workspace folder that's to be added. + */ + readonly name?: string; + }[]): boolean; + + /** + * Creates a file system watcher that is notified on file events (create, change, delete) + * depending on the parameters provided. + * + * By default, all opened {@link workspace.workspaceFolders workspace folders} will be watched + * for file changes recursively. + * + * Additional paths can be added for file watching by providing a {@link RelativePattern} with + * a `base` path to watch. If the path is a folder and the `pattern` is complex (e.g. contains + * `**` or path segments), it will be watched recursively and otherwise will be watched + * non-recursively (i.e. only changes to the first level of the path will be reported). + * + * *Note* that paths that do not exist in the file system will be monitored with a delay until + * created and then watched depending on the parameters provided. If a watched path is deleted, + * the watcher will suspend and not report any events until the path is created again. + * + * If possible, keep the use of recursive watchers to a minimum because recursive file watching + * is quite resource intense. + * + * Providing a `string` as `globPattern` acts as convenience method for watching file events in + * all opened workspace folders. It cannot be used to add more folders for file watching, nor will + * it report any file events from folders that are not part of the opened workspace folders. + * + * Optionally, flags to ignore certain kinds of events can be provided. + * + * To stop listening to events the watcher must be disposed. + * + * *Note* that file events from recursive file watchers may be excluded based on user configuration. + * The setting `files.watcherExclude` helps to reduce the overhead of file events from folders + * that are known to produce many file changes at once (such as `.git` folders). As such, + * it is highly recommended to watch with simple patterns that do not require recursive watchers + * where the exclude settings are ignored and you have full control over the events. + * + * *Note* that symbolic links are not automatically followed for file watching unless the path to + * watch itself is a symbolic link. + * + * *Note* that the file paths that are reported for having changed may have a different path casing + * compared to the actual casing on disk on case-insensitive platforms (typically macOS and Windows + * but not Linux). We allow a user to open a workspace folder with any desired path casing and try + * to preserve that. This means: + * * if the path is within any of the workspace folders, the path will match the casing of the + * workspace folder up to that portion of the path and match the casing on disk for children + * * if the path is outside of any of the workspace folders, the casing will match the case of the + * path that was provided for watching + * In the same way, symbolic links are preserved, i.e. the file event will report the path of the + * symbolic link as it was provided for watching and not the target. + * + * ### Examples + * + * The basic anatomy of a file watcher is as follows: + * + * ```ts + * const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(, )); + * + * watcher.onDidChange(uri => { ... }); // listen to files being changed + * watcher.onDidCreate(uri => { ... }); // listen to files/folders being created + * watcher.onDidDelete(uri => { ... }); // listen to files/folders getting deleted + * + * watcher.dispose(); // dispose after usage + * ``` + * + * #### Workspace file watching + * + * If you only care about file events in a specific workspace folder: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], '**​/*.js')); + * ``` + * + * If you want to monitor file events across all opened workspace folders: + * + * ```ts + * vscode.workspace.createFileSystemWatcher('**​/*.js'); + * ``` + * + * *Note:* the array of workspace folders can be empty if no workspace is opened (empty window). + * + * #### Out of workspace file watching + * + * To watch a folder for changes to *.js files outside the workspace (non recursively), pass in a `Uri` to such + * a folder: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.file(), '*.js')); + * ``` + * + * And use a complex glob pattern to watch recursively: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.Uri.file(), '**​/*.js')); + * ``` + * + * Here is an example for watching the active editor for file changes: + * + * ```ts + * vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(vscode.window.activeTextEditor.document.uri, '*')); + * ``` + * + * @param globPattern A {@link GlobPattern glob pattern} that controls which file events the watcher should report. + * @param ignoreCreateEvents Ignore when files have been created. + * @param ignoreChangeEvents Ignore when files have been changed. + * @param ignoreDeleteEvents Ignore when files have been deleted. + * @returns A new file system watcher instance. Must be disposed when no longer needed. + */ + export function createFileSystemWatcher(globPattern: GlobPattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): FileSystemWatcher; + + /** + * Find files across all {@link workspace.workspaceFolders workspace folders} in the workspace. + * + * @example + * findFiles('**​/*.js', '**​/node_modules/**', 10) + * + * @param include A {@link GlobPattern glob pattern} that defines the files to search for. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. Use a {@link RelativePattern relative pattern} + * to restrict the search results to a {@link WorkspaceFolder workspace folder}. + * @param exclude A {@link GlobPattern glob pattern} that defines files and folders to exclude. The glob pattern + * will be matched against the file paths of resulting matches relative to their workspace. When `undefined`, default file-excludes (e.g. the `files.exclude`-setting + * but not `search.exclude`) will apply. When `null`, no excludes will apply. + * @param maxResults An upper-bound for the result. + * @param token A token that can be used to signal cancellation to the underlying search engine. + * @returns A thenable that resolves to an array of resource identifiers. Will return no results if no + * {@link workspace.workspaceFolders workspace folders} are opened. + */ + export function findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Thenable; + + /** + * Saves the editor identified by the given resource and returns the resulting resource or `undefined` + * if save was not successful or no editor with the given resource was found. + * + * **Note** that an editor with the provided resource must be opened in order to be saved. + * + * @param uri the associated uri for the opened editor to save. + * @returns A thenable that resolves when the save operation has finished. + */ + export function save(uri: Uri): Thenable; + + /** + * Saves the editor identified by the given resource to a new file name as provided by the user and + * returns the resulting resource or `undefined` if save was not successful or cancelled or no editor + * with the given resource was found. + * + * **Note** that an editor with the provided resource must be opened in order to be saved as. + * + * @param uri the associated uri for the opened editor to save as. + * @returns A thenable that resolves when the save-as operation has finished. + */ + export function saveAs(uri: Uri): Thenable; + + /** + * Save all dirty files. + * + * @param includeUntitled Also save files that have been created during this session. + * @returns A thenable that resolves when the files have been saved. Will return `false` + * for any file that failed to save. + */ + export function saveAll(includeUntitled?: boolean): Thenable; + + /** + * Make changes to one or many resources or create, delete, and rename resources as defined by the given + * {@link WorkspaceEdit workspace edit}. + * + * All changes of a workspace edit are applied in the same order in which they have been added. If + * multiple textual inserts are made at the same position, these strings appear in the resulting text + * in the order the 'inserts' were made, unless that are interleaved with resource edits. Invalid sequences + * like 'delete file a' -> 'insert text in file a' cause failure of the operation. + * + * When applying a workspace edit that consists only of text edits an 'all-or-nothing'-strategy is used. + * A workspace edit with resource creations or deletions aborts the operation, e.g. consecutive edits will + * not be attempted, when a single edit fails. + * + * @param edit A workspace edit. + * @param metadata Optional {@link WorkspaceEditMetadata metadata} for the edit. + * @returns A thenable that resolves when the edit could be applied. + */ + export function applyEdit(edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata): Thenable; + + /** + * All text documents currently known to the editor. + */ + export const textDocuments: readonly TextDocument[]; + + /** + * Opens a document. Will return early if this document is already open. Otherwise + * the document is loaded and the {@link workspace.onDidOpenTextDocument didOpen}-event fires. + * + * The document is denoted by an {@link Uri}. Depending on the {@link Uri.scheme scheme} the + * following rules apply: + * * `file`-scheme: Open a file on disk (`openTextDocument(Uri.file(path))`). Will be rejected if the file + * does not exist or cannot be loaded. + * * `untitled`-scheme: Open a blank untitled file with associated path (`openTextDocument(Uri.file(path).with({ scheme: 'untitled' }))`). + * The language will be derived from the file name. + * * For all other schemes contributed {@link TextDocumentContentProvider text document content providers} and + * {@link FileSystemProvider file system providers} are consulted. + * + * *Note* that the lifecycle of the returned document is owned by the editor and not by the extension. That means an + * {@linkcode workspace.onDidCloseTextDocument onDidClose}-event can occur at any time after opening it. + * + * @param uri Identifies the resource to open. + * @returns A promise that resolves to a {@link TextDocument document}. + */ + export function openTextDocument(uri: Uri): Thenable; + + /** + * A short-hand for `openTextDocument(Uri.file(path))`. + * + * @see {@link workspace.openTextDocument} + * @param path A path of a file on disk. + * @returns A promise that resolves to a {@link TextDocument document}. + */ + export function openTextDocument(path: string): Thenable; + + /** + * Opens an untitled text document. The editor will prompt the user for a file + * path when the document is to be saved. The `options` parameter allows to + * specify the *language* and/or the *content* of the document. + * + * @param options Options to control how the document will be created. + * @returns A promise that resolves to a {@link TextDocument document}. + */ + export function openTextDocument(options?: { + /** + * The {@link TextDocument.languageId language} of the document. + */ + language?: string; + /** + * The initial contents of the document. + */ + content?: string; + }): Thenable; + + /** + * Register a text document content provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The uri-scheme to register for. + * @param provider A content provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTextDocumentContentProvider(scheme: string, provider: TextDocumentContentProvider): Disposable; + + /** + * An event that is emitted when a {@link TextDocument text document} is opened or when the language id + * of a text document {@link languages.setTextDocumentLanguage has been changed}. + * + * To add an event listener when a visible text document is opened, use the {@link TextEditor} events in the + * {@link window} namespace. Note that: + * + * - The event is emitted before the {@link TextDocument document} is updated in the + * {@link window.activeTextEditor active text editor} + * - When a {@link TextDocument text document} is already open (e.g.: open in another {@link window.visibleTextEditors visible text editor}) this event is not emitted + * + */ + export const onDidOpenTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} is disposed or when the language id + * of a text document {@link languages.setTextDocumentLanguage has been changed}. + * + * *Note 1:* There is no guarantee that this event fires when an editor tab is closed, use the + * {@linkcode window.onDidChangeVisibleTextEditors onDidChangeVisibleTextEditors}-event to know when editors change. + * + * *Note 2:* A document can be open but not shown in an editor which means this event can fire + * for a document that has not been shown in an editor. + */ + export const onDidCloseTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} is changed. This usually happens + * when the {@link TextDocument.getText contents} changes but also when other things like the + * {@link TextDocument.isDirty dirty}-state changes. + */ + export const onDidChangeTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} will be saved to disk. + * + * *Note 1:* Subscribers can delay saving by registering asynchronous work. For the sake of data integrity the editor + * might save without firing this event. For instance when shutting down with dirty files. + * + * *Note 2:* Subscribers are called sequentially and they can {@link TextDocumentWillSaveEvent.waitUntil delay} saving + * by registering asynchronous work. Protection against misbehaving listeners is implemented as such: + * * there is an overall time budget that all listeners share and if that is exhausted no further listener is called + * * listeners that take a long time or produce errors frequently will not be called anymore + * + * The current thresholds are 1.5 seconds as overall time budget and a listener can misbehave 3 times before being ignored. + */ + export const onWillSaveTextDocument: Event; + + /** + * An event that is emitted when a {@link TextDocument text document} is saved to disk. + */ + export const onDidSaveTextDocument: Event; + + /** + * All notebook documents currently known to the editor. + */ + export const notebookDocuments: readonly NotebookDocument[]; + + /** + * Open a notebook. Will return early if this notebook is already {@link notebookDocuments loaded}. Otherwise + * the notebook is loaded and the {@linkcode onDidOpenNotebookDocument}-event fires. + * + * *Note* that the lifecycle of the returned notebook is owned by the editor and not by the extension. That means an + * {@linkcode onDidCloseNotebookDocument}-event can occur at any time after. + * + * *Note* that opening a notebook does not show a notebook editor. This function only returns a notebook document which + * can be shown in a notebook editor but it can also be used for other things. + * + * @param uri The resource to open. + * @returns A promise that resolves to a {@link NotebookDocument notebook} + */ + export function openNotebookDocument(uri: Uri): Thenable; + + /** + * Open an untitled notebook. The editor will prompt the user for a file + * path when the document is to be saved. + * + * @see {@link workspace.openNotebookDocument} + * @param notebookType The notebook type that should be used. + * @param content The initial contents of the notebook. + * @returns A promise that resolves to a {@link NotebookDocument notebook}. + */ + export function openNotebookDocument(notebookType: string, content?: NotebookData): Thenable; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} has changed. + */ + export const onDidChangeNotebookDocument: Event; + + /** + * An event that is emitted when a {@link NotebookDocument notebook document} will be saved to disk. + * + * *Note 1:* Subscribers can delay saving by registering asynchronous work. For the sake of data integrity the editor + * might save without firing this event. For instance when shutting down with dirty files. + * + * *Note 2:* Subscribers are called sequentially and they can {@link NotebookDocumentWillSaveEvent.waitUntil delay} saving + * by registering asynchronous work. Protection against misbehaving listeners is implemented as such: + * * there is an overall time budget that all listeners share and if that is exhausted no further listener is called + * * listeners that take a long time or produce errors frequently will not be called anymore + * + * The current thresholds are 1.5 seconds as overall time budget and a listener can misbehave 3 times before being ignored. + */ + export const onWillSaveNotebookDocument: Event; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} is saved. + */ + export const onDidSaveNotebookDocument: Event; + + /** + * Register a {@link NotebookSerializer notebook serializer}. + * + * A notebook serializer must be contributed through the `notebooks` extension point. When opening a notebook file, the editor will send + * the `onNotebook:` activation event, and extensions must register their serializer in return. + * + * @param notebookType A notebook. + * @param serializer A notebook serializer. + * @param options Optional context options that define what parts of a notebook should be persisted + * @returns A {@link Disposable} that unregisters this serializer when being disposed. + */ + export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions): Disposable; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} is opened. + */ + export const onDidOpenNotebookDocument: Event; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} is disposed. + * + * *Note 1:* There is no guarantee that this event fires when an editor tab is closed. + * + * *Note 2:* A notebook can be open but not shown in an editor which means this event can fire + * for a notebook that has not been shown in an editor. + */ + export const onDidCloseNotebookDocument: Event; + + /** + * An event that is emitted when files are being created. + * + * *Note 1:* This event is triggered by user gestures, like creating a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api. This event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When this event is fired, edits to files that are are being created cannot be applied. + */ + export const onWillCreateFiles: Event; + + /** + * An event that is emitted when files have been created. + * + * *Note:* This event is triggered by user gestures, like creating a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + */ + export const onDidCreateFiles: Event; + + /** + * An event that is emitted when files are being deleted. + * + * *Note 1:* This event is triggered by user gestures, like deleting a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When deleting a folder with children only one event is fired. + */ + export const onWillDeleteFiles: Event; + + /** + * An event that is emitted when files have been deleted. + * + * *Note 1:* This event is triggered by user gestures, like deleting a file from the + * explorer, or from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When deleting a folder with children only one event is fired. + */ + export const onDidDeleteFiles: Event; + + /** + * An event that is emitted when files are being renamed. + * + * *Note 1:* This event is triggered by user gestures, like renaming a file from the + * explorer, and from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When renaming a folder with children only one event is fired. + */ + export const onWillRenameFiles: Event; + + /** + * An event that is emitted when files have been renamed. + * + * *Note 1:* This event is triggered by user gestures, like renaming a file from the + * explorer, and from the {@linkcode workspace.applyEdit}-api, but this event is *not* fired when + * files change on disk, e.g triggered by another application, or when using the + * {@linkcode FileSystem workspace.fs}-api. + * + * *Note 2:* When renaming a folder with children only one event is fired. + */ + export const onDidRenameFiles: Event; + + /** + * Get a workspace configuration object. + * + * When a section-identifier is provided only that part of the configuration + * is returned. Dots in the section-identifier are interpreted as child-access, + * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. + * + * When a scope is provided configuration confined to that scope is returned. Scope can be a resource or a language identifier or both. + * + * @param section A dot-separated identifier. + * @param scope A scope for which the configuration is asked for. + * @returns The full configuration or a subset. + */ + export function getConfiguration(section?: string, scope?: ConfigurationScope | null): WorkspaceConfiguration; + + /** + * An event that is emitted when the {@link WorkspaceConfiguration configuration} changed. + */ + export const onDidChangeConfiguration: Event; + + /** + * Register a task provider. + * + * @deprecated Use the corresponding function on the `tasks` namespace instead + * + * @param type The task kind type this provider is registered for. + * @param provider A task provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; + + /** + * Register a filesystem provider for a given scheme, e.g. `ftp`. + * + * There can only be one provider per scheme and an error is being thrown when a scheme + * has been claimed by another provider or when it is reserved. + * + * @param scheme The uri-{@link Uri.scheme scheme} the provider registers for. + * @param provider The filesystem provider. + * @param options Immutable metadata about the provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider, options?: { + /** + * Whether the file system provider use case sensitive compare for {@link Uri.path paths} + */ + readonly isCaseSensitive?: boolean; + /** + * Whether the file system provider is readonly, no modifications like write, delete, create are possible. + * If a {@link MarkdownString} is given, it will be shown as the reason why the file system is readonly. + */ + readonly isReadonly?: boolean | MarkdownString; + }): Disposable; + + /** + * When true, the user has explicitly trusted the contents of the workspace. + */ + export const isTrusted: boolean; + + /** + * Event that fires when the current workspace has been trusted. + */ + export const onDidGrantWorkspaceTrust: Event; + } + + /** + * The configuration scope which can be: + * - a {@link Uri} representing a resource + * - a {@link TextDocument} representing an open text document + * - a {@link WorkspaceFolder} representing a workspace folder + * - an object containing: + * - `uri`: an optional {@link Uri} of a text document + * - `languageId`: the language identifier of a text document + */ + export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { + /** + * The uri of a {@link TextDocument text document} + */ + uri?: Uri; + /** + * The language of a text document + */ + languageId: string; + }; + + /** + * An event describing the change in Configuration + */ + export interface ConfigurationChangeEvent { + + /** + * Checks if the given section has changed. + * If scope is provided, checks if the section has changed for resources under the given scope. + * + * @param section Configuration name, supports _dotted_ names. + * @param scope A scope in which to check. + * @returns `true` if the given section has changed. + */ + affectsConfiguration(section: string, scope?: ConfigurationScope): boolean; + } + + /** + * Namespace for participating in language-specific editor [features](https://code.visualstudio.com/docs/editor/editingevolved), + * like IntelliSense, code actions, diagnostics etc. + * + * Many programming languages exist and there is huge variety in syntaxes, semantics, and paradigms. Despite that, features + * like automatic word-completion, code navigation, or code checking have become popular across different tools for different + * programming languages. + * + * The editor provides an API that makes it simple to provide such common features by having all UI and actions already in place and + * by allowing you to participate by providing data only. For instance, to contribute a hover all you have to do is provide a function + * that can be called with a {@link TextDocument} and a {@link Position} returning hover info. The rest, like tracking the + * mouse, positioning the hover, keeping the hover stable etc. is taken care of by the editor. + * + * ```javascript + * languages.registerHoverProvider('javascript', { + * provideHover(document, position, token) { + * return new Hover('I am a hover!'); + * } + * }); + * ``` + * + * Registration is done using a {@link DocumentSelector document selector} which is either a language id, like `javascript` or + * a more complex {@link DocumentFilter filter} like `{ language: 'typescript', scheme: 'file' }`. Matching a document against such + * a selector will result in a {@link languages.match score} that is used to determine if and how a provider shall be used. When + * scores are equal the provider that came last wins. For features that allow full arity, like {@link languages.registerHoverProvider hover}, + * the score is only checked to be `>0`, for other features, like {@link languages.registerCompletionItemProvider IntelliSense} the + * score is used for determining the order in which providers are asked to participate. + */ + export namespace languages { + + /** + * Return the identifiers of all known languages. + * @returns Promise resolving to an array of identifier strings. + */ + export function getLanguages(): Thenable; + + /** + * Set (and change) the {@link TextDocument.languageId language} that is associated + * with the given document. + * + * *Note* that calling this function will trigger the {@linkcode workspace.onDidCloseTextDocument onDidCloseTextDocument} event + * followed by the {@linkcode workspace.onDidOpenTextDocument onDidOpenTextDocument} event. + * + * @param document The document which language is to be changed + * @param languageId The new language identifier. + * @returns A thenable that resolves with the updated document. + */ + export function setTextDocumentLanguage(document: TextDocument, languageId: string): Thenable; + + /** + * Compute the match between a document {@link DocumentSelector selector} and a document. Values + * greater than zero mean the selector matches the document. + * + * A match is computed according to these rules: + * 1. When {@linkcode DocumentSelector} is an array, compute the match for each contained `DocumentFilter` or language identifier and take the maximum value. + * 2. A string will be desugared to become the `language`-part of a {@linkcode DocumentFilter}, so `"fooLang"` is like `{ language: "fooLang" }`. + * 3. A {@linkcode DocumentFilter} will be matched against the document by comparing its parts with the document. The following rules apply: + * 1. When the `DocumentFilter` is empty (`{}`) the result is `0` + * 2. When `scheme`, `language`, `pattern`, or `notebook` are defined but one doesn't match, the result is `0` + * 3. Matching against `*` gives a score of `5`, matching via equality or via a glob-pattern gives a score of `10` + * 4. The result is the maximum value of each match + * + * Samples: + * ```js + * // default document from disk (file-scheme) + * doc.uri; //'file:///my/file.js' + * doc.languageId; // 'javascript' + * match('javascript', doc); // 10; + * match({ language: 'javascript' }, doc); // 10; + * match({ language: 'javascript', scheme: 'file' }, doc); // 10; + * match('*', doc); // 5 + * match('fooLang', doc); // 0 + * match(['fooLang', '*'], doc); // 5 + * + * // virtual document, e.g. from git-index + * doc.uri; // 'git:/my/file.js' + * doc.languageId; // 'javascript' + * match('javascript', doc); // 10; + * match({ language: 'javascript', scheme: 'git' }, doc); // 10; + * match('*', doc); // 5 + * + * // notebook cell document + * doc.uri; // `vscode-notebook-cell:///my/notebook.ipynb#gl65s2pmha`; + * doc.languageId; // 'python' + * match({ notebookType: 'jupyter-notebook' }, doc) // 10 + * match({ notebookType: 'fooNotebook', language: 'python' }, doc) // 0 + * match({ language: 'python' }, doc) // 10 + * match({ notebookType: '*' }, doc) // 5 + * ``` + * + * @param selector A document selector. + * @param document A text document. + * @returns A number `>0` when the selector matches and `0` when the selector does not match. + */ + export function match(selector: DocumentSelector, document: TextDocument): number; + + /** + * An {@link Event} which fires when the global set of diagnostics changes. This is + * newly added and removed diagnostics. + */ + export const onDidChangeDiagnostics: Event; + + /** + * Get all diagnostics for a given resource. + * + * @param resource A resource + * @returns An array of {@link Diagnostic diagnostics} objects or an empty array. + */ + export function getDiagnostics(resource: Uri): Diagnostic[]; + + /** + * Get all diagnostics. + * + * @returns An array of uri-diagnostics tuples or an empty array. + */ + export function getDiagnostics(): [Uri, Diagnostic[]][]; + + /** + * Create a diagnostics collection. + * + * @param name The {@link DiagnosticCollection.name name} of the collection. + * @returns A new diagnostic collection. + */ + export function createDiagnosticCollection(name?: string): DiagnosticCollection; + + /** + * Creates a new {@link LanguageStatusItem language status item}. + * + * @param id The identifier of the item. + * @param selector The document selector that defines for what editors the item shows. + * @returns A new language status item. + */ + export function createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem; + + /** + * Register a completion provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and groups of equal score are sequentially asked for + * completion items. The process stops when one or many providers of a group return a + * result. A failing provider (rejected promise or exception) will not fail the whole + * operation. + * + * A completion item provider can be associated with a set of `triggerCharacters`. When trigger + * characters are being typed, completions are requested but only from providers that registered + * the typed character. Because of that trigger characters should be different than {@link LanguageConfiguration.wordPattern word characters}, + * a common trigger character is `.` to trigger member completions. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A completion provider. + * @param triggerCharacters Trigger completion when the user types one of the characters. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionItemProvider, ...triggerCharacters: string[]): Disposable; + + /** + * Registers an inline completion provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline completion provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; + + /** + * Register a code action provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A code action provider. + * @param metadata Metadata about the kind of code actions the provider provides. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCodeActionsProvider(selector: DocumentSelector, provider: CodeActionProvider, metadata?: CodeActionProviderMetadata): Disposable; + + /** + * Register a code lens provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A code lens provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable; + + /** + * Register a definition provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A definition provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable; + + /** + * Register an implementation provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An implementation provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerImplementationProvider(selector: DocumentSelector, provider: ImplementationProvider): Disposable; + + /** + * Register a type definition provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A type definition provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTypeDefinitionProvider(selector: DocumentSelector, provider: TypeDefinitionProvider): Disposable; + + /** + * Register a declaration provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A declaration provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDeclarationProvider(selector: DocumentSelector, provider: DeclarationProvider): Disposable; + + /** + * Register a hover provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A hover provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerHoverProvider(selector: DocumentSelector, provider: HoverProvider): Disposable; + + /** + * Register a provider that locates evaluatable expressions in text documents. + * The editor will evaluate the expression in the active debug session and will show the result in the debug hover. + * + * If multiple providers are registered for a language an arbitrary provider will be used. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An evaluatable expression provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable; + + /** + * Register a provider that returns data for the debugger's 'inline value' feature. + * Whenever the generic debugger has stopped in a source file, providers registered for the language of the file + * are called to return textual data that will be shown in the editor at the end of lines. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inline values provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; + + /** + * Register a document highlight provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and groups sequentially asked for document highlights. + * The process stops when a provider returns a `non-falsy` or `non-failure` result. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document highlight provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentHighlightProvider(selector: DocumentSelector, provider: DocumentHighlightProvider): Disposable; + + /** + * Register a document symbol provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document symbol provider. + * @param metaData metadata about the provider + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider, metaData?: DocumentSymbolProviderMetadata): Disposable; + + /** + * Register a workspace symbol provider. + * + * Multiple providers can be registered. In that case providers are asked in parallel and + * the results are merged. A failing provider (rejected promise or exception) will not cause + * a failure of the whole operation. + * + * @param provider A workspace symbol provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerWorkspaceSymbolProvider(provider: WorkspaceSymbolProvider): Disposable; + + /** + * Register a reference provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A reference provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerReferenceProvider(selector: DocumentSelector, provider: ReferenceProvider): Disposable; + + /** + * Register a rename provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and asked in sequence. The first provider producing a result + * defines the result of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A rename provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerRenameProvider(selector: DocumentSelector, provider: RenameProvider): Disposable; + + /** + * Register a semantic tokens provider for a whole document. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document semantic tokens provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentSemanticTokensProvider(selector: DocumentSelector, provider: DocumentSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; + + /** + * Register a semantic tokens provider for a document range. + * + * *Note:* If a document has both a `DocumentSemanticTokensProvider` and a `DocumentRangeSemanticTokensProvider`, + * the range provider will be invoked only initially, for the time in which the full document provider takes + * to resolve the first request. Once the full document provider resolves the first request, the semantic tokens + * provided via the range provider will be discarded and from that point forward, only the document provider + * will be used. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document range semantic tokens provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentRangeSemanticTokensProvider(selector: DocumentSelector, provider: DocumentRangeSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; + + /** + * Register a formatting provider for a document. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document formatting edit provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentFormattingEditProvider(selector: DocumentSelector, provider: DocumentFormattingEditProvider): Disposable; + + /** + * Register a formatting provider for a document range. + * + * *Note:* A document range provider is also a {@link DocumentFormattingEditProvider document formatter} + * which means there is no need to {@link languages.registerDocumentFormattingEditProvider register} a document + * formatter when also registering a range provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document range formatting edit provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentRangeFormattingEditProvider(selector: DocumentSelector, provider: DocumentRangeFormattingEditProvider): Disposable; + + /** + * Register a formatting provider that works on type. The provider is active when the user enables the setting `editor.formatOnType`. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and the best-matching provider is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An on type formatting edit provider. + * @param firstTriggerCharacter A character on which formatting should be triggered, like `}`. + * @param moreTriggerCharacter More trigger characters. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerOnTypeFormattingEditProvider(selector: DocumentSelector, provider: OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacter: string[]): Disposable; + + /** + * Register a signature help provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and called sequentially until a provider returns a + * valid result. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A signature help provider. + * @param triggerCharacters Trigger signature help when the user types one of the characters, like `,` or `(`. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; + + /** + * @see {@link languages.registerSignatureHelpProvider} + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A signature help provider. + * @param metadata Information about the provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; + + /** + * Register a document link provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A document link provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDocumentLinkProvider(selector: DocumentSelector, provider: DocumentLinkProvider): Disposable; + + /** + * Register a color provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A color provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; + + /** + * Register a inlay hints provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An inlay hints provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable; + + /** + * Register a folding range provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. + * If multiple folding ranges start at the same position, only the range of the first registered provider is used. + * If a folding range overlaps with an other range that has a smaller position, it is also ignored. + * + * A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A folding range provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerFoldingRangeProvider(selector: DocumentSelector, provider: FoldingRangeProvider): Disposable; + + /** + * Register a selection range provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A selection range provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; + + /** + * Register a call hierarchy provider. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A call hierarchy provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyProvider): Disposable; + + /** + * Register a type hierarchy provider. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A type hierarchy provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerTypeHierarchyProvider(selector: DocumentSelector, provider: TypeHierarchyProvider): Disposable; + + /** + * Register a linked editing range provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and the best-matching provider that has a result is used. Failure + * of the selected provider will cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A linked editing range provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerLinkedEditingRangeProvider(selector: DocumentSelector, provider: LinkedEditingRangeProvider): Disposable; + + /** + * Registers a new {@link DocumentDropEditProvider}. + * + * @param selector A selector that defines the documents this provider applies to. + * @param provider A drop provider. + * + * @returns A {@link Disposable} that unregisters this provider when disposed of. + */ + export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider): Disposable; + + /** + * Set a {@link LanguageConfiguration language configuration} for a language. + * + * @param language A language identifier like `typescript`. + * @param configuration Language configuration. + * @returns A {@link Disposable} that unsets this configuration. + */ + export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; + } + + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + */ + export enum NotebookEditorRevealType { + /** + * The range will be revealed with as little scrolling as possible. + */ + Default = 0, + + /** + * The range will always be revealed in the center of the viewport. + */ + InCenter = 1, + + /** + * If the range is outside the viewport, it will be revealed in the center of the viewport. + * Otherwise, it will be revealed with as little scrolling as possible. + */ + InCenterIfOutsideViewport = 2, + + /** + * The range will always be revealed at the top of the viewport. + */ + AtTop = 3 + } + + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + * Additional properties of the NotebookEditor are available in the proposed + * API, which will be finalized later. + */ + export interface NotebookEditor { + + /** + * The {@link NotebookDocument notebook document} associated with this notebook editor. + */ + readonly notebook: NotebookDocument; + + /** + * The primary selection in this notebook editor. + */ + selection: NotebookRange; + + /** + * All selections in this notebook editor. + * + * The primary selection (or focused range) is `selections[0]`. When the document has no cells, the primary selection is empty `{ start: 0, end: 0 }`; + */ + selections: readonly NotebookRange[]; + + /** + * The current visible ranges in the editor (vertically). + */ + readonly visibleRanges: readonly NotebookRange[]; + + /** + * The column in which this editor shows. + */ + readonly viewColumn?: ViewColumn; + + /** + * Scroll as indicated by `revealType` in order to reveal the given range. + * + * @param range A range. + * @param revealType The scrolling strategy for revealing `range`. + */ + revealRange(range: NotebookRange, revealType?: NotebookEditorRevealType): void; + } + + /** + * Renderer messaging is used to communicate with a single renderer. It's returned from {@link notebooks.createRendererMessaging}. + */ + export interface NotebookRendererMessaging { + /** + * An event that fires when a message is received from a renderer. + */ + readonly onDidReceiveMessage: Event<{ + /** + * The {@link NotebookEditor editor} that sent the message. + */ + readonly editor: NotebookEditor; + /** + * The actual message. + */ + readonly message: any; + }>; + + /** + * Send a message to one or all renderer. + * + * @param message Message to send + * @param editor Editor to target with the message. If not provided, the + * message is sent to all renderers. + * @returns a boolean indicating whether the message was successfully + * delivered to any renderer. + */ + postMessage(message: any, editor?: NotebookEditor): Thenable; + } + + /** + * A notebook cell kind. + */ + export enum NotebookCellKind { + + /** + * A markup-cell is formatted source that is used for display. + */ + Markup = 1, + + /** + * A code-cell is source that can be {@link NotebookController executed} and that + * produces {@link NotebookCellOutput output}. + */ + Code = 2 + } + + /** + * Represents a cell of a {@link NotebookDocument notebook}, either a {@link NotebookCellKind.Code code}-cell + * or {@link NotebookCellKind.Markup markup}-cell. + * + * NotebookCell instances are immutable and are kept in sync for as long as they are part of their notebook. + */ + export interface NotebookCell { + + /** + * The index of this cell in its {@link NotebookDocument.cellAt containing notebook}. The + * index is updated when a cell is moved within its notebook. The index is `-1` + * when the cell has been removed from its notebook. + */ + readonly index: number; + + /** + * The {@link NotebookDocument notebook} that contains this cell. + */ + readonly notebook: NotebookDocument; + + /** + * The kind of this cell. + */ + readonly kind: NotebookCellKind; + + /** + * The {@link TextDocument text} of this cell, represented as text document. + */ + readonly document: TextDocument; + + /** + * The metadata of this cell. Can be anything but must be JSON-stringifyable. + */ + readonly metadata: { readonly [key: string]: any }; + + /** + * The outputs of this cell. + */ + readonly outputs: readonly NotebookCellOutput[]; + + /** + * The most recent {@link NotebookCellExecutionSummary execution summary} for this cell. + */ + readonly executionSummary: NotebookCellExecutionSummary | undefined; + } + + /** + * Represents a notebook which itself is a sequence of {@link NotebookCell code or markup cells}. Notebook documents are + * created from {@link NotebookData notebook data}. + */ + export interface NotebookDocument { + + /** + * The associated uri for this notebook. + * + * *Note* that most notebooks use the `file`-scheme, which means they are files on disk. However, **not** all notebooks are + * saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk. + * + * @see {@link FileSystemProvider} + */ + readonly uri: Uri; + + /** + * The type of notebook. + */ + readonly notebookType: string; + + /** + * The version number of this notebook (it will strictly increase after each + * change, including undo/redo). + */ + readonly version: number; + + /** + * `true` if there are unpersisted changes. + */ + readonly isDirty: boolean; + + /** + * Is this notebook representing an untitled file which has not been saved yet. + */ + readonly isUntitled: boolean; + + /** + * `true` if the notebook has been closed. A closed notebook isn't synchronized anymore + * and won't be re-used when the same resource is opened again. + */ + readonly isClosed: boolean; + + /** + * Arbitrary metadata for this notebook. Can be anything but must be JSON-stringifyable. + */ + readonly metadata: { [key: string]: any }; + + /** + * The number of cells in the notebook. + */ + readonly cellCount: number; + + /** + * Return the cell at the specified index. The index will be adjusted to the notebook. + * + * @param index - The index of the cell to retrieve. + * @returns A {@link NotebookCell cell}. + */ + cellAt(index: number): NotebookCell; + + /** + * Get the cells of this notebook. A subset can be retrieved by providing + * a range. The range will be adjusted to the notebook. + * + * @param range A notebook range. + * @returns The cells contained by the range or all cells. + */ + getCells(range?: NotebookRange): NotebookCell[]; + + /** + * Save the document. The saving will be handled by the corresponding {@link NotebookSerializer serializer}. + * + * @returns A promise that will resolve to true when the document + * has been saved. Will return false if the file was not dirty or when save failed. + */ + save(): Thenable; + } + + /** + * Describes a change to a notebook cell. + * + * @see {@link NotebookDocumentChangeEvent} + */ + export interface NotebookDocumentCellChange { + + /** + * The affected cell. + */ + readonly cell: NotebookCell; + + /** + * The document of the cell or `undefined` when it did not change. + * + * *Note* that you should use the {@link workspace.onDidChangeTextDocument onDidChangeTextDocument}-event + * for detailed change information, like what edits have been performed. + */ + readonly document: TextDocument | undefined; + + /** + * The new metadata of the cell or `undefined` when it did not change. + */ + readonly metadata: { [key: string]: any } | undefined; + + /** + * The new outputs of the cell or `undefined` when they did not change. + */ + readonly outputs: readonly NotebookCellOutput[] | undefined; + + /** + * The new execution summary of the cell or `undefined` when it did not change. + */ + readonly executionSummary: NotebookCellExecutionSummary | undefined; + } + + /** + * Describes a structural change to a notebook document, e.g newly added and removed cells. + * + * @see {@link NotebookDocumentChangeEvent} + */ + export interface NotebookDocumentContentChange { + + /** + * The range at which cells have been either added or removed. + * + * Note that no cells have been {@link NotebookDocumentContentChange.removedCells removed} + * when this range is {@link NotebookRange.isEmpty empty}. + */ + readonly range: NotebookRange; + + /** + * Cells that have been added to the document. + */ + readonly addedCells: readonly NotebookCell[]; + + /** + * Cells that have been removed from the document. + */ + readonly removedCells: readonly NotebookCell[]; + } + + /** + * An event describing a transactional {@link NotebookDocument notebook} change. + */ + export interface NotebookDocumentChangeEvent { + + /** + * The affected notebook. + */ + readonly notebook: NotebookDocument; + + /** + * The new metadata of the notebook or `undefined` when it did not change. + */ + readonly metadata: { [key: string]: any } | undefined; + + /** + * An array of content changes describing added or removed {@link NotebookCell cells}. + */ + readonly contentChanges: readonly NotebookDocumentContentChange[]; + + /** + * An array of {@link NotebookDocumentCellChange cell changes}. + */ + readonly cellChanges: readonly NotebookDocumentCellChange[]; + } + + /** + * An event that is fired when a {@link NotebookDocument notebook document} will be saved. + * + * To make modifications to the document before it is being saved, call the + * {@linkcode NotebookDocumentWillSaveEvent.waitUntil waitUntil}-function with a thenable + * that resolves to a {@link WorkspaceEdit workspace edit}. + */ + export interface NotebookDocumentWillSaveEvent { + /** + * A cancellation token. + */ + readonly token: CancellationToken; + + /** + * The {@link NotebookDocument notebook document} that will be saved. + */ + readonly notebook: NotebookDocument; + + /** + * The reason why save was triggered. + */ + readonly reason: TextDocumentSaveReason; + + /** + * Allows to pause the event loop and to apply {@link WorkspaceEdit workspace edit}. + * Edits of subsequent calls to this function will be applied in order. The + * edits will be *ignored* if concurrent modifications of the notebook document happened. + * + * *Note:* This function can only be called during event dispatch and not + * in an asynchronous manner: + * + * ```ts + * workspace.onWillSaveNotebookDocument(event => { + * // async, will *throw* an error + * setTimeout(() => event.waitUntil(promise)); + * + * // sync, OK + * event.waitUntil(promise); + * }) + * ``` + * + * @param thenable A thenable that resolves to {@link WorkspaceEdit workspace edit}. + */ + waitUntil(thenable: Thenable): void; + + /** + * Allows to pause the event loop until the provided thenable resolved. + * + * *Note:* This function can only be called during event dispatch. + * + * @param thenable A thenable that delays saving. + */ + waitUntil(thenable: Thenable): void; + } + + /** + * The summary of a notebook cell execution. + */ + export interface NotebookCellExecutionSummary { + + /** + * The order in which the execution happened. + */ + readonly executionOrder?: number; + + /** + * If the execution finished successfully. + */ + readonly success?: boolean; + + /** + * The times at which execution started and ended, as unix timestamps + */ + readonly timing?: { + /** + * Execution start time. + */ + readonly startTime: number; + /** + * Execution end time. + */ + readonly endTime: number; + }; + } + + /** + * A notebook range represents an ordered pair of two cell indices. + * It is guaranteed that start is less than or equal to end. + */ + export class NotebookRange { + + /** + * The zero-based start index of this range. + */ + readonly start: number; + + /** + * The exclusive end index of this range (zero-based). + */ + readonly end: number; + + /** + * `true` if `start` and `end` are equal. + */ + readonly isEmpty: boolean; + + /** + * Create a new notebook range. If `start` is not + * before or equal to `end`, the values will be swapped. + * + * @param start start index + * @param end end index. + */ + constructor(start: number, end: number); + + /** + * Derive a new range for this range. + * + * @param change An object that describes a change to this range. + * @returns A range that reflects the given change. Will return `this` range if the change + * is not changing anything. + */ + with(change: { + /** + * New start index, defaults to `this.start`. + */ + start?: number; + /** + * New end index, defaults to `this.end`. + */ + end?: number; + }): NotebookRange; + } + + /** + * One representation of a {@link NotebookCellOutput notebook output}, defined by MIME type and data. + */ + export class NotebookCellOutputItem { + + /** + * Factory function to create a `NotebookCellOutputItem` from a string. + * + * *Note* that an UTF-8 encoder is used to create bytes for the string. + * + * @param value A string. + * @param mime Optional MIME type, defaults to `text/plain`. + * @returns A new output item object. + */ + static text(value: string, mime?: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` from + * a JSON object. + * + * *Note* that this function is not expecting "stringified JSON" but + * an object that can be stringified. This function will throw an error + * when the passed value cannot be JSON-stringified. + * + * @param value A JSON-stringifyable value. + * @param mime Optional MIME type, defaults to `application/json` + * @returns A new output item object. + */ + static json(value: any, mime?: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` that uses + * uses the `application/vnd.code.notebook.stdout` mime type. + * + * @param value A string. + * @returns A new output item object. + */ + static stdout(value: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` that uses + * uses the `application/vnd.code.notebook.stderr` mime type. + * + * @param value A string. + * @returns A new output item object. + */ + static stderr(value: string): NotebookCellOutputItem; + + /** + * Factory function to create a `NotebookCellOutputItem` that uses + * uses the `application/vnd.code.notebook.error` mime type. + * + * @param value An error object. + * @returns A new output item object. + */ + static error(value: Error): NotebookCellOutputItem; + + /** + * The mime type which determines how the {@linkcode NotebookCellOutputItem.data data}-property + * is interpreted. + * + * Notebooks have built-in support for certain mime-types, extensions can add support for new + * types and override existing types. + */ + mime: string; + + /** + * The data of this output item. Must always be an array of unsigned 8-bit integers. + */ + data: Uint8Array; + + /** + * Create a new notebook cell output item. + * + * @param data The value of the output item. + * @param mime The mime type of the output item. + */ + constructor(data: Uint8Array, mime: string); + } + + /** + * Notebook cell output represents a result of executing a cell. It is a container type for multiple + * {@link NotebookCellOutputItem output items} where contained items represent the same result but + * use different MIME types. + */ + export class NotebookCellOutput { + + /** + * The output items of this output. Each item must represent the same result. _Note_ that repeated + * MIME types per output is invalid and that the editor will just pick one of them. + * + * ```ts + * new vscode.NotebookCellOutput([ + * vscode.NotebookCellOutputItem.text('Hello', 'text/plain'), + * vscode.NotebookCellOutputItem.text('Hello', 'text/html'), + * vscode.NotebookCellOutputItem.text('_Hello_', 'text/markdown'), + * vscode.NotebookCellOutputItem.text('Hey', 'text/plain'), // INVALID: repeated type, editor will pick just one + * ]) + * ``` + */ + items: NotebookCellOutputItem[]; + + /** + * Arbitrary metadata for this cell output. Can be anything but must be JSON-stringifyable. + */ + metadata?: { [key: string]: any }; + + /** + * Create new notebook output. + * + * @param items Notebook output items. + * @param metadata Optional metadata. + */ + constructor(items: NotebookCellOutputItem[], metadata?: { [key: string]: any }); + } + + /** + * NotebookCellData is the raw representation of notebook cells. Its is part of {@linkcode NotebookData}. + */ + export class NotebookCellData { + + /** + * The {@link NotebookCellKind kind} of this cell data. + */ + kind: NotebookCellKind; + + /** + * The source value of this cell data - either source code or formatted text. + */ + value: string; + + /** + * The language identifier of the source value of this cell data. Any value from + * {@linkcode languages.getLanguages getLanguages} is possible. + */ + languageId: string; + + /** + * The outputs of this cell data. + */ + outputs?: NotebookCellOutput[]; + + /** + * Arbitrary metadata of this cell data. Can be anything but must be JSON-stringifyable. + */ + metadata?: { [key: string]: any }; + + /** + * The execution summary of this cell data. + */ + executionSummary?: NotebookCellExecutionSummary; + + /** + * Create new cell data. Minimal cell data specifies its kind, its source value, and the + * language identifier of its source. + * + * @param kind The kind. + * @param value The source value. + * @param languageId The language identifier of the source value. + */ + constructor(kind: NotebookCellKind, value: string, languageId: string); + } + + /** + * Raw representation of a notebook. + * + * Extensions are responsible for creating {@linkcode NotebookData} so that the editor + * can create a {@linkcode NotebookDocument}. + * + * @see {@link NotebookSerializer} + */ + export class NotebookData { + /** + * The cell data of this notebook data. + */ + cells: NotebookCellData[]; + + /** + * Arbitrary metadata of notebook data. + */ + metadata?: { [key: string]: any }; + + /** + * Create new notebook data. + * + * @param cells An array of cell data. + */ + constructor(cells: NotebookCellData[]); + } + + /** + * The notebook serializer enables the editor to open notebook files. + * + * At its core the editor only knows a {@link NotebookData notebook data structure} but not + * how that data structure is written to a file, nor how it is read from a file. The + * notebook serializer bridges this gap by deserializing bytes into notebook data and + * vice versa. + */ + export interface NotebookSerializer { + + /** + * Deserialize contents of a notebook file into the notebook data structure. + * + * @param content Contents of a notebook file. + * @param token A cancellation token. + * @returns Notebook data or a thenable that resolves to such. + */ + deserializeNotebook(content: Uint8Array, token: CancellationToken): NotebookData | Thenable; + + /** + * Serialize notebook data into file contents. + * + * @param data A notebook data structure. + * @param token A cancellation token. + * @returns An array of bytes or a thenable that resolves to such. + */ + serializeNotebook(data: NotebookData, token: CancellationToken): Uint8Array | Thenable; + } + + /** + * Notebook content options define what parts of a notebook are persisted. Note + * + * For instance, a notebook serializer can opt-out of saving outputs and in that case the editor doesn't mark a + * notebooks as {@link NotebookDocument.isDirty dirty} when its output has changed. + */ + export interface NotebookDocumentContentOptions { + /** + * Controls if output change events will trigger notebook document content change events and + * if it will be used in the diff editor, defaults to false. If the content provider doesn't + * persist the outputs in the file document, this should be set to true. + */ + transientOutputs?: boolean; + + /** + * Controls if a cell metadata property change event will trigger notebook document content + * change events and if it will be used in the diff editor, defaults to false. If the + * content provider doesn't persist a metadata property in the file document, it should be + * set to true. + */ + transientCellMetadata?: { [key: string]: boolean | undefined }; + + /** + * Controls if a document metadata property change event will trigger notebook document + * content change event and if it will be used in the diff editor, defaults to false. If the + * content provider doesn't persist a metadata property in the file document, it should be + * set to true. + */ + transientDocumentMetadata?: { [key: string]: boolean | undefined }; + } + + /** + * Notebook controller affinity for notebook documents. + * + * @see {@link NotebookController.updateNotebookAffinity} + */ + export enum NotebookControllerAffinity { + /** + * Default affinity. + */ + Default = 1, + /** + * A controller is preferred for a notebook. + */ + Preferred = 2 + } + + /** + * A notebook controller represents an entity that can execute notebook cells. This is often referred to as a kernel. + * + * There can be multiple controllers and the editor will let users choose which controller to use for a certain notebook. The + * {@linkcode NotebookController.notebookType notebookType}-property defines for what kind of notebooks a controller is for and + * the {@linkcode NotebookController.updateNotebookAffinity updateNotebookAffinity}-function allows controllers to set a preference + * for specific notebook documents. When a controller has been selected its + * {@link NotebookController.onDidChangeSelectedNotebooks onDidChangeSelectedNotebooks}-event fires. + * + * When a cell is being run the editor will invoke the {@linkcode NotebookController.executeHandler executeHandler} and a controller + * is expected to create and finalize a {@link NotebookCellExecution notebook cell execution}. However, controllers are also free + * to create executions by themselves. + */ + export interface NotebookController { + + /** + * The identifier of this notebook controller. + * + * _Note_ that controllers are remembered by their identifier and that extensions should use + * stable identifiers across sessions. + */ + readonly id: string; + + /** + * The notebook type this controller is for. + */ + readonly notebookType: string; + + /** + * An array of language identifiers that are supported by this + * controller. Any language identifier from {@linkcode languages.getLanguages getLanguages} + * is possible. When falsy all languages are supported. + * + * Samples: + * ```js + * // support JavaScript and TypeScript + * myController.supportedLanguages = ['javascript', 'typescript'] + * + * // support all languages + * myController.supportedLanguages = undefined; // falsy + * myController.supportedLanguages = []; // falsy + * ``` + */ + supportedLanguages?: string[]; + + /** + * The human-readable label of this notebook controller. + */ + label: string; + + /** + * The human-readable description which is rendered less prominent. + */ + description?: string; + + /** + * The human-readable detail which is rendered less prominent. + */ + detail?: string; + + /** + * Whether this controller supports execution order so that the + * editor can render placeholders for them. + */ + supportsExecutionOrder?: boolean; + + /** + * Create a cell execution task. + * + * _Note_ that there can only be one execution per cell at a time and that an error is thrown if + * a cell execution is created while another is still active. + * + * This should be used in response to the {@link NotebookController.executeHandler execution handler} + * being called or when cell execution has been started else, e.g when a cell was already + * executing or when cell execution was triggered from another source. + * + * @param cell The notebook cell for which to create the execution. + * @returns A notebook cell execution. + */ + createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution; + + /** + * The execute handler is invoked when the run gestures in the UI are selected, e.g Run Cell, Run All, + * Run Selection etc. The execute handler is responsible for creating and managing {@link NotebookCellExecution execution}-objects. + */ + executeHandler: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable; + + /** + * Optional interrupt handler. + * + * By default cell execution is canceled via {@link NotebookCellExecution.token tokens}. Cancellation + * tokens require that a controller can keep track of its execution so that it can cancel a specific execution at a later + * point. Not all scenarios allow for that, eg. REPL-style controllers often work by interrupting whatever is currently + * running. For those cases the interrupt handler exists - it can be thought of as the equivalent of `SIGINT` + * or `Control+C` in terminals. + * + * _Note_ that supporting {@link NotebookCellExecution.token cancellation tokens} is preferred and that interrupt handlers should + * only be used when tokens cannot be supported. + */ + interruptHandler?: (notebook: NotebookDocument) => void | Thenable; + + /** + * An event that fires whenever a controller has been selected or un-selected for a notebook document. + * + * There can be multiple controllers for a notebook and in that case a controllers needs to be _selected_. This is a user gesture + * and happens either explicitly or implicitly when interacting with a notebook for which a controller was _suggested_. When possible, + * the editor _suggests_ a controller that is most likely to be _selected_. + * + * _Note_ that controller selection is persisted (by the controllers {@link NotebookController.id id}) and restored as soon as a + * controller is re-created or as a notebook is {@link workspace.onDidOpenNotebookDocument opened}. + */ + readonly onDidChangeSelectedNotebooks: Event<{ + /** + * The notebook for which the controller has been selected or un-selected. + */ + readonly notebook: NotebookDocument; + /** + * Whether the controller has been selected or un-selected. + */ + readonly selected: boolean; + }>; + + /** + * A controller can set affinities for specific notebook documents. This allows a controller + * to be presented more prominent for some notebooks. + * + * @param notebook The notebook for which a priority is set. + * @param affinity A controller affinity + */ + updateNotebookAffinity(notebook: NotebookDocument, affinity: NotebookControllerAffinity): void; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + /** + * A NotebookCellExecution is how {@link NotebookController notebook controller} modify a notebook cell as + * it is executing. + * + * When a cell execution object is created, the cell enters the {@linkcode NotebookCellExecutionState.Pending Pending} state. + * When {@linkcode NotebookCellExecution.start start(...)} is called on the execution task, it enters the {@linkcode NotebookCellExecutionState.Executing Executing} state. When + * {@linkcode NotebookCellExecution.end end(...)} is called, it enters the {@linkcode NotebookCellExecutionState.Idle Idle} state. + */ + export interface NotebookCellExecution { + + /** + * The {@link NotebookCell cell} for which this execution has been created. + */ + readonly cell: NotebookCell; + + /** + * A cancellation token which will be triggered when the cell execution is canceled + * from the UI. + * + * _Note_ that the cancellation token will not be triggered when the {@link NotebookController controller} + * that created this execution uses an {@link NotebookController.interruptHandler interrupt-handler}. + */ + readonly token: CancellationToken; + + /** + * Set and unset the order of this cell execution. + */ + executionOrder: number | undefined; + + /** + * Signal that the execution has begun. + * + * @param startTime The time that execution began, in milliseconds in the Unix epoch. Used to drive the clock + * that shows for how long a cell has been running. If not given, the clock won't be shown. + */ + start(startTime?: number): void; + + /** + * Signal that execution has ended. + * + * @param success If true, a green check is shown on the cell status bar. + * If false, a red X is shown. + * If undefined, no check or X icon is shown. + * @param endTime The time that execution finished, in milliseconds in the Unix epoch. + */ + end(success: boolean | undefined, endTime?: number): void; + + /** + * Clears the output of the cell that is executing or of another cell that is affected by this execution. + * + * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of + * this execution. + * @returns A thenable that resolves when the operation finished. + */ + clearOutput(cell?: NotebookCell): Thenable; + + /** + * Replace the output of the cell that is executing or of another cell that is affected by this execution. + * + * @param out Output that replaces the current output. + * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of + * this execution. + * @returns A thenable that resolves when the operation finished. + */ + replaceOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; + + /** + * Append to the output of the cell that is executing or to another cell that is affected by this execution. + * + * @param out Output that is appended to the current output. + * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of + * this execution. + * @returns A thenable that resolves when the operation finished. + */ + appendOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; + + /** + * Replace all output items of existing cell output. + * + * @param items Output items that replace the items of existing output. + * @param output Output object that already exists. + * @returns A thenable that resolves when the operation finished. + */ + replaceOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; + + /** + * Append output items to existing cell output. + * + * @param items Output items that are append to existing output. + * @param output Output object that already exists. + * @returns A thenable that resolves when the operation finished. + */ + appendOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; + } + + /** + * Represents the alignment of status bar items. + */ + export enum NotebookCellStatusBarAlignment { + + /** + * Aligned to the left side. + */ + Left = 1, + + /** + * Aligned to the right side. + */ + Right = 2 + } + + /** + * A contribution to a cell's status bar + */ + export class NotebookCellStatusBarItem { + /** + * The text to show for the item. + */ + text: string; + + /** + * Whether the item is aligned to the left or right. + */ + alignment: NotebookCellStatusBarAlignment; + + /** + * An optional {@linkcode Command} or identifier of a command to run on click. + * + * The command must be {@link commands.getCommands known}. + * + * Note that if this is a {@linkcode Command} object, only the {@linkcode Command.command command} and {@linkcode Command.arguments arguments} + * are used by the editor. + */ + command?: string | Command; + + /** + * A tooltip to show when the item is hovered. + */ + tooltip?: string; + + /** + * The priority of the item. A higher value item will be shown more to the left. + */ + priority?: number; + + /** + * Accessibility information used when a screen reader interacts with this item. + */ + accessibilityInformation?: AccessibilityInformation; + + /** + * Creates a new NotebookCellStatusBarItem. + * @param text The text to show for the item. + * @param alignment Whether the item is aligned to the left or right. + */ + constructor(text: string, alignment: NotebookCellStatusBarAlignment); + } + + /** + * A provider that can contribute items to the status bar that appears below a cell's editor. + */ + export interface NotebookCellStatusBarItemProvider { + /** + * An optional event to signal that statusbar items have changed. The provide method will be called again. + */ + onDidChangeCellStatusBarItems?: Event; + + /** + * The provider will be called when the cell scrolls into view, when its content, outputs, language, or metadata change, and when it changes execution state. + * @param cell The cell for which to return items. + * @param token A token triggered if this request should be cancelled. + * @returns One or more {@link NotebookCellStatusBarItem cell statusbar items} + */ + provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult; + } + + /** + * Namespace for notebooks. + * + * The notebooks functionality is composed of three loosely coupled components: + * + * 1. {@link NotebookSerializer} enable the editor to open, show, and save notebooks + * 2. {@link NotebookController} own the execution of notebooks, e.g they create output from code cells. + * 3. NotebookRenderer present notebook output in the editor. They run in a separate context. + */ + export namespace notebooks { + + /** + * Creates a new notebook controller. + * + * @param id Identifier of the controller. Must be unique per extension. + * @param notebookType A notebook type for which this controller is for. + * @param label The label of the controller. + * @param handler The execute-handler of the controller. + * @returns A new notebook controller. + */ + export function createNotebookController(id: string, notebookType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable): NotebookController; + + /** + * Register a {@link NotebookCellStatusBarItemProvider cell statusbar item provider} for the given notebook type. + * + * @param notebookType The notebook type to register for. + * @param provider A cell status bar provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable; + + /** + * Creates a new messaging instance used to communicate with a specific renderer. + * + * * *Note 1:* Extensions can only create renderer that they have defined in their `package.json`-file + * * *Note 2:* A renderer only has access to messaging if `requiresMessaging` is set to `always` or `optional` in + * its `notebookRenderer` contribution. + * + * @param rendererId The renderer ID to communicate with + * @returns A new notebook renderer messaging object. + */ + export function createRendererMessaging(rendererId: string): NotebookRendererMessaging; + } + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Setter and getter for the contents of the input box. + */ + value: string; + + /** + * A string to show as placeholder in the input box to guide the user. + */ + placeholder: string; + + /** + * Controls whether the input box is enabled (default is `true`). + */ + enabled: boolean; + + /** + * Controls whether the input box is visible (default is `true`). + */ + visible: boolean; + } + + /** + * A quick diff provider provides a {@link Uri uri} to the original state of a + * modified resource. The editor will use this information to render ad'hoc diffs + * within the text. + */ + export interface QuickDiffProvider { + + /** + * Provide a {@link Uri} to the original resource of any given resource uri. + * + * @param uri The uri of the resource open in a text editor. + * @param token A cancellation token. + * @returns A thenable that resolves to uri of the matching original resource. + */ + provideOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult; + } + + /** + * The theme-aware decorations for a + * {@link SourceControlResourceState source control resource state}. + */ + export interface SourceControlResourceThemableDecorations { + + /** + * The icon path for a specific + * {@link SourceControlResourceState source control resource state}. + */ + readonly iconPath?: string | Uri | ThemeIcon; + } + + /** + * The decorations for a {@link SourceControlResourceState source control resource state}. + * Can be independently specified for light and dark themes. + */ + export interface SourceControlResourceDecorations extends SourceControlResourceThemableDecorations { + + /** + * Whether the {@link SourceControlResourceState source control resource state} should + * be striked-through in the UI. + */ + readonly strikeThrough?: boolean; + + /** + * Whether the {@link SourceControlResourceState source control resource state} should + * be faded in the UI. + */ + readonly faded?: boolean; + + /** + * The title for a specific + * {@link SourceControlResourceState source control resource state}. + */ + readonly tooltip?: string; + + /** + * The light theme decorations. + */ + readonly light?: SourceControlResourceThemableDecorations; + + /** + * The dark theme decorations. + */ + readonly dark?: SourceControlResourceThemableDecorations; + } + + /** + * An source control resource state represents the state of an underlying workspace + * resource within a certain {@link SourceControlResourceGroup source control group}. + */ + export interface SourceControlResourceState { + + /** + * The {@link Uri} of the underlying resource inside the workspace. + */ + readonly resourceUri: Uri; + + /** + * The {@link Command} which should be run when the resource + * state is open in the Source Control viewlet. + */ + readonly command?: Command; + + /** + * The {@link SourceControlResourceDecorations decorations} for this source control + * resource state. + */ + readonly decorations?: SourceControlResourceDecorations; + + /** + * Context value of the resource state. This can be used to contribute resource specific actions. + * For example, if a resource is given a context value as `diffable`. When contributing actions to `scm/resourceState/context` + * using `menus` extension point, you can specify context value for key `scmResourceState` in `when` expressions, like `scmResourceState == diffable`. + * ```json + * "contributes": { + * "menus": { + * "scm/resourceState/context": [ + * { + * "command": "extension.diff", + * "when": "scmResourceState == diffable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.diff` only for resources with `contextValue` is `diffable`. + */ + readonly contextValue?: string; + } + + /** + * A source control resource group is a collection of + * {@link SourceControlResourceState source control resource states}. + */ + export interface SourceControlResourceGroup { + + /** + * The id of this source control resource group. + */ + readonly id: string; + + /** + * The label of this source control resource group. + */ + label: string; + + /** + * Whether this source control resource group is hidden when it contains + * no {@link SourceControlResourceState source control resource states}. + */ + hideWhenEmpty?: boolean; + + /** + * This group's collection of + * {@link SourceControlResourceState source control resource states}. + */ + resourceStates: SourceControlResourceState[]; + + /** + * Dispose this source control resource group. + */ + dispose(): void; + } + + /** + * An source control is able to provide {@link SourceControlResourceState resource states} + * to the editor and interact with the editor in several source control related ways. + */ + export interface SourceControl { + + /** + * The id of this source control. + */ + readonly id: string; + + /** + * The human-readable label of this source control. + */ + readonly label: string; + + /** + * The (optional) Uri of the root of this source control. + */ + readonly rootUri: Uri | undefined; + + /** + * The {@link SourceControlInputBox input box} for this source control. + */ + readonly inputBox: SourceControlInputBox; + + /** + * The UI-visible count of {@link SourceControlResourceState resource states} of + * this source control. + * + * If undefined, this source control will + * - display its UI-visible count as zero, and + * - contribute the count of its {@link SourceControlResourceState resource states} to the UI-visible aggregated count for all source controls + */ + count?: number; + + /** + * An optional {@link QuickDiffProvider quick diff provider}. + */ + quickDiffProvider?: QuickDiffProvider; + + /** + * Optional commit template string. + * + * The Source Control viewlet will populate the Source Control + * input with this value when appropriate. + */ + commitTemplate?: string; + + /** + * Optional accept input command. + * + * This command will be invoked when the user accepts the value + * in the Source Control input. + */ + acceptInputCommand?: Command; + + /** + * Optional status bar commands. + * + * These commands will be displayed in the editor's status bar. + */ + statusBarCommands?: Command[]; + + /** + * Create a new {@link SourceControlResourceGroup resource group}. + */ + createResourceGroup(id: string, label: string): SourceControlResourceGroup; + + /** + * Dispose this source control. + */ + dispose(): void; + } + + /** + * Namespace for source control mangement. + */ + export namespace scm { + + /** + * The {@link SourceControlInputBox input box} for the last source control + * created by the extension. + * + * @deprecated Use SourceControl.inputBox instead + */ + export const inputBox: SourceControlInputBox; + + /** + * Creates a new {@link SourceControl source control} instance. + * + * @param id An `id` for the source control. Something short, e.g.: `git`. + * @param label A human-readable string for the source control. E.g.: `Git`. + * @param rootUri An optional Uri of the root of the source control. E.g.: `Uri.parse(workspaceRoot)`. + * @returns An instance of {@link SourceControl source control}. + */ + export function createSourceControl(id: string, label: string, rootUri?: Uri): SourceControl; + } + + /** + * A DebugProtocolMessage is an opaque stand-in type for the [ProtocolMessage](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolMessage { + // Properties: see [ProtocolMessage details](https://microsoft.github.io/debug-adapter-protocol/specification#Base_Protocol_ProtocolMessage). + } + + /** + * A DebugProtocolSource is an opaque stand-in type for the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolSource { + // Properties: see [Source details](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source). + } + + /** + * A DebugProtocolBreakpoint is an opaque stand-in type for the [Breakpoint](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint) type defined in the Debug Adapter Protocol. + */ + export interface DebugProtocolBreakpoint { + // Properties: see [Breakpoint details](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Breakpoint). + } + + /** + * Configuration for a debug session. + */ + export interface DebugConfiguration { + /** + * The type of the debug session. + */ + type: string; + + /** + * The name of the debug session. + */ + name: string; + + /** + * The request type of the debug session. + */ + request: string; + + /** + * Additional debug type specific properties. + */ + [key: string]: any; + } + + /** + * A debug session. + */ + export interface DebugSession { + + /** + * The unique ID of this debug session. + */ + readonly id: string; + + /** + * The debug session's type from the {@link DebugConfiguration debug configuration}. + */ + readonly type: string; + + /** + * The parent session of this debug session, if it was created as a child. + * @see DebugSessionOptions.parentSession + */ + readonly parentSession?: DebugSession; + + /** + * The debug session's name is initially taken from the {@link DebugConfiguration debug configuration}. + * Any changes will be properly reflected in the UI. + */ + name: string; + + /** + * The workspace folder of this session or `undefined` for a folderless setup. + */ + readonly workspaceFolder: WorkspaceFolder | undefined; + + /** + * The "resolved" {@link DebugConfiguration debug configuration} of this session. + * "Resolved" means that + * - all variables have been substituted and + * - platform specific attribute sections have been "flattened" for the matching platform and removed for non-matching platforms. + */ + readonly configuration: DebugConfiguration; + + /** + * Send a custom request to the debug adapter. + */ + customRequest(command: string, args?: any): Thenable; + + /** + * Maps a breakpoint in the editor to the corresponding Debug Adapter Protocol (DAP) breakpoint that is managed by the debug adapter of the debug session. + * If no DAP breakpoint exists (either because the editor breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned. + * + * @param breakpoint A {@link Breakpoint} in the editor. + * @returns A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`. + */ + getDebugProtocolBreakpoint(breakpoint: Breakpoint): Thenable; + } + + /** + * A custom Debug Adapter Protocol event received from a {@link DebugSession debug session}. + */ + export interface DebugSessionCustomEvent { + /** + * The {@link DebugSession debug session} for which the custom event was received. + */ + readonly session: DebugSession; + + /** + * Type of event. + */ + readonly event: string; + + /** + * Event specific information. + */ + readonly body: any; + } + + /** + * A debug configuration provider allows to add debug configurations to the debug service + * and to resolve launch configurations before they are used to start a debug session. + * A debug configuration provider is registered via {@link debug.registerDebugConfigurationProvider}. + */ + export interface DebugConfigurationProvider { + /** + * Provides {@link DebugConfiguration debug configuration} to the debug service. If more than one debug configuration provider is + * registered for the same type, debug configurations are concatenated in arbitrary order. + * + * @param folder The workspace folder for which the configurations are used or `undefined` for a folderless setup. + * @param token A cancellation token. + * @returns An array of {@link DebugConfiguration debug configurations}. + */ + provideDebugConfigurations?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; + + /** + * Resolves a {@link DebugConfiguration debug configuration} by filling in missing values or by adding/changing/removing attributes. + * If more than one debug configuration provider is registered for the same type, the resolveDebugConfiguration calls are chained + * in arbitrary order and the initial debug configuration is piped through the chain. + * Returning the value 'undefined' prevents the debug session from starting. + * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. + * + * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. + * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. + * @param token A cancellation token. + * @returns The resolved debug configuration or undefined or null. + */ + resolveDebugConfiguration?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; + + /** + * This hook is directly called after 'resolveDebugConfiguration' but with all variables substituted. + * It can be used to resolve or verify a {@link DebugConfiguration debug configuration} by filling in missing values or by adding/changing/removing attributes. + * If more than one debug configuration provider is registered for the same type, the 'resolveDebugConfigurationWithSubstitutedVariables' calls are chained + * in arbitrary order and the initial debug configuration is piped through the chain. + * Returning the value 'undefined' prevents the debug session from starting. + * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. + * + * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. + * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. + * @param token A cancellation token. + * @returns The resolved debug configuration or undefined or null. + */ + resolveDebugConfigurationWithSubstitutedVariables?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; + } + + /** + * Represents a debug adapter executable and optional arguments and runtime options passed to it. + */ + export class DebugAdapterExecutable { + + /** + * Creates a description for a debug adapter based on an executable program. + * + * @param command The command or executable path that implements the debug adapter. + * @param args Optional arguments to be passed to the command or executable. + * @param options Optional options to be used when starting the command or executable. + */ + constructor(command: string, args?: string[], options?: DebugAdapterExecutableOptions); + + /** + * The command or path of the debug adapter executable. + * A command must be either an absolute path of an executable or the name of an command to be looked up via the PATH environment variable. + * The special value 'node' will be mapped to the editor's built-in Node.js runtime. + */ + readonly command: string; + + /** + * The arguments passed to the debug adapter executable. Defaults to an empty array. + */ + readonly args: string[]; + + /** + * Optional options to be used when the debug adapter is started. + * Defaults to undefined. + */ + readonly options?: DebugAdapterExecutableOptions; + } + + /** + * Options for a debug adapter executable. + */ + export interface DebugAdapterExecutableOptions { + + /** + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + + /** + * The current working directory for the executed debug adapter. + */ + cwd?: string; + } + + /** + * Represents a debug adapter running as a socket based server. + */ + export class DebugAdapterServer { + + /** + * The port. + */ + readonly port: number; + + /** + * The host. + */ + readonly host?: string | undefined; + + /** + * Create a description for a debug adapter running as a socket based server. + */ + constructor(port: number, host?: string); + } + + /** + * Represents a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server. + */ + export class DebugAdapterNamedPipeServer { + /** + * The path to the NamedPipe/UNIX Domain Socket. + */ + readonly path: string; + + /** + * Create a description for a debug adapter running as a Named Pipe (on Windows)/UNIX Domain Socket (on non-Windows) based server. + */ + constructor(path: string); + } + + /** + * A debug adapter that implements the Debug Adapter Protocol can be registered with the editor if it implements the DebugAdapter interface. + */ + export interface DebugAdapter extends Disposable { + + /** + * An event which fires after the debug adapter has sent a Debug Adapter Protocol message to the editor. + * Messages can be requests, responses, or events. + */ + readonly onDidSendMessage: Event; + + /** + * Handle a Debug Adapter Protocol message. + * Messages can be requests, responses, or events. + * Results or errors are returned via onSendMessage events. + * @param message A Debug Adapter Protocol message + */ + handleMessage(message: DebugProtocolMessage): void; + } + + /** + * A debug adapter descriptor for an inline implementation. + */ + export class DebugAdapterInlineImplementation { + + /** + * Create a descriptor for an inline implementation of a debug adapter. + */ + constructor(implementation: DebugAdapter); + } + + /** + * Represents the different types of debug adapters + */ + export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation; + + /** + * A debug adaper factory that creates {@link DebugAdapterDescriptor debug adapter descriptors}. + */ + export interface DebugAdapterDescriptorFactory { + /** + * 'createDebugAdapterDescriptor' is called at the start of a debug session to provide details about the debug adapter to use. + * These details must be returned as objects of type {@link DebugAdapterDescriptor}. + * Currently two types of debug adapters are supported: + * - a debug adapter executable is specified as a command path and arguments (see {@link DebugAdapterExecutable}), + * - a debug adapter server reachable via a communication port (see {@link DebugAdapterServer}). + * If the method is not implemented the default behavior is this: + * createDebugAdapter(session: DebugSession, executable: DebugAdapterExecutable) { + * if (typeof session.configuration.debugServer === 'number') { + * return new DebugAdapterServer(session.configuration.debugServer); + * } + * return executable; + * } + * @param session The {@link DebugSession debug session} for which the debug adapter will be used. + * @param executable The debug adapter's executable information as specified in the package.json (or undefined if no such information exists). + * @returns a {@link DebugAdapterDescriptor debug adapter descriptor} or undefined. + */ + createDebugAdapterDescriptor(session: DebugSession, executable: DebugAdapterExecutable | undefined): ProviderResult; + } + + /** + * A Debug Adapter Tracker is a means to track the communication between the editor and a Debug Adapter. + */ + export interface DebugAdapterTracker { + /** + * A session with the debug adapter is about to be started. + */ + onWillStartSession?(): void; + /** + * The debug adapter is about to receive a Debug Adapter Protocol message from the editor. + */ + onWillReceiveMessage?(message: any): void; + /** + * The debug adapter has sent a Debug Adapter Protocol message to the editor. + */ + onDidSendMessage?(message: any): void; + /** + * The debug adapter session is about to be stopped. + */ + onWillStopSession?(): void; + /** + * An error with the debug adapter has occurred. + */ + onError?(error: Error): void; + /** + * The debug adapter has exited with the given exit code or signal. + */ + onExit?(code: number | undefined, signal: string | undefined): void; + } + + /** + * A debug adaper factory that creates {@link DebugAdapterTracker debug adapter trackers}. + */ + export interface DebugAdapterTrackerFactory { + /** + * The method 'createDebugAdapterTracker' is called at the start of a debug session in order + * to return a "tracker" object that provides read-access to the communication between the editor and a debug adapter. + * + * @param session The {@link DebugSession debug session} for which the debug adapter tracker will be used. + * @returns A {@link DebugAdapterTracker debug adapter tracker} or undefined. + */ + createDebugAdapterTracker(session: DebugSession): ProviderResult; + } + + /** + * Represents the debug console. + */ + export interface DebugConsole { + /** + * Append the given value to the debug console. + * + * @param value A string, falsy values will not be printed. + */ + append(value: string): void; + + /** + * Append the given value and a line feed character + * to the debug console. + * + * @param value A string, falsy values will be printed. + */ + appendLine(value: string): void; + } + + /** + * An event describing the changes to the set of {@link Breakpoint breakpoints}. + */ + export interface BreakpointsChangeEvent { + /** + * Added breakpoints. + */ + readonly added: readonly Breakpoint[]; + + /** + * Removed breakpoints. + */ + readonly removed: readonly Breakpoint[]; + + /** + * Changed breakpoints. + */ + readonly changed: readonly Breakpoint[]; + } + + /** + * The base class of all breakpoint types. + */ + export class Breakpoint { + /** + * The unique ID of the breakpoint. + */ + readonly id: string; + /** + * Is breakpoint enabled. + */ + readonly enabled: boolean; + /** + * An optional expression for conditional breakpoints. + */ + readonly condition?: string | undefined; + /** + * An optional expression that controls how many hits of the breakpoint are ignored. + */ + readonly hitCondition?: string | undefined; + /** + * An optional message that gets logged when this breakpoint is hit. Embedded expressions within {} are interpolated by the debug adapter. + */ + readonly logMessage?: string | undefined; + + /** + * Creates a new breakpoint + * + * @param enabled Is breakpoint enabled. + * @param condition Expression for conditional breakpoints + * @param hitCondition Expression that controls how many hits of the breakpoint are ignored + * @param logMessage Log message to display when breakpoint is hit + */ + protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); + } + + /** + * A breakpoint specified by a source location. + */ + export class SourceBreakpoint extends Breakpoint { + /** + * The source and line position of this breakpoint. + */ + readonly location: Location; + + /** + * Create a new breakpoint for a source location. + */ + constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); + } + + /** + * A breakpoint specified by a function name. + */ + export class FunctionBreakpoint extends Breakpoint { + /** + * The name of the function to which this breakpoint is attached. + */ + readonly functionName: string; + + /** + * Create a new function breakpoint. + */ + constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); + } + + /** + * Debug console mode used by debug session, see {@link DebugSessionOptions options}. + */ + export enum DebugConsoleMode { + /** + * Debug session should have a separate debug console. + */ + Separate = 0, + + /** + * Debug session should share debug console with its parent session. + * This value has no effect for sessions which do not have a parent session. + */ + MergeWithParent = 1 + } + + /** + * Options for {@link debug.startDebugging starting a debug session}. + */ + export interface DebugSessionOptions { + + /** + * When specified the newly created debug session is registered as a "child" session of this + * "parent" debug session. + */ + parentSession?: DebugSession; + + /** + * Controls whether lifecycle requests like 'restart' are sent to the newly created session or its parent session. + * By default (if the property is false or missing), lifecycle requests are sent to the new session. + * This property is ignored if the session has no parent session. + */ + lifecycleManagedByParent?: boolean; + + /** + * Controls whether this session should have a separate debug console or share it + * with the parent session. Has no effect for sessions which do not have a parent session. + * Defaults to Separate. + */ + consoleMode?: DebugConsoleMode; + + /** + * Controls whether this session should run without debugging, thus ignoring breakpoints. + * When this property is not specified, the value from the parent session (if there is one) is used. + */ + noDebug?: boolean; + + /** + * Controls if the debug session's parent session is shown in the CALL STACK view even if it has only a single child. + * By default, the debug session will never hide its parent. + * If compact is true, debug sessions with a single child are hidden in the CALL STACK view to make the tree more compact. + */ + compact?: boolean; + + /** + * When true, a save will not be triggered for open editors when starting a debug session, regardless of the value of the `debug.saveBeforeStart` setting. + */ + suppressSaveBeforeStart?: boolean; + + /** + * When true, the debug toolbar will not be shown for this session. + */ + suppressDebugToolbar?: boolean; + + /** + * When true, the window statusbar color will not be changed for this session. + */ + suppressDebugStatusbar?: boolean; + + /** + * When true, the debug viewlet will not be automatically revealed for this session. + */ + suppressDebugView?: boolean; + + /** + * Signals to the editor that the debug session was started from a test run + * request. This is used to link the lifecycle of the debug session and + * test run in UI actions. + */ + testRun?: TestRun; + } + + /** + * A DebugConfigurationProviderTriggerKind specifies when the `provideDebugConfigurations` method of a `DebugConfigurationProvider` is triggered. + * Currently there are two situations: to provide the initial debug configurations for a newly created launch.json or + * to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command). + * A trigger kind is used when registering a `DebugConfigurationProvider` with {@link debug.registerDebugConfigurationProvider}. + */ + export enum DebugConfigurationProviderTriggerKind { + /** + * `DebugConfigurationProvider.provideDebugConfigurations` is called to provide the initial debug configurations for a newly created launch.json. + */ + Initial = 1, + /** + * `DebugConfigurationProvider.provideDebugConfigurations` is called to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command). + */ + Dynamic = 2 + } + + /** + * Represents a thread in a debug session. + */ + export class DebugThread { + /** + * Debug session for thread. + */ + readonly session: DebugSession; + + /** + * ID of the associated thread in the debug protocol. + */ + readonly threadId: number; + + /** + * @hidden + */ + private constructor(session: DebugSession, threadId: number); + } + + /** + * Represents a stack frame in a debug session. + */ + export class DebugStackFrame { + /** + * Debug session for thread. + */ + readonly session: DebugSession; + + /** + * ID of the associated thread in the debug protocol. + */ + readonly threadId: number; + /** + * ID of the stack frame in the debug protocol. + */ + readonly frameId: number; + + /** + * @hidden + */ + private constructor(session: DebugSession, threadId: number, frameId: number); + } + + /** + * Namespace for debug functionality. + */ + export namespace debug { + + /** + * The currently active {@link DebugSession debug session} or `undefined`. The active debug session is the one + * represented by the debug action floating window or the one currently shown in the drop down menu of the debug action floating window. + * If no debug session is active, the value is `undefined`. + */ + export let activeDebugSession: DebugSession | undefined; + + /** + * The currently active {@link DebugConsole debug console}. + * If no debug session is active, output sent to the debug console is not shown. + */ + export let activeDebugConsole: DebugConsole; + + /** + * List of breakpoints. + */ + export let breakpoints: readonly Breakpoint[]; + + /** + * An {@link Event} which fires when the {@link debug.activeDebugSession active debug session} + * has changed. *Note* that the event also fires when the active debug session changes + * to `undefined`. + */ + export const onDidChangeActiveDebugSession: Event; + + /** + * An {@link Event} which fires when a new {@link DebugSession debug session} has been started. + */ + export const onDidStartDebugSession: Event; + + /** + * An {@link Event} which fires when a custom DAP event is received from the {@link DebugSession debug session}. + */ + export const onDidReceiveDebugSessionCustomEvent: Event; + + /** + * An {@link Event} which fires when a {@link DebugSession debug session} has terminated. + */ + export const onDidTerminateDebugSession: Event; + + /** + * An {@link Event} that is emitted when the set of breakpoints is added, removed, or changed. + */ + export const onDidChangeBreakpoints: Event; + + /** + * The currently focused thread or stack frame, or `undefined` if no + * thread or stack is focused. A thread can be focused any time there is + * an active debug session, while a stack frame can only be focused when + * a session is paused and the call stack has been retrieved. + */ + export const activeStackItem: DebugThread | DebugStackFrame | undefined; + + /** + * An event which fires when the {@link debug.activeStackItem} has changed. + */ + export const onDidChangeActiveStackItem: Event; + + /** + * Register a {@link DebugConfigurationProvider debug configuration provider} for a specific debug type. + * The optional {@link DebugConfigurationProviderTriggerKind triggerKind} can be used to specify when the `provideDebugConfigurations` method of the provider is triggered. + * Currently two trigger kinds are possible: with the value `Initial` (or if no trigger kind argument is given) the `provideDebugConfigurations` method is used to provide the initial debug configurations to be copied into a newly created launch.json. + * With the trigger kind `Dynamic` the `provideDebugConfigurations` method is used to dynamically determine debug configurations to be presented to the user (in addition to the static configurations from the launch.json). + * Please note that the `triggerKind` argument only applies to the `provideDebugConfigurations` method: so the `resolveDebugConfiguration` methods are not affected at all. + * Registering a single provider with resolve methods for different trigger kinds, results in the same resolve methods called multiple times. + * More than one provider can be registered for the same type. + * + * @param debugType The debug type for which the provider is registered. + * @param provider The {@link DebugConfigurationProvider debug configuration provider} to register. + * @param triggerKind The {@link DebugConfigurationProviderTriggerKind trigger} for which the 'provideDebugConfiguration' method of the provider is registered. If `triggerKind` is missing, the value `DebugConfigurationProviderTriggerKind.Initial` is assumed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, triggerKind?: DebugConfigurationProviderTriggerKind): Disposable; + + /** + * Register a {@link DebugAdapterDescriptorFactory debug adapter descriptor factory} for a specific debug type. + * An extension is only allowed to register a DebugAdapterDescriptorFactory for the debug type(s) defined by the extension. Otherwise an error is thrown. + * Registering more than one DebugAdapterDescriptorFactory for a debug type results in an error. + * + * @param debugType The debug type for which the factory is registered. + * @param factory The {@link DebugAdapterDescriptorFactory debug adapter descriptor factory} to register. + * @returns A {@link Disposable} that unregisters this factory when being disposed. + */ + export function registerDebugAdapterDescriptorFactory(debugType: string, factory: DebugAdapterDescriptorFactory): Disposable; + + /** + * Register a debug adapter tracker factory for the given debug type. + * + * @param debugType The debug type for which the factory is registered or '*' for matching all debug types. + * @param factory The {@link DebugAdapterTrackerFactory debug adapter tracker factory} to register. + * @returns A {@link Disposable} that unregisters this factory when being disposed. + */ + export function registerDebugAdapterTrackerFactory(debugType: string, factory: DebugAdapterTrackerFactory): Disposable; + + /** + * Start debugging by using either a named launch or named compound configuration, + * or by directly passing a {@link DebugConfiguration}. + * The named configurations are looked up in '.vscode/launch.json' found in the given folder. + * Before debugging starts, all unsaved files are saved and the launch configurations are brought up-to-date. + * Folder specific variables used in the configuration (e.g. '${workspaceFolder}') are resolved against the given folder. + * @param folder The {@link WorkspaceFolder workspace folder} for looking up named configurations and resolving variables or `undefined` for a non-folder setup. + * @param nameOrConfiguration Either the name of a debug or compound configuration or a {@link DebugConfiguration} object. + * @param parentSessionOrOptions Debug session options. When passed a parent {@link DebugSession debug session}, assumes options with just this parent session. + * @returns A thenable that resolves when debugging could be successfully started. + */ + export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSessionOrOptions?: DebugSession | DebugSessionOptions): Thenable; + + /** + * Stop the given debug session or stop all debug sessions if session is omitted. + * + * @param session The {@link DebugSession debug session} to stop; if omitted all sessions are stopped. + * @returns A thenable that resolves when the session(s) have been stopped. + */ + export function stopDebugging(session?: DebugSession): Thenable; + + /** + * Add breakpoints. + * @param breakpoints The breakpoints to add. + */ + export function addBreakpoints(breakpoints: readonly Breakpoint[]): void; + + /** + * Remove breakpoints. + * @param breakpoints The breakpoints to remove. + */ + export function removeBreakpoints(breakpoints: readonly Breakpoint[]): void; + + /** + * Converts a "Source" descriptor object received via the Debug Adapter Protocol into a Uri that can be used to load its contents. + * If the source descriptor is based on a path, a file Uri is returned. + * If the source descriptor uses a reference number, a specific debug Uri (scheme 'debug') is constructed that requires a corresponding ContentProvider and a running debug session + * + * If the "Source" descriptor has insufficient information for creating the Uri, an error is thrown. + * + * @param source An object conforming to the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. + * @param session An optional debug session that will be used when the source descriptor uses a reference number to load the contents from an active debug session. + * @returns A uri that can be used to load the contents of the source. + */ + export function asDebugSourceUri(source: DebugProtocolSource, session?: DebugSession): Uri; + } + + /** + * Namespace for dealing with installed extensions. Extensions are represented + * by an {@link Extension}-interface which enables reflection on them. + * + * Extension writers can provide APIs to other extensions by returning their API public + * surface from the `activate`-call. + * + * ```javascript + * export function activate(context: vscode.ExtensionContext) { + * let api = { + * sum(a, b) { + * return a + b; + * }, + * mul(a, b) { + * return a * b; + * } + * }; + * // 'export' public api-surface + * return api; + * } + * ``` + * When depending on the API of another extension add an `extensionDependencies`-entry + * to `package.json`, and use the {@link extensions.getExtension getExtension}-function + * and the {@link Extension.exports exports}-property, like below: + * + * ```javascript + * let mathExt = extensions.getExtension('genius.math'); + * let importedApi = mathExt.exports; + * + * console.log(importedApi.mul(42, 1)); + * ``` + */ + export namespace extensions { + + /** + * Get an extension by its full identifier in the form of: `publisher.name`. + * + * @param extensionId An extension identifier. + * @returns An extension or `undefined`. + */ + export function getExtension(extensionId: string): Extension | undefined; + + /** + * All extensions currently known to the system. + */ + export const all: readonly Extension[]; + + /** + * An event which fires when `extensions.all` changes. This can happen when extensions are + * installed, uninstalled, enabled or disabled. + */ + export const onDidChange: Event; + } + + /** + * Collapsible state of a {@link CommentThread comment thread} + */ + export enum CommentThreadCollapsibleState { + /** + * Determines an item is collapsed + */ + Collapsed = 0, + + /** + * Determines an item is expanded + */ + Expanded = 1 + } + + /** + * Comment mode of a {@link Comment} + */ + export enum CommentMode { + /** + * Displays the comment editor + */ + Editing = 0, + + /** + * Displays the preview of the comment + */ + Preview = 1 + } + + /** + * The state of a comment thread. + */ + export enum CommentThreadState { + /** + * Unresolved thread state + */ + Unresolved = 0, + /** + * Resolved thread state + */ + Resolved = 1 + } + + /** + * A collection of {@link Comment comments} representing a conversation at a particular range in a document. + */ + export interface CommentThread { + /** + * The uri of the document the thread has been created on. + */ + readonly uri: Uri; + + /** + * The range the comment thread is located within the document. The thread icon will be shown + * at the last line of the range. When set to undefined, the comment will be associated with the + * file, and not a specific range. + */ + range: Range | undefined; + + /** + * The ordered comments of the thread. + */ + comments: readonly Comment[]; + + /** + * Whether the thread should be collapsed or expanded when opening the document. + * Defaults to Collapsed. + */ + collapsibleState: CommentThreadCollapsibleState; + + /** + * Whether the thread supports reply. + * Defaults to true. + */ + canReply: boolean; + + /** + * Context value of the comment thread. This can be used to contribute thread specific actions. + * For example, a comment thread is given a context value as `editable`. When contributing actions to `comments/commentThread/title` + * using `menus` extension point, you can specify context value for key `commentThread` in `when` expression like `commentThread == editable`. + * ```json + * "contributes": { + * "menus": { + * "comments/commentThread/title": [ + * { + * "command": "extension.deleteCommentThread", + * "when": "commentThread == editable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.deleteCommentThread` only for comment threads with `contextValue` is `editable`. + */ + contextValue?: string; + + /** + * The optional human-readable label describing the {@link CommentThread Comment Thread} + */ + label?: string; + + /** + * The optional state of a comment thread, which may affect how the comment is displayed. + */ + state?: CommentThreadState; + + /** + * Dispose this comment thread. + * + * Once disposed, this comment thread will be removed from visible editors and Comment Panel when appropriate. + */ + dispose(): void; + } + + /** + * Author information of a {@link Comment} + */ + export interface CommentAuthorInformation { + /** + * The display name of the author of the comment + */ + name: string; + + /** + * The optional icon path for the author + */ + iconPath?: Uri; + } + + /** + * Reactions of a {@link Comment} + */ + export interface CommentReaction { + /** + * The human-readable label for the reaction + */ + readonly label: string; + + /** + * Icon for the reaction shown in UI. + */ + readonly iconPath: string | Uri; + + /** + * The number of users who have reacted to this reaction + */ + readonly count: number; + + /** + * Whether the {@link CommentAuthorInformation author} of the comment has reacted to this reaction + */ + readonly authorHasReacted: boolean; + } + + /** + * A comment is displayed within the editor or the Comments Panel, depending on how it is provided. + */ + export interface Comment { + /** + * The human-readable comment body + */ + body: string | MarkdownString; + + /** + * {@link CommentMode Comment mode} of the comment + */ + mode: CommentMode; + + /** + * The {@link CommentAuthorInformation author information} of the comment + */ + author: CommentAuthorInformation; + + /** + * Context value of the comment. This can be used to contribute comment specific actions. + * For example, a comment is given a context value as `editable`. When contributing actions to `comments/comment/title` + * using `menus` extension point, you can specify context value for key `comment` in `when` expression like `comment == editable`. + * ```json + * "contributes": { + * "menus": { + * "comments/comment/title": [ + * { + * "command": "extension.deleteComment", + * "when": "comment == editable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.deleteComment` only for comments with `contextValue` is `editable`. + */ + contextValue?: string; + + /** + * Optional reactions of the {@link Comment} + */ + reactions?: CommentReaction[]; + + /** + * Optional label describing the {@link Comment} + * Label will be rendered next to authorName if exists. + */ + label?: string; + + /** + * Optional timestamp that will be displayed in comments. + * The date will be formatted according to the user's locale and settings. + */ + timestamp?: Date; + } + + /** + * Command argument for actions registered in `comments/commentThread/context`. + */ + export interface CommentReply { + /** + * The active {@link CommentThread comment thread} + */ + thread: CommentThread; + + /** + * The value in the comment editor + */ + text: string; + } + + /** + * The ranges a CommentingRangeProvider enables commenting on. + */ + export interface CommentingRanges { + /** + * Enables comments to be added to a file without a specific range. + */ + enableFileComments: boolean; + + /** + * The ranges which allow new comment threads creation. + */ + ranges?: Range[]; + } + + /** + * Commenting range provider for a {@link CommentController comment controller}. + */ + export interface CommentingRangeProvider { + /** + * Provide a list of ranges which allow new comment threads creation or null for a given document + */ + provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; + } + + /** + * Represents a {@link CommentController comment controller}'s {@link CommentController.options options}. + */ + export interface CommentOptions { + /** + * An optional string to show on the comment input box when it's collapsed. + */ + prompt?: string; + + /** + * An optional string to show as placeholder in the comment input box when it's focused. + */ + placeHolder?: string; + } + + /** + * A comment controller is able to provide {@link CommentThread comments} support to the editor and + * provide users various ways to interact with comments. + */ + export interface CommentController { + /** + * The id of this comment controller. + */ + readonly id: string; + + /** + * The human-readable label of this comment controller. + */ + readonly label: string; + + /** + * Comment controller options + */ + options?: CommentOptions; + + /** + * Optional commenting range provider. Provide a list {@link Range ranges} which support commenting to any given resource uri. + * + * If not provided, users cannot leave any comments. + */ + commentingRangeProvider?: CommentingRangeProvider; + + /** + * Create a {@link CommentThread comment thread}. The comment thread will be displayed in visible text editors (if the resource matches) + * and Comments Panel once created. + * + * @param uri The uri of the document the thread has been created on. + * @param range The range the comment thread is located within the document. + * @param comments The ordered comments of the thread. + */ + createCommentThread(uri: Uri, range: Range, comments: readonly Comment[]): CommentThread; + + /** + * Optional reaction handler for creating and deleting reactions on a {@link Comment}. + */ + reactionHandler?: (comment: Comment, reaction: CommentReaction) => Thenable; + + /** + * Dispose this comment controller. + * + * Once disposed, all {@link CommentThread comment threads} created by this comment controller will also be removed from the editor + * and Comments Panel. + */ + dispose(): void; + } + + namespace comments { + /** + * Creates a new {@link CommentController comment controller} instance. + * + * @param id An `id` for the comment controller. + * @param label A human-readable string for the comment controller. + * @returns An instance of {@link CommentController comment controller}. + */ + export function createCommentController(id: string, label: string): CommentController; + } + + /** + * Represents a session of a currently logged in user. + */ + export interface AuthenticationSession { + /** + * The identifier of the authentication session. + */ + readonly id: string; + + /** + * The access token. + */ + readonly accessToken: string; + + /** + * The account associated with the session. + */ + readonly account: AuthenticationSessionAccountInformation; + + /** + * The permissions granted by the session's access token. Available scopes + * are defined by the {@link AuthenticationProvider}. + */ + readonly scopes: readonly string[]; + } + + /** + * The information of an account associated with an {@link AuthenticationSession}. + */ + export interface AuthenticationSessionAccountInformation { + /** + * The unique identifier of the account. + */ + readonly id: string; + + /** + * The human-readable name of the account. + */ + readonly label: string; + } + + /** + * Optional options to be used when calling {@link authentication.getSession} with the flag `forceNewSession`. + */ + export interface AuthenticationForceNewSessionOptions { + /** + * An optional message that will be displayed to the user when we ask to re-authenticate. Providing additional context + * as to why you are asking a user to re-authenticate can help increase the odds that they will accept. + */ + detail?: string; + } + + /** + * Options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. + */ + export interface AuthenticationGetSessionOptions { + /** + * Whether the existing session preference should be cleared. + * + * For authentication providers that support being signed into multiple accounts at once, the user will be + * prompted to select an account to use when {@link authentication.getSession getSession} is called. This preference + * is remembered until {@link authentication.getSession getSession} is called with this flag. + * + * Note: + * The preference is extension specific. So if one extension calls {@link authentication.getSession getSession}, it will not + * affect the session preference for another extension calling {@link authentication.getSession getSession}. Additionally, + * the preference is set for the current workspace and also globally. This means that new workspaces will use the "global" + * value at first and then when this flag is provided, a new value can be set for that workspace. This also means + * that pre-existing workspaces will not lose their preference if a new workspace sets this flag. + * + * Defaults to false. + */ + clearSessionPreference?: boolean; + + /** + * Whether login should be performed if there is no matching session. + * + * If true, a modal dialog will be shown asking the user to sign in. If false, a numbered badge will be shown + * on the accounts activity bar icon. An entry for the extension will be added under the menu to sign in. This + * allows quietly prompting the user to sign in. + * + * If there is a matching session but the extension has not been granted access to it, setting this to true + * will also result in an immediate modal dialog, and false will add a numbered badge to the accounts icon. + * + * Defaults to false. + * + * Note: you cannot use this option with {@link AuthenticationGetSessionOptions.silent silent}. + */ + createIfNone?: boolean; + + /** + * Whether we should attempt to reauthenticate even if there is already a session available. + * + * If true, a modal dialog will be shown asking the user to sign in again. This is mostly used for scenarios + * where the token needs to be re minted because it has lost some authorization. + * + * If there are no existing sessions and forceNewSession is true, it will behave identically to + * {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. + * + * This defaults to false. + */ + forceNewSession?: boolean | AuthenticationForceNewSessionOptions; + + /** + * Whether we should show the indication to sign in in the Accounts menu. + * + * If false, the user will be shown a badge on the Accounts menu with an option to sign in for the extension. + * If true, no indication will be shown. + * + * Defaults to false. + * + * Note: you cannot use this option with any other options that prompt the user like {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. + */ + silent?: boolean; + + /** + * The account that you would like to get a session for. This is passed down to the Authentication Provider to be used for creating the correct session. + */ + account?: AuthenticationSessionAccountInformation; + } + + /** + * Basic information about an {@link AuthenticationProvider} + */ + export interface AuthenticationProviderInformation { + /** + * The unique identifier of the authentication provider. + */ + readonly id: string; + + /** + * The human-readable name of the authentication provider. + */ + readonly label: string; + } + + /** + * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. + */ + export interface AuthenticationSessionsChangeEvent { + /** + * The {@link AuthenticationProvider} that has had its sessions change. + */ + readonly provider: AuthenticationProviderInformation; + } + + /** + * Options for creating an {@link AuthenticationProvider}. + */ + export interface AuthenticationProviderOptions { + /** + * Whether it is possible to be signed into multiple accounts at once with this provider. + * If not specified, will default to false. + */ + readonly supportsMultipleAccounts?: boolean; + } + + /** + * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. + */ + export interface AuthenticationProviderAuthenticationSessionsChangeEvent { + /** + * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been added. + */ + readonly added: readonly AuthenticationSession[] | undefined; + + /** + * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been removed. + */ + readonly removed: readonly AuthenticationSession[] | undefined; + + /** + * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been changed. + * A session changes when its data excluding the id are updated. An example of this is a session refresh that results in a new + * access token being set for the session. + */ + readonly changed: readonly AuthenticationSession[] | undefined; + } + + /** + * The options passed in to the {@link AuthenticationProvider.getSessions} and + * {@link AuthenticationProvider.createSession} call. + */ + export interface AuthenticationProviderSessionOptions { + /** + * The account that is being asked about. If this is passed in, the provider should + * attempt to return the sessions that are only related to this account. + */ + account?: AuthenticationSessionAccountInformation; + } + + /** + * A provider for performing authentication to a service. + */ + export interface AuthenticationProvider { + /** + * An {@link Event} which fires when the array of sessions has changed, or data + * within a session has changed. + */ + readonly onDidChangeSessions: Event; + + /** + * Get a list of sessions. + * @param scopes An optional list of scopes. If provided, the sessions returned should match + * these permissions, otherwise all sessions should be returned. + * @param options Additional options for getting sessions. + * @returns A promise that resolves to an array of authentication sessions. + */ + getSessions(scopes: readonly string[] | undefined, options: AuthenticationProviderSessionOptions): Thenable; + + /** + * Prompts a user to login. + * + * If login is successful, the onDidChangeSessions event should be fired. + * + * If login fails, a rejected promise should be returned. + * + * If the provider has specified that it does not support multiple accounts, + * then this should never be called if there is already an existing session matching these + * scopes. + * @param scopes A list of scopes, permissions, that the new session should be created with. + * @param options Additional options for creating a session. + * @returns A promise that resolves to an authentication session. + */ + createSession(scopes: readonly string[], options: AuthenticationProviderSessionOptions): Thenable; + + /** + * Removes the session corresponding to session id. + * + * If the removal is successful, the onDidChangeSessions event should be fired. + * + * If a session cannot be removed, the provider should reject with an error message. + * @param sessionId The id of the session to remove. + */ + removeSession(sessionId: string): Thenable; + } + + + /** + * Namespace for authentication. + */ + export namespace authentication { + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @param providerId The id of the provider to use + * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider + * @param options The {@link AuthenticationGetSessionOptions} to use + * @returns A thenable that resolves to an authentication session + */ + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** */createIfNone: true }): Thenable; + + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @param providerId The id of the provider to use + * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider + * @param options The {@link AuthenticationGetSessionOptions} to use + * @returns A thenable that resolves to an authentication session + */ + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** literal-type defines return type */forceNewSession: true | AuthenticationForceNewSessionOptions }): Thenable; + + /** + * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not + * registered, or if the user does not consent to sharing authentication information with + * the extension. If there are multiple sessions with the same scopes, the user will be shown a + * quickpick to select which account they would like to use. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * @param providerId The id of the provider to use + * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider + * @param options The {@link AuthenticationGetSessionOptions} to use + * @returns A thenable that resolves to an authentication session if available, or undefined if there are no sessions + */ + export function getSession(providerId: string, scopes: readonly string[], options?: AuthenticationGetSessionOptions): Thenable; + + /** + * Get all accounts that the user is logged in to for the specified provider. + * Use this paired with {@link getSession} in order to get an authentication session for a specific account. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * + * Note: Getting accounts does not imply that your extension has access to that account or its authentication sessions. You can verify access to the account by calling {@link getSession}. + * + * @param providerId The id of the provider to use + * @returns A thenable that resolves to a readonly array of authentication accounts. + */ + export function getAccounts(providerId: string): Thenable; + + /** + * An {@link Event} which fires when the authentication sessions of an authentication provider have + * been added, removed, or changed. + */ + export const onDidChangeSessions: Event; + + /** + * Register an authentication provider. + * + * There can only be one provider per id and an error is being thrown when an id + * has already been used by another provider. Ids are case-sensitive. + * + * @param id The unique identifier of the provider. + * @param label The human-readable name of the provider. + * @param provider The authentication provider provider. + * @param options Additional options for the provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerAuthenticationProvider(id: string, label: string, provider: AuthenticationProvider, options?: AuthenticationProviderOptions): Disposable; + } + + /** + * Namespace for localization-related functionality in the extension API. To use this properly, + * you must have `l10n` defined in your extension manifest and have bundle.l10n..json files. + * For more information on how to generate bundle.l10n..json files, check out the + * [vscode-l10n repo](https://github.com/microsoft/vscode-l10n). + * + * Note: Built-in extensions (for example, Git, TypeScript Language Features, GitHub Authentication) + * are excluded from the `l10n` property requirement. In other words, they do not need to specify + * a `l10n` in the extension manifest because their translated strings come from Language Packs. + */ + export namespace l10n { + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected {@link args} values for any templated values). + * + * @param message - The message to localize. Supports index templating where strings like `{0}` and `{1}` are + * replaced by the item at that index in the {@link args} array. + * @param args - The arguments to be used in the localized string. The index of the argument is used to + * match the template placeholder in the localized string. + * @returns localized string with injected arguments. + * + * @example + * l10n.t('Hello {0}!', 'World'); + */ + export function t(message: string, ...args: Array): string; + + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected {@link args} values for any templated values). + * + * @param message The message to localize. Supports named templating where strings like `{foo}` and `{bar}` are + * replaced by the value in the Record for that key (foo, bar, etc). + * @param args The arguments to be used in the localized string. The name of the key in the record is used to + * match the template placeholder in the localized string. + * @returns localized string with injected arguments. + * + * @example + * l10n.t('Hello {name}', { name: 'Erich' }); + */ + export function t(message: string, args: Record): string; + /** + * Marks a string for localization. If a localized bundle is available for the language specified by + * {@link env.language} and the bundle has a localized value for this message, then that localized + * value will be returned (with injected args values for any templated values). + * + * @param options The options to use when localizing the message. + * @returns localized string with injected arguments. + */ + export function t(options: { + /** + * The message to localize. If {@link options.args args} is an array, this message supports index templating where strings like + * `{0}` and `{1}` are replaced by the item at that index in the {@link options.args args} array. If `args` is a `Record`, + * this supports named templating where strings like `{foo}` and `{bar}` are replaced by the value in + * the Record for that key (foo, bar, etc). + */ + message: string; + /** + * The arguments to be used in the localized string. As an array, the index of the argument is used to + * match the template placeholder in the localized string. As a Record, the key is used to match the template + * placeholder in the localized string. + */ + args?: Array | Record; + /** + * A comment to help translators understand the context of the message. + */ + comment: string | string[]; + }): string; + /** + * The bundle of localized strings that have been loaded for the extension. + * It's undefined if no bundle has been loaded. The bundle is typically not loaded if + * there was no bundle found or when we are running with the default language. + */ + export const bundle: { [key: string]: string } | undefined; + /** + * The URI of the localization bundle that has been loaded for the extension. + * It's undefined if no bundle has been loaded. The bundle is typically not loaded if + * there was no bundle found or when we are running with the default language. + */ + export const uri: Uri | undefined; + } + + /** + * Namespace for testing functionality. Tests are published by registering + * {@link TestController} instances, then adding {@link TestItem TestItems}. + * Controllers may also describe how to run tests by creating one or more + * {@link TestRunProfile} instances. + */ + export namespace tests { + /** + * Creates a new test controller. + * + * @param id Identifier for the controller, must be globally unique. + * @param label A human-readable label for the controller. + * @returns An instance of the {@link TestController}. + */ + export function createTestController(id: string, label: string): TestController; + } + + /** + * The kind of executions that {@link TestRunProfile TestRunProfiles} control. + */ + export enum TestRunProfileKind { + /** + * The `Run` test profile kind. + */ + Run = 1, + /** + * The `Debug` test profile kind. + */ + Debug = 2, + /** + * The `Coverage` test profile kind. + */ + Coverage = 3, + } + + /** + * Tags can be associated with {@link TestItem TestItems} and + * {@link TestRunProfile TestRunProfiles}. A profile with a tag can only + * execute tests that include that tag in their {@link TestItem.tags} array. + */ + export class TestTag { + /** + * ID of the test tag. `TestTag` instances with the same ID are considered + * to be identical. + */ + readonly id: string; + + /** + * Creates a new TestTag instance. + * @param id ID of the test tag. + */ + constructor(id: string); + } + + /** + * A TestRunProfile describes one way to execute tests in a {@link TestController}. + */ + export interface TestRunProfile { + /** + * Label shown to the user in the UI. + * + * Note that the label has some significance if the user requests that + * tests be re-run in a certain way. For example, if tests were run + * normally and the user requests to re-run them in debug mode, the editor + * will attempt use a configuration with the same label of the `Debug` + * kind. If there is no such configuration, the default will be used. + */ + label: string; + + /** + * Configures what kind of execution this profile controls. If there + * are no profiles for a kind, it will not be available in the UI. + */ + readonly kind: TestRunProfileKind; + + /** + * Controls whether this profile is the default action that will + * be taken when its kind is actioned. For example, if the user clicks + * the generic "run all" button, then the default profile for + * {@link TestRunProfileKind.Run} will be executed, although the + * user can configure this. + * + * Changes the user makes in their default profiles will be reflected + * in this property after a {@link onDidChangeDefault} event. + */ + isDefault: boolean; + + /** + * Fired when a user has changed whether this is a default profile. The + * event contains the new value of {@link isDefault} + */ + onDidChangeDefault: Event; + + /** + * Whether this profile supports continuous running of requests. If so, + * then {@link TestRunRequest.continuous} may be set to `true`. Defaults + * to false. + */ + supportsContinuousRun: boolean; + + /** + * Associated tag for the profile. If this is set, only {@link TestItem} + * instances with the same tag will be eligible to execute in this profile. + */ + tag: TestTag | undefined; + + /** + * If this method is present, a configuration gear will be present in the + * UI, and this method will be invoked when it's clicked. When called, + * you can take other editor actions, such as showing a quick pick or + * opening a configuration file. + */ + configureHandler: (() => void) | undefined; + + /** + * Handler called to start a test run. When invoked, the function should call + * {@link TestController.createTestRun} at least once, and all test runs + * associated with the request should be created before the function returns + * or the returned promise is resolved. + * + * If {@link supportsContinuousRun} is set, then {@link TestRunRequest.continuous} + * may be `true`. In this case, the profile should observe changes to + * source code and create new test runs by calling {@link TestController.createTestRun}, + * until the cancellation is requested on the `token`. + * + * @param request Request information for the test run. + * @param cancellationToken Token that signals the used asked to abort the + * test run. If cancellation is requested on this token, all {@link TestRun} + * instances associated with the request will be + * automatically cancelled as well. + */ + runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void; + + /** + * An extension-provided function that provides detailed statement and + * function-level coverage for a file. The editor will call this when more + * detail is needed for a file, such as when it's opened in an editor or + * expanded in the **Test Coverage** view. + * + * The {@link FileCoverage} object passed to this function is the same instance + * emitted on {@link TestRun.addCoverage} calls associated with this profile. + */ + loadDetailedCoverage?: (testRun: TestRun, fileCoverage: FileCoverage, token: CancellationToken) => Thenable; + + /** + * An extension-provided function that provides detailed statement and + * function-level coverage for a single test in a file. This is the per-test + * sibling of {@link TestRunProfile.loadDetailedCoverage}, called only if + * a test item is provided in {@link FileCoverage.includesTests} and only + * for files where such data is reported. + * + * Often {@link TestRunProfile.loadDetailedCoverage} will be called first + * when a user opens a file, and then this method will be called if they + * drill down into specific per-test coverage information. This method + * should then return coverage data only for statements and declarations + * executed by the specific test during the run. + * + * The {@link FileCoverage} object passed to this function is the same + * instance emitted on {@link TestRun.addCoverage} calls associated with this profile. + * + * @param testRun The test run that generated the coverage data. + * @param fileCoverage The file coverage object to load detailed coverage for. + * @param fromTestItem The test item to request coverage information for. + * @param token A cancellation token that indicates the operation should be cancelled. + */ + loadDetailedCoverageForTest?: (testRun: TestRun, fileCoverage: FileCoverage, fromTestItem: TestItem, token: CancellationToken) => Thenable; + + /** + * Deletes the run profile. + */ + dispose(): void; + } + + /** + * Entry point to discover and execute tests. It contains {@link TestController.items} which + * are used to populate the editor UI, and is associated with + * {@link TestController.createRunProfile run profiles} to allow + * for tests to be executed. + */ + export interface TestController { + /** + * The id of the controller passed in {@link tests.createTestController}. + * This must be globally unique. + */ + readonly id: string; + + /** + * Human-readable label for the test controller. + */ + label: string; + + /** + * A collection of "top-level" {@link TestItem} instances, which can in + * turn have their own {@link TestItem.children children} to form the + * "test tree." + * + * The extension controls when to add tests. For example, extensions should + * add tests for a file when {@link workspace.onDidOpenTextDocument} + * fires in order for decorations for tests within a file to be visible. + * + * However, the editor may sometimes explicitly request children using the + * {@link resolveHandler} See the documentation on that method for more details. + */ + readonly items: TestItemCollection; + + /** + * Creates a profile used for running tests. Extensions must create + * at least one profile in order for tests to be run. + * @param label A human-readable label for this profile. + * @param kind Configures what kind of execution this profile manages. + * @param runHandler Function called to start a test run. + * @param isDefault Whether this is the default action for its kind. + * @param tag Profile test tag. + * @param supportsContinuousRun Whether the profile supports continuous running. + * @returns An instance of a {@link TestRunProfile}, which is automatically + * associated with this controller. + */ + createRunProfile(label: string, kind: TestRunProfileKind, runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void, isDefault?: boolean, tag?: TestTag, supportsContinuousRun?: boolean): TestRunProfile; + + /** + * A function provided by the extension that the editor may call to request + * children of a test item, if the {@link TestItem.canResolveChildren} is + * `true`. When called, the item should discover children and call + * {@link TestController.createTestItem} as children are discovered. + * + * Generally the extension manages the lifecycle of test items, but under + * certain conditions the editor may request the children of a specific + * item to be loaded. For example, if the user requests to re-run tests + * after reloading the editor, the editor may need to call this method + * to resolve the previously-run tests. + * + * The item in the explorer will automatically be marked as "busy" until + * the function returns or the returned thenable resolves. + * + * @param item An unresolved test item for which children are being + * requested, or `undefined` to resolve the controller's initial {@link TestController.items items}. + */ + resolveHandler?: (item: TestItem | undefined) => Thenable | void; + + /** + * If this method is present, a refresh button will be present in the + * UI, and this method will be invoked when it's clicked. When called, + * the extension should scan the workspace for any new, changed, or + * removed tests. + * + * It's recommended that extensions try to update tests in realtime, using + * a {@link FileSystemWatcher} for example, and use this method as a fallback. + * + * @returns A thenable that resolves when tests have been refreshed. + */ + refreshHandler: ((token: CancellationToken) => Thenable | void) | undefined; + + /** + * Creates a {@link TestRun}. This should be called by the + * {@link TestRunProfile} when a request is made to execute tests, and may + * also be called if a test run is detected externally. Once created, tests + * that are included in the request will be moved into the queued state. + * + * All runs created using the same `request` instance will be grouped + * together. This is useful if, for example, a single suite of tests is + * run on multiple platforms. + * + * @param request Test run request. Only tests inside the `include` may be + * modified, and tests in its `exclude` are ignored. + * @param name The human-readable name of the run. This can be used to + * disambiguate multiple sets of results in a test run. It is useful if + * tests are run across multiple platforms, for example. + * @param persist Whether the results created by the run should be + * persisted in the editor. This may be false if the results are coming from + * a file already saved externally, such as a coverage information file. + * @returns An instance of the {@link TestRun}. It will be considered "running" + * from the moment this method is invoked until {@link TestRun.end} is called. + */ + createTestRun(request: TestRunRequest, name?: string, persist?: boolean): TestRun; + + /** + * Creates a new managed {@link TestItem} instance. It can be added into + * the {@link TestItem.children} of an existing item, or into the + * {@link TestController.items}. + * + * @param id Identifier for the TestItem. The test item's ID must be unique + * in the {@link TestItemCollection} it's added to. + * @param label Human-readable label of the test item. + * @param uri URI this TestItem is associated with. May be a file or directory. + */ + createTestItem(id: string, label: string, uri?: Uri): TestItem; + + /** + * Marks an item's results as being outdated. This is commonly called when + * code or configuration changes and previous results should no longer + * be considered relevant. The same logic used to mark results as outdated + * may be used to drive {@link TestRunRequest.continuous continuous test runs}. + * + * If an item is passed to this method, test results for the item and all of + * its children will be marked as outdated. If no item is passed, then all + * test owned by the TestController will be marked as outdated. + * + * Any test runs started before the moment this method is called, including + * runs which may still be ongoing, will be marked as outdated and deprioritized + * in the editor's UI. + * + * @param items Item to mark as outdated. If undefined, all the controller's items are marked outdated. + */ + invalidateTestResults(items?: TestItem | readonly TestItem[]): void; + + /** + * Unregisters the test controller, disposing of its associated tests + * and unpersisted results. + */ + dispose(): void; + } + + /** + * A TestRunRequest is a precursor to a {@link TestRun}, which in turn is + * created by passing a request to {@link TestController.createTestRun}. The + * TestRunRequest contains information about which tests should be run, which + * should not be run, and how they are run (via the {@link TestRunRequest.profile profile}). + * + * In general, TestRunRequests are created by the editor and pass to + * {@link TestRunProfile.runHandler}, however you can also create test + * requests and runs outside of the `runHandler`. + */ + export class TestRunRequest { + /** + * A filter for specific tests to run. If given, the extension should run + * all of the included tests and all their children, excluding any tests + * that appear in {@link TestRunRequest.exclude}. If this property is + * undefined, then the extension should simply run all tests. + * + * The process of running tests should resolve the children of any test + * items who have not yet been resolved. + */ + readonly include: readonly TestItem[] | undefined; + + /** + * An array of tests the user has marked as excluded from the test included + * in this run; exclusions should apply after inclusions. + * + * May be omitted if no exclusions were requested. Test controllers should + * not run excluded tests or any children of excluded tests. + */ + readonly exclude: readonly TestItem[] | undefined; + + /** + * The profile used for this request. This will always be defined + * for requests issued from the editor UI, though extensions may + * programmatically create requests not associated with any profile. + */ + readonly profile: TestRunProfile | undefined; + + /** + * Whether the profile should run continuously as source code changes. Only + * relevant for profiles that set {@link TestRunProfile.supportsContinuousRun}. + */ + readonly continuous?: boolean; + + /** + * Controls how test Test Results view is focused. If true, the editor + * will keep the maintain the user's focus. If false, the editor will + * prefer to move focus into the Test Results view, although + * this may be configured by users. + */ + readonly preserveFocus: boolean; + + /** + * @param include Array of specific tests to run, or undefined to run all tests + * @param exclude An array of tests to exclude from the run. + * @param profile The run profile used for this request. + * @param continuous Whether to run tests continuously as source changes. + * @param preserveFocus Whether to preserve the user's focus when the run is started + */ + constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile, continuous?: boolean, preserveFocus?: boolean); + } + + /** + * A TestRun represents an in-progress or completed test run and + * provides methods to report the state of individual tests in the run. + */ + export interface TestRun { + /** + * The human-readable name of the run. This can be used to + * disambiguate multiple sets of results in a test run. It is useful if + * tests are run across multiple platforms, for example. + */ + readonly name: string | undefined; + + /** + * A cancellation token which will be triggered when the test run is + * canceled from the UI. + */ + readonly token: CancellationToken; + + /** + * Whether the test run will be persisted across reloads by the editor. + */ + readonly isPersisted: boolean; + + /** + * Indicates a test is queued for later execution. + * @param test Test item to update. + */ + enqueued(test: TestItem): void; + + /** + * Indicates a test has started running. + * @param test Test item to update. + */ + started(test: TestItem): void; + + /** + * Indicates a test has been skipped. + * @param test Test item to update. + */ + skipped(test: TestItem): void; + + /** + * Indicates a test has failed. You should pass one or more + * {@link TestMessage TestMessages} to describe the failure. + * @param test Test item to update. + * @param message Messages associated with the test failure. + * @param duration How long the test took to execute, in milliseconds. + */ + failed(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void; + + /** + * Indicates a test has errored. You should pass one or more + * {@link TestMessage TestMessages} to describe the failure. This differs + * from the "failed" state in that it indicates a test that couldn't be + * executed at all, from a compilation error for example. + * @param test Test item to update. + * @param message Messages associated with the test failure. + * @param duration How long the test took to execute, in milliseconds. + */ + errored(test: TestItem, message: TestMessage | readonly TestMessage[], duration?: number): void; + + /** + * Indicates a test has passed. + * @param test Test item to update. + * @param duration How long the test took to execute, in milliseconds. + */ + passed(test: TestItem, duration?: number): void; + + /** + * Appends raw output from the test runner. On the user's request, the + * output will be displayed in a terminal. ANSI escape sequences, + * such as colors and text styles, are supported. New lines must be given + * as CRLF (`\r\n`) rather than LF (`\n`). + * + * @param output Output text to append. + * @param location Indicate that the output was logged at the given + * location. + * @param test Test item to associate the output with. + */ + appendOutput(output: string, location?: Location, test?: TestItem): void; + + /** + * Adds coverage for a file in the run. + */ + addCoverage(fileCoverage: FileCoverage): void; + + /** + * Signals the end of the test run. Any tests included in the run whose + * states have not been updated will have their state reset. + */ + end(): void; + + /** + * An event fired when the editor is no longer interested in data + * associated with the test run. + */ + onDidDispose: Event; + } + + /** + * Collection of test items, found in {@link TestItem.children} and + * {@link TestController.items}. + */ + export interface TestItemCollection extends Iterable<[id: string, testItem: TestItem]> { + /** + * Gets the number of items in the collection. + */ + readonly size: number; + + /** + * Replaces the items stored by the collection. + * @param items Items to store. + */ + replace(items: readonly TestItem[]): void; + + /** + * Iterate over each entry in this collection. + * + * @param callback Function to execute for each entry. + * @param thisArg The `this` context used when invoking the handler function. + */ + forEach(callback: (item: TestItem, collection: TestItemCollection) => unknown, thisArg?: any): void; + + /** + * Adds the test item to the children. If an item with the same ID already + * exists, it'll be replaced. + * @param item Item to add. + */ + add(item: TestItem): void; + + /** + * Removes a single test item from the collection. + * @param itemId Item ID to delete. + */ + delete(itemId: string): void; + + /** + * Efficiently gets a test item by ID, if it exists, in the children. + * @param itemId Item ID to get. + * @returns The found item or undefined if it does not exist. + */ + get(itemId: string): TestItem | undefined; + } + + /** + * An item shown in the "test explorer" view. + * + * A `TestItem` can represent either a test suite or a test itself, since + * they both have similar capabilities. + */ + export interface TestItem { + /** + * Identifier for the `TestItem`. This is used to correlate + * test results and tests in the document with those in the workspace + * (test explorer). This cannot change for the lifetime of the `TestItem`, + * and must be unique among its parent's direct children. + */ + readonly id: string; + + /** + * URI this `TestItem` is associated with. May be a file or directory. + */ + readonly uri: Uri | undefined; + + /** + * The children of this test item. For a test suite, this may contain the + * individual test cases or nested suites. + */ + readonly children: TestItemCollection; + + /** + * The parent of this item. It's set automatically, and is undefined + * top-level items in the {@link TestController.items} and for items that + * aren't yet included in another item's {@link TestItem.children children}. + */ + readonly parent: TestItem | undefined; + + /** + * Tags associated with this test item. May be used in combination with + * {@link TestRunProfile.tag tags}, or simply as an organizational feature. + */ + tags: readonly TestTag[]; + + /** + * Indicates whether this test item may have children discovered by resolving. + * + * If true, this item is shown as expandable in the Test Explorer view and + * expanding the item will cause {@link TestController.resolveHandler} + * to be invoked with the item. + * + * Default to `false`. + */ + canResolveChildren: boolean; + + /** + * Controls whether the item is shown as "busy" in the Test Explorer view. + * This is useful for showing status while discovering children. + * + * Defaults to `false`. + */ + busy: boolean; + + /** + * Display name describing the test case. + */ + label: string; + + /** + * Optional description that appears next to the label. + */ + description?: string; + + /** + * A string that should be used when comparing this item + * with other items. When `falsy` the {@link TestItem.label label} + * is used. + */ + sortText?: string | undefined; + + /** + * Location of the test item in its {@link TestItem.uri uri}. + * + * This is only meaningful if the `uri` points to a file. + */ + range: Range | undefined; + + /** + * Optional error encountered while loading the test. + * + * Note that this is not a test result and should only be used to represent errors in + * test discovery, such as syntax errors. + */ + error: string | MarkdownString | undefined; + } + + /** + * A stack frame found in the {@link TestMessage.stackTrace}. + */ + export class TestMessageStackFrame { + /** + * The location of this stack frame. This should be provided as a URI if the + * location of the call frame can be accessed by the editor. + */ + uri?: Uri; + + /** + * Position of the stack frame within the file. + */ + position?: Position; + + /** + * The name of the stack frame, typically a method or function name. + */ + label: string; + + /** + * @param label The name of the stack frame + * @param file The file URI of the stack frame + * @param position The position of the stack frame within the file + */ + constructor(label: string, uri?: Uri, position?: Position); + } + + /** + * Message associated with the test state. Can be linked to a specific + * source range -- useful for assertion failures, for example. + */ + export class TestMessage { + /** + * Human-readable message text to display. + */ + message: string | MarkdownString; + + /** + * Expected test output. If given with {@link TestMessage.actualOutput actualOutput }, a diff view will be shown. + */ + expectedOutput?: string; + + /** + * Actual test output. If given with {@link TestMessage.expectedOutput expectedOutput }, a diff view will be shown. + */ + actualOutput?: string; + + /** + * Associated file location. + */ + location?: Location; + + /** + * Context value of the test item. This can be used to contribute message- + * specific actions to the test peek view. The value set here can be found + * in the `testMessage` property of the following `menus` contribution points: + * + * - `testing/message/context` - context menu for the message in the results tree + * - `testing/message/content` - a prominent button overlaying editor content where + * the message is displayed. + * + * For example: + * + * ```json + * "contributes": { + * "menus": { + * "testing/message/content": [ + * { + * "command": "extension.deleteCommentThread", + * "when": "testMessage == canApplyRichDiff" + * } + * ] + * } + * } + * ``` + * + * The command will be called with an object containing: + * - `test`: the {@link TestItem} the message is associated with, *if* it + * is still present in the {@link TestController.items} collection. + * - `message`: the {@link TestMessage} instance. + */ + contextValue?: string; + + /** + * The stack trace associated with the message or failure. + */ + stackTrace?: TestMessageStackFrame[]; + + /** + * Creates a new TestMessage that will present as a diff in the editor. + * @param message Message to display to the user. + * @param expected Expected output. + * @param actual Actual output. + */ + static diff(message: string | MarkdownString, expected: string, actual: string): TestMessage; + + /** + * Creates a new TestMessage instance. + * @param message The message to show to the user. + */ + constructor(message: string | MarkdownString); + } + + /** + * A class that contains information about a covered resource. A count can + * be give for lines, branches, and declarations in a file. + */ + export class TestCoverageCount { + /** + * Number of items covered in the file. + */ + covered: number; + /** + * Total number of covered items in the file. + */ + total: number; + + /** + * @param covered Value for {@link TestCoverageCount.covered} + * @param total Value for {@link TestCoverageCount.total} + */ + constructor(covered: number, total: number); + } + + /** + * Contains coverage metadata for a file. + */ + export class FileCoverage { + /** + * File URI. + */ + readonly uri: Uri; + + /** + * Statement coverage information. If the reporter does not provide statement + * coverage information, this can instead be used to represent line coverage. + */ + statementCoverage: TestCoverageCount; + + /** + * Branch coverage information. + */ + branchCoverage?: TestCoverageCount; + + /** + * Declaration coverage information. Depending on the reporter and + * language, this may be types such as functions, methods, or namespaces. + */ + declarationCoverage?: TestCoverageCount; + + /** + * A list of {@link TestItem test cases} that generated coverage in this + * file. If set, then {@link TestRunProfile.loadDetailedCoverageForTest} + * should also be defined in order to retrieve detailed coverage information. + */ + includesTests?: TestItem[]; + + /** + * Creates a {@link FileCoverage} instance with counts filled in from + * the coverage details. + * @param uri Covered file URI + * @param detailed Detailed coverage information + */ + static fromDetails(uri: Uri, details: readonly FileCoverageDetail[]): FileCoverage; + + /** + * @param uri Covered file URI + * @param statementCoverage Statement coverage information. If the reporter + * does not provide statement coverage information, this can instead be + * used to represent line coverage. + * @param branchCoverage Branch coverage information + * @param declarationCoverage Declaration coverage information + * @param includesTests Test cases included in this coverage report, see {@link includesTests} + */ + constructor( + uri: Uri, + statementCoverage: TestCoverageCount, + branchCoverage?: TestCoverageCount, + declarationCoverage?: TestCoverageCount, + includesTests?: TestItem[], + ); + } + + /** + * Contains coverage information for a single statement or line. + */ + export class StatementCoverage { + /** + * The number of times this statement was executed, or a boolean indicating + * whether it was executed if the exact count is unknown. If zero or false, + * the statement will be marked as un-covered. + */ + executed: number | boolean; + + /** + * Statement location. + */ + location: Position | Range; + + /** + * Coverage from branches of this line or statement. If it's not a + * conditional, this will be empty. + */ + branches: BranchCoverage[]; + + /** + * @param location The statement position. + * @param executed The number of times this statement was executed, or a + * boolean indicating whether it was executed if the exact count is + * unknown. If zero or false, the statement will be marked as un-covered. + * @param branches Coverage from branches of this line. If it's not a + * conditional, this should be omitted. + */ + constructor(executed: number | boolean, location: Position | Range, branches?: BranchCoverage[]); + } + + /** + * Contains coverage information for a branch of a {@link StatementCoverage}. + */ + export class BranchCoverage { + /** + * The number of times this branch was executed, or a boolean indicating + * whether it was executed if the exact count is unknown. If zero or false, + * the branch will be marked as un-covered. + */ + executed: number | boolean; + + /** + * Branch location. + */ + location?: Position | Range; + + /** + * Label for the branch, used in the context of "the ${label} branch was + * not taken," for example. + */ + label?: string; + + /** + * @param executed The number of times this branch was executed, or a + * boolean indicating whether it was executed if the exact count is + * unknown. If zero or false, the branch will be marked as un-covered. + * @param location The branch position. + */ + constructor(executed: number | boolean, location?: Position | Range, label?: string); + } + + /** + * Contains coverage information for a declaration. Depending on the reporter + * and language, this may be types such as functions, methods, or namespaces. + */ + export class DeclarationCoverage { + /** + * Name of the declaration. + */ + name: string; + + /** + * The number of times this declaration was executed, or a boolean + * indicating whether it was executed if the exact count is unknown. If + * zero or false, the declaration will be marked as un-covered. + */ + executed: number | boolean; + + /** + * Declaration location. + */ + location: Position | Range; + + /** + * @param executed The number of times this declaration was executed, or a + * boolean indicating whether it was executed if the exact count is + * unknown. If zero or false, the declaration will be marked as un-covered. + * @param location The declaration position. + */ + constructor(name: string, executed: number | boolean, location: Position | Range); + } + + /** + * Coverage details returned from {@link TestRunProfile.loadDetailedCoverage}. + */ + export type FileCoverageDetail = StatementCoverage | DeclarationCoverage; + + /** + * The tab represents a single text based resource. + */ + export class TabInputText { + /** + * The uri represented by the tab. + */ + readonly uri: Uri; + /** + * Constructs a text tab input with the given URI. + * @param uri The URI of the tab. + */ + constructor(uri: Uri); + } + + /** + * The tab represents two text based resources + * being rendered as a diff. + */ + export class TabInputTextDiff { + /** + * The uri of the original text resource. + */ + readonly original: Uri; + /** + * The uri of the modified text resource. + */ + readonly modified: Uri; + /** + * Constructs a new text diff tab input with the given URIs. + * @param original The uri of the original text resource. + * @param modified The uri of the modified text resource. + */ + constructor(original: Uri, modified: Uri); + } + + /** + * The tab represents a custom editor. + */ + export class TabInputCustom { + /** + * The uri that the tab is representing. + */ + readonly uri: Uri; + /** + * The type of custom editor. + */ + readonly viewType: string; + /** + * Constructs a custom editor tab input. + * @param uri The uri of the tab. + * @param viewType The viewtype of the custom editor. + */ + constructor(uri: Uri, viewType: string); + } + + /** + * The tab represents a webview. + */ + export class TabInputWebview { + /** + * The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} + */ + readonly viewType: string; + /** + * Constructs a webview tab input with the given view type. + * @param viewType The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} + */ + constructor(viewType: string); + } + + /** + * The tab represents a notebook. + */ + export class TabInputNotebook { + /** + * The uri that the tab is representing. + */ + readonly uri: Uri; + /** + * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + readonly notebookType: string; + /** + * Constructs a new tab input for a notebook. + * @param uri The uri of the notebook. + * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + constructor(uri: Uri, notebookType: string); + } + + /** + * The tabs represents two notebooks in a diff configuration. + */ + export class TabInputNotebookDiff { + /** + * The uri of the original notebook. + */ + readonly original: Uri; + /** + * The uri of the modified notebook. + */ + readonly modified: Uri; + /** + * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + readonly notebookType: string; + /** + * Constructs a notebook diff tab input. + * @param original The uri of the original unmodified notebook. + * @param modified The uri of the modified notebook. + * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + constructor(original: Uri, modified: Uri, notebookType: string); + } + + /** + * The tab represents a terminal in the editor area. + */ + export class TabInputTerminal { + /** + * Constructs a terminal tab input. + */ + constructor(); + } + + /** + * Represents a tab within a {@link TabGroup group of tabs}. + * Tabs are merely the graphical representation within the editor area. + * A backing editor is not a guarantee. + */ + export interface Tab { + + /** + * The text displayed on the tab. + */ + readonly label: string; + + /** + * The group which the tab belongs to. + */ + readonly group: TabGroup; + + /** + * Defines the structure of the tab i.e. text, notebook, custom, etc. + * Resource and other useful properties are defined on the tab kind. + */ + readonly input: TabInputText | TabInputTextDiff | TabInputCustom | TabInputWebview | TabInputNotebook | TabInputNotebookDiff | TabInputTerminal | unknown; + + /** + * Whether or not the tab is currently active. + * This is dictated by being the selected tab in the group. + */ + readonly isActive: boolean; + + /** + * Whether or not the dirty indicator is present on the tab. + */ + readonly isDirty: boolean; + + /** + * Whether or not the tab is pinned (pin icon is present). + */ + readonly isPinned: boolean; + + /** + * Whether or not the tab is in preview mode. + */ + readonly isPreview: boolean; + } + + /** + * An event describing change to tabs. + */ + export interface TabChangeEvent { + /** + * The tabs that have been opened. + */ + readonly opened: readonly Tab[]; + /** + * The tabs that have been closed. + */ + readonly closed: readonly Tab[]; + /** + * Tabs that have changed, e.g have changed + * their {@link Tab.isActive active} state. + */ + readonly changed: readonly Tab[]; + } + + /** + * An event describing changes to tab groups. + */ + export interface TabGroupChangeEvent { + /** + * Tab groups that have been opened. + */ + readonly opened: readonly TabGroup[]; + /** + * Tab groups that have been closed. + */ + readonly closed: readonly TabGroup[]; + /** + * Tab groups that have changed, e.g have changed + * their {@link TabGroup.isActive active} state. + */ + readonly changed: readonly TabGroup[]; + } + + /** + * Represents a group of tabs. A tab group itself consists of multiple tabs. + */ + export interface TabGroup { + /** + * Whether or not the group is currently active. + * + * *Note* that only one tab group is active at a time, but that multiple tab + * groups can have an {@link activeTab active tab}. + * + * @see {@link Tab.isActive} + */ + readonly isActive: boolean; + + /** + * The view column of the group. + */ + readonly viewColumn: ViewColumn; + + /** + * The active {@link Tab tab} in the group. This is the tab whose contents are currently + * being rendered. + * + * *Note* that there can be one active tab per group but there can only be one {@link TabGroups.activeTabGroup active group}. + */ + readonly activeTab: Tab | undefined; + + /** + * The list of tabs contained within the group. + * This can be empty if the group has no tabs open. + */ + readonly tabs: readonly Tab[]; + } + + /** + * Represents the main editor area which consists of multiple groups which contain tabs. + */ + export interface TabGroups { + /** + * All the groups within the group container. + */ + readonly all: readonly TabGroup[]; + + /** + * The currently active group. + */ + readonly activeTabGroup: TabGroup; + + /** + * An {@link Event event} which fires when {@link TabGroup tab groups} have changed. + */ + readonly onDidChangeTabGroups: Event; + + /** + * An {@link Event event} which fires when {@link Tab tabs} have changed. + */ + readonly onDidChangeTabs: Event; + + /** + * Closes the tab. This makes the tab object invalid and the tab + * should no longer be used for further actions. + * Note: In the case of a dirty tab, a confirmation dialog will be shown which may be cancelled. If cancelled the tab is still valid + * + * @param tab The tab to close. + * @param preserveFocus When `true` focus will remain in its current position. If `false` it will jump to the next tab. + * @returns A promise that resolves to `true` when all tabs have been closed. + */ + close(tab: Tab | readonly Tab[], preserveFocus?: boolean): Thenable; + + /** + * Closes the tab group. This makes the tab group object invalid and the tab group + * should no longer be used for further actions. + * @param tabGroup The tab group to close. + * @param preserveFocus When `true` focus will remain in its current position. + * @returns A promise that resolves to `true` when all tab groups have been closed. + */ + close(tabGroup: TabGroup | readonly TabGroup[], preserveFocus?: boolean): Thenable; + } + + /** + * A special value wrapper denoting a value that is safe to not clean. + * This is to be used when you can guarantee no identifiable information is contained in the value and the cleaning is improperly redacting it. + */ + export class TelemetryTrustedValue { + + /** + * The value that is trusted to not contain PII. + */ + readonly value: T; + + /** + * Creates a new telementry trusted value. + * + * @param value A value to trust + */ + constructor(value: T); + } + + /** + * A telemetry logger which can be used by extensions to log usage and error telementry. + * + * A logger wraps around an {@link TelemetrySender sender} but it guarantees that + * - user settings to disable or tweak telemetry are respected, and that + * - potential sensitive data is removed + * + * It also enables an "echo UI" that prints whatever data is send and it allows the editor + * to forward unhandled errors to the respective extensions. + * + * To get an instance of a `TelemetryLogger`, use + * {@link env.createTelemetryLogger `createTelemetryLogger`}. + */ + export interface TelemetryLogger { + + /** + * An {@link Event} which fires when the enablement state of usage or error telemetry changes. + */ + readonly onDidChangeEnableStates: Event; + + /** + * Whether or not usage telemetry is enabled for this logger. + */ + readonly isUsageEnabled: boolean; + + /** + * Whether or not error telemetry is enabled for this logger. + */ + readonly isErrorsEnabled: boolean; + + /** + * Log a usage event. + * + * After completing cleaning, telemetry setting checks, and data mix-in calls `TelemetrySender.sendEventData` to log the event. + * Automatically supports echoing to extension telemetry output channel. + * @param eventName The event name to log + * @param data The data to log + */ + logUsage(eventName: string, data?: Record): void; + + /** + * Log an error event. + * + * After completing cleaning, telemetry setting checks, and data mix-in calls `TelemetrySender.sendEventData` to log the event. Differs from `logUsage` in that it will log the event if the telemetry setting is Error+. + * Automatically supports echoing to extension telemetry output channel. + * @param eventName The event name to log + * @param data The data to log + */ + logError(eventName: string, data?: Record): void; + + /** + * Log an error event. + * + * Calls `TelemetrySender.sendErrorData`. Does cleaning, telemetry checks, and data mix-in. + * Automatically supports echoing to extension telemetry output channel. + * Will also automatically log any exceptions thrown within the extension host process. + * @param error The error object which contains the stack trace cleaned of PII + * @param data Additional data to log alongside the stack trace + */ + logError(error: Error, data?: Record): void; + + /** + * Dispose this object and free resources. + */ + dispose(): void; + } + + /** + * The telemetry sender is the contract between a telemetry logger and some telemetry service. **Note** that extensions must NOT + * call the methods of their sender directly as the logger provides extra guards and cleaning. + * + * ```js + * const sender: vscode.TelemetrySender = {...}; + * const logger = vscode.env.createTelemetryLogger(sender); + * + * // GOOD - uses the logger + * logger.logUsage('myEvent', { myData: 'myValue' }); + * + * // BAD - uses the sender directly: no data cleansing, ignores user settings, no echoing to the telemetry output channel etc + * sender.logEvent('myEvent', { myData: 'myValue' }); + * ``` + */ + export interface TelemetrySender { + /** + * Function to send event data without a stacktrace. Used within a {@link TelemetryLogger} + * + * @param eventName The name of the event which you are logging + * @param data A serializable key value pair that is being logged + */ + sendEventData(eventName: string, data?: Record): void; + + /** + * Function to send an error. Used within a {@link TelemetryLogger} + * + * @param error The error being logged + * @param data Any additional data to be collected with the exception + */ + sendErrorData(error: Error, data?: Record): void; + + /** + * Optional flush function which will give this sender a chance to send any remaining events + * as its {@link TelemetryLogger} is being disposed + */ + flush?(): void | Thenable; + } + + /** + * Options for creating a {@link TelemetryLogger} + */ + export interface TelemetryLoggerOptions { + /** + * Whether or not you want to avoid having the built-in common properties such as os, extension name, etc injected into the data object. + * Defaults to `false` if not defined. + */ + readonly ignoreBuiltInCommonProperties?: boolean; + + /** + * Whether or not unhandled errors on the extension host caused by your extension should be logged to your sender. + * Defaults to `false` if not defined. + */ + readonly ignoreUnhandledErrors?: boolean; + + /** + * Any additional common properties which should be injected into the data object. + */ + readonly additionalCommonProperties?: Record; + } + + /** + * Represents a user request in chat history. + */ + export class ChatRequestTurn { + /** + * The prompt as entered by the user. + * + * Information about references used in this request is stored in {@link ChatRequestTurn.references}. + * + * *Note* that the {@link ChatParticipant.name name} of the participant and the {@link ChatCommand.name command} + * are not part of the prompt. + */ + readonly prompt: string; + + /** + * The id of the chat participant to which this request was directed. + */ + readonly participant: string; + + /** + * The name of the {@link ChatCommand command} that was selected for this request. + */ + readonly command?: string; + + /** + * The references that were used in this message. + */ + readonly references: ChatPromptReference[]; + + /** + * The list of tools were attached to this request. + */ + readonly toolReferences: readonly ChatLanguageModelToolReference[]; + + /** + * @hidden + */ + private constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string, toolReferences: ChatLanguageModelToolReference[]); + } + + /** + * Represents a chat participant's response in chat history. + */ + export class ChatResponseTurn { + /** + * The content that was received from the chat participant. Only the stream parts that represent actual content (not metadata) are represented. + */ + readonly response: ReadonlyArray; + + /** + * The result that was received from the chat participant. + */ + readonly result: ChatResult; + + /** + * The id of the chat participant that this response came from. + */ + readonly participant: string; + + /** + * The name of the command that this response came from. + */ + readonly command?: string; + + /** + * @hidden + */ + private constructor(response: ReadonlyArray, result: ChatResult, participant: string); + } + + /** + * Extra context passed to a participant. + */ + export interface ChatContext { + /** + * All of the chat messages so far in the current chat session. Currently, only chat messages for the current participant are included. + */ + readonly history: ReadonlyArray; + } + + /** + * Represents an error result from a chat request. + */ + export interface ChatErrorDetails { + /** + * An error message that is shown to the user. + */ + message: string; + + /** + * If set to true, the response will be partly blurred out. + */ + responseIsFiltered?: boolean; + } + + /** + * The result of a chat request. + */ + export interface ChatResult { + /** + * If the request resulted in an error, this property defines the error details. + */ + errorDetails?: ChatErrorDetails; + + /** + * Arbitrary metadata for this result. Can be anything, but must be JSON-stringifyable. + */ + readonly metadata?: { readonly [key: string]: any }; + } + + /** + * Represents the type of user feedback received. + */ + export enum ChatResultFeedbackKind { + /** + * The user marked the result as unhelpful. + */ + Unhelpful = 0, + + /** + * The user marked the result as helpful. + */ + Helpful = 1, + } + + /** + * Represents user feedback for a result. + */ + export interface ChatResultFeedback { + /** + * The ChatResult for which the user is providing feedback. + * This object has the same properties as the result returned from the participant callback, including `metadata`, but is not the same instance. + */ + readonly result: ChatResult; + + /** + * The kind of feedback that was received. + */ + readonly kind: ChatResultFeedbackKind; + } + + /** + * A followup question suggested by the participant. + */ + export interface ChatFollowup { + /** + * The message to send to the chat. + */ + prompt: string; + + /** + * A title to show the user. The prompt will be shown by default, when this is unspecified. + */ + label?: string; + + /** + * By default, the followup goes to the same participant/command. But this property can be set to invoke a different participant by ID. + * Followups can only invoke a participant that was contributed by the same extension. + */ + participant?: string; + + /** + * By default, the followup goes to the same participant/command. But this property can be set to invoke a different command. + */ + command?: string; + } + + /** + * Will be invoked once after each request to get suggested followup questions to show the user. The user can click the followup to send it to the chat. + */ + export interface ChatFollowupProvider { + /** + * Provide followups for the given result. + * + * @param result This object has the same properties as the result returned from the participant callback, including `metadata`, but is not the same instance. + * @param context Extra context passed to a participant. + * @param token A cancellation token. + */ + provideFollowups(result: ChatResult, context: ChatContext, token: CancellationToken): ProviderResult; + } + + /** + * A chat request handler is a callback that will be invoked when a request is made to a chat participant. + */ + export type ChatRequestHandler = (request: ChatRequest, context: ChatContext, response: ChatResponseStream, token: CancellationToken) => ProviderResult; + + /** + * A chat participant can be invoked by the user in a chat session, using the `@` prefix. When it is invoked, it handles the chat request and is solely + * responsible for providing a response to the user. A ChatParticipant is created using {@link chat.createChatParticipant}. + */ + export interface ChatParticipant { + /** + * A unique ID for this participant. + */ + readonly id: string; + + /** + * An icon for the participant shown in UI. + */ + iconPath?: IconPath; + + /** + * The handler for requests to this participant. + */ + requestHandler: ChatRequestHandler; + + /** + * This provider will be called once after each request to retrieve suggested followup questions. + */ + followupProvider?: ChatFollowupProvider; + + /** + * An event that fires whenever feedback for a result is received, e.g. when a user up- or down-votes + * a result. + * + * The passed {@link ChatResultFeedback.result result} is guaranteed to have the same properties as the result that was + * previously returned from this chat participant's handler. + */ + onDidReceiveFeedback: Event; + + /** + * Dispose this participant and free resources. + */ + dispose(): void; + } + + /** + * A reference to a value that the user added to their chat request. + */ + export interface ChatPromptReference { + /** + * A unique identifier for this kind of reference. + */ + readonly id: string; + + /** + * The start and end index of the reference in the {@link ChatRequest.prompt prompt}. When undefined, the reference was not part of the prompt text. + * + * *Note* that the indices take the leading `#`-character into account which means they can + * used to modify the prompt as-is. + */ + readonly range?: [start: number, end: number]; + + /** + * A description of this value that could be used in an LLM prompt. + */ + readonly modelDescription?: string; + + /** + * The value of this reference. The `string | Uri | Location` types are used today, but this could expand in the future. + */ + readonly value: string | Uri | Location | unknown; + } + + /** + * A request to a chat participant. + */ + export interface ChatRequest { + /** + * The prompt as entered by the user. + * + * Information about references used in this request is stored in {@link ChatRequest.references}. + * + * *Note* that the {@link ChatParticipant.name name} of the participant and the {@link ChatCommand.name command} + * are not part of the prompt. + */ + readonly prompt: string; + + /** + * The name of the {@link ChatCommand command} that was selected for this request. + */ + readonly command: string | undefined; + + /** + * The list of references and their values that are referenced in the prompt. + * + * *Note* that the prompt contains references as authored and that it is up to the participant + * to further modify the prompt, for instance by inlining reference values or creating links to + * headings which contain the resolved values. References are sorted in reverse by their range + * in the prompt. That means the last reference in the prompt is the first in this list. This simplifies + * string-manipulation of the prompt. + */ + readonly references: readonly ChatPromptReference[]; + + /** + * The list of tools that the user attached to their request. + * + * When a tool reference is present, the chat participant should make a chat request using + * {@link LanguageModelChatToolMode.Required} to force the language model to generate input for the tool. Then, the + * participant can use {@link lm.invokeTool} to use the tool attach the result to its request for the user's prompt. The + * tool may contribute useful extra context for the user's request. + */ + readonly toolReferences: readonly ChatLanguageModelToolReference[]; + + /** + * A token that can be passed to {@link lm.invokeTool} when invoking a tool inside the context of handling a chat request. + * This associates the tool invocation to a chat session. + */ + readonly toolInvocationToken: ChatParticipantToolToken; + + /** + * This is the model that is currently selected in the UI. Extensions can use this or use {@link chat.selectChatModels} to + * pick another model. Don't hold onto this past the lifetime of the request. + */ + readonly model: LanguageModelChat; + } + + /** + * The ChatResponseStream is how a participant is able to return content to the chat view. It provides several methods for streaming different types of content + * which will be rendered in an appropriate way in the chat view. A participant can use the helper method for the type of content it wants to return, or it + * can instantiate a {@link ChatResponsePart} and use the generic {@link ChatResponseStream.push} method to return it. + */ + export interface ChatResponseStream { + /** + * Push a markdown part to this stream. Short-hand for + * `push(new ChatResponseMarkdownPart(value))`. + * + * @see {@link ChatResponseStream.push} + * @param value A markdown string or a string that should be interpreted as markdown. The boolean form of {@link MarkdownString.isTrusted} is NOT supported. + */ + markdown(value: string | MarkdownString): void; + + /** + * Push an anchor part to this stream. Short-hand for + * `push(new ChatResponseAnchorPart(value, title))`. + * An anchor is an inline reference to some type of resource. + * + * @param value A uri or location. + * @param title An optional title that is rendered with value. + */ + anchor(value: Uri | Location, title?: string): void; + + /** + * Push a command button part to this stream. Short-hand for + * `push(new ChatResponseCommandButtonPart(value, title))`. + * + * @param command A Command that will be executed when the button is clicked. + */ + button(command: Command): void; + + /** + * Push a filetree part to this stream. Short-hand for + * `push(new ChatResponseFileTreePart(value))`. + * + * @param value File tree data. + * @param baseUri The base uri to which this file tree is relative. + */ + filetree(value: ChatResponseFileTree[], baseUri: Uri): void; + + /** + * Push a progress part to this stream. Short-hand for + * `push(new ChatResponseProgressPart(value))`. + * + * @param value A progress message + */ + progress(value: string): void; + + /** + * Push a reference to this stream. Short-hand for + * `push(new ChatResponseReferencePart(value))`. + * + * *Note* that the reference is not rendered inline with the response. + * + * @param value A uri or location + * @param iconPath Icon for the reference shown in UI + */ + reference(value: Uri | Location, iconPath?: IconPath): void; + + /** + * Pushes a part to this stream. + * + * @param part A response part, rendered or metadata + */ + push(part: ChatResponsePart): void; + } + + /** + * Represents a part of a chat response that is formatted as Markdown. + */ + export class ChatResponseMarkdownPart { + /** + * A markdown string or a string that should be interpreted as markdown. + */ + value: MarkdownString; + + /** + * Create a new ChatResponseMarkdownPart. + * + * @param value A markdown string or a string that should be interpreted as markdown. The boolean form of {@link MarkdownString.isTrusted} is NOT supported. + */ + constructor(value: string | MarkdownString); + } + + /** + * Represents a file tree structure in a chat response. + */ + export interface ChatResponseFileTree { + /** + * The name of the file or directory. + */ + name: string; + + /** + * An array of child file trees, if the current file tree is a directory. + */ + children?: ChatResponseFileTree[]; + } + + /** + * Represents a part of a chat response that is a file tree. + */ + export class ChatResponseFileTreePart { + /** + * File tree data. + */ + value: ChatResponseFileTree[]; + + /** + * The base uri to which this file tree is relative + */ + baseUri: Uri; + + /** + * Create a new ChatResponseFileTreePart. + * @param value File tree data. + * @param baseUri The base uri to which this file tree is relative. + */ + constructor(value: ChatResponseFileTree[], baseUri: Uri); + } + + /** + * Represents a part of a chat response that is an anchor, that is rendered as a link to a target. + */ + export class ChatResponseAnchorPart { + /** + * The target of this anchor. + */ + value: Uri | Location; + + /** + * An optional title that is rendered with value. + */ + title?: string; + + /** + * Create a new ChatResponseAnchorPart. + * @param value A uri or location. + * @param title An optional title that is rendered with value. + */ + constructor(value: Uri | Location, title?: string); + } + + /** + * Represents a part of a chat response that is a progress message. + */ + export class ChatResponseProgressPart { + /** + * The progress message + */ + value: string; + + /** + * Create a new ChatResponseProgressPart. + * @param value A progress message + */ + constructor(value: string); + } + + /** + * Represents a part of a chat response that is a reference, rendered separately from the content. + */ + export class ChatResponseReferencePart { + /** + * The reference target. + */ + value: Uri | Location; + + /** + * The icon for the reference. + */ + iconPath?: IconPath; + + /** + * Create a new ChatResponseReferencePart. + * @param value A uri or location + * @param iconPath Icon for the reference shown in UI + */ + constructor(value: Uri | Location, iconPath?: IconPath); + } + + /** + * Represents a part of a chat response that is a button that executes a command. + */ + export class ChatResponseCommandButtonPart { + /** + * The command that will be executed when the button is clicked. + */ + value: Command; + + /** + * Create a new ChatResponseCommandButtonPart. + * @param value A Command that will be executed when the button is clicked. + */ + constructor(value: Command); + } + + /** + * Represents the different chat response types. + */ + export type ChatResponsePart = ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart + | ChatResponseProgressPart | ChatResponseReferencePart | ChatResponseCommandButtonPart; + + + /** + * Namespace for chat functionality. Users interact with chat participants by sending messages + * to them in the chat view. Chat participants can respond with markdown or other types of content + * via the {@link ChatResponseStream}. + */ + export namespace chat { + /** + * Create a new {@link ChatParticipant chat participant} instance. + * + * @param id A unique identifier for the participant. + * @param handler A request handler for the participant. + * @returns A new chat participant + */ + export function createChatParticipant(id: string, handler: ChatRequestHandler): ChatParticipant; + } + + /** + * Represents the role of a chat message. This is either the user or the assistant. + */ + export enum LanguageModelChatMessageRole { + /** + * The user role, e.g the human interacting with a language model. + */ + User = 1, + + /** + * The assistant role, e.g. the language model generating responses. + */ + Assistant = 2 + } + + /** + * Represents a message in a chat. Can assume different roles, like user or assistant. + */ + export class LanguageModelChatMessage { + + /** + * Utility to create a new user message. + * + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ + static User(content: string | Array, name?: string): LanguageModelChatMessage; + + /** + * Utility to create a new assistant message. + * + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ + static Assistant(content: string | Array, name?: string): LanguageModelChatMessage; + + /** + * The role of this message. + */ + role: LanguageModelChatMessageRole; + + /** + * A string or heterogeneous array of things that a message can contain as content. Some parts may be message-type + * specific for some models. + */ + content: Array; + + /** + * The optional name of a user for this message. + */ + name: string | undefined; + + /** + * Create a new user message. + * + * @param role The role of the message. + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ + constructor(role: LanguageModelChatMessageRole, content: string | Array, name?: string); + } + + /** + * Represents a language model response. + * + * @see {@link LanguageModelAccess.chatRequest} + */ + export interface LanguageModelChatResponse { + + /** + * An async iterable that is a stream of text and tool-call parts forming the overall response. A + * {@link LanguageModelTextPart} is part of the assistant's response to be shown to the user. A + * {@link LanguageModelToolCallPart} is a request from the language model to call a tool. The latter will + * only be returned if tools were passed in the request via {@link LanguageModelChatRequestOptions.tools}. The + * `unknown`-type is used as a placeholder for future parts, like image data parts. + * + * *Note* that this stream will error when during data receiving an error occurs. Consumers of the stream should handle + * the errors accordingly. + * + * To cancel the stream, the consumer can {@link CancellationTokenSource.cancel cancel} the token that was used to make + * the request or break from the for-loop. + * + * @example + * ```ts + * try { + * // consume stream + * for await (const chunk of response.stream) { + * if (chunk instanceof LanguageModelTextPart) { + * console.log("TEXT", chunk); + * } else if (chunk instanceof LanguageModelToolCallPart) { + * console.log("TOOL CALL", chunk); + * } + * } + * + * } catch(e) { + * // stream ended with an error + * console.error(e); + * } + * ``` + */ + stream: AsyncIterable; + + /** + * This is equivalent to filtering everything except for text parts from a {@link LanguageModelChatResponse.stream}. + * + * @see {@link LanguageModelChatResponse.stream} + */ + text: AsyncIterable; + } + + /** + * Represents a language model for making chat requests. + * + * @see {@link lm.selectChatModels} + */ + export interface LanguageModelChat { + + /** + * Human-readable name of the language model. + */ + readonly name: string; + + /** + * Opaque identifier of the language model. + */ + readonly id: string; + + /** + * A well-known identifier of the vendor of the language model. An example is `copilot`, but + * values are defined by extensions contributing chat models and need to be looked up with them. + */ + readonly vendor: string; + + /** + * Opaque family-name of the language model. Values might be `gpt-3.5-turbo`, `gpt4`, `phi2`, or `llama` + * but they are defined by extensions contributing languages and subject to change. + */ + readonly family: string; + + /** + * Opaque version string of the model. This is defined by the extension contributing the language model + * and subject to change. + */ + readonly version: string; + + /** + * The maximum number of tokens that can be sent to the model in a single request. + */ + readonly maxInputTokens: number; + + /** + * Make a chat request using a language model. + * + * *Note* that language model use may be subject to access restrictions and user consent. Calling this function + * for the first time (for an extension) will show a consent dialog to the user and because of that this function + * must _only be called in response to a user action!_ Extensions can use {@link LanguageModelAccessInformation.canSendRequest} + * to check if they have the necessary permissions to make a request. + * + * This function will return a rejected promise if making a request to the language model is not + * possible. Reasons for this can be: + * + * - user consent not given, see {@link LanguageModelError.NoPermissions `NoPermissions`} + * - model does not exist anymore, see {@link LanguageModelError.NotFound `NotFound`} + * - quota limits exceeded, see {@link LanguageModelError.Blocked `Blocked`} + * - other issues in which case extension must check {@link LanguageModelError.cause `LanguageModelError.cause`} + * + * An extension can make use of language model tool calling by passing a set of tools to + * {@link LanguageModelChatRequestOptions.tools}. The language model will return a {@link LanguageModelToolCallPart} and + * the extension can invoke the tool and make another request with the result. + * + * @param messages An array of message instances. + * @param options Options that control the request. + * @param token A cancellation token which controls the request. See {@link CancellationTokenSource} for how to create one. + * @returns A thenable that resolves to a {@link LanguageModelChatResponse}. The promise will reject when the request couldn't be made. + */ + sendRequest(messages: LanguageModelChatMessage[], options?: LanguageModelChatRequestOptions, token?: CancellationToken): Thenable; + + /** + * Count the number of tokens in a message using the model specific tokenizer-logic. + + * @param text A string or a message instance. + * @param token Optional cancellation token. See {@link CancellationTokenSource} for how to create one. + * @returns A thenable that resolves to the number of tokens. + */ + countTokens(text: string | LanguageModelChatMessage, token?: CancellationToken): Thenable; + } + + /** + * Describes how to select language models for chat requests. + * + * @see {@link lm.selectChatModels} + */ + export interface LanguageModelChatSelector { + + /** + * A vendor of language models. + * @see {@link LanguageModelChat.vendor} + */ + vendor?: string; + + /** + * A family of language models. + * @see {@link LanguageModelChat.family} + */ + family?: string; + + /** + * The version of a language model. + * @see {@link LanguageModelChat.version} + */ + version?: string; + + /** + * The identifier of a language model. + * @see {@link LanguageModelChat.id} + */ + id?: string; + } + + /** + * An error type for language model specific errors. + * + * Consumers of language models should check the code property to determine specific + * failure causes, like `if(someError.code === vscode.LanguageModelError.NotFound.name) {...}` + * for the case of referring to an unknown language model. For unspecified errors the `cause`-property + * will contain the actual error. + */ + export class LanguageModelError extends Error { + + /** + * The requestor does not have permissions to use this + * language model + */ + static NoPermissions(message?: string): LanguageModelError; + + /** + * The requestor is blocked from using this language model. + */ + static Blocked(message?: string): LanguageModelError; + + /** + * The language model does not exist. + */ + static NotFound(message?: string): LanguageModelError; + + /** + * A code that identifies this error. + * + * Possible values are names of errors, like {@linkcode LanguageModelError.NotFound NotFound}, + * or `Unknown` for unspecified errors from the language model itself. In the latter case the + * `cause`-property will contain the actual error. + */ + readonly code: string; + } + + /** + * Options for making a chat request using a language model. + * + * @see {@link LanguageModelChat.sendRequest} + */ + export interface LanguageModelChatRequestOptions { + + /** + * A human-readable message that explains why access to a language model is needed and what feature is enabled by it. + */ + justification?: string; + + /** + * A set of options that control the behavior of the language model. These options are specific to the language model + * and need to be lookup in the respective documentation. + */ + modelOptions?: { [name: string]: any }; + + /** + * An optional list of tools that are available to the language model. These could be registered tools available via + * {@link lm.tools}, or private tools that are just implemented within the calling extension. + * + * If the LLM requests to call one of these tools, it will return a {@link LanguageModelToolCallPart} in + * {@link LanguageModelChatResponse.stream}. It's the caller's responsibility to invoke the tool. If it's a tool + * registered in {@link lm.tools}, that means calling {@link lm.invokeTool}. + * + * Then, the tool result can be provided to the LLM by creating an Assistant-type {@link LanguageModelChatMessage} with a + * {@link LanguageModelToolCallPart}, followed by a User-type message with a {@link LanguageModelToolResultPart}. + */ + tools?: LanguageModelChatTool[]; + + /** + * The tool-selecting mode to use. {@link LanguageModelChatToolMode.Auto} by default. + */ + toolMode?: LanguageModelChatToolMode; + } + + /** + * Namespace for language model related functionality. + */ + export namespace lm { + + /** + * An event that is fired when the set of available chat models changes. + */ + export const onDidChangeChatModels: Event; + + /** + * Select chat models by a {@link LanguageModelChatSelector selector}. This can yield multiple or no chat models and + * extensions must handle these cases, esp. when no chat model exists, gracefully. + * + * ```ts + * const models = await vscode.lm.selectChatModels({ family: 'gpt-3.5-turbo' }); + * if (models.length > 0) { + * const [first] = models; + * const response = await first.sendRequest(...) + * // ... + * } else { + * // NO chat models available + * } + * ``` + * + * A selector can be written to broadly match all models of a given vendor or family, or it can narrowly select one model by ID. + * Keep in mind that the available set of models will change over time, but also that prompts may perform differently in + * different models. + * + * *Note* that extensions can hold on to the results returned by this function and use them later. However, when the + * {@link onDidChangeChatModels}-event is fired the list of chat models might have changed and extensions should re-query. + * + * @param selector A chat model selector. When omitted all chat models are returned. + * @returns An array of chat models, can be empty! + */ + export function selectChatModels(selector?: LanguageModelChatSelector): Thenable; + + /** + * Register a LanguageModelTool. The tool must also be registered in the package.json `languageModelTools` contribution + * point. A registered tool is available in the {@link lm.tools} list for any extension to see. But in order for it to + * be seen by a language model, it must be passed in the list of available tools in {@link LanguageModelChatRequestOptions.tools}. + * @returns A {@link Disposable} that unregisters the tool when disposed. + */ + export function registerTool(name: string, tool: LanguageModelTool): Disposable; + + /** + * A list of all available tools that were registered by all extensions using {@link lm.registerTool}. They can be called + * with {@link lm.invokeTool} with input that match their declared `inputSchema`. + */ + export const tools: readonly LanguageModelToolInformation[]; + + /** + * Invoke a tool listed in {@link lm.tools} by name with the given input. The input will be validated against + * the schema declared by the tool + * + * A tool can be invoked by a chat participant, in the context of handling a chat request, or globally by any extension in + * any custom flow. + * + * In the former case, the caller shall pass the + * {@link LanguageModelToolInvocationOptions.toolInvocationToken toolInvocationToken}, which comes with the a + * {@link ChatRequest.toolInvocationToken chat request}. This makes sure the chat UI shows the tool invocation for the + * correct conversation. + * + * A tool {@link LanguageModelToolResult result} is an array of {@link LanguageModelTextPart text-} and + * {@link LanguageModelPromptTsxPart prompt-tsx}-parts. If the tool caller is using `@vscode/prompt-tsx`, it can + * incorporate the response parts into its prompt using a `ToolResult`. If not, the parts can be passed along to the + * {@link LanguageModelChat} via a user message with a {@link LanguageModelToolResultPart}. + * + * If a chat participant wants to preserve tool results for requests across multiple turns, it can store tool results in + * the {@link ChatResult.metadata} returned from the handler and retrieve them on the next turn from + * {@link ChatResponseTurn.result}. + * + * @param name The name of the tool to call. + * @param options The options to use when invoking the tool. + * @param token A cancellation token. See {@link CancellationTokenSource} for how to create one. + * @returns The result of the tool invocation. + */ + export function invokeTool(name: string, options: LanguageModelToolInvocationOptions, token?: CancellationToken): Thenable; + } + + /** + * Represents extension specific information about the access to language models. + */ + export interface LanguageModelAccessInformation { + + /** + * An event that fires when access information changes. + */ + onDidChange: Event; + + /** + * Checks if a request can be made to a language model. + * + * *Note* that calling this function will not trigger a consent UI but just checks for a persisted state. + * + * @param chat A language model chat object. + * @return `true` if a request can be made, `false` if not, `undefined` if the language + * model does not exist or consent hasn't been asked for. + */ + canSendRequest(chat: LanguageModelChat): boolean | undefined; + } + + /** + * A tool that is available to the language model via {@link LanguageModelChatRequestOptions}. A language model uses all the + * properties of this interface to decide which tool to call, and how to call it. + */ + export interface LanguageModelChatTool { + /** + * The name of the tool. + */ + name: string; + + /** + * The description of the tool. + */ + description: string; + + /** + * A JSON schema for the input this tool accepts. + */ + inputSchema?: object; + } + + /** + * A tool-calling mode for the language model to use. + */ + export enum LanguageModelChatToolMode { + /** + * The language model can choose to call a tool or generate a message. Is the default. + */ + Auto = 1, + + /** + * The language model must call one of the provided tools. Note- some models only support a single tool when using this + * mode. + */ + Required = 2 + } + + /** + * A language model response part indicating a tool call, returned from a {@link LanguageModelChatResponse}, and also can be + * included as a content part on a {@link LanguageModelChatMessage}, to represent a previous tool call in a chat request. + */ + export class LanguageModelToolCallPart { + /** + * The ID of the tool call. This is a unique identifier for the tool call within the chat request. + */ + callId: string; + + /** + * The name of the tool to call. + */ + name: string; + + /** + * The input with which to call the tool. + */ + input: object; + + /** + * Create a new LanguageModelToolCallPart. + * + * @param callId The ID of the tool call. + * @param name The name of the tool to call. + * @param input The input with which to call the tool. + */ + constructor(callId: string, name: string, input: object); + } + + /** + * The result of a tool call. This is the counterpart of a {@link LanguageModelToolCallPart tool call} and + * it can only be included in the content of a User message + */ + export class LanguageModelToolResultPart { + /** + * The ID of the tool call. + * + * *Note* that this should match the {@link LanguageModelToolCallPart.callId callId} of a tool call part. + */ + callId: string; + + /** + * The value of the tool result. + */ + content: Array; + + /** + * @param callId The ID of the tool call. + * @param content The content of the tool result. + */ + constructor(callId: string, content: Array); + } + + /** + * A language model response part containing a piece of text, returned from a {@link LanguageModelChatResponse}. + */ + export class LanguageModelTextPart { + /** + * The text content of the part. + */ + value: string; + + /** + * Construct a text part with the given content. + * @param value The text content of the part. + */ + constructor(value: string); + } + + /** + * A language model response part containing a PromptElementJSON from `@vscode/prompt-tsx`. + * @see {@link LanguageModelToolResult} + */ + export class LanguageModelPromptTsxPart { + /** + * The value of the part. + */ + value: unknown; + + /** + * Construct a prompt-tsx part with the given content. + * @param value The value of the part, the result of `renderPromptElementJSON` from `@vscode/prompt-tsx`. + */ + constructor(value: unknown); + } + + /** + * A result returned from a tool invocation. If using `@vscode/prompt-tsx`, this result may be rendered using a `ToolResult`. + */ + export class LanguageModelToolResult { + /** + * A list of tool result content parts. Includes `unknown` becauses this list may be extended with new content types in + * the future. + * @see {@link lm.invokeTool}. + */ + content: Array; + + /** + * Create a LanguageModelToolResult + * @param content A list of tool result content parts + */ + constructor(content: Array); + } + + /** + * A token that can be passed to {@link lm.invokeTool} when invoking a tool inside the context of handling a chat request. + */ + export type ChatParticipantToolToken = never; + + /** + * Options provided for tool invocation. + */ + export interface LanguageModelToolInvocationOptions { + /** + * An opaque object that ties a tool invocation to a chat request from a {@link ChatParticipant chat participant}. + * + * The _only_ way to get a valid tool invocation token is using the provided {@link ChatRequest.toolInvocationToken toolInvocationToken} + * from a chat request. In that case, a progress bar will be automatically shown for the tool invocation in the chat response view, and if + * the tool requires user confirmation, it will show up inline in the chat view. + * + * If the tool is being invoked outside of a chat request, `undefined` should be passed instead, and no special UI except for + * confirmations will be shown. + * + * *Note* that a tool that invokes another tool during its invocation, can pass along the `toolInvocationToken` that it received. + */ + toolInvocationToken: ChatParticipantToolToken | undefined; + + /** + * The input with which to invoke the tool. The input must match the schema defined in + * {@link LanguageModelToolInformation.inputSchema} + */ + input: T; + + /** + * Options to hint at how many tokens the tool should return in its response, and enable the tool to count tokens + * accurately. + */ + tokenizationOptions?: LanguageModelToolTokenizationOptions; + } + + /** + * Options related to tokenization for a tool invocation. + */ + export interface LanguageModelToolTokenizationOptions { + /** + * If known, the maximum number of tokens the tool should emit in its result. + */ + tokenBudget: number; + + /** + * Count the number of tokens in a message using the model specific tokenizer-logic. + * @param text A string. + * @param token Optional cancellation token. See {@link CancellationTokenSource} for how to create one. + * @returns A thenable that resolves to the number of tokens. + */ + countTokens(text: string, token?: CancellationToken): Thenable; + } + + /** + * Information about a registered tool available in {@link lm.tools}. + */ + export interface LanguageModelToolInformation { + /** + * A unique name for the tool. + */ + readonly name: string; + + /** + * A description of this tool that may be passed to a language model. + */ + readonly description: string; + + /** + * A JSON schema for the input this tool accepts. + */ + readonly inputSchema: object | undefined; + + /** + * A set of tags, declared by the tool, that roughly describe the tool's capabilities. A tool user may use these to filter + * the set of tools to just ones that are relevant for the task at hand. + */ + readonly tags: readonly string[]; + } + + /** + * Options for {@link LanguageModelTool.prepareInvocation}. + */ + export interface LanguageModelToolInvocationPrepareOptions { + /** + * The input that the tool is being invoked with. + */ + input: T; + } + + /** + * A tool that can be invoked by a call to a {@link LanguageModelChat}. + */ + export interface LanguageModelTool { + /** + * Invoke the tool with the given input and return a result. + * + * The provided {@link LanguageModelToolInvocationOptions.input} has been validated against the declared schema. + */ + invoke(options: LanguageModelToolInvocationOptions, token: CancellationToken): ProviderResult; + + /** + * Called once before a tool is invoked. It's recommended to implement this to customize the progress message that appears + * while the tool is running, and to provide a more useful message with context from the invocation input. Can also + * signal that a tool needs user confirmation before running, if appropriate. + * + * * *Note 1:* Must be free of side-effects. + * * *Note 2:* A call to `prepareInvocation` is not necessarily followed by a call to `invoke`. + */ + prepareInvocation?(options: LanguageModelToolInvocationPrepareOptions, token: CancellationToken): ProviderResult; + } + + /** + * When this is returned in {@link PreparedToolInvocation}, the user will be asked to confirm before running the tool. These + * messages will be shown with buttons that say "Continue" and "Cancel". + */ + export interface LanguageModelToolConfirmationMessages { + /** + * The title of the confirmation message. + */ + title: string; + + /** + * The body of the confirmation message. + */ + message: string | MarkdownString; + } + + /** + * The result of a call to {@link LanguageModelTool.prepareInvocation}. + */ + export interface PreparedToolInvocation { + /** + * A customized progress message to show while the tool runs. + */ + invocationMessage?: string | MarkdownString; + + /** + * The presence of this property indicates that the user should be asked to confirm before running the tool. The user + * should be asked for confirmation for any tool that has a side-effect or may potentially be dangerous. + */ + confirmationMessages?: LanguageModelToolConfirmationMessages; + } + + /** + * A reference to a tool that the user manually attached to their request, either using the `#`-syntax inline, or as an + * attachment via the paperclip button. + */ + export interface ChatLanguageModelToolReference { + /** + * The tool name. Refers to a tool listed in {@link lm.tools}. + */ + readonly name: string; + + /** + * The start and end index of the reference in the {@link ChatRequest.prompt prompt}. When undefined, the reference was + * not part of the prompt text. + * + * *Note* that the indices take the leading `#`-character into account which means they can be used to modify the prompt + * as-is. + */ + readonly range?: [start: number, end: number]; + } +} + +/** + * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, + * and others. This API makes no assumption about what promise library is being used which + * enables reusing existing code without migrating to a specific promise implementation. Still, + * we recommend the use of native promises which are available in this editor. + */ +interface Thenable extends PromiseLike { } diff --git a/code/extensions/js-debug/src/typings/vscode.proposed.browser.d.ts b/code/extensions/js-debug/src/typings/vscode.proposed.browser.d.ts new file mode 100644 index 000000000000..b81b6955c299 --- /dev/null +++ b/code/extensions/js-debug/src/typings/vscode.proposed.browser.d.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // @kycutler https://github.com/microsoft/vscode/issues/300319 + + /** + * An integrated browser page displayed in an editor tab. + */ + export interface BrowserTab { + /** The current URL of the page. */ + readonly url: string; + + /** The current page title. */ + readonly title: string; + + /** The page icon (favicon or a default globe icon). */ + readonly icon: IconPath; + + /** Create a new CDP session that exposes this browser tab. */ + startCDPSession(): Thenable; + } + + /** + * A CDP (Chrome DevTools Protocol) session that provides a bidirectional message channel. + * + * Create a session via {@link BrowserTab.startCDPSession}. + */ + export interface BrowserCDPSession { + /** Fires when a CDP message is received from an attached target. */ + readonly onDidReceiveMessage: Event; + + /** Fires when this session is closed. */ + readonly onDidClose: Event; + + /** Send a CDP request message to an attached target. */ + sendMessage(message: unknown): Thenable; + + /** Close this session and detach all targets. */ + close(): Thenable; + } + + /** Options for {@link window.openBrowserTab}. */ + export interface BrowserTabShowOptions { + /** + * The view column to show the browser in. Defaults to {@link ViewColumn.Active}. + * Use {@linkcode ViewColumn.Beside} to open next to the current editor. + */ + viewColumn?: ViewColumn; + + /** When `true`, the browser tab will not take focus. */ + preserveFocus?: boolean; + + /** When `true`, the browser tab will open in the background. */ + background?: boolean; + } + + export namespace window { + /** The currently open browser tabs. */ + export const browserTabs: readonly BrowserTab[]; + + /** Fires when a browser tab is opened. */ + export const onDidOpenBrowserTab: Event; + + /** Fires when a browser tab is closed. */ + export const onDidCloseBrowserTab: Event; + + /** The currently active browser tab. */ + export const activeBrowserTab: BrowserTab | undefined; + + /** Fires when {@link activeBrowserTab} changes. */ + export const onDidChangeActiveBrowserTab: Event; + + /** Fires when a browser tab's state (url, title, or icon) changes. */ + export const onDidChangeBrowserTabState: Event; + + /** + * Open a browser tab at the given URL. + * + * @param url The URL to navigate to. + * @param options Controls where and how the browser tab is shown. + * @returns The {@link BrowserTab} representing the opened page. + */ + export function openBrowserTab(url: string, options?: BrowserTabShowOptions): Thenable; + } +} diff --git a/code/extensions/js-debug/src/typings/vscode.proposed.portsAttributes.d.ts b/code/extensions/js-debug/src/typings/vscode.proposed.portsAttributes.d.ts new file mode 100644 index 000000000000..e3cb4b617f96 --- /dev/null +++ b/code/extensions/js-debug/src/typings/vscode.proposed.portsAttributes.d.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/115616 @alexr00 + + /** + * The action that should be taken when a port is discovered through automatic port forwarding discovery. + */ + export enum PortAutoForwardAction { + /** + * Notify the user that the port is being forwarded. This is the default action. + */ + Notify = 1, + /** + * Once the port is forwarded, open the user's web browser to the forwarded port. + */ + OpenBrowser = 2, + /** + * Once the port is forwarded, open the preview browser to the forwarded port. + */ + OpenPreview = 3, + /** + * Forward the port silently. + */ + Silent = 4, + /** + * Do not forward the port. + */ + Ignore = 5 + } + + /** + * The attributes that a forwarded port can have. + */ + export class PortAttributes { + /** + * The action to be taken when this port is detected for auto forwarding. + */ + autoForwardAction: PortAutoForwardAction; + + /** + * Creates a new PortAttributes object + * @param port the port number + * @param autoForwardAction the action to take when this port is detected + */ + constructor(autoForwardAction: PortAutoForwardAction); + } + + /** + * A provider of port attributes. Port attributes are used to determine what action should be taken when a port is discovered. + */ + export interface PortAttributesProvider { + /** + * Provides attributes for the given port. For ports that your extension doesn't know about, simply + * return undefined. For example, if `providePortAttributes` is called with ports 3000 but your + * extension doesn't know anything about 3000 you should return undefined. + * @param port The port number of the port that attributes are being requested for. + * @param pid The pid of the process that is listening on the port. If the pid is unknown, undefined will be passed. + * @param commandLine The command line of the process that is listening on the port. If the command line is unknown, undefined will be passed. + * @param token A cancellation token that indicates the result is no longer needed. + */ + providePortAttributes(attributes: { port: number; pid?: number; commandLine?: string }, token: CancellationToken): ProviderResult; + } + + /** + * A selector that will be used to filter which {@link PortAttributesProvider} should be called for each port. + */ + export interface PortAttributesSelector { + /** + * Specifying a port range will cause your provider to only be called for ports within the range. + * The start is inclusive and the end is exclusive. + */ + portRange?: [number, number] | number; + + /** + * Specifying a command pattern will cause your provider to only be called for processes whose command line matches the pattern. + */ + commandPattern?: RegExp; + } + + export namespace workspace { + /** + * If your extension listens on ports, consider registering a PortAttributesProvider to provide information + * about the ports. For example, a debug extension may know about debug ports in it's debuggee. By providing + * this information with a PortAttributesProvider the extension can tell the editor that these ports should be + * ignored, since they don't need to be user facing. + * + * The results of the PortAttributesProvider are merged with the user setting `remote.portsAttributes`. If the values conflict, the user setting takes precedence. + * + * @param portSelector It is best practice to specify a port selector to avoid unnecessary calls to your provider. + * If you don't specify a port selector your provider will be called for every port, which will result in slower port forwarding for the user. + * @param provider The {@link PortAttributesProvider PortAttributesProvider}. + */ + export function registerPortAttributesProvider(portSelector: PortAttributesSelector, provider: PortAttributesProvider): Disposable; + } +} diff --git a/code/extensions/js-debug/src/typings/vscode.proposed.tunnels.d.ts b/code/extensions/js-debug/src/typings/vscode.proposed.tunnels.d.ts new file mode 100644 index 000000000000..1f83bbbeb909 --- /dev/null +++ b/code/extensions/js-debug/src/typings/vscode.proposed.tunnels.d.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // tunnels @alexr00 + + export interface TunnelOptions { + remoteAddress: { port: number; host: string }; + // The desired local port. If this port can't be used, then another will be chosen. + localAddressPort?: number; + label?: string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; + privacy?: string; + protocol?: string; + } + + export interface TunnelDescription { + remoteAddress: { port: number; host: string }; + //The complete local address(ex. localhost:1234) + localAddress: { port: number; host: string } | string; + /** + * @deprecated Use privacy instead + */ + public?: boolean; + privacy?: string; + // If protocol is not provided it is assumed to be http, regardless of the localAddress. + protocol?: string; + } + + export interface Tunnel extends TunnelDescription { + // Implementers of Tunnel should fire onDidDispose when dispose is called. + onDidDispose: Event; + dispose(): void | Thenable; + } + + export namespace workspace { + /** + * Forwards a port. If the current resolver implements RemoteAuthorityResolver:forwardPort then that will be used to make the tunnel. + * By default, openTunnel only support localhost; however, RemoteAuthorityResolver:tunnelFactory can be used to support other ips. + * + * @throws When run in an environment without a remote. + * + * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. + */ + export function openTunnel(tunnelOptions: TunnelOptions): Thenable; + + /** + * Gets an array of the currently available tunnels. This does not include environment tunnels, only tunnels that have been created by the user. + * Note that these are of type TunnelDescription and cannot be disposed. + */ + export let tunnels: Thenable; + + /** + * Fired when the list of tunnels has changed. + */ + export const onDidChangeTunnels: Event; + } +} diff --git a/code/extensions/js-debug/src/typings/vscode.proposed.workspaceTrust.d.ts b/code/extensions/js-debug/src/typings/vscode.proposed.workspaceTrust.d.ts new file mode 100644 index 000000000000..d48071af2ccb --- /dev/null +++ b/code/extensions/js-debug/src/typings/vscode.proposed.workspaceTrust.d.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/120173 + + /** + * The object describing the properties of the workspace trust request + */ + export interface WorkspaceTrustRequestOptions { + /** + * Custom message describing the user action that requires workspace + * trust. If omitted, a generic message will be displayed in the workspace + * trust request dialog. + */ + readonly message?: string; + } + + export namespace workspace { + /** + * Prompt the user to chose whether to trust the current workspace + * @param options Optional object describing the properties of the + * workspace trust request. + */ + export function requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Thenable; + } +} diff --git a/code/extensions/js-debug/src/ui/autoAttach.ts b/code/extensions/js-debug/src/ui/autoAttach.ts new file mode 100644 index 000000000000..c8102faa51ab --- /dev/null +++ b/code/extensions/js-debug/src/ui/autoAttach.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +import * as l10n from '@vscode/l10n'; +import { Container } from 'inversify'; +import * as vscode from 'vscode'; +import { IPortLeaseTracker } from '../adapter/portLeaseTracker'; +import { Commands, Configuration, readConfig, registerCommand } from '../common/contributionUtils'; +import { ProxyLogger } from '../common/logging/proxyLogger'; +import { FS } from '../ioc-extras'; +import { DelegateLauncherFactory } from '../targets/delegate/delegateLauncherFactory'; +import { + AutoAttachLauncher, + AutoAttachPreconditionFailed, +} from '../targets/node/autoAttachLauncher'; +import { NodeBinaryProvider } from '../targets/node/nodeBinaryProvider'; +import { noPackageJsonProvider } from '../targets/node/packageJsonProvider'; +import { NodeOnlyPathResolverFactory } from '../targets/sourcePathResolverFactory'; +import { launchVirtualTerminalParent } from './debugTerminalUI'; + +export function registerAutoAttach( + context: vscode.ExtensionContext, + delegate: DelegateLauncherFactory, + services: Container, +) { + const launchers = new Map>(); + let disposeTimeout: NodeJS.Timeout | undefined; + + const acquireLauncher = (workspaceFolder: vscode.WorkspaceFolder | undefined) => { + const prev = launchers.get(workspaceFolder); + if (prev) { + return prev; + } + + const launcher = (async () => { + const logger = new ProxyLogger(); + let config = readConfig(vscode.workspace, Configuration.TerminalDebugConfig); + if (workspaceFolder) { + const fsPath = workspaceFolder?.uri.fsPath; + config = { ...config, cwd: fsPath, __workspaceFolder: fsPath }; + } + // TODO: Figure out how to inject FsUtils + const inst = new AutoAttachLauncher( + new NodeBinaryProvider(logger, services.get(FS), noPackageJsonProvider, config || {}), + logger, + context, + services.get(FS), + services.get(NodeOnlyPathResolverFactory), + services.get(IPortLeaseTracker), + ); + + await launchVirtualTerminalParent(delegate, inst, config); + + inst.onTargetListChanged(() => { + if (inst.targetList().length === 0 && !disposeTimeout) { + disposeTimeout = setTimeout(() => { + launchers.delete(workspaceFolder); + inst.terminate(); + }, 5 * 60 * 1000); + } else if (disposeTimeout) { + clearTimeout(disposeTimeout); + disposeTimeout = undefined; + } + }); + + return inst; + })(); + + launchers.set(workspaceFolder, launcher); + + return launcher; + }; + + context.subscriptions.push( + registerCommand(vscode.commands, Commands.AutoAttachSetVariables, async () => { + try { + const launcher = await acquireLauncher(vscode.workspace.workspaceFolders?.[0]); + return { ipcAddress: launcher.deferredSocketName as string }; + } catch (e) { + if (e instanceof AutoAttachPreconditionFailed && e.helpLink) { + const details = l10n.t('Details'); + if ((await vscode.window.showErrorMessage(e.message, details)) === details) { + vscode.env.openExternal(vscode.Uri.parse(e.helpLink)); + } + } else { + await vscode.window.showErrorMessage(e.message); + } + } + }), + registerCommand(vscode.commands, Commands.AutoAttachToProcess, async info => { + try { + const wf = info.scriptName + && vscode.workspace.getWorkspaceFolder(vscode.Uri.file(info.scriptName)); + const launcher = await acquireLauncher(wf || vscode.workspace.workspaceFolders?.[0]); + launcher.spawnForChild(info); + } catch (err) { + console.error(err); + vscode.window.showErrorMessage(`Error activating auto attach: ${err.stack || err}`); + } + }), + registerCommand(vscode.commands, Commands.AutoAttachClearVariables, () => { + AutoAttachLauncher.clearVariables(context); + + for (const [key, value] of launchers.entries()) { + launchers.delete(key); + value.then(v => v.terminate()); + } + }), + ); +} diff --git a/code/extensions/js-debug/src/ui/basic-wat.configuration.json b/code/extensions/js-debug/src/ui/basic-wat.configuration.json new file mode 100644 index 000000000000..4f0f2966ddbd --- /dev/null +++ b/code/extensions/js-debug/src/ui/basic-wat.configuration.json @@ -0,0 +1,17 @@ +{ + "comments": { + "lineComment": ";;", + "blockComment": ["(; ", " ;)"] + }, + "brackets": [ + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"" } + ], + "surroundingPairs": [ + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"" } + ] +} diff --git a/code/extensions/js-debug/src/ui/basic-wat.tmLanguage.json b/code/extensions/js-debug/src/ui/basic-wat.tmLanguage.json new file mode 100644 index 000000000000..fe1f3b59408e --- /dev/null +++ b/code/extensions/js-debug/src/ui/basic-wat.tmLanguage.json @@ -0,0 +1,93 @@ +{ + "name": "WebAssembly Text Format", + "scopeName": "text.wat", + "patterns": [ + { "include": "#block-comment" }, + { "include": "#line-comment" }, + { "include": "#expr" } + ], + "repository": { + "op": { + "match": "[a-zA-Z0-9!#$%&`*+\\-/:<=>?@\\\\^_|~\\.]+", + "name": "keyword" + }, + "id": { + "match": "\\$[A-Za-z0-9!#$%&`*+\\-/:<=>?@\\\\^_|~\\.]+", + "name": "variable" + }, + "decimal-number": { + "match": "\\b[+-]?[0-9_]+(.[0-9_]+)?([Ee][+-][0-9_]+)?\\b", + "name": "constant.numeric" + }, + "hexadecimal-number": { + "match": "\\b[+-]?0x[0-9a-fA-F_]+(.[0-9a-fA-F_]+)?([pP][+-][0-9a-fA-F_]+)?\\b", + "name": "constant.numeric" + }, + "number-special": { + "match": "\\b[+-]?(inf|nan(:0x[0-9]+)?)\\b", + "name": "constant.numeric" + }, + "memarg": { + "match": "\\b(offset|align)(=)([0-9_]+)\\b", + "name": "keyword", + "captures": { + "1": { "name": "keyword" }, + "2": { "name": "keyword.operator.arithmetic" }, + "3": { "name": "constant.numeric" } + } + }, + "any-number": { + "patterns": [ + { "include": "#decimal-number" }, + { "include": "#hexadecimal-number" }, + { "include": "#number-special" } + ] + }, + "types": { + "match": "\\b([if](32|64)|v128|funcref|externref|func|extern|func|param|result|mut)\\b", + "name": "keyword" + }, + "string": { + "begin": "\"", + "end": "\"", + "name": "string.quoted", + "patterns": [ + { + "name": "constant.character.escape", + "match": "\\\\[\"\\\\]" + } + ] + }, + "line-comment": { + "match": ";;.*$", + "name": "comment.line.double-semicolon" + }, + "block-comment": { + "begin": "\\(;", + "end": ";\\)", + "name": "comment.block" + }, + "expr": { + "begin": "\\(", + "end": "\\)", + "beginCaptures": { + "0": { "name": "punctuation.paren.open" } + }, + "endCaptures": { + "0": { "name": "punctuation.paren.close" } + }, + "name": "expression.group", + "patterns": [ + { "include": "#block-comment" }, + { "include": "$self" }, + { "include": "#types" }, + { "include": "#line-comment" }, + { "include": "#any-number" }, + { "include": "#memarg" }, + { "include": "#id" }, + { "include": "#string" }, + { "include": "#op" } + ] + } + } +} diff --git a/code/extensions/js-debug/src/ui/cascadeTerminateTracker.ts b/code/extensions/js-debug/src/ui/cascadeTerminateTracker.ts new file mode 100644 index 000000000000..45426fe649f2 --- /dev/null +++ b/code/extensions/js-debug/src/ui/cascadeTerminateTracker.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { IExtensionContribution } from '../ioc-extras'; +import { DebugSessionTracker } from './debugSessionTracker'; + +/** + * Watches for sessions to be terminated. When they are, it runs cascading + * termination if configured. + */ +@injectable() +export class CascadeTerminationTracker implements IExtensionContribution { + constructor(@inject(DebugSessionTracker) private readonly tracker: DebugSessionTracker) {} + + /** + * Registers the tracker for the extension. + */ + public register(context: vscode.ExtensionContext) { + context.subscriptions.push( + this.tracker.onSessionEnded(session => { + const targets: string[] = session.configuration.cascadeTerminateToConfigurations; + if (!targets || !(targets instanceof Array)) { + return; // may be a nested session + } + + for (const configName of targets) { + for (const session of this.tracker.getByName(configName)) { + vscode.debug.stopDebugging(session); + } + } + }), + ); + } +} diff --git a/code/extensions/js-debug/src/ui/companionBrowserLaunch.ts b/code/extensions/js-debug/src/ui/companionBrowserLaunch.ts new file mode 100644 index 000000000000..f53457ea5494 --- /dev/null +++ b/code/extensions/js-debug/src/ui/companionBrowserLaunch.ts @@ -0,0 +1,114 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { URL } from 'url'; +import * as vscode from 'vscode'; +import { Configuration, readConfig } from '../common/contributionUtils'; +import Dap from '../dap/api'; +import { DebugSessionTunnels } from './debugSessionTunnels'; + +const isTunnelForPort = (port: number) => (tunnel: vscode.TunnelDescription) => + typeof tunnel.localAddress === 'string' + ? tunnel.localAddress.endsWith(`:${port}`) + : tunnel.localAddress.port === port; + +const tunnelRemoteServerIfNecessary = async (args: Dap.LaunchBrowserInCompanionEventParams) => { + const urlStr = (args.params as { url?: string }).url; + if (!urlStr) { + return; + } + + let url: URL; + try { + url = new URL(urlStr); + } catch (e) { + return; + } + + if (!readConfig(vscode.workspace, Configuration.AutoServerTunnelOpen)) { + return; + } + + const port = Number(url.port) || 80; + const tunnels = await vscode.workspace.tunnels; + if (tunnels.some(isTunnelForPort(port))) { + return; + } + + try { + await vscode.workspace.openTunnel({ + remoteAddress: { port, host: 'localhost' }, + localAddressPort: port, + }); + } catch { + // throws if already forwarded by user or by us previously + } +}; + +const launchCompanionBrowser = async ( + session: vscode.DebugSession, + sessionTunnels: DebugSessionTunnels, + args: Dap.LaunchBrowserInCompanionEventParams, +) => { + if (vscode.env.uiKind === vscode.UIKind.Web) { + vscode.debug.stopDebugging(session); + return vscode.window.showErrorMessage( + l10n.t( + "We can't launch a browser in debug mode from here. Open this workspace in VS Code on your desktop to enable debugging.", + ), + ); + } + + try { + const [, tunnel] = await Promise.all([ + tunnelRemoteServerIfNecessary(args), + sessionTunnels + .request(session.id, { + remotePort: args.serverPort, + label: 'Browser Debug Tunnel', + }) + .catch(() => undefined), + ]); + + await vscode.commands.executeCommand('js-debug-companion.launchAndAttach', { + proxyUri: tunnel ? `127.0.0.1:${tunnel.localAddress.port}` : `127.0.0.1:${args.serverPort}`, + wslInfo: process.env.WSL_DISTRO_NAME && { + execPath: process.execPath, + distro: process.env.WSL_DISTRO_NAME, + user: process.env.USER, + }, + ...args, + }); + } catch (e) { + vscode.window.showErrorMessage(`Error launching browser: ${e.message || e.stack}`); + } +}; + +const killCompanionBrowser = async ( + session: vscode.DebugSession, + tunnels: DebugSessionTunnels, + { launchId }: Dap.KillCompanionBrowserEventParams, +) => { + await vscode.commands.executeCommand('js-debug-companion.kill', { launchId }); + tunnels.destroySession(session.id); +}; + +export function registerCompanionBrowserLaunch(context: vscode.ExtensionContext) { + const tunnels = new DebugSessionTunnels(); + + context.subscriptions.push( + tunnels, + vscode.debug.onDidReceiveDebugSessionCustomEvent(async event => { + switch (event.event) { + case 'launchBrowserInCompanion': + return launchCompanionBrowser(event.session, tunnels, event.body); + case 'killCompanionBrowser': + return killCompanionBrowser(event.session, tunnels, event.body); + default: + // ignored + } + }), + ); +} diff --git a/code/extensions/js-debug/src/ui/configuration/baseConfigurationProvider.ts b/code/extensions/js-debug/src/ui/configuration/baseConfigurationProvider.ts new file mode 100644 index 000000000000..13f39dd76a96 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/baseConfigurationProvider.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { preferredDebugTypes } from '../../common/contributionUtils'; +import { AnyLaunchConfiguration, ResolvingConfiguration } from '../../configuration'; +import { IDebugConfigurationProvider } from './configurationProvider'; + +/** + * Base configuration provider that handles some resolution around common + * options and handles errors. + */ +@injectable() +export abstract class BaseConfigurationProvider + implements IDebugConfigurationProvider +{ + /** + * @inheritdoc + */ + public get type() { + return this.getType(); + } + + /** + * @inheritdoc + */ + public get triggerKind() { + return this.getTriggerKind(); + } + + public async provideDebugConfigurations( + folder: vscode.WorkspaceFolder | undefined, + token?: vscode.CancellationToken, + ): Promise { + try { + const r = await this.provide(folder, token); + if (!r) { + return []; + } + + const configs = r instanceof Array ? r : [r]; + const preferredType = preferredDebugTypes.get(this.type); + if (preferredType) { + for (const config of configs) { + if (config.type === this.type) { + config.type = preferredType as T['type']; + } + } + } + + return configs; + } catch (err) { + vscode.window.showErrorMessage(err.message, { modal: true }); + return []; + } + } + + protected abstract getType(): T['type']; + + protected abstract getTriggerKind(): vscode.DebugConfigurationProviderTriggerKind; + + protected abstract provide( + folder: vscode.WorkspaceFolder | undefined, + token?: vscode.CancellationToken, + ): + | Promise[] | ResolvingConfiguration> + | ResolvingConfiguration[] + | ResolvingConfiguration; +} diff --git a/code/extensions/js-debug/src/ui/configuration/baseConfigurationResolver.ts b/code/extensions/js-debug/src/ui/configuration/baseConfigurationResolver.ts new file mode 100644 index 000000000000..fa8791f7f8b5 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/baseConfigurationResolver.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import { isAbsolute } from 'path'; +import * as vscode from 'vscode'; +import { Configuration, DebugType, readConfig } from '../../common/contributionUtils'; +import { fulfillLoggerOptions } from '../../common/logging'; +import { truthy } from '../../common/objUtils'; +import { + AnyLaunchConfiguration, + removeOptionalWorkspaceFolderUsages, + resolveWorkspaceInConfig, + ResolvingConfiguration, +} from '../../configuration'; +import { ExtensionContext } from '../../ioc-extras'; +import { sourceMapSteppingEnabled } from '../sourceSteppingUI'; +import { IDebugConfigurationResolver } from './configurationProvider'; + +/** + * Base configuration provider that handles some resolution around common + * options and handles errors. + */ +@injectable() +export abstract class BaseConfigurationResolver + implements IDebugConfigurationResolver +{ + /** + * @inheritdoc + */ + public get type() { + return this.getType(); + } + + constructor( + @inject(ExtensionContext) protected readonly extensionContext: vscode.ExtensionContext, + ) {} + + /** + * @inheritdoc + */ + public async resolveDebugConfiguration( + folder: vscode.WorkspaceFolder | undefined, + config: vscode.DebugConfiguration, + token?: vscode.CancellationToken, + ): Promise { + if (config.type) { + config.type = this.getType(); // ensure type is set for aliased configurations + } + + if ('__pendingTargetId' in config) { + return config as T; + } + + const castConfig = config as ResolvingConfiguration; + castConfig.sourceMaps ??= sourceMapSteppingEnabled.read(this.extensionContext.workspaceState); + + try { + const resolved = await this.resolveDebugConfigurationAsync(folder, castConfig, token); + return resolved && this.commonResolution(resolved, folder); + } catch (err) { + vscode.window.showErrorMessage(err.message, { modal: true }); + } + } + + /** + * Gets the default runtime executable for the type, if configured. + */ + protected applyDefaultRuntimeExecutable(cfg: { + type: DebugType; + runtimeExecutable?: string | null; + }) { + if (cfg.runtimeExecutable) { + return; + } + + const allDefaults = readConfig(vscode.workspace, Configuration.DefaultRuntimeExecutables); + const defaultValue = allDefaults ? allDefaults[cfg.type] : undefined; + if (defaultValue) { + cfg.runtimeExecutable = defaultValue; + } + } + + /** + * Resolves the configuration for the debug adapter. + */ + protected abstract resolveDebugConfigurationAsync( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingConfiguration, + token?: vscode.CancellationToken, + ): Promise; + + /** + * Fulfills resolution common between all resolver configs. + */ + protected commonResolution(config: T, folder: vscode.WorkspaceFolder | undefined): T { + config.trace = fulfillLoggerOptions(config.trace, this.extensionContext.logPath); + config.__workspaceCachePath = this.extensionContext.storagePath; + config.__breakOnConditionalError = + readConfig(vscode.workspace, Configuration.BreakOnConditionalError, folder) ?? false; + + if (folder) { + // all good, we know the VS Code will resolve the workspace + config.__workspaceFolder = folder.uri.fsPath; + } else { + // otherwise, try to manually figure out an appropriate __workspaceFolder + // if we don't already have it. + config.__workspaceFolder ||= this.getSuggestedWorkspaceFolders(config) + .filter(truthy) + .filter(f => !f.includes('${workspaceFolder}')) + .map(f => + isAbsolute(f) ? vscode.workspace.getWorkspaceFolder(vscode.Uri.file(f))?.uri.fsPath : f + ) + .find(truthy) + || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath + || ''; + + // If we found it, replace appropriately. Otherwise remove the 'optional' + // usages, there's a chance we can still make it work. + if (config.__workspaceFolder) { + config = resolveWorkspaceInConfig(config); + } else { + config = removeOptionalWorkspaceFolderUsages(config); + } + } + + return config; + } + + /** + * Gets a list of folders that might be workspace folders, if we need to + * resolve them. This lets users set _a_ folder to be the right folder in + * a multi-root configuration, without having to manually override every default. + * @see https://github.com/microsoft/vscode-js-debug/issues/525 + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected getSuggestedWorkspaceFolders(_config: T): (string | undefined)[] { + return []; + } + + /** + * Gets the type for this debug configuration. + */ + protected abstract getType(): T['type']; +} diff --git a/code/extensions/js-debug/src/ui/configuration/chromeDebugConfigurationProvider.ts b/code/extensions/js-debug/src/ui/configuration/chromeDebugConfigurationProvider.ts new file mode 100644 index 000000000000..b34004fc7e7f --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/chromeDebugConfigurationProvider.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { + AnyChromeConfiguration, + chromeAttachConfigDefaults, + chromeLaunchConfigDefaults, + IChromeLaunchConfiguration, + ResolvingChromeConfiguration, +} from '../../configuration'; +import { + ChromiumDebugConfigurationProvider, + ChromiumDebugConfigurationResolver, +} from './chromiumDebugConfigurationProvider'; + +/** + * Configuration provider for Chrome debugging. + */ +@injectable() +export class ChromeDebugConfigurationResolver + extends ChromiumDebugConfigurationResolver + implements vscode.DebugConfigurationProvider +{ + /** + * @override + */ + protected async resolveDebugConfigurationAsync( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingChromeConfiguration, + ): Promise { + if (!config.name && !config.type && !config.request) { + const fromContext = new ChromeDebugConfigurationProvider().createLaunchConfigFromContext(); + if (!fromContext) { + // Return null so it will create a launch.json and fall back on + // provideDebugConfigurations - better to point the user towards + // the config than try to work automagically for complex scenarios. + return null; + } + + config = fromContext; + } + + await this.resolveBrowserCommon(folder, config); + + return config.request === 'attach' + ? { ...chromeAttachConfigDefaults, ...config } + : { ...chromeLaunchConfigDefaults, ...config }; + } + + protected getType() { + return DebugType.Chrome as const; + } +} + +@injectable() +export class ChromeDebugConfigurationProvider + extends ChromiumDebugConfigurationProvider +{ + protected getType() { + return DebugType.Chrome as const; + } +} diff --git a/code/extensions/js-debug/src/ui/configuration/chromiumDebugConfigurationProvider.ts b/code/extensions/js-debug/src/ui/configuration/chromiumDebugConfigurationProvider.ts new file mode 100644 index 000000000000..fa06667c5a32 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/chromiumDebugConfigurationProvider.ts @@ -0,0 +1,235 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import { tmpdir } from 'os'; +import { basename, join } from 'path'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { isPortOpen } from '../../common/findOpenPort'; +import { existsWithoutDeref } from '../../common/fsUtils'; +import { some } from '../../common/promiseUtil'; +import { + AnyChromiumConfiguration, + AnyChromiumLaunchConfiguration, + IChromiumAttachConfiguration, + IChromiumLaunchConfiguration, + INodeLaunchConfiguration, + ResolvingConfiguration, +} from '../../configuration'; +import { ExtensionContext, ExtensionLocation, FS, FsPromises } from '../../ioc-extras'; +import { BaseConfigurationProvider } from './baseConfigurationProvider'; +import { BaseConfigurationResolver } from './baseConfigurationResolver'; +import { NodeConfigurationResolver } from './nodeDebugConfigurationResolver'; +import { TerminalDebugConfigurationResolver } from './terminalDebugConfigurationResolver'; + +const isLaunch = ( + value: ResolvingConfiguration, +): value is ResolvingConfiguration => value.request === 'launch'; + +const isAttach = ( + value: ResolvingConfiguration, +): value is ResolvingConfiguration => value.request === 'attach'; + +/** + * Configuration provider for Chrome debugging. + */ +@injectable() +export abstract class ChromiumDebugConfigurationResolver + extends BaseConfigurationResolver + implements vscode.DebugConfigurationProvider +{ + constructor( + @inject(ExtensionContext) context: vscode.ExtensionContext, + @inject(NodeConfigurationResolver) private readonly nodeProvider: NodeConfigurationResolver, + @inject(TerminalDebugConfigurationResolver) private readonly terminalProvider: + TerminalDebugConfigurationResolver, + @inject(ExtensionLocation) private readonly location: ExtensionLocation, + @inject(FS) private readonly fs: FsPromises, + ) { + super(context); + } + + protected async resolveBrowserCommon( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingConfiguration, + ) { + if (config.request === 'attach') { + // todo https://github.com/microsoft/vscode-chrome-debug/blob/ee5ae7ac7734f369dba58ba57bb910aac467c97a/src/extension.ts#L48 + } + + if (config.server && 'program' in config.server) { + const serverOpts = { + ...config.server, + type: DebugType.Node, + request: 'launch', + name: `${config.name}: Server`, + }; + + config.server = (await this.nodeProvider.resolveDebugConfiguration( + folder, + serverOpts, + )) as INodeLaunchConfiguration; + } else if (config.server && 'command' in config.server) { + config.server = await this.terminalProvider.resolveDebugConfiguration(folder, { + ...config.server, + type: DebugType.Terminal, + request: 'launch', + name: `${config.name}: Server`, + }); + } + + const browserLocation = this.location === 'remote' ? 'ui' : 'workspace'; + if (isLaunch(config) && !config.browserLaunchLocation) { + config.browserLaunchLocation = browserLocation; + } + + if (isAttach(config) && !config.browserAttachLocation) { + config.browserAttachLocation = browserLocation; + } + + if (config.request === 'launch') { + const cast = config as ResolvingConfiguration; + this.applyDefaultRuntimeExecutable(cast); + } + } + + /** + * @override + */ + protected getSuggestedWorkspaceFolders(config: AnyChromiumConfiguration) { + return [config.rootPath, config.webRoot]; + } + + /** + * @inheritdoc + */ + public async resolveDebugConfigurationWithSubstitutedVariables?( + _folder: vscode.WorkspaceFolder | undefined, + debugConfiguration: vscode.DebugConfiguration, + ): Promise { + if ('__pendingTargetId' in debugConfiguration) { + return debugConfiguration as T; + } + + let config = debugConfiguration as T; + if ('port' in config && typeof config.port === 'string') { + config.port = Number(config.port); + } + + if (config.request === 'launch') { + const resolvedDataDir = await this.ensureNoLockfile(config); + if (resolvedDataDir === undefined) { + return; + } + + config = resolvedDataDir; + } + + return config; + } + + protected async ensureNoLockfile(config: T): Promise { + if (config.request !== 'launch') { + return config; + } + + const cast = config as ResolvingConfiguration; + + // for no user data dirs, with have nothing to look at + if (cast.userDataDir === false) { + return config; + } + + // if there's a port configured and something's there, we can connect to it regardless + if (cast.port && !(await isPortOpen(cast.port))) { + return config; + } + + const userDataDir = typeof cast.userDataDir === 'string' + ? cast.userDataDir + : join( + this.extensionContext.storagePath ?? tmpdir(), + cast.runtimeArgs?.includes('--headless') ? '.headless-profile' : '.profile', + ); + + // Warn if there's an existing instance, so we probably can't launch it in debug mode: + const platformLock = join( + userDataDir, + process.platform === 'win32' ? 'lockfile' : 'SingletonLock', + ); + const lockfileExists = await some([ + existsWithoutDeref(this.fs, platformLock), + this.isVsCodeLocked(join(userDataDir, 'code.lock')), + ]); + + if (lockfileExists) { + const debugAnyway = l10n.t('Debug Anyway'); + const result = await vscode.window.showErrorMessage( + l10n.t( + 'It looks like a browser is already running from {0}. Please close it before trying to debug, otherwise VS Code may not be able to connect to it.', + cast.userDataDir === true + ? l10n.t('an old debug session') + : l10n.t('the configured userDataDir'), + ), + { modal: true }, + debugAnyway, + ); + + if (result !== debugAnyway) { + return undefined; + } + } + + return { ...config, userDataDir }; + } + + private async isVsCodeLocked(filepath: string) { + try { + const pid = Number(await this.fs.readFile(filepath, 'utf-8')); + process.kill(pid, 0); // throws if the process does not exist + return true; + } catch { + return false; + } + } +} + +@injectable() +export abstract class ChromiumDebugConfigurationProvider< + T extends AnyChromiumLaunchConfiguration, +> extends BaseConfigurationProvider { + protected provide() { + return this.createLaunchConfigFromContext() || this.getDefaultLaunch(); + } + + protected getTriggerKind() { + return vscode.DebugConfigurationProviderTriggerKind.Initial; + } + + public createLaunchConfigFromContext() { + const editor = vscode.window.activeTextEditor; + if (editor && editor.document.languageId === 'html') { + return { + type: this.getType(), + request: 'launch', + name: `Open ${basename(editor.document.uri.fsPath)}`, + file: editor.document.uri.fsPath, + } as ResolvingConfiguration; + } + + return undefined; + } + + protected getDefaultLaunch() { + return { + type: this.getType(), + request: 'launch', + name: l10n.t('Launch Chrome against localhost'), + url: 'http://localhost:8080', + webRoot: '${workspaceFolder}', + } as ResolvingConfiguration; + } +} diff --git a/code/extensions/js-debug/src/ui/configuration/configurationProvider.ts b/code/extensions/js-debug/src/ui/configuration/configurationProvider.ts new file mode 100644 index 000000000000..90059cd1d22c --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/configurationProvider.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +// Here we create separate sets of interfaces for providing and resolving +// debug configuration. + +export interface IDebugConfigurationResolver { + readonly type: string; + + /** + * Prevent accidentally having this on a resolver. + */ + provideDebugConfigurations?: never; + + /** + * @see DebugConfigurationProvider.resolveDebugConfiguration + */ + resolveDebugConfiguration: Required< + vscode.DebugConfigurationProvider['resolveDebugConfiguration'] + >; + + /** + * @see DebugConfigurationProvider.resolveDebugConfigurationWithSubstitutedVariables + */ + resolveDebugConfigurationWithSubstitutedVariables?: + vscode.DebugConfigurationProvider['resolveDebugConfigurationWithSubstitutedVariables']; +} + +export const IDebugConfigurationResolver = Symbol('IDebugConfigurationResolver'); + +export interface IDebugConfigurationProvider { + readonly type: string; + readonly triggerKind: vscode.DebugConfigurationProviderTriggerKind; + + /** + * @see DebugConfigurationProvider.provideDebugConfigurations + */ + provideDebugConfigurations: Required< + vscode.DebugConfigurationProvider['provideDebugConfigurations'] + >; +} + +export const IDebugConfigurationProvider = Symbol('IDebugConfigurationProvider'); diff --git a/code/extensions/js-debug/src/ui/configuration/edgeDebugConfigurationProvider.ts b/code/extensions/js-debug/src/ui/configuration/edgeDebugConfigurationProvider.ts new file mode 100644 index 000000000000..6934c5fc7e9f --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/edgeDebugConfigurationProvider.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { + AnyEdgeConfiguration, + edgeAttachConfigDefaults, + edgeLaunchConfigDefaults, + IEdgeLaunchConfiguration, + ResolvingEdgeConfiguration, +} from '../../configuration'; +import { + ChromiumDebugConfigurationProvider, + ChromiumDebugConfigurationResolver, +} from './chromiumDebugConfigurationProvider'; + +/** + * Configuration provider for Chrome debugging. + */ +@injectable() +export class EdgeDebugConfigurationResolver + extends ChromiumDebugConfigurationResolver + implements vscode.DebugConfigurationProvider +{ + /** + * @override + */ + protected async resolveDebugConfigurationAsync( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingEdgeConfiguration, + ): Promise { + if (!config.name && !config.type && !config.request) { + const fromContext = new EdgeDebugConfigurationProvider().createLaunchConfigFromContext(); + if (!fromContext) { + // Return null so it will create a launch.json and fall back on + // provideDebugConfigurations - better to point the user towards + // the config than try to work automagically for complex scenarios. + return; + } + + config = fromContext; + } + + await this.resolveBrowserCommon(folder, config); + + // Disable attachment timeouts for webview apps. We aren't opening a + // browser immediately, and it may take an arbitrary amount of time within + // the app until a debuggable webview appears. + if (config.useWebView) { + config.timeout = config.timeout ?? 0; + } + + return config.request === 'attach' + ? { ...edgeAttachConfigDefaults, ...config } + : { ...edgeLaunchConfigDefaults, ...config }; + } + + protected getType() { + return DebugType.Edge as const; + } +} + +@injectable() +export class EdgeDebugConfigurationProvider + extends ChromiumDebugConfigurationProvider +{ + protected getType() { + return DebugType.Edge as const; + } + + protected getDefaultLaunch() { + return { + ...super.getDefaultLaunch(), + name: l10n.t('Launch Edge against localhost'), + }; + } +} diff --git a/code/extensions/js-debug/src/ui/configuration/editorBrowserDebugConfigurationProvider.ts b/code/extensions/js-debug/src/ui/configuration/editorBrowserDebugConfigurationProvider.ts new file mode 100644 index 000000000000..b20159b13aaf --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/editorBrowserDebugConfigurationProvider.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { injectable } from 'inversify'; +import { basename } from 'path'; +import * as vscode from 'vscode'; +import { DebugType } from '../../common/contributionUtils'; +import { + AnyEditorBrowserConfiguration, + editorBrowserAttachConfigDefaults, + editorBrowserLaunchConfigDefaults, + IEditorBrowserLaunchConfiguration, + ResolvingConfiguration, + ResolvingEditorBrowserConfiguration, +} from '../../configuration'; +import { BaseConfigurationProvider } from './baseConfigurationProvider'; +import { BaseConfigurationResolver } from './baseConfigurationResolver'; + +/** + * Configuration provider for VS Code integrated browser debugging. + * Only available on desktop. + */ +@injectable() +export class EditorBrowserDebugConfigurationResolver + extends BaseConfigurationResolver + implements vscode.DebugConfigurationProvider +{ + protected async resolveDebugConfigurationAsync( + _folder: vscode.WorkspaceFolder | undefined, + config: ResolvingEditorBrowserConfiguration, + ): Promise { + if (vscode.env.uiKind === vscode.UIKind.Web) { + vscode.window.showErrorMessage( + 'Integrated Browser debugging is only available on VS Code Desktop.', + ); + return null; + } + + if (!config.name && !config.type && !config.request) { + return null; + } + + return config.request === 'attach' + ? { ...editorBrowserAttachConfigDefaults, ...config } + : { ...editorBrowserLaunchConfigDefaults, ...config }; + } + + protected getType() { + return DebugType.EditorBrowser as const; + } + + protected getSuggestedWorkspaceFolders(config: AnyEditorBrowserConfiguration) { + return [config.rootPath, config.webRoot]; + } +} + +@injectable() +export class EditorBrowserDebugConfigurationProvider + extends BaseConfigurationProvider +{ + protected getType() { + return DebugType.EditorBrowser as const; + } + + protected getTriggerKind() { + return vscode.DebugConfigurationProviderTriggerKind.Initial; + } + + protected provide(): ResolvingConfiguration { + return this.createLaunchConfigFromContext() || this.getDefaultLaunch(); + } + + public createLaunchConfigFromContext(): + | ResolvingConfiguration + | undefined + { + const editor = vscode.window.activeTextEditor; + if (editor && editor.document.languageId === 'html') { + return { + type: this.getType(), + request: 'launch', + name: `Open ${basename(editor.document.uri.fsPath)}`, + url: editor.document.uri.toString(), + } as ResolvingConfiguration; + } + + return undefined; + } + + private getDefaultLaunch(): ResolvingConfiguration { + return { + type: this.getType(), + request: 'launch', + name: l10n.t('Launch Integrated Browser against localhost'), + url: 'http://localhost:8080', + webRoot: '${workspaceFolder}', + } as ResolvingConfiguration; + } +} diff --git a/code/extensions/js-debug/src/ui/configuration/extensionHostConfigurationResolver.ts b/code/extensions/js-debug/src/ui/configuration/extensionHostConfigurationResolver.ts new file mode 100644 index 000000000000..1b34d3930a84 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/extensionHostConfigurationResolver.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import execa from 'execa'; +import { promises as fs } from 'fs'; +import { injectable } from 'inversify'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { TaskCancelledError } from '../../common/cancellation'; +import { DebugType } from '../../common/contributionUtils'; +import { canAccess } from '../../common/fsUtils'; +import { ProcessArgs } from '../../common/processArgs'; +import { nearestDirectoryWhere } from '../../common/urlUtils'; +import { + applyNodeishDefaults, + extensionHostConfigDefaults, + IExtensionHostLaunchConfiguration, + resolveVariableInConfig, + ResolvingExtensionHostConfiguration, +} from '../../configuration'; +import { BaseConfigurationResolver } from './baseConfigurationResolver'; + +const defaultExtensionKind = ['workspace']; + +/** + * Configuration provider for Extension host debugging. + */ +@injectable() +export class ExtensionHostConfigurationResolver + extends BaseConfigurationResolver + implements vscode.DebugConfigurationProvider +{ + protected async resolveDebugConfigurationAsync( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingExtensionHostConfiguration, + ): Promise { + const args = new ProcessArgs(config.args); + const pkgJson = await readExtensionPackageJson(folder, config, args); + if (config.debugWebWorkerHost === undefined) { + const extensionKind = pkgJson?.value.extensionKind ?? defaultExtensionKind; + config = { + ...config, + debugWebWorkerHost: extensionKind.length === 1 && extensionKind[0] === 'web', + }; + } + + if (config.debugWebWorkerHost) { + config.outFiles = []; // will have a runtime script offset which invalidates any predictions + config.resolveSourceMapLocations = extensionHostConfigDefaults.resolveSourceMapLocations; + } + + if (!config.outFiles && pkgJson && folder) { + const outFiles = await guessOutFiles(folder, pkgJson); + if (outFiles) { + config.outFiles = outFiles; + // Ensure the default resolution paths (the entire workspace folder) + // is kept so that transpiled sources work (#2101) + config.resolveSourceMapLocations ??= extensionHostConfigDefaults.resolveSourceMapLocations; + } + } + + applyNodeishDefaults(config); + + return Promise.resolve({ + ...extensionHostConfigDefaults, + ...config, + }); + } + + /** + * @inheritdoc + */ + public async resolveDebugConfigurationWithSubstitutedVariables( + _folder: vscode.WorkspaceFolder | undefined, + debugConfiguration: vscode.DebugConfiguration, + ): Promise { + const config = debugConfiguration as ResolvingExtensionHostConfiguration; + try { + const testCfg = await resolveTestConfiguration(config); + if (testCfg) { + config.env = { ...config.env, ...testCfg.env }; + config.args = [ + ...(config.args || []), + ...(testCfg.config.launchArgs || []), + `--extensionDevelopmentPath=${testCfg.extensionDevelopmentPath}`, + `--extensionTestsPath=${testCfg.extensionTestsPath}`, + ]; + } + } catch (e) { + if (e instanceof TaskCancelledError) { + return undefined; + } + throw e; + } + + return config; + } + + protected getType() { + return DebugType.ExtensionHost as const; + } +} + +const guessOutFiles = async (wf: vscode.WorkspaceFolder, pkgJson: IPackageJsonInfo) => { + if (!pkgJson.value.main) { + return undefined; + } + + const extensionMain = path.resolve(path.dirname(pkgJson.path), pkgJson.value.main); + const relativeToExt = path.relative(wf.uri.fsPath, path.dirname(pkgJson.path)); + const relativeToMain = path.relative(path.dirname(pkgJson.path), extensionMain); + const subdirOfMain = relativeToMain.split(path.sep)[0]; + + return [ + path.join('${workspaceFolder}', relativeToExt, subdirOfMain, '**/*.js').replaceAll('\\', '/'), + ]; +}; + +const devPathArg = '--extensionDevelopmentPath'; + +interface IPackageJsonInfo { + path: string; + value: { + main?: string; + extensionKind?: string; + }; +} + +const readExtensionPackageJson = async ( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingExtensionHostConfiguration, + args: ProcessArgs, +): Promise => { + const arg = args.get(devPathArg); + if (!arg) { + return undefined; + } + + const resolvedFolder = resolveVariableInConfig( + arg, + 'workspaceFolder', + folder?.uri.fsPath ?? config.__workspaceFolder ?? '', + ); + + try { + const pkgPath = path.join(resolvedFolder, 'package.json'); + const json = await fs.readFile(pkgPath, 'utf-8'); + return { value: JSON.parse(json), path: pkgPath }; + } catch { + return undefined; + } +}; + +const resolveTestConfiguration = async (config: ResolvingExtensionHostConfiguration) => { + const { testConfiguration } = config; + let { testConfigurationLabel } = config; + if (!testConfiguration) { + return; + } + + const suffix = path.join('node_modules', '@vscode', 'test-cli', 'out', 'bin.mjs'); + const dirWithModules = await nearestDirectoryWhere(testConfiguration, async dir => { + const binary = path.join(dir, suffix); + return (await canAccess(fs, binary)) ? dir : undefined; + }); + + if (!dirWithModules) { + throw new Error( + l10n.t( + 'Cannot find `{0}` installed in {1}', + '@vscode/test-cli', + path.dirname(testConfiguration), + ), + ); + } + + const result = await execa( + process.execPath, + [path.join(dirWithModules, suffix), '--config', testConfiguration, '--list-configuration'], + { + env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }, + }, + ); + + const configs: { + config: { label?: string; launchArgs?: string[] }; + extensionTestsPath: string; + extensionDevelopmentPath: string; + env: Record; + }[] = JSON.parse(result.stdout); + + if (configs.length === 1) { + return configs[0]; + } + + if (configs.length && !testConfigurationLabel) { + testConfigurationLabel = await vscode.window.showQuickPick( + configs.map((c, i) => c.config.label || String(i)), + { + title: l10n.t('Select test configuration to run'), + }, + ); + if (!testConfigurationLabel) { + throw new TaskCancelledError('cancelled'); + } + } + + const found = configs.find( + (c, i) => c.config.label === testConfigurationLabel || String(i) === testConfigurationLabel, + ); + if (!found) { + throw new Error( + l10n.t( + 'Cannot find test configuration with label `{0}`, got: {1}', + String(testConfigurationLabel), + configs.map((c, i) => c.config.label || i).join(', '), + ), + ); + } + + return found; +}; diff --git a/code/extensions/js-debug/src/ui/configuration/index.ts b/code/extensions/js-debug/src/ui/configuration/index.ts new file mode 100644 index 000000000000..0564aff51322 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/index.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +export * from './configurationProvider'; +import { + ChromeDebugConfigurationProvider, + ChromeDebugConfigurationResolver, +} from './chromeDebugConfigurationProvider'; +import { + EdgeDebugConfigurationProvider, + EdgeDebugConfigurationResolver, +} from './edgeDebugConfigurationProvider'; +import { + EditorBrowserDebugConfigurationProvider, + EditorBrowserDebugConfigurationResolver, +} from './editorBrowserDebugConfigurationProvider'; +import { ExtensionHostConfigurationResolver } from './extensionHostConfigurationResolver'; +import { + NodeDynamicDebugConfigurationProvider, + NodeInitialDebugConfigurationProvider, +} from './nodeDebugConfigurationProvider'; +import { NodeConfigurationResolver } from './nodeDebugConfigurationResolver'; +import { TerminalDebugConfigurationResolver } from './terminalDebugConfigurationResolver'; + +export const allConfigurationResolvers = [ + ChromeDebugConfigurationResolver, + EdgeDebugConfigurationResolver, + EditorBrowserDebugConfigurationResolver, + ExtensionHostConfigurationResolver, + NodeConfigurationResolver, + TerminalDebugConfigurationResolver, +]; + +export const allConfigurationProviders = [ + ChromeDebugConfigurationProvider, + EdgeDebugConfigurationProvider, + EditorBrowserDebugConfigurationProvider, + NodeInitialDebugConfigurationProvider, + NodeDynamicDebugConfigurationProvider, +]; diff --git a/code/extensions/js-debug/src/ui/configuration/nodeDebugConfigurationProvider.ts b/code/extensions/js-debug/src/ui/configuration/nodeDebugConfigurationProvider.ts new file mode 100644 index 000000000000..65e06f809409 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/nodeDebugConfigurationProvider.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { injectable } from 'inversify'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { DebugType, getPreferredOrDebugType } from '../../common/contributionUtils'; +import { flatten } from '../../common/objUtils'; +import { + AnyNodeConfiguration, + AnyResolvingConfiguration, + AnyTerminalConfiguration, + breakpointLanguages, + ResolvingNodeConfiguration, + ResolvingTerminalConfiguration, +} from '../../configuration'; +import { findScripts } from '../debugNpmScript'; +import { getScriptRunner } from '../getRunScriptCommand'; +import { BaseConfigurationProvider } from './baseConfigurationProvider'; +import { createLaunchConfigFromContext } from './nodeDebugConfigurationResolver'; + +@injectable() +export class NodeInitialDebugConfigurationProvider + extends BaseConfigurationProvider +{ + protected provide(folder?: vscode.WorkspaceFolder) { + return createLaunchConfigFromContext(folder, true); + } + + protected getType() { + return DebugType.Node as const; + } + + protected getTriggerKind() { + return vscode.DebugConfigurationProviderTriggerKind.Initial; + } +} + +type DynamicConfig = ResolvingNodeConfiguration | ResolvingTerminalConfiguration; + +const keysToRelativize: ReadonlyArray = ['cwd', 'program']; + +@injectable() +export class NodeDynamicDebugConfigurationProvider extends BaseConfigurationProvider< + AnyNodeConfiguration | AnyTerminalConfiguration +> { + protected async provide(folder?: vscode.WorkspaceFolder) { + const configs = flatten( + await Promise.all([this.getFromNpmScripts(folder), this.getFromActiveFile()]), + ); + + // convert any absolute paths to directories or files to nicer ${workspaceFolder}-based paths + if (folder) { + for (const configRaw of configs) { + const config = configRaw as unknown as { [key: string]: string | undefined }; + for (const key of keysToRelativize) { + const value = config[key]; + if (value && path.isAbsolute(value)) { + config[key] = path.join( + '${workspaceFolder}', + path.relative(folder.uri.fsPath, value), + ); + } + } + } + } + + return configs; + } + + protected getType() { + return DebugType.Node as const; + } + + protected getTriggerKind() { + return vscode.DebugConfigurationProviderTriggerKind.Dynamic; + } + + /** + * Adds suggestions discovered from npm scripts. + */ + protected async getFromNpmScripts(folder?: vscode.WorkspaceFolder): Promise { + const openTerminal: AnyResolvingConfiguration = { + type: getPreferredOrDebugType(DebugType.Terminal), + name: l10n.t('JavaScript Debug Terminal'), + request: 'launch', + cwd: folder?.uri.fsPath, + }; + + if (!folder) { + return [openTerminal]; + } + + const scripts = await findScripts([folder], true); + if (!scripts) { + return [openTerminal]; + } + + const packageManager = await getScriptRunner(folder); + + // Check if there are multiple directories to distinguish scripts in monorepos + const multiDir = scripts.some(s => s.directory !== scripts[0].directory); + + return scripts + .map(script => ({ + type: getPreferredOrDebugType(DebugType.Terminal), + name: multiDir + ? l10n.t('Run Script: {0} ({1})', script.name, path.basename(script.directory)) + : l10n.t('Run Script: {0}', script.name), + request: 'launch', + command: `${packageManager} run ${script.name}`, + cwd: script.directory, + })) + .concat(openTerminal); + } + + /** + * Adds a suggestion to run the active file, if it's debuggable. + */ + protected getFromActiveFile(): DynamicConfig[] { + const editor = vscode.window.activeTextEditor; + if ( + !editor + || !breakpointLanguages.includes(editor.document.languageId) + || editor.document.uri.scheme !== 'file' + ) { + return []; + } + + return [ + { + type: getPreferredOrDebugType(DebugType.Node), + name: l10n.t('Run Current File'), + request: 'launch', + program: editor.document.uri.fsPath, + }, + ]; + } +} diff --git a/code/extensions/js-debug/src/ui/configuration/nodeDebugConfigurationResolver.ts b/code/extensions/js-debug/src/ui/configuration/nodeDebugConfigurationResolver.ts new file mode 100644 index 000000000000..201e8458bef4 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/nodeDebugConfigurationResolver.ts @@ -0,0 +1,513 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { promises as fs } from 'fs'; +import { inject, injectable } from 'inversify'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { CancellationToken } from 'vscode'; +import { writeToConsole } from '../../common/console'; +import { DebugType } from '../../common/contributionUtils'; +import { EnvironmentVars } from '../../common/environmentVars'; +import { findOpenPort } from '../../common/findOpenPort'; +import { existsInjected, IFsUtils, LocalFsUtils } from '../../common/fsUtils'; +import { nodeInternalsToken } from '../../common/node15Internal'; +import { forceForwardSlashes, isSubpathOrEqualTo } from '../../common/pathUtils'; +import { some } from '../../common/promiseUtil'; +import { getNormalizedBinaryName, nearestDirectoryWhere } from '../../common/urlUtils'; +import { + AnyNodeConfiguration, + applyNodeDefaults, + baseDefaults, + breakpointLanguages, + resolveVariableInConfig, + ResolvingNodeAttachConfiguration, + ResolvingNodeLaunchConfiguration, +} from '../../configuration'; +import { ExtensionContext } from '../../ioc-extras'; +import { INvmResolver } from '../../targets/node/nvmResolver'; +import { fixInspectFlags } from '../configurationUtils'; +import { resolveProcessId } from '../processPicker'; +import { BaseConfigurationResolver } from './baseConfigurationResolver'; + +type ResolvingNodeConfiguration = + | ResolvingNodeAttachConfiguration + | ResolvingNodeLaunchConfiguration; + +/** + * Configuration provider for node debugging. In order to allow for a + * close to 1:1 drop-in, this is nearly identical to the original vscode- + * node-debug, with support for some legacy options (mern, useWSL) removed. + */ +@injectable() +export class NodeConfigurationResolver extends BaseConfigurationResolver { + constructor( + @inject(ExtensionContext) context: vscode.ExtensionContext, + @inject(INvmResolver) private readonly nvmResolver: INvmResolver, + @inject(IFsUtils) private readonly fsUtils: LocalFsUtils, + ) { + super(context); + } + + /** + * @inheritdoc + */ + public async resolveDebugConfigurationWithSubstitutedVariables( + _folder: vscode.WorkspaceFolder | undefined, + rawConfig: vscode.DebugConfiguration, + ): Promise { + const config = rawConfig as AnyNodeConfiguration; + if ( + config.type === DebugType.Node + && config.request === 'attach' + && typeof config.processId === 'string' + ) { + await resolveProcessId(this.fsUtils, config); + } + + if ('port' in config && typeof config.port === 'string') { + config.port = Number(config.port); + } + if ('attachSimplePort' in config && typeof config.attachSimplePort === 'string') { + config.attachSimplePort = Number(config.attachSimplePort); + } + + // check that the cwd is valid to avoid mysterious ENOENTs (vscode#133310) + if (config.cwd) { + const stats = await existsInjected(fs, config.cwd); + if (!stats) { + vscode.window.showErrorMessage( + l10n.t('The configured `cwd` {0} does not exist.', config.cwd), + { modal: true }, + ); + return; + } + + if (!stats.isDirectory()) { + vscode.window.showErrorMessage( + l10n.t('The configured `cwd` {0} is not a folder.', config.cwd), + { modal: true }, + ); + return; + } + } + + return config; + } + + /** + * @override + */ + protected async resolveDebugConfigurationAsync( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingNodeConfiguration, + cancellationToken: CancellationToken, + ): Promise { + if (!config.name && !config.type && !config.request) { + config = await createLaunchConfigFromContext(folder, true, config); + if (config.request === 'launch' && !config.program) { + vscode.window.showErrorMessage(l10n.t('Cannot find a program to debug'), { + modal: true, + }); + return; + } + } + + // make sure that config has a 'cwd' attribute set + if (!config.cwd) { + config.cwd = config.localRoot // https://github.com/microsoft/vscode-js-debug/issues/894#issuecomment-745449195 + || guessWorkingDirectory(config.request === 'launch' ? config.program : undefined, folder); + } + + // if a 'remoteRoot' is specified without a corresponding 'localRoot', set 'localRoot' to the workspace folder. + // see https://github.com/Microsoft/vscode/issues/63118 + if (config.remoteRoot && !config.localRoot) { + config.localRoot = '${workspaceFolder}'; + } + + if (config.request === 'launch') { + // custom node install + this.applyDefaultRuntimeExecutable(config); + + // Deno does not support NODE_OPTIONS, so if we see it, try to set the + // necessary options automatically. + if ( + config.runtimeExecutable + && getNormalizedBinaryName(config.runtimeExecutable) === 'deno' + ) { + // If the user manually set up attachSimplePort, do nothing. + if (!config.attachSimplePort) { + const port = await findOpenPort(); + config.attachSimplePort = port; + config.continueOnAttach ??= true; + + const runtimeArgs = [`--inspect-brk=127.0.0.1:${port}`]; + if (!config.runtimeArgs) { + runtimeArgs.push('--allow-all'); + config.runtimeArgs = ['run', ...runtimeArgs]; + } else { + if (!config.runtimeArgs.includes('--allow-all') && !config.runtimeArgs.includes('-A')) { + runtimeArgs.push('--allow-all'); + } + if (!config.runtimeArgs.includes('run')) { + config.runtimeArgs = ['run', ...runtimeArgs, ...config.runtimeArgs]; + } else { + config.runtimeArgs = [...config.runtimeArgs, ...runtimeArgs]; + } + } + } + } + + // nvm support + const nvmVersion = config.runtimeVersion; + if (typeof nvmVersion === 'string' && nvmVersion !== 'default') { + const { directory, binary } = await this.nvmResolver.resolveNvmVersionPath(nvmVersion); + config.env = new EnvironmentVars(config.env).addToPath(directory, 'prepend', true).value; + config.runtimeExecutable = !config.runtimeExecutable || config.runtimeExecutable === 'node' + ? binary + : config.runtimeExecutable; + } + + // when using "integratedTerminal" ensure that debug console doesn't get activated; see https://github.com/Microsoft/vscode/issues/43164 + if (config.console === 'integratedTerminal' && !config.internalConsoleOptions) { + config.internalConsoleOptions = 'neverOpen'; + } + + // assign a random debug port if requested, otherwise remove manual + // --inspect-brk flags, which are no longer needed and interfere + if (config.attachSimplePort === null || config.attachSimplePort === undefined) { + fixInspectFlags(config); + } else { + if (config.attachSimplePort === 0) { + config.attachSimplePort = await findOpenPort(undefined, cancellationToken); + const arg = `--inspect-brk=${config.attachSimplePort}`; + config.runtimeArgs = config.runtimeArgs ? [...config.runtimeArgs, arg] : [arg]; + } + + config.continueOnAttach = !config.stopOnEntry; + config.stopOnEntry = false; // handled by --inspect-brk + } + + // update outfiles to the nearest package root + await guessOutFiles(this.fsUtils, folder, config); + } + + return applyNodeDefaults(config); + } + + protected getType() { + return DebugType.Node as const; + } + + /** + * @override + */ + protected getSuggestedWorkspaceFolders(config: AnyNodeConfiguration) { + return [config.rootPath, config.cwd]; + } +} + +export function guessWorkingDirectory(program?: string, folder?: vscode.WorkspaceFolder): string { + if (folder) { + return folder.uri.fsPath; + } + + // no folder -> config is a user or workspace launch config + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { + return vscode.workspace.workspaceFolders[0].uri.fsPath; + } + + // no folder case + if (program) { + if (program === '${file}') { + return '${fileDirname}'; + } + + // program is some absolute path + if (path.isAbsolute(program)) { + // derive 'cwd' from 'program' + return path.dirname(program); + } + } + + // last resort + return '${workspaceFolder}'; +} + +function getAbsoluteLocation(folder: vscode.WorkspaceFolder | undefined, relpath: string) { + if (folder) { + relpath = resolveVariableInConfig(relpath, 'workspaceFolder', folder.uri.fsPath); + } + + if (vscode.workspace.workspaceFolders?.length) { + relpath = resolveVariableInConfig( + relpath, + 'workspaceRoot', + vscode.workspace.workspaceFolders[0].uri.fsPath, + ); + } + + if (path.isAbsolute(relpath)) { + return relpath; + } + + if (folder) { + return path.join(folder.uri.fsPath, relpath); + } + + return undefined; +} + +/** + * Set the outFiles to the nearest package.json-containing folder relative + * to the program, if it's not already included in the workspace folder. + * + * This used to narrow (#326), but I think this is undesirable behavior for + * most users (vscode#142641), so now it only widens the `outFiles`. + */ +async function guessOutFiles( + fsUtils: LocalFsUtils, + folder: vscode.WorkspaceFolder | undefined, + config: ResolvingNodeLaunchConfiguration, +) { + if (config.outFiles || !folder) { + return; + } + + let programLocation: string | undefined; + if (config.program) { + programLocation = getAbsoluteLocation(folder, config.program); + if (programLocation) { + programLocation = path.dirname(programLocation); + } + } else if (config.cwd) { + programLocation = getAbsoluteLocation(folder, config.cwd); + } + + if (!programLocation || isSubpathOrEqualTo(folder.uri.fsPath, programLocation)) { + return; + } + + const root = await nearestDirectoryWhere( + programLocation, + async p => + !p.includes('node_modules') && (await fsUtils.exists(path.join(p, 'package.json'))) + ? p + : undefined, + ); + + if (root) { + const rel = forceForwardSlashes(path.relative(folder.uri.fsPath, root)); + if (rel.length) { + config.outFiles = [ + ...baseDefaults.outFiles, + `\${workspaceFolder}/${rel}/**/*.js`, + `!\${workspaceFolder}/${rel}/**/node_modules/**`, + ]; + } + } +} + +interface ITSConfig { + compilerOptions?: { + outDir: string; + }; +} + +interface IPartialPackageJson { + name?: string; + main?: string; + scripts?: { [key: string]: string }; +} + +const commonEntrypoints = ['index.js', 'main.js']; + +export async function createLaunchConfigFromContext( + folder: vscode.WorkspaceFolder | undefined, + resolve: boolean, + existingConfig?: ResolvingNodeConfiguration, +): Promise { + const config: ResolvingNodeConfiguration = { + type: DebugType.Node, + request: 'launch', + name: l10n.t('Launch Program'), + skipFiles: [`${nodeInternalsToken}/**`], + }; + + if (existingConfig && existingConfig.noDebug) { + config.noDebug = true; + } + + const pkg = await loadJSON(folder, 'package.json'); + let program: string | undefined; + let useSourceMaps = false; + + if (pkg && pkg.name === 'mern-starter') { + if (resolve) { + writeToConsole(l10n.t("Launch configuration for '{0}' project created.", 'Mern Starter')); + } + configureMern(config); + return config; + } + + if (pkg) { + // try to find a value for 'program' by analysing package.json + program = await guessProgramFromPackage(folder, pkg, resolve); + if (program && resolve) { + writeToConsole(l10n.t("Launch configuration created based on 'package.json'.")); + } + } + + if (!program) { + // try to use file open in editor + const editor = vscode.window.activeTextEditor; + if (editor && breakpointLanguages.includes(editor.document.languageId)) { + useSourceMaps = editor.document.languageId !== 'javascript'; + program = folder + ? path.relative(folder.uri.fsPath, editor.document.uri.fsPath) + : editor.document.uri.fsPath; + + if (!path.isAbsolute(program)) { + // we don't use path.join here since it destroys the workspaceFolder with ../ (vscode#125796) + program = '${workspaceFolder}' + path.sep + program; + } + } + } + + if (!program && folder) { + const basePath = folder.uri.fsPath; + program = await some( + commonEntrypoints.map( + async file => + (await existsInjected(fs, path.join(basePath, file))) + && '${workspaceFolder}' + path.sep + file, + ), + ); + } + + // just use `${file}` which'll prompt the user to open an active file + if (!program) { + program = '${file}'; + } + + if (program) { + config.program = program; + + if (!folder) { + config.__workspaceFolder = path.dirname(program); + } + } + + // prepare for source maps by adding 'outFiles' if typescript or coffeescript is detected + if ( + useSourceMaps + || vscode.workspace.textDocuments.some(document => isTranspiledLanguage(document.languageId)) + ) { + if (resolve) { + writeToConsole( + l10n.t( + "Adjust glob pattern(s) in the 'outFiles' attribute so that they cover the generated JavaScript.", + ), + ); + } + + let dir = ''; + const tsConfig = await loadJSON(folder, 'tsconfig.json'); + if (tsConfig?.compilerOptions?.outDir && canDetectTsBuildTask()) { + const outDir = tsConfig.compilerOptions.outDir; + if (!path.isAbsolute(outDir)) { + dir = outDir; + if (dir.indexOf('./') === 0) { + dir = dir.substr(2); + } + if (dir[dir.length - 1] !== '/') { + dir += '/'; + } + } + config.preLaunchTask = 'tsc: build - tsconfig.json'; + } + config['outFiles'] = ['${workspaceFolder}/' + dir + '**/*.js']; + } + + return config; +} + +function canDetectTsBuildTask() { + const value = vscode.workspace.getConfiguration().get('typescript.tsc.autoDetect'); + return value !== 'off' && value !== 'watch'; +} + +function configureMern(config: ResolvingNodeConfiguration) { + if (config.request !== 'launch') { + return; + } + + config.runtimeExecutable = 'nodemon'; + config.program = '${workspaceFolder}/index.js'; + config.restart = true; + config.env = { BABEL_DISABLE_CACHE: '1', NODE_ENV: 'development' }; + config.console = 'integratedTerminal'; + config.internalConsoleOptions = 'neverOpen'; +} + +function isTranspiledLanguage(languagId: string): boolean { + return languagId === 'typescript' || languagId === 'coffeescript'; +} + +async function loadJSON( + folder: vscode.WorkspaceFolder | undefined, + file: string, +): Promise { + if (folder) { + try { + const content = await fs.readFile(path.join(folder.uri.fsPath, file), 'utf8'); + return JSON.parse(content); + } catch (error) { + // silently ignore + } + } + return undefined; +} +/* + * try to find the entry point ('main') from the package.json + */ +async function guessProgramFromPackage( + folder: vscode.WorkspaceFolder | undefined, + packageJson: IPartialPackageJson, + resolve: boolean, +): Promise { + let program: string | undefined; + + try { + if (packageJson.main) { + program = packageJson.main; + } else if (packageJson.scripts && typeof packageJson.scripts.start === 'string') { + // assume a start script of the form 'node server.js' + program = packageJson.scripts.start.split(' ').pop(); + } + + if (program) { + let targetPath: string | undefined; + if (path.isAbsolute(program)) { + targetPath = program; + } else { + targetPath = folder ? path.join(folder.uri.fsPath, program) : undefined; + program = path.join('${workspaceFolder}', program); + } + if ( + resolve + && targetPath + && !(await existsInjected(fs, targetPath)) + && !(await existsInjected(fs, targetPath + '.js')) + ) { + return undefined; + } + } + } catch (error) { + // silently ignore + } + + return program; +} diff --git a/code/extensions/js-debug/src/ui/configuration/terminalDebugConfigurationResolver.ts b/code/extensions/js-debug/src/ui/configuration/terminalDebugConfigurationResolver.ts new file mode 100644 index 000000000000..21b8377e0232 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configuration/terminalDebugConfigurationResolver.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Commands, DebugType, runCommand } from '../../common/contributionUtils'; +import { + applyTerminalDefaults, + ITerminalLaunchConfiguration, + ResolvedConfiguration, +} from '../../configuration'; +import { BaseConfigurationResolver } from './baseConfigurationResolver'; +import { guessWorkingDirectory } from './nodeDebugConfigurationResolver'; + +/** + * Configuration provider for node debugging. In order to allow for a + * close to 1:1 drop-in, this is nearly identical to the original vscode- + * node-debug, with support for some legacy options (mern, useWSL) removed. + */ +export class TerminalDebugConfigurationResolver + extends BaseConfigurationResolver + implements vscode.DebugConfigurationProvider +{ + protected async resolveDebugConfigurationAsync( + folder: vscode.WorkspaceFolder | undefined, + config: ResolvedConfiguration, + ): Promise { + if (!config.cwd) { + config.cwd = guessWorkingDirectory(undefined, folder); + } + + if (config.request === 'launch' && !config.command) { + await runCommand(vscode.commands, Commands.CreateDebuggerTerminal, undefined, folder); + return undefined; + } + + // if a 'remoteRoot' is specified without a corresponding 'localRoot', set 'localRoot' to the workspace folder. + // see https://github.com/Microsoft/vscode/issues/63118 + if (config.remoteRoot && !config.localRoot) { + config.localRoot = '${workspaceFolder}'; + } + + return applyTerminalDefaults(config) as ITerminalLaunchConfiguration; + } + + protected getType() { + return DebugType.Terminal as const; + } +} diff --git a/code/extensions/js-debug/src/ui/configurationUtils.ts b/code/extensions/js-debug/src/ui/configurationUtils.ts new file mode 100644 index 000000000000..f138c19c8926 --- /dev/null +++ b/code/extensions/js-debug/src/ui/configurationUtils.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { ResolvingNodeLaunchConfiguration } from '../configuration'; + +/** + * Removes any --inspect-brk flags from the launch configuration and sets + * stopOnEntry instead, otherwise we break inside the bootloader. + */ +export function fixInspectFlags(config: ResolvingNodeLaunchConfiguration) { + if (!config.runtimeArgs || config.attachSimplePort) { + return; + } + + const resolved: string[] = []; + for (const arg of config.runtimeArgs) { + if (/^--inspect-brk(=|$)/.test(arg)) { + config.stopOnEntry = config.stopOnEntry || true; + } else { + resolved.push(arg); + } + } + + config.runtimeArgs = resolved; +} diff --git a/code/extensions/js-debug/src/ui/customBreakpointsUI.ts b/code/extensions/js-debug/src/ui/customBreakpointsUI.ts new file mode 100644 index 000000000000..e045d603c5ab --- /dev/null +++ b/code/extensions/js-debug/src/ui/customBreakpointsUI.ts @@ -0,0 +1,458 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { l10n } from 'vscode'; +import { customBreakpoints, ICustomBreakpoint, IXHRBreakpoint } from '../adapter/customBreakpoints'; +import { Commands, CustomViews } from '../common/contributionUtils'; +import { EventEmitter } from '../common/events'; +import Dap from '../dap/api'; +import { DebugSessionTracker } from './debugSessionTracker'; + +const xhrBreakpointsCategory = () => l10n.t('XHR/Fetch URLs'); +const focusEmulationStorageKey = 'jsDebug.focusEmulation.enabled'; + +class XHRBreakpoint extends vscode.TreeItem { + public get checked() { + return this.checkboxState === vscode.TreeItemCheckboxState.Checked; + } + + match: string; + constructor(xhr: IXHRBreakpoint, enabled: boolean) { + super( + xhr.match ? l10n.t('URL contains "{0}"', xhr.match) : l10n.t('Any XHR or fetch'), + vscode.TreeItemCollapsibleState.None, + ); + this.contextValue = 'xhrBreakpoint'; + this.id = xhr.match; + this.match = xhr.match; + this.checkboxState = enabled + ? vscode.TreeItemCheckboxState.Checked + : vscode.TreeItemCheckboxState.Unchecked; + } + + static compare(a: XHRBreakpoint, b: XHRBreakpoint) { + return a.match.localeCompare(b.match); + } +} + +class Breakpoint extends vscode.TreeItem { + id: string; + group: string; + + public get checked() { + return this.checkboxState === vscode.TreeItemCheckboxState.Checked; + } + + constructor(cb: ICustomBreakpoint, public readonly parent: Category) { + super(cb.title, vscode.TreeItemCollapsibleState.None); + this.checkboxState = vscode.TreeItemCheckboxState.Unchecked; + this.id = cb.id; + this.group = cb.group; + } + + static compare(a: Breakpoint, b: Breakpoint) { + return a.id.localeCompare(b.id); + } +} + +class Category extends vscode.TreeItem { + public readonly children: Breakpoint[] = []; + + public get checked() { + return this.checkboxState === vscode.TreeItemCheckboxState.Checked; + } + + constructor(public readonly label: string) { + super(label, vscode.TreeItemCollapsibleState.Collapsed); + this.checkboxState = vscode.TreeItemCheckboxState.Unchecked; + } +} + +class BreakpointsRoot extends vscode.TreeItem { + public get checked() { + return this.checkboxState === vscode.TreeItemCheckboxState.Checked; + } + + constructor() { + super(l10n.t('Browser Breakpoints'), vscode.TreeItemCollapsibleState.Collapsed); + this.checkboxState = vscode.TreeItemCheckboxState.Unchecked; + this.id = 'browserBreakpoints'; + } +} + +class FocusEmulationOption extends vscode.TreeItem { + constructor(enabled: boolean) { + super(l10n.t('Emulate a focused page'), vscode.TreeItemCollapsibleState.None); + this.checkboxState = enabled + ? vscode.TreeItemCheckboxState.Checked + : vscode.TreeItemCheckboxState.Unchecked; + this.tooltip = l10n.t( + 'When enabled, the debugged page will behave as if it has focus', + ); + this.id = 'focusEmulation'; + } +} + +type BreakpointItem = BreakpointsRoot | Breakpoint | Category | XHRBreakpoint; +type BrowserOptionItem = BreakpointItem | FocusEmulationOption; + +class BrowserOptionsDataProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData = new EventEmitter(); + readonly onDidChangeTreeData = this._onDidChangeTreeData.event; + + private _debugSessionTracker: DebugSessionTracker; + + private readonly _breakpointsRoot = new BreakpointsRoot(); + private readonly categories = new Map(); + xhrBreakpoints: XHRBreakpoint[] = []; + + private readonly _emulationSessions = new Set(); + private _focusEmulationEnabled = false; + + /** Gets all breakpoint categories */ + public get allCategories() { + return this.categories.values(); + } + + /** Gets all custom breakpoints */ + public get allBreakpoints() { + return [...this.allCategories].flatMap(c => c.children); + } + + constructor(debugSessionTracker: DebugSessionTracker, focusEmulationEnabled: boolean) { + this._focusEmulationEnabled = focusEmulationEnabled; + + for (const breakpoint of [...customBreakpoints().values()]) { + let category = this.categories.get(breakpoint.group); + if (!category) { + category = new Category(breakpoint.group); + this.categories.set(breakpoint.group, category); + } + category.children.push(new Breakpoint(breakpoint, category)); + } + + const xhrCategory = new Category(xhrBreakpointsCategory()); + xhrCategory.contextValue = 'xhrCategory'; + xhrCategory.checkboxState = undefined; + this.categories.set(xhrBreakpointsCategory(), xhrCategory); + + this.xhrBreakpoints = []; + + this._debugSessionTracker = debugSessionTracker; + debugSessionTracker.onSessionAdded(session => { + if (!DebugSessionTracker.isConcreteSession(session)) { + return; + } + + const toEnable = this.allBreakpoints.filter(b => b.checked).map(b => b.id); + if (toEnable.length > 0) { + session.customRequest('setCustomBreakpoints', { + xhr: this.xhrBreakpoints.filter(b => b.checkboxState).map(b => b.id), + ids: toEnable, + }); + } + + this._checkEmulationSupport(session); + }); + + debugSessionTracker.onSessionEnded(session => { + if (this._emulationSessions.delete(session.id)) { + this._onDidChangeTreeData.fire(undefined); + } + }); + } + + /** @inheritdoc */ + getTreeItem(item: BrowserOptionItem): vscode.TreeItem { + return item; + } + + /** @inheritdoc */ + getChildren(item?: BrowserOptionItem): vscode.ProviderResult { + if (!item) { + const items: BrowserOptionItem[] = [this._breakpointsRoot]; + if (this._emulationSessions.size > 0) { + items.unshift(new FocusEmulationOption(this._focusEmulationEnabled)); + } + return items; + } + + if (item instanceof BreakpointsRoot) { + return [...this.categories.values()].sort((a, b) => a.label.localeCompare(b.label)); + } + + if (item instanceof Category) { + if (item.contextValue === 'xhrCategory') { + const title = l10n.t('Add new URL...'); + const addNew = new vscode.TreeItem(title) as XHRBreakpoint; + addNew.command = { title, command: Commands.AddXHRBreakpoints }; + return [...this.xhrBreakpoints, addNew]; + } + return this.categories.get(item.label)?.children; + } + + return []; + } + + /** @inheritdoc */ + getParent(item: BrowserOptionItem): vscode.ProviderResult { + if (item instanceof Category) { + return this._breakpointsRoot; + } else if (item instanceof Breakpoint) { + return this.categories.get(item.group); + } else if (item instanceof XHRBreakpoint) { + return this.categories.get(xhrBreakpointsCategory()); + } + + return undefined; + } + + public setFocusEmulation(enabled: boolean): void { + this._focusEmulationEnabled = enabled; + this._onFocusEmulationChanged?.(enabled); + this._applyFocusEmulationToAllSessions(); + this._onDidChangeTreeData.fire(undefined); + } + + /** Updates the enablement state of the breakpoints/categories */ + public setBreakpointsEnabled(breakpoints: [BreakpointItem, boolean][]) { + for (const [breakpoint, enabled] of breakpoints) { + const state = enabled + ? vscode.TreeItemCheckboxState.Checked + : vscode.TreeItemCheckboxState.Unchecked; + + breakpoint.checkboxState = state; + + if (breakpoint instanceof BreakpointsRoot) { + for (const category of this.categories.values()) { + category.checkboxState = state; + for (const child of category.children) { + child.checkboxState = state; + } + } + for (const xhr of this.xhrBreakpoints) { + xhr.checkboxState = state; + } + this.syncXHRCategoryState(); + } else if (breakpoint instanceof Category) { + for (const child of this.getChildren(breakpoint) as XHRBreakpoint[]) { + if (child.checkboxState !== state) { + child.checkboxState = state; + } + } + this._syncBreakpointsRootState(); + } else if (breakpoint instanceof Breakpoint || breakpoint instanceof XHRBreakpoint) { + const parent = this.getParent(breakpoint) as Category; + if (!enabled && parent.checked) { + parent.checkboxState = state; + } else if ( + enabled + && (this.getChildren(parent) as XHRBreakpoint[]).every( + c => c.checked || c.checkboxState == undefined, + ) + ) { + parent.checkboxState = state; + } + this._syncBreakpointsRootState(); + } + } + + this.updateDebuggersState(); + this._onDidChangeTreeData.fire(undefined); + } + + private updateDebuggersState() { + const ids = this.allBreakpoints.filter(b => b.checked).map(b => b.id); + const xhr = this.xhrBreakpoints.filter(b => b.checked).map(b => b.id); + for (const session of this._debugSessionTracker.getConcreteSessions()) { + session.customRequest('setCustomBreakpoints', { xhr, ids }); + } + } + + addXHRBreakpoints(breakpoint: XHRBreakpoint) { + if (this.xhrBreakpoints.some(b => b.id === breakpoint.id)) { + return; + } + + this.xhrBreakpoints.push(breakpoint); + this.updateDebuggersState(); + this.syncXHRCategoryState(); + this._onDidChangeTreeData.fire(undefined); + } + + removeXHRBreakpoint(breakpoint: XHRBreakpoint) { + this.xhrBreakpoints = this.xhrBreakpoints.filter(b => b !== breakpoint); + this.updateDebuggersState(); + this.syncXHRCategoryState(); + this._onDidChangeTreeData.fire(undefined); + } + + syncXHRCategoryState() { + const category = this.categories.get(xhrBreakpointsCategory()); + if (!category) { + return; + } + + if (!this.xhrBreakpoints.length) { + category.checkboxState = undefined; + return; + } + + category.checkboxState = this.xhrBreakpoints.every( + b => b.checkboxState === vscode.TreeItemCheckboxState.Checked, + ) + ? vscode.TreeItemCheckboxState.Checked + : vscode.TreeItemCheckboxState.Unchecked; + } + + private _syncBreakpointsRootState(): void { + const allChecked = [...this.categories.values()].every(c => c.checked); + this._breakpointsRoot.checkboxState = allChecked + ? vscode.TreeItemCheckboxState.Checked + : vscode.TreeItemCheckboxState.Unchecked; + } + + _onFocusEmulationChanged?: (enabled: boolean) => void; + + private async _checkEmulationSupport(session: vscode.DebugSession): Promise { + try { + const result: Dap.CanEmulateResult = await session.customRequest('canEmulate', {}); + if (result.supported) { + this._emulationSessions.add(session.id); + this._onDidChangeTreeData.fire(undefined); + + if (this._focusEmulationEnabled) { + session.customRequest('setFocusEmulation', { enabled: true }); + } + } + } catch { + // Session doesn't support emulation + } + } + + private _applyFocusEmulationToAllSessions(): void { + for (const sessionId of this._emulationSessions) { + const session = this._debugSessionTracker.getById(sessionId); + if (session) { + session.customRequest('setFocusEmulation', { enabled: this._focusEmulationEnabled }); + } + } + } +} + +export function registerCustomBreakpointsUI( + context: vscode.ExtensionContext, + debugSessionTracker: DebugSessionTracker, +) { + const focusEmulationEnabled = context.workspaceState.get(focusEmulationStorageKey, false); + const provider = new BrowserOptionsDataProvider(debugSessionTracker, focusEmulationEnabled); + provider._onFocusEmulationChanged = enabled => + context.workspaceState.update(focusEmulationStorageKey, enabled); + + const view = vscode.window.createTreeView(CustomViews.EventListenerBreakpoints, { + treeDataProvider: provider, + showCollapseAll: true, + manageCheckboxStateManually: true, + }); + + context.subscriptions.push( + view.onDidChangeCheckboxState(async e => { + const breakpointChanges: [BreakpointItem, boolean][] = []; + for (const [item, state] of e.items) { + const enabled = state === vscode.TreeItemCheckboxState.Checked; + if (item instanceof FocusEmulationOption) { + provider.setFocusEmulation(enabled); + } else { + breakpointChanges.push([item as BreakpointItem, enabled]); + } + } + if (breakpointChanges.length) { + provider.setBreakpointsEnabled(breakpointChanges); + } + }), + ); + + context.subscriptions.push(view); + + context.subscriptions.push( + vscode.commands.registerCommand(Commands.ToggleCustomBreakpoints, async () => { + const items: (vscode.QuickPickItem & { id: string })[] = [...provider.allCategories].flatMap( + category => [ + { + kind: vscode.QuickPickItemKind.Separator, + id: '', + label: category.label, + }, + ...category.children.map(bp => ({ + id: bp.id, + label: `${bp.label}`, + picked: bp.checked, + })), + ], + ); + + const picked = await vscode.window.showQuickPick(items, { + canPickMany: true, + placeHolder: 'Select breakpoints to enable', + }); + + if (!picked) { + return; + } + + const pickedSet = new Set(picked.map(i => i.id)); + provider.setBreakpointsEnabled(provider.allBreakpoints.map(i => [i, pickedSet.has(i.id)])); + }), + ); + + context.subscriptions.push( + vscode.commands.registerCommand(Commands.RemoveAllCustomBreakpoints, () => { + provider.setBreakpointsEnabled( + [...provider.allBreakpoints, ...provider.xhrBreakpoints].map(bp => [bp, false]), + ); + }), + ); + + context.subscriptions.push( + vscode.commands.registerCommand(Commands.AddXHRBreakpoints, () => { + const inputBox = vscode.window.createInputBox(); + inputBox.title = l10n.t('Add XHR Breakpoint'); + inputBox.placeholder = l10n.t('Break when URL Contains'); + inputBox.onDidAccept(() => { + const match = inputBox.value; + provider.addXHRBreakpoints(new XHRBreakpoint({ match }, true)); + inputBox.dispose(); + }); + inputBox.show(); + }), + ); + + context.subscriptions.push( + vscode.commands.registerCommand(Commands.EditXHRBreakpoint, (treeItem: vscode.TreeItem) => { + const inputBox = vscode.window.createInputBox(); + inputBox.title = l10n.t('Edit XHR Breakpoint'); + inputBox.placeholder = l10n.t('Enter a URL or a pattern to match'); + inputBox.value = (treeItem as XHRBreakpoint).match; + inputBox.onDidAccept(() => { + const match = inputBox.value; + provider.removeXHRBreakpoint(treeItem as XHRBreakpoint); + provider.addXHRBreakpoints( + new XHRBreakpoint( + { match }, + treeItem.checkboxState == vscode.TreeItemCheckboxState.Checked, + ), + ); + inputBox.dispose(); + }); + inputBox.show(); + }), + ); + + context.subscriptions.push( + vscode.commands.registerCommand(Commands.RemoveXHRBreakpoints, (treeItem: vscode.TreeItem) => { + provider.removeXHRBreakpoint(treeItem as XHRBreakpoint); + }), + ); +} diff --git a/code/extensions/js-debug/src/ui/debugLinkUI.ts b/code/extensions/js-debug/src/ui/debugLinkUI.ts new file mode 100644 index 000000000000..f458e366cfc0 --- /dev/null +++ b/code/extensions/js-debug/src/ui/debugLinkUI.ts @@ -0,0 +1,165 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import * as dns from 'dns/promises'; +import { inject, injectable } from 'inversify'; +import { URL } from 'url'; +import * as vscode from 'vscode'; +import { + Commands, + Configuration, + DebugType, + getPreferredOrDebugType, + readConfig, +} from '../common/contributionUtils'; +import { DefaultBrowser, IDefaultBrowserProvider } from '../common/defaultBrowserProvider'; +import { delay } from '../common/promiseUtil'; +import { ExtensionContext, IExtensionContribution } from '../ioc-extras'; + +async function assertResolves(hostname: string, timeout = 1000) { + return Promise.race([ + dns.lookup(hostname), + delay(timeout), + ]); +} + +async function getPossibleUrl(link: string, requirePort: boolean): Promise { + if (!link) { + return; + } + + // if the link is already valid, all good + try { + const url = new URL(link); + if (url.hostname) { + await assertResolves(url.hostname); // ensure it can be resolved + return link; + } + } catch { + // not a valid link + } + + // if it's in the format `:` then assume it's a url + try { + const prefixed = `http://${link}`; + const url = new URL(prefixed); + await assertResolves(url.hostname); + if (!requirePort || url.port) { + return prefixed; + } + } catch { + // not a valid link + } +} + +@injectable() +export class DebugLinkUi implements IExtensionContribution { + private mostRecentLink: string | undefined; + + constructor( + @inject(IDefaultBrowserProvider) private defaultBrowser: IDefaultBrowserProvider, + @inject(ExtensionContext) private context: vscode.ExtensionContext, + ) {} + + /** + * Registers the link UI for the extension. + */ + public register(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.commands.registerCommand(Commands.DebugLink, link => this.handle(link)), + ); + } + + /** + * Handles a command, optionally called with a link. + */ + public async handle(link?: string) { + link = link ?? (await this.getLinkFromTextEditor()) ?? (await this.getLinkFromQuickInput()); + if (!link) { + return; + } + + let debugType: DebugType.Chrome | DebugType.Edge = DebugType.Chrome; + try { + if ((await this.defaultBrowser.lookup()) === DefaultBrowser.Edge) { + debugType = DebugType.Edge; + } + } catch { + // ignored + } + + const baseConfig = readConfig(vscode.workspace, Configuration.DebugByLinkOptions) ?? {}; + const config = { + ...(typeof baseConfig === 'string' ? {} : baseConfig), + type: getPreferredOrDebugType(debugType), + name: link, + request: 'launch', + url: link, + }; + + vscode.debug.startDebugging(vscode.workspace.workspaceFolders?.[0], config); + this.persistConfig(config); + } + + private getLinkFromTextEditor() { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + + return getPossibleUrl(editor.document.getText(editor.selection), true); + } + + private async getLinkFromQuickInput() { + const clipboard = await vscode.env.clipboard.readText(); + const link = await vscode.window.showInputBox({ + value: await getPossibleUrl(clipboard, false) || this.mostRecentLink, + placeHolder: 'https://localhost:8080', + validateInput: input => { + if (input && !URL.canParse(input)) { + return l10n.t('The URL provided is invalid'); + } + }, + }); + + if (!link) { + return; + } + + this.mostRecentLink = link; + return link; + } + + private async persistConfig(config: { url: string }) { + if (this.context.globalState.get('saveDebugLinks') === false) { + return; + } + + const launchJson = vscode.workspace.getConfiguration('launch'); + const configs = (launchJson.get('configurations') ?? []) as { url?: string }[]; + if (configs.some(c => c.url === config.url)) { + return; + } + + const yes = l10n.t('Yes'); + const never = l10n.t('Never'); + const r = await vscode.window.showInformationMessage( + l10n.t('Would you like to save a configuration in your launch.json for easy access later?'), + yes, + l10n.t('No'), + never, + ); + + if (r === never) { + this.context.globalState.update('saveDebugLinks', false); + } + + if (r !== yes) { + return; + } + + await launchJson.update('configurations', [...configs, config]); + } +} diff --git a/code/extensions/js-debug/src/ui/debugNpmScript.ts b/code/extensions/js-debug/src/ui/debugNpmScript.ts new file mode 100644 index 000000000000..c94f33a664f5 --- /dev/null +++ b/code/extensions/js-debug/src/ui/debugNpmScript.ts @@ -0,0 +1,216 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { Commands, runCommand } from '../common/contributionUtils'; +import { readfile } from '../common/fsUtils'; +import { getRunScriptCommand } from './getRunScriptCommand'; + +interface IScript { + directory: string; + name: string; + command: string; +} + +type ScriptPickItem = vscode.QuickPickItem & { script?: IScript }; + +/** + * Opens a quickpick and them subsequently debugs a configured npm script. + * @param inFolder - Optionally scopes lookups to the given workspace folder + */ +export async function debugNpmScript(inFolder?: vscode.WorkspaceFolder | string) { + const scripts = await findScripts(inFolder ? [inFolder] : undefined); + if (!scripts) { + return; // cancelled + } + + const runScript = async (script: IScript) => { + const workspaceFolder = vscode.workspace.getWorkspaceFolder( + vscode.Uri.file(script.directory), + ); + runCommand( + vscode.commands, + Commands.CreateDebuggerTerminal, + await getRunScriptCommand(script.name, workspaceFolder), + workspaceFolder, + { cwd: script.directory }, + ); + }; + + if (scripts.length === 1) { + return runScript(scripts[0]); + } + + // For multi-root workspaces, prefix the script name with the workspace + // directory name so the user knows where it's coming from. + const multiDir = scripts.some(s => s.directory !== scripts[0].directory); + const quickPick = vscode.window.createQuickPick(); + + let lastDir: string | undefined; + const items: ScriptPickItem[] = []; + for (const script of scripts) { + if (script.directory !== lastDir && multiDir) { + items.push({ + label: path.basename(script.directory), + kind: vscode.QuickPickItemKind.Separator, + }); + lastDir = script.directory; + } + + items.push({ + script, + label: script.name, + description: script.command, + }); + } + quickPick.items = items; + + quickPick.onDidAccept(async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + runScript(quickPick.selectedItems[0].script!); + quickPick.dispose(); + }); + + quickPick.show(); +} + +interface IEditCandidate { + path?: string; + score: number; +} + +const updateEditCandidate = (existing: IEditCandidate, updated: IEditCandidate) => + existing.score > updated.score ? existing : updated; + +/** + * Finds configured npm scripts in the workspace. + */ +export async function findScripts( + inFolders: (vscode.WorkspaceFolder | string)[] | undefined, + silent = false, +): Promise { + const folders = inFolders ?? vscode.workspace.workspaceFolders ?? []; + + // 1. If there are no open folders, show an error and abort. + if (!folders || folders.length === 0) { + if (!silent) { + vscode.window.showErrorMessage( + l10n.t('You need to open a workspace folder to debug npm scripts.'), + ); + } + return; + } + + // Otherwise, go through all package.json's in the folder and pull all the npm scripts we find. + const candidates = ( + await Promise.all( + folders.map(f => + vscode.workspace.findFiles( + new vscode.RelativePattern(f, '**/package.json'), + // matches https://github.com/microsoft/vscode/blob/18f743d534ef3f528c5e81e82e695b87c60d2ebf/extensions/npm/src/tasks.ts#L189 + '**/{node_modules,.vscode-test}/**', + ) + ), + ) + ).flat(); + + if (candidates.length === 0) { + if (!silent) { + vscode.window.showErrorMessage(l10n.t('No package.json files found in your workspace.')); + } + return; + } + + const scripts: IScript[] = []; + + // editCandidate is the file we'll edit if we don't find any npm scripts. + // We 'narrow' this as we parse to files that look more like a package.json we want + let editCandidate: IEditCandidate = { path: candidates[0].fsPath, score: 0 }; + for (const { fsPath } of new Set(candidates)) { + // update this now, because we know it exists + editCandidate = updateEditCandidate(editCandidate, { + path: fsPath, + score: 1, + }); + + let parsed: { scripts?: { [key: string]: string } }; + try { + parsed = JSON.parse(await readfile(fsPath)); + } catch (e) { + if (!silent) { + promptToOpen( + 'showWarningMessage', + l10n.t('Could not read {0}: {1}', fsPath, e.message), + fsPath, + ); + } + // set the candidate to 'undefined', since we already displayed an error + // and if there are no other candidates then that alone is fine. + editCandidate = updateEditCandidate(editCandidate, { path: undefined, score: 3 }); + continue; + } + + // update this now, because we know it is valid + editCandidate = updateEditCandidate(editCandidate, { path: undefined, score: 2 }); + + if (!parsed.scripts) { + continue; + } + + for (const key of Object.keys(parsed.scripts)) { + scripts.push({ + directory: path.dirname(fsPath), + name: key, + command: parsed.scripts[key], + }); + } + } + + if (scripts.length === 0) { + if (editCandidate.path && !silent) { + promptToOpen( + 'showErrorMessage', + l10n.t('No npm scripts found in your package.json'), + editCandidate.path, + ); + } + return; + } + + scripts.sort((a, b) => (a.name === 'start' ? -1 : 0) + (b.name === 'start' ? 1 : 0)); + + return scripts; +} + +const defaultPackageJsonContents = `{\n "scripts": {\n \n }\n}\n`; + +async function promptToOpen( + method: 'showWarningMessage' | 'showErrorMessage', + message: string, + file: string, +) { + const openAction = l10n.t('Edit package.json'); + if ((await vscode.window[method](message, openAction)) !== openAction) { + return; + } + + // If the file exists, open it, otherwise create a new untitled file and + // fill it in with some minimal "scripts" section. + if (fs.existsSync(file)) { + const document = await vscode.workspace.openTextDocument(file); + await vscode.window.showTextDocument(document); + return; + } + + const document = await vscode.workspace.openTextDocument( + vscode.Uri.file(file).with({ scheme: 'untitled' }), + ); + const editor = await vscode.window.showTextDocument(document); + await editor.edit(e => e.insert(new vscode.Position(0, 0), defaultPackageJsonContents)); + const pos = new vscode.Position(2, 5); + editor.selection = new vscode.Selection(pos, pos); +} diff --git a/code/extensions/js-debug/src/ui/debugSessionTracker.ts b/code/extensions/js-debug/src/ui/debugSessionTracker.ts new file mode 100644 index 000000000000..29c277d4d9c9 --- /dev/null +++ b/code/extensions/js-debug/src/ui/debugSessionTracker.ts @@ -0,0 +1,154 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { isDebugType } from '../common/contributionUtils'; +import Dap from '../dap/api'; + +/** + * Keeps a list of known js-debug sessions. + */ +@injectable() +export class DebugSessionTracker implements vscode.Disposable { + /** + * Returns whether the session is a concrete debug + * session -- that is, not a logical session wrapper. + */ + public static isConcreteSession(session: vscode.DebugSession) { + return !!session.configuration.__pendingTargetId; + } + + /** + * Prompts the user to pick one of the given debug sessions. Will not show + * a prompt if candidates < 2. + */ + public static pickSession(candidates: vscode.DebugSession[], title: string) { + if (candidates.length < 2) { + return candidates[0]; + } + + const qp = vscode.window.createQuickPick<{ id: string; label: string }>(); + qp.title = title; + qp.items = candidates.map(c => ({ label: c.name, id: c.id })); + qp.ignoreFocusOut = true; + + return new Promise(resolve => { + qp.onDidAccept(() => resolve(candidates.find(i => i.id === qp.selectedItems[0]?.id))); + qp.onDidHide(() => resolve(undefined)); + qp.show(); + }).finally(() => qp.dispose()); + } + + private _onSessionAddedEmitter = new vscode.EventEmitter(); + private _onSessionEndedEmitter = new vscode.EventEmitter(); + private _disposables: vscode.Disposable[] = []; + private readonly sessions = new Map(); + + /** + * Fires when any new js-debug session comes in. + */ + public onSessionAdded = this._onSessionAddedEmitter.event; + + /** + * Fires when any js-debug session ends. + */ + public onSessionEnded = this._onSessionEndedEmitter.event; + + /** + * Gets whether there's any active JS debug session. + */ + public get isDebugging() { + return this.sessions.size > 0; + } + + /** + * Returns the session with the given ID. + */ + public getById(id: string) { + return this.sessions.get(id); + } + + /** + * Gets whether the js-debug session is still running. + */ + public isRunning(session: vscode.DebugSession) { + return [...this.sessions.values()].includes(session); + } + + /** + * Returns a list of sessions with the given debug session name. + */ + public getByName(name: string) { + return [...this.sessions.values()].filter(s => s.name === name); + } + + /** + * Gets physical debug sessions -- that is, avoids the logical session wrapper. + */ + public getConcreteSessions() { + return [...this.sessions.values()].filter(DebugSessionTracker.isConcreteSession); + } + + /** + * Gets all direct children of the given session. + */ + public getChildren(session: vscode.DebugSession) { + return [...this.sessions.values()].filter(s => s.configuration.__parentId === session.id); + } + + public attach() { + vscode.debug.onDidStartDebugSession( + session => { + if (isDebugType(session.type)) { + this.sessions.set(session.id, session); + this._onSessionAddedEmitter.fire(session); + } + }, + undefined, + this._disposables, + ); + + vscode.debug.onDidTerminateDebugSession( + session => { + if (isDebugType(session.type)) { + this.sessions.delete(session.id); + this._onSessionEndedEmitter.fire(session); + } + }, + undefined, + this._disposables, + ); + + // todo: move this into its own class + vscode.debug.onDidReceiveDebugSessionCustomEvent( + event => { + if (!isDebugType(event.session.type)) { + return; + } + + if (event.event === 'revealLocationRequested') { + const params = event.body as Dap.RevealLocationRequestedEventParams; + const uri = vscode.debug.asDebugSourceUri(event.body.source); + const options: vscode.TextDocumentShowOptions = {}; + if (params.line) { + const position = new vscode.Position((params.line || 1) - 1, (params.column || 1) - 1); + options.selection = new vscode.Range(position, position); + } + vscode.window.showTextDocument(uri, options); + } else if (event.event === 'copyRequested') { + const params = event.body as Dap.CopyRequestedEventParams; + vscode.env.clipboard.writeText(params.text); + } + }, + undefined, + this._disposables, + ); + } + + dispose() { + for (const disposable of this._disposables) disposable.dispose(); + this._disposables = []; + } +} diff --git a/code/extensions/js-debug/src/ui/debugSessionTunnels.ts b/code/extensions/js-debug/src/ui/debugSessionTunnels.ts new file mode 100644 index 000000000000..79d8349245bf --- /dev/null +++ b/code/extensions/js-debug/src/ui/debugSessionTunnels.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { DisposableList, IDisposable } from '../common/disposable'; + +/** + * Simple tracker class that allows up to one Tunnel per debug session, + * and disposes the tunnels when the session ends. + */ +export class DebugSessionTunnels implements IDisposable { + private readonly tunnels = new Map(); + private readonly disposable = new DisposableList(); + + constructor() { + this.disposable.push( + vscode.debug.onDidTerminateDebugSession(session => this.destroySession(session.id)), + ); + } + + /** + * @inheritdoc + */ + public dispose() { + return this.disposable.dispose(); + } + + /** + * Removes a session tunnel if it exists. + */ + public destroySession(sessionId: string) { + const tunnel = this.tunnels.get(sessionId); + if (tunnel) { + tunnel.dispose(); + this.tunnels.delete(sessionId); + } + } + + /** + * Requests a tunnel. Note that if a tunnel was previously created for the + * session, it'll be returned regardless of the localPort/remotePort. + */ + public async request( + sessionId: string, + opts: { + label: string; + localPort?: number; + remotePort: number; + }, + ) { + let tunnel = this.tunnels.get(sessionId); + if (!tunnel) { + tunnel = await vscode.workspace.openTunnel({ + remoteAddress: { port: opts.remotePort, host: 'localhost' }, + localAddressPort: opts.localPort ?? opts.remotePort, + label: opts.label, + }); + this.tunnels.set(sessionId, tunnel); + } + + let localAddress: { host: string; port: number }; + if (typeof tunnel.localAddress === 'string') { + const [host, port] = tunnel.localAddress.split(':'); + localAddress = { host, port: Number(port) }; + } else { + localAddress = tunnel.localAddress; + } + + return { + remoteAddress: tunnel.remoteAddress, + localAddress, + }; + } +} diff --git a/code/extensions/js-debug/src/ui/debugTerminalUI.ts b/code/extensions/js-debug/src/ui/debugTerminalUI.ts new file mode 100644 index 000000000000..34ed98255985 --- /dev/null +++ b/code/extensions/js-debug/src/ui/debugTerminalUI.ts @@ -0,0 +1,321 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { Container } from 'inversify'; +import { homedir } from 'os'; +import * as vscode from 'vscode'; +import { IPortLeaseTracker } from '../adapter/portLeaseTracker'; +import { NeverCancelled } from '../common/cancellation'; +import { + Commands, + Configuration, + DebugType, + readConfig, + registerCommand, +} from '../common/contributionUtils'; +import { EventEmitter } from '../common/events'; +import { ProxyLogger } from '../common/logging/proxyLogger'; +import { ITerminalLinkProvider } from '../common/terminalLinkProvider'; +import { + applyDefaults, + ITerminalLaunchConfiguration, + terminalBaseDefaults, +} from '../configuration'; +import { createPendingDapApi } from '../dap/pending-api'; +import { FS, IDebugTerminalOptionsProviders } from '../ioc-extras'; +import { DelegateLauncherFactory } from '../targets/delegate/delegateLauncherFactory'; +import { NodeBinaryProvider } from '../targets/node/nodeBinaryProvider'; +import { noPackageJsonProvider } from '../targets/node/packageJsonProvider'; +import { ITerminalLauncherLike, TerminalNodeLauncher } from '../targets/node/terminalNodeLauncher'; +import { NodeOnlyPathResolverFactory } from '../targets/sourcePathResolverFactory'; +import { MutableTargetOrigin } from '../targets/targetOrigin'; +import { ITarget } from '../targets/targets'; +import { DapTelemetryReporter } from '../telemetry/dapTelemetryReporter'; + +export const launchVirtualTerminalParent = ( + delegate: DelegateLauncherFactory, + launcher: ITerminalLauncherLike, + options: Partial = {}, + filterTarget: (target: ITarget) => boolean = () => true, +) => { + const telemetry = new DapTelemetryReporter(); + const baseDebugOptions: Partial = { + ...readConfig(vscode.workspace, Configuration.TerminalDebugConfig), + // Prevent switching over the the Debug Console whenever a process starts + internalConsoleOptions: 'neverOpen', + }; + + // We don't have a debug session initially when we launch the terminal, so, + // we create a shell DAP instance that queues messages until it gets attached + // to a connection. Terminal processes don't use this too much except for + // telemetry. + const dap = createPendingDapApi(); + telemetry.attachDap(dap); + + // Watch the set of targets we get from this terminal launcher. Remember + // that we can get targets from child processes of session too. When we + // get a new top-level target (one without a parent session), start + // debugging. Removing delegated targets will automatically end debug + // sessions. Once all are removed, reset the DAP since we'll get a new + // instance for the next process that starts. + let previousTargets = new Set(); + + // Gets the ideal workspace folder for the given process. + const getWorkingDirectory = async (target: ITarget) => { + const telemetry = await launcher.getProcessTelemetry(target); + const fromTelemetry = telemetry && vscode.Uri.file(telemetry.cwd); + const preferred = fromTelemetry && vscode.workspace.getWorkspaceFolder(fromTelemetry); + if (preferred) { + return preferred.uri; + } + + if (options.__workspaceFolder) { + return vscode.Uri.file(options.__workspaceFolder); + } + + return vscode.workspace.workspaceFolders?.[0].uri ?? fromTelemetry; + }; + + launcher.onTargetListChanged(async () => { + const trusted = await vscode.workspace.requestWorkspaceTrust(); + const newTargets = new Set(); + for (const target of launcher.targetList()) { + newTargets.add(target); + + if (previousTargets.has(target)) { + previousTargets.delete(target); + continue; + } + + const delegateId = delegate.addDelegate(target, dap, target.parent()); + + // Skip targets the consumer asked to filter out. + if (!filterTarget(target)) { + target.detach(); + continue; + } + + // Check that we didn't detach from the parent session. + if (target.targetInfo.openerId && !target.parent()) { + target.detach(); + continue; + } + + // Detach from targets if workspace trust was not granted + if (!trusted) { + target.detach(); + continue; + } + + if (!target.parent()) { + const cwd = await getWorkingDirectory(target); + vscode.debug.startDebugging(cwd && vscode.workspace.getWorkspaceFolder(cwd), { + ...baseDebugOptions, + type: DebugType.Terminal, + name: 'Node.js Process', + request: 'attach', + delegateId, + cwd: cwd?.fsPath, + __workspaceFolder: cwd, + }); + } + } + + for (const target of previousTargets) { + delegate.removeDelegate(target); + } + + previousTargets = newTargets; + }); + + // Create a 'fake' launch request to the terminal, and run it! + return launcher.launch( + applyDefaults({ + ...baseDebugOptions, + type: DebugType.Terminal, + name: terminalBaseDefaults.name, + request: 'launch', + ...options, + }), + { + dap, + telemetryReporter: telemetry, + cancellationToken: NeverCancelled, + get targetOrigin() { + // Use a getter so that each new session receives a new mutable origin. + // This is needed so that processes booted in parallel each get their + // own apparent debug session. + return new MutableTargetOrigin(''); + }, + }, + ); +}; + +const Abort = Symbol('Abort'); + +const home = homedir(); +const tildify: (s: string) => string = process.platform === 'win32' + ? s => s + : s => (s.startsWith(home) ? `~${s.slice(home.length)}` : s); + +async function getWorkspaceFolder() { + const folders = vscode.workspace.workspaceFolders; + if (!folders || folders.length < 2) { + return folders?.[0]; + } + + const picked = await vscode.window.showQuickPick( + folders.map(folder => ({ + label: folder.name, + description: tildify(folder.uri.fsPath), + folder, + })), + { + placeHolder: l10n.t('Select current working directory for new terminal'), + }, + ); + + return picked?.folder ?? Abort; +} + +class ProfileTerminalLauncher extends TerminalNodeLauncher { + private optionsReadyEmitter = new EventEmitter(); + public readonly onOptionsReady = this.optionsReadyEmitter.event; + + /** @override */ + protected createTerminal(options: vscode.TerminalOptions) { + this.optionsReadyEmitter.fire(options); + return new Promise(resolve => { + const listener = vscode.window.onDidOpenTerminal(t => { + listener.dispose(); + resolve(t); + }); + }); + } +} + +/** + * Registers a command to launch the debugger terminal. + */ +export function registerDebugTerminalUI( + context: vscode.ExtensionContext, + delegateFactory: DelegateLauncherFactory, + services: Container, +) { + const terminals = new Map< + vscode.Terminal, + { launcher: TerminalNodeLauncher; folder?: vscode.WorkspaceFolder; cwd?: string } + >(); + + const createLauncher = (logger: ProxyLogger, binary: NodeBinaryProvider) => + new TerminalNodeLauncher( + binary, + logger, + services.get(FS), + services.get(NodeOnlyPathResolverFactory), + services.get(IPortLeaseTracker), + services.get(IDebugTerminalOptionsProviders), + services.get(ITerminalLinkProvider), + ); + + /** + * See docblocks on {@link DelegateLauncher} for more information on + * how this works. + */ + async function launchTerminal( + delegate: DelegateLauncherFactory, + command?: string, + workspaceFolder?: vscode.WorkspaceFolder, + defaultConfig?: Partial, + createLauncherFn = createLauncher, + ) { + if (!workspaceFolder) { + const picked = await getWorkspaceFolder(); + if (picked === Abort) { + return; + } + + workspaceFolder = picked; + } + + // try to reuse a terminal if invoked programmatically to run a command + if (command) { + for (const [terminal, config] of terminals) { + if ( + config.folder === workspaceFolder + && config.cwd === defaultConfig?.cwd + && !config.launcher.targetList().length + ) { + terminal.show(true); + terminal.sendText(command); + return; + } + } + } + + const logger = new ProxyLogger(); + const launcher = createLauncherFn( + logger, + new NodeBinaryProvider(logger, services.get(FS), noPackageJsonProvider, {}), + ); + + launcher.onTerminalCreated(terminal => { + terminals.set(terminal, { launcher, folder: workspaceFolder, cwd: defaultConfig?.cwd }); + }); + + try { + await launchVirtualTerminalParent( + delegate, + launcher, + { + command, + ...defaultConfig, + __workspaceFolder: workspaceFolder?.uri.fsPath, + }, + () => { + for (const [terminal, rec] of terminals) { + if (rec.launcher === launcher && terminal.state.isInteractedWith) { + return true; + } + } + return false; + }, + ); + } catch (e) { + vscode.window.showErrorMessage(e.message); + } + } + + context.subscriptions.push( + vscode.window.onDidCloseTerminal(terminal => { + terminals.delete(terminal); + }), + registerCommand( + vscode.commands, + Commands.CreateDebuggerTerminal, + (command, folder, config) => launchTerminal(delegateFactory, command, folder, config), + ), + vscode.window.registerTerminalProfileProvider('extension.js-debug.debugTerminal', { + provideTerminalProfile: () => + new Promise(resolve => + launchTerminal(delegateFactory, undefined, undefined, undefined, (logger, binary) => { + const launcher = new ProfileTerminalLauncher( + binary, + logger, + services.get(FS), + services.get(NodeOnlyPathResolverFactory), + services.get(IPortLeaseTracker), + services.get(IDebugTerminalOptionsProviders), + services.get(ITerminalLinkProvider), + ); + + launcher.onOptionsReady(options => resolve(new vscode.TerminalProfile(options))); + + return launcher; + }) + ), + }), + ); +} diff --git a/code/extensions/js-debug/src/ui/diagnosticsUI.ts b/code/extensions/js-debug/src/ui/diagnosticsUI.ts new file mode 100644 index 000000000000..96da9e6a8797 --- /dev/null +++ b/code/extensions/js-debug/src/ui/diagnosticsUI.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { Commands, Contributions, isDebugType, registerCommand } from '../common/contributionUtils'; +import { ExtensionContext, FS, FsPromises, IExtensionContribution } from '../ioc-extras'; +import { DebugSessionTracker } from './debugSessionTracker'; + +const neverRemindKey = 'neverRemind'; + +@injectable() +export class DiagnosticsUI implements IExtensionContribution { + private dismissedForSession = false; + private isPrompting = false; + + constructor( + @inject(FS) private readonly fs: FsPromises, + @inject(ExtensionContext) private readonly context: vscode.ExtensionContext, + @inject(DebugSessionTracker) private readonly tracker: DebugSessionTracker, + ) {} + + public register(context: vscode.ExtensionContext) { + context.subscriptions.push( + registerCommand(vscode.commands, Commands.GetDiagnosticLogs, async () => { + const session = await this.getTargetSession(); + if (!session) { + return; + } + + const uri = await vscode.window.showSaveDialog({ filters: { JSON: ['json'] } }); + if (uri) { + session.customRequest('saveDiagnosticLogs', { + toFile: uri.fsPath, + }); + } + }), + registerCommand( + vscode.commands, + Commands.CreateDiagnostics, + async () => this.getDiagnosticInfo(await this.getTargetSession()), + ), + vscode.debug.onDidReceiveDebugSessionCustomEvent(async evt => { + if (evt.event === 'openDiagnosticTool') { + return this.openDiagnosticTool(evt.body.file); + } + + if ( + evt.event !== 'suggestDiagnosticTool' + || this.dismissedForSession + || this.context.workspaceState.get(neverRemindKey) + || this.isPrompting + ) { + return; + } + + this.isPrompting = true; + + const yes = l10n.t('Yes'); + const notNow = l10n.t('Not Now'); + const never = l10n.t('Never'); + const response = await vscode.window.showInformationMessage( + 'It looks like you might be having trouble with breakpoints. Would you like to open our diagnostic tool?', + yes, + notNow, + never, + ); + + this.isPrompting = false; + + switch (response) { + case yes: + this.getDiagnosticInfo(await this.getTargetSession(), true); + break; + case never: + context.workspaceState.update(neverRemindKey, true); + break; + case notNow: + this.dismissedForSession = true; + break; + } + }), + ); + } + + private getTargetSession() { + const active = vscode.debug.activeDebugSession; + if (!active || !isDebugType(active?.type)) { + return this.pickSession(); + } + + if (DebugSessionTracker.isConcreteSession(active)) { + return active; + } + + const children = this.tracker.getChildren(active); + if (children.length === 1) { + return children[0]; + } + + return this.pickSession(); + } + + private pickSession() { + return DebugSessionTracker.pickSession( + this.tracker.getConcreteSessions(), + l10n.t('Select the session you want to inspect:'), + ); + } + + private async getDiagnosticInfo( + session: vscode.DebugSession | undefined, + fromSuggestion = false, + ) { + if (!session || !this.tracker.isRunning(session)) { + vscode.window.showErrorMessage( + l10n.t( + 'It looks like your debug session has already ended. Try debugging again, then run the "Debug: Diagnose Breakpoint Problems" command.', + ), + ); + + return; + } + + const { file } = await session.customRequest('createDiagnostics', { fromSuggestion }); + await this.openDiagnosticTool(file); + } + + private async openDiagnosticTool(file: string) { + const panel = vscode.window.createWebviewPanel( + Contributions.DiagnosticsView, + 'Debug Diagnostics', + vscode.ViewColumn.Active, + { + enableScripts: true, + }, + ); + + panel.webview.html = await this.fs.readFile(file, 'utf-8'); + } +} diff --git a/code/extensions/js-debug/src/ui/disableSourceMapUI.ts b/code/extensions/js-debug/src/ui/disableSourceMapUI.ts new file mode 100644 index 000000000000..85748e381999 --- /dev/null +++ b/code/extensions/js-debug/src/ui/disableSourceMapUI.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { ExtensionContext } from 'vscode'; +import { Configuration, isDebugType, readConfig, writeConfig } from '../common/contributionUtils'; +import Dap from '../dap/api'; +import { IExtensionContribution } from '../ioc-extras'; + +@injectable() +export class DisableSourceMapUI implements IExtensionContribution { + public register(context: ExtensionContext) { + context.subscriptions.push( + vscode.debug.onDidReceiveDebugSessionCustomEvent(evt => { + if (evt.event !== 'suggestDisableSourcemap' || !isDebugType(evt.session.type)) { + return; + } + + const body = evt.body as Dap.SuggestDisableSourcemapEventParams; + this.unmap(evt.session, body.source).catch(err => + vscode.window.showErrorMessage(err.message) + ); + }), + ); + } + + private async unmap(session: vscode.DebugSession, source: Dap.Source) { + const autoUnmap = readConfig(vscode.workspace, Configuration.UnmapMissingSources); + if (autoUnmap || (await this.prompt())) { + await session.customRequest('disableSourcemap', { source }); + } + } + + private async prompt() { + const always = l10n.t('Always'); + const alwayInWorkspace = l10n.t('Always in this Workspace'); + const yes = l10n.t('Yes'); + + const result = await vscode.window.showInformationMessage( + l10n.t( + 'This is a missing file path referenced by a sourcemap. Would you like to debug the compiled version instead?', + ), + always, + alwayInWorkspace, + l10n.t('No'), + yes, + ); + + switch (result) { + case always: + writeConfig( + vscode.workspace, + Configuration.UnmapMissingSources, + true, + vscode.ConfigurationTarget.Global, + ); + return true; + case alwayInWorkspace: + writeConfig( + vscode.workspace, + Configuration.UnmapMissingSources, + true, + vscode.ConfigurationTarget.Workspace, + ); + return true; + case yes: + return true; + default: + return false; + } + } +} diff --git a/code/extensions/js-debug/src/ui/dwarfModuleProviderImpl.ts b/code/extensions/js-debug/src/ui/dwarfModuleProviderImpl.ts new file mode 100644 index 000000000000..5059c87610f5 --- /dev/null +++ b/code/extensions/js-debug/src/ui/dwarfModuleProviderImpl.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import type * as dwf from '@vscode/dwarf-debugging'; +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { IDwarfModuleProvider } from '../adapter/dwarf/dwarfModuleProvider'; +import { ExtensionContext } from '../ioc-extras'; + +const EXT_ID = 'ms-vscode.wasm-dwarf-debugging'; +const NEVER_REMIND = 'dwarf.neverRemind'; + +@injectable() +export class DwarfModuleProvider implements IDwarfModuleProvider { + private didPromptForSession = this.context.workspaceState.get(NEVER_REMIND, false); + + constructor(@inject(ExtensionContext) private readonly context: vscode.ExtensionContext) {} + + /** @inheritdoc */ + public async load(): Promise { + try { + // for development, use the module to avoid having to install the extension + return await import('@vscode/dwarf-debugging'); + } catch { + // fall through + } + + const ext = vscode.extensions.getExtension(EXT_ID); + if (!ext) { + return undefined; + } + if (!ext.isActive) { + await ext.activate(); + } + + return ext.exports; + } + + /** @inheritdoc */ + public async prompt() { + if (this.didPromptForSession) { + return; + } + + this.didPromptForSession = true; + + const yes = l10n.t('Yes'); + const never = l10n.t('Never'); + const response = await vscode.window.showInformationMessage( + l10n.t({ + message: + 'VS Code can provide better debugging experience for WebAssembly via "DWARF Debugging" extension. Would you like to install it?', + comment: '"DWARF Debugging" is the extension name and should not be localized.', + }), + yes, + l10n.t('Not Now'), + never, + ); + + if (response === yes) { + this.install(); + } else if (response === never) { + this.context.workspaceState.update(NEVER_REMIND, true); + } + } + + private async install() { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: l10n.t('Installing the DWARF debugger...'), + }, + async () => { + try { + await vscode.commands.executeCommand('workbench.extensions.installExtension', EXT_ID); + vscode.window.showInformationMessage( + l10n.t( + 'Installation complete! The extension will be used after you restart your debug session.', + ), + ); + } catch (e) { + vscode.window.showErrorMessage(e.message || String(e)); + } + }, + ); + } +} diff --git a/code/extensions/js-debug/src/ui/edgeDevToolOpener.ts b/code/extensions/js-debug/src/ui/edgeDevToolOpener.ts new file mode 100644 index 000000000000..5869800e350d --- /dev/null +++ b/code/extensions/js-debug/src/ui/edgeDevToolOpener.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { Commands, DebugType, registerCommand } from '../common/contributionUtils'; +import { IExtensionContribution } from '../ioc-extras'; +import { BrowserTargetType } from '../targets/browser/browserTargets'; +import { DebugSessionTracker } from './debugSessionTracker'; + +const qualifies = (session: vscode.DebugSession) => { + if (session?.type !== DebugType.Edge) { + return false; + } + + const type: BrowserTargetType = session.configuration.__browserTargetType; + return type === BrowserTargetType.IFrame || type === BrowserTargetType.Page; +}; + +const toolExtensionId = 'ms-edgedevtools.vscode-edge-devtools'; +const commandId = 'vscode-edge-devtools.attachToCurrentDebugTarget'; + +function findRootSession(session: vscode.DebugSession): vscode.DebugSession { + let root = session; + while (root.parentSession) { + root = root.parentSession; + } + return root; +} + +@injectable() +export class EdgeDevToolOpener implements IExtensionContribution { + constructor(@inject(DebugSessionTracker) private readonly tracker: DebugSessionTracker) {} + + /** @inheritdoc */ + public register(context: vscode.ExtensionContext) { + context.subscriptions.push( + registerCommand(vscode.commands, Commands.OpenEdgeDevTools, async () => { + const session = + vscode.debug.activeDebugSession && qualifies(vscode.debug.activeDebugSession) + ? vscode.debug.activeDebugSession + : await DebugSessionTracker.pickSession( + this.tracker.getConcreteSessions().filter(qualifies), + l10n.t('Select the page where you want to open the devtools'), + ); + + if (!session) { + return; + } + + const rootSession = findRootSession(session); + + try { + return await vscode.commands.executeCommand( + commandId, + session.id, + rootSession.configuration, + ); + } catch (e) { + if (e instanceof Error && /command .+ not found/.test(e.message)) { + return vscode.commands.executeCommand( + 'workbench.extensions.action.showExtensionsWithIds', + [toolExtensionId], + ); + } else { + throw e; + } + } + }), + ); + } +} diff --git a/code/extensions/js-debug/src/ui/excludedCallersUI.ts b/code/extensions/js-debug/src/ui/excludedCallersUI.ts new file mode 100644 index 000000000000..5dc2ad1515c7 --- /dev/null +++ b/code/extensions/js-debug/src/ui/excludedCallersUI.ts @@ -0,0 +1,172 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { createHash } from 'crypto'; +import { inject, injectable } from 'inversify'; +import { basename } from 'path'; +import * as vscode from 'vscode'; +import { + Commands, + ContextKey, + CustomViews, + registerCommand, + setContextKey, +} from '../common/contributionUtils'; +import type Dap from '../dap/api'; +import { IExtensionContribution } from '../ioc-extras'; +import { DebugSessionTracker } from './debugSessionTracker'; + +interface ICallerWithName extends Dap.CallerLocation { + name: string; +} + +const locationLabel = ({ name, line, column, source }: ICallerWithName) => + `${name} (${basename(String(source.path))}:${line}:${column})`; + +const fullLabel = ({ name, line, column, source }: ICallerWithName) => + `${name} (${source.path}:${line}:${column})`; + +const revealLocation = async ({ line, column, source }: Dap.CallerLocation) => { + if (source.sourceReference !== 0 || !source.path) { + return; + } + + const uri = vscode.Uri.file(source.path); + const doc = await vscode.workspace.openTextDocument(uri); + const editor = await vscode.window.showTextDocument(doc); + const position = new vscode.Position(line - 1, column - 1); + editor.revealRange(new vscode.Range(position, position)); + editor.selection = new vscode.Selection(position, position); +}; + +export class ExcludedCaller { + public readonly treeItem: vscode.TreeItem; + public readonly id: string; + + constructor(public readonly caller: ICallerWithName, public readonly target: ICallerWithName) { + this.treeItem = new vscode.TreeItem( + `${locationLabel(caller)} → ${locationLabel(target)}`, + vscode.TreeItemCollapsibleState.None, + ); + + this.treeItem.tooltip = `Breaks at ${fullLabel(target)} containing ${ + fullLabel( + caller, + ) + } will be skipped`; + + this.id = this.treeItem.id = createHash('sha256') + .update(JSON.stringify([caller, target])) + .digest('base64'); + } + + public toDap(): Dap.ExcludedCaller { + return { + caller: this.caller, + target: this.target, + }; + } +} + +@injectable() +export class ExcludedCallersUI + implements vscode.TreeDataProvider, IExtensionContribution +{ + private readonly _onDidChangeTreeData = new vscode.EventEmitter(); + private allCallers = new Map(); + private lastHadCallers = false; + + constructor( + @inject(DebugSessionTracker) private readonly sessionTracker: DebugSessionTracker, + ) {} + + /** @inheritdoc */ + register(context: vscode.ExtensionContext): void { + context.subscriptions.push( + vscode.window.createTreeView(CustomViews.ExcludedCallers, { + treeDataProvider: this, + }), + registerCommand(vscode.commands, Commands.CallersAdd, async (_uri, context) => { + const stack = await this.sessionTracker + .getById(context.sessionId) + ?.customRequest('stackTrace', { + threadId: 0, // js-debug doesn't do threads, so ID is always 0 + startFrame: 0, + levels: 1, + }); + + if (!stack?.stackFrames.length) { + return; + } + + const topOfStack = stack.stackFrames[0]; + const caller = new ExcludedCaller( + { + name: context.frameName, + column: context.frameLocation.range.startColumn, + line: context.frameLocation.range.startLineNumber, + source: context.frameLocation.source, + }, + { + name: topOfStack.name, + column: topOfStack.column, + line: topOfStack.line, + source: topOfStack.source, + }, + ); + + this.allCallers.set(caller.id, caller); + this.triggerUpdate(); + }), + registerCommand(vscode.commands, Commands.CallersGoToCaller, c => revealLocation(c.caller)), + registerCommand(vscode.commands, Commands.CallersGoToTarget, c => revealLocation(c.target)), + registerCommand(vscode.commands, Commands.CallersRemove, async c => { + this.allCallers.delete(c.id); + this.triggerUpdate(); + }), + registerCommand(vscode.commands, Commands.CallersRemoveAll, () => { + this.allCallers.clear(); + this.triggerUpdate(); + }), + this.sessionTracker.onSessionAdded(e => { + if (this.allCallers.size > 0) { + this.sendCallersToSession(e); + } + }), + ); + } + + /** @inheritdoc */ + public readonly onDidChangeTreeData = this._onDidChangeTreeData.event; + + /** @inheritdoc */ + getTreeItem(element: ExcludedCaller): vscode.TreeItem { + return element.treeItem; + } + + /** @inheritdoc */ + getChildren(element?: ExcludedCaller): ExcludedCaller[] { + return element ? [] : [...this.allCallers.values()]; + } + + private sendCallersToSession(session: vscode.DebugSession) { + session.customRequest('setExcludedCallers', { + callers: [...this.allCallers.values()].map(c => c.toDap()), + }); + } + + private triggerUpdate() { + this._onDidChangeTreeData.fire(undefined); + + for (const session of this.sessionTracker.getConcreteSessions()) { + this.sendCallersToSession(session); + } + + const hasCallers = this.allCallers.size > 0; + if (hasCallers !== this.lastHadCallers) { + setContextKey(vscode.commands, ContextKey.HasExcludedCallers, hasCallers); + this.lastHadCallers = hasCallers; + } + } +} diff --git a/code/extensions/js-debug/src/ui/extensionApi.ts b/code/extensions/js-debug/src/ui/extensionApi.ts new file mode 100644 index 000000000000..97006f3e23f4 --- /dev/null +++ b/code/extensions/js-debug/src/ui/extensionApi.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import type { IDebugTerminalOptionsProvider, IExports } from '@vscode/js-debug'; +import { inject, injectable } from 'inversify'; +import { IDebugTerminalOptionsProviders } from '../ioc-extras'; + +@injectable() +export class ExtensionApiFactory { + constructor( + @inject(IDebugTerminalOptionsProviders) private readonly debugTerminalOptionsProviders: Set< + IDebugTerminalOptionsProvider + >, + ) {} + + public create(): IExports { + return { + registerDebugTerminalOptionsProvider: provider => { + this.debugTerminalOptionsProviders.add(provider); + return { dispose: () => this.debugTerminalOptionsProviders.delete(provider) }; + }, + }; + } +} diff --git a/code/extensions/js-debug/src/ui/getRunScriptCommand.ts b/code/extensions/js-debug/src/ui/getRunScriptCommand.ts new file mode 100644 index 000000000000..062516b4276d --- /dev/null +++ b/code/extensions/js-debug/src/ui/getRunScriptCommand.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { commands, WorkspaceFolder } from 'vscode'; + +/** + * Gets the package manager the user configured in the folder. + */ +export const getScriptRunner = async (folder: WorkspaceFolder | undefined) => { + try { + return await commands.executeCommand('npm.scriptRunner', folder?.uri); + } catch { + try { + return await commands.executeCommand('npm.packageManager', folder?.uri); + } catch { + return 'npm'; + } + } +}; + +/** + * Gets a command to run a script + */ +export const getRunScriptCommand = async (name: string, folder?: WorkspaceFolder) => { + const scriptRunner = await getScriptRunner(folder); + return `${scriptRunner} ${scriptRunner === 'node' ? '--run' : 'run'} ${name}`; +}; diff --git a/code/extensions/js-debug/src/ui/launchJsonCompletions.ts b/code/extensions/js-debug/src/ui/launchJsonCompletions.ts new file mode 100644 index 000000000000..750fdab938b9 --- /dev/null +++ b/code/extensions/js-debug/src/ui/launchJsonCompletions.ts @@ -0,0 +1,176 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import * as fs from 'fs/promises'; +import { injectable } from 'inversify'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { + Commands, + DebugType, + getPreferredOrDebugType, + registerCommand, +} from '../common/contributionUtils'; +import { existsInjected } from '../common/fsUtils'; +import { truthy } from '../common/objUtils'; +import { IExtensionContribution } from '../ioc-extras'; +import { LaunchJsonUpdaterHelper } from './launchJsonUpdateHelper'; + +// jsonc-parser by default builds a UMD bundle that esbuild can't resolve. +// We alias it but that breaks the default types :( so require and explicitly type here +const { getLocation }: typeof import('jsonc-parser/lib/esm/main') = require('jsonc-parser'); + +// Based on Python's: https://github.com/microsoft/vscode-python-debugger/blob/30560bb94989a6510765d78bed3f636d6f0d0227/src/extension/debugger/configuration/launch.json/completionProvider.ts + +@injectable() +export class LaunchJsonCompletions + implements vscode.CompletionItemProvider, IExtensionContribution +{ + private hasNodeModules = new Map(); + + register(context: vscode.ExtensionContext): void { + context.subscriptions.push( + vscode.languages.registerCompletionItemProvider( + { language: 'jsonc', pattern: '**/launch.json' }, + this, + ), + registerCommand(vscode.commands, Commands.CompletionNodeTool, async (document, position) => { + await new NodeToolInserter().selectAndInsertDebugConfig(document, position); + }), + ); + } + + public async provideCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + ): Promise { + if (!(await this.canProvideCompletions(document, position))) { + return []; + } + + return [ + { + command: { + command: Commands.CompletionNodeTool, + title: l10n.t('Run Node.js tool'), + arguments: [document, position, token], + }, + documentation: l10n.t( + 'Runs a Node.js command-line installed in the workspace node_modules.', + ), + sortText: 'AAAA', + preselect: true, + kind: vscode.CompletionItemKind.Enum, + label: l10n.t('Run Node.js tool'), + insertText: new vscode.SnippetString(), + }, + ]; + } + + private async canProvideCompletions( + document: vscode.TextDocument, + position: vscode.Position, + ): Promise { + if (path.basename(document.uri.fsPath) !== 'launch.json') { + return false; + } + + const location = getLocation(document.getText(), document.offsetAt(position)); + // Cursor must be inside the configurations array and not in any nested items. + // Hence path[0] = array, path[1] = array element index. + if (!(location.path[0] === 'configurations' && location.path.length === 2)) { + return false; + } + + const wf = vscode.workspace.getWorkspaceFolder(document.uri); + if (!wf) { + return false; + } + + const hasNodeModules = this.hasNodeModules.get(wf) + ?? !!(await existsInjected(fs, path.join(wf.uri.fsPath, 'node_modules', '.bin'))); + this.hasNodeModules.set(wf, hasNodeModules); + return hasNodeModules; + } +} + +class NodeToolInserter extends LaunchJsonUpdaterHelper { + protected async getLaunchConfig( + folder: vscode.WorkspaceFolder | undefined, + ): Promise { + type TItem = vscode.QuickPickItem & { relativeDir: string }; + const pick = vscode.window.createQuickPick(); + pick.title = l10n.t('Select a tool to run'); + pick.busy = true; + pick.show(); + + const options = await this.getOptions(folder); + if (options.length === 0) { + pick.dispose(); + vscode.window.showWarningMessage(l10n.t('No npm scripts found in the workspace folder.')); + return; + } + + let items: (vscode.QuickPickItem & { relativeDir: string })[] = []; + for (const { names, relativeDir } of options) { + if (relativeDir) { + items.push({ label: relativeDir, kind: vscode.QuickPickItemKind.Separator, relativeDir }); + } + items = items.concat(names.map(name => ({ label: name, relativeDir }))); + } + pick.busy = false; + pick.items = items; + + const chosen = await new Promise(resolve => { + pick.onDidAccept(() => resolve(pick.selectedItems[0])); + pick.onDidHide(() => resolve(undefined)); + }); + pick.dispose(); + + if (!chosen) { + return; + } + + return { + type: getPreferredOrDebugType(DebugType.Node), + request: 'launch', + name: `Run ${chosen.label}`, + runtimeExecutable: chosen.label, + cwd: path.join('${workspaceFolder}', chosen.relativeDir).replaceAll('\\', '/'), + args: [], + }; + } + + private async getOptions(f: vscode.WorkspaceFolder | undefined) { + if (!f) { + return []; + } + + const packageJsons = await vscode.workspace.findFiles( + new vscode.RelativePattern(f, '**/package.json'), + ); + const scripts = await Promise.all(packageJsons.map(async p => { + try { + const absoluteDir = path.dirname(p.fsPath); + const bins = await fs.readdir(path.join(absoluteDir, 'node_modules', '.bin')); + const names = new Set(); + for (const bin of bins) { + const ext = path.extname(bin); + names.add(ext ? bin.slice(0, -ext.length) : bin); + } + + return { + relativeDir: path.relative(f.uri.fsPath, absoluteDir), + names: Array.from(names), + }; + } catch { + return undefined; + } + })); + + return scripts.filter(truthy); + } +} diff --git a/code/extensions/js-debug/src/ui/launchJsonUpdateHelper.ts b/code/extensions/js-debug/src/ui/launchJsonUpdateHelper.ts new file mode 100644 index 000000000000..ffe5461efac0 --- /dev/null +++ b/code/extensions/js-debug/src/ui/launchJsonUpdateHelper.ts @@ -0,0 +1,161 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +// jsonc-parser by default builds a UMD bundle that esbuild can't resolve. +// We alias it but that breaks the default types :( so require and explicitly type here +const { createScanner, parse, SyntaxKind }: typeof import('jsonc-parser/lib/esm/main') = require( + 'jsonc-parser', +); + +type PositionOfCursor = 'InsideEmptyArray' | 'BeforeItem' | 'AfterItem'; +type PositionOfComma = 'BeforeCursor'; + +// Based on Python's service here: https://github.com/microsoft/vscode-python-debugger/blob/main/src/extension/debugger/configuration/launch.json/updaterServiceHelper.ts + +export abstract class LaunchJsonUpdaterHelper { + public async selectAndInsertDebugConfig( + document: vscode.TextDocument, + position: vscode.Position, + ): Promise { + const activeTextEditor = vscode.window.activeTextEditor; + if (activeTextEditor && activeTextEditor.document === document) { + const folder = vscode.workspace.getWorkspaceFolder(document.uri); + const config = await this.getLaunchConfig(folder); + if (config) { + await LaunchJsonUpdaterHelper.insertDebugConfiguration(document, position, config); + } + } + } + + protected abstract getLaunchConfig( + folder: vscode.WorkspaceFolder | undefined, + ): Promise; + + /** + * Inserts the debug configuration into the document. + * Invokes the document formatter to ensure JSON is formatted nicely. + * @param {TextDocument} document + * @param {Position} position + * @param {DebugConfiguration} config + * @returns {Promise} + * @memberof LaunchJsonCompletionItemProvider + */ + public static async insertDebugConfiguration( + document: vscode.TextDocument, + position: vscode.Position, + config: vscode.DebugConfiguration, + ): Promise { + const cursorPosition = LaunchJsonUpdaterHelper.getCursorPositionInConfigurationsArray( + document, + position, + ); + if (!cursorPosition) { + return; + } + const commaPosition = LaunchJsonUpdaterHelper.isCommaImmediatelyBeforeCursor(document, position) + ? 'BeforeCursor' + : undefined; + const formattedJson = LaunchJsonUpdaterHelper.getTextForInsertion( + config, + cursorPosition, + commaPosition, + ); + const workspaceEdit = new vscode.WorkspaceEdit(); + workspaceEdit.insert(document.uri, position, formattedJson); + await vscode.workspace.applyEdit(workspaceEdit); + Promise.resolve(vscode.commands.executeCommand('editor.action.formatDocument')).then(() => { + // noop + }); + } + + /** + * Gets the string representation of the debug config for insertion in the document. + * Adds necessary leading or trailing commas (remember the text is added into an array). + * @param {DebugConfiguration} config + * @param {PositionOfCursor} cursorPosition + * @param {PositionOfComma} [commaPosition] + * @returns + * @memberof LaunchJsonCompletionItemProvider + */ + public static getTextForInsertion( + config: vscode.DebugConfiguration, + cursorPosition: PositionOfCursor, + commaPosition?: PositionOfComma, + ): string { + const json = JSON.stringify(config); + if (cursorPosition === 'AfterItem') { + // If we already have a comma immediately before the cursor, then no need of adding a comma. + return commaPosition === 'BeforeCursor' ? json : `,${json}`; + } + if (cursorPosition === 'BeforeItem') { + return `${json},`; + } + return json; + } + + public static getCursorPositionInConfigurationsArray( + document: vscode.TextDocument, + position: vscode.Position, + ): PositionOfCursor | undefined { + if (LaunchJsonUpdaterHelper.isConfigurationArrayEmpty(document)) { + return 'InsideEmptyArray'; + } + const scanner = createScanner(document.getText(), true); + scanner.setPosition(document.offsetAt(position)); + const nextToken = scanner.scan(); + if (nextToken === SyntaxKind.CommaToken || nextToken === SyntaxKind.CloseBracketToken) { + return 'AfterItem'; + } + if (nextToken === SyntaxKind.OpenBraceToken) { + return 'BeforeItem'; + } + return undefined; + } + + public static isConfigurationArrayEmpty(document: vscode.TextDocument): boolean { + const configuration = parse(document.getText(), [], { + allowTrailingComma: true, + disallowComments: false, + }) as { + configurations: []; + }; + return ( + !configuration || !Array.isArray(configuration.configurations) + || configuration.configurations.length === 0 + ); + } + + public static isCommaImmediatelyBeforeCursor( + document: vscode.TextDocument, + position: vscode.Position, + ): boolean { + const line = document.lineAt(position.line); + // Get text from start of line until the cursor. + const currentLine = document.getText(new vscode.Range(line.range.start, position)); + if (currentLine.trim().endsWith(',')) { + return true; + } + // If there are other characters, then don't bother. + if (currentLine.trim().length !== 0) { + return false; + } + + // Keep walking backwards until we hit a non-comma character or a comm character. + let startLineNumber = position.line - 1; + while (startLineNumber > 0) { + const lineText = document.lineAt(startLineNumber).text; + if (lineText.trim().endsWith(',')) { + return true; + } + // If there are other characters, then don't bother. + if (lineText.trim().length !== 0) { + return false; + } + startLineNumber -= 1; + } + return false; + } +} diff --git a/code/extensions/js-debug/src/ui/linkedBreakpointLocation.ts b/code/extensions/js-debug/src/ui/linkedBreakpointLocation.ts new file mode 100644 index 000000000000..9987f7040214 --- /dev/null +++ b/code/extensions/js-debug/src/ui/linkedBreakpointLocation.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +/** + * Interface that can warn the user if a breakpoint is in a symlinked location + * without an obvious preservation flag. + */ +export interface ILinkedBreakpointLocation { + warn(): void; +} + +export const ILinkedBreakpointLocation = Symbol('ILinkedBreakpointLocation'); diff --git a/code/extensions/js-debug/src/ui/linkedBreakpointLocationUI.ts b/code/extensions/js-debug/src/ui/linkedBreakpointLocationUI.ts new file mode 100644 index 000000000000..daf156f797ee --- /dev/null +++ b/code/extensions/js-debug/src/ui/linkedBreakpointLocationUI.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import type * as vscodeType from 'vscode'; +import { ExtensionContext, VSCodeApi } from '../ioc-extras'; +import { ILinkedBreakpointLocation } from './linkedBreakpointLocation'; + +const ignoreStorageKey = 'linkBpWarnIgnored'; +const docLink = + 'https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_can-i-debug-if-im-using-symlinks'; + +@injectable() +export class LinkedBreakpointLocationUI implements ILinkedBreakpointLocation { + private didWarn = this.context.workspaceState.get(ignoreStorageKey, false); + + constructor( + @inject(VSCodeApi) private readonly vscode: typeof vscodeType, + @inject(ExtensionContext) private readonly context: vscodeType.ExtensionContext, + ) {} + + async warn() { + if (this.didWarn) { + return; + } + + this.didWarn = true; + const readMore = l10n.t('Read More'); + const ignore = l10n.t('Ignore'); + + const r = await this.vscode.window.showWarningMessage( + 'It looks like you have symlinked files. You might need to update your configuration to make this work as expected.', + ignore, + readMore, + ); + + if (r === ignore) { + this.context.workspaceState.update(ignoreStorageKey, true); + } else if (r === readMore) { + this.vscode.env.openExternal(this.vscode.Uri.parse(docLink)); + } + } +} diff --git a/code/extensions/js-debug/src/ui/longPredictionUI.ts b/code/extensions/js-debug/src/ui/longPredictionUI.ts new file mode 100644 index 000000000000..7809b9c84c53 --- /dev/null +++ b/code/extensions/js-debug/src/ui/longPredictionUI.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import { join } from 'path'; +import * as vscode from 'vscode'; +import { ExtensionContext, IExtensionContribution } from '../ioc-extras'; + +const omitLongPredictionKey = 'omitLongPredictions'; + +@injectable() +export class LongPredictionUI implements IExtensionContribution { + constructor(@inject(ExtensionContext) private readonly context: vscode.ExtensionContext) {} + + /** + * Registers the link UI for the extension. + */ + public register(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.debug.onDidReceiveDebugSessionCustomEvent(event => { + if (event.event === 'longPrediction') { + this.promptLongBreakpoint(event.session.workspaceFolder); + } + }), + ); + } + private async promptLongBreakpoint(workspaceFolder?: vscode.WorkspaceFolder) { + if (this.context.workspaceState.get(omitLongPredictionKey)) { + return; + } + + const message = l10n.t( + "It's taking a while to configure your breakpoints. You can speed this up by updating the 'outFiles' in your launch.json.", + ); + const openLaunch = l10n.t('Open launch.json'); + const dontShow = l10n.t("Don't show again"); + const result = await vscode.window.showWarningMessage(message, dontShow, openLaunch); + + if (result === dontShow) { + this.context.workspaceState.update(omitLongPredictionKey, true); + return; + } + + if (result !== openLaunch) { + return; + } + + if (!workspaceFolder) { + workspaceFolder = await vscode.window.showWorkspaceFolderPick(); + } + + if (!workspaceFolder) { + await vscode.window.showWarningMessage(l10n.t('No workspace folder open.')); + return; + } + + const doc = await vscode.workspace.openTextDocument( + join(workspaceFolder.uri.fsPath, '.vscode', 'launch.json'), + ); + await vscode.window.showTextDocument(doc); + } +} diff --git a/code/extensions/js-debug/src/ui/managedContextKey.ts b/code/extensions/js-debug/src/ui/managedContextKey.ts new file mode 100644 index 000000000000..60b234fa5a00 --- /dev/null +++ b/code/extensions/js-debug/src/ui/managedContextKey.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { ContextKey, IContextKeyTypes } from '../common/contributionUtils'; + +export class ManagedContextKey { + private _value: IContextKeyTypes[T] | undefined; + + public set value(value: IContextKeyTypes[T] | undefined) { + if (value !== this._value) { + this._value = value; + vscode.commands.executeCommand('setContext', this.key, value); + } + } + + public get value() { + return this._value; + } + + constructor(private readonly key: T) {} +} diff --git a/code/extensions/js-debug/src/ui/managedState.ts b/code/extensions/js-debug/src/ui/managedState.ts new file mode 100644 index 000000000000..0cf95aecb9da --- /dev/null +++ b/code/extensions/js-debug/src/ui/managedState.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +const Uninitialized = Symbol('Uninitialized'); + +export class ManagedState { + private _value: T | typeof Uninitialized = Uninitialized; + + public write(memento: vscode.Memento, value: T) { + if (value !== this.read(memento)) { + this._value = value; + memento.update(this.key, value); + } + } + + public read(memento: vscode.Memento) { + if (this._value === Uninitialized) { + this._value = memento.get(this.key) ?? this.defaultValue; + } + + return this._value; + } + + constructor(private readonly key: string, private readonly defaultValue: T) {} +} diff --git a/code/extensions/js-debug/src/ui/networkTree.ts b/code/extensions/js-debug/src/ui/networkTree.ts new file mode 100644 index 000000000000..f4a6259602fc --- /dev/null +++ b/code/extensions/js-debug/src/ui/networkTree.ts @@ -0,0 +1,526 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { isUtf8 } from 'buffer'; +import { inject, injectable } from 'inversify'; +import * as vscode from 'vscode'; +import Cdp from '../cdp/api'; +import { + Commands, + Configuration, + ContextKey, + CustomViews, + DebugType, + networkFilesystemScheme, + readConfig, + registerCommand, + setContextKey, +} from '../common/contributionUtils'; +import { DisposableList, noOpDisposable } from '../common/disposable'; +import { IMirroredNetworkEvents, mirroredNetworkEvents } from '../common/networkEvents'; +import { assertNever, once } from '../common/objUtils'; +import Dap from '../dap/api'; +import { IExtensionContribution } from '../ioc-extras'; +import { DebugSessionTracker } from './debugSessionTracker'; + +type NetworkNode = NetworkRequest; + +@injectable() +export class NetworkTree implements IExtensionContribution, vscode.TreeDataProvider { + private readonly disposables = new DisposableList(); + private readonly activeListeners = new DisposableList(); + private readonly treeDataChangeEmitter = new vscode.EventEmitter< + void | NetworkNode | NetworkNode[] | null | undefined + >(); + private readonly models = new Map(); + private current: NetworkModel | undefined; + + constructor( + @inject(DebugSessionTracker) private readonly debugSessionTracker: DebugSessionTracker, + ) { + this.disposables.push( + vscode.debug.onDidChangeActiveDebugSession(() => { + this.listenToActiveSession(); + }), + this.debugSessionTracker.onSessionEnded(session => { + this.models.delete(session.id); + }), + vscode.debug.onDidReceiveDebugSessionCustomEvent(event => { + if (event.event === 'networkEvent') { + this.models.get(event.session.id)?.append([event.body.event, event.body.data]); + } + }), + this.debugSessionTracker.onSessionAdded(session => { + if (!this.isEnabled()) { + return; + } + + session.customRequest('enableNetworking', { mirrorEvents: mirroredNetworkEvents }).then( + () => { + this.models.set(session.id, new NetworkModel(session)); + if (session === vscode.debug.activeDebugSession) { + this.listenToActiveSession(); + } + }, + () => { + /* ignored */ + }, + ); + }), + registerCommand( + vscode.commands, + Commands.NetworkViewRequest, + async (request: NetworkRequest) => { + const doc = await vscode.workspace.openTextDocument(request.fsUri); + await vscode.window.showTextDocument(doc); + }, + ), + registerCommand( + vscode.commands, + Commands.NetworkCopyUri, + async (request: NetworkRequest) => { + await vscode.env.clipboard.writeText(request.init.request.url); + }, + ), + registerCommand( + vscode.commands, + Commands.NetworkOpenBody, + async (request: NetworkRequest) => { + const doc = await vscode.workspace.openTextDocument(request.fsBodyUri); + await vscode.window.showTextDocument(doc); + }, + ), + registerCommand( + vscode.commands, + Commands.NetworkOpenBodyHex, + async (request: NetworkRequest) => { + await vscode.commands.executeCommand( + 'vscode.openWith', + request.fsBodyUri, + 'hexEditor.hexedit', + ); + }, + ), + registerCommand( + vscode.commands, + Commands.NetworkReplayXHR, + async (request: NetworkRequest) => { + await request.session.customRequest( + 'networkCall', + { + method: 'replayXHR', + params: { requestId: request.id }, + } satisfies Dap.NetworkCallParams, + ); + }, + ), + registerCommand(vscode.commands, Commands.NetworkClear, () => { + for (const model of this.models.values()) { + model.clear(); + } + this.treeDataChangeEmitter.fire(); + }), + ); + } + + /** @inheritdoc */ + onDidChangeTreeData = this.treeDataChangeEmitter.event; + + /** @inheritdoc */ + getTreeItem(element: NetworkNode): vscode.TreeItem | Thenable { + return element.toTreeItem(); + } + + /** @inheritdoc */ + getChildren(element?: NetworkNode | undefined): vscode.ProviderResult { + if (!element && this.current) { + return this.current.allRequests; + } + + return []; + } + + /** @inheritdoc */ + register(context: vscode.ExtensionContext): void { + context.subscriptions.push( + vscode.window.registerTreeDataProvider(CustomViews.Network, this), + vscode.workspace.registerFileSystemProvider( + networkFilesystemScheme, + new FilesystemProvider(this.debugSessionTracker, this.models), + { isCaseSensitive: true, isReadonly: true }, + ), + ); + } + + private isEnabled() { + return readConfig(vscode.workspace, Configuration.EnableNetworkView); + } + + private listenToActiveSession() { + this.activeListeners.clear(); + const model = (this.current = vscode.debug.activeDebugSession + && this.models.get(vscode.debug.activeDebugSession.id)); + let hasRequests = !!model && model.hasRequests; + if (model) { + this.activeListeners.push( + model.onDidChange(ev => { + this.treeDataChangeEmitter.fire(ev.isNew ? undefined : ev.request); + + if (model.hasRequests && !hasRequests) { + hasRequests = true; + setContextKey(vscode.commands, ContextKey.NetworkAvailable, true); + } + }), + ); + } + + setContextKey(vscode.commands, ContextKey.NetworkAvailable, hasRequests); + this.treeDataChangeEmitter.fire(undefined); + } +} + +class FilesystemProvider implements vscode.FileSystemProvider { + private readonly changeFileEmitter = new vscode.EventEmitter(); + + /** @inheritdoc */ + public readonly onDidChangeFile = this.changeFileEmitter.event; + + constructor( + private tracker: DebugSessionTracker, + private readonly models: Map, + ) {} + + /** @inheritdoc */ + watch(watchUri: vscode.Uri): vscode.Disposable { + const [sessionId, requestId] = watchUri.path.split('/').slice(1); + const model = this.models.get(sessionId); + if (!model) { + return noOpDisposable; + } + + return model.onDidChange(({ request, isNew }) => { + const uri = watchUri.with({ path: `${sessionId}/${request.id}` }); + if (isNew && !requestId) { + this.changeFileEmitter.fire([{ type: vscode.FileChangeType.Created, uri }]); + } else if (requestId === request.id) { + this.changeFileEmitter.fire([{ type: vscode.FileChangeType.Changed, uri }]); + } + }); + } + + /** @inheritdoc */ + async stat(uri: vscode.Uri): Promise { + const [sessionId, requestId] = uri.path.split('/').slice(1); + const model = this.models.get(sessionId); + if (!model) { + throw vscode.FileSystemError.FileNotFound(uri); + } + + if (!requestId) { + return { type: vscode.FileType.Directory, ctime: 0, mtime: 0, size: 0 }; + } + + const request = model.getRequest(requestId); + if (!request) { + throw vscode.FileSystemError.FileNotFound(uri); + } + + return { + type: vscode.FileType.File, + ctime: request.ctime, + mtime: request.mtime, + size: request.isComplete ? await request.body().then(b => b?.length || 0) : 0, + }; + } + + /** @inheritdoc */ + readDirectory(): [string, vscode.FileType][] { + return []; + } + + /** @inheritdoc */ + createDirectory(): void { + // no-op + } + + /** @inheritdoc */ + async readFile(uri: vscode.Uri): Promise { + const [sessionId, requestId, aspect] = uri.path.split('/').slice(1); + const request = this.models.get(sessionId)?.getRequest(requestId); + if (!request) { + throw vscode.FileSystemError.FileNotFound(uri); + } + + if (aspect === 'body') { + if (!request.isComplete) { + // we'll fire a watcher change event as this updates: + return Buffer.from('Response is still loading...'); + } + + return (await request.body()) || Buffer.from('Body not available'); + } + + return Buffer.from(await request.toCurl(this.tracker.getById(sessionId))); + } + + /** @inheritdoc */ + writeFile(): void { + // no-op + } + + /** @inheritdoc */ + delete(): void { + // no-op + } + + /** @inheritdoc */ + rename(): void { + // no-op + } +} + +class NetworkModel { + private readonly requests = new Map(); + + private readonly didChangeEmitter = new vscode.EventEmitter<{ + request: NetworkRequest; + isNew: boolean; + }>(); + public readonly onDidChange = this.didChangeEmitter.event; + + constructor(private readonly session: vscode.DebugSession) {} + + public get allRequests() { + return [...this.requests.values()]; + } + + public get hasRequests() { + return this.requests.size > 0; + } + + public getRequest(id: string) { + return this.requests.get(id); + } + + public clear() { + this.requests.clear(); + } + + public append([key, event]: KeyValue) { + if (key === 'requestWillBeSent') { + const request = new NetworkRequest(event, this.session); + this.requests.set(event.requestId, request); + this.didChangeEmitter.fire({ request, isNew: true }); + } else if ( + key === 'responseReceived' + || key === 'loadingFailed' + || key === 'loadingFinished' + || key === 'responseReceivedExtraInfo' + ) { + const request = this.requests.get(event.requestId); + if (!request) { + return; + } + + if (key === 'responseReceived') { + request.response = event.response || {}; // node.js response is just empty right now + } else if (key === 'loadingFailed') { + request.failed = event; + } else if (key === 'loadingFinished') { + request.finished = event; + } else if (key === 'responseReceivedExtraInfo') { + request.responseExtra = event; + } + request.mtime = Date.now(); + this.didChangeEmitter.fire({ request, isNew: false }); + } else { + assertNever(key, 'unexpected network event'); + } + } +} + +export class NetworkRequest { + public readonly ctime = Date.now(); + public mtime = Date.now(); + public response?: Cdp.Network.Response; + public responseExtra?: Cdp.Network.ResponseReceivedExtraInfoEvent; + public failed?: Cdp.Network.LoadingFailedEvent; + public finished?: Cdp.Network.LoadingFinishedEvent; + + public get isComplete() { + return !!(this.finished || this.failed); + } + + public get id() { + return this.init.requestId; + } + + public get fsUri() { + return vscode.Uri.from({ + scheme: networkFilesystemScheme, + path: `/${this.session.id}/${this.id}`, + }); + } + + public get fsBodyUri() { + return vscode.Uri.from({ + scheme: networkFilesystemScheme, + path: `/${this.session.id}/${this.id}/body`, + }); + } + + constructor( + public readonly init: Cdp.Network.RequestWillBeSentEvent, + public readonly session: vscode.DebugSession, + ) {} + + /** Returns a tree-item representation of the request. */ + public toTreeItem() { + let icon: vscode.ThemeIcon; + if (!this.isComplete) { + icon = new vscode.ThemeIcon( + 'sync~spin', + new vscode.ThemeColor('notebookStatusRunningIcon.foreground'), + ); + } else if (this.failed) { + icon = new vscode.ThemeIcon( + 'error', + new vscode.ThemeColor('notebookStatusErrorIcon.foreground'), + ); + } else if (this.response && this.response.status >= 400) { + icon = new vscode.ThemeIcon('warning'); + } else { + icon = new vscode.ThemeIcon( + 'check', + new vscode.ThemeColor('notebookStatusSuccessIcon.foreground'), + ); + } + + let label = ''; + if (this.failed) { + label += `[${this.failed.errorText}] `; + } else if (this.response) { + label += `[${this.response.status}] `; + } + + let host: string | undefined; + let path: string; + try { + const url = new URL(this.init.request.url); + host = url.host; + path = url.pathname; + } catch { + path = this.init.request.url; + } + + label += `${this.init.request.method.toUpperCase()} ${path}`; + const treeItem = new vscode.TreeItem(label, vscode.TreeItemCollapsibleState.Collapsed); + treeItem.iconPath = icon; + treeItem.description = host; + treeItem.tooltip = this.init.request.url; + treeItem.id = this.init.requestId; + treeItem.collapsibleState = vscode.TreeItemCollapsibleState.None; + return treeItem; + } + + /** Converts the request to a curl-style command. */ + public async toCurl(session: vscode.DebugSession | undefined) { + const command = this.toCurlCommand(); + if (!this.response) { + return command; + } + + const parts = [command]; + parts.push(`< HTTP ${this.responseExtra?.statusCode || this.response.status || 'UNKOWN'}`); + for ( + const header of Object.entries( + this.responseExtra?.headers || this.response.headers || {}, + ) + ) { + parts.push(`< ${header[0]}: ${header[1]}`); + } + parts.push('<'); + + if (this.failed) { + parts.push('', `${this.failed.errorText}`); + if (this.failed.blockedReason) { + parts.push(`Blocked: ${this.failed.blockedReason}`); + } else if (this.failed.corsErrorStatus) { + parts.push(`CORS error: ${this.failed.corsErrorStatus.corsError}`); + } + } + + if (!this.isComplete || !session) { + return parts.join('\n'); + } + + const body = (await this.body()) || Buffer.from(''); + if (!isUtf8(body)) { + parts.push(`[binary data as base64]: ${body.toString('base64')}`); + } else { + const str = body.toString(); + try { + const parsed = JSON.parse(str); + parts.push(JSON.stringify(parsed, null, 2)); + } catch { + parts.push(str); + } + } + + return parts.join('\n'); + } + + private toCurlCommand() { + const args = ['curl', '-v']; + if (this.init.request.method !== 'GET') { + args.push(`-X ${this.init.request.method}`); + } + + // note: although headers is required by CDP types, it's undefined in Node.js right now (22.6.0) + for (const [headerName, headerValue] of Object.entries(this.init.request.headers || {})) { + args.push(`-H '${headerName}: ${headerValue}'`); + } + + if (this.init.request.postDataEntries?.length) { + const parts = this.init.request.postDataEntries.map(e => e.bytes || '').join(''); + const bytes = Buffer.from(parts, 'base64'); + args.push(isUtf8(bytes) ? `-d '${bytes.toString()}'` : `--data-binary ''`); + } + + args.push(`'${this.init.request.url}'`); + + return args.join(' '); + } + + /** Gets the response body. */ + public body = once(async () => { + try { + const res: Cdp.Network.GetResponseBodyResult = await this.session.customRequest( + 'networkCall', + { + method: 'getResponseBody', + params: { requestId: this.init.requestId }, + } satisfies Dap.NetworkCallParams, + ); + + if (!res.body) { + // only say this on failure so that we gracefully support it once available: + if (this.session.type === DebugType.Node) { + return Buffer.from('Response body inspection is not supported in Node.js yet.'); + } + return undefined; + } + if (res.base64Encoded) { + return Buffer.from(res.body, 'base64'); + } + return Buffer.from(res.body); + } catch { + return undefined; + } + }); +} + +type KeyValue = keyof T extends infer K ? K extends keyof T ? [key: K, value: T[K]] + : never + : never; diff --git a/code/extensions/js-debug/src/ui/portAttributesProvider.ts b/code/extensions/js-debug/src/ui/portAttributesProvider.ts new file mode 100644 index 000000000000..68959b2d1252 --- /dev/null +++ b/code/extensions/js-debug/src/ui/portAttributesProvider.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import { ExtensionContext, PortAttributesProvider, PortAutoForwardAction, workspace } from 'vscode'; +import { IPortLeaseTracker } from '../adapter/portLeaseTracker'; +import { DefaultJsDebugPorts } from '../common/findOpenPort'; +import { IExtensionContribution } from '../ioc-extras'; + +@injectable() +export class JsDebugPortAttributesProvider + implements IExtensionContribution, PortAttributesProvider +{ + /** Cache of used ports (#1092) */ + private cachedResolutions: string[] = new Array(16).fill(''); + /** Index counter for the next cached resolution index in the list */ + private cachedResolutionIndex = 0; + + constructor(@inject(IPortLeaseTracker) private readonly leaseTracker: IPortLeaseTracker) {} + + /** + * @inheritdoc + */ + public register(context: ExtensionContext) { + if (typeof workspace.registerPortAttributesProvider === 'function') { + context.subscriptions.push( + workspace.registerPortAttributesProvider( + { portRange: [DefaultJsDebugPorts.Min, DefaultJsDebugPorts.Max] }, + this, + ), + ); + } + } + + /** + * @inheritdoc + */ + public async providePortAttributes({ port, pid }: { port: number; pid?: number }) { + if (pid && this.cachedResolutions.includes(`${port}:${pid}`)) { + return { port, autoForwardAction: PortAutoForwardAction.Ignore }; + } + + if (!(await this.leaseTracker.isRegistered(port))) { + return undefined; + } + + if (pid) { + const index = this.cachedResolutionIndex++ % this.cachedResolutions.length; + this.cachedResolutions[index] = `${port}:${pid}`; + } + + return { port, autoForwardAction: PortAutoForwardAction.Ignore }; + } +} diff --git a/code/extensions/js-debug/src/ui/prettyPrint.ts b/code/extensions/js-debug/src/ui/prettyPrint.ts new file mode 100644 index 000000000000..93213bc9b190 --- /dev/null +++ b/code/extensions/js-debug/src/ui/prettyPrint.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import * as qs from 'querystring'; +import * as vscode from 'vscode'; +import { Commands, ContextKey, registerCommand } from '../common/contributionUtils'; +import Dap from '../dap/api'; +import { IExtensionContribution } from '../ioc-extras'; +import { DebugSessionTracker } from './debugSessionTracker'; +import { ManagedContextKey } from './managedContextKey'; + +@injectable() +export class PrettyPrintUI implements IExtensionContribution { + private readonly canPrettyPrintKey = new ManagedContextKey(ContextKey.CanPrettyPrint); + + constructor(@inject(DebugSessionTracker) private readonly tracker: DebugSessionTracker) {} + + /** @inheritdoc */ + public register(context: vscode.ExtensionContext): void { + context.subscriptions.push( + registerCommand(vscode.commands, Commands.PrettyPrint, () => this.prettifyActive()), + vscode.window.onDidChangeActiveTextEditor(editor => this.updateEditorState(editor)), + this.tracker.onSessionAdded(() => this.updateEditorState(vscode.window.activeTextEditor)), + this.tracker.onSessionEnded(() => this.updateEditorState(vscode.window.activeTextEditor)), + ); + } + + /** + * Prettifies the active file in the editor. + */ + public async prettifyActive() { + const editor = vscode.window.activeTextEditor; + if (!editor || !this.canPrettyPrint(editor)) { + return; + } + + const { sessionId, source } = sourceForUri(editor.document.uri); + const session = sessionId && this.tracker.getById(sessionId); + + // For ephemeral files, they're attached to a single session, so go ahead + // and send it to the owning session. For files on disk, send it to all + // sessions--they will no-op if they don't know about the source. + let prettied: { session: vscode.DebugSession; result: Dap.PrettyPrintSourceResult }[]; + if (session) { + prettied = [{ + session, + result: await sendPrintCommand(session, source, editor.selection.start), + }]; + } else { + prettied = await Promise.all( + this.tracker.getConcreteSessions().map(async session => { + const result = await sendPrintCommand(session, source, editor.selection.start); + return { session, result }; + }), + ); + } + + if (!prettied.some(p => p.result.didReveal)) { + const reveal = prettied.find(p => p.result.source); + if (reveal) { + const doc = await vscode.workspace.openTextDocument( + dapSourceToDebugUri(reveal.session, reveal.result.source!), + ); + await vscode.window.showTextDocument(doc); + } + } + } + + private canPrettyPrint(editor: vscode.TextEditor) { + return ( + this.tracker.isDebugging + && editor.document.languageId === 'javascript' + && !editor.document.uri.path.endsWith('-pretty.js') + ); + } + + private updateEditorState(editor: vscode.TextEditor | undefined) { + if (!this.tracker.isDebugging) { + this.canPrettyPrintKey.value = undefined; + return; + } + + if (editor && this.canPrettyPrint(editor)) { + const value = editor.document.uri.toString(); + if (value !== this.canPrettyPrintKey.value?.[0]) { + this.canPrettyPrintKey.value = [editor.document.uri.toString()]; + } + } + } +} + +const sendPrintCommand = ( + session: vscode.DebugSession, + source: Dap.Source, + cursor: vscode.Position, +): Thenable => + session.customRequest('prettyPrintSource', { + source, + line: cursor.line, + column: cursor.character, + }); + +/** + * Gets the DAP source and session for a VS Code document URI. + */ +const sourceForUri = (uri: vscode.Uri) => { + const query = qs.parse(uri.query); + const sessionId: string | undefined = query['session'] as string; + const source = { + path: uri.fsPath, + sourceReference: Number(query['ref']) || 0, + }; + + return { sessionId, source }; +}; + +// https://github.com/microsoft/vscode/blob/7f9c7a41873b88861744d06aed33d2dbcaa3a92e/src/vs/workbench/contrib/debug/common/debugSource.ts#L132 +const dapSourceToDebugUri = (session: vscode.DebugSession, source: Dap.Source) => { + if (!source.sourceReference) { + return vscode.Uri.file(source.path || ''); + } + + return vscode.Uri.from({ + scheme: 'debug', + path: source.path?.replace(/^\/+/g, '/'), // #174054 + query: `session=${session.id}&ref=${source.sourceReference}`, + }); +}; diff --git a/code/extensions/js-debug/src/ui/processPicker.ts b/code/extensions/js-debug/src/ui/processPicker.ts new file mode 100644 index 000000000000..a338ea2f27b3 --- /dev/null +++ b/code/extensions/js-debug/src/ui/processPicker.ts @@ -0,0 +1,213 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { execSync } from 'child_process'; +import { promises as fsPromises } from 'fs'; +import { basename } from 'path'; +import * as vscode from 'vscode'; +import { Configuration, readConfig } from '../common/contributionUtils'; +import { LocalFsUtils } from '../common/fsUtils'; +import { isSubdirectoryOf } from '../common/pathUtils'; +import { nearestDirectoryContaining } from '../common/urlUtils'; +import { + INodeAttachConfiguration, + nodeAttachConfigDefaults, + ResolvingNodeAttachConfiguration, +} from '../configuration'; +import { analyseArguments, processTree } from './processTree/processTree'; + +const INSPECTOR_PORT_DEFAULT = 9229; + +interface IProcessItem extends vscode.QuickPickItem { + pidAndPort: string; // picker result + sortKey: number; +} + +/** + * end user action for picking a process and attaching debugger to it + */ +export async function attachProcess() { + // We pick here, rather than just putting the command as the process ID, so + // that the cwd is set correctly in multi-root workspaces. + const processId = await pickProcess(); + if (!processId) { + return; + } + + const userDefaults = readConfig(vscode.workspace, Configuration.PickAndAttachDebugOptions); + + const config: INodeAttachConfiguration = { + ...nodeAttachConfigDefaults, + ...userDefaults, + name: 'process', + processId, + }; + + // TODO: Figure out how to inject FsUtils + await resolveProcessId(new LocalFsUtils(fsPromises), config, true); + await vscode.debug.startDebugging( + config.cwd ? vscode.workspace.getWorkspaceFolder(vscode.Uri.file(config.cwd)) : undefined, + config, + ); +} + +/** + * Resolves the requested process ID, and updates the config object + * appropriately. Returns true if the configuration was updated, false + * if it was cancelled. + */ +export async function resolveProcessId( + fsUtils: LocalFsUtils, + config: ResolvingNodeAttachConfiguration, + setCwd = false, +) { + // we resolve Process Picker early (before VS Code) so that we can probe the process for its protocol + const processId = config.processId?.trim(); + const result = processId && decodePidAndPort(processId); + if (!result || isNaN(result.pid)) { + throw new Error( + l10n.t("Attach to process: '{0}' doesn't look like a process id.", processId || ''), + ); + } + + if (!result.port) { + putPidInDebugMode(result.pid); + } + + config.port = result.port || INSPECTOR_PORT_DEFAULT; + delete config.processId; + + if (setCwd) { + const inferredWd = await inferWorkingDirectory(fsUtils, result.pid); + if (inferredWd) { + config.cwd = inferredWd; + } + } +} + +async function inferWorkingDirectory(fsUtils: LocalFsUtils, processId?: number) { + const inferredWd = processId && (await processTree.getWorkingDirectory(processId)); + + // If we couldn't infer the working directory, just use the first workspace folder + if (!inferredWd) { + return vscode.workspace.workspaceFolders?.[0].uri.fsPath; + } + + const packageRoot = await nearestDirectoryContaining(fsUtils, inferredWd, 'package.json'); + if (!packageRoot) { + return inferredWd; + } + + // Find the working directory package root. If the original inferred working + // directory was inside a workspace folder, don't go past that. + const parentWorkspaceFolder = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(inferredWd)); + return !parentWorkspaceFolder || isSubdirectoryOf(parentWorkspaceFolder.uri.fsPath, packageRoot) + ? packageRoot + : parentWorkspaceFolder.uri.fsPath; +} + +/** + * Process picker command (for launch config variable). Returns a string in + * the format `pid:port`, where port is optional. + */ +export async function pickProcess(): Promise { + try { + const item = await listProcesses(); + return item ? item.pidAndPort : null; + } catch (err) { + await vscode.window.showErrorMessage(l10n.t('Process picker failed ({0})', err.message), { + modal: true, + }); + return null; + } +} + +// ---- private + +const encodePidAndPort = (processId: number, port?: number) => `${processId}:${port ?? ''}`; +const decodePidAndPort = (encoded: string) => { + const [pid, port] = encoded.split(':'); + return { pid: Number(pid), port: port ? Number(port) : undefined }; +}; + +async function listProcesses(): Promise { + const nodeProcessPattern = /^(?:node|iojs)(?:$|\b)/i; + let seq = 0; // default sort key + + const quickPick = vscode.window.createQuickPick(); + quickPick.placeholder = l10n.t('Pick the node.js process to attach to'); + quickPick.matchOnDescription = true; + quickPick.matchOnDetail = true; + quickPick.busy = true; + quickPick.show(); + + let hasPicked = false; + const itemPromise = new Promise(resolve => { + quickPick.onDidAccept(() => resolve(quickPick.selectedItems[0])); + quickPick.onDidHide(() => resolve(undefined)); + }); + + processTree + .lookup((leaf, acc) => { + if (hasPicked) { + return acc; + } + + if (process.platform === 'win32' && leaf.command.indexOf('\\??\\') === 0) { + // remove leading device specifier + leaf.command = leaf.command.replace('\\??\\', ''); + } + + const executableName = basename(leaf.command, '.exe'); + const { port } = analyseArguments(leaf.args); + if (!port && !nodeProcessPattern.test(executableName)) { + return acc; + } + + const newItem = { + label: executableName, + description: leaf.args, + pidAndPort: encodePidAndPort(leaf.pid, port), + sortKey: leaf.date ? leaf.date : seq++, + detail: port + ? l10n.t('process id: {0}, debug port: {1} ({2})', leaf.pid, port, 'SIGUSR1') + : l10n.t('process id: {0} ({1})', leaf.pid, 'SIGUSR1'), + }; + + const index = acc.findIndex(item => item.sortKey < newItem.sortKey); + acc.splice(index === -1 ? acc.length : index, 0, newItem); + quickPick.items = acc; + return acc; + }, []) + .then(() => (quickPick.busy = false)) + .catch(err => { + vscode.window.showErrorMessage(`Error listing processes: ${err.message}`); + quickPick.dispose(); + }); + + const item = await itemPromise; + hasPicked = true; + quickPick.dispose(); + return item; +} + +function putPidInDebugMode(pid: number): void { + try { + if (process.platform === 'win32') { + // regular node has an undocumented API function for forcing another node process into debug mode. + // (process)._debugProcess(pid); + // But since we are running on Electron's node, process._debugProcess doesn't work (for unknown reasons). + // So we use a regular node instead: + const command = `node -e process._debugProcess(${pid})`; + execSync(command); + } else { + process.kill(pid, 'SIGUSR1'); + } + } catch (e) { + throw new Error( + l10n.t("Attach to process: cannot enable debug mode for process '{0}' ({1}).", pid, e), + ); + } +} diff --git a/code/extensions/js-debug/src/ui/processTree/baseProcessTree.ts b/code/extensions/js-debug/src/ui/processTree/baseProcessTree.ts new file mode 100644 index 000000000000..befceaba2957 --- /dev/null +++ b/code/extensions/js-debug/src/ui/processTree/baseProcessTree.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { ChildProcessWithoutNullStreams, spawn as defaultSpawn } from 'child_process'; +import { StreamSplitter } from '../../common/streamSplitter'; +import { IProcess, IProcessTree } from './processTree'; + +/** + * Base process tree that others can extend. + */ +export abstract class BaseProcessTree implements IProcessTree { + constructor(protected readonly spawn = defaultSpawn) {} + + /** + * @inheritdoc + */ + public abstract getWorkingDirectory(processId: number): Promise; + + /** + * @inheritdoc + */ + public lookup(onEntry: (process: IProcess, accumulator: T) => T, value: T): Promise { + return new Promise((resolve, reject) => { + const proc = this.createProcess(); + const parser = this.createParser(); + + proc.on('error', reject); + proc.stderr.on('error', data => reject(`Error finding processes: ${data.toString()}`)); + proc.stdout.pipe(new StreamSplitter('\n')).on('data', line => { + const process = parser(line.toString()); + if (process) { + value = onEntry(process, value); + } + }); + + proc.on('close', (code, signal) => { + if (code === 0) { + resolve(value); + } else if (signal) { + reject(new Error(`process terminated with signal: ${signal}`)); + } else if (code) { + reject(new Error(`process terminated with exit code: ${code}`)); + } + }); + }); + } + + /** + * Spawns the child process that reads data. + */ + protected abstract createProcess(): ChildProcessWithoutNullStreams; + + /** + * Creates a function that is called for each line of + * the output and should parse processes. + */ + protected abstract createParser(): (line: string) => IProcess | void; +} diff --git a/code/extensions/js-debug/src/ui/processTree/darwinProcessTree.ts b/code/extensions/js-debug/src/ui/processTree/darwinProcessTree.ts new file mode 100644 index 000000000000..7c9690b39119 --- /dev/null +++ b/code/extensions/js-debug/src/ui/processTree/darwinProcessTree.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { isAbsolute } from 'path'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { ChildProcessError, spawnAsync } from '../../common/processUtils'; +import { BaseProcessTree } from './baseProcessTree'; +import { IProcess } from './processTree'; + +export class DarwinProcessTree extends BaseProcessTree { + public constructor(private readonly fsUtils: LocalFsUtils) { + super(); + } + + public async getWorkingDirectory(processId: number) { + try { + const { stdout } = await spawnAsync('lsof', [ + // AND options + '-a', + // Get the cwd + '-dcwd', + // Filter to the cwd + '-Fn', + // For this process + `-p${processId}`, + ]); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const cwd = stdout.trim().split('\n').pop()!.slice(1); + + return cwd && isAbsolute(cwd) && (await this.fsUtils.exists(cwd)) ? cwd : undefined; + } catch (e) { + if (e instanceof ChildProcessError) { + return undefined; + } + + throw e; + } + } + + /** + * @inheritdoc + */ + protected createProcess() { + return this.spawn('ps', [ + '-xo', + // The "aaaa" is needed otherwise the command name can get truncated. + `pid=PID,ppid=PPID,comm=${'a'.repeat(256)},command=COMMAND`, + ]); + } + + /** + * @inheritdoc + */ + protected createParser(): (line: string) => IProcess | void { + // We know PID and PPID are numbers, so we can split and trim those easily. + // The command column is headed with "COMMAND", so the alg is to: + // 1. Split [pid, ppid] until the third set of whitespace in the string + // 2. Trim the binary between the third whitespace and index of COMMAND + // 3. The COMMAND is everything else, trimmed. + + let commandOffset: number | void; + return line => { + if (!commandOffset) { + commandOffset = line.indexOf('COMMAND'); + return; + } + + const ids = /^\W*([0-9]+)\W*([0-9]+)\W*/.exec(line); + if (!ids) { + return; + } + + return { + pid: Number(ids[1]), + ppid: Number(ids[2]), + command: line.slice(ids[0].length, commandOffset).trim(), + args: line.slice(commandOffset).trim(), + }; + }; + } +} diff --git a/code/extensions/js-debug/src/ui/processTree/posixProcessTree.ts b/code/extensions/js-debug/src/ui/processTree/posixProcessTree.ts new file mode 100644 index 000000000000..ba536d85191c --- /dev/null +++ b/code/extensions/js-debug/src/ui/processTree/posixProcessTree.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { DarwinProcessTree } from './darwinProcessTree'; +import { IProcess } from './processTree'; + +export class PosixProcessTree extends DarwinProcessTree { + /** + * @inheritdoc + */ + protected createProcess() { + return this.spawn('ps', ['-axo', `pid=PID,ppid=PPID,comm:30,command=COMMAND`]); + } + + /** + * @inheritdoc + */ + protected createParser(): (line: string) => IProcess | void { + const parser = super.createParser(); + return line => { + const process = parser(line); + if (!process) { + return; + } + + let pos = process.args.indexOf(process.command); + if (pos === -1) { + return process; + } + + pos = pos + process.command.length; + while (pos < process.args.length) { + if (process.args[pos] === ' ') { + break; + } + pos++; + } + + process.command = process.args.substr(0, pos); + process.args = process.args.substr(pos + 1); + return process; + }; + } +} diff --git a/code/extensions/js-debug/src/ui/processTree/processTree.test.ts b/code/extensions/js-debug/src/ui/processTree/processTree.test.ts new file mode 100644 index 000000000000..00c72662cc22 --- /dev/null +++ b/code/extensions/js-debug/src/ui/processTree/processTree.test.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { expect } from 'chai'; +import { analyseArguments } from './processTree'; + +describe('process tree', () => { + describe('analyze arguments', () => { + const tt = [ + { input: 'node --inspect', address: '127.0.0.1', port: 9229 }, + { input: 'node --inspect=:1234', address: '127.0.0.1', port: 1234 }, + { input: 'node --inspect=0.0.0.0', address: '0.0.0.0', port: 9229 }, + { input: 'node --inspect=0.0.0.0:1234', address: '0.0.0.0', port: 1234 }, + { input: 'node --inspect=[::1]:1234', address: '[::1]', port: 1234 }, + + { input: 'node --inspect-brk', address: '127.0.0.1', port: 9229 }, + { input: 'node --inspect-brk=0.0.0.0', address: '0.0.0.0', port: 9229 }, + { input: 'node --inspect-brk=0.0.0.0:1234', address: '0.0.0.0', port: 1234 }, + { input: 'node --inspect-brk=[::1]:1234', address: '[::1]', port: 1234 }, + + { + input: 'node --inspect-brk=0.0.0.0:1234 --inspect-port=3456', + address: '0.0.0.0', + port: 3456, + }, + ]; + + for (const t of tt) { + it(`should analyze ${t.input}`, () => { + const a = analyseArguments(t.input); + expect(a).to.deep.equal({ + address: t.address, + port: t.port, + }); + }); + } + }); +}); diff --git a/code/extensions/js-debug/src/ui/processTree/processTree.ts b/code/extensions/js-debug/src/ui/processTree/processTree.ts new file mode 100644 index 000000000000..969fff582004 --- /dev/null +++ b/code/extensions/js-debug/src/ui/processTree/processTree.ts @@ -0,0 +1,136 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { promises as fsPromises } from 'fs'; +import { LocalFsUtils } from '../../common/fsUtils'; +import { once } from '../../common/objUtils'; +import { DarwinProcessTree } from './darwinProcessTree'; +import { PosixProcessTree } from './posixProcessTree'; +import { WindowsProcessTree } from './windowsProcessTree'; + +/** + * IProcess is parsed from the {@link IProcessTree} + */ +export interface IProcess { + /** + * Process ID. + */ + pid: number; + /** + * Parent process ID, or 0. + */ + ppid: number; + + /** + * Binary or command used to start the process. + */ + command: string; + + /** + * Process arguments. + */ + args: string; + + /** + * Time at which the process was started. + */ + date?: number; +} + +/** + * Device that looks up processes running on the current machine. + */ +export interface IProcessTree { + /** + * Looks up process in the tree, accumulating them into a result. + */ + lookup(onEntry: (process: IProcess, accumulator: T) => T, initial: T): Promise; + + /** + * Gets the working directory of the process, if possible. + */ + getWorkingDirectory(processId: number): Promise; +} + +/** + * The process tree implementation for the current platform. + */ +// TODO: Figure out how to inject the fsUtils here +const fsUtils = new LocalFsUtils(fsPromises); +export const processTree: IProcessTree = process.platform === 'win32' + ? new WindowsProcessTree() + : process.platform === 'darwin' + ? new DarwinProcessTree(fsUtils) + : new PosixProcessTree(fsUtils); + +const DEBUG_FLAGS_PATTERN = once(() => { + const parts = [ + // base inspect argument + '--inspect(?:-brk)?', + + // START = argument. (Note that --inspect does not allow a space delimiter, so no need to handle it) + '(?:=', + + // Host+port or port alternate: + [ + '(?:', + + // Address or hostname with optional port: + [ + '(?:', + // IPv6, IPv4, or hostname + '(?
\\[[0-9a-f:]*\\]|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+|(?:[a-z][a-z0-9\\.]*))', + // Optional port + '(?::(?\\d+))?', + ')', + ], + + '|', + + // Simple port: + [ + '(?:', + ':?', // optional ':' before port, #2063 + '(?\\d+)?', + ')', + ], + + ')', + ], + + // END = argument + ')?', + ] + .flat(Infinity) + .join(''); + + return new RegExp(parts, 'i'); +}); + +/* + * Analyse the given command line arguments and extract debug port and protocol from it. + */ +export function analyseArguments(args: string) { + const DEBUG_PORT_PATTERN = /--inspect-port=(\d+)/; + + let address: string | undefined; + let port: number | undefined; + + // match --inspect, --inspect=1234, --inspect-brk, --inspect-brk=1234 + let matches = DEBUG_FLAGS_PATTERN().exec(args); + if (matches?.groups) { + const portStr = matches.groups.port1 || matches.groups.port2; + port = portStr ? Number(portStr) : 9229; + address = matches.groups.address ?? '127.0.0.1'; + } + + // a --inspect-port=1234 overrides the port + matches = DEBUG_PORT_PATTERN.exec(args); + if (matches && matches.length === 2) { + address ||= '127.0.0.1'; + port = parseInt(matches[1]); + } + + return { address, port }; +} diff --git a/code/extensions/js-debug/src/ui/processTree/windowsProcessTree.ts b/code/extensions/js-debug/src/ui/processTree/windowsProcessTree.ts new file mode 100644 index 000000000000..0ded9eefd8da --- /dev/null +++ b/code/extensions/js-debug/src/ui/processTree/windowsProcessTree.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { getWinUtils } from '../../common/win32Utils'; +import { IProcess, IProcessTree } from './processTree'; + +export class WindowsProcessTree implements IProcessTree { + /** + * @inheritdoc + */ + public async getWorkingDirectory() { + return undefined; // not supported + } + + /** + * @inheritdoc + */ + async lookup(onEntry: (process: IProcess, accumulator: T) => T, initial: T): Promise { + const win = await getWinUtils(); + for (const proc of win.getProcessInfo()) { + let args = ''; + let command: string; + + const quoteEnd = proc.commandLine.indexOf('" '); + if (quoteEnd === -1) { + const space = proc.commandLine.indexOf(' '); + if (space === -1) { + command = proc.commandLine; + } else { + command = proc.commandLine.slice(0, space); + args = proc.commandLine.slice(space + 1); + } + } else { + command = proc.commandLine.slice(1, quoteEnd); + args = proc.commandLine.slice(quoteEnd + 2); + } + + initial = onEntry({ + args, + command, + date: proc.creationDate * 1000, + pid: proc.processId, + ppid: proc.parentProcessId, + }, initial); + } + + return initial; + } +} diff --git a/code/extensions/js-debug/src/ui/profiling.ts b/code/extensions/js-debug/src/ui/profiling.ts new file mode 100644 index 000000000000..d068b95dc64b --- /dev/null +++ b/code/extensions/js-debug/src/ui/profiling.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Container } from 'inversify'; +import * as vscode from 'vscode'; +import { Commands, registerCommand } from '../common/contributionUtils'; +import { UiProfileManager } from './profiling/uiProfileManager'; + +export const registerProfilingCommand = ( + context: vscode.ExtensionContext, + container: Container, +) => { + const manager = container.get(UiProfileManager); + + context.subscriptions.push( + registerCommand(vscode.commands, Commands.StartProfile, sessionIdOrArgs => + manager.start( + typeof sessionIdOrArgs === 'string' + ? { sessionId: sessionIdOrArgs } + : sessionIdOrArgs ?? {}, + )), + registerCommand(vscode.commands, Commands.StopProfile, sessionId => manager.stop(sessionId)), + ); +}; diff --git a/code/extensions/js-debug/src/ui/profiling/breakpointTerminationCondition.ts b/code/extensions/js-debug/src/ui/profiling/breakpointTerminationCondition.ts new file mode 100644 index 000000000000..de04c5d7f696 --- /dev/null +++ b/code/extensions/js-debug/src/ui/profiling/breakpointTerminationCondition.ts @@ -0,0 +1,162 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { memoize, truthy } from '../../common/objUtils'; +import Dap from '../../dap/api'; +import { ExtensionContext, FS, FsPromises } from '../../ioc-extras'; +import { ITerminationCondition, ITerminationConditionFactory } from './terminationCondition'; + +const warnedKey = 'breakpointTerminationWarnedSlow'; + +type BreakpointPickItem = { + id: number; + location: vscode.Location; +} & vscode.QuickPickItem; + +@injectable() +export class BreakpointTerminationConditionFactory implements ITerminationConditionFactory { + public readonly sortOrder = 2; + public readonly id = 'breakpoint'; + public readonly label = l10n.t('Pick Breakpoint'); + public readonly description = l10n.t('Run until a specific breakpoint is hit'); + + constructor( + @inject(FS) private readonly fs: FsPromises, + @inject(ExtensionContext) private readonly context: vscode.ExtensionContext, + ) {} + + public async onPick(session: vscode.DebugSession, breakpointIds?: ReadonlyArray) { + if (breakpointIds) { + return new BreakpointTerminationCondition(breakpointIds); + } + + const quickPick = vscode.window.createQuickPick(); + quickPick.canSelectMany = true; + quickPick.matchOnDescription = true; + quickPick.busy = true; + + const chosen = await new Promise | undefined>(resolve => { + quickPick.onDidAccept(() => resolve(quickPick.selectedItems)); + quickPick.onDidHide(() => resolve(undefined)); + quickPick.onDidChangeActive(async active => { + if (!active.length) { + return; + } + + const location = active[0].location; + const document = await vscode.workspace.openTextDocument(location.uri); + vscode.window.showTextDocument(document, { + selection: location.range, + preview: true, + preserveFocus: true, + }); + }); + + quickPick.show(); + + (async () => { + const codeBps = vscode.debug.breakpoints.filter( + bp => bp.enabled && bp instanceof vscode.SourceBreakpoint, + ); + const dapBps = await Promise.all( + codeBps.map(bp => session.getDebugProtocolBreakpoint(bp)), + ); + const candidates = await this.getCandidates( + dapBps as (Dap.Breakpoint | undefined)[], + codeBps as vscode.SourceBreakpoint[], + ); + + quickPick.items = candidates; + quickPick.selectedItems = candidates; + quickPick.busy = false; + })(); + }); + + quickPick.dispose(); + + if (!chosen) { + return; + } + + await this.warnSlowCode(); + return new BreakpointTerminationCondition(chosen.map(c => Number(c.id))); + } + + private async warnSlowCode() { + if (this.context.workspaceState.get(warnedKey)) { + return; + } + + vscode.window.showWarningMessage( + l10n.t( + 'Profiling with breakpoints enabled can change the performance of your code. It can be useful to validate your findings with the "duration" or "manual" termination conditions.', + ), + l10n.t('Got it!'), + ); + await this.context.workspaceState.update(warnedKey, true); + } + + private async getCandidates( + dapBps: ReadonlyArray, + codeBps: ReadonlyArray, + ): Promise { + if (dapBps.length !== codeBps.length) { + throw new Error('Mismatched breakpoint array lengths'); + } + + const getLines = memoize((f: string) => this.getFileLines(f)); + + const candidates = await Promise.all( + codeBps.map(async (codeBp, i): Promise => { + const dapBp = dapBps[i]; + if (!dapBp || !dapBp.id) { + return; // does not apply to this session + } + + const location = codeBp.location; + const folder = vscode.workspace.getWorkspaceFolder(location.uri); + const labelPath = folder + ? path.relative(folder.uri.fsPath, location.uri.fsPath) + : location.uri.fsPath; + const lines = await getLines(location.uri.fsPath); + + return { + id: dapBp.id, + label: `${labelPath}:${location.range.start.line}:${location.range.start.character}`, + location, + description: lines?.[location.range.start.line]?.trim(), + }; + }), + ); + + return candidates.filter(truthy); + } + + private async getFileLines(path: string): Promise { + try { + const contents = await this.fs.readFile(path, 'utf-8'); + return contents.split('\n'); + } catch { + return undefined; + } + } +} + +class BreakpointTerminationCondition implements ITerminationCondition { + public get customData() { + return { + stopAtBreakpoint: this.breakpointIds.slice(), + }; + } + + constructor(private readonly breakpointIds: ReadonlyArray) {} + + public dispose() { + // no-op + } +} diff --git a/code/extensions/js-debug/src/ui/profiling/durationTerminationCondition.ts b/code/extensions/js-debug/src/ui/profiling/durationTerminationCondition.ts new file mode 100644 index 000000000000..0fce8f3be6e3 --- /dev/null +++ b/code/extensions/js-debug/src/ui/profiling/durationTerminationCondition.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { DisposableList } from '../../common/disposable'; +import { ITerminationCondition, ITerminationConditionFactory } from './terminationCondition'; +import { Category, UiProfileSession } from './uiProfileSession'; + +@injectable() +export class DurationTerminationConditionFactory implements ITerminationConditionFactory { + private lastDuration?: number; + + public readonly sortOrder = 1; + public readonly id = 'duration'; + public readonly label = l10n.t('Duration'); + public readonly description = l10n.t('Run for a specific amount of time'); + + public async onPick(_session: vscode.DebugSession, duration?: number) { + if (duration) { + return new DurationTerminationCondition(duration * 1000); + } + + const input = vscode.window.createInputBox(); + input.title = l10n.t('Duration of Profile'); + input.placeholder = l10n.t('Profile duration in seconds, e.g "5"'); + + if (this.lastDuration) { + input.value = String(this.lastDuration); + } + + input.onDidChangeValue(value => { + if (!/^[0-9]+$/.test(value)) { + input.validationMessage = l10n.t('Please enter a number'); + } else if (Number(value) < 1) { + input.validationMessage = l10n.t('Please enter a number greater than 1'); + } else { + input.validationMessage = undefined; + } + }); + + const condition = await new Promise(resolve => { + input.onDidAccept(() => { + if (input.validationMessage) { + return resolve(undefined); + } + + this.lastDuration = Number(input.value); + resolve(new DurationTerminationCondition(this.lastDuration * 1000)); + }); + + input.onDidHide(() => resolve(undefined)); + input.show(); + }); + + input.dispose(); + + return condition; + } +} + +class DurationTerminationCondition implements ITerminationCondition { + private disposable = new DisposableList(); + + constructor(private readonly duration: number) {} + + public attachTo(session: UiProfileSession) { + const deadline = Date.now() + this.duration; + const updateTimer = () => + session.setStatus( + Category.TerminationTimer, + `${Math.round((deadline - Date.now()) / 1000)}s`, + ); + const stopTimeout = setTimeout(() => session.stop(), this.duration); + const updateInterval = setInterval(updateTimer, 1000); + updateTimer(); + + this.disposable.callback(() => { + clearTimeout(stopTimeout); + clearInterval(updateInterval); + }); + } + + public dispose() { + this.disposable.dispose(); + } +} diff --git a/code/extensions/js-debug/src/ui/profiling/manualTerminationCondition.ts b/code/extensions/js-debug/src/ui/profiling/manualTerminationCondition.ts new file mode 100644 index 000000000000..526773db9141 --- /dev/null +++ b/code/extensions/js-debug/src/ui/profiling/manualTerminationCondition.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { injectable } from 'inversify'; +import { ITerminationCondition, ITerminationConditionFactory } from './terminationCondition'; + +@injectable() +export class ManualTerminationConditionFactory implements ITerminationConditionFactory { + public readonly sortOrder = 0; + public readonly id = 'manual'; + public readonly label = l10n.t('Manual'); + public readonly description = l10n.t('Run until manually stopped'); + + public async onPick() { + return new ManualTerminationCondition(); + } +} + +export class ManualTerminationCondition implements ITerminationCondition { + public dispose() { + // no-op + } +} diff --git a/code/extensions/js-debug/src/ui/profiling/terminationCondition.ts b/code/extensions/js-debug/src/ui/profiling/terminationCondition.ts new file mode 100644 index 000000000000..918082be227e --- /dev/null +++ b/code/extensions/js-debug/src/ui/profiling/terminationCondition.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { DebugSession } from 'vscode'; +import { IDisposable } from '../../common/disposable'; +import Dap from '../../dap/api'; +import { UiProfileSession } from './uiProfileSession'; + +/** + * Item displayed to the user when picking when their profile should end. + */ +export interface ITerminationConditionFactory { + readonly id: string; + readonly label: string; + readonly description?: string; + readonly sortOrder: number; + + /** + * Called when the user picks this termination factory. Can return undefined + * to cancel the picking process. + */ + onPick( + session: DebugSession, + ...args: ReadonlyArray + ): Promise; +} + +export const ITerminationConditionFactory = Symbol('ITerminationConditionFactory'); + +export interface ITerminationCondition extends IDisposable { + /** + * Custom object to be merged into the `startProfile` request. + */ + readonly customData?: Partial; + + /** + * Called when the profile starts running. + */ + attachTo?(session: UiProfileSession): void; +} diff --git a/code/extensions/js-debug/src/ui/profiling/uiProfileManager.ts b/code/extensions/js-debug/src/ui/profiling/uiProfileManager.ts new file mode 100644 index 000000000000..4cf3e1adcc10 --- /dev/null +++ b/code/extensions/js-debug/src/ui/profiling/uiProfileManager.ts @@ -0,0 +1,403 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable, multiInject } from 'inversify'; +import { homedir } from 'os'; +import { basename, join } from 'path'; +import * as vscode from 'vscode'; +import { getDefaultProfileName, ProfilerFactory } from '../../adapter/profiling'; +import { iteratorFirst } from '../../common/arrayUtils'; +import { Commands, ContextKey, setContextKey } from '../../common/contributionUtils'; +import { DisposableList, IDisposable } from '../../common/disposable'; +import { moveFile } from '../../common/fsUtils'; +import { AnyLaunchConfiguration } from '../../configuration'; +import Dap from '../../dap/api'; +import { FS, FsPromises, SessionSubStates } from '../../ioc-extras'; +import { DebugSessionTracker } from '../debugSessionTracker'; +import { ManualTerminationCondition } from './manualTerminationCondition'; +import { ITerminationCondition, ITerminationConditionFactory } from './terminationCondition'; +import { UiProfileSession } from './uiProfileSession'; + +const isProfileCandidate = (session: vscode.DebugSession) => + '__pendingTargetId' in session.configuration; + +/** + * Arguments provided in the `startProfile` command. + */ +export interface IStartProfileArguments { + /** + * Session ID to capture. If not provided, the user may be asked to pick + * an available session. + */ + sessionId?: string; + /** + * Type of profile to take. One of the "IProfiler.type" types. Currently, + * only 'cpu' is available. If not provided, the user will be asked to pick. + */ + type?: string; + /** + * Termination condition. If not provided, the user will be asked to pick. + * Optionally pass arguments: + * - `manual` takes no arguments + * - `duration` takes a [number] of seconds + * - `breakpoint` takes a `[Array]` of DAP breakpoint IDs. These can + * be found by calling the custom `getBreakpoints` method on a debug session. + */ + termination?: string | { type: string; args?: ReadonlyArray }; + + /** + * Command to run when the profile has completed. If not provided, the + * profile will be opened in a new untitled editor. The command will receive + * an `IProfileCallbackArguments` object. + */ + onCompleteCommand?: string; +} + +/** + * Arguments given to the `onCompleteCommand`. + */ +export interface IProfileCallbackArguments { + /** + * String contents of the profile. + */ + contents: string; + + /** + * Suggested file name of the profile. + */ + basename: string; +} + +@injectable() +export class UiProfileManager implements IDisposable { + private statusBarItem?: vscode.StatusBarItem; + private lastChosenType: string | undefined; + private lastChosenTermination: string | undefined; + private readonly activeSessions = new Map(); + private readonly disposables = new DisposableList(); + + constructor( + @inject(DebugSessionTracker) private readonly tracker: DebugSessionTracker, + @inject(FS) private readonly fs: FsPromises, + @inject(SessionSubStates) private readonly sessionStates: SessionSubStates, + @multiInject(ITerminationConditionFactory) private readonly terminationConditions: + ReadonlyArray, + ) { + this.disposables.push( + vscode.debug.onDidReceiveDebugSessionCustomEvent(event => { + if (event.event !== 'profileStarted') { + return; + } + + const args = event.body as Dap.ProfileStartedEventParams; + let session = this.activeSessions.get(event.session.id); + if (!session) { + session = new UiProfileSession( + event.session, + ProfilerFactory.ctors.find(t => t.type === args.type) || ProfilerFactory.ctors[0], + new ManualTerminationCondition(), + ); + this.registerSession(session); + } + + session.setFile(args.file); + }), + ); + } + + /** + * Starts a profiling session. + */ + public async start(args: IStartProfileArguments) { + let maybeSession: vscode.DebugSession | undefined; + const candidates = [...this.tracker.getConcreteSessions()].filter(isProfileCandidate); + if (args.sessionId) { + maybeSession = candidates.find(s => s.id === args.sessionId); + } else { + maybeSession = await this.pickSession(candidates); + } + + if (!maybeSession) { + return; // cancelled or invalid + } + + const session = maybeSession; + const existing = this.activeSessions.get(session.id); + if (existing) { + if (!(await this.alreadyRunningSession(existing))) { + return; + } + } + + const impl = await this.pickType(session, args.type); + if (!impl) { + return; + } + + let termination: ITerminationCondition | undefined; + if (!impl.instant) { + termination = await this.pickTermination(session, args.termination); + if (!termination) { + return; + } + } + + const uiSession = new UiProfileSession(session, impl, termination); + if (!uiSession) { + return; + } + + this.registerSession(uiSession, args.onCompleteCommand); + await uiSession.start(); + + if (impl.instant) { + await uiSession.stop(); + } + } + + /** + * Stops the profiling session if it exists. + */ + public async stop(sessionId?: string) { + let uiSession: UiProfileSession | undefined; + if (sessionId) { + uiSession = this.activeSessions.get(sessionId); + } else { + const session = await this.pickSession( + [...this.activeSessions.values()].map(s => s.session), + ); + uiSession = session && this.activeSessions.get(session.id); + } + + if (!uiSession) { + return; + } + + this.sessionStates.remove(uiSession.session.id); + await uiSession.stop(); + } + + /** + * @inheritdoc + */ + public dispose() { + for (const session of this.activeSessions.values()) { + session.dispose(); + } + + this.activeSessions.clear(); + this.disposables.dispose(); + } + + /** + * Starts tracking a UI profile session in the manager. + */ + private registerSession(uiSession: UiProfileSession, onCompleteCommand?: string) { + this.activeSessions.set(uiSession.session.id, uiSession); + this.sessionStates.add(uiSession.session.id, l10n.t('Profiling')); + uiSession.onStatusChange(() => this.updateStatusBar()); + uiSession.onStop(file => { + if (file) { + this.openProfileFile(uiSession, onCompleteCommand, uiSession.session, file); + } + + this.activeSessions.delete(uiSession.session.id); + uiSession.dispose(); + this.updateStatusBar(); + }); + this.updateStatusBar(); + } + + /** + * Opens the profile file within the UI, called + * when a session ends gracefully. + */ + private async openProfileFile( + uiSession: UiProfileSession, + onCompleteCommand: string | undefined, + session: vscode.DebugSession, + sourceFile: string, + ) { + if (onCompleteCommand) { + return Promise.all([ + vscode.commands.executeCommand(onCompleteCommand, { + contents: await this.fs.readFile(sourceFile, 'utf-8'), + basename: basename(sourceFile) + uiSession.impl.extension, + } as IProfileCallbackArguments), + this.fs.unlink(sourceFile), + ]); + } + + const directory = session.workspaceFolder?.uri.fsPath + ?? vscode.workspace.workspaceFolders?.[0].uri.fsPath + ?? homedir(); + + const filename = getDefaultProfileName() + uiSession.impl.extension; + // todo: open as untitled, see: https://github.com/microsoft/vscode/issues/93441 + const fileUri = vscode.Uri.file(join(directory, filename)); + await moveFile(this.fs, sourceFile, fileUri.fsPath); + + await vscode.commands.executeCommand( + uiSession.impl.editable ? 'vscode.open' : 'revealInExplorer', + fileUri, + ); + } + + /** + * Updates the status bar based on the state of current debug sessions. + */ + private updateStatusBar() { + if (this.activeSessions.size === 0) { + this.statusBarItem?.hide(); + setContextKey(vscode.commands, ContextKey.IsProfiling, false); + return; + } + + if (!this.statusBarItem) { + this.statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 500); + this.statusBarItem.command = Commands.StopProfile; + } + + setContextKey(vscode.commands, ContextKey.IsProfiling, true); + + const session = iteratorFirst(this.activeSessions.values()); + if (session && this.activeSessions.size === 1) { + this.statusBarItem.text = session.status + ? l10n.t('{0} Click to Stop Profiling ({1})', '$(loading~spin)', session.status) + : l10n.t('{0} Click to Stop Profiling', '$(loading~spin)'); + } else { + this.statusBarItem.text = l10n.t( + '{0} Click to Stop Profiling ({1} sessions)', + '$(loading~spin)', + this.activeSessions.size, + ); + } + + this.statusBarItem.show(); + } + + /** + * Triggered when we try to profile a session we're already profiling. Asks + * if they want to stop and start profiling it again. + */ + private async alreadyRunningSession(existing: UiProfileSession) { + const yes = l10n.t('Yes'); + const no = l10n.t('No'); + const stopExisting = await vscode.window.showErrorMessage( + l10n.t( + 'A profiling session is already running, would you like to stop it and start a new session?', + ), + yes, + no, + ); + + if (stopExisting !== yes) { + return false; + } + + await this.stop(existing.session.id); + return true; + } + + /** + * Quickpick to select any of the given candidate sessions. + */ + private async pickSession(candidates: ReadonlyArray) { + if (candidates.length === 0) { + return; + } + + if (candidates.length === 1) { + return candidates[0]; + } + + const chosen = await vscode.window.showQuickPick( + candidates.map(c => ({ label: c.name, id: c.id })), + ); + return chosen && candidates.find(c => c.id === chosen.id); + } + + /** + * Picks the profiler type to run in the session. + */ + private async pickType(session: vscode.DebugSession, suggestedType?: string) { + const params = session.configuration as AnyLaunchConfiguration; + if (suggestedType) { + return ProfilerFactory.ctors.find(t => t.type === suggestedType && t.canApplyTo(params)); + } + + const chosen = await this.pickWithLastDefault( + l10n.t('Type of profile'), + ProfilerFactory.ctors.filter(ctor => ctor.canApplyTo(params)), + this.lastChosenType, + ); + if (chosen) { + this.lastChosenType = chosen.label; + } + + return chosen; + } + + /** + * Picks the termination condition to use for the session. + */ + private async pickTermination( + session: vscode.DebugSession, + suggested: IStartProfileArguments['termination'], + ) { + if (suggested) { + const s = typeof suggested === 'string' ? { type: suggested } : suggested; + return this.terminationConditions + .find(t => t.id === s.type) + ?.onPick(session, ...(s.args ?? [])); + } + + const chosen = await this.pickWithLastDefault( + l10n.t('How long to run the profile'), + this.terminationConditions, + this.lastChosenTermination, + ); + if (chosen) { + this.lastChosenTermination = chosen.label; + } + + return chosen?.onPick(session); + } + + private async pickWithLastDefault< + T extends { label: string; description?: string; sortOrder?: number }, + >(title: string, items: ReadonlyArray, lastLabel?: string): Promise { + if (items.length <= 1) { + return items[0]; // first T or undefined + } + + const quickpick = vscode.window.createQuickPick(); + quickpick.title = title; + quickpick.items = items + .slice() + .sort((a, b) => { + if (a.label === lastLabel || b.label === lastLabel) { + return a.label === lastLabel ? -1 : 1; + } + + return (a.sortOrder ?? 0) - (b.sortOrder ?? 0); + }) + .map(ctor => ({ label: ctor.label, description: ctor.description, alwaysShow: true })); + + const chosen = await new Promise(resolve => { + quickpick.onDidAccept(() => resolve(quickpick.selectedItems[0]?.label)); + quickpick.onDidHide(() => resolve(undefined)); + quickpick.show(); + }); + + quickpick.dispose(); + + if (!chosen) { + return; + } + + return items.find(c => c.label === chosen); + } +} diff --git a/code/extensions/js-debug/src/ui/profiling/uiProfileSession.ts b/code/extensions/js-debug/src/ui/profiling/uiProfileSession.ts new file mode 100644 index 000000000000..37f4af240a50 --- /dev/null +++ b/code/extensions/js-debug/src/ui/profiling/uiProfileSession.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import * as vscode from 'vscode'; +import { IProfilerCtor } from '../../adapter/profiling'; +import { DisposableList, IDisposable } from '../../common/disposable'; +import { EventEmitter } from '../../common/events'; +import Dap from '../../dap/api'; +import { ITerminationCondition } from './terminationCondition'; + +const enum State { + Collecting, + Saving, + Stopped, +} + +export const enum Category { + Overwrite = -1, + Adapter, + TerminationTimer, +} + +/** + * UI-side tracker for profiling sessions. + */ +export class UiProfileSession implements IDisposable { + private statusChangeEmitter = new EventEmitter(); + private stopEmitter = new EventEmitter(); + private _innerStatus: string[] = []; + private disposables = new DisposableList(); + private state = State.Collecting; + private file?: string; + + /** + * Event that fires when the status changes. + */ + public readonly onStatusChange = this.statusChangeEmitter.event; + + /** + * Event that fires when the session stops, containing the file that + * the profile is saved in. + */ + public readonly onStop = this.stopEmitter.event; + + /** + * Gets the current session status. + */ + public get status() { + return this._innerStatus.filter(s => !!s).join(', ') || undefined; + } + + constructor( + public readonly session: vscode.DebugSession, + public readonly impl: IProfilerCtor, + private readonly termination?: ITerminationCondition, + ) { + this.disposables.push( + vscode.debug.onDidReceiveDebugSessionCustomEvent(event => { + if (event.session === session && event.event === 'profilerStateUpdate') { + this.onStateUpdate(event.body); + } + }), + vscode.debug.onDidTerminateDebugSession(s => { + if (s === session) { + this.stopEmitter.fire(undefined); + } + }), + ); + + if (termination) { + this.disposables.push(termination); + termination.attachTo?.(this); + } + } + + /** + * Starts the session and returns its ui-side tracker. + */ + public async start() { + try { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Window, + title: l10n.t('Starting profile...'), + }, + () => + this.session.customRequest('startProfile', { + type: this.impl.type, + ...this.termination?.customData, + }), + ); + } catch (e) { + vscode.window.showErrorMessage(e.message); + this.stopEmitter.fire(undefined); + } + } + + /** + * @inheritdoc + */ + public dispose() { + this.state = State.Stopped; + this.disposables.dispose(); + } + + /** + * Updates the file the profile is saved in. + */ + public setFile(file: string) { + this.file = file; + } + + /** + * Stops the profile, and returns the file that profiling information was + * saved in. + */ + public async stop() { + if (this.state !== State.Collecting) { + return; + } + + this.setStatus(Category.Overwrite, l10n.t('Saving')); + this.state = State.Saving; + + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Window, + title: l10n.t('Stopping profile...'), + }, + () => this.session.customRequest('stopProfile', {}), + ); + + // this will trigger a profileStateUpdate with running=false + // to finish up the session. + } + + public onStateUpdate(update: Dap.ProfilerStateUpdateEventParams) { + if (update.running) { + this.setStatus(Category.Adapter, update.label); + return; + } + + this.state = State.Stopped; + this.stopEmitter.fire(this.file); + this.dispose(); + } + + /** + * Updates the session state, notifying the manager. + */ + public setStatus(category: Category, status: string) { + if (this.state !== State.Collecting) { + return; + } + + if (category === Category.Overwrite) { + this._innerStatus = [status]; + } else { + this._innerStatus[category] = status; + } + + this.statusChangeEmitter.fire(this.status as string); + } +} diff --git a/code/extensions/js-debug/src/ui/requestCDPProxy.ts b/code/extensions/js-debug/src/ui/requestCDPProxy.ts new file mode 100644 index 000000000000..cf2ec8a88149 --- /dev/null +++ b/code/extensions/js-debug/src/ui/requestCDPProxy.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Commands, registerCommand } from '../common/contributionUtils'; +import Dap from '../dap/api'; +import { DebugSessionTracker } from './debugSessionTracker'; +import { DebugSessionTunnels } from './debugSessionTunnels'; + +export const registerRequestCDPProxy = ( + context: vscode.ExtensionContext, + tracker: DebugSessionTracker, +) => { + const tunnels = new DebugSessionTunnels(); + + context.subscriptions.push( + tunnels, + registerCommand(vscode.commands, Commands.RequestCDPProxy, async (sessionId, forwardToUi) => { + const session = tracker.getById(sessionId); + if (!session) { + return undefined; + } + + const proxied: Dap.RequestCDPProxyResult = await session.customRequest('requestCDPProxy'); + if (!forwardToUi) { + return proxied; + } + + try { + if (vscode.env.remoteName !== undefined) { + const tunneled = await tunnels.request(sessionId, { + label: 'Edge Devtools Tunnel', + remotePort: proxied.port, + }); + + return { + host: tunneled.localAddress.host, + port: tunneled.localAddress.port, + path: proxied.path, + }; + } + } catch { + // fall through + } + + return proxied; + }), + ); +}; diff --git a/code/extensions/js-debug/src/ui/revealPage.ts b/code/extensions/js-debug/src/ui/revealPage.ts new file mode 100644 index 000000000000..a0724c36992e --- /dev/null +++ b/code/extensions/js-debug/src/ui/revealPage.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Commands, registerCommand } from '../common/contributionUtils'; +import { DebugSessionTracker } from './debugSessionTracker'; + +export const registerRevealPage = ( + context: vscode.ExtensionContext, + tracker: DebugSessionTracker, +) => { + context.subscriptions.push( + registerCommand(vscode.commands, Commands.RevealPage, async sessionId => { + const session = tracker.getById(sessionId); + await session?.customRequest('revealPage'); + }), + ); +}; diff --git a/code/extensions/js-debug/src/ui/settingRequestOptionsProvider.ts b/code/extensions/js-debug/src/ui/settingRequestOptionsProvider.ts new file mode 100644 index 000000000000..a30b1e78baae --- /dev/null +++ b/code/extensions/js-debug/src/ui/settingRequestOptionsProvider.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { OptionsOfTextResponseBody } from 'got'; +import { injectable } from 'inversify'; +import { workspace } from 'vscode'; +import { mergeOptions } from '../adapter/resourceProvider/helpers'; +import { IRequestOptionsProvider } from '../adapter/resourceProvider/requestOptionsProvider'; +import { Configuration, readConfig } from '../common/contributionUtils'; +import { once } from '../common/objUtils'; + +@injectable() +export class SettingRequestOptionsProvider implements IRequestOptionsProvider { + private readonly read = once(() => readConfig(workspace, Configuration.ResourceRequestOptions)); + + /** + * @inheritdoc + */ + public provideOptions(obj: OptionsOfTextResponseBody) { + mergeOptions(obj, (this.read() || {}) as Partial); + } +} diff --git a/code/extensions/js-debug/src/ui/shutdownParticipants.ts b/code/extensions/js-debug/src/ui/shutdownParticipants.ts new file mode 100644 index 000000000000..9f58156e3389 --- /dev/null +++ b/code/extensions/js-debug/src/ui/shutdownParticipants.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { injectable } from 'inversify'; +import { IDisposable, noOpDisposable } from '../common/disposable'; + +/** Order of shutdown participants. */ +export const enum ShutdownOrder { + // Participant is awaited before loaded scripts are cleared in an execution context. + ExecutionContexts = 0, + // Participant is run after everything else. + Final = 1, +} + +export interface IShutdownParticipants { + /** + * Registers the function to be called in the specified order. + * + * A participant that happens on ExecutionContexts may be called multiple + * times of the course of the application's lifetime. + */ + register(order: ShutdownOrder, p: (isFinal: boolean) => Promise): IDisposable; + + /** + * Runs shutdown participants that trigger when an execution context is cleared. + */ + shutdownContext(): Promise; + + /** + * Runs all shutdown participants. + */ + shutdownAll(): Promise; +} + +export const IShutdownParticipants = Symbol('IShutdownParticipants'); + +@injectable() +export class ShutdownParticipants implements IShutdownParticipants { + private participants: Set<(isFinal: boolean) => Promise>[] = []; + private shutdownStage: ShutdownOrder | undefined; + + register(order: ShutdownOrder, p: (isFinal: boolean) => Promise): IDisposable { + if (this.shutdownStage !== undefined && this.shutdownStage >= order) { + p(true); + return noOpDisposable; + } + + while (this.participants.length <= order) { + this.participants.push(new Set()); + } + + this.participants[order].add(p); + return { dispose: () => this.participants[order].delete(p) }; + } + + async shutdownContext(): Promise { + if (this.shutdownStage === undefined || this.shutdownStage < ShutdownOrder.ExecutionContexts) { + await Promise.all( + [...this.participants[ShutdownOrder.ExecutionContexts]].map(p => p(false)), + ); + } + } + + async shutdownAll(): Promise { + for ( + this.shutdownStage = 0; + this.shutdownStage < this.participants.length; + this.shutdownStage++ + ) { + await Promise.all([...this.participants[this.shutdownStage]].map(p => p(true))); + } + } +} diff --git a/code/extensions/js-debug/src/ui/sourceSteppingUI.ts b/code/extensions/js-debug/src/ui/sourceSteppingUI.ts new file mode 100644 index 000000000000..4cbc7c78fb25 --- /dev/null +++ b/code/extensions/js-debug/src/ui/sourceSteppingUI.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { inject, injectable } from 'inversify'; +import * as vscode from 'vscode'; +import { Commands, ContextKey, registerCommand } from '../common/contributionUtils'; +import { ExtensionContext, IExtensionContribution } from '../ioc-extras'; +import { DebugSessionTracker } from './debugSessionTracker'; +import { ManagedContextKey } from './managedContextKey'; +import { ManagedState } from './managedState'; + +export const sourceMapSteppingEnabled = new ManagedState('sourceSteppingEnabled', true); + +@injectable() +export class SourceSteppingUI implements IExtensionContribution { + constructor( + @inject(ExtensionContext) private readonly context: vscode.ExtensionContext, + @inject(DebugSessionTracker) private readonly tracker: DebugSessionTracker, + ) {} + + /** @inheritdoc */ + public register(context: vscode.ExtensionContext) { + const isDisabled = new ManagedContextKey(ContextKey.IsMapSteppingDisabled); + + if (sourceMapSteppingEnabled.read(this.context.workspaceState) === false) { + isDisabled.value = true; + } + + const setEnabled = (enabled: boolean) => { + isDisabled.value = !enabled; + sourceMapSteppingEnabled.write(this.context.workspaceState, enabled); + for (const session of this.tracker.getConcreteSessions()) { + session.customRequest('setSourceMapStepping', { enabled }); + } + }; + + context.subscriptions.push( + registerCommand(vscode.commands, Commands.EnableSourceMapStepping, () => { + setEnabled(true); + }), + registerCommand(vscode.commands, Commands.DisableSourceMapStepping, () => { + setEnabled(false); + }), + ); + } +} diff --git a/code/extensions/js-debug/src/ui/startDebuggingAndStopOnEntry.ts b/code/extensions/js-debug/src/ui/startDebuggingAndStopOnEntry.ts new file mode 100644 index 000000000000..0053f8e02b4b --- /dev/null +++ b/code/extensions/js-debug/src/ui/startDebuggingAndStopOnEntry.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { injectable } from 'inversify'; +import { commands, ExtensionContext } from 'vscode'; +import { Commands, registerCommand } from '../common/contributionUtils'; +import { IExtensionContribution } from '../ioc-extras'; + +@injectable() +export class StartDebugingAndStopOnEntry implements IExtensionContribution { + public register(context: ExtensionContext) { + context.subscriptions.push( + registerCommand( + commands, + Commands.StartWithStopOnEntry, + () => + commands.executeCommand('workbench.action.debug.start', { + config: { + stopOnEntry: true, + }, + }), + ), + ); + } +} diff --git a/code/extensions/js-debug/src/ui/terminalLinkHandler.ts b/code/extensions/js-debug/src/ui/terminalLinkHandler.ts new file mode 100644 index 000000000000..4d9283c5d8cd --- /dev/null +++ b/code/extensions/js-debug/src/ui/terminalLinkHandler.ts @@ -0,0 +1,210 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import * as l10n from '@vscode/l10n'; +import { inject, injectable } from 'inversify'; +import { find as findLink } from 'linkifyjs'; +import { URL } from 'url'; +import * as vscode from 'vscode'; +import { + Configuration, + DebugByLinkState, + DebugType, + readConfig, +} from '../common/contributionUtils'; +import { DefaultBrowser, IDefaultBrowserProvider } from '../common/defaultBrowserProvider'; +import { DisposableList, IDisposable } from '../common/disposable'; +import { once } from '../common/objUtils'; +import { ITerminalLinkProvider } from '../common/terminalLinkProvider'; +import { isLoopbackIp, isMetaAddress } from '../common/urlUtils'; + +interface ITerminalLink extends vscode.TerminalLink { + target: URL; + workspaceFolder?: number; +} + +const enum Protocol { + Http = 'http:', + Https = 'https:', +} + +@injectable() +export class TerminalLinkHandler implements ITerminalLinkProvider, IDisposable { + private readonly enabledTerminals = new WeakSet(); + private readonly disposable = new DisposableList(); + private notifiedCantOpenOnWeb = false; + private baseConfiguration = this.readConfig(); + + constructor(@inject(IDefaultBrowserProvider) private defaultBrowser: IDefaultBrowserProvider) { + this.disposable.push( + vscode.workspace.onDidChangeConfiguration(evt => { + if (evt.affectsConfiguration(Configuration.DebugByLinkOptions)) { + this.baseConfiguration = this.readConfig(); + } + }), + vscode.window.registerTerminalLinkProvider(this), + ); + } + + /** + * Turns on link handling in the given terminal. + */ + public enableHandlingInTerminal(terminal: vscode.Terminal) { + this.enabledTerminals.add(terminal); + } + + /** + * @inheritdoc + */ + public dispose() { + this.disposable.dispose(); + } + + /** + * @inheritdoc + */ + public provideTerminalLinks(context: vscode.TerminalLinkContext): ITerminalLink[] { + switch (this.baseConfiguration.enabled) { + case 'off': + return []; + case 'always': + break; + case 'on': + default: + if (!this.enabledTerminals.has(context.terminal)) { + return []; + } + } + + const links: ITerminalLink[] = []; + const getCwd = once(() => { + // Do our best to resolve the right workspace folder to launch in, and debug + if ('cwd' in context.terminal.creationOptions && context.terminal.creationOptions.cwd) { + const folder = vscode.workspace.getWorkspaceFolder( + typeof context.terminal.creationOptions.cwd === 'string' + ? vscode.Uri.file(context.terminal.creationOptions.cwd) + : context.terminal.creationOptions.cwd, + ); + + if (folder) { + return folder; + } + } + + return vscode.workspace.workspaceFolders?.[0]; + }); + + for (const link of findLink(context.line, 'url')) { + let start = -1; + while ((start = context.line.indexOf(link.value, start + 1)) !== -1) { + let uri: URL; + try { + uri = new URL(link.href); + } catch { + continue; + } + + // hack for https://github.com/Soapbox/linkifyjs/issues/317 + if ( + uri.protocol === Protocol.Http + && !link.value.startsWith(Protocol.Http) + && !isLoopbackIp(uri.hostname) + ) { + uri.protocol = Protocol.Https; + } + + if (uri.protocol !== Protocol.Http && uri.protocol !== Protocol.Https) { + continue; + } + + links.push({ + startIndex: start, + length: link.value.length, + tooltip: l10n.t('Debug URL'), + target: uri, + workspaceFolder: getCwd()?.index, + }); + } + } + + return links; + } + + /** + * @inheritdoc + */ + public async handleTerminalLink(terminal: ITerminalLink): Promise { + if (!(await this.handleTerminalLinkInner(terminal))) { + vscode.env.openExternal(vscode.Uri.parse(terminal.target.toString())); + } + } + + /** + * Launches a browser debug session when a link is clicked from a debug terminal. + */ + public async handleTerminalLinkInner(terminal: ITerminalLink): Promise { + if (!terminal.target) { + return false; + } + + const uri = terminal.target; + + if (vscode.env.uiKind === vscode.UIKind.Web) { + if (this.notifiedCantOpenOnWeb) { + return false; + } + + vscode.window.showInformationMessage( + l10n.t( + "We can't launch a browser in debug mode from here. If you want to debug this webpage, open this workspace from VS Code on your desktop.", + ), + ); + + this.notifiedCantOpenOnWeb = true; + return false; + } + + if (isMetaAddress(uri.hostname)) { + uri.hostname = 'localhost'; + } + + let debugType: DebugType.Chrome | DebugType.Edge = DebugType.Chrome; + try { + if ((await this.defaultBrowser.lookup()) === DefaultBrowser.Edge) { + debugType = DebugType.Edge; + } + } catch { + // ignored + } + + const cwd = terminal.workspaceFolder !== undefined + ? vscode.workspace.workspaceFolders?.[terminal.workspaceFolder] + : undefined; + + vscode.debug.startDebugging(cwd, { + ...this.baseConfiguration, + type: debugType, + name: uri.toString(), + request: 'launch', + url: uri.toString(), + }); + + return true; + } + + private readConfig() { + let baseConfig = readConfig(vscode.workspace, Configuration.DebugByLinkOptions); + + if (typeof baseConfig === 'boolean') { + // old setting + baseConfig = (baseConfig ? 'on' : 'off') as DebugByLinkState; + } + + if (typeof baseConfig === 'string') { + return { enabled: baseConfig }; + } + + return { enabled: 'on' as DebugByLinkState, ...baseConfig }; + } +} diff --git a/code/extensions/js-debug/src/ui/toggleSkippingFile.ts b/code/extensions/js-debug/src/ui/toggleSkippingFile.ts new file mode 100644 index 000000000000..a1fbdbbba97b --- /dev/null +++ b/code/extensions/js-debug/src/ui/toggleSkippingFile.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { fileURLToPath } from 'url'; +import * as vscode from 'vscode'; +import { isFileUrl } from '../common/urlUtils'; +import Dap from '../dap/api'; + +export async function toggleSkippingFile(aPath: string | number): Promise { + if (!aPath) { + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) return; + aPath = activeEditor && activeEditor.document.fileName; + } + + if (aPath && vscode.debug.activeDebugSession) { + let args: Dap.ToggleSkipFileStatusParams; + if (typeof aPath === 'string') { + if (isFileUrl(aPath)) { + args = { resource: fileURLToPath(aPath) }; + } else { + args = { resource: aPath }; + } + } else { + args = { sourceReference: aPath }; + } + + await vscode.debug.activeDebugSession.customRequest('toggleSkipFileStatus', args); + } +} diff --git a/code/extensions/js-debug/src/ui/ui-ioc.extensionOnly.ts b/code/extensions/js-debug/src/ui/ui-ioc.extensionOnly.ts new file mode 100644 index 000000000000..2ebfb9160cc0 --- /dev/null +++ b/code/extensions/js-debug/src/ui/ui-ioc.extensionOnly.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Container } from 'inversify'; +import { IDwarfModuleProvider } from '../adapter/dwarf/dwarfModuleProvider'; +import { IRequestOptionsProvider } from '../adapter/resourceProvider/requestOptionsProvider'; +import { ITerminalLinkProvider } from '../common/terminalLinkProvider'; +import { + IDebugTerminalOptionsProviders, + IExtensionContribution, + trackDispose, + VSCodeApi, +} from '../ioc-extras'; +import { EditorBrowserAttacher } from '../targets/browser/editorBrowserAttacher'; +import { EditorBrowserLauncher } from '../targets/browser/editorBrowserLauncher'; +import { TerminalNodeLauncher } from '../targets/node/terminalNodeLauncher'; +import { ILauncher } from '../targets/targets'; +import { IExperimentationService } from '../telemetry/experimentationService'; +import { VSCodeExperimentationService } from '../telemetry/vscodeExperimentationService'; +import { CascadeTerminationTracker } from './cascadeTerminateTracker'; +import { + allConfigurationProviders, + allConfigurationResolvers, + IDebugConfigurationProvider, + IDebugConfigurationResolver, +} from './configuration'; +import { DebugLinkUi } from './debugLinkUI'; +import { DebugSessionTracker } from './debugSessionTracker'; +import { DiagnosticsUI } from './diagnosticsUI'; +import { DisableSourceMapUI } from './disableSourceMapUI'; +import { DwarfModuleProvider } from './dwarfModuleProviderImpl'; +import { EdgeDevToolOpener } from './edgeDevToolOpener'; +import { ExcludedCallersUI } from './excludedCallersUI'; +import { ExtensionApiFactory } from './extensionApi'; +import { LaunchJsonCompletions } from './launchJsonCompletions'; +import { ILinkedBreakpointLocation } from './linkedBreakpointLocation'; +import { LinkedBreakpointLocationUI } from './linkedBreakpointLocationUI'; +import { LongPredictionUI } from './longPredictionUI'; +import { NetworkTree } from './networkTree'; +import { JsDebugPortAttributesProvider } from './portAttributesProvider'; +import { PrettyPrintUI } from './prettyPrint'; +import { BreakpointTerminationConditionFactory } from './profiling/breakpointTerminationCondition'; +import { DurationTerminationConditionFactory } from './profiling/durationTerminationCondition'; +import { ManualTerminationConditionFactory } from './profiling/manualTerminationCondition'; +import { ITerminationConditionFactory } from './profiling/terminationCondition'; +import { UiProfileManager } from './profiling/uiProfileManager'; +import { SettingRequestOptionsProvider } from './settingRequestOptionsProvider'; +import { SourceSteppingUI } from './sourceSteppingUI'; +import { StartDebugingAndStopOnEntry } from './startDebuggingAndStopOnEntry'; +import { TerminalLinkHandler } from './terminalLinkHandler'; + +export const registerUiComponents = (container: Container) => { + container.bind(VSCodeApi).toConstantValue(require('vscode')); + + allConfigurationResolvers.forEach(cls => { + container + .bind(cls as { new(...args: unknown[]): unknown }) + .toSelf() + .inSingletonScope(); + container.bind(IDebugConfigurationResolver).to(cls); + }); + + allConfigurationProviders.forEach(cls => + container.bind(IDebugConfigurationProvider).to(cls).inSingletonScope() + ); + + container.bind(IExtensionContribution).to(LongPredictionUI).inSingletonScope(); + container.bind(IExtensionContribution).to(DebugLinkUi).inSingletonScope(); + container.bind(IExtensionContribution).to(CascadeTerminationTracker).inSingletonScope(); + container.bind(IExtensionContribution).to(DisableSourceMapUI).inSingletonScope(); + container.bind(IExtensionContribution).to(DiagnosticsUI).inSingletonScope(); + container.bind(IExtensionContribution).to(StartDebugingAndStopOnEntry).inSingletonScope(); + container.bind(IExtensionContribution).to(JsDebugPortAttributesProvider).inSingletonScope(); + container.bind(IExtensionContribution).to(EdgeDevToolOpener).inSingletonScope(); + container.bind(IExtensionContribution).to(ExcludedCallersUI).inSingletonScope(); + container.bind(IExtensionContribution).to(PrettyPrintUI).inSingletonScope(); + container.bind(IExtensionContribution).to(SourceSteppingUI).inSingletonScope(); + container.bind(IExtensionContribution).to(NetworkTree).inSingletonScope(); + container.bind(IExtensionContribution).to(LaunchJsonCompletions).inSingletonScope().onActivation( + trackDispose, + ); + container.bind(ILinkedBreakpointLocation).to(LinkedBreakpointLocationUI).inSingletonScope(); + container.bind(DebugSessionTracker).toSelf().inSingletonScope().onActivation(trackDispose); + container.bind(UiProfileManager).toSelf().inSingletonScope().onActivation(trackDispose); + container.bind(DisableSourceMapUI).toSelf().inSingletonScope(); + container.bind(IDwarfModuleProvider).to(DwarfModuleProvider).inSingletonScope(); + container + .bind(ITerminalLinkProvider) + .to(TerminalLinkHandler) + .inSingletonScope() + .onActivation(trackDispose); + + container + .bind(ITerminationConditionFactory) + .to(DurationTerminationConditionFactory) + .inSingletonScope(); + container + .bind(ITerminationConditionFactory) + .to(ManualTerminationConditionFactory) + .inSingletonScope(); + container + .bind(ITerminationConditionFactory) + .to(BreakpointTerminationConditionFactory) + .inSingletonScope(); + + container.bind(IDebugTerminalOptionsProviders) + .toConstantValue(new Set()); + + container.bind(ExtensionApiFactory).toSelf().inSingletonScope(); +}; + +export const registerTopLevelSessionComponents = (container: Container) => { + container.bind(ILauncher).to(TerminalNodeLauncher).onActivation(trackDispose); + container.bind(ILauncher).to(EditorBrowserLauncher).onActivation(trackDispose); + container.bind(ILauncher).to(EditorBrowserAttacher).onActivation(trackDispose); + + // request options: + container.bind(IRequestOptionsProvider).to(SettingRequestOptionsProvider).inSingletonScope(); + + container.bind(IExperimentationService).to(VSCodeExperimentationService).inSingletonScope() + .onActivation(trackDispose); +}; diff --git a/code/extensions/js-debug/src/ui/ui-ioc.ts b/code/extensions/js-debug/src/ui/ui-ioc.ts new file mode 100644 index 000000000000..9b1297e3bb93 --- /dev/null +++ b/code/extensions/js-debug/src/ui/ui-ioc.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Container } from 'inversify'; + +export const registerUiComponents = (_container: Container) => { + // no-op function that's loaded in standalone debug servers +}; + +export const registerTopLevelSessionComponents = (_container: Container) => { + // no-op function that's loaded in standalone debug servers +}; diff --git a/code/extensions/js-debug/src/ui/vsCodeSessionManager.ts b/code/extensions/js-debug/src/ui/vsCodeSessionManager.ts new file mode 100644 index 000000000000..66963abcf8cd --- /dev/null +++ b/code/extensions/js-debug/src/ui/vsCodeSessionManager.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +import { Container } from 'inversify'; +import * as vscode from 'vscode'; +import { IDisposable } from '../common/events'; +import { IPseudoAttachConfiguration } from '../configuration'; +import { ServerSessionManager } from '../serverSessionManager'; +import { ISessionLauncher, RootSession, Session } from '../sessionManager'; +import { ITarget } from '../targets/targets'; + +/** + * Session launcher which uses vscode's `startDebugging` method to start a new debug session + * @param parentSession The parent debug session to pass to `startDebugging` + * @param config Launch configuration for the new debug session + */ +class VsCodeSessionLauncher implements ISessionLauncher { + launch( + parentSession: Session, + target: ITarget, + config: IPseudoAttachConfiguration, + ) { + vscode.debug.startDebugging( + parentSession.debugSession.workspaceFolder, + { + ...config, + ...target.supplementalConfig, + serverReadyAction: parentSession.debugSession.configuration.serverReadyAction, + __parentId: parentSession.debugSession.id, + } as vscode.DebugConfiguration, + { + parentSession: parentSession.debugSession, + consoleMode: vscode.DebugConsoleMode.MergeWithParent, + noDebug: parentSession.debugSession.configuration.noDebug, + compact: parentSession instanceof RootSession, // don't compact workers/child processes + lifecycleManagedByParent: target.independentLifeycle ? false : true, + }, + ); + } +} + +/** + * VS Code specific session manager which also implements the DebugAdapterDescriptorFactory + * interface + */ +export class VSCodeSessionManager implements vscode.DebugAdapterDescriptorFactory, IDisposable { + private readonly sessionServerManager: ServerSessionManager; + + constructor(globalContainer: Container) { + this.sessionServerManager = new ServerSessionManager( + globalContainer, + new VsCodeSessionLauncher(), + ); + } + + /** + * @inheritdoc + */ + public async createDebugAdapterDescriptor( + debugSession: vscode.DebugSession, + ): Promise { + const useLocal = process.env.JS_DEBUG_USE_LOCAL_DAP_PORT; + if (useLocal) { + return new vscode.DebugAdapterServer(+useLocal); + } + + const result = await this.sessionServerManager.createDebugServer(debugSession); + return new vscode.DebugAdapterNamedPipeServer(result.server.address() as string); + } + + /** + * @inheritdoc + */ + public terminate(debugSession: vscode.DebugSession) { + this.sessionServerManager.terminate(debugSession); + } + + /** + * @inheritdoc + */ + public dispose() { + this.sessionServerManager.dispose(); + } +} diff --git a/code/extensions/js-debug/src/vsDebugServer.ts b/code/extensions/js-debug/src/vsDebugServer.ts new file mode 100644 index 000000000000..6e1a4754202b --- /dev/null +++ b/code/extensions/js-debug/src/vsDebugServer.ts @@ -0,0 +1,156 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +require('source-map-support').install(); // Enable TypeScript stack traces translation +import * as l10n from '@vscode/l10n'; +import * as fs from 'fs'; +/** + * This script launches vscode-js-debug in server mode for Visual Studio + */ +import * as net from 'net'; +import * as os from 'os'; +import * as path from 'path'; +import 'reflect-metadata'; +import { Readable, Writable } from 'stream'; +import { DebugConfiguration } from 'vscode'; +import { DebugType } from './common/contributionUtils'; +import { getDeferred, IDeferred } from './common/promiseUtil'; +import { IPseudoAttachConfiguration } from './configuration'; +import DapConnection from './dap/connection'; +import { createGlobalContainer } from './ioc'; +import { ServerSessionManager } from './serverSessionManager'; +import { IDebugSessionLike, ISessionLauncher, Session } from './sessionManager'; +import { ITarget } from './targets/targets'; + +const storagePath = fs.mkdtempSync(path.join(os.tmpdir(), 'vscode-js-debug-')); + +if (process.env.L10N_FSPATH_TO_BUNDLE) { + l10n.config({ fsPath: process.env.L10N_FSPATH_TO_BUNDLE }); +} + +class VSDebugSession implements IDebugSessionLike { + constructor( + public id: string, + name: string, + private readonly childConnection: Promise, + public readonly configuration: DebugConfiguration, + ) { + this._name = name; + } + + private _name: string; + set name(newName: string) { + this._name = newName; + this.childConnection + .then(conn => conn.initializedBlocker) + .then(conn => conn.dap().process({ name: newName })); + } + get name() { + return this._name; + } +} + +class VsDebugServer implements ISessionLauncher { + private readonly sessionServer: ServerSessionManager; + + constructor(host?: string, inputStream?: Readable, outputStream?: Writable) { + const services = createGlobalContainer({ storagePath, isVsCode: false }); + this.sessionServer = new ServerSessionManager(services, this, host); + + const deferredConnection: IDeferred = getDeferred(); + const rootSession = new VSDebugSession( + 'root', + l10n.t('JavaScript debug adapter'), + deferredConnection.promise, + { type: DebugType.Chrome, name: 'root', request: 'launch' }, + ); + if (inputStream && outputStream) { + this.launchRootFromExisting(deferredConnection, rootSession, inputStream, outputStream); + } else { + this.launchRoot(deferredConnection, rootSession); + } + } + + private launchRootFromExisting( + deferredConnection: IDeferred, + session: VSDebugSession, + inputStream: Readable, + outputStream: Writable, + ) { + const newSession = this.sessionServer.createRootDebugSessionFromStreams( + session, + inputStream, + outputStream, + ); + deferredConnection.resolve(newSession.connection); + } + + async launchRoot(deferredConnection: IDeferred, session: VSDebugSession) { + const result = await this.sessionServer.createRootDebugServer(session, debugServerPort ?? 0); + result.connectionPromise.then(x => deferredConnection.resolve(x)); + console.log((result.server.address() as net.AddressInfo).port.toString()); + } + + public launch( + parentSession: Session, + target: ITarget, + config: IPseudoAttachConfiguration, + ): void { + const childAttachConfig = { ...config, sessionId: target.id, __jsDebugChildServer: '' }; + const deferredConnection: IDeferred = getDeferred(); + const session = new VSDebugSession( + target.id(), + target.name(), + deferredConnection.promise, + childAttachConfig, + ); + + this.sessionServer.createChildDebugServer(session, 0).then( + ({ server, connectionPromise }) => { + connectionPromise.then(x => deferredConnection.resolve(x)); + childAttachConfig.__jsDebugChildServer = ( + server.address() as net.AddressInfo + ).port.toString(); + + // Custom message currently not part of DAP + parentSession.connection._send({ + seq: 0, + command: 'attachedChildSession', + type: 'request', + arguments: { + config: childAttachConfig, + }, + }); + }, + ); + } +} + +let debugServerPort: number | undefined = undefined; +let debugServerHost: string | undefined = undefined; + +if (process.argv.length >= 3) { + debugServerPort = +process.argv[2]; + if (process.argv.length >= 4) { + debugServerHost = process.argv[3]; + } +} + +if (debugServerPort !== undefined) { + const server = net + .createServer(socket => { + new VsDebugServer(debugServerHost, socket, socket); + }) + .listen(debugServerPort, debugServerHost); + + server.on('listening', () => { + console.log( + `Listening at ${(server.address() as net.AddressInfo).address}:${ + (server.address() as net.AddressInfo).port + }`, + ); + }); +} else { + new VsDebugServer(debugServerHost); +} diff --git a/code/extensions/js-debug/testWorkspace/babelLineNumbers/app.tsx b/code/extensions/js-debug/testWorkspace/babelLineNumbers/app.tsx new file mode 100644 index 000000000000..a973d0e38cdc --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/babelLineNumbers/app.tsx @@ -0,0 +1,4 @@ +export default function App() { + console.log('greetings!'); + return 'hello world'; +} diff --git a/code/extensions/js-debug/testWorkspace/babelLineNumbers/columns-test.txt b/code/extensions/js-debug/testWorkspace/babelLineNumbers/columns-test.txt new file mode 100644 index 000000000000..9bfeb1da5d69 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/babelLineNumbers/columns-test.txt @@ -0,0 +1,12 @@ +Sit + culpa ea + +exercitation qui qui + + fugiat velit reprehenderit +officia +ipsum +ex +laboris +proident +ipsum. diff --git a/code/extensions/js-debug/testWorkspace/babelLineNumbers/compiled.js b/code/extensions/js-debug/testWorkspace/babelLineNumbers/compiled.js new file mode 100644 index 000000000000..12ab16c47932 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/babelLineNumbers/compiled.js @@ -0,0 +1,122 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/"; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./src/index.tsx"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./src/app.tsx": +/*!*********************!*\ + !*** ./src/app.tsx ***! + \*********************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return App; }); +function App() { + console.log('greetings!'); + return 'hello world'; +} + +/***/ }), + +/***/ "./src/index.tsx": +/*!***********************!*\ + !*** ./src/index.tsx ***! + \***********************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _app__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./app */ "./src/app.tsx"); + +Object(_app__WEBPACK_IMPORTED_MODULE_0__["default"])(); + +/***/ }) + +/******/ }); +//# sourceMappingURL=compiled.js.map diff --git a/code/extensions/js-debug/testWorkspace/babelLineNumbers/compiled.js.map b/code/extensions/js-debug/testWorkspace/babelLineNumbers/compiled.js.map new file mode 100644 index 000000000000..12b50a51a91d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/babelLineNumbers/compiled.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","app.tsx","index.tsx"],"names":["App","console","log"],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;;AClFA;AAAA;AAAe,SAASA,GAAT,GAAe;AAC5BC,SAAO,CAACC,GAAR,CAAY,YAAZ;AACA,SAAO,aAAP;AACD,C;;;;;;;;;;;;ACHD;AAAA;AAAA;AAEAF,oDAAG,G","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/index.tsx\");\n","export default function App() {\n console.log('greetings!');\n return 'hello world';\n}\n","import App from \"./app\";\n\nApp();\n"],"sourceRoot":"."} diff --git a/code/extensions/js-debug/testWorkspace/babelLineNumbers/index.tsx b/code/extensions/js-debug/testWorkspace/babelLineNumbers/index.tsx new file mode 100644 index 000000000000..7b159b695643 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/babelLineNumbers/index.tsx @@ -0,0 +1,3 @@ +import App from "./app"; + +App(); diff --git a/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.Debugger.Sample.exe b/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.Debugger.Sample.exe new file mode 100644 index 000000000000..ec25a26eba62 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.Debugger.Sample.exe differ diff --git a/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.Debugger.dll b/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.Debugger.dll new file mode 100644 index 000000000000..90da1f6497f9 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.Debugger.dll differ diff --git a/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.dll b/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.dll new file mode 100644 index 000000000000..be6a08fbee54 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/chakracore/ChakraCore.dll differ diff --git a/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.js b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.js new file mode 100644 index 000000000000..dbc77d4b53dd --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.js @@ -0,0 +1,62 @@ +"use strict"; +function customDebuggerDescription(obj, defaultValue) { + if (obj.constructor && obj.constructor.prototype === obj) { + // object is a prototype + if (obj.constructor.name) { + return `Prototype of ${obj.constructor.name}`; + } + else { + return defaultValue; + } + } + else if (obj.customDescription && obj.customDescription instanceof Function) { + return obj.customDescription(); + } + else if (defaultValue.startsWith("class ")) { + // just print class name without the constructor source code + const className = defaultValue.split(" ", 2)[1]; + return `class ${className}`; + } + else { + return defaultValue; + } +} +global.customDebuggerDescription = customDebuggerDescription; +function customPropertiesGenerator(obj) { + if (obj && obj.customPropertiesGenerator && obj.customPropertiesGenerator instanceof Function) { + return obj.customPropertiesGenerator(); + } + else { + return obj; + } +} +global.customPropertiesGenerator = customPropertiesGenerator; +class Fraction { + constructor(numerator, denominator) { + this.numerator = numerator; + this.denominator = denominator; + } + customDescription() { + return `${this.numerator}/${this.denominator}`; + } + floatValue() { + return this.numerator / this.denominator; + } + customPropertiesGenerator() { + const properties = Object.create(this.__proto__); + Object.assign(properties, Object.assign(Object.assign({}, this), { asRational: this.floatValue() })); + return properties; + } +} +const fraction1 = new Fraction(2, 3); +const fraction2 = new Fraction(3, 4); +const fraction3 = new Fraction(5, 6); +console.log("Line 1"); +console.log("Line 2"); +console.log("Line 3"); +console.log("Line 4"); +console.log("Line 5"); +console.log("Line 6"); +console.log("Line 7"); +console.log("Line 8"); +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.js.map b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.js.map new file mode 100644 index 000000000000..1ccefe6ae1a8 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";AAAA,SAAS,yBAAyB,CAAC,GAAQ,EAAE,YAAoB;IAC/D,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,SAAS,KAAK,GAAG,EAAE;QACxD,wBAAwB;QACxB,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE;YACxB,OAAO,gBAAgB,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;SAC/C;aAAM;YACL,OAAO,YAAY,CAAC;SACrB;KACF;SAAM,IAAI,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC,iBAAiB,YAAY,QAAQ,EAAE;QAC7E,OAAO,GAAG,CAAC,iBAAiB,EAAE,CAAC;KAChC;SAAM,IAAI,YAAY,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5C,4DAA4D;QAC5D,MAAM,SAAS,GAAW,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,OAAO,SAAS,SAAS,EAAE,CAAC;KAC7B;SAAM;QACL,OAAO,YAAY,CAAC;KACrB;AACH,CAAC;AAEA,MAAc,CAAC,yBAAyB,GAAG,yBAAyB,CAAC;AAEtE,SAAS,wBAAwB,CAAC,GAAQ;IACxC,IAAI,GAAG,IAAI,GAAG,CAAC,wBAAwB,IAAI,GAAG,CAAC,wBAAwB,YAAY,QAAQ,EAAE;QAC3F,OAAO,GAAG,CAAC,wBAAwB,EAAE,CAAC;KACvC;SAAM;QACL,OAAO,GAAG,CAAC;KACZ;AACH,CAAC;AAEA,MAAc,CAAC,wBAAwB,GAAG,wBAAwB,CAAC;AAEpE,MAAM,QAAQ;IACZ,YAAoC,SAAiB,EAAmB,WAAmB;QAAvD,cAAS,GAAT,SAAS,CAAQ;QAAmB,gBAAW,GAAX,WAAW,CAAQ;IAAI,CAAC;IAEzF,iBAAiB;QACtB,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;IACjD,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC;IAC3C,CAAC;IAEM,wBAAwB;QAC7B,MAAM,UAAU,GAAW,MAAM,CAAC,MAAM,CAAE,IAAY,CAAC,SAAS,CAAC,CAAC;QAClE,MAAM,CAAC,MAAM,CAAC,UAAU,kCAAO,IAAI,KAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,IAAG,CAAC;QACtE,OAAO,UAAU,CAAC;IACpB,CAAC;CACF;AAED,MAAM,SAAS,GAAa,IAAI,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/C,MAAM,SAAS,GAAa,IAAI,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/C,MAAM,SAAS,GAAa,IAAI,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE/C,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC"} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.ts b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.ts new file mode 100644 index 000000000000..22b2edd970ae --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/app.ts @@ -0,0 +1,61 @@ +function customDebuggerDescription(obj: any, defaultValue: string): string { + if (obj.constructor && obj.constructor.prototype === obj) { + // object is a prototype + if (obj.constructor.name) { + return `Prototype of ${obj.constructor.name}`; + } else { + return defaultValue; + } + } else if (obj.customDescription && obj.customDescription instanceof Function) { + return obj.customDescription(); + } else if (defaultValue.startsWith("class ")) { + // just print class name without the constructor source code + const className: string = defaultValue.split(" ", 2)[1]; + return `class ${className}`; + } else { + return defaultValue; + } +} + +(global as any).customDebuggerDescription = customDebuggerDescription; + +function customPropertiesGenerator(obj: any): object { + if (obj && obj.customPropertiesGenerator && obj.customPropertiesGenerator instanceof Function) { + return obj.customPropertiesGenerator(); + } else { + return obj; + } +} + +(global as any).customPropertiesGenerator = customPropertiesGenerator; + +class Fraction { + public constructor(private readonly numerator: number, private readonly denominator: number) { } + + public customDescription(): string { + return `${this.numerator}/${this.denominator}`; + } + + public floatValue(): number { + return this.numerator / this.denominator; + } + + public customPropertiesGenerator(): object { + const properties: object = Object.create((this as any).__proto__); + Object.assign(properties, { ...this, asRational: this.floatValue() }); + return properties; + } +} + +const fraction1: Fraction = new Fraction(2, 3); +const fraction2: Fraction = new Fraction(3, 4); +const fraction3: Fraction = new Fraction(5, 6); + +console.log("Line 1"); +console.log("Line 2"); +console.log("Line 3"); +console.log("Line 4"); +console.log("Line 5"); +console.log("Line 6"); +console.log("Line 7"); +console.log("Line 8"); diff --git a/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/tsconfig.json b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/tsconfig.json new file mode 100644 index 000000000000..e599a9e80bb6 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/customDebuggerDescriptions/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": ".", + "lib": [ + "dom" + ], + "sourceMap": true, + "rootDir": ".", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "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. */ + }, + "files": ["app.ts"] +} + diff --git a/code/extensions/js-debug/testWorkspace/glob(chars)/app.js b/code/extensions/js-debug/testWorkspace/glob(chars)/app.js new file mode 100644 index 000000000000..d069dca998a6 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/glob(chars)/app.js @@ -0,0 +1,10 @@ +"use strict"; +console.log("Line 1"); +console.log("Line 2"); +console.log("Line 3"); +console.log("Line 4"); +console.log("Line 5"); +console.log("Line 6"); +console.log("Line 7"); +console.log("Line 8"); +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/glob(chars)/app.js.map b/code/extensions/js-debug/testWorkspace/glob(chars)/app.js.map new file mode 100644 index 000000000000..1b9095582b3d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/glob(chars)/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";AAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC"} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/glob(chars)/app.ts b/code/extensions/js-debug/testWorkspace/glob(chars)/app.ts new file mode 100644 index 000000000000..5c77622557e2 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/glob(chars)/app.ts @@ -0,0 +1,8 @@ +console.log("Line 1"); +console.log("Line 2"); +console.log("Line 3"); +console.log("Line 4"); +console.log("Line 5"); +console.log("Line 6"); +console.log("Line 7"); +console.log("Line 8"); diff --git a/code/extensions/js-debug/testWorkspace/hashTestCases/blns.js b/code/extensions/js-debug/testWorkspace/hashTestCases/blns.js new file mode 100644 index 000000000000..8142de241cfc --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/hashTestCases/blns.js @@ -0,0 +1,760 @@ +/* + +Naughty strings: https://github.com/minimaxir/big-list-of-naughty-strings + +The MIT License (MIT) + +Copyright (c) 2015-2020 Max Woolf + +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. + + */ + +const a = `# Reserved Strings +# +# Strings which may be used elsewhere in code + +undefined +undef +null +NULL +(null) +nil +NIL +true +false +True +False +TRUE +FALSE +None +hasOwnProperty +then +constructor +\ +\\ + +# Numeric Strings +# +# Strings which can be interpreted as numeric + +0 +1 +1.00 +$1.00 +1/2 +1E2 +1E02 +1E+02 +-1 +-1.00 +-$1.00 +-1/2 +-1E2 +-1E02 +-1E+02 +1/0 +0/0 +-2147483648/-1 +-9223372036854775808/-1 +-0 +-0.0 ++0 ++0.0 +0.00 +0..0 +. +0.0.0 +0,00 +0,,0 +, +0,0,0 +0.0/0 +1.0/0.0 +0.0/0.0 +1,0/0,0 +0,0/0,0 +--1 +- +-. +-, +999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +NaN +Infinity +-Infinity +INF +1#INF +-1#IND +1#QNAN +1#SNAN +1#IND +0x0 +0xffffffff +0xffffffffffffffff +0xabad1dea +123456789012345678901234567890123456789 +1,000.00 +1 000.00 +1'000.00 +1,000,000.00 +1 000 000.00 +1'000'000.00 +1.000,00 +1 000,00 +1'000,00 +1.000.000,00 +1 000 000,00 +1'000'000,00 +01000 +08 +09 +2.2250738585072011e-308 + +# Special Characters +# +# ASCII punctuation. All of these characters may need to be escaped in some +# contexts. Divided into three groups based on (US-layout) keyboard position. + +,./;'[]\-= +<>?:"{}|_+ +!@#$%^&*()\`~ + +# Non-whitespace C0 controls: U+0001 through U+0008, U+000E through U+001F, +# and U+007F (DEL) +# Often forbidden to appear in various text-based file formats (e.g. XML), +# or reused for internal delimiters on the theory that they should never +# appear in input. +# The next line may appear to be blank or mojibake in some viewers. + + +# Non-whitespace C1 controls: U+0080 through U+0084 and U+0086 through U+009F. +# Commonly misinterpreted as additional graphic characters. +# The next line may appear to be blank, mojibake, or dingbats in some viewers. +€‚ƒ„†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ + +# Whitespace: all of the characters with category Zs, Zl, or Zp (in Unicode +# version 8.0.0), plus U+0009 (HT), U+000B (VT), U+000C (FF), U+0085 (NEL), +# and U+200B (ZERO WIDTH SPACE), which are in the C categories but are often +# treated as whitespace in some contexts. +# This file unfortunately cannot express strings containing +# U+0000, U+000A, or U+000D (NUL, LF, CR). +# The next line may appear to be blank or mojibake in some viewers. +# The next line may be flagged for "trailing whitespace" in some viewers. + …             ​

    + +# Unicode additional control characters: all of the characters with +# general category Cf (in Unicode 8.0.0). +# The next line may appear to be blank or mojibake in some viewers. +­؀؁؂؃؄؅؜۝܏᠎​‌‍‎‏‪‫‬‭‮⁠⁡⁢⁣⁤⁦⁧⁨⁩𑂽𛲠𛲡𛲢𛲣𝅳𝅴𝅵𝅶𝅷𝅸𝅹𝅺󠀁󠀠󠀡󠀢󠀣󠀤󠀥󠀦󠀧󠀨󠀩󠀪󠀫󠀬󠀭󠀮󠀯󠀰󠀱󠀲󠀳󠀴󠀵󠀶󠀷󠀸󠀹󠀺󠀻󠀼󠀽󠀾󠀿󠁀󠁁󠁂󠁃󠁄󠁅󠁆󠁇󠁈󠁉󠁊󠁋󠁌󠁍󠁎󠁏󠁐󠁑󠁒󠁓󠁔󠁕󠁖󠁗󠁘󠁙󠁚󠁛󠁜󠁝󠁞󠁟󠁠󠁡󠁢󠁣󠁤󠁥󠁦󠁧󠁨󠁩󠁪󠁫󠁬󠁭󠁮󠁯󠁰󠁱󠁲󠁳󠁴󠁵󠁶󠁷󠁸󠁹󠁺󠁻󠁼󠁽󠁾󠁿 + +# "Byte order marks", U+FEFF and U+FFFE, each on its own line. +# The next two lines may appear to be blank or mojibake in some viewers. + +￾ + +# Unicode Symbols +# +# Strings which contain common unicode symbols (e.g. smart quotes) + +Ω≈ç√∫˜µ≤≥÷ +åß∂ƒ©˙∆˚¬…æ +œ∑´®†¥¨ˆøπ“‘ +¡™£¢∞§¶•ªº–≠ +¸˛Ç◊ı˜Â¯˘¿ +ÅÍÎÏ˝ÓÔÒÚÆ☃ +Œ„´‰ˇÁ¨ˆØ∏”’ +\`⁄€‹›fifl‡°·‚—± +⅛⅜⅝⅞ +ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя +٠١٢٣٤٥٦٧٨٩ + +# Unicode Subscript/Superscript/Accents +# +# Strings which contain unicode subscripts/superscripts; can cause rendering issues + +⁰⁴⁵ +₀₁₂ +⁰⁴⁵₀₁₂ +ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็ + +# Quotation Marks +# +# Strings which contain misplaced quotation marks; can cause encoding errors + +' +" +'' +"" +'"' +"''''"'" +"'"'"''''" + + + + + +# Two-Byte Characters +# +# Strings which contain two-byte characters: can cause rendering issues or character-length issues + +田中さんにあげて下さい +パーティーへ行かないか +和製漢語 +部落格 +사회과학원 어학연구소 +찦차를 타고 온 펲시맨과 쑛다리 똠방각하 +社會科學院語學研究所 +울란바토르 +𠜎𠜱𠝹𠱓𠱸𠲖𠳏 + +# Strings which contain two-byte letters: can cause issues with naïve UTF-16 capitalizers which think that 16 bits == 1 character + +𐐜 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐙𐐊𐐡𐐝𐐓/𐐝𐐇𐐗𐐊𐐤𐐔 𐐒𐐋𐐗 𐐒𐐌 𐐜 𐐡𐐀𐐖𐐇𐐤𐐓𐐝 𐐱𐑂 𐑄 𐐔𐐇𐐝𐐀𐐡𐐇𐐓 𐐏𐐆𐐅𐐤𐐆𐐚𐐊𐐡𐐝𐐆𐐓𐐆 + +# Special Unicode Characters Union +# +# A super string recommended by VMware Inc. Globalization Team: can effectively cause rendering issues or character-length issues to validate product globalization readiness. +# +# 表 CJK_UNIFIED_IDEOGRAPHS (U+8868) +# ポ KATAKANA LETTER PO (U+30DD) +# あ HIRAGANA LETTER A (U+3042) +# A LATIN CAPITAL LETTER A (U+0041) +# 鷗 CJK_UNIFIED_IDEOGRAPHS (U+9DD7) +# Œ LATIN SMALL LIGATURE OE (U+0153) +# é LATIN SMALL LETTER E WITH ACUTE (U+00E9) +# B FULLWIDTH LATIN CAPITAL LETTER B (U+FF22) +# 逍 CJK_UNIFIED_IDEOGRAPHS (U+900D) +# Ü LATIN SMALL LETTER U WITH DIAERESIS (U+00FC) +# ß LATIN SMALL LETTER SHARP S (U+00DF) +# ª FEMININE ORDINAL INDICATOR (U+00AA) +# ą LATIN SMALL LETTER A WITH OGONEK (U+0105) +# ñ LATIN SMALL LETTER N WITH TILDE (U+00F1) +# 丂 CJK_UNIFIED_IDEOGRAPHS (U+4E02) +# 㐀 CJK Ideograph Extension A, First (U+3400) +# 𠀀 CJK Ideograph Extension B, First (U+20000) + +表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀 + +# Changing length when lowercased +# +# Characters which increase in length (2 to 3 bytes) when lowercased +# Credit: https://twitter.com/jifa/status/625776454479970304 + +Ⱥ +Ⱦ + +# Japanese Emoticons +# +# Strings which consists of Japanese-style emoticons which are popular on the web + +ヽ༼ຈل͜ຈ༽ノ ヽ༼ຈل͜ຈ༽ノ +(。◕ ∀ ◕。) +`ィ(´∀`∩ +__ロ(,_,*) +・( ̄∀ ̄)・:*: +゚・✿ヾ╲(。◕‿◕。)╱✿・゚ +,。・:*:・゜’( ☻ ω ☻ )。・:*:・゜’ +(╯°□°)╯︵ ┻━┻) +(ノಥ益ಥ)ノ ┻━┻ +┬─┬ノ( º _ ºノ) +( ͡° ͜ʖ ͡°) +¯\_(ツ)_/¯ + +# Emoji +# +# Strings which contain Emoji; should be the same behavior as two-byte characters, but not always + +😍 +👩🏽 +👨‍🦰 👨🏿‍🦰 👨‍🦱 👨🏿‍🦱 🦹🏿‍♂️ +👾 🙇 💁 🙅 🙆 🙋 🙎 🙍 +🐵 🙈 🙉 🙊 +❤️ 💔 💌 💕 💞 💓 💗 💖 💘 💝 💟 💜 💛 💚 💙 +✋🏿 💪🏿 👐🏿 🙌🏿 👏🏿 🙏🏿 +🚾 🆒 🆓 🆕 🆖 🆗 🆙 🏧 +0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 + +# Regional Indicator Symbols +# +# Regional Indicator Symbols can be displayed differently across +# fonts, and have a number of special behaviors + +🇺🇸🇷🇺🇸 🇦🇫🇦🇲🇸 +🇺🇸🇷🇺🇸🇦🇫🇦🇲 +🇺🇸🇷🇺🇸🇦 + +# Unicode Numbers +# +# Strings which contain unicode numbers; if the code is localized, it should see the input as numeric + +123 +١٢٣ + +# Right-To-Left Strings +# +# Strings which contain text that should be rendered RTL if possible (e.g. Arabic, Hebrew) + +ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو. +בְּרֵאשִׁית, בָּרָא אֱלֹהִים, אֵת הַשָּׁמַיִם, וְאֵת הָאָרֶץ +הָיְתָהtestالصفحات التّحول +﷽ +ﷺ +مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ، +الكل في المجمو عة (5) + +# Ogham Text +# +# The only unicode alphabet to use a space which isn't empty but should still act like a space. + +᚛ᚄᚓᚐᚋᚒᚄ ᚑᚄᚂᚑᚏᚅ᚜ +᚛                 ᚜ + +# Trick Unicode +# +# Strings which contain unicode with unusual properties (e.g. Right-to-left override) (c.f. http://www.unicode.org/charts/PDF/U2000.pdf) + +‪‪test‪ +‫test‫ +
test
 +test⁠test‫ +⁦test⁧ + +# Zalgo Text +# +# Strings which contain "corrupted" text. The corruption will not appear in non-HTML text, however. (via http://www.eeemo.net) + +Ṱ̺̺̕o͞ ̷i̲̬͇̪͙n̝̗͕v̟̜̘̦͟o̶̙̰̠kè͚̮̺̪̹̱̤ ̖t̝͕̳̣̻̪͞h̼͓̲̦̳̘̲e͇̣̰̦̬͎ ̢̼̻̱̘h͚͎͙̜̣̲ͅi̦̲̣̰̤v̻͍e̺̭̳̪̰-m̢iͅn̖̺̞̲̯̰d̵̼̟͙̩̼̘̳ ̞̥̱̳̭r̛̗̘e͙p͠r̼̞̻̭̗e̺̠̣͟s̘͇̳͍̝͉e͉̥̯̞̲͚̬͜ǹ̬͎͎̟̖͇̤t͍̬̤͓̼̭͘ͅi̪̱n͠g̴͉ ͏͉ͅc̬̟h͡a̫̻̯͘o̫̟̖͍̙̝͉s̗̦̲.̨̹͈̣ +̡͓̞ͅI̗̘̦͝n͇͇͙v̮̫ok̲̫̙͈i̖͙̭̹̠̞n̡̻̮̣̺g̲͈͙̭͙̬͎ ̰t͔̦h̞̲e̢̤ ͍̬̲͖f̴̘͕̣è͖ẹ̥̩l͖͔͚i͓͚̦͠n͖͍̗͓̳̮g͍ ̨o͚̪͡f̘̣̬ ̖̘͖̟͙̮c҉͔̫͖͓͇͖ͅh̵̤̣͚͔á̗̼͕ͅo̼̣̥s̱͈̺̖̦̻͢.̛̖̞̠̫̰ +̗̺͖̹̯͓Ṯ̤͍̥͇͈h̲́e͏͓̼̗̙̼̣͔ ͇̜̱̠͓͍ͅN͕͠e̗̱z̘̝̜̺͙p̤̺̹͍̯͚e̠̻̠͜r̨̤͍̺̖͔̖̖d̠̟̭̬̝͟i̦͖̩͓͔̤a̠̗̬͉̙n͚͜ ̻̞̰͚ͅh̵͉i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟ +̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕ +Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮ + +# Unicode Upsidedown +# +# Strings which contain unicode with an "upsidedown" effect (via http://www.upsidedowntext.com) + +˙ɐnbᴉlɐ ɐuƃɐɯ ǝɹolop ʇǝ ǝɹoqɐl ʇn ʇunpᴉpᴉɔuᴉ ɹodɯǝʇ poɯsnᴉǝ op pǝs 'ʇᴉlǝ ƃuᴉɔsᴉdᴉpɐ ɹnʇǝʇɔǝsuoɔ 'ʇǝɯɐ ʇᴉs ɹolop ɯnsdᴉ ɯǝɹo˥ +00˙Ɩ$- + +# Unicode font +# +# Strings which contain bold/italic/etc. versions of normal characters + +The quick brown fox jumps over the lazy dog +𝐓𝐡𝐞 𝐪𝐮𝐢𝐜𝐤 𝐛𝐫𝐨𝐰𝐧 𝐟𝐨𝐱 𝐣𝐮𝐦𝐩𝐬 𝐨𝐯𝐞𝐫 𝐭𝐡𝐞 𝐥𝐚𝐳𝐲 𝐝𝐨𝐠 +𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌 +𝑻𝒉𝒆 𝒒𝒖𝒊𝒄𝒌 𝒃𝒓𝒐𝒘𝒏 𝒇𝒐𝒙 𝒋𝒖𝒎𝒑𝒔 𝒐𝒗𝒆𝒓 𝒕𝒉𝒆 𝒍𝒂𝒛𝒚 𝒅𝒐𝒈 +𝓣𝓱𝓮 𝓺𝓾𝓲𝓬𝓴 𝓫𝓻𝓸𝔀𝓷 𝓯𝓸𝔁 𝓳𝓾𝓶𝓹𝓼 𝓸𝓿𝓮𝓻 𝓽𝓱𝓮 𝓵𝓪𝔃𝔂 𝓭𝓸𝓰 +𝕋𝕙𝕖 𝕢𝕦𝕚𝕔𝕜 𝕓𝕣𝕠𝕨𝕟 𝕗𝕠𝕩 𝕛𝕦𝕞𝕡𝕤 𝕠𝕧𝕖𝕣 𝕥𝕙𝕖 𝕝𝕒𝕫𝕪 𝕕𝕠𝕘 +𝚃𝚑𝚎 𝚚𝚞𝚒𝚌𝚔 𝚋𝚛𝚘𝚠𝚗 𝚏𝚘𝚡 𝚓𝚞𝚖𝚙𝚜 𝚘𝚟𝚎𝚛 𝚝𝚑𝚎 𝚕𝚊𝚣𝚢 𝚍𝚘𝚐 +⒯⒣⒠ ⒬⒰⒤⒞⒦ ⒝⒭⒪⒲⒩ ⒡⒪⒳ ⒥⒰⒨⒫⒮ ⒪⒱⒠⒭ ⒯⒣⒠ ⒧⒜⒵⒴ ⒟⒪⒢ + +# Script Injection +# +# Strings which attempt to invoke a benign script injection; shows vulnerability to XSS + + +<script>alert('123');</script> + + +"> +'> +> + +< / script >< script >alert(123)< / script > + onfocus=JaVaSCript:alert(123) autofocus +" onfocus=JaVaSCript:alert(123) autofocus +' onfocus=JaVaSCript:alert(123) autofocus +<script>alert(123)</script> +ript>alert(123)ript> +--> +";alert(123);t=" +';alert(123);t=' +JavaSCript:alert(123) +;alert(123); +src=JaVaSCript:prompt(132) +">javascript:alert(1); +javascript:alert(1); +javascript:alert(1); +javascript:alert(1); +javascript:alert(1); +javascript:alert(1); +javascript:alert(1); +'\`"><\x3Cscript>javascript:alert(1) +'\`"><\x00script>javascript:alert(1) +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +ABC
DEF +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +test +\`"'> +\`"'> +\`"'> +\`"'> +\`"'> +\`"'> +\`"'> +\`"'> +\`"'> +\`"'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> +"\`'> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +XXX + + + +<a href=http://foo.bar/#x=\`y></a><img alt="\`><img src=x:x onerror=javascript:alert(1)></a>"> +<!--[if]><script>javascript:alert(1)</script --> +<!--[if<img src=x onerror=javascript:alert(1)//]> --> +<script src="/\%(jscript)s"></script> +<script src="\\%(jscript)s"></script> +<IMG """><SCRIPT>alert("XSS")</SCRIPT>"> +<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))> +<IMG SRC=# onmouseover="alert('xxs')"> +<IMG SRC= onmouseover="alert('xxs')"> +<IMG onmouseover="alert('xxs')"> +<IMG SRC=javascript:alert('XSS')> +<IMG SRC=javascript:alert('XSS')> +<IMG SRC=javascript:alert('XSS')> +<IMG SRC="jav ascript:alert('XSS');"> +<IMG SRC="jav ascript:alert('XSS');"> +<IMG SRC="jav ascript:alert('XSS');"> +<IMG SRC="jav ascript:alert('XSS');"> +perl -e 'print "<IMG SRC=java\0script:alert(\"XSS\")>";' > out +<IMG SRC="  javascript:alert('XSS');"> +<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT> +<BODY onload!#$%&()*~+-_.,:;?@[/|\]^\`=alert("XSS")> +<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT> +<<SCRIPT>alert("XSS");//<</SCRIPT> +<SCRIPT SRC=http://ha.ckers.org/xss.js?< B > +<SCRIPT SRC=//ha.ckers.org/.j> +<IMG SRC="javascript:alert('XSS')" +<iframe src=http://ha.ckers.org/scriptlet.html < +\";alert('XSS');// +<u oncopy=alert()> Copy me</u> +<i onwheel=alert(1)> Scroll over me </i> +<plaintext> +http://a/%%30%30 +</textarea><script>alert(123)</script> + +# SQL Injection +# +# Strings which can cause a SQL injection if inputs are not sanitized + +1;DROP TABLE users +1'; DROP TABLE users-- 1 +' OR 1=1 -- 1 +' OR '1'='1 + +% +_ + +# Server Code Injection +# +# Strings which can cause user to run code on server as a privileged user (c.f. https://news.ycombinator.com/item?id=7665153) + +- +-- +--version +--help +$USER +/dev/null; touch /tmp/blns.fail ; echo +\`touch /tmp/blns.fail\` +$(touch /tmp/blns.fail) +@{[system "touch /tmp/blns.fail"]} + +# Command Injection (Ruby) +# +# Strings which can call system commands within Ruby/Rails applications + +eval("puts 'hello world'") +System("ls -al /") +\`ls -al /\` +Kernel.exec("ls -al /") +Kernel.exit(1) +%x('ls -al /') + +# XXE Injection (XML) +# +# String which can reveal system files when parsed by a badly configured XML parser + +<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo> + +# Unwanted Interpolation +# +# Strings which can be accidentally expanded into different strings if evaluated in the wrong context, e.g. used as a printf format string or via Perl or shell eval. Might expose sensitive data from the program doing the interpolation, or might just represent the wrong string. + +$HOME +$ENV{'HOME'} +%d +%s%s%s%s%s +{0} +%*.*s +%@ +%n +File:/// + +# File Inclusion +# +# Strings which can cause user to pull in files that should not be a part of a web server + +../../../../../../../../../../../etc/passwd%00 +../../../../../../../../../../../etc/hosts + +# Known CVEs and Vulnerabilities +# +# Strings that test for known vulnerabilities + +() { 0; }; touch /tmp/blns.shellshock1.fail; +() { _; } >_[$($())] { touch /tmp/blns.shellshock2.fail; } +<<< %s(un='%s') = %u ++++ATH0 + +# MSDOS/Windows Special Filenames +# +# Strings which are reserved characters in MSDOS/Windows + +CON +PRN +AUX +CLOCK$ +NUL +A: +ZZ: +COM1 +LPT1 +LPT2 +LPT3 +COM2 +COM3 +COM4 + +# IRC specific strings +# +# Strings that may occur on IRC clients that make security products freak out + +DCC SEND STARTKEYLOGGER 0 0 0 + +# Scunthorpe Problem +# +# Innocuous strings which may be blocked by profanity filters (https://en.wikipedia.org/wiki/Scunthorpe_problem) + +Scunthorpe General Hospital +Penistone Community Church +Lightwater Country Park +Jimmy Clitheroe +Horniman Museum +shitake mushrooms +RomansInSussex.co.uk +http://www.cum.qc.ca/ +Craig Cockburn, Software Specialist +Linda Callahan +Dr. Herman I. Libshitz +magna cum laude +Super Bowl XXX +medieval erection of parapets +evaluate +mocha +expression +Arsenal canal +classic +Tyson Gay +Dick Van Dyke +basement + +# Human injection +# +# Strings which may cause human to reinterpret worldview + +If you're reading this, you've been in a coma for almost 20 years now. We're trying a new technique. We don't know where this message will end up in your dream, but we hope it works. Please wake up, we miss you. + +# Terminal escape codes +# +# Strings which punish the fools who use cat/type on this file + +Roses are red, violets are blue. Hope you enjoy terminal hue +But now...for my greatest trick... +The quick brown fox... [Beeeep] + +# iOS Vulnerabilities +# +# Strings which crashed iMessage in various versions of iOS + +Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗 +🏳0🌈️ +జ్ఞ‌ా + +# Persian special characters +# +# This is a four characters string which includes Persian special characters (گچپژ) + +گچپژ` diff --git a/code/extensions/js-debug/testWorkspace/hashTestCases/index.html b/code/extensions/js-debug/testWorkspace/hashTestCases/index.html new file mode 100644 index 000000000000..69d92fa33689 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/hashTestCases/index.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document + + + + + + + + + diff --git a/code/extensions/js-debug/testWorkspace/hashTestCases/simple.js b/code/extensions/js-debug/testWorkspace/hashTestCases/simple.js new file mode 100644 index 000000000000..78f6787f0373 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/hashTestCases/simple.js @@ -0,0 +1 @@ +function add(a, b) { return a + b } diff --git a/code/extensions/js-debug/testWorkspace/hashTestCases/utf16be.js b/code/extensions/js-debug/testWorkspace/hashTestCases/utf16be.js new file mode 100644 index 000000000000..d401c022a69e Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/hashTestCases/utf16be.js differ diff --git a/code/extensions/js-debug/testWorkspace/hashTestCases/utf16le.js b/code/extensions/js-debug/testWorkspace/hashTestCases/utf16le.js new file mode 100644 index 000000000000..d8b6527c4704 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/hashTestCases/utf16le.js differ diff --git a/code/extensions/js-debug/testWorkspace/hashTestCases/utf8-bom.js b/code/extensions/js-debug/testWorkspace/hashTestCases/utf8-bom.js new file mode 100644 index 000000000000..d0a1bc8192cd --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/hashTestCases/utf8-bom.js @@ -0,0 +1 @@ +function add(a, b) { return a + b } diff --git a/code/extensions/js-debug/testWorkspace/moduleWrapper/customWrapper.js b/code/extensions/js-debug/testWorkspace/moduleWrapper/customWrapper.js new file mode 100644 index 000000000000..e98d3e5437ac --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/moduleWrapper/customWrapper.js @@ -0,0 +1,12 @@ +const fs = require('fs'); +const extension = '.js'; +const previous = require.extensions[extension]; + +require.extensions[extension] = (module, fname) => { + const contents = fs.readFileSync(fname, 'utf8'); + const wrapped = `(function (myCustomWrapper) { ${contents}\n});`; + module._compile(wrapped, fname); +}; + +require('./test'); +debugger; // make sure it runs long enough for us to get the event :P diff --git a/code/extensions/js-debug/testWorkspace/moduleWrapper/index.js b/code/extensions/js-debug/testWorkspace/moduleWrapper/index.js new file mode 100644 index 000000000000..f7768ec5ef7f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/moduleWrapper/index.js @@ -0,0 +1,16 @@ +// Hook into Node's require to add the module wrapper code. Normally we don't +// see this in the debugger, but some environments like Electron seem to +// include it, so this lets us have a test case for it. +// https://nodejs.org/api/modules.html#modules_the_module_wrapper +const fs = require('fs'); +const extension = '.js'; +const previous = require.extensions[extension]; + +require.extensions[extension] = (module, fname) => { + const contents = fs.readFileSync(fname, 'utf8'); + const wrapped = `(function (exports, require, module, __filename, __dirname) { ${contents}\n});`; + module._compile(wrapped, fname); +}; + +require('./test'); +debugger; // make sure it runs long enough for us to get the event :P diff --git a/code/extensions/js-debug/testWorkspace/moduleWrapper/test.js b/code/extensions/js-debug/testWorkspace/moduleWrapper/test.js new file mode 100644 index 000000000000..b9d3e23cbff7 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/moduleWrapper/test.js @@ -0,0 +1 @@ +console.log('Hello world!'); diff --git a/code/extensions/js-debug/testWorkspace/nestedAbsRoot/index.js b/code/extensions/js-debug/testWorkspace/nestedAbsRoot/index.js new file mode 100644 index 000000000000..b0f31d546185 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedAbsRoot/index.js @@ -0,0 +1,29 @@ +const compiled = + `"use strict"; + +console.log('hello world'); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,` + + Buffer.from( + JSON.stringify({ + version: 3, + sources: ['test.js'], + names: ['console', 'log'], + mappings: ';;AAAAA,OAAO,CAACC,GAAR,CAAY,aAAZ', + sourceRoot: __dirname, + sourcesContent: ["console.log('hello world');\n"], + }), + ).toString('base64'); + +const fs = require('fs'); +const path = require('path'); +const extension = '.js'; +const previous = require.extensions[extension]; + +require.extensions[extension] = (module, fname) => { + module._compile( + fname === path.join(__dirname, 'test.js') ? compiled : fs.readFileSync(fname, 'utf8'), + fname, + ); +}; + +require('./test'); diff --git a/code/extensions/js-debug/testWorkspace/nestedAbsRoot/test.js b/code/extensions/js-debug/testWorkspace/nestedAbsRoot/test.js new file mode 100644 index 000000000000..6be02374db11 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedAbsRoot/test.js @@ -0,0 +1 @@ +console.log('hello world'); diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/.gitignore b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/.gitignore new file mode 100644 index 000000000000..d5f19d89b308 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/main.bundle.js b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/main.bundle.js new file mode 100644 index 000000000000..b51b5c1b6756 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/main.bundle.js @@ -0,0 +1,139 @@ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./b/lib.bundle.js": +/*!*************************!*\ + !*** ./b/lib.bundle.js ***! + \*************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "add": () => (/* binding */ __webpack_exports__add) +/* harmony export */ }); +/******/ // The require scope +/******/ var __nested_webpack_require_43__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nested_webpack_require_43__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nested_webpack_require_43__.o(definition, key) && !__nested_webpack_require_43__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nested_webpack_require_43__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __nested_webpack_require_43__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +/*!******************!*\ + !*** ./b/lib.js ***! + \******************/ +__nested_webpack_require_43__.r(__webpack_exports__); +/* harmony export */ __nested_webpack_require_43__.d(__webpack_exports__, { +/* harmony export */ "add": () => (/* binding */ add) +/* harmony export */ }); +function add(a, b) { + return a + b; +} + +var __webpack_exports__add = __webpack_exports__.add; + + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGliLmJ1bmRsZS5qcyIsIm1hcHBpbmdzIjoiU0FBQTtTQUNBOzs7OztVQ0RBO1VBQ0E7VUFDQTtVQUNBO1VBQ0EseUNBQXlDLHdDQUF3QztVQUNqRjtVQUNBO1VBQ0E7Ozs7O1VDUEE7Ozs7O1VDQUE7VUFDQTtVQUNBO1VBQ0EsdURBQXVELGlCQUFpQjtVQUN4RTtVQUNBLGdEQUFnRCxhQUFhO1VBQzdEOzs7Ozs7Ozs7Ozs7QUNOTztBQUNQO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9kZWZpbmUgcHJvcGVydHkgZ2V0dGVycyIsIndlYnBhY2s6Ly8vd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCIsIndlYnBhY2s6Ly8vd2VicGFjay9ydW50aW1lL21ha2UgbmFtZXNwYWNlIG9iamVjdCIsIndlYnBhY2s6Ly8vLi9iL2xpYi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBUaGUgcmVxdWlyZSBzY29wZVxudmFyIF9fd2VicGFja19yZXF1aXJlX18gPSB7fTtcblxuIiwiLy8gZGVmaW5lIGdldHRlciBmdW5jdGlvbnMgZm9yIGhhcm1vbnkgZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5kID0gKGV4cG9ydHMsIGRlZmluaXRpb24pID0+IHtcblx0Zm9yKHZhciBrZXkgaW4gZGVmaW5pdGlvbikge1xuXHRcdGlmKF9fd2VicGFja19yZXF1aXJlX18ubyhkZWZpbml0aW9uLCBrZXkpICYmICFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywga2V5KSkge1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIGtleSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGRlZmluaXRpb25ba2V5XSB9KTtcblx0XHR9XG5cdH1cbn07IiwiX193ZWJwYWNrX3JlcXVpcmVfXy5vID0gKG9iaiwgcHJvcCkgPT4gKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApKSIsIi8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uciA9IChleHBvcnRzKSA9PiB7XG5cdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuXHR9XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG59OyIsImV4cG9ydCBmdW5jdGlvbiBhZGQoYSwgYikge1xuICByZXR1cm4gYSArIGI7XG59XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0= + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +/*!*******************!*\ + !*** ./a/main.js ***! + \*******************/ +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _b_lib_bundle_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../b/lib.bundle.js */ "./b/lib.bundle.js"); + + +console.log('addition', _b_lib_bundle_js__WEBPACK_IMPORTED_MODULE_0__.add(1, 2)); + +})(); + +/******/ })() +; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5idW5kbGUuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUNBLGFBQWEsNkJBQW1CO0FBQ2hDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVLDZCQUFtQjtBQUM3QjtBQUNBLGVBQWUsNkJBQW1CLHdCQUF3Qiw2QkFBbUI7QUFDN0UsbURBQW1ELHdDQUF3QztBQUMzRjtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0EsVUFBVSw2QkFBbUI7QUFDN0IsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVSw2QkFBbUI7QUFDN0I7QUFDQSxpRUFBaUUsaUJBQWlCO0FBQ2xGO0FBQ0EsMERBQTBELGFBQWE7QUFDdkU7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsNkJBQW1CO0FBQ25CLHFCQUFxQiw2QkFBbUI7QUFDeEM7QUFDQSxzQkFBc0I7QUFDdEI7QUFDQTtBQUNBOztBQUVBO0FBQ3lDOztBQUV6QywyQ0FBMkMsY0FBYzs7Ozs7O1VDaER6RDtVQUNBOztVQUVBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBOztVQUVBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBOzs7OztXQ3RCQTtXQUNBO1dBQ0E7V0FDQTtXQUNBLHlDQUF5Qyx3Q0FBd0M7V0FDakY7V0FDQTtXQUNBOzs7OztXQ1BBOzs7OztXQ0FBO1dBQ0E7V0FDQTtXQUNBLHVEQUF1RCxpQkFBaUI7V0FDeEU7V0FDQSxnREFBZ0QsYUFBYTtXQUM3RDs7Ozs7Ozs7Ozs7O0FDTjBDOztBQUUxQyx3QkFBd0IsaURBQU8iLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9iL2xpYi5idW5kbGUuanMiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svYm9vdHN0cmFwIiwid2VicGFjazovLy93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9oYXNPd25Qcm9wZXJ0eSBzaG9ydGhhbmQiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9tYWtlIG5hbWVzcGFjZSBvYmplY3QiLCJ3ZWJwYWNrOi8vLy4vYS9tYWluLmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKiovIC8vIFRoZSByZXF1aXJlIHNjb3BlXG4vKioqKioqLyB2YXIgX193ZWJwYWNrX3JlcXVpcmVfXyA9IHt9O1xuLyoqKioqKi8gXG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2RlZmluZSBwcm9wZXJ0eSBnZXR0ZXJzICovXG4vKioqKioqLyAoKCkgPT4ge1xuLyoqKioqKi8gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9ucyBmb3IgaGFybW9ueSBleHBvcnRzXG4vKioqKioqLyBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IChleHBvcnRzLCBkZWZpbml0aW9uKSA9PiB7XG4vKioqKioqLyBcdFx0Zm9yKHZhciBrZXkgaW4gZGVmaW5pdGlvbikge1xuLyoqKioqKi8gXHRcdFx0aWYoX193ZWJwYWNrX3JlcXVpcmVfXy5vKGRlZmluaXRpb24sIGtleSkgJiYgIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBrZXkpKSB7XG4vKioqKioqLyBcdFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBkZWZpbml0aW9uW2tleV0gfSk7XG4vKioqKioqLyBcdFx0XHR9XG4vKioqKioqLyBcdFx0fVxuLyoqKioqKi8gXHR9O1xuLyoqKioqKi8gfSkoKTtcbi8qKioqKiovIFxuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCAqL1xuLyoqKioqKi8gKCgpID0+IHtcbi8qKioqKiovIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gKG9iaiwgcHJvcCkgPT4gKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApKVxuLyoqKioqKi8gfSkoKTtcbi8qKioqKiovIFxuLyoqKioqKi8gLyogd2VicGFjay9ydW50aW1lL21ha2UgbmFtZXNwYWNlIG9iamVjdCAqL1xuLyoqKioqKi8gKCgpID0+IHtcbi8qKioqKiovIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSAoZXhwb3J0cykgPT4ge1xuLyoqKioqKi8gXHRcdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuLyoqKioqKi8gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4vKioqKioqLyBcdFx0fVxuLyoqKioqKi8gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4vKioqKioqLyBcdH07XG4vKioqKioqLyB9KSgpO1xuLyoqKioqKi8gXG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xudmFyIF9fd2VicGFja19leHBvcnRzX18gPSB7fTtcbi8qISoqKioqKioqKioqKioqKioqKiEqXFxcbiAgISoqKiAuL2IvbGliLmpzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKiovXG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIoX193ZWJwYWNrX2V4cG9ydHNfXyk7XG4vKiBoYXJtb255IGV4cG9ydCAqLyBfX3dlYnBhY2tfcmVxdWlyZV9fLmQoX193ZWJwYWNrX2V4cG9ydHNfXywge1xuLyogaGFybW9ueSBleHBvcnQgKi8gICBcImFkZFwiOiAoKSA9PiAoLyogYmluZGluZyAqLyBhZGQpXG4vKiBoYXJtb255IGV4cG9ydCAqLyB9KTtcbmZ1bmN0aW9uIGFkZChhLCBiKSB7XG4gIHJldHVybiBhICsgYjtcbn1cblxudmFyIF9fd2VicGFja19leHBvcnRzX19hZGQgPSBfX3dlYnBhY2tfZXhwb3J0c19fLmFkZDtcbmV4cG9ydCB7IF9fd2VicGFja19leHBvcnRzX19hZGQgYXMgYWRkIH07XG5cbi8vIyBzb3VyY2VNYXBwaW5nVVJMPWRhdGE6YXBwbGljYXRpb24vanNvbjtjaGFyc2V0PXV0Zi04O2Jhc2U2NCxleUoyWlhKemFXOXVJam96TENKbWFXeGxJam9pYkdsaUxtSjFibVJzWlM1cWN5SXNJbTFoY0hCcGJtZHpJam9pVTBGQlFUdFRRVU5CT3pzN096dFZRMFJCTzFWQlEwRTdWVUZEUVR0VlFVTkJPMVZCUTBFc2VVTkJRWGxETEhkRFFVRjNRenRWUVVOcVJqdFZRVU5CTzFWQlEwRTdPenM3TzFWRFVFRTdPenM3TzFWRFFVRTdWVUZEUVR0VlFVTkJPMVZCUTBFc2RVUkJRWFZFTEdsQ1FVRnBRanRWUVVONFJUdFZRVU5CTEdkRVFVRm5SQ3hoUVVGaE8xVkJRemRFT3pzN096czdPenM3T3pzN1FVTk9UenRCUVVOUU8wRkJRMEVpTENKemIzVnlZMlZ6SWpwYkluZGxZbkJoWTJzNkx5OHZkMlZpY0dGamF5OWliMjkwYzNSeVlYQWlMQ0ozWldKd1lXTnJPaTh2TDNkbFluQmhZMnN2Y25WdWRHbHRaUzlrWldacGJtVWdjSEp2Y0dWeWRIa2daMlYwZEdWeWN5SXNJbmRsWW5CaFkyczZMeTh2ZDJWaWNHRmpheTl5ZFc1MGFXMWxMMmhoYzA5M2JsQnliM0JsY25SNUlITm9iM0owYUdGdVpDSXNJbmRsWW5CaFkyczZMeTh2ZDJWaWNHRmpheTl5ZFc1MGFXMWxMMjFoYTJVZ2JtRnRaWE53WVdObElHOWlhbVZqZENJc0luZGxZbkJoWTJzNkx5OHZMaTlpTDJ4cFlpNXFjeUpkTENKemIzVnlZMlZ6UTI5dWRHVnVkQ0k2V3lJdkx5QlVhR1VnY21WeGRXbHlaU0J6WTI5d1pWeHVkbUZ5SUY5ZmQyVmljR0ZqYTE5eVpYRjFhWEpsWDE4Z1BTQjdmVHRjYmx4dUlpd2lMeThnWkdWbWFXNWxJR2RsZEhSbGNpQm1kVzVqZEdsdmJuTWdabTl5SUdoaGNtMXZibmtnWlhod2IzSjBjMXh1WDE5M1pXSndZV05yWDNKbGNYVnBjbVZmWHk1a0lEMGdLR1Y0Y0c5eWRITXNJR1JsWm1sdWFYUnBiMjRwSUQwK0lIdGNibHgwWm05eUtIWmhjaUJyWlhrZ2FXNGdaR1ZtYVc1cGRHbHZiaWtnZTF4dVhIUmNkR2xtS0Y5ZmQyVmljR0ZqYTE5eVpYRjFhWEpsWDE4dWJ5aGtaV1pwYm1sMGFXOXVMQ0JyWlhrcElDWW1JQ0ZmWDNkbFluQmhZMnRmY21WeGRXbHlaVjlmTG04b1pYaHdiM0owY3l3Z2EyVjVLU2tnZTF4dVhIUmNkRngwVDJKcVpXTjBMbVJsWm1sdVpWQnliM0JsY25SNUtHVjRjRzl5ZEhNc0lHdGxlU3dnZXlCbGJuVnRaWEpoWW14bE9pQjBjblZsTENCblpYUTZJR1JsWm1sdWFYUnBiMjViYTJWNVhTQjlLVHRjYmx4MFhIUjlYRzVjZEgxY2JuMDdJaXdpWDE5M1pXSndZV05yWDNKbGNYVnBjbVZmWHk1dklEMGdLRzlpYWl3Z2NISnZjQ2tnUFQ0Z0tFOWlhbVZqZEM1d2NtOTBiM1I1Y0dVdWFHRnpUM2R1VUhKdmNHVnlkSGt1WTJGc2JDaHZZbW9zSUhCeWIzQXBLU0lzSWk4dklHUmxabWx1WlNCZlgyVnpUVzlrZFd4bElHOXVJR1Y0Y0c5eWRITmNibDlmZDJWaWNHRmphMTl5WlhGMWFYSmxYMTh1Y2lBOUlDaGxlSEJ2Y25SektTQTlQaUI3WEc1Y2RHbG1LSFI1Y0dWdlppQlRlVzFpYjJ3Z0lUMDlJQ2QxYm1SbFptbHVaV1FuSUNZbUlGTjViV0p2YkM1MGIxTjBjbWx1WjFSaFp5a2dlMXh1WEhSY2RFOWlhbVZqZEM1a1pXWnBibVZRY205d1pYSjBlU2hsZUhCdmNuUnpMQ0JUZVcxaWIyd3VkRzlUZEhKcGJtZFVZV2NzSUhzZ2RtRnNkV1U2SUNkTmIyUjFiR1VuSUgwcE8xeHVYSFI5WEc1Y2RFOWlhbVZqZEM1a1pXWnBibVZRY205d1pYSjBlU2hsZUhCdmNuUnpMQ0FuWDE5bGMwMXZaSFZzWlNjc0lIc2dkbUZzZFdVNklIUnlkV1VnZlNrN1hHNTlPeUlzSW1WNGNHOXlkQ0JtZFc1amRHbHZiaUJoWkdRb1lTd2dZaWtnZTF4dUlDQnlaWFIxY200Z1lTQXJJR0k3WEc1OVhHNGlYU3dpYm1GdFpYTWlPbHRkTENKemIzVnlZMlZTYjI5MElqb2lJbjA9IiwiLy8gVGhlIG1vZHVsZSBjYWNoZVxudmFyIF9fd2VicGFja19tb2R1bGVfY2FjaGVfXyA9IHt9O1xuXG4vLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcblx0dmFyIGNhY2hlZE1vZHVsZSA9IF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF07XG5cdGlmIChjYWNoZWRNb2R1bGUgIT09IHVuZGVmaW5lZCkge1xuXHRcdHJldHVybiBjYWNoZWRNb2R1bGUuZXhwb3J0cztcblx0fVxuXHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuXHR2YXIgbW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXSA9IHtcblx0XHQvLyBubyBtb2R1bGUuaWQgbmVlZGVkXG5cdFx0Ly8gbm8gbW9kdWxlLmxvYWRlZCBuZWVkZWRcblx0XHRleHBvcnRzOiB7fVxuXHR9O1xuXG5cdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuXHRfX3dlYnBhY2tfbW9kdWxlc19fW21vZHVsZUlkXShtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuXHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuXHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG59XG5cbiIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IChleHBvcnRzLCBkZWZpbml0aW9uKSA9PiB7XG5cdGZvcih2YXIga2V5IGluIGRlZmluaXRpb24pIHtcblx0XHRpZihfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZGVmaW5pdGlvbiwga2V5KSAmJiAhX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIGtleSkpIHtcblx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBkZWZpbml0aW9uW2tleV0gfSk7XG5cdFx0fVxuXHR9XG59OyIsIl9fd2VicGFja19yZXF1aXJlX18ubyA9IChvYmosIHByb3ApID0+IChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSkiLCIvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSAoZXhwb3J0cykgPT4ge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCJpbXBvcnQgKiBhcyBsaWIgZnJvbSAnLi4vYi9saWIuYnVuZGxlLmpzJztcblxuY29uc29sZS5sb2coJ2FkZGl0aW9uJywgbGliLmFkZCgxLCAyKSk7XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/main.js b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/main.js new file mode 100644 index 000000000000..649ea63a070f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/main.js @@ -0,0 +1,3 @@ +import * as lib from '../b/lib.bundle.js'; + +console.log('addition', lib.add(1, 2)); diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/webpack.config.js b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/webpack.config.js new file mode 100644 index 000000000000..8ff82b658918 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/a/webpack.config.js @@ -0,0 +1,11 @@ +const path = require('path'); + +module.exports = { + entry: path.join(__dirname, 'main.js'), + mode: 'development', + devtool: 'inline-source-map', + output: { + path: __dirname, + filename: 'main.bundle.js', + }, +}; diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/lib.bundle.js b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/lib.bundle.js new file mode 100644 index 000000000000..076214a27c3e --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/lib.bundle.js @@ -0,0 +1,49 @@ +/******/ // The require scope +/******/ var __webpack_require__ = {}; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +/*!******************!*\ + !*** ./b/lib.js ***! + \******************/ +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "add": () => (/* binding */ add) +/* harmony export */ }); +function add(a, b) { + return a + b; +} + +var __webpack_exports__add = __webpack_exports__.add; +export { __webpack_exports__add as add }; + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGliLmJ1bmRsZS5qcyIsIm1hcHBpbmdzIjoiU0FBQTtTQUNBOzs7OztVQ0RBO1VBQ0E7VUFDQTtVQUNBO1VBQ0EseUNBQXlDLHdDQUF3QztVQUNqRjtVQUNBO1VBQ0E7Ozs7O1VDUEE7Ozs7O1VDQUE7VUFDQTtVQUNBO1VBQ0EsdURBQXVELGlCQUFpQjtVQUN4RTtVQUNBLGdEQUFnRCxhQUFhO1VBQzdEOzs7Ozs7Ozs7Ozs7QUNOTztBQUNQO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9kZWZpbmUgcHJvcGVydHkgZ2V0dGVycyIsIndlYnBhY2s6Ly8vd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCIsIndlYnBhY2s6Ly8vd2VicGFjay9ydW50aW1lL21ha2UgbmFtZXNwYWNlIG9iamVjdCIsIndlYnBhY2s6Ly8vLi9iL2xpYi5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBUaGUgcmVxdWlyZSBzY29wZVxudmFyIF9fd2VicGFja19yZXF1aXJlX18gPSB7fTtcblxuIiwiLy8gZGVmaW5lIGdldHRlciBmdW5jdGlvbnMgZm9yIGhhcm1vbnkgZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5kID0gKGV4cG9ydHMsIGRlZmluaXRpb24pID0+IHtcblx0Zm9yKHZhciBrZXkgaW4gZGVmaW5pdGlvbikge1xuXHRcdGlmKF9fd2VicGFja19yZXF1aXJlX18ubyhkZWZpbml0aW9uLCBrZXkpICYmICFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywga2V5KSkge1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIGtleSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGRlZmluaXRpb25ba2V5XSB9KTtcblx0XHR9XG5cdH1cbn07IiwiX193ZWJwYWNrX3JlcXVpcmVfXy5vID0gKG9iaiwgcHJvcCkgPT4gKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApKSIsIi8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uciA9IChleHBvcnRzKSA9PiB7XG5cdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuXHR9XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG59OyIsImV4cG9ydCBmdW5jdGlvbiBhZGQoYSwgYikge1xuICByZXR1cm4gYSArIGI7XG59XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/lib.js b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/lib.js new file mode 100644 index 000000000000..7d658310b0d9 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/lib.js @@ -0,0 +1,3 @@ +export function add(a, b) { + return a + b; +} diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/webpack.config.js b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/webpack.config.js new file mode 100644 index 000000000000..8bb3716ab5aa --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/b/webpack.config.js @@ -0,0 +1,15 @@ +const path = require('path'); + +module.exports = { + entry: path.join(__dirname, 'lib.js'), + mode: 'development', + devtool: 'inline-source-map', + output: { + path: __dirname, + filename: 'lib.bundle.js', + library: { type: 'module' }, + }, + experiments: { + outputModule: true, + }, +}; diff --git a/code/extensions/js-debug/testWorkspace/nestedSourceMaps/package.json b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/package.json new file mode 100644 index 000000000000..8011bc2d4470 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nestedSourceMaps/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "build": "webpack -c b/webpack.config.js && webpack -c a/webpack.config.js" + }, + "dependencies": { + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" + } +} diff --git a/code/extensions/js-debug/testWorkspace/nodeModuleBreakpoint/index.js b/code/extensions/js-debug/testWorkspace/nodeModuleBreakpoint/index.js new file mode 100644 index 000000000000..c8f2a17d9980 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nodeModuleBreakpoint/index.js @@ -0,0 +1,3 @@ +const { double } = require(process.env.MODULE); + +console.log(double(21)); diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/Makefile b/code/extensions/js-debug/testWorkspace/nodePathProvider/Makefile new file mode 100644 index 000000000000..64142d32a493 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nodePathProvider/Makefile @@ -0,0 +1,17 @@ +FLAG_OUTDATED = -ldflags "-s -w -X main.version=v6.0.0" +FLAG_CURRENT = -ldflags "-s -w -X main.version=v12.0.0" +SRC = program.go +OUT = outdated/node.exe up-to-date/node.exe + +all: $(OUT) + +clean: + rm -f $(OUT) + +outdated/node.exe: $(SRC) + GOOS=windows GOARCH=amd64 go build $(FLAG_OUTDATED) -o $@ ./program.go + +up-to-date/node.exe: $(SRC) + GOOS=windows GOARCH=amd64 go build $(FLAG_CURRENT) -o $@ ./program.go + +.PHONY: all clean diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/no-node/babel.cmd b/code/extensions/js-debug/testWorkspace/nodePathProvider/no-node/babel.cmd new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/no-node/npm b/code/extensions/js-debug/testWorkspace/nodePathProvider/no-node/npm new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/no-node/npm.exe b/code/extensions/js-debug/testWorkspace/nodePathProvider/no-node/npm.exe new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/node-module/package.json b/code/extensions/js-debug/testWorkspace/nodePathProvider/node-module/package.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nodePathProvider/node-module/package.json @@ -0,0 +1 @@ +{} diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/node b/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/node new file mode 100755 index 000000000000..1325d3aed372 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/node @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "v6.0.0" diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/node.exe b/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/node.exe new file mode 100755 index 000000000000..8beb3fed1000 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/node.exe differ diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/npm b/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/npm new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/npm.exe b/code/extensions/js-debug/testWorkspace/nodePathProvider/outdated/npm.exe new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/program.go b/code/extensions/js-debug/testWorkspace/nodePathProvider/program.go new file mode 100644 index 000000000000..77d02a19706f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nodePathProvider/program.go @@ -0,0 +1,9 @@ +package main + +import "fmt" + +var version string + +func main() { + fmt.Println(version) +} diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/node b/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/node new file mode 100755 index 000000000000..25b4e89f7c5e --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/node @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "v12.0.0" diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/node.exe b/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/node.exe new file mode 100755 index 000000000000..3bb958e203c8 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/node.exe differ diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/npm b/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/npm new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/npm.exe b/code/extensions/js-debug/testWorkspace/nodePathProvider/up-to-date/npm.exe new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/debuggerStmt.js b/code/extensions/js-debug/testWorkspace/simpleNode/debuggerStmt.js new file mode 100644 index 000000000000..0b24bde36a2a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/debuggerStmt.js @@ -0,0 +1,2 @@ +require('inspector'); // make sure require is available +debugger; diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/index.js b/code/extensions/js-debug/testWorkspace/simpleNode/index.js new file mode 100644 index 000000000000..cc58b496f3be --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/index.js @@ -0,0 +1,2 @@ +console.log('hello world'); +console.log('goodbye world'); diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/logNodeOptions.js b/code/extensions/js-debug/testWorkspace/simpleNode/logNodeOptions.js new file mode 100644 index 000000000000..9566fc6c086a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/logNodeOptions.js @@ -0,0 +1,2 @@ +console.log('NODE_OPTIONS=', process.env.NODE_OPTIONS); +debugger; diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/package-lock.json b/code/extensions/js-debug/testWorkspace/simpleNode/package-lock.json new file mode 100644 index 000000000000..0b03b6e60e3d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "simpleNode", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/package.json b/code/extensions/js-debug/testWorkspace/simpleNode/package.json new file mode 100644 index 000000000000..38e129749d92 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/package.json @@ -0,0 +1,6 @@ +{ + "scripts": { + "startWithBrk": "node --inspect-brk=29204 logNodeOptions.js", + "startWithoutBrk": "node logNodeOptions.js" + } +} diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/profilePlayground.js b/code/extensions/js-debug/testWorkspace/simpleNode/profilePlayground.js new file mode 100644 index 000000000000..3b0d6b41b8cd --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/profilePlayground.js @@ -0,0 +1,24 @@ +const crypto = require('crypto'); +const micromatch = require('micromatch'); + +function doBusyWork() { + let n = 0; + for (let i = 0; i < 10; i++) { + const input = crypto.randomBytes(8).toString('hex'); + for (let i = 0; i < 200; i++) { + n += micromatch([input], [`${i}*`]).length; + } + } + + return n; +} + +const noop = () => { + console.log('do nothing'); +}; + +setInterval(() => { + const start = Date.now(); + const busyStuff = doBusyWork(); + console.log('hello'); +}, 100); diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.js b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.js new file mode 100644 index 000000000000..176fddb4d863 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.js @@ -0,0 +1,102 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./index.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./index.ts": +/*!******************!*\ + !*** ./index.ts ***! + \******************/ +/*! no static exports found */ +/***/ (function(module, exports) { + + debugger; + + + /***/ }) + + /******/ }); + //# sourceMappingURL=simpleWebpack.js.map diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.js.map b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.js.map new file mode 100644 index 000000000000..fee380a4fbd6 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./simpleWebpack.ts"],"names":[],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;AClFA,QAAQ,CAAC","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./index.ts\");\n","debugger;\r\n"],"sourceRoot":""} diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.ts b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.ts new file mode 100644 index 000000000000..eab74692130a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpack.ts @@ -0,0 +1 @@ +debugger; diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.js b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.js new file mode 100644 index 000000000000..2a9f6fd9004c --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.js @@ -0,0 +1,102 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./index.ts"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./index.ts": +/*!******************!*\ + !*** ./index.ts ***! + \******************/ +/*! no static exports found */ +/***/ (function(module, exports) { + + debugger; + + + /***/ }) + + /******/ }); + //# sourceMappingURL=simpleWebpackWithQuery.js.map diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.js.map b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.js.map new file mode 100644 index 000000000000..28aa83508305 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./simpleWebpackWithQuery.ts?potato"],"names":[],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;AClFA,QAAQ,CAAC","file":"index.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./index.ts\");\n","debugger;\r\n"],"sourceRoot":""} diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.ts b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.ts new file mode 100644 index 000000000000..eab74692130a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/simpleWebpackWithQuery.ts @@ -0,0 +1 @@ +debugger; diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/skipFiles.js b/code/extensions/js-debug/testWorkspace/simpleNode/skipFiles.js new file mode 100644 index 000000000000..07f9950143e8 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/skipFiles.js @@ -0,0 +1,17 @@ +const skipped = require('./skippedScript'); + +const fns = { + ...skipped, + caughtInUserCode: () => { + try { + skipped.uncaught(); + } catch (e) { + // ignored + } + }, +}; + +setTimeout(() => { + fns[process.argv[3]](); + process.exit(0); +}, process.argv[2]); diff --git a/code/extensions/js-debug/testWorkspace/simpleNode/skippedScript.js b/code/extensions/js-debug/testWorkspace/simpleNode/skippedScript.js new file mode 100644 index 000000000000..99ae9440681b --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/simpleNode/skippedScript.js @@ -0,0 +1,19 @@ +exports.uncaught = () => { + throw 'uncaught'; +} + +exports.caught = () => { + try { + throw 'caught'; + } catch (e) { + // ignored + } +} + +exports.rethrown = () => { + try { + throw 'rethrown'; + } catch (e) { + throw e; + } +} diff --git a/code/extensions/js-debug/testWorkspace/sourceMapLocations/babel.js b/code/extensions/js-debug/testWorkspace/sourceMapLocations/babel.js new file mode 100644 index 000000000000..33882de1cf46 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/sourceMapLocations/babel.js @@ -0,0 +1,27 @@ +"use strict"; + +var a = 0; + +function foo() { + console.log(a); + a++; + console.log(a); +} + +foo(); + +//# sourceMappingURL=babel.js.map + +/* Original via `babel test.ts --source-maps --plugins @babel/plugin-transform-typescript --presets @babel/preset-env`: + +let a = 0; + +function foo() { + console.log(a); + a++; + console.log(a); +} + +foo(); + +*/ diff --git a/code/extensions/js-debug/testWorkspace/sourceMapLocations/babel.js.map b/code/extensions/js-debug/testWorkspace/sourceMapLocations/babel.js.map new file mode 100644 index 000000000000..255a80e70ab8 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/sourceMapLocations/babel.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["test.ts"],"names":[],"mappings":";;AAAA,IAAI,CAAC,GAAG,CAAR;;AAEA,SAAS,GAAT,GAAe;AACb,EAAA,OAAO,CAAC,GAAR,CAAY,CAAZ;AACA,EAAA,CAAC;AACD,EAAA,OAAO,CAAC,GAAR,CAAY,CAAZ;AACD;;AAED,GAAG","file":"stdout","sourcesContent":["let a = 0;\n\nfunction foo() {\n console.log(a);\n a++;\n console.log(a);\n}\n\nfoo();\n"]} diff --git a/code/extensions/js-debug/testWorkspace/sourceMapLocations/tsc.js b/code/extensions/js-debug/testWorkspace/sourceMapLocations/tsc.js new file mode 100644 index 000000000000..add574b6be3c --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/sourceMapLocations/tsc.js @@ -0,0 +1,23 @@ +var a = 0; +function foo() { + console.log(a); + a++; + console.log(a); +} +foo(); +//# sourceMappingURL=tsc.js.map + + +/* Original via `tsc test.ts --sourceMap`: + +let a = 0; + +function foo() { + console.log(a); + a++; + console.log(a); +} + +foo(); + +*/ diff --git a/code/extensions/js-debug/testWorkspace/sourceMapLocations/tsc.js.map b/code/extensions/js-debug/testWorkspace/sourceMapLocations/tsc.js.map new file mode 100644 index 000000000000..72cf27ae0b8d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/sourceMapLocations/tsc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"test.js","sourceRoot":"","sources":["test.ts"],"names":[],"mappings":"AAAA,IAAI,CAAC,GAAG,CAAC,CAAC;AAEV,SAAS,GAAG;IACV,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACf,CAAC,EAAE,CAAC;IACJ,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,GAAG,EAAE,CAAC"} diff --git a/code/extensions/js-debug/testWorkspace/sourceQueryString/input.ts b/code/extensions/js-debug/testWorkspace/sourceQueryString/input.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/sourceQueryString/output.js b/code/extensions/js-debug/testWorkspace/sourceQueryString/output.js new file mode 100644 index 000000000000..efea760d58d4 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/sourceQueryString/output.js @@ -0,0 +1,3 @@ +"use strict"; +debugger; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5wdXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbnB1dC50cz9oZWxsb3dvcmxkIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxRQUFRLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJkZWJ1Z2dlcjtcbiJdfQ== diff --git a/code/extensions/js-debug/testWorkspace/tsNode/double.ts b/code/extensions/js-debug/testWorkspace/tsNode/double.ts new file mode 100644 index 000000000000..a65a1910b560 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNode/double.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +console.assert(true); // some statement since you cannot set a breakpoint at a fn declaration + +export function triple(n: number) { + return n * 3; +} + +export interface ISomeStuffToMakeLinesNotMatch { + some: true; + properties: false; + here: string; +} + +export function double(n: number) { + return n * 2; // this line is a different # in the compiled source +} diff --git a/code/extensions/js-debug/testWorkspace/tsNode/index.js b/code/extensions/js-debug/testWorkspace/tsNode/index.js new file mode 100644 index 000000000000..7ae680bca09d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNode/index.js @@ -0,0 +1,11 @@ +const tsn = require('ts-node'); + +tsn.register({ transpileModule: true }); + +const { double, triple } = require('./double.ts'); + +console.log(triple(3)); +console.log(double(21)); + +require('./matching-line.ts'); +require('./log.ts'); diff --git a/code/extensions/js-debug/testWorkspace/tsNode/log.ts b/code/extensions/js-debug/testWorkspace/tsNode/log.ts new file mode 100644 index 000000000000..6fb1f5306f4b --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNode/log.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line header/header +console.log('hi'); diff --git a/code/extensions/js-debug/testWorkspace/tsNode/matching-line.ts b/code/extensions/js-debug/testWorkspace/tsNode/matching-line.ts new file mode 100644 index 000000000000..24c815588d79 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNode/matching-line.ts @@ -0,0 +1,4 @@ +'use strict'; +console.log('a'); +console.log('b'); +console.log('c'); diff --git a/code/extensions/js-debug/testWorkspace/tsNode/withAbsRoot.js b/code/extensions/js-debug/testWorkspace/tsNode/withAbsRoot.js new file mode 100644 index 000000000000..f5b4003f41db --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNode/withAbsRoot.js @@ -0,0 +1,10 @@ +const tsn = require('ts-node'); + +tsn.register({ + transpileModule: true, + compilerOptions: { sourceRoot: __dirname.replace(/\\/g, '/') }, +}); + +const { double } = require('./double.ts'); + +console.log(double(21)); diff --git a/code/extensions/js-debug/testWorkspace/tsNodeApp/app.js b/code/extensions/js-debug/testWorkspace/tsNodeApp/app.js new file mode 100644 index 000000000000..d069dca998a6 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNodeApp/app.js @@ -0,0 +1,10 @@ +"use strict"; +console.log("Line 1"); +console.log("Line 2"); +console.log("Line 3"); +console.log("Line 4"); +console.log("Line 5"); +console.log("Line 6"); +console.log("Line 7"); +console.log("Line 8"); +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/tsNodeApp/app.js.map b/code/extensions/js-debug/testWorkspace/tsNodeApp/app.js.map new file mode 100644 index 000000000000..1b9095582b3d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNodeApp/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";AAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC"} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/tsNodeApp/app.ts b/code/extensions/js-debug/testWorkspace/tsNodeApp/app.ts new file mode 100644 index 000000000000..5c77622557e2 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNodeApp/app.ts @@ -0,0 +1,8 @@ +console.log("Line 1"); +console.log("Line 2"); +console.log("Line 3"); +console.log("Line 4"); +console.log("Line 5"); +console.log("Line 6"); +console.log("Line 7"); +console.log("Line 8"); diff --git a/code/extensions/js-debug/testWorkspace/tsNodeApp/tsconfig.json b/code/extensions/js-debug/testWorkspace/tsNodeApp/tsconfig.json new file mode 100644 index 000000000000..e599a9e80bb6 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/tsNodeApp/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "outDir": ".", + "lib": [ + "dom" + ], + "sourceMap": true, + "rootDir": ".", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "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. */ + }, + "files": ["app.ts"] +} + diff --git a/code/extensions/js-debug/testWorkspace/web/addWorker.js b/code/extensions/js-debug/testWorkspace/web/addWorker.js new file mode 100644 index 000000000000..d4ccddeb19c0 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/addWorker.js @@ -0,0 +1 @@ +window.w = new Worker('worker.js'); diff --git a/code/extensions/js-debug/testWorkspace/web/asyncStack.html b/code/extensions/js-debug/testWorkspace/web/asyncStack.html new file mode 100644 index 000000000000..23c7344c7e05 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/asyncStack.html @@ -0,0 +1,5 @@ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/asyncStack.js b/code/extensions/js-debug/testWorkspace/web/asyncStack.js new file mode 100644 index 000000000000..15761fdd0d45 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/asyncStack.js @@ -0,0 +1,7 @@ +setTimeout(() => { + debugger; + + setTimeout(() => { + debugger; + }, 100); +}, 100); diff --git a/code/extensions/js-debug/testWorkspace/web/basic.html b/code/extensions/js-debug/testWorkspace/web/basic.html new file mode 100644 index 000000000000..4de4c1ce0d1b --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/basic.html @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/basic.js b/code/extensions/js-debug/testWorkspace/web/basic.js new file mode 100644 index 000000000000..653ed2eace27 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/basic.js @@ -0,0 +1,20 @@ +"use strict"; +function plusTwo(num) { + return num + 2; +} +function printArr(arr) { + for (const num of arr) { + console.log(plusTwo(num)); + } +} +function abcdef() { + var obj1 = { + a: 1, + b: 2, + c: " " + }; + console.log("hello!"); + printArr([obj1.a, obj1.b]); +} +abcdef(); +//# sourceMappingURL=basic.js.map \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/basic.js.map b/code/extensions/js-debug/testWorkspace/web/basic.js.map new file mode 100644 index 000000000000..1e2d87f66385 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/basic.js.map @@ -0,0 +1 @@ +{"version":3,"file":"basic.js","sourceRoot":"","sources":["basic.ts"],"names":[],"mappings":";AAAA,SAAS,OAAO,CAAC,GAAW;IACxB,OAAO,GAAG,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,GAAa;IAC3B,KAAI,MAAM,GAAG,IAAI,GAAG,EAAE;QAClB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;KAC7B;AACL,CAAC;AAED,SAAS,MAAM;IACX,IAAI,IAAI,GAAG;QACP,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,GAAG;KACT,CAAA;IACD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtB,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,EAAE,CAAC"} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/basic.ts b/code/extensions/js-debug/testWorkspace/web/basic.ts new file mode 100644 index 000000000000..59a49d6f0f0f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/basic.ts @@ -0,0 +1,21 @@ +function plusTwo(num: number) { + return num + 2; +} + +function printArr(arr: number[]) { + for(const num of arr) { + console.log(plusTwo(num)); + } +} + +function abcdef(): void { + var obj1 = { + a: 1, + b: 2, + c: " " + } + console.log("hello!"); + printArr([obj1.a, obj1.b]); +} + +abcdef(); \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/browserify/browserify.html b/code/extensions/js-debug/testWorkspace/web/browserify/browserify.html new file mode 100644 index 000000000000..f856a0a19205 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/browserify/browserify.html @@ -0,0 +1 @@ + diff --git a/code/extensions/js-debug/testWorkspace/web/browserify/bundle.js b/code/extensions/js-debug/testWorkspace/web/browserify/bundle.js new file mode 100644 index 000000000000..c4409c1dc4a9 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/browserify/bundle.js @@ -0,0 +1,45 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i diff --git a/code/extensions/js-debug/testWorkspace/web/browserify/pause.js b/code/extensions/js-debug/testWorkspace/web/browserify/pause.js new file mode 100644 index 000000000000..45b874dd864f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/browserify/pause.js @@ -0,0 +1,39 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;ichild + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/condition.html b/code/extensions/js-debug/testWorkspace/web/condition.html new file mode 100644 index 000000000000..cd13714baaaf --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/condition.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/condition.js b/code/extensions/js-debug/testWorkspace/web/condition.js new file mode 100644 index 000000000000..62058a4b9241 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/condition.js @@ -0,0 +1,5 @@ +for (var i = 0; i < 5; i++) { + console.log('iteration ' + i); +} + +debugger; diff --git a/code/extensions/js-debug/testWorkspace/web/dir/helloworld.js b/code/extensions/js-debug/testWorkspace/web/dir/helloworld.js new file mode 100644 index 000000000000..e9fe0090d638 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dir/helloworld.js @@ -0,0 +1 @@ +console.log('Hello, world!'); diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.c b/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.c new file mode 100644 index 000000000000..6733d610e3f0 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.c @@ -0,0 +1,17 @@ + +// Compile with: +// clang -O0 -g -fdebug-compilation-dir=. --target=wasm32-unknown-unknown -nostdlib c-with-struct.c -o c-with-struct.wasm +typedef struct data_t { + char id[12]; + int x; + int y; +} data_t; + +int process(void *data) { + return 0; // Break here and evaluate (data_t*)data in the repl interface +} + +int _start() { + data_t data = { .id = "Hello world", .x = 12, .y = 34 }; + return process(&data); +} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.html b/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.html new file mode 100644 index 000000000000..e765dd47cc9f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.html @@ -0,0 +1,14 @@ + + + + C with struct + + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.wasm b/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.wasm new file mode 100755 index 000000000000..93ca842ee59a Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/web/dwarf/c-with-struct.wasm differ diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining-extern.c b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining-extern.c new file mode 100644 index 000000000000..be1d098ee29a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining-extern.c @@ -0,0 +1,7 @@ +#define INLINE __attribute__((always_inline)) +#include "diverse-inlining.h" + +int bar(int x) { + return foo(x); +} + diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining-main.c b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining-main.c new file mode 100644 index 000000000000..fdd7df82fe35 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining-main.c @@ -0,0 +1,10 @@ +#define INLINE __attribute__((noinline)) +#include "diverse-inlining.h" + +extern int bar(int); + +int main(int argc, char** argv) { + argc = foo(argc); + argc = bar(argc); + return argc; +} diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.h b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.h new file mode 100644 index 000000000000..98df27de49c3 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.h @@ -0,0 +1,5 @@ +INLINE static int foo(int x) { + x = x + 1; + return x; +} + diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.html b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.html new file mode 100644 index 000000000000..e422d97728cf --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.html @@ -0,0 +1,1294 @@ + + + + + + Emscripten-Generated Code + + + + + image/svg+xml + + +
+
Downloading...
+ + + Resize canvas + Lock/hide mouse pointer     + + + + +
+ +
+ + +
+ +
+ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.js b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.js new file mode 100644 index 000000000000..dbffeaa50f5d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.js @@ -0,0 +1,1740 @@ +// include: shell.js +// The Module object: Our interface to the outside world. We import +// and export values on it. There are various ways Module can be used: +// 1. Not defined. We create it here +// 2. A function parameter, function(Module) { ..generated code.. } +// 3. pre-run appended it, var Module = {}; ..generated code.. +// 4. External script tag defines var Module. +// We need to check if Module already exists (e.g. case 3 above). +// Substitution will be replaced with actual code on later stage of the build, +// this way Closure Compiler will not mangle it (e.g. case 4. above). +// Note that if you want to run closure, and also to use Module +// after the generated code, you will need to define var Module = {}; +// before the code. Then that object will be used in the code, and you +// can continue to use Module afterwards as well. +var Module = typeof Module != 'undefined' ? Module : {}; + +// --pre-jses are emitted after the Module integration code, so that they can +// refer to Module (if they choose; they can also define Module) + + +// Sometimes an existing Module object exists with properties +// meant to overwrite the default module functionality. Here +// we collect those properties and reapply _after_ we configure +// the current environment's defaults to avoid having to be so +// defensive during initialization. +var moduleOverrides = Object.assign({}, Module); + +var arguments_ = []; +var thisProgram = './this.program'; +var quit_ = (status, toThrow) => { + throw toThrow; +}; + +// Determine the runtime environment we are in. You can customize this by +// setting the ENVIRONMENT setting at compile time (see settings.js). + +// Attempt to auto-detect the environment +var ENVIRONMENT_IS_WEB = typeof window == 'object'; +var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function'; +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string'; +var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + +if (Module['ENVIRONMENT']) { + throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); +} + +// `/` should be present at the end if `scriptDirectory` is not empty +var scriptDirectory = ''; +function locateFile(path) { + if (Module['locateFile']) { + return Module['locateFile'](path, scriptDirectory); + } + return scriptDirectory + path; +} + +// Hooks that are implemented differently in different runtime environments. +var read_, + readAsync, + readBinary, + setWindowTitle; + +if (ENVIRONMENT_IS_NODE) { + if (typeof process == 'undefined' || !process.release || process.release.name !== 'node') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + var nodeVersion = process.versions.node; + var numericVersion = nodeVersion.split('.').slice(0, 3); + numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1); + var minVersion = 160000; + if (numericVersion < 160000) { + throw new Error('This emscripten-generated code requires node v16.0.0 (detected v' + nodeVersion + ')'); + } + + // `require()` is no-op in an ESM module, use `createRequire()` to construct + // the require()` function. This is only necessary for multi-environment + // builds, `-sENVIRONMENT=node` emits a static import declaration instead. + // TODO: Swap all `require()`'s with `import()`'s? + // These modules will usually be used on Node.js. Load them eagerly to avoid + // the complexity of lazy-loading. + var fs = require('fs'); + var nodePath = require('path'); + + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = nodePath.dirname(scriptDirectory) + '/'; + } else { + scriptDirectory = __dirname + '/'; + } + +// include: node_shell_read.js +read_ = (filename, binary) => { + // We need to re-wrap `file://` strings to URLs. Normalizing isn't + // necessary in that case, the path should already be absolute. + filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); + return fs.readFileSync(filename, binary ? undefined : 'utf8'); +}; + +readBinary = (filename) => { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; +}; + +readAsync = (filename, onload, onerror, binary = true) => { + // See the comment in the `read_` function. + filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); + fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => { + if (err) onerror(err); + else onload(binary ? data.buffer : data); + }); +}; +// end include: node_shell_read.js + if (!Module['thisProgram'] && process.argv.length > 1) { + thisProgram = process.argv[1].replace(/\\/g, '/'); + } + + arguments_ = process.argv.slice(2); + + if (typeof module != 'undefined') { + module['exports'] = Module; + } + + process.on('uncaughtException', (ex) => { + // suppress ExitStatus exceptions from showing an error + if (ex !== 'unwind' && !(ex instanceof ExitStatus) && !(ex.context instanceof ExitStatus)) { + throw ex; + } + }); + + quit_ = (status, toThrow) => { + process.exitCode = status; + throw toThrow; + }; + + Module['inspect'] = () => '[Emscripten Module object]'; + +} else +if (ENVIRONMENT_IS_SHELL) { + + if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + if (typeof read != 'undefined') { + read_ = read; + } + + readBinary = (f) => { + if (typeof readbuffer == 'function') { + return new Uint8Array(readbuffer(f)); + } + let data = read(f, 'binary'); + assert(typeof data == 'object'); + return data; + }; + + readAsync = (f, onload, onerror) => { + setTimeout(() => onload(readBinary(f))); + }; + + if (typeof clearTimeout == 'undefined') { + globalThis.clearTimeout = (id) => {}; + } + + if (typeof setTimeout == 'undefined') { + // spidermonkey lacks setTimeout but we use it above in readAsync. + globalThis.setTimeout = (f) => (typeof f == 'function') ? f() : abort(); + } + + if (typeof scriptArgs != 'undefined') { + arguments_ = scriptArgs; + } else if (typeof arguments != 'undefined') { + arguments_ = arguments; + } + + if (typeof quit == 'function') { + quit_ = (status, toThrow) => { + // Unlike node which has process.exitCode, d8 has no such mechanism. So we + // have no way to set the exit code and then let the program exit with + // that code when it naturally stops running (say, when all setTimeouts + // have completed). For that reason, we must call `quit` - the only way to + // set the exit code - but quit also halts immediately. To increase + // consistency with node (and the web) we schedule the actual quit call + // using a setTimeout to give the current stack and any exception handlers + // a chance to run. This enables features such as addOnPostRun (which + // expected to be able to run code after main returns). + setTimeout(() => { + if (!(toThrow instanceof ExitStatus)) { + let toLog = toThrow; + if (toThrow && typeof toThrow == 'object' && toThrow.stack) { + toLog = [toThrow, toThrow.stack]; + } + err(`exiting due to exception: ${toLog}`); + } + quit(status); + }); + throw toThrow; + }; + } + + if (typeof print != 'undefined') { + // Prefer to use print/printErr where they exist, as they usually work better. + if (typeof console == 'undefined') console = /** @type{!Console} */({}); + console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); + console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr != 'undefined' ? printErr : print); + } + +} else + +// Note that this includes Node.js workers when relevant (pthreads is enabled). +// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and +// ENVIRONMENT_IS_NODE. +if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled + scriptDirectory = self.location.href; + } else if (typeof document != 'undefined' && document.currentScript) { // web + scriptDirectory = document.currentScript.src; + } + // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. + // otherwise, slice off the final part of the url to find the script directory. + // if scriptDirectory does not contain a slash, lastIndexOf will return -1, + // and scriptDirectory will correctly be replaced with an empty string. + // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), + // they are removed because they could contain a slash. + if (scriptDirectory.indexOf('blob:') !== 0) { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf('/')+1); + } else { + scriptDirectory = ''; + } + + if (!(typeof window == 'object' || typeof importScripts == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + // Differentiate the Web Worker from the Node Worker case, as reading must + // be done differently. + { +// include: web_or_worker_shell_read.js +read_ = (url) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.send(null); + return xhr.responseText; + } + + if (ENVIRONMENT_IS_WORKER) { + readBinary = (url) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response)); + }; + } + + readAsync = (url, onload, onerror) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = () => { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + onload(xhr.response); + return; + } + onerror(); + }; + xhr.onerror = onerror; + xhr.send(null); + } + +// end include: web_or_worker_shell_read.js + } + + setWindowTitle = (title) => document.title = title; +} else +{ + throw new Error('environment detection error'); +} + +var out = Module['print'] || console.log.bind(console); +var err = Module['printErr'] || console.error.bind(console); + +// Merge back in the overrides +Object.assign(Module, moduleOverrides); +// Free the object hierarchy contained in the overrides, this lets the GC +// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. +moduleOverrides = null; +checkIncomingModuleAPI(); + +// Emit code to handle expected values on the Module object. This applies Module.x +// to the proper local x. This has two benefits: first, we only emit it if it is +// expected to arrive, and second, by using a local everywhere else that can be +// minified. + +if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('arguments', 'arguments_'); + +if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram'); + +if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_'); + +// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message +// Assertions on removed incoming Module JS APIs. +assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)'); +assert(typeof Module['readAsync'] == 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); +assert(typeof Module['readBinary'] == 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); +assert(typeof Module['setWindowTitle'] == 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)'); +assert(typeof Module['TOTAL_MEMORY'] == 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); +legacyModuleProp('asm', 'wasmExports'); +legacyModuleProp('read', 'read_'); +legacyModuleProp('readAsync', 'readAsync'); +legacyModuleProp('readBinary', 'readBinary'); +legacyModuleProp('setWindowTitle', 'setWindowTitle'); +var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js'; +var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js'; +var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js'; +var FETCHFS = 'FETCHFS is no longer included by default; build with -lfetchfs.js'; +var ICASEFS = 'ICASEFS is no longer included by default; build with -licasefs.js'; +var JSFILEFS = 'JSFILEFS is no longer included by default; build with -ljsfilefs.js'; +var OPFS = 'OPFS is no longer included by default; build with -lopfs.js'; + +var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; + +assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at build time. Add 'shell' to `-sENVIRONMENT` to enable."); + + +// end include: shell.js +// include: preamble.js +// === Preamble library stuff === + +// Documentation for the public APIs defined in this file must be updated in: +// site/source/docs/api_reference/preamble.js.rst +// A prebuilt local version of the documentation is available at: +// site/build/text/docs/api_reference/preamble.js.txt +// You can also build docs locally as HTML or other formats in site/ +// An online HTML version (which may be of a different version of Emscripten) +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html + +var wasmBinary; +if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary'); +var noExitRuntime = Module['noExitRuntime'] || true;legacyModuleProp('noExitRuntime', 'noExitRuntime'); + +if (typeof WebAssembly != 'object') { + abort('no native wasm support detected'); +} + +// Wasm globals + +var wasmMemory; + +//======================================== +// Runtime essentials +//======================================== + +// whether we are quitting the application. no code should run after this. +// set in exit() and abort() +var ABORT = false; + +// set by exit() and abort(). Passed to 'onExit' handler. +// NOTE: This is also used as the process return code code in shell environments +// but only when noExitRuntime is false. +var EXITSTATUS; + +/** @type {function(*, string=)} */ +function assert(condition, text) { + if (!condition) { + abort('Assertion failed' + (text ? ': ' + text : '')); + } +} + +// We used to include malloc/free by default in the past. Show a helpful error in +// builds with assertions. +function _malloc() { + abort("malloc() called but not included in the build - add '_malloc' to EXPORTED_FUNCTIONS"); +} +function _free() { + // Show a helpful error since we used to include free by default in the past. + abort("free() called but not included in the build - add '_free' to EXPORTED_FUNCTIONS"); +} + +// Memory management + +var HEAP, +/** @type {!Int8Array} */ + HEAP8, +/** @type {!Uint8Array} */ + HEAPU8, +/** @type {!Int16Array} */ + HEAP16, +/** @type {!Uint16Array} */ + HEAPU16, +/** @type {!Int32Array} */ + HEAP32, +/** @type {!Uint32Array} */ + HEAPU32, +/** @type {!Float32Array} */ + HEAPF32, +/** @type {!Float64Array} */ + HEAPF64; + +function updateMemoryViews() { + var b = wasmMemory.buffer; + Module['HEAP8'] = HEAP8 = new Int8Array(b); + Module['HEAP16'] = HEAP16 = new Int16Array(b); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(b); + Module['HEAPU16'] = HEAPU16 = new Uint16Array(b); + Module['HEAP32'] = HEAP32 = new Int32Array(b); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(b); + Module['HEAPF32'] = HEAPF32 = new Float32Array(b); + Module['HEAPF64'] = HEAPF64 = new Float64Array(b); +} + +assert(!Module['STACK_SIZE'], 'STACK_SIZE can no longer be set at runtime. Use -sSTACK_SIZE at link time') + +assert(typeof Int32Array != 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray != undefined && Int32Array.prototype.set != undefined, + 'JS engine does not provide full typed array support'); + +// If memory is defined in wasm, the user can't provide it, or set INITIAL_MEMORY +assert(!Module['wasmMemory'], 'Use of `wasmMemory` detected. Use -sIMPORTED_MEMORY to define wasmMemory externally'); +assert(!Module['INITIAL_MEMORY'], 'Detected runtime INITIAL_MEMORY setting. Use -sIMPORTED_MEMORY to define wasmMemory dynamically'); + +// include: runtime_init_table.js +// In regular non-RELOCATABLE mode the table is exported +// from the wasm module and this will be assigned once +// the exports are available. +var wasmTable; +// end include: runtime_init_table.js +// include: runtime_stack_check.js +// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. +function writeStackCookie() { + var max = _emscripten_stack_get_end(); + assert((max & 3) == 0); + // If the stack ends at address zero we write our cookies 4 bytes into the + // stack. This prevents interference with SAFE_HEAP and ASAN which also + // monitor writes to address zero. + if (max == 0) { + max += 4; + } + // The stack grow downwards towards _emscripten_stack_get_end. + // We write cookies to the final two words in the stack and detect if they are + // ever overwritten. + HEAPU32[((max)>>2)] = 0x02135467; + HEAPU32[(((max)+(4))>>2)] = 0x89BACDFE; + // Also test the global address 0 for integrity. + HEAPU32[((0)>>2)] = 1668509029; +} + +function checkStackCookie() { + if (ABORT) return; + var max = _emscripten_stack_get_end(); + // See writeStackCookie(). + if (max == 0) { + max += 4; + } + var cookie1 = HEAPU32[((max)>>2)]; + var cookie2 = HEAPU32[(((max)+(4))>>2)]; + if (cookie1 != 0x02135467 || cookie2 != 0x89BACDFE) { + abort(`Stack overflow! Stack cookie has been overwritten at ${ptrToString(max)}, expected hex dwords 0x89BACDFE and 0x2135467, but received ${ptrToString(cookie2)} ${ptrToString(cookie1)}`); + } + // Also test the global address 0 for integrity. + if (HEAPU32[((0)>>2)] != 0x63736d65 /* 'emsc' */) { + abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); + } +} +// end include: runtime_stack_check.js +// include: runtime_assertions.js +// Endianness check +(function() { + var h16 = new Int16Array(1); + var h8 = new Int8Array(h16.buffer); + h16[0] = 0x6373; + if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'; +})(); + +// end include: runtime_assertions.js +var __ATPRERUN__ = []; // functions called before the runtime is initialized +var __ATINIT__ = []; // functions called during startup +var __ATMAIN__ = []; // functions called when main() is to be run +var __ATEXIT__ = []; // functions called during shutdown +var __ATPOSTRUN__ = []; // functions called after the main() is called + +var runtimeInitialized = false; + +var runtimeKeepaliveCounter = 0; + +function keepRuntimeAlive() { + return noExitRuntime || runtimeKeepaliveCounter > 0; +} + +function preRun() { + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); +} + +function initRuntime() { + assert(!runtimeInitialized); + runtimeInitialized = true; + + checkStackCookie(); + + + callRuntimeCallbacks(__ATINIT__); +} + +function preMain() { + checkStackCookie(); + + callRuntimeCallbacks(__ATMAIN__); +} + +function postRun() { + checkStackCookie(); + + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } + + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} + +function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); +} + +function addOnExit(cb) { +} + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} + +// include: runtime_math.js +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc + +assert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +// end include: runtime_math.js +// A counter of dependencies for calling run(). If we need to +// do asynchronous work before running, increment this and +// decrement it. Incrementing must happen in a place like +// Module.preRun (used by emcc to add file preloading). +// Note that you can add dependencies in preRun, even though +// it happens right before run - run will be postponed until +// the dependencies are met. +var runDependencies = 0; +var runDependencyWatcher = null; +var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled +var runDependencyTracking = {}; + +function getUniqueRunDependency(id) { + var orig = id; + while (1) { + if (!runDependencyTracking[id]) return id; + id = orig + Math.random(); + } +} + +function addRunDependency(id) { + runDependencies++; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + + if (id) { + assert(!runDependencyTracking[id]); + runDependencyTracking[id] = 1; + if (runDependencyWatcher === null && typeof setInterval != 'undefined') { + // Check for missing dependencies every few seconds + runDependencyWatcher = setInterval(() => { + if (ABORT) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + return; + } + var shown = false; + for (var dep in runDependencyTracking) { + if (!shown) { + shown = true; + err('still waiting on run dependencies:'); + } + err(`dependency: ${dep}`); + } + if (shown) { + err('(end of list)'); + } + }, 10000); + } + } else { + err('warning: run dependency added without ID'); + } +} + +function removeRunDependency(id) { + runDependencies--; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + + if (id) { + assert(runDependencyTracking[id]); + delete runDependencyTracking[id]; + } else { + err('warning: run dependency removed without ID'); + } + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } +} + +/** @param {string|number=} what */ +function abort(what) { + if (Module['onAbort']) { + Module['onAbort'](what); + } + + what = 'Aborted(' + what + ')'; + // TODO(sbc): Should we remove printing and leave it up to whoever + // catches the exception? + err(what); + + ABORT = true; + EXITSTATUS = 1; + + // Use a wasm runtime error, because a JS error might be seen as a foreign + // exception, which means we'd run destructors on it. We need the error to + // simply make the program stop. + // FIXME This approach does not work in Wasm EH because it currently does not assume + // all RuntimeErrors are from traps; it decides whether a RuntimeError is from + // a trap or not based on a hidden field within the object. So at the moment + // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that + // allows this in the wasm spec. + + // Suppress closure compiler warning here. Closure compiler's builtin extern + // defintion for WebAssembly.RuntimeError claims it takes no arguments even + // though it can. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. + /** @suppress {checkTypes} */ + var e = new WebAssembly.RuntimeError(what); + + // Throw the error whether or not MODULARIZE is set because abort is used + // in code paths apart from instantiation where an exception is expected + // to be thrown when abort is called. + throw e; +} + +// include: memoryprofiler.js +// end include: memoryprofiler.js +// show errors on likely calls to FS when it was not included +var FS = { + error() { + abort('Filesystem support (FS) was not included. The problem is that you are using files from JS, but files were not used from C/C++, so filesystem support was not auto-included. You can force-include filesystem support with -sFORCE_FILESYSTEM'); + }, + init() { FS.error() }, + createDataFile() { FS.error() }, + createPreloadedFile() { FS.error() }, + createLazyFile() { FS.error() }, + open() { FS.error() }, + mkdev() { FS.error() }, + registerDevice() { FS.error() }, + analyzePath() { FS.error() }, + + ErrnoError() { FS.error() }, +}; +Module['FS_createDataFile'] = FS.createDataFile; +Module['FS_createPreloadedFile'] = FS.createPreloadedFile; + +// include: URIUtils.js +// Prefix of data URIs emitted by SINGLE_FILE and related options. +var dataURIPrefix = 'data:application/octet-stream;base64,'; + +// Indicates whether filename is a base64 data URI. +function isDataURI(filename) { + // Prefix of data URIs emitted by SINGLE_FILE and related options. + return filename.startsWith(dataURIPrefix); +} + +// Indicates whether filename is delivered via file protocol (as opposed to http/https) +function isFileURI(filename) { + return filename.startsWith('file://'); +} +// end include: URIUtils.js +function createExportWrapper(name) { + return function() { + assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`); + var f = wasmExports[name]; + assert(f, `exported native function \`${name}\` not found`); + return f.apply(null, arguments); + }; +} + +// include: runtime_exceptions.js +// end include: runtime_exceptions.js +var wasmBinaryFile; + wasmBinaryFile = 'diverse-inlining.wasm'; + if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile); + } + +function getBinarySync(file) { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + if (readBinary) { + return readBinary(file); + } + throw "both async and sync fetching of the wasm failed"; +} + +function getBinaryPromise(binaryFile) { + // If we don't have the binary yet, try to load it asynchronously. + // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. + // See https://github.com/github/fetch/pull/92#issuecomment-140665932 + // Cordova or Electron apps are typically loaded from a file:// url. + // So use fetch if it is available and the url is not a file, otherwise fall back to XHR. + if (!wasmBinary + && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { + if (typeof fetch == 'function' + && !isFileURI(binaryFile) + ) { + return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => { + if (!response['ok']) { + throw "failed to load wasm binary file at '" + binaryFile + "'"; + } + return response['arrayBuffer'](); + }).catch(() => getBinarySync(binaryFile)); + } + else if (readAsync) { + // fetch is not available or url is file => try XHR (readAsync uses XHR internally) + return new Promise((resolve, reject) => { + readAsync(binaryFile, (response) => resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))), reject) + }); + } + } + + // Otherwise, getBinarySync should be able to get it synchronously + return Promise.resolve().then(() => getBinarySync(binaryFile)); +} + +function instantiateArrayBuffer(binaryFile, imports, receiver) { + return getBinaryPromise(binaryFile).then((binary) => { + return WebAssembly.instantiate(binary, imports); + }).then((instance) => { + return instance; + }).then(receiver, (reason) => { + err(`failed to asynchronously prepare wasm: ${reason}`); + + // Warn on some common problems. + if (isFileURI(wasmBinaryFile)) { + err(`warning: Loading from a file URI (${wasmBinaryFile}) is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing`); + } + abort(reason); + }); +} + +function instantiateAsync(binary, binaryFile, imports, callback) { + if (!binary && + typeof WebAssembly.instantiateStreaming == 'function' && + !isDataURI(binaryFile) && + // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. + !isFileURI(binaryFile) && + // Avoid instantiateStreaming() on Node.js environment for now, as while + // Node.js v18.1.0 implements it, it does not have a full fetch() + // implementation yet. + // + // Reference: + // https://github.com/emscripten-core/emscripten/pull/16917 + !ENVIRONMENT_IS_NODE && + typeof fetch == 'function') { + return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => { + // Suppress closure warning here since the upstream definition for + // instantiateStreaming only allows Promise rather than + // an actual Response. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. + /** @suppress {checkTypes} */ + var result = WebAssembly.instantiateStreaming(response, imports); + + return result.then( + callback, + function(reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + err(`wasm streaming compile failed: ${reason}`); + err('falling back to ArrayBuffer instantiation'); + return instantiateArrayBuffer(binaryFile, imports, callback); + }); + }); + } + return instantiateArrayBuffer(binaryFile, imports, callback); +} + +// Create the wasm instance. +// Receives the wasm imports, returns the exports. +function createWasm() { + // prepare imports + var info = { + 'env': wasmImports, + 'wasi_snapshot_preview1': wasmImports, + }; + // Load the wasm module and create an instance of using native support in the JS engine. + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + /** @param {WebAssembly.Module=} module*/ + function receiveInstance(instance, module) { + var exports = instance.exports; + + wasmExports = exports; + + + wasmMemory = wasmExports['memory']; + + assert(wasmMemory, "memory not found in wasm exports"); + // This assertion doesn't hold when emscripten is run in --post-link + // mode. + // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode. + //assert(wasmMemory.buffer.byteLength === 16777216); + updateMemoryViews(); + + wasmTable = wasmExports['__indirect_function_table']; + + assert(wasmTable, "table not found in wasm exports"); + + addOnInit(wasmExports['__wasm_call_ctors']); + + removeRunDependency('wasm-instantiate'); + return exports; + } + // wait for the pthread pool (if any) + addRunDependency('wasm-instantiate'); + + // Prefer streaming instantiation if available. + // Async compilation can be confusing when an error on the page overwrites Module + // (for example, if the order of elements is wrong, and the one defining Module is + // later), so we save Module and check it later. + var trueModule = Module; + function receiveInstantiationResult(result) { + // 'result' is a ResultObject object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called + assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); + trueModule = null; + // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. + // When the regression is fixed, can restore the above PTHREADS-enabled path. + receiveInstance(result['instance']); + } + + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback + // to manually instantiate the Wasm module themselves. This allows pages to + // run the instantiation parallel to any other async startup actions they are + // performing. + // Also pthreads and wasm workers initialize the wasm instance through this + // path. + if (Module['instantiateWasm']) { + + try { + return Module['instantiateWasm'](info, receiveInstance); + } catch(e) { + err(`Module.instantiateWasm callback failed with error: ${e}`); + return false; + } + } + + instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult); + return {}; // no exports yet; we'll fill them in later +} + +// Globals used by JS i64 conversions (see makeSetValue) +var tempDouble; +var tempI64; + +// include: runtime_debug.js +function legacyModuleProp(prop, newName, incomming=true) { + if (!Object.getOwnPropertyDescriptor(Module, prop)) { + Object.defineProperty(Module, prop, { + configurable: true, + get() { + let extra = incomming ? ' (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)' : ''; + abort(`\`Module.${prop}\` has been replaced by \`${newName}\`` + extra); + + } + }); + } +} + +function ignoredModuleProp(prop) { + if (Object.getOwnPropertyDescriptor(Module, prop)) { + abort(`\`Module.${prop}\` was supplied but \`${prop}\` not included in INCOMING_MODULE_JS_API`); + } +} + +// forcing the filesystem exports a few things by default +function isExportedByForceFilesystem(name) { + return name === 'FS_createPath' || + name === 'FS_createDataFile' || + name === 'FS_createPreloadedFile' || + name === 'FS_unlink' || + name === 'addRunDependency' || + // The old FS has some functionality that WasmFS lacks. + name === 'FS_createLazyFile' || + name === 'FS_createDevice' || + name === 'removeRunDependency'; +} + +function missingGlobal(sym, msg) { + if (typeof globalThis !== 'undefined') { + Object.defineProperty(globalThis, sym, { + configurable: true, + get() { + warnOnce('`' + sym + '` is not longer defined by emscripten. ' + msg); + return undefined; + } + }); + } +} + +missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer'); +missingGlobal('asm', 'Please use wasmExports instead'); + +function missingLibrarySymbol(sym) { + if (typeof globalThis !== 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) { + Object.defineProperty(globalThis, sym, { + configurable: true, + get() { + // Can't `abort()` here because it would break code that does runtime + // checks. e.g. `if (typeof SDL === 'undefined')`. + var msg = '`' + sym + '` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line'; + // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in + // library.js, which means $name for a JS name with no prefix, or name + // for a JS name like _name. + var librarySymbol = sym; + if (!librarySymbol.startsWith('_')) { + librarySymbol = '$' + sym; + } + msg += " (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='" + librarySymbol + "')"; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + warnOnce(msg); + return undefined; + } + }); + } + // Any symbol that is not included from the JS libary is also (by definition) + // not exported on the Module object. + unexportedRuntimeSymbol(sym); +} + +function unexportedRuntimeSymbol(sym) { + if (!Object.getOwnPropertyDescriptor(Module, sym)) { + Object.defineProperty(Module, sym, { + configurable: true, + get() { + var msg = "'" + sym + "' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)"; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + abort(msg); + } + }); + } +} + +// Used by XXXXX_DEBUG settings to output debug messages. +function dbg(text) { + // TODO(sbc): Make this configurable somehow. Its not always convenient for + // logging to show up as warnings. + console.warn.apply(console, arguments); +} +// end include: runtime_debug.js +// === Body === + +// end include: preamble.js + + /** @constructor */ + function ExitStatus(status) { + this.name = 'ExitStatus'; + this.message = `Program terminated with exit(${status})`; + this.status = status; + } + + var callRuntimeCallbacks = (callbacks) => { + while (callbacks.length > 0) { + // Pass the module as the first argument. + callbacks.shift()(Module); + } + }; + + + /** + * @param {number} ptr + * @param {string} type + */ + function getValue(ptr, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': return HEAP8[((ptr)>>0)]; + case 'i8': return HEAP8[((ptr)>>0)]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': abort('to do getValue(i64) use WASM_BIGINT'); + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + case '*': return HEAPU32[((ptr)>>2)]; + default: abort(`invalid type for getValue: ${type}`); + } + } + + var ptrToString = (ptr) => { + assert(typeof ptr === 'number'); + // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. + ptr >>>= 0; + return '0x' + ptr.toString(16).padStart(8, '0'); + }; + + + /** + * @param {number} ptr + * @param {number} value + * @param {string} type + */ + function setValue(ptr, value, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': HEAP8[((ptr)>>0)] = value; break; + case 'i8': HEAP8[((ptr)>>0)] = value; break; + case 'i16': HEAP16[((ptr)>>1)] = value; break; + case 'i32': HEAP32[((ptr)>>2)] = value; break; + case 'i64': abort('to do setValue(i64) use WASM_BIGINT'); + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + case '*': HEAPU32[((ptr)>>2)] = value; break; + default: abort(`invalid type for setValue: ${type}`); + } + } + + var warnOnce = (text) => { + if (!warnOnce.shown) warnOnce.shown = {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; + if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text; + err(text); + } + }; + + + var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined; + + /** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number} idx + * @param {number=} maxBytesToRead + * @return {string} + */ + var UTF8ArrayToString = (heapOrArray, idx, maxBytesToRead) => { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. Also, use the length info to avoid running tiny + // strings through TextDecoder, since .subarray() allocates garbage. + // (As a tiny code save trick, compare endPtr against endIdx using a negation, + // so that undefined means Infinity) + while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; + + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); + } + var str = ''; + // If building with TextDecoder, we have already computed the string length + // above, so test loop end condition against that + while (idx < endPtr) { + // For UTF8 byte structure, see: + // http://en.wikipedia.org/wiki/UTF-8#Description + // https://www.ietf.org/rfc/rfc2279.txt + // https://tools.ietf.org/html/rfc3629 + var u0 = heapOrArray[idx++]; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + var u1 = heapOrArray[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + var u2 = heapOrArray[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!'); + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); + } + + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } + return str; + }; + + /** + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index (i.e. maxBytesToRead will not + * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing + * frequent uses of UTF8ToString() with and without maxBytesToRead may throw + * JS JIT optimizations off, so it is worth to consider consistently using one + * @return {string} + */ + var UTF8ToString = (ptr, maxBytesToRead) => { + assert(typeof ptr == 'number'); + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; + }; + var SYSCALLS = { + varargs:undefined, + get() { + assert(SYSCALLS.varargs != undefined); + var ret = HEAP32[((SYSCALLS.varargs)>>2)]; + SYSCALLS.varargs += 4; + return ret; + }, + getp() { return SYSCALLS.get() }, + getStr(ptr) { + var ret = UTF8ToString(ptr); + return ret; + }, + }; + var _proc_exit = (code) => { + EXITSTATUS = code; + if (!keepRuntimeAlive()) { + if (Module['onExit']) Module['onExit'](code); + ABORT = true; + } + quit_(code, new ExitStatus(code)); + }; + /** @param {boolean|number=} implicit */ + var exitJS = (status, implicit) => { + EXITSTATUS = status; + + checkUnflushedContent(); + + // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down + if (keepRuntimeAlive() && !implicit) { + var msg = `program exited (with status: ${status}), but keepRuntimeAlive() is set (counter=${runtimeKeepaliveCounter}) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)`; + err(msg); + } + + _proc_exit(status); + }; + + var handleException = (e) => { + // Certain exception types we do not treat as errors since they are used for + // internal control flow. + // 1. ExitStatus, which is thrown by exit() + // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others + // that wish to return to JS event loop. + if (e instanceof ExitStatus || e == 'unwind') { + return EXITSTATUS; + } + checkStackCookie(); + if (e instanceof WebAssembly.RuntimeError) { + if (_emscripten_stack_get_current() <= 0) { + err('Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to 65536)'); + } + } + quit_(1, e); + }; + + var lengthBytesUTF8 = (str) => { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code + // unit, not a Unicode code point of the character! So decode + // UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var c = str.charCodeAt(i); // possibly a lead surrogate + if (c <= 0x7F) { + len++; + } else if (c <= 0x7FF) { + len += 2; + } else if (c >= 0xD800 && c <= 0xDFFF) { + len += 4; ++i; + } else { + len += 3; + } + } + return len; + }; + + var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { + assert(typeof str === 'string'); + // Parameter maxBytesToWrite is not optional. Negative values, 0, null, + // undefined and false each don't write out any bytes. + if (!(maxBytesToWrite > 0)) + return 0; + + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code + // unit, not a Unicode code point of the character! So decode + // UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description + // and https://www.ietf.org/rfc/rfc2279.txt + // and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) { + var u1 = str.charCodeAt(++i); + u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); + } + if (u <= 0x7F) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 0xC0 | (u >> 6); + heap[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 0xE0 | (u >> 12); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + if (u > 0x10FFFF) warnOnce('Invalid Unicode code point ' + ptrToString(u) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF).'); + heap[outIdx++] = 0xF0 | (u >> 18); + heap[outIdx++] = 0x80 | ((u >> 12) & 63); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } + } + // Null-terminate the pointer to the buffer. + heap[outIdx] = 0; + return outIdx - startIdx; + }; + var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { + assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); + return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); + }; + var stringToUTF8OnStack = (str) => { + var size = lengthBytesUTF8(str) + 1; + var ret = stackAlloc(size); + stringToUTF8(str, ret, size); + return ret; + }; +function checkIncomingModuleAPI() { + ignoredModuleProp('fetchSettings'); +} +var wasmImports = { + +}; +var wasmExports = createWasm(); +var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors'); +var _main = Module['_main'] = createExportWrapper('__main_argc_argv'); +var ___errno_location = createExportWrapper('__errno_location'); +var _fflush = Module['_fflush'] = createExportWrapper('fflush'); +var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])(); +var _emscripten_stack_get_free = () => (_emscripten_stack_get_free = wasmExports['emscripten_stack_get_free'])(); +var _emscripten_stack_get_base = () => (_emscripten_stack_get_base = wasmExports['emscripten_stack_get_base'])(); +var _emscripten_stack_get_end = () => (_emscripten_stack_get_end = wasmExports['emscripten_stack_get_end'])(); +var stackSave = createExportWrapper('stackSave'); +var stackRestore = createExportWrapper('stackRestore'); +var stackAlloc = createExportWrapper('stackAlloc'); +var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports['emscripten_stack_get_current'])(); + + +// include: postamble.js +// === Auto-generated postamble setup entry stuff === + +var missingLibrarySymbols = [ + 'writeI53ToI64', + 'writeI53ToI64Clamped', + 'writeI53ToI64Signaling', + 'writeI53ToU64Clamped', + 'writeI53ToU64Signaling', + 'readI53FromI64', + 'readI53FromU64', + 'convertI32PairToI53', + 'convertI32PairToI53Checked', + 'convertU32PairToI53', + 'zeroMemory', + 'getHeapMax', + 'abortOnCannotGrowMemory', + 'growMemory', + 'isLeapYear', + 'ydayFromDate', + 'arraySum', + 'addDays', + 'setErrNo', + 'inetPton4', + 'inetNtop4', + 'inetPton6', + 'inetNtop6', + 'readSockaddr', + 'writeSockaddr', + 'getHostByName', + 'initRandomFill', + 'randomFill', + 'getCallstack', + 'emscriptenLog', + 'convertPCtoSourceLocation', + 'readEmAsmArgs', + 'jstoi_q', + 'jstoi_s', + 'getExecutableName', + 'listenOnce', + 'autoResumeAudioContext', + 'dynCallLegacy', + 'getDynCaller', + 'dynCall', + 'runtimeKeepalivePush', + 'runtimeKeepalivePop', + 'callUserCallback', + 'maybeExit', + 'safeSetTimeout', + 'asmjsMangle', + 'asyncLoad', + 'alignMemory', + 'mmapAlloc', + 'handleAllocatorInit', + 'HandleAllocator', + 'getNativeTypeSize', + 'STACK_SIZE', + 'STACK_ALIGN', + 'POINTER_SIZE', + 'ASSERTIONS', + 'getCFunc', + 'ccall', + 'cwrap', + 'uleb128Encode', + 'sigToWasmTypes', + 'generateFuncType', + 'convertJsFunctionToWasm', + 'getEmptyTableSlot', + 'updateTableMap', + 'getFunctionAddress', + 'addFunction', + 'removeFunction', + 'reallyNegative', + 'unSign', + 'strLen', + 'reSign', + 'formatString', + 'intArrayFromString', + 'intArrayToString', + 'AsciiToString', + 'stringToAscii', + 'UTF16ToString', + 'stringToUTF16', + 'lengthBytesUTF16', + 'UTF32ToString', + 'stringToUTF32', + 'lengthBytesUTF32', + 'stringToNewUTF8', + 'writeArrayToMemory', + 'registerKeyEventCallback', + 'maybeCStringToJsString', + 'findEventTarget', + 'findCanvasEventTarget', + 'getBoundingClientRect', + 'fillMouseEventData', + 'registerMouseEventCallback', + 'registerWheelEventCallback', + 'registerUiEventCallback', + 'registerFocusEventCallback', + 'fillDeviceOrientationEventData', + 'registerDeviceOrientationEventCallback', + 'fillDeviceMotionEventData', + 'registerDeviceMotionEventCallback', + 'screenOrientation', + 'fillOrientationChangeEventData', + 'registerOrientationChangeEventCallback', + 'fillFullscreenChangeEventData', + 'registerFullscreenChangeEventCallback', + 'JSEvents_requestFullscreen', + 'JSEvents_resizeCanvasForFullscreen', + 'registerRestoreOldStyle', + 'hideEverythingExceptGivenElement', + 'restoreHiddenElements', + 'setLetterbox', + 'softFullscreenResizeWebGLRenderTarget', + 'doRequestFullscreen', + 'fillPointerlockChangeEventData', + 'registerPointerlockChangeEventCallback', + 'registerPointerlockErrorEventCallback', + 'requestPointerLock', + 'fillVisibilityChangeEventData', + 'registerVisibilityChangeEventCallback', + 'registerTouchEventCallback', + 'fillGamepadEventData', + 'registerGamepadEventCallback', + 'registerBeforeUnloadEventCallback', + 'fillBatteryEventData', + 'battery', + 'registerBatteryEventCallback', + 'setCanvasElementSize', + 'getCanvasElementSize', + 'demangle', + 'demangleAll', + 'jsStackTrace', + 'stackTrace', + 'getEnvStrings', + 'checkWasiClock', + 'flush_NO_FILESYSTEM', + 'wasiRightsToMuslOFlags', + 'wasiOFlagsToMuslOFlags', + 'createDyncallWrapper', + 'setImmediateWrapped', + 'clearImmediateWrapped', + 'polyfillSetImmediate', + 'getPromise', + 'makePromise', + 'idsToPromises', + 'makePromiseCallback', + 'ExceptionInfo', + 'findMatchingCatch', + 'setMainLoop', + 'getSocketFromFD', + 'getSocketAddress', + 'FS_createPreloadedFile', + 'FS_modeStringToFlags', + 'FS_getMode', + 'FS_stdin_getChar', + '_setNetworkCallback', + 'heapObjectForWebGLType', + 'heapAccessShiftForWebGLHeap', + 'webgl_enable_ANGLE_instanced_arrays', + 'webgl_enable_OES_vertex_array_object', + 'webgl_enable_WEBGL_draw_buffers', + 'webgl_enable_WEBGL_multi_draw', + 'emscriptenWebGLGet', + 'computeUnpackAlignedImageSize', + 'colorChannelsInGlTextureFormat', + 'emscriptenWebGLGetTexPixelData', + '__glGenObject', + 'emscriptenWebGLGetUniform', + 'webglGetUniformLocation', + 'webglPrepareUniformLocationsBeforeFirstUse', + 'webglGetLeftBracePos', + 'emscriptenWebGLGetVertexAttrib', + '__glGetActiveAttribOrUniform', + 'writeGLArray', + 'registerWebGlEventCallback', + 'runAndAbortIfError', + 'SDL_unicode', + 'SDL_ttfContext', + 'SDL_audio', + 'GLFW_Window', + 'ALLOC_NORMAL', + 'ALLOC_STACK', + 'allocate', + 'writeStringToMemory', + 'writeAsciiToMemory', +]; +missingLibrarySymbols.forEach(missingLibrarySymbol) + +var unexportedSymbols = [ + 'run', + 'addOnPreRun', + 'addOnInit', + 'addOnPreMain', + 'addOnExit', + 'addOnPostRun', + 'addRunDependency', + 'removeRunDependency', + 'FS_createFolder', + 'FS_createPath', + 'FS_createDataFile', + 'FS_createLazyFile', + 'FS_createLink', + 'FS_createDevice', + 'FS_readFile', + 'FS_unlink', + 'out', + 'err', + 'callMain', + 'abort', + 'keepRuntimeAlive', + 'wasmMemory', + 'wasmTable', + 'wasmExports', + 'stackAlloc', + 'stackSave', + 'stackRestore', + 'getTempRet0', + 'setTempRet0', + 'writeStackCookie', + 'checkStackCookie', + 'ptrToString', + 'exitJS', + 'ENV', + 'MONTH_DAYS_REGULAR', + 'MONTH_DAYS_LEAP', + 'MONTH_DAYS_REGULAR_CUMULATIVE', + 'MONTH_DAYS_LEAP_CUMULATIVE', + 'ERRNO_CODES', + 'ERRNO_MESSAGES', + 'DNS', + 'Protocols', + 'Sockets', + 'timers', + 'warnOnce', + 'UNWIND_CACHE', + 'readEmAsmArgsArray', + 'handleException', + 'freeTableIndexes', + 'functionsInTableMap', + 'setValue', + 'getValue', + 'PATH', + 'PATH_FS', + 'UTF8Decoder', + 'UTF8ArrayToString', + 'UTF8ToString', + 'stringToUTF8Array', + 'stringToUTF8', + 'lengthBytesUTF8', + 'UTF16Decoder', + 'stringToUTF8OnStack', + 'JSEvents', + 'specialHTMLTargets', + 'currentFullscreenStrategy', + 'restoreOldWindowedStyle', + 'ExitStatus', + 'promiseMap', + 'uncaughtExceptionCount', + 'exceptionLast', + 'exceptionCaught', + 'Browser', + 'wget', + 'SYSCALLS', + 'preloadPlugins', + 'FS_stdin_getChar_buffer', + 'FS', + 'MEMFS', + 'TTY', + 'PIPEFS', + 'SOCKFS', + 'tempFixedLengthArray', + 'miniTempWebGLFloatBuffers', + 'miniTempWebGLIntBuffers', + 'GL', + 'emscripten_webgl_power_preferences', + 'AL', + 'GLUT', + 'EGL', + 'GLEW', + 'IDBStore', + 'SDL', + 'SDL_gfx', + 'GLFW', + 'allocateUTF8', + 'allocateUTF8OnStack', +]; +unexportedSymbols.forEach(unexportedRuntimeSymbol); + + + +var calledRun; + +dependenciesFulfilled = function runCaller() { + // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled +}; + +function callMain(args = []) { + assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); + assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); + + var entryFunction = _main; + + args.unshift(thisProgram); + + var argc = args.length; + var argv = stackAlloc((argc + 1) * 4); + var argv_ptr = argv; + args.forEach((arg) => { + HEAPU32[((argv_ptr)>>2)] = stringToUTF8OnStack(arg); + argv_ptr += 4; + }); + HEAPU32[((argv_ptr)>>2)] = 0; + + try { + + var ret = entryFunction(argc, argv); + + // if we're not running an evented main loop, it's time to exit + exitJS(ret, /* implicit = */ true); + return ret; + } + catch (e) { + return handleException(e); + } +} + +function stackCheckInit() { + // This is normally called automatically during __wasm_call_ctors but need to + // get these values before even running any of the ctors so we call it redundantly + // here. + _emscripten_stack_init(); + // TODO(sbc): Move writeStackCookie to native to to avoid this. + writeStackCookie(); +} + +function run(args = arguments_) { + + if (runDependencies > 0) { + return; + } + + stackCheckInit(); + + preRun(); + + // a preRun added a dependency, run will be called later + if (runDependencies > 0) { + return; + } + + function doRun() { + // run may have just been called through dependencies being fulfilled just in this very frame, + // or while the async setStatus time below was happening + if (calledRun) return; + calledRun = true; + Module['calledRun'] = true; + + if (ABORT) return; + + initRuntime(); + + preMain(); + + if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); + + if (shouldRunNow) callMain(args); + + postRun(); + } + + if (Module['setStatus']) { + Module['setStatus']('Running...'); + setTimeout(function() { + setTimeout(function() { + Module['setStatus'](''); + }, 1); + doRun(); + }, 1); + } else + { + doRun(); + } + checkStackCookie(); +} + +function checkUnflushedContent() { + // Compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + // Normally we would not even include flush() at all, but in ASSERTIONS + // builds we do so just for this check, and here we see if there is any + // content to flush, that is, we check if there would have been + // something a non-ASSERTIONS build would have not seen. + // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 + // mode (which has its own special function for this; otherwise, all + // the code is inside libc) + var oldOut = out; + var oldErr = err; + var has = false; + out = err = (x) => { + has = true; + } + try { // it doesn't matter if it fails + _fflush(0); + } catch(e) {} + out = oldOut; + err = oldErr; + if (has) { + warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the Emscripten FAQ), or make sure to emit a newline when you printf etc.'); + warnOnce('(this may also be due to not including full filesystem support - try building with -sFORCE_FILESYSTEM)'); + } +} + +if (Module['preInit']) { + if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; + while (Module['preInit'].length > 0) { + Module['preInit'].pop()(); + } +} + +// shouldRunNow refers to calling main(), not run(). +var shouldRunNow = true; + +if (Module['noInitialRun']) shouldRunNow = false; + +run(); + + +// end include: postamble.js diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.wasm b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.wasm new file mode 100644 index 000000000000..e11660eff62e Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/web/dwarf/diverse-inlining.wasm differ diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.c b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.c new file mode 100644 index 000000000000..de73c4f64aff --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.c @@ -0,0 +1,19 @@ +#include + +int fib(int n) { + int a, b = 0, c = 1; + for (int i = 1; i < n; ++i) { + a = b; + b = c; + c = a + b; + } + return c; +} + +int main() { + int a = fib(9); + printf("9th fib: %d\n", a); + int b = fib(5); + printf("5th fib: %d\n", b); + return 0; +} diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.html b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.html new file mode 100644 index 000000000000..7cae3bf26547 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.html @@ -0,0 +1,1294 @@ + + + + + + Emscripten-Generated Code + + + + + image/svg+xml + + +
+
Downloading...
+ + + Resize canvas + Lock/hide mouse pointer     + + + + +
+ +
+ + +
+ +
+ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.js b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.js new file mode 100644 index 000000000000..bc1365623369 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.js @@ -0,0 +1,1694 @@ +// include: shell.js +// The Module object: Our interface to the outside world. We import +// and export values on it. There are various ways Module can be used: +// 1. Not defined. We create it here +// 2. A function parameter, function(Module) { ..generated code.. } +// 3. pre-run appended it, var Module = {}; ..generated code.. +// 4. External script tag defines var Module. +// We need to check if Module already exists (e.g. case 3 above). +// Substitution will be replaced with actual code on later stage of the build, +// this way Closure Compiler will not mangle it (e.g. case 4. above). +// Note that if you want to run closure, and also to use Module +// after the generated code, you will need to define var Module = {}; +// before the code. Then that object will be used in the code, and you +// can continue to use Module afterwards as well. +var Module = typeof Module != 'undefined' ? Module : {}; + +// --pre-jses are emitted after the Module integration code, so that they can +// refer to Module (if they choose; they can also define Module) + + +// Sometimes an existing Module object exists with properties +// meant to overwrite the default module functionality. Here +// we collect those properties and reapply _after_ we configure +// the current environment's defaults to avoid having to be so +// defensive during initialization. +var moduleOverrides = Object.assign({}, Module); + +var arguments_ = []; +var thisProgram = './this.program'; +var quit_ = (status, toThrow) => { + throw toThrow; +}; + +// Determine the runtime environment we are in. You can customize this by +// setting the ENVIRONMENT setting at compile time (see settings.js). + +// Attempt to auto-detect the environment +var ENVIRONMENT_IS_WEB = typeof window == 'object'; +var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function'; +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string'; +var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + +if (Module['ENVIRONMENT']) { + throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); +} + +// `/` should be present at the end if `scriptDirectory` is not empty +var scriptDirectory = ''; +function locateFile(path) { + if (Module['locateFile']) { + return Module['locateFile'](path, scriptDirectory); + } + return scriptDirectory + path; +} + +// Hooks that are implemented differently in different runtime environments. +var read_, + readAsync, + readBinary, + setWindowTitle; + +if (ENVIRONMENT_IS_NODE) { + if (typeof process == 'undefined' || !process.release || process.release.name !== 'node') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + var nodeVersion = process.versions.node; + var numericVersion = nodeVersion.split('.').slice(0, 3); + numericVersion = (numericVersion[0] * 10000) + (numericVersion[1] * 100) + (numericVersion[2].split('-')[0] * 1); + var minVersion = 160000; + if (numericVersion < 160000) { + throw new Error('This emscripten-generated code requires node v16.0.0 (detected v' + nodeVersion + ')'); + } + + // `require()` is no-op in an ESM module, use `createRequire()` to construct + // the require()` function. This is only necessary for multi-environment + // builds, `-sENVIRONMENT=node` emits a static import declaration instead. + // TODO: Swap all `require()`'s with `import()`'s? + // These modules will usually be used on Node.js. Load them eagerly to avoid + // the complexity of lazy-loading. + var fs = require('fs'); + var nodePath = require('path'); + + if (ENVIRONMENT_IS_WORKER) { + scriptDirectory = nodePath.dirname(scriptDirectory) + '/'; + } else { + scriptDirectory = __dirname + '/'; + } + +// include: node_shell_read.js +read_ = (filename, binary) => { + // We need to re-wrap `file://` strings to URLs. Normalizing isn't + // necessary in that case, the path should already be absolute. + filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); + return fs.readFileSync(filename, binary ? undefined : 'utf8'); +}; + +readBinary = (filename) => { + var ret = read_(filename, true); + if (!ret.buffer) { + ret = new Uint8Array(ret); + } + assert(ret.buffer); + return ret; +}; + +readAsync = (filename, onload, onerror, binary = true) => { + // See the comment in the `read_` function. + filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename); + fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => { + if (err) onerror(err); + else onload(binary ? data.buffer : data); + }); +}; +// end include: node_shell_read.js + if (!Module['thisProgram'] && process.argv.length > 1) { + thisProgram = process.argv[1].replace(/\\/g, '/'); + } + + arguments_ = process.argv.slice(2); + + if (typeof module != 'undefined') { + module['exports'] = Module; + } + + process.on('uncaughtException', (ex) => { + // suppress ExitStatus exceptions from showing an error + if (ex !== 'unwind' && !(ex instanceof ExitStatus) && !(ex.context instanceof ExitStatus)) { + throw ex; + } + }); + + quit_ = (status, toThrow) => { + process.exitCode = status; + throw toThrow; + }; + + Module['inspect'] = () => '[Emscripten Module object]'; + +} else +if (ENVIRONMENT_IS_SHELL) { + + if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + if (typeof read != 'undefined') { + read_ = read; + } + + readBinary = (f) => { + if (typeof readbuffer == 'function') { + return new Uint8Array(readbuffer(f)); + } + let data = read(f, 'binary'); + assert(typeof data == 'object'); + return data; + }; + + readAsync = (f, onload, onerror) => { + setTimeout(() => onload(readBinary(f))); + }; + + if (typeof clearTimeout == 'undefined') { + globalThis.clearTimeout = (id) => {}; + } + + if (typeof setTimeout == 'undefined') { + // spidermonkey lacks setTimeout but we use it above in readAsync. + globalThis.setTimeout = (f) => (typeof f == 'function') ? f() : abort(); + } + + if (typeof scriptArgs != 'undefined') { + arguments_ = scriptArgs; + } else if (typeof arguments != 'undefined') { + arguments_ = arguments; + } + + if (typeof quit == 'function') { + quit_ = (status, toThrow) => { + // Unlike node which has process.exitCode, d8 has no such mechanism. So we + // have no way to set the exit code and then let the program exit with + // that code when it naturally stops running (say, when all setTimeouts + // have completed). For that reason, we must call `quit` - the only way to + // set the exit code - but quit also halts immediately. To increase + // consistency with node (and the web) we schedule the actual quit call + // using a setTimeout to give the current stack and any exception handlers + // a chance to run. This enables features such as addOnPostRun (which + // expected to be able to run code after main returns). + setTimeout(() => { + if (!(toThrow instanceof ExitStatus)) { + let toLog = toThrow; + if (toThrow && typeof toThrow == 'object' && toThrow.stack) { + toLog = [toThrow, toThrow.stack]; + } + err(`exiting due to exception: ${toLog}`); + } + quit(status); + }); + throw toThrow; + }; + } + + if (typeof print != 'undefined') { + // Prefer to use print/printErr where they exist, as they usually work better. + if (typeof console == 'undefined') console = /** @type{!Console} */({}); + console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); + console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr != 'undefined' ? printErr : print); + } + +} else + +// Note that this includes Node.js workers when relevant (pthreads is enabled). +// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and +// ENVIRONMENT_IS_NODE. +if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled + scriptDirectory = self.location.href; + } else if (typeof document != 'undefined' && document.currentScript) { // web + scriptDirectory = document.currentScript.src; + } + // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. + // otherwise, slice off the final part of the url to find the script directory. + // if scriptDirectory does not contain a slash, lastIndexOf will return -1, + // and scriptDirectory will correctly be replaced with an empty string. + // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), + // they are removed because they could contain a slash. + if (scriptDirectory.indexOf('blob:') !== 0) { + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf('/')+1); + } else { + scriptDirectory = ''; + } + + if (!(typeof window == 'object' || typeof importScripts == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + // Differentiate the Web Worker from the Node Worker case, as reading must + // be done differently. + { +// include: web_or_worker_shell_read.js +read_ = (url) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.send(null); + return xhr.responseText; + } + + if (ENVIRONMENT_IS_WORKER) { + readBinary = (url) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + xhr.responseType = 'arraybuffer'; + xhr.send(null); + return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response)); + }; + } + + readAsync = (url, onload, onerror) => { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'arraybuffer'; + xhr.onload = () => { + if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 + onload(xhr.response); + return; + } + onerror(); + }; + xhr.onerror = onerror; + xhr.send(null); + } + +// end include: web_or_worker_shell_read.js + } + + setWindowTitle = (title) => document.title = title; +} else +{ + throw new Error('environment detection error'); +} + +var out = Module['print'] || console.log.bind(console); +var err = Module['printErr'] || console.error.bind(console); + +// Merge back in the overrides +Object.assign(Module, moduleOverrides); +// Free the object hierarchy contained in the overrides, this lets the GC +// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. +moduleOverrides = null; +checkIncomingModuleAPI(); + +// Emit code to handle expected values on the Module object. This applies Module.x +// to the proper local x. This has two benefits: first, we only emit it if it is +// expected to arrive, and second, by using a local everywhere else that can be +// minified. + +if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('arguments', 'arguments_'); + +if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram'); + +if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_'); + +// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message +// Assertions on removed incoming Module JS APIs. +assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); +assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)'); +assert(typeof Module['readAsync'] == 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); +assert(typeof Module['readBinary'] == 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); +assert(typeof Module['setWindowTitle'] == 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)'); +assert(typeof Module['TOTAL_MEMORY'] == 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); +legacyModuleProp('asm', 'wasmExports'); +legacyModuleProp('read', 'read_'); +legacyModuleProp('readAsync', 'readAsync'); +legacyModuleProp('readBinary', 'readBinary'); +legacyModuleProp('setWindowTitle', 'setWindowTitle'); +var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js'; +var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js'; +var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js'; +var FETCHFS = 'FETCHFS is no longer included by default; build with -lfetchfs.js'; +var ICASEFS = 'ICASEFS is no longer included by default; build with -licasefs.js'; +var JSFILEFS = 'JSFILEFS is no longer included by default; build with -ljsfilefs.js'; +var OPFS = 'OPFS is no longer included by default; build with -lopfs.js'; + +var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; + +assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at build time. Add 'shell' to `-sENVIRONMENT` to enable."); + + +// end include: shell.js +// include: preamble.js +// === Preamble library stuff === + +// Documentation for the public APIs defined in this file must be updated in: +// site/source/docs/api_reference/preamble.js.rst +// A prebuilt local version of the documentation is available at: +// site/build/text/docs/api_reference/preamble.js.txt +// You can also build docs locally as HTML or other formats in site/ +// An online HTML version (which may be of a different version of Emscripten) +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html + +var wasmBinary; +if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary'); +var noExitRuntime = Module['noExitRuntime'] || true;legacyModuleProp('noExitRuntime', 'noExitRuntime'); + +if (typeof WebAssembly != 'object') { + abort('no native wasm support detected'); +} + +// Wasm globals + +var wasmMemory; + +//======================================== +// Runtime essentials +//======================================== + +// whether we are quitting the application. no code should run after this. +// set in exit() and abort() +var ABORT = false; + +// set by exit() and abort(). Passed to 'onExit' handler. +// NOTE: This is also used as the process return code code in shell environments +// but only when noExitRuntime is false. +var EXITSTATUS; + +/** @type {function(*, string=)} */ +function assert(condition, text) { + if (!condition) { + abort('Assertion failed' + (text ? ': ' + text : '')); + } +} + +// We used to include malloc/free by default in the past. Show a helpful error in +// builds with assertions. +function _malloc() { + abort("malloc() called but not included in the build - add '_malloc' to EXPORTED_FUNCTIONS"); +} +function _free() { + // Show a helpful error since we used to include free by default in the past. + abort("free() called but not included in the build - add '_free' to EXPORTED_FUNCTIONS"); +} + +// Memory management + +var HEAP, +/** @type {!Int8Array} */ + HEAP8, +/** @type {!Uint8Array} */ + HEAPU8, +/** @type {!Int16Array} */ + HEAP16, +/** @type {!Uint16Array} */ + HEAPU16, +/** @type {!Int32Array} */ + HEAP32, +/** @type {!Uint32Array} */ + HEAPU32, +/** @type {!Float32Array} */ + HEAPF32, +/** @type {!Float64Array} */ + HEAPF64; + +function updateMemoryViews() { + var b = wasmMemory.buffer; + Module['HEAP8'] = HEAP8 = new Int8Array(b); + Module['HEAP16'] = HEAP16 = new Int16Array(b); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(b); + Module['HEAPU16'] = HEAPU16 = new Uint16Array(b); + Module['HEAP32'] = HEAP32 = new Int32Array(b); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(b); + Module['HEAPF32'] = HEAPF32 = new Float32Array(b); + Module['HEAPF64'] = HEAPF64 = new Float64Array(b); +} + +assert(!Module['STACK_SIZE'], 'STACK_SIZE can no longer be set at runtime. Use -sSTACK_SIZE at link time') + +assert(typeof Int32Array != 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray != undefined && Int32Array.prototype.set != undefined, + 'JS engine does not provide full typed array support'); + +// If memory is defined in wasm, the user can't provide it, or set INITIAL_MEMORY +assert(!Module['wasmMemory'], 'Use of `wasmMemory` detected. Use -sIMPORTED_MEMORY to define wasmMemory externally'); +assert(!Module['INITIAL_MEMORY'], 'Detected runtime INITIAL_MEMORY setting. Use -sIMPORTED_MEMORY to define wasmMemory dynamically'); + +// include: runtime_init_table.js +// In regular non-RELOCATABLE mode the table is exported +// from the wasm module and this will be assigned once +// the exports are available. +var wasmTable; +// end include: runtime_init_table.js +// include: runtime_stack_check.js +// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. +function writeStackCookie() { + var max = _emscripten_stack_get_end(); + assert((max & 3) == 0); + // If the stack ends at address zero we write our cookies 4 bytes into the + // stack. This prevents interference with SAFE_HEAP and ASAN which also + // monitor writes to address zero. + if (max == 0) { + max += 4; + } + // The stack grow downwards towards _emscripten_stack_get_end. + // We write cookies to the final two words in the stack and detect if they are + // ever overwritten. + HEAPU32[((max)>>2)] = 0x02135467; + HEAPU32[(((max)+(4))>>2)] = 0x89BACDFE; + // Also test the global address 0 for integrity. + HEAPU32[((0)>>2)] = 1668509029; +} + +function checkStackCookie() { + if (ABORT) return; + var max = _emscripten_stack_get_end(); + // See writeStackCookie(). + if (max == 0) { + max += 4; + } + var cookie1 = HEAPU32[((max)>>2)]; + var cookie2 = HEAPU32[(((max)+(4))>>2)]; + if (cookie1 != 0x02135467 || cookie2 != 0x89BACDFE) { + abort(`Stack overflow! Stack cookie has been overwritten at ${ptrToString(max)}, expected hex dwords 0x89BACDFE and 0x2135467, but received ${ptrToString(cookie2)} ${ptrToString(cookie1)}`); + } + // Also test the global address 0 for integrity. + if (HEAPU32[((0)>>2)] != 0x63736d65 /* 'emsc' */) { + abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); + } +} +// end include: runtime_stack_check.js +// include: runtime_assertions.js +// Endianness check +(function() { + var h16 = new Int16Array(1); + var h8 = new Int8Array(h16.buffer); + h16[0] = 0x6373; + if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'; +})(); + +// end include: runtime_assertions.js +var __ATPRERUN__ = []; // functions called before the runtime is initialized +var __ATINIT__ = []; // functions called during startup +var __ATMAIN__ = []; // functions called when main() is to be run +var __ATEXIT__ = []; // functions called during shutdown +var __ATPOSTRUN__ = []; // functions called after the main() is called + +var runtimeInitialized = false; + +var runtimeKeepaliveCounter = 0; + +function keepRuntimeAlive() { + return noExitRuntime || runtimeKeepaliveCounter > 0; +} + +function preRun() { + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); +} + +function initRuntime() { + assert(!runtimeInitialized); + runtimeInitialized = true; + + checkStackCookie(); + + + callRuntimeCallbacks(__ATINIT__); +} + +function preMain() { + checkStackCookie(); + + callRuntimeCallbacks(__ATMAIN__); +} + +function postRun() { + checkStackCookie(); + + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } + + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} + +function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); +} + +function addOnExit(cb) { +} + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} + +// include: runtime_math.js +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc + +assert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +assert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); +// end include: runtime_math.js +// A counter of dependencies for calling run(). If we need to +// do asynchronous work before running, increment this and +// decrement it. Incrementing must happen in a place like +// Module.preRun (used by emcc to add file preloading). +// Note that you can add dependencies in preRun, even though +// it happens right before run - run will be postponed until +// the dependencies are met. +var runDependencies = 0; +var runDependencyWatcher = null; +var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled +var runDependencyTracking = {}; + +function getUniqueRunDependency(id) { + var orig = id; + while (1) { + if (!runDependencyTracking[id]) return id; + id = orig + Math.random(); + } +} + +function addRunDependency(id) { + runDependencies++; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + + if (id) { + assert(!runDependencyTracking[id]); + runDependencyTracking[id] = 1; + if (runDependencyWatcher === null && typeof setInterval != 'undefined') { + // Check for missing dependencies every few seconds + runDependencyWatcher = setInterval(() => { + if (ABORT) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + return; + } + var shown = false; + for (var dep in runDependencyTracking) { + if (!shown) { + shown = true; + err('still waiting on run dependencies:'); + } + err(`dependency: ${dep}`); + } + if (shown) { + err('(end of list)'); + } + }, 10000); + } + } else { + err('warning: run dependency added without ID'); + } +} + +function removeRunDependency(id) { + runDependencies--; + + if (Module['monitorRunDependencies']) { + Module['monitorRunDependencies'](runDependencies); + } + + if (id) { + assert(runDependencyTracking[id]); + delete runDependencyTracking[id]; + } else { + err('warning: run dependency removed without ID'); + } + if (runDependencies == 0) { + if (runDependencyWatcher !== null) { + clearInterval(runDependencyWatcher); + runDependencyWatcher = null; + } + if (dependenciesFulfilled) { + var callback = dependenciesFulfilled; + dependenciesFulfilled = null; + callback(); // can add another dependenciesFulfilled + } + } +} + +/** @param {string|number=} what */ +function abort(what) { + if (Module['onAbort']) { + Module['onAbort'](what); + } + + what = 'Aborted(' + what + ')'; + // TODO(sbc): Should we remove printing and leave it up to whoever + // catches the exception? + err(what); + + ABORT = true; + EXITSTATUS = 1; + + // Use a wasm runtime error, because a JS error might be seen as a foreign + // exception, which means we'd run destructors on it. We need the error to + // simply make the program stop. + // FIXME This approach does not work in Wasm EH because it currently does not assume + // all RuntimeErrors are from traps; it decides whether a RuntimeError is from + // a trap or not based on a hidden field within the object. So at the moment + // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that + // allows this in the wasm spec. + + // Suppress closure compiler warning here. Closure compiler's builtin extern + // defintion for WebAssembly.RuntimeError claims it takes no arguments even + // though it can. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. + /** @suppress {checkTypes} */ + var e = new WebAssembly.RuntimeError(what); + + // Throw the error whether or not MODULARIZE is set because abort is used + // in code paths apart from instantiation where an exception is expected + // to be thrown when abort is called. + throw e; +} + +// include: memoryprofiler.js +// end include: memoryprofiler.js +// show errors on likely calls to FS when it was not included +var FS = { + error() { + abort('Filesystem support (FS) was not included. The problem is that you are using files from JS, but files were not used from C/C++, so filesystem support was not auto-included. You can force-include filesystem support with -sFORCE_FILESYSTEM'); + }, + init() { FS.error() }, + createDataFile() { FS.error() }, + createPreloadedFile() { FS.error() }, + createLazyFile() { FS.error() }, + open() { FS.error() }, + mkdev() { FS.error() }, + registerDevice() { FS.error() }, + analyzePath() { FS.error() }, + + ErrnoError() { FS.error() }, +}; +Module['FS_createDataFile'] = FS.createDataFile; +Module['FS_createPreloadedFile'] = FS.createPreloadedFile; + +// include: URIUtils.js +// Prefix of data URIs emitted by SINGLE_FILE and related options. +var dataURIPrefix = 'data:application/octet-stream;base64,'; + +// Indicates whether filename is a base64 data URI. +function isDataURI(filename) { + // Prefix of data URIs emitted by SINGLE_FILE and related options. + return filename.startsWith(dataURIPrefix); +} + +// Indicates whether filename is delivered via file protocol (as opposed to http/https) +function isFileURI(filename) { + return filename.startsWith('file://'); +} +// end include: URIUtils.js +function createExportWrapper(name) { + return function() { + assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`); + var f = wasmExports[name]; + assert(f, `exported native function \`${name}\` not found`); + return f.apply(null, arguments); + }; +} + +// include: runtime_exceptions.js +// end include: runtime_exceptions.js +var wasmBinaryFile; + wasmBinaryFile = 'fibonacci.wasm'; + if (!isDataURI(wasmBinaryFile)) { + wasmBinaryFile = locateFile(wasmBinaryFile); + } + +function getBinarySync(file) { + if (file == wasmBinaryFile && wasmBinary) { + return new Uint8Array(wasmBinary); + } + if (readBinary) { + return readBinary(file); + } + throw "both async and sync fetching of the wasm failed"; +} + +function getBinaryPromise(binaryFile) { + // If we don't have the binary yet, try to load it asynchronously. + // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. + // See https://github.com/github/fetch/pull/92#issuecomment-140665932 + // Cordova or Electron apps are typically loaded from a file:// url. + // So use fetch if it is available and the url is not a file, otherwise fall back to XHR. + if (!wasmBinary + && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { + if (typeof fetch == 'function' + && !isFileURI(binaryFile) + ) { + return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => { + if (!response['ok']) { + throw "failed to load wasm binary file at '" + binaryFile + "'"; + } + return response['arrayBuffer'](); + }).catch(() => getBinarySync(binaryFile)); + } + else if (readAsync) { + // fetch is not available or url is file => try XHR (readAsync uses XHR internally) + return new Promise((resolve, reject) => { + readAsync(binaryFile, (response) => resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))), reject) + }); + } + } + + // Otherwise, getBinarySync should be able to get it synchronously + return Promise.resolve().then(() => getBinarySync(binaryFile)); +} + +function instantiateArrayBuffer(binaryFile, imports, receiver) { + return getBinaryPromise(binaryFile).then((binary) => { + return WebAssembly.instantiate(binary, imports); + }).then((instance) => { + return instance; + }).then(receiver, (reason) => { + err(`failed to asynchronously prepare wasm: ${reason}`); + + // Warn on some common problems. + if (isFileURI(wasmBinaryFile)) { + err(`warning: Loading from a file URI (${wasmBinaryFile}) is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing`); + } + abort(reason); + }); +} + +function instantiateAsync(binary, binaryFile, imports, callback) { + if (!binary && + typeof WebAssembly.instantiateStreaming == 'function' && + !isDataURI(binaryFile) && + // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. + !isFileURI(binaryFile) && + // Avoid instantiateStreaming() on Node.js environment for now, as while + // Node.js v18.1.0 implements it, it does not have a full fetch() + // implementation yet. + // + // Reference: + // https://github.com/emscripten-core/emscripten/pull/16917 + !ENVIRONMENT_IS_NODE && + typeof fetch == 'function') { + return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => { + // Suppress closure warning here since the upstream definition for + // instantiateStreaming only allows Promise rather than + // an actual Response. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. + /** @suppress {checkTypes} */ + var result = WebAssembly.instantiateStreaming(response, imports); + + return result.then( + callback, + function(reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + err(`wasm streaming compile failed: ${reason}`); + err('falling back to ArrayBuffer instantiation'); + return instantiateArrayBuffer(binaryFile, imports, callback); + }); + }); + } + return instantiateArrayBuffer(binaryFile, imports, callback); +} + +// Create the wasm instance. +// Receives the wasm imports, returns the exports. +function createWasm() { + // prepare imports + var info = { + 'env': wasmImports, + 'wasi_snapshot_preview1': wasmImports, + }; + // Load the wasm module and create an instance of using native support in the JS engine. + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + /** @param {WebAssembly.Module=} module*/ + function receiveInstance(instance, module) { + var exports = instance.exports; + + wasmExports = exports; + + + wasmMemory = wasmExports['memory']; + + assert(wasmMemory, "memory not found in wasm exports"); + // This assertion doesn't hold when emscripten is run in --post-link + // mode. + // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode. + //assert(wasmMemory.buffer.byteLength === 16777216); + updateMemoryViews(); + + wasmTable = wasmExports['__indirect_function_table']; + + assert(wasmTable, "table not found in wasm exports"); + + addOnInit(wasmExports['__wasm_call_ctors']); + + removeRunDependency('wasm-instantiate'); + return exports; + } + // wait for the pthread pool (if any) + addRunDependency('wasm-instantiate'); + + // Prefer streaming instantiation if available. + // Async compilation can be confusing when an error on the page overwrites Module + // (for example, if the order of elements is wrong, and the one defining Module is + // later), so we save Module and check it later. + var trueModule = Module; + function receiveInstantiationResult(result) { + // 'result' is a ResultObject object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called + assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); + trueModule = null; + // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. + // When the regression is fixed, can restore the above PTHREADS-enabled path. + receiveInstance(result['instance']); + } + + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback + // to manually instantiate the Wasm module themselves. This allows pages to + // run the instantiation parallel to any other async startup actions they are + // performing. + // Also pthreads and wasm workers initialize the wasm instance through this + // path. + if (Module['instantiateWasm']) { + + try { + return Module['instantiateWasm'](info, receiveInstance); + } catch(e) { + err(`Module.instantiateWasm callback failed with error: ${e}`); + return false; + } + } + + instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult); + return {}; // no exports yet; we'll fill them in later +} + +// Globals used by JS i64 conversions (see makeSetValue) +var tempDouble; +var tempI64; + +// include: runtime_debug.js +function legacyModuleProp(prop, newName, incomming=true) { + if (!Object.getOwnPropertyDescriptor(Module, prop)) { + Object.defineProperty(Module, prop, { + configurable: true, + get() { + let extra = incomming ? ' (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)' : ''; + abort(`\`Module.${prop}\` has been replaced by \`${newName}\`` + extra); + + } + }); + } +} + +function ignoredModuleProp(prop) { + if (Object.getOwnPropertyDescriptor(Module, prop)) { + abort(`\`Module.${prop}\` was supplied but \`${prop}\` not included in INCOMING_MODULE_JS_API`); + } +} + +// forcing the filesystem exports a few things by default +function isExportedByForceFilesystem(name) { + return name === 'FS_createPath' || + name === 'FS_createDataFile' || + name === 'FS_createPreloadedFile' || + name === 'FS_unlink' || + name === 'addRunDependency' || + // The old FS has some functionality that WasmFS lacks. + name === 'FS_createLazyFile' || + name === 'FS_createDevice' || + name === 'removeRunDependency'; +} + +function missingGlobal(sym, msg) { + if (typeof globalThis !== 'undefined') { + Object.defineProperty(globalThis, sym, { + configurable: true, + get() { + warnOnce('`' + sym + '` is not longer defined by emscripten. ' + msg); + return undefined; + } + }); + } +} + +missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer'); +missingGlobal('asm', 'Please use wasmExports instead'); + +function missingLibrarySymbol(sym) { + if (typeof globalThis !== 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) { + Object.defineProperty(globalThis, sym, { + configurable: true, + get() { + // Can't `abort()` here because it would break code that does runtime + // checks. e.g. `if (typeof SDL === 'undefined')`. + var msg = '`' + sym + '` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line'; + // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in + // library.js, which means $name for a JS name with no prefix, or name + // for a JS name like _name. + var librarySymbol = sym; + if (!librarySymbol.startsWith('_')) { + librarySymbol = '$' + sym; + } + msg += " (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='" + librarySymbol + "')"; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + warnOnce(msg); + return undefined; + } + }); + } + // Any symbol that is not included from the JS libary is also (by definition) + // not exported on the Module object. + unexportedRuntimeSymbol(sym); +} + +function unexportedRuntimeSymbol(sym) { + if (!Object.getOwnPropertyDescriptor(Module, sym)) { + Object.defineProperty(Module, sym, { + configurable: true, + get() { + var msg = "'" + sym + "' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)"; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + abort(msg); + } + }); + } +} + +// Used by XXXXX_DEBUG settings to output debug messages. +function dbg(text) { + // TODO(sbc): Make this configurable somehow. Its not always convenient for + // logging to show up as warnings. + console.warn.apply(console, arguments); +} +// end include: runtime_debug.js +// === Body === + +// end include: preamble.js + + /** @constructor */ + function ExitStatus(status) { + this.name = 'ExitStatus'; + this.message = `Program terminated with exit(${status})`; + this.status = status; + } + + var callRuntimeCallbacks = (callbacks) => { + while (callbacks.length > 0) { + // Pass the module as the first argument. + callbacks.shift()(Module); + } + }; + + + /** + * @param {number} ptr + * @param {string} type + */ + function getValue(ptr, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': return HEAP8[((ptr)>>0)]; + case 'i8': return HEAP8[((ptr)>>0)]; + case 'i16': return HEAP16[((ptr)>>1)]; + case 'i32': return HEAP32[((ptr)>>2)]; + case 'i64': abort('to do getValue(i64) use WASM_BIGINT'); + case 'float': return HEAPF32[((ptr)>>2)]; + case 'double': return HEAPF64[((ptr)>>3)]; + case '*': return HEAPU32[((ptr)>>2)]; + default: abort(`invalid type for getValue: ${type}`); + } + } + + var ptrToString = (ptr) => { + assert(typeof ptr === 'number'); + // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. + ptr >>>= 0; + return '0x' + ptr.toString(16).padStart(8, '0'); + }; + + + /** + * @param {number} ptr + * @param {number} value + * @param {string} type + */ + function setValue(ptr, value, type = 'i8') { + if (type.endsWith('*')) type = '*'; + switch (type) { + case 'i1': HEAP8[((ptr)>>0)] = value; break; + case 'i8': HEAP8[((ptr)>>0)] = value; break; + case 'i16': HEAP16[((ptr)>>1)] = value; break; + case 'i32': HEAP32[((ptr)>>2)] = value; break; + case 'i64': abort('to do setValue(i64) use WASM_BIGINT'); + case 'float': HEAPF32[((ptr)>>2)] = value; break; + case 'double': HEAPF64[((ptr)>>3)] = value; break; + case '*': HEAPU32[((ptr)>>2)] = value; break; + default: abort(`invalid type for setValue: ${type}`); + } + } + + var warnOnce = (text) => { + if (!warnOnce.shown) warnOnce.shown = {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; + if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text; + err(text); + } + }; + + var _emscripten_memcpy_big = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num); + + var printCharBuffers = [null,[],[]]; + + var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined; + + /** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number} idx + * @param {number=} maxBytesToRead + * @return {string} + */ + var UTF8ArrayToString = (heapOrArray, idx, maxBytesToRead) => { + var endIdx = idx + maxBytesToRead; + var endPtr = idx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. Also, use the length info to avoid running tiny + // strings through TextDecoder, since .subarray() allocates garbage. + // (As a tiny code save trick, compare endPtr against endIdx using a negation, + // so that undefined means Infinity) + while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; + + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); + } + var str = ''; + // If building with TextDecoder, we have already computed the string length + // above, so test loop end condition against that + while (idx < endPtr) { + // For UTF8 byte structure, see: + // http://en.wikipedia.org/wiki/UTF-8#Description + // https://www.ietf.org/rfc/rfc2279.txt + // https://tools.ietf.org/html/rfc3629 + var u0 = heapOrArray[idx++]; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + var u1 = heapOrArray[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + var u2 = heapOrArray[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!'); + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); + } + + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } + return str; + }; + var printChar = (stream, curr) => { + var buffer = printCharBuffers[stream]; + assert(buffer); + if (curr === 0 || curr === 10) { + (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); + buffer.length = 0; + } else { + buffer.push(curr); + } + }; + + var flush_NO_FILESYSTEM = () => { + // flush anything remaining in the buffers during shutdown + _fflush(0); + if (printCharBuffers[1].length) printChar(1, 10); + if (printCharBuffers[2].length) printChar(2, 10); + }; + + + + /** + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index (i.e. maxBytesToRead will not + * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing + * frequent uses of UTF8ToString() with and without maxBytesToRead may throw + * JS JIT optimizations off, so it is worth to consider consistently using one + * @return {string} + */ + var UTF8ToString = (ptr, maxBytesToRead) => { + assert(typeof ptr == 'number'); + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; + }; + var SYSCALLS = { + varargs:undefined, + get() { + assert(SYSCALLS.varargs != undefined); + var ret = HEAP32[((SYSCALLS.varargs)>>2)]; + SYSCALLS.varargs += 4; + return ret; + }, + getp() { return SYSCALLS.get() }, + getStr(ptr) { + var ret = UTF8ToString(ptr); + return ret; + }, + }; + var _fd_write = (fd, iov, iovcnt, pnum) => { + // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 + var num = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[((iov)>>2)]; + var len = HEAPU32[(((iov)+(4))>>2)]; + iov += 8; + for (var j = 0; j < len; j++) { + printChar(fd, HEAPU8[ptr+j]); + } + num += len; + } + HEAPU32[((pnum)>>2)] = num; + return 0; + }; + + + var _proc_exit = (code) => { + EXITSTATUS = code; + if (!keepRuntimeAlive()) { + if (Module['onExit']) Module['onExit'](code); + ABORT = true; + } + quit_(code, new ExitStatus(code)); + }; + /** @param {boolean|number=} implicit */ + var exitJS = (status, implicit) => { + EXITSTATUS = status; + + checkUnflushedContent(); + + // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down + if (keepRuntimeAlive() && !implicit) { + var msg = `program exited (with status: ${status}), but keepRuntimeAlive() is set (counter=${runtimeKeepaliveCounter}) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)`; + err(msg); + } + + _proc_exit(status); + }; + + var handleException = (e) => { + // Certain exception types we do not treat as errors since they are used for + // internal control flow. + // 1. ExitStatus, which is thrown by exit() + // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others + // that wish to return to JS event loop. + if (e instanceof ExitStatus || e == 'unwind') { + return EXITSTATUS; + } + checkStackCookie(); + if (e instanceof WebAssembly.RuntimeError) { + if (_emscripten_stack_get_current() <= 0) { + err('Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to 65536)'); + } + } + quit_(1, e); + }; +function checkIncomingModuleAPI() { + ignoredModuleProp('fetchSettings'); +} +var wasmImports = { + emscripten_memcpy_big: _emscripten_memcpy_big, + fd_write: _fd_write +}; +var wasmExports = createWasm(); +var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors'); +var _main = Module['_main'] = createExportWrapper('main'); +var ___errno_location = createExportWrapper('__errno_location'); +var _fflush = Module['_fflush'] = createExportWrapper('fflush'); +var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])(); +var _emscripten_stack_get_free = () => (_emscripten_stack_get_free = wasmExports['emscripten_stack_get_free'])(); +var _emscripten_stack_get_base = () => (_emscripten_stack_get_base = wasmExports['emscripten_stack_get_base'])(); +var _emscripten_stack_get_end = () => (_emscripten_stack_get_end = wasmExports['emscripten_stack_get_end'])(); +var stackSave = createExportWrapper('stackSave'); +var stackRestore = createExportWrapper('stackRestore'); +var stackAlloc = createExportWrapper('stackAlloc'); +var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports['emscripten_stack_get_current'])(); +var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji'); + + +// include: postamble.js +// === Auto-generated postamble setup entry stuff === + +var missingLibrarySymbols = [ + 'writeI53ToI64', + 'writeI53ToI64Clamped', + 'writeI53ToI64Signaling', + 'writeI53ToU64Clamped', + 'writeI53ToU64Signaling', + 'readI53FromI64', + 'readI53FromU64', + 'convertI32PairToI53', + 'convertI32PairToI53Checked', + 'convertU32PairToI53', + 'zeroMemory', + 'getHeapMax', + 'abortOnCannotGrowMemory', + 'growMemory', + 'isLeapYear', + 'ydayFromDate', + 'arraySum', + 'addDays', + 'setErrNo', + 'inetPton4', + 'inetNtop4', + 'inetPton6', + 'inetNtop6', + 'readSockaddr', + 'writeSockaddr', + 'getHostByName', + 'initRandomFill', + 'randomFill', + 'getCallstack', + 'emscriptenLog', + 'convertPCtoSourceLocation', + 'readEmAsmArgs', + 'jstoi_q', + 'jstoi_s', + 'getExecutableName', + 'listenOnce', + 'autoResumeAudioContext', + 'dynCallLegacy', + 'getDynCaller', + 'dynCall', + 'runtimeKeepalivePush', + 'runtimeKeepalivePop', + 'callUserCallback', + 'maybeExit', + 'safeSetTimeout', + 'asmjsMangle', + 'asyncLoad', + 'alignMemory', + 'mmapAlloc', + 'handleAllocatorInit', + 'HandleAllocator', + 'getNativeTypeSize', + 'STACK_SIZE', + 'STACK_ALIGN', + 'POINTER_SIZE', + 'ASSERTIONS', + 'getCFunc', + 'ccall', + 'cwrap', + 'uleb128Encode', + 'sigToWasmTypes', + 'generateFuncType', + 'convertJsFunctionToWasm', + 'getEmptyTableSlot', + 'updateTableMap', + 'getFunctionAddress', + 'addFunction', + 'removeFunction', + 'reallyNegative', + 'unSign', + 'strLen', + 'reSign', + 'formatString', + 'stringToUTF8Array', + 'stringToUTF8', + 'lengthBytesUTF8', + 'intArrayFromString', + 'intArrayToString', + 'AsciiToString', + 'stringToAscii', + 'UTF16ToString', + 'stringToUTF16', + 'lengthBytesUTF16', + 'UTF32ToString', + 'stringToUTF32', + 'lengthBytesUTF32', + 'stringToNewUTF8', + 'stringToUTF8OnStack', + 'writeArrayToMemory', + 'registerKeyEventCallback', + 'maybeCStringToJsString', + 'findEventTarget', + 'findCanvasEventTarget', + 'getBoundingClientRect', + 'fillMouseEventData', + 'registerMouseEventCallback', + 'registerWheelEventCallback', + 'registerUiEventCallback', + 'registerFocusEventCallback', + 'fillDeviceOrientationEventData', + 'registerDeviceOrientationEventCallback', + 'fillDeviceMotionEventData', + 'registerDeviceMotionEventCallback', + 'screenOrientation', + 'fillOrientationChangeEventData', + 'registerOrientationChangeEventCallback', + 'fillFullscreenChangeEventData', + 'registerFullscreenChangeEventCallback', + 'JSEvents_requestFullscreen', + 'JSEvents_resizeCanvasForFullscreen', + 'registerRestoreOldStyle', + 'hideEverythingExceptGivenElement', + 'restoreHiddenElements', + 'setLetterbox', + 'softFullscreenResizeWebGLRenderTarget', + 'doRequestFullscreen', + 'fillPointerlockChangeEventData', + 'registerPointerlockChangeEventCallback', + 'registerPointerlockErrorEventCallback', + 'requestPointerLock', + 'fillVisibilityChangeEventData', + 'registerVisibilityChangeEventCallback', + 'registerTouchEventCallback', + 'fillGamepadEventData', + 'registerGamepadEventCallback', + 'registerBeforeUnloadEventCallback', + 'fillBatteryEventData', + 'battery', + 'registerBatteryEventCallback', + 'setCanvasElementSize', + 'getCanvasElementSize', + 'demangle', + 'demangleAll', + 'jsStackTrace', + 'stackTrace', + 'getEnvStrings', + 'checkWasiClock', + 'wasiRightsToMuslOFlags', + 'wasiOFlagsToMuslOFlags', + 'createDyncallWrapper', + 'setImmediateWrapped', + 'clearImmediateWrapped', + 'polyfillSetImmediate', + 'getPromise', + 'makePromise', + 'idsToPromises', + 'makePromiseCallback', + 'ExceptionInfo', + 'findMatchingCatch', + 'setMainLoop', + 'getSocketFromFD', + 'getSocketAddress', + 'FS_createPreloadedFile', + 'FS_modeStringToFlags', + 'FS_getMode', + 'FS_stdin_getChar', + '_setNetworkCallback', + 'heapObjectForWebGLType', + 'heapAccessShiftForWebGLHeap', + 'webgl_enable_ANGLE_instanced_arrays', + 'webgl_enable_OES_vertex_array_object', + 'webgl_enable_WEBGL_draw_buffers', + 'webgl_enable_WEBGL_multi_draw', + 'emscriptenWebGLGet', + 'computeUnpackAlignedImageSize', + 'colorChannelsInGlTextureFormat', + 'emscriptenWebGLGetTexPixelData', + '__glGenObject', + 'emscriptenWebGLGetUniform', + 'webglGetUniformLocation', + 'webglPrepareUniformLocationsBeforeFirstUse', + 'webglGetLeftBracePos', + 'emscriptenWebGLGetVertexAttrib', + '__glGetActiveAttribOrUniform', + 'writeGLArray', + 'registerWebGlEventCallback', + 'runAndAbortIfError', + 'SDL_unicode', + 'SDL_ttfContext', + 'SDL_audio', + 'GLFW_Window', + 'ALLOC_NORMAL', + 'ALLOC_STACK', + 'allocate', + 'writeStringToMemory', + 'writeAsciiToMemory', +]; +missingLibrarySymbols.forEach(missingLibrarySymbol) + +var unexportedSymbols = [ + 'run', + 'addOnPreRun', + 'addOnInit', + 'addOnPreMain', + 'addOnExit', + 'addOnPostRun', + 'addRunDependency', + 'removeRunDependency', + 'FS_createFolder', + 'FS_createPath', + 'FS_createDataFile', + 'FS_createLazyFile', + 'FS_createLink', + 'FS_createDevice', + 'FS_readFile', + 'FS_unlink', + 'out', + 'err', + 'callMain', + 'abort', + 'keepRuntimeAlive', + 'wasmMemory', + 'wasmTable', + 'wasmExports', + 'stackAlloc', + 'stackSave', + 'stackRestore', + 'getTempRet0', + 'setTempRet0', + 'writeStackCookie', + 'checkStackCookie', + 'ptrToString', + 'exitJS', + 'ENV', + 'MONTH_DAYS_REGULAR', + 'MONTH_DAYS_LEAP', + 'MONTH_DAYS_REGULAR_CUMULATIVE', + 'MONTH_DAYS_LEAP_CUMULATIVE', + 'ERRNO_CODES', + 'ERRNO_MESSAGES', + 'DNS', + 'Protocols', + 'Sockets', + 'timers', + 'warnOnce', + 'UNWIND_CACHE', + 'readEmAsmArgsArray', + 'handleException', + 'freeTableIndexes', + 'functionsInTableMap', + 'setValue', + 'getValue', + 'PATH', + 'PATH_FS', + 'UTF8Decoder', + 'UTF8ArrayToString', + 'UTF8ToString', + 'UTF16Decoder', + 'JSEvents', + 'specialHTMLTargets', + 'currentFullscreenStrategy', + 'restoreOldWindowedStyle', + 'ExitStatus', + 'flush_NO_FILESYSTEM', + 'promiseMap', + 'uncaughtExceptionCount', + 'exceptionLast', + 'exceptionCaught', + 'Browser', + 'wget', + 'SYSCALLS', + 'preloadPlugins', + 'FS_stdin_getChar_buffer', + 'FS', + 'MEMFS', + 'TTY', + 'PIPEFS', + 'SOCKFS', + 'tempFixedLengthArray', + 'miniTempWebGLFloatBuffers', + 'miniTempWebGLIntBuffers', + 'GL', + 'emscripten_webgl_power_preferences', + 'AL', + 'GLUT', + 'EGL', + 'GLEW', + 'IDBStore', + 'SDL', + 'SDL_gfx', + 'GLFW', + 'allocateUTF8', + 'allocateUTF8OnStack', +]; +unexportedSymbols.forEach(unexportedRuntimeSymbol); + + + +var calledRun; + +dependenciesFulfilled = function runCaller() { + // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) + if (!calledRun) run(); + if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled +}; + +function callMain() { + assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); + assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); + + var entryFunction = _main; + + var argc = 0; + var argv = 0; + + try { + + var ret = entryFunction(argc, argv); + + // if we're not running an evented main loop, it's time to exit + exitJS(ret, /* implicit = */ true); + return ret; + } + catch (e) { + return handleException(e); + } +} + +function stackCheckInit() { + // This is normally called automatically during __wasm_call_ctors but need to + // get these values before even running any of the ctors so we call it redundantly + // here. + _emscripten_stack_init(); + // TODO(sbc): Move writeStackCookie to native to to avoid this. + writeStackCookie(); +} + +function run() { + + if (runDependencies > 0) { + return; + } + + stackCheckInit(); + + preRun(); + + // a preRun added a dependency, run will be called later + if (runDependencies > 0) { + return; + } + + function doRun() { + // run may have just been called through dependencies being fulfilled just in this very frame, + // or while the async setStatus time below was happening + if (calledRun) return; + calledRun = true; + Module['calledRun'] = true; + + if (ABORT) return; + + initRuntime(); + + preMain(); + + if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); + + if (shouldRunNow) callMain(); + + postRun(); + } + + if (Module['setStatus']) { + Module['setStatus']('Running...'); + setTimeout(function() { + setTimeout(function() { + Module['setStatus'](''); + }, 1); + doRun(); + }, 1); + } else + { + doRun(); + } + checkStackCookie(); +} + +function checkUnflushedContent() { + // Compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + // Normally we would not even include flush() at all, but in ASSERTIONS + // builds we do so just for this check, and here we see if there is any + // content to flush, that is, we check if there would have been + // something a non-ASSERTIONS build would have not seen. + // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 + // mode (which has its own special function for this; otherwise, all + // the code is inside libc) + var oldOut = out; + var oldErr = err; + var has = false; + out = err = (x) => { + has = true; + } + try { // it doesn't matter if it fails + flush_NO_FILESYSTEM(); + } catch(e) {} + out = oldOut; + err = oldErr; + if (has) { + warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the Emscripten FAQ), or make sure to emit a newline when you printf etc.'); + warnOnce('(this may also be due to not including full filesystem support - try building with -sFORCE_FILESYSTEM)'); + } +} + +if (Module['preInit']) { + if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; + while (Module['preInit'].length > 0) { + Module['preInit'].pop()(); + } +} + +// shouldRunNow refers to calling main(), not run(). +var shouldRunNow = true; + +if (Module['noInitialRun']) shouldRunNow = false; + +run(); + + +// end include: postamble.js diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.wasm b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.wasm new file mode 100644 index 000000000000..ddae359f26a6 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/web/dwarf/fibonacci.wasm differ diff --git a/code/extensions/js-debug/testWorkspace/web/dwarf/readme.md b/code/extensions/js-debug/testWorkspace/web/dwarf/readme.md new file mode 100644 index 000000000000..c2617ba41724 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/dwarf/readme.md @@ -0,0 +1 @@ +These examples are a subset of those from https://github.com/bmeurer/emscripten-dbg-stories diff --git a/code/extensions/js-debug/testWorkspace/web/empty.js b/code/extensions/js-debug/testWorkspace/web/empty.js new file mode 100644 index 000000000000..55f42fe2d129 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/empty.js @@ -0,0 +1 @@ +"111111111111111111111111111111111111111111111111111" diff --git a/code/extensions/js-debug/testWorkspace/web/empty2.js b/code/extensions/js-debug/testWorkspace/web/empty2.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/web/frames.html b/code/extensions/js-debug/testWorkspace/web/frames.html new file mode 100644 index 000000000000..215b433eaf3c --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/frames.html @@ -0,0 +1,6 @@ +main + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/grandchild.html b/code/extensions/js-debug/testWorkspace/web/grandchild.html new file mode 100644 index 000000000000..18a7ac26bd38 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/grandchild.html @@ -0,0 +1,4 @@ +grandchild + + + diff --git a/code/extensions/js-debug/testWorkspace/web/hello.js b/code/extensions/js-debug/testWorkspace/web/hello.js new file mode 100644 index 000000000000..32cd681d1b12 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/hello.js @@ -0,0 +1,3 @@ +setInterval(() => { + console.log('boop'); +}, 100); diff --git a/code/extensions/js-debug/testWorkspace/web/iframe-1582/index.html b/code/extensions/js-debug/testWorkspace/web/iframe-1582/index.html new file mode 100644 index 000000000000..a27fd65bb3a9 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/iframe-1582/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/iframe-1582/inner.html b/code/extensions/js-debug/testWorkspace/web/iframe-1582/inner.html new file mode 100644 index 000000000000..e732d815f95a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/iframe-1582/inner.html @@ -0,0 +1,12 @@ + + + + + + + Document + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/iframe-1582/inner.js b/code/extensions/js-debug/testWorkspace/web/iframe-1582/inner.js new file mode 100644 index 000000000000..35ad40cf77e6 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/iframe-1582/inner.js @@ -0,0 +1,4 @@ +let i = 0; +setInterval(() => { + i++; +}, 50); diff --git a/code/extensions/js-debug/testWorkspace/web/index.html b/code/extensions/js-debug/testWorkspace/web/index.html new file mode 100644 index 000000000000..97645c23819d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/index.html @@ -0,0 +1,4 @@ + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/inlinescript.html b/code/extensions/js-debug/testWorkspace/web/inlinescript.html new file mode 100644 index 000000000000..f38a2f0d1ef3 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/inlinescript.html @@ -0,0 +1,7 @@ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/inlinescriptpause.html b/code/extensions/js-debug/testWorkspace/web/inlinescriptpause.html new file mode 100644 index 000000000000..d8eaefc19e07 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/inlinescriptpause.html @@ -0,0 +1,11 @@ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/logging.html b/code/extensions/js-debug/testWorkspace/web/logging.html new file mode 100644 index 000000000000..8ab01244f29e --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/logging.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/logging.js b/code/extensions/js-debug/testWorkspace/web/logging.js new file mode 100644 index 000000000000..c777b3aae5cf --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/logging.js @@ -0,0 +1,35 @@ +var foo = 1; +var bar = 'bar'; +var baz = 'baz'; + +function f() { + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; + 1+2; +} +f(); +f(); + +function g() { + 1+2; +} + +function z() { + return 4 + 3; +} + +z(); diff --git a/code/extensions/js-debug/testWorkspace/web/minified/.gitignore b/code/extensions/js-debug/testWorkspace/web/minified/.gitignore new file mode 100644 index 000000000000..d50251241e7b --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/minified/.gitignore @@ -0,0 +1,2 @@ +/node_modules +/package-lock.json diff --git a/code/extensions/js-debug/testWorkspace/web/minified/index.html b/code/extensions/js-debug/testWorkspace/web/minified/index.html new file mode 100644 index 000000000000..36ffeb09b6da --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/minified/index.html @@ -0,0 +1,5 @@ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/minified/index.js b/code/extensions/js-debug/testWorkspace/web/minified/index.js new file mode 100644 index 000000000000..14be90dac680 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/minified/index.js @@ -0,0 +1,15 @@ +function test() { + const outer = 1; + if (outer) { + const inner1 = 2; + const inner2 = 3; + hitDebugger(inner1, inner2); + } + + const later = 4; + hitDebugger(later); + + function hitDebugger(arg1, arg2) { + debugger; + } +} diff --git a/code/extensions/js-debug/testWorkspace/web/minified/index.min.js b/code/extensions/js-debug/testWorkspace/web/minified/index.min.js new file mode 100644 index 000000000000..cb8bbf91f01f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/minified/index.min.js @@ -0,0 +1,2 @@ +function test(){const n=1;if(n){const n=2;const t=3;c(n,t)}const t=4;c(t);function c(n,t){debugger}} +//# sourceMappingURL=index.min.js.map \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/minified/index.min.js.map b/code/extensions/js-debug/testWorkspace/web/minified/index.min.js.map new file mode 100644 index 000000000000..ecd8bff67f57 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/minified/index.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["index.js"],"names":["test","outer","inner1","inner2","hitDebugger","later","arg1","arg2"],"mappings":"AAAA,SAASA,OACP,MAAMC,EAAQ,EACd,GAAIA,EAAO,CACT,MAAMC,EAAS,EACf,MAAMC,EAAS,EACfC,EAAYF,EAAQC,GAGtB,MAAME,EAAQ,EACdD,EAAYC,GAEZ,SAASD,EAAYE,EAAMC,GACzB"} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/minified/package.json b/code/extensions/js-debug/testWorkspace/web/minified/package.json new file mode 100644 index 000000000000..da21b8125ce6 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/minified/package.json @@ -0,0 +1,8 @@ +{ + "scripts": { + "minify": "terser index.js -m -o index.min.js --source-map \"url=index.min.js.map\"" + }, + "dependencies": { + "terser": "^5.7.0" + } +} diff --git a/code/extensions/js-debug/testWorkspace/web/pathMapped/app.js b/code/extensions/js-debug/testWorkspace/web/pathMapped/app.js new file mode 100644 index 000000000000..97a06f2a6100 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/pathMapped/app.js @@ -0,0 +1,5 @@ +function foo() { + void (0); // break here +} +foo(); +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/pathMapped/app.js.map b/code/extensions/js-debug/testWorkspace/web/pathMapped/app.js.map new file mode 100644 index 000000000000..48b7626165b1 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/pathMapped/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"AAAA,SAAS,GAAG;IACV,KAAI,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa;AACxB,CAAC;AAED,GAAG,EAAE,CAAC"} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/pathMapped/app.ts b/code/extensions/js-debug/testWorkspace/web/pathMapped/app.ts new file mode 100644 index 000000000000..3c83e90b2750 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/pathMapped/app.ts @@ -0,0 +1,5 @@ +function foo() { + void(0); // break here +} + +foo(); diff --git a/code/extensions/js-debug/testWorkspace/web/pathMapped/index.html b/code/extensions/js-debug/testWorkspace/web/pathMapped/index.html new file mode 100644 index 000000000000..167c3b1d1337 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/pathMapped/index.html @@ -0,0 +1 @@ + diff --git a/code/extensions/js-debug/testWorkspace/web/pretty/pretty.html b/code/extensions/js-debug/testWorkspace/web/pretty/pretty.html new file mode 100644 index 000000000000..fb53238f3df7 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/pretty/pretty.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/pretty/ugly.js b/code/extensions/js-debug/testWorkspace/web/pretty/ugly.js new file mode 100644 index 000000000000..b1e76e707549 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/pretty/ugly.js @@ -0,0 +1,9 @@ +let i = 0; + +i++;i++;i++;i++;i++;i++;i++;i++;i++; + +console.log(i); + +i++;i++;i++;i++;i++;i++;i++;i++;i++; + +console.log(i); diff --git a/code/extensions/js-debug/testWorkspace/web/restart.html b/code/extensions/js-debug/testWorkspace/web/restart.html new file mode 100644 index 000000000000..41144ab36036 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/restart.html @@ -0,0 +1,5 @@ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/restart.js b/code/extensions/js-debug/testWorkspace/web/restart.js new file mode 100644 index 000000000000..0f599f087329 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/restart.js @@ -0,0 +1,8 @@ +let a = 0; + +(() => { + a++; + a++; + a++; +})(); + diff --git a/code/extensions/js-debug/testWorkspace/web/script-with-query-param.html b/code/extensions/js-debug/testWorkspace/web/script-with-query-param.html new file mode 100644 index 000000000000..0481cd9d7cf9 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/script-with-query-param.html @@ -0,0 +1,5 @@ + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/script.html b/code/extensions/js-debug/testWorkspace/web/script.html new file mode 100644 index 000000000000..39be7b0b3a6f --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/script.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/script.js b/code/extensions/js-debug/testWorkspace/web/script.js new file mode 100644 index 000000000000..85b569a2c3b1 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/script.js @@ -0,0 +1,11 @@ +function foo() { + bar(); +} + +function bar() { + console.log('here'); +} + +foo(3); +debugger; +foo(); diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/async.js b/code/extensions/js-debug/testWorkspace/web/smartStep/async.js new file mode 100644 index 000000000000..d4d65139983b --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/async.js @@ -0,0 +1,23 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +function foo() { + return __awaiter(this, void 0, void 0, function* () { + let x = 1; + return x + 3; + }); +} +function main() { + return __awaiter(this, void 0, void 0, function* () { + debugger; + const z = yield foo(); + }); +} +main(); +//# sourceMappingURL=async.js.map diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/async.js.map b/code/extensions/js-debug/testWorkspace/web/smartStep/async.js.map new file mode 100644 index 000000000000..df04eb5c7eca --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/async.js.map @@ -0,0 +1 @@ +{"version":3,"file":"test.js","sourceRoot":"","sources":["async.ts"],"names":[],"mappings":";;;;;;;;;AAAA,SAAe,GAAG;;QAChB,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;CAAA;AAED,SAAe,IAAI;;QACjB,QAAQ,CAAC;QACT,MAAM,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC;IACxB,CAAC;CAAA;AAED,IAAI,EAAE,CAAC"} diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/async.ts b/code/extensions/js-debug/testWorkspace/web/smartStep/async.ts new file mode 100644 index 000000000000..5b71f9ada589 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/async.ts @@ -0,0 +1,11 @@ +async function foo(): Promise { + let x = 1; + return x + 3; +} + +async function main(): Promise { + debugger; + const z = await foo(); +} + +main(); diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/directional.js b/code/extensions/js-debug/testWorkspace/web/smartStep/directional.js new file mode 100644 index 000000000000..4a8f7690c759 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/directional.js @@ -0,0 +1,11 @@ +"use strict"; +function mapped1() { + return 1; +} +function mapped2() { + return 2; +} +function doCall(fn) { + fn(); +} +//# sourceMappingURL=directional.js.map diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/directional.js.map b/code/extensions/js-debug/testWorkspace/web/smartStep/directional.js.map new file mode 100644 index 000000000000..77af2de74f2a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/directional.js.map @@ -0,0 +1 @@ +{"version":3,"file":"directional.js","sourceRoot":"","sources":["directional.ts"],"names":[],"mappings":";AAAA,SAAS,OAAO;IACd,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,OAAO;IACd,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,MAAM,CAAC,EAAc;IAC5B,EAAE,EAAE,CAAC;AACP,CAAC"} diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/exceptionBp.js b/code/extensions/js-debug/testWorkspace/web/smartStep/exceptionBp.js new file mode 100644 index 000000000000..e704a9f2aca8 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/exceptionBp.js @@ -0,0 +1,10 @@ +"use strict"; +function bar() { + foo(); +} +bar(); +//# sourceMappingURL=exceptionBp.js.map + +function foo() { + throw new Error('oh no!'); +} diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/exceptionBp.js.map b/code/extensions/js-debug/testWorkspace/web/smartStep/exceptionBp.js.map new file mode 100644 index 000000000000..171c36d025cd --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/exceptionBp.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["exceptionBp.ts"],"names":[],"mappings":";AAEA,SAAS,GAAG;IACV,GAAG,EAAE,CAAC;AACR,CAAC;AAED,GAAG,EAAE,CAAC"} diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/missingMap.js b/code/extensions/js-debug/testWorkspace/web/smartStep/missingMap.js new file mode 100644 index 000000000000..37cda06234f1 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/missingMap.js @@ -0,0 +1,4 @@ +function doCallback(callback) { + callback(); +} +//# sourceMappingURL=missingMap.js.map diff --git a/code/extensions/js-debug/testWorkspace/web/smartStep/tsconfig.json b/code/extensions/js-debug/testWorkspace/web/smartStep/tsconfig.json new file mode 100644 index 000000000000..7ca97c503f19 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/smartStep/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "es2015", + "sourceMap": true + } +} \ No newline at end of file diff --git a/code/extensions/js-debug/testWorkspace/web/stepInTargets.html b/code/extensions/js-debug/testWorkspace/web/stepInTargets.html new file mode 100644 index 000000000000..08501fb80cf1 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/stepInTargets.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/code/extensions/js-debug/testWorkspace/web/stepInTargets.js b/code/extensions/js-debug/testWorkspace/web/stepInTargets.js new file mode 100644 index 000000000000..6b1e482deae5 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/stepInTargets.js @@ -0,0 +1,6 @@ +function doTest() { + class Foo { bar() { return 42 } } + function identity(a) { return a } + identity(new Foo()) + identity(identity(new Foo().bar())) +} diff --git a/code/extensions/js-debug/testWorkspace/web/stringFormats.html b/code/extensions/js-debug/testWorkspace/web/stringFormats.html new file mode 100644 index 000000000000..a59ed2571b99 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/stringFormats.html @@ -0,0 +1,21 @@ + + + diff --git a/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.js b/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.js new file mode 100644 index 000000000000..06a6aa45538d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.js @@ -0,0 +1,12 @@ +"use strict"; +let i = 0; +i++; +debugger; +i++; +i++; +i++; +i++; +i++; +console.log(i); +//# sourceMappingURL=http://localhost:8001/urlSourcemap/index.js.map +//# sourceURL=http://localhost:8001/urlSourcemap/index.js diff --git a/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.js.map b/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.js.map new file mode 100644 index 000000000000..c624c32effc5 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":".","sources":["./index.ts"],"names":[],"mappings":";AAAA,IAAI,CAAC,GAAG,CAAC,CAAC;AACV,CAAC,EAAE,CAAC;AACJ,QAAQ,CAAC;AACT,CAAC,EAAE,CAAC;AACJ,CAAC,EAAE,CAAC;AACJ,CAAC,EAAE,CAAC;AACJ,CAAC,EAAE,CAAC;AACJ,CAAC,EAAE,CAAC;AACJ,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC"} diff --git a/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.ts b/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.ts new file mode 100644 index 000000000000..6238a88d5618 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/urlSourcemap/index.ts @@ -0,0 +1,9 @@ +let i = 0; +i++; +debugger; +i++; +i++; +i++; +i++; +i++; +console.log(i); diff --git a/code/extensions/js-debug/testWorkspace/web/vscode-204784/.gitignore b/code/extensions/js-debug/testWorkspace/web/vscode-204784/.gitignore new file mode 100644 index 000000000000..ee74419564f1 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/vscode-204784/.gitignore @@ -0,0 +1 @@ +!/dist diff --git a/code/extensions/js-debug/testWorkspace/web/vscode-204784/dist/index.js b/code/extensions/js-debug/testWorkspace/web/vscode-204784/dist/index.js new file mode 100644 index 000000000000..a055319ec12c --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/vscode-204784/dist/index.js @@ -0,0 +1,3 @@ +setInterval(() =>{},1000) +//# sourceURL=mapped://dist/index.js +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjb25zb2xlIiwibG9nIl0sInNvdXJjZXMiOlsibWFwcGVkOi8vc3JjL29yaWdpbmFsLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnNvbGUubG9nKCdoZWxsbyB3b3JsZCcpO1xuIl0sIm1hcHBpbmdzIjoiQUFBQUEsT0FBTyxDQUFDQyxHQUFHLENBQUMsYUFBYSxDQUFDIn0= diff --git a/code/extensions/js-debug/testWorkspace/web/vscode-204784/index.html b/code/extensions/js-debug/testWorkspace/web/vscode-204784/index.html new file mode 100644 index 000000000000..df2a62b3db1a --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/vscode-204784/index.html @@ -0,0 +1 @@ + diff --git a/code/extensions/js-debug/testWorkspace/web/vscode-204784/src/original.js b/code/extensions/js-debug/testWorkspace/web/vscode-204784/src/original.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/code/extensions/js-debug/testWorkspace/web/vue/favicon.ico b/code/extensions/js-debug/testWorkspace/web/vue/favicon.ico new file mode 100644 index 000000000000..df36fcfb7258 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/web/vue/favicon.ico differ diff --git a/code/extensions/js-debug/testWorkspace/web/vue/img/logo.82b9c7a5.png b/code/extensions/js-debug/testWorkspace/web/vue/img/logo.82b9c7a5.png new file mode 100644 index 000000000000..f3d2503fc2a4 Binary files /dev/null and b/code/extensions/js-debug/testWorkspace/web/vue/img/logo.82b9c7a5.png differ diff --git a/code/extensions/js-debug/testWorkspace/web/vue/index.html b/code/extensions/js-debug/testWorkspace/web/vue/index.html new file mode 100644 index 000000000000..c8f28304f41d --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/vue/index.html @@ -0,0 +1,17 @@ + + + + + + + + hello-vue + + + +
+ + + diff --git a/code/extensions/js-debug/testWorkspace/web/vue/js/app.js b/code/extensions/js-debug/testWorkspace/web/vue/js/app.js new file mode 100644 index 000000000000..a8854a26ce88 --- /dev/null +++ b/code/extensions/js-debug/testWorkspace/web/vue/js/app.js @@ -0,0 +1,818 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // install a JSONP callback for chunk loading +/******/ function webpackJsonpCallback(data) { +/******/ var chunkIds = data[0]; +/******/ var moreModules = data[1]; +/******/ var executeModules = data[2]; +/******/ +/******/ // add "moreModules" to the modules object, +/******/ // then flag all "chunkIds" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0, resolves = []; +/******/ for(;i < chunkIds.length; i++) { +/******/ chunkId = chunkIds[i]; +/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) { +/******/ resolves.push(installedChunks[chunkId][0]); +/******/ } +/******/ installedChunks[chunkId] = 0; +/******/ } +/******/ for(moduleId in moreModules) { +/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { +/******/ modules[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(parentJsonpFunction) parentJsonpFunction(data); +/******/ +/******/ while(resolves.length) { +/******/ resolves.shift()(); +/******/ } +/******/ +/******/ // add entry modules from loaded chunk to deferred list +/******/ deferredModules.push.apply(deferredModules, executeModules || []); +/******/ +/******/ // run deferred modules when all chunks ready +/******/ return checkDeferredModules(); +/******/ }; +/******/ function checkDeferredModules() { +/******/ var result; +/******/ for(var i = 0; i < deferredModules.length; i++) { +/******/ var deferredModule = deferredModules[i]; +/******/ var fulfilled = true; +/******/ for(var j = 1; j < deferredModule.length; j++) { +/******/ var depId = deferredModule[j]; +/******/ if(installedChunks[depId] !== 0) fulfilled = false; +/******/ } +/******/ if(fulfilled) { +/******/ deferredModules.splice(i--, 1); +/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]); +/******/ } +/******/ } +/******/ +/******/ return result; +/******/ } +/******/ +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // Promise = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ "app": 0 +/******/ }; +/******/ +/******/ var deferredModules = []; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/"; +/******/ +/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; +/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); +/******/ jsonpArray.push = webpackJsonpCallback; +/******/ jsonpArray = jsonpArray.slice(); +/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); +/******/ var parentJsonpFunction = oldJsonpFunction; +/******/ +/******/ +/******/ // add entry module to deferred list +/******/ deferredModules.push([0,"chunk-vendors"]); +/******/ // run deferred modules when ready +/******/ return checkDeferredModules(); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=script&lang=js&": +/*!*************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& ***! + \*************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _components_HelloWorld_vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./components/HelloWorld.vue */ "./src/components/HelloWorld.vue"); +// +// +// +// +// +// +// + +/* harmony default export */ __webpack_exports__["default"] = ({ + name: 'app', + components: { + HelloWorld: _components_HelloWorld_vue__WEBPACK_IMPORTED_MODULE_0__["default"] + } +}); +var foo = 42; +foo * foo; + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/HelloWorld.vue?vue&type=script&lang=js&": +/*!*******************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/HelloWorld.vue?vue&type=script&lang=js& ***! + \*******************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +/* harmony default export */ __webpack_exports__["default"] = ({ + name: 'HelloWorld', + props: { + msg: String + } +}); + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"10f897ea-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=template&id=7ba5bd90&": +/*!*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"10f897ea-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=7ba5bd90& ***! + \*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "render", function() { return render; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "staticRenderFns", function() { return staticRenderFns; }); +var render = function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c( + "div", + { attrs: { id: "app" } }, + [ + _c("img", { + attrs: { alt: "Vue logo", src: __webpack_require__(/*! ./assets/logo.png */ "./src/assets/logo.png") } + }), + _c("HelloWorld", { attrs: { msg: "Welcome to Your Vue.js App" } }) + ], + 1 + ) +} +var staticRenderFns = [] +render._withStripped = true + + + +/***/ }), + +/***/ "./node_modules/cache-loader/dist/cjs.js?{\"cacheDirectory\":\"node_modules/.cache/vue-loader\",\"cacheIdentifier\":\"10f897ea-vue-loader-template\"}!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/HelloWorld.vue?vue&type=template&id=469af010&scoped=true&": +/*!***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"10f897ea-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/HelloWorld.vue?vue&type=template&id=469af010&scoped=true& ***! + \***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "render", function() { return render; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "staticRenderFns", function() { return staticRenderFns; }); +var render = function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c("div", { staticClass: "hello" }, [ + _c("h1", [_vm._v(_vm._s(_vm.msg))]), + _vm._m(0), + _c("h3", [_vm._v("Installed CLI Plugins")]), + _vm._m(1), + _c("h3", [_vm._v("Essential Links")]), + _vm._m(2), + _c("h3", [_vm._v("Ecosystem")]), + _vm._m(3) + ]) +} +var staticRenderFns = [ + function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c("p", [ + _vm._v( + " For a guide and recipes on how to configure / customize this project," + ), + _c("br"), + _vm._v(" check out the "), + _c( + "a", + { + attrs: { + href: "https://cli.vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("vue-cli documentation")] + ), + _vm._v(". ") + ]) + }, + function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c("ul", [ + _c("li", [ + _c( + "a", + { + attrs: { + href: + "https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("babel")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: + "https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("eslint")] + ) + ]) + ]) + }, + function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c("ul", [ + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("Core Docs")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://forum.vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("Forum")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://chat.vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("Community Chat")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://twitter.com/vuejs", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("Twitter")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://news.vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("News")] + ) + ]) + ]) + }, + function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c("ul", [ + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://router.vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("vue-router")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://vuex.vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("vuex")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://github.com/vuejs/vue-devtools#vue-devtools", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("vue-devtools")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://vue-loader.vuejs.org", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("vue-loader")] + ) + ]), + _c("li", [ + _c( + "a", + { + attrs: { + href: "https://github.com/vuejs/awesome-vue", + target: "_blank", + rel: "noopener" + } + }, + [_vm._v("awesome-vue")] + ) + ]) + ]) + } +] +render._withStripped = true + + + +/***/ }), + +/***/ "./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=style&index=0&lang=css&": +/*!*******************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=style&index=0&lang=css& ***! + \*******************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +// Imports +var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js"); +exports = ___CSS_LOADER_API_IMPORT___(false); +// Module +exports.push([module.i, "\n#app {\n font-family: 'Avenir', Helvetica, Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-align: center;\n color: #2c3e50;\n margin-top: 60px;\n}\n", ""]); +// Exports +module.exports = exports; + + +/***/ }), + +/***/ "./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/HelloWorld.vue?vue&type=style&index=0&id=469af010&scoped=true&lang=css&": +/*!*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/HelloWorld.vue?vue&type=style&index=0&id=469af010&scoped=true&lang=css& ***! + \*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +// Imports +var ___CSS_LOADER_API_IMPORT___ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js"); +exports = ___CSS_LOADER_API_IMPORT___(false); +// Module +exports.push([module.i, "\nh3[data-v-469af010] {\n margin: 40px 0 0;\n}\nul[data-v-469af010] {\n list-style-type: none;\n padding: 0;\n}\nli[data-v-469af010] {\n display: inline-block;\n margin: 0 10px;\n}\na[data-v-469af010] {\n color: #42b983;\n}\n", ""]); +// Exports +module.exports = exports; + + +/***/ }), + +/***/ "./node_modules/vue-style-loader/index.js?!./node_modules/css-loader/dist/cjs.js?!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src/index.js?!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=style&index=0&lang=css&": +/*!*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/vue-style-loader??ref--6-oneOf-1-0!./node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--6-oneOf-1-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=style&index=0&lang=css& ***! + \*********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +// style-loader: Adds some css to the DOM by adding a \n","\n\n\n\n\n\n","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\n \"div\",\n { attrs: { id: \"app\" } },\n [\n _c(\"img\", {\n attrs: { alt: \"Vue logo\", src: require(\"./assets/logo.png\") }\n }),\n _c(\"HelloWorld\", { attrs: { msg: \"Welcome to Your Vue.js App\" } })\n ],\n 1\n )\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\nexport { render, staticRenderFns }","var render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", { staticClass: \"hello\" }, [\n _c(\"h1\", [_vm._v(_vm._s(_vm.msg))]),\n _vm._m(0),\n _c(\"h3\", [_vm._v(\"Installed CLI Plugins\")]),\n _vm._m(1),\n _c(\"h3\", [_vm._v(\"Essential Links\")]),\n _vm._m(2),\n _c(\"h3\", [_vm._v(\"Ecosystem\")]),\n _vm._m(3)\n ])\n}\nvar staticRenderFns = [\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"p\", [\n _vm._v(\n \" For a guide and recipes on how to configure / customize this project,\"\n ),\n _c(\"br\"),\n _vm._v(\" check out the \"),\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://cli.vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"vue-cli documentation\")]\n ),\n _vm._v(\". \")\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"ul\", [\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href:\n \"https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"babel\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href:\n \"https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"eslint\")]\n )\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"ul\", [\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"Core Docs\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://forum.vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"Forum\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://chat.vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"Community Chat\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://twitter.com/vuejs\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"Twitter\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://news.vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"News\")]\n )\n ])\n ])\n },\n function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"ul\", [\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://router.vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"vue-router\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://vuex.vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"vuex\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://github.com/vuejs/vue-devtools#vue-devtools\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"vue-devtools\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://vue-loader.vuejs.org\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"vue-loader\")]\n )\n ]),\n _c(\"li\", [\n _c(\n \"a\",\n {\n attrs: {\n href: \"https://github.com/vuejs/awesome-vue\",\n target: \"_blank\",\n rel: \"noopener\"\n }\n },\n [_vm._v(\"awesome-vue\")]\n )\n ])\n ])\n }\n]\nrender._withStripped = true\n\nexport { render, staticRenderFns }","// Imports\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.id, \"\\n#app {\\n font-family: 'Avenir', Helvetica, Arial, sans-serif;\\n -webkit-font-smoothing: antialiased;\\n -moz-osx-font-smoothing: grayscale;\\n text-align: center;\\n color: #2c3e50;\\n margin-top: 60px;\\n}\\n\", \"\"]);\n// Exports\nmodule.exports = exports;\n","// Imports\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.id, \"\\nh3[data-v-469af010] {\\n margin: 40px 0 0;\\n}\\nul[data-v-469af010] {\\n list-style-type: none;\\n padding: 0;\\n}\\nli[data-v-469af010] {\\n display: inline-block;\\n margin: 0 10px;\\n}\\na[data-v-469af010] {\\n color: #42b983;\\n}\\n\", \"\"]);\n// Exports\nmodule.exports = exports;\n","// style-loader: Adds some css to the DOM by adding a