diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..ec79a83 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,88 @@ +module.exports = { + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + root: true, + ignorePatterns: [ + "out", + "dist", + "**/*.d.ts", + ".eslintrc.cjs" + ], + rules: { + "@typescript-eslint/naming-convention": "error", + "@typescript-eslint/semi": "error", + "@typescript-eslint/no-unused-vars": "error", + "eqeqeq": "error", + "no-throw-literal": "error", + "semi": "warn", + "indent": ["error", "tab", { "SwitchCase": 1 }], + "no-unused-vars": "error", + "@typescript-eslint/member-ordering": "error", + "@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-var-requires": "error", + "@typescript-eslint/unified-signatures": "error", + "require-await": "error", + "no-return-await": "error", + "curly": "error", + "no-invalid-this": "error", + "@typescript-eslint/no-shadow": "error", + "no-fallthrough": "error", + "no-await-in-loop": "error", + "@typescript-eslint/array-type": "error", + "no-unreachable": "error", + "no-unsafe-negation": "error", + "no-unsafe-finally": "error", + "valid-jsdoc": "warn", + "valid-typeof": "error", + "consistent-return": "error", + "default-case": "error", + "no-multi-spaces": "warn", + "no-redeclare": "error", + "no-self-assign": "error", + "no-unused-labels": "error", + "no-useless-escape": "error", + "wrap-iife": "error", + "no-delete-var": "error", + "callback-return": "error", + "no-mixed-requires": "error", + "no-new-require": "error", + "no-path-concat": "error", + "no-process-exit": "error", + "no-sync": "error", + "comma-dangle": "warn", + "comma-style": "warn", + "computed-property-spacing": "warn", + "consistent-this": "warn", + "eol-last": "warn", + "func-name-matching": "warn", + "func-style": "warn", + "function-paren-newline": "warn", + "id-length": ["error", { "min": 2 }], + "jsx-quotes": "warn", + "key-spacing": "warn", + "keyword-spacing": "warn", + "linebreak-style": "warn", + "max-depth": "warn", + "max-len": ["warn", { "code": 150, "comments": 200 }], + "max-lines": ["warn", { "max": 1000}], + "max-nested-callbacks": "error", + "max-params": ["warn",{ "max": 8 }], + "new-cap": "warn", + "new-parens": "warn", + "no-bitwise": "warn", + "no-lonely-if": "warn", + "no-mixed-spaces-and-tabs": "warn", + "no-multiple-empty-lines": "warn", + "object-curly-newline": "warn", + "object-curly-spacing": ["warn", "always"], + "operator-linebreak": "warn", + "semi-style": "warn", + "sort-keys": "warn", + "require-jsdoc": "warn", + "no-useless-constructor": "warn", + "prefer-destructuring": "warn", + "no-var": "error", + "no-trailing-spaces": "error" + }, + }; diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..815b613 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @pyrsia/collaborators diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..194bbeb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,35 @@ +name: "❌ Bug report πŸ›" +description: Report errors bugs and unexpected behavior +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Please make sure to [search for existing issues](https://github.com/pyrsia/pyrsia-vscode-extension/issues) before creating a new report. + + If possible, upload an image of the case as well, it'd be useful for the maintainers. + + - type: textarea + attributes: + label: Steps taken/Steps to reproduce + placeholder: Tell us the steps required to experience the bug. + validations: + required: true + + - type: textarea + attributes: + label: Expected Behavior + description: If you want to include screenshots, paste them into the markdown editor below. + placeholder: What were you expecting? + validations: + required: false + + - type: textarea + attributes: + label: Experienced Instance of the bug + placeholder: | + VS Code Version: + Operating system: + Pyrsia IDE Extension SHA/Version: + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml new file mode 100644 index 0000000..270e7b1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yaml @@ -0,0 +1,22 @@ +name: "πŸ’‘ Feature suggestion βœ…" +description: Suggest new features and propose new ideas +labels: ["triage", "enhancement"] +body: + - type: markdown + attributes: + value: | + Please make sure to [search for existing suggestions](https://github.com/pyrsia/pyrsia-vscode-extension/issues) before creating a new feature-request. + + - type: textarea + attributes: + label: Suggestion details + placeholder: Tell us about the new suggestion + validations: + required: true + + - type: textarea + attributes: + label: Additional details + placeholder: Any additional details including images, links, media + validations: + required: false diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..5fb5bd3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,43 @@ + + +## Description + + + +Fixes pyrsia/pyrsia-vscode-extension# + +This PR does... by accomplishing... and it can be reviewed by... you can also test the changes by running... + +## Screenshots (optional) + +## PR Checklist + + + +- [ ] I've read the [contributing guidelines](https://github.com/pyrsia/.github/blob/main/contributing.md). +- [ ] I've read ["What is a Good PR?"](https://github.com/pyrsia/pyrsia/blob/main/docs/community/get_involved/good_pr.md) +- [ ] I've included a good title and brief description along with how to review them. +- [ ] I've linked any associated an [issue](https://github.com/pyrsia/pyrsia-vscode-extension/issues). + +### Code Contributions + + + +- [ ] I've run [pre-commit.sh](https://github.com/pyrsia/pyrsia-vscode-extension/blob/main/pre-commit.sh) successfully. diff --git a/.github/workflows/build_lint_test.yml b/.github/workflows/build_lint_test.yml new file mode 100644 index 0000000..02874e8 --- /dev/null +++ b/.github/workflows/build_lint_test.yml @@ -0,0 +1,25 @@ +name: Pyrsia VS Code Extension Merge Check (build, lint, test) + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + push: + branches: + - main +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 19 + - run: sudo apt-get install xvfb + - run: npm install + - run: npm run compile + - run: npm run lint + - run: xvfb-run --auto-servernum npm run test diff --git a/.gitignore b/.gitignore index 088ba6b..ff37e77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,5 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk +node_modules +.idea +out +.vscode-test +*.vsix diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7c4e2bd --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": [ + "${workspaceFolder}/out/test/**/*.js" + ] + }, + { + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "name": "Launch Extension", + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "preLaunchTask": "", + "request": "launch", + "type": "extensionHost", + "env": { + "VSCODE_DEBUG_MODE": "true" + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d3afbcf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "cSpell.words": [ + "callstack", + "codespaces", + "configfile", + "configurenode", + "dockerode", + "Pyrsia", + "pyrsiaintall", + "pyrsiaoverview", + "reportissue", + "VSIX", + "warningconnection", + "warningupdatenode", + "webviews" + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3968b63 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,13 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "group": "build", + "problemMatcher": [], + "label": "npm: watch", + "detail": "tsc -watch -p ./" + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..58ea8cc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Pyrsia VS Code Extension Changelog + +## [Unreleased] + +Not available in the VS Code store yet. + +## [0.1.0] - Jan 23th, 2023 + +* Initial release. Features: + * Views: + * Pyrsia node connection status. + * Integrations (Docker support). + * Help. + * New commands: + * Update Pyrsia node configuration. + * Update the docker config. + * Add docker image to Pyrsia (request build). + * Open the transparency log (docker images). + * Request to replace the local docker images with Pyrsia images. diff --git a/README.md b/README.md index a96c938..4067a9d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,84 @@ -# pyrsia-vscode-extension -vs code extension to be used with Pyrsia +# Pyrsia VS Code Extension + +## Overview + +Pyrsia support for Microsoft VS Code (extension).**This is an early prototype and proof of concept, it’s not "production" ready.** + +## Requirements + +- VS Code 1.7 +- Node 19.x required + +## How to run and debug the extension + +- Open the repo folder in VS Code. +- Install the dependencies and compile the extension. + + ```sh + npm install + npm run watch + ``` +- Open VS Code, in the Activity Bar select "Run and Debug" and make sure the "Lunch Extension" configuration is selected. +- Press F5 to run the Pyrsia extension (debug mode), a new VS Code instance will appear and should have the Pyrsia extension installed (should be shown in the Activity Bar). + +## How to test the project (and debug the tests) + +```sh +npm run test +``` + +To debug the tests, in the Activity Bar select "Run and Debug" and make sure the "Lunch Tests" configuration the active one, press F5 to start the tests. + +## Before merging or creating PR + +- Run the tests and linter. + + ```sh + npm run compile + npm run lint + npm run test + ``` + +## How to package, install and uninstall Pyrsia extension in the IDE + +The Pyrsia extension is not available in the VS Code store yet, it's necessary to manually install the extension as described below. + +### Package and Install (side-load extension) + +- Install [Visual Studio Code Extensions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#vsce) with the following command. + + ```sh + npm install -g @vscode/vsce + ``` + +- In the extension repository folder package the extension as follows. + + ```sh + vsce package + ``` + +- If the packaging was successful the last line of the VSCE logs should contain the `vsix` file path, for example: + + ```sh + DONE Packaged: /home/joed/repositories/pyrsia-vscode-extension/pyrsia-integration-0.0.1.vsix (960 files, 2.2MB) + ``` + +- Copy the `vsix` file path and install the extension as follows. + + ```sh + code --install-extension + ``` + +### Uninstall (side-load) + +- Find the extension in the list of the extensions (look for "pyrsia", for example `undefined_publisher.pyrsia-integration`). + + ```sh + code --list-extensions + ``` + +- Use the extension name from the list to uninstall the extension. + + ```sh + code --uninstall-extension + ``` diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f5b01f3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3001 @@ +{ + "name": "pyrsia-extension", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pyrsia-extension", + "version": "0.1.0", + "dependencies": { + "@vscode/webview-ui-toolkit": "^1.2.0", + "axios": "^1.2.1", + "dockerode": "3.3.4", + "fs": "^0.0.1-security", + "os": "^0.1.2" + }, + "devDependencies": { + "@types/dockerode": "^3.3.14", + "@types/glob": "^8.0.0", + "@types/mocha": "^10.0.1", + "@types/node": "16.x", + "@types/vscode": "^1.73.1", + "@typescript-eslint/eslint-plugin": "^5.47.0", + "@typescript-eslint/parser": "^5.47.0", + "@vscode/test-electron": "^2.2.0", + "eslint": "^8.30.0", + "glob": "^8.0.3", + "mocha": "^10.1.0", + "typescript": "^4.9.4" + }, + "engines": { + "vscode": "^1.74.0" + } + }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==" + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", + "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.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/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@microsoft/fast-element": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.11.0.tgz", + "integrity": "sha512-VKJYMkS5zgzHHb66sY7AFpYv6IfFhXrjQcAyNgi2ivD65My1XOhtjfKez5ELcLFRJfgZNAxvI8kE69apXERTkw==" + }, + "node_modules/@microsoft/fast-foundation": { + "version": "2.47.0", + "resolved": "https://registry.npmjs.org/@microsoft/fast-foundation/-/fast-foundation-2.47.0.tgz", + "integrity": "sha512-EyFuioaZQ9ngjUNRQi8R3dIPPsaNQdUOS+tP0G7b1MJRhXmQWIitBM6IeveQA6ZvXG6H21dqgrfEWlsYrUZ2sw==", + "dependencies": { + "@microsoft/fast-element": "^1.11.0", + "@microsoft/fast-web-utilities": "^5.4.1", + "tabbable": "^5.2.0", + "tslib": "^1.13.0" + } + }, + "node_modules/@microsoft/fast-react-wrapper": { + "version": "0.1.48", + "resolved": "https://registry.npmjs.org/@microsoft/fast-react-wrapper/-/fast-react-wrapper-0.1.48.tgz", + "integrity": "sha512-9NvEjru9Kn5ZKjomAMX6v+eF0DR+eDkxKDwDfi+Wb73kTbrNzcnmlwd4diN15ygH97kldgj2+lpvI4CKLQQWLg==", + "dependencies": { + "@microsoft/fast-element": "^1.9.0", + "@microsoft/fast-foundation": "^2.41.1" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, + "node_modules/@microsoft/fast-web-utilities": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@microsoft/fast-web-utilities/-/fast-web-utilities-5.4.1.tgz", + "integrity": "sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==", + "dependencies": { + "exenv-es6": "^1.1.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/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/docker-modem": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.2.tgz", + "integrity": "sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.14", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.14.tgz", + "integrity": "sha512-PUTwtySPzCbjZ/uqRMBWKHtLGqBAlhnLitzHuom19NEX0KBYsQXqbVlig+zbUgYQU1paDeQURXj7QNglh1RI6A==", + "dev": true, + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.0.0.tgz", + "integrity": "sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "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/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", + "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.18.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.10.tgz", + "integrity": "sha512-XU1+v7h81p7145ddPfjv7jtWvkSilpcnON3mQ+bDi9Yuf7OI56efOglXRyXWgQ57xH3fEQgh7WOJMncRHVew5w==", + "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/ssh2": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.11.6.tgz", + "integrity": "sha512-8Mf6bhzYYBLEB/G6COux7DS/F5bCWwojv/qFo2yH/e4cLzAavJnxvFXrYW59iKfXdhG6OmzJcXDasgOb/s0rxw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/vscode": { + "version": "1.74.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.74.0.tgz", + "integrity": "sha512-LyeCIU3jb9d38w0MXFwta9r0Jx23ugujkAxdwLTNCyspdZTKUc43t7ppPbCiPoQ/Ivd/pnDFZrb4hWd45wrsgA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz", + "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.47.0", + "@typescript-eslint/type-utils": "5.47.0", + "@typescript-eslint/utils": "5.47.0", + "debug": "^4.3.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.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.0.tgz", + "integrity": "sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.47.0", + "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/typescript-estree": "5.47.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.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz", + "integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/visitor-keys": "5.47.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.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz", + "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/utils": "5.47.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.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz", + "integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==", + "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.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz", + "integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/visitor-keys": "5.47.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.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz", + "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.47.0", + "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/typescript-estree": "5.47.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.47.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz", + "integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.47.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/@vscode/test-electron": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.1.tgz", + "integrity": "sha512-DUdwSYVc9p/PbGveaq20dbAAXHfvdq4zQ24ILp6PKizOBxrOfMsOq8Vts5nMzeIo0CxtA/RxZLFyDv001PiUSg==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/webview-ui-toolkit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.2.0.tgz", + "integrity": "sha512-3ai3B2iFK0myqSnEgK9JZd1nKJIR1zgOlQbwSqHS9Y15cyO0diyjsKzcDFCnuBI1UG4vmekWp+xWSTbrh36kLw==", + "dependencies": { + "@microsoft/fast-element": "^1.6.2", + "@microsoft/fast-foundation": "^2.38.0", + "@microsoft/fast-react-wrapper": "^0.1.18" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "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/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.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-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "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/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "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==", + "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/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "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/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "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/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/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/buildcheck": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.3.tgz", + "integrity": "sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "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": "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/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "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/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/chokidar/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/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "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/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cpu-features": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.4.tgz", + "integrity": "sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "0.0.3", + "nan": "^2.15.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "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/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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/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/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-h0Ow21gclbYsZ3mkHDfsYNDqtRhXS8fXr51bU0qr1dxgTMJj0XufbzX+jhNOvA8KuEEzn6JbvLVhXyv+fny9Uw==", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.11.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/docker-modem/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/dockerode": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.4.tgz", + "integrity": "sha512-3EUwuXnCU+RUlQEheDjmBE0B7q66PV9Rw5NiH1sXwINq0M9c5ERP9fxgkw36ZHOtzf4AGEEYySnkx/sACC9EgQ==", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "docker-modem": "^3.0.0", + "tar-fs": "~2.0.1" + }, + "engines": { + "node": ">= 8.0" + } + }, + "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/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "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/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/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-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.30.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", + "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.4.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "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.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "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", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "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.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "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.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "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/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "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/exenv-es6": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exenv-es6/-/exenv-es6-1.1.1.tgz", + "integrity": "sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==" + }, + "node_modules/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.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "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.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": { + "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/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/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "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==" + }, + "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": "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/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/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/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "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/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "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/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": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "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/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "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/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/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/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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==" + }, + "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/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-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-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-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/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "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-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "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-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/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/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, + "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/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/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==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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/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-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.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/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "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==" + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/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/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/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/mocha/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 + }, + "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/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "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.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz", + "integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ==" + }, + "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/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/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/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/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/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "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.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "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/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/readable-stream/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/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/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/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "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/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/rimraf/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/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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "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/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "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/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/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==" + }, + "node_modules/ssh2": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.11.0.tgz", + "integrity": "sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.4", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.4", + "nan": "^2.16.0" + } + }, + "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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/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==" + }, + "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/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/tabbable": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", + "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" + }, + "node_modules/tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + } + }, + "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==", + "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/tar-stream/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.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/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/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/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/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "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": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "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/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "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/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "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/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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/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/package.json b/package.json new file mode 100644 index 0000000..db06871 --- /dev/null +++ b/package.json @@ -0,0 +1,161 @@ +{ + "name": "pyrsia-extension", + "displayName": "Pyrsia", + "description": "Pyrsia VS Code Extension", + "version": "0.1.0", + "repository": { + "type": "git", + "url": "https://github.com/pyrsia/pyrsia-vscode-extension" + }, + "engines": { + "vscode": "^1.74.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "*" + ], + "main": "./out/extension.js", + "contributes": { + "viewsContainers": { + "activitybar": [ + { + "id": "pyrsia", + "title": "Pyrsia", + "icon": "resources/images/pyrsia_white.svg" + } + ] + }, + "views": { + "pyrsia": [ + { + "id": "pyrsia.node-config", + "name": "Node Status" + }, + { + "id": "pyrsia.node-integrations", + "name": "Integrations" + }, + { + "id": "pyrsia.help", + "name": "Help nad Feedback" + } + ] + }, + "commands": [ + { + "command": "pyrsia.docker.replace-images", + "title": "Reload Docker Images with Pyrsia", + "shortTitle": "Replace images", + "icon": "$(refresh)" + }, + { + "command": "pyrsia.node-config.update-url", + "title": "Update Pyrsia Node URL", + "shortTitle": "Update Pyrsia node URL", + "icon": "$(gear)" + }, + { + "command": "pyrsia.docker.update-config", + "title": "Open Docker Configuration", + "shortTitle": "Open and Update Docker Configuration", + "icon": "$(gear)" + }, + { + "command": "pyrsia.docker.request-build", + "title": "Add Image to Pyrsia", + "shortTitle": "Add selected image to Pyrsia", + "icon": "$(server-environment)" + }, + { + "command": "pyrsia.docker.open-trans-log", + "title": "Open Pyrsia Transparency Log", + "shortTitle": "Open Pyrsia Transparency Log", + "icon": "$(file-code)" + }, + { + "command": "pyrsia.integrations.update", + "title": "Refresh", + "shortTitle": "Refresh", + "icon": "$(refresh)" + }, + { + "command": "pyrsia.node-config.update-view", + "title": "Refresh", + "shortTitle": "Refresh", + "icon": "$(refresh)" + } + ], + "menus": { + "view/title": [ + { + "command": "pyrsia.integrations.update", + "when": "view == pyrsia.node-integrations", + "group": "navigation" + }, + { + "command": "pyrsia.node-config.update-view", + "when": "view == pyrsia.node-config", + "group": "navigation" + } + ], + "view/item/context": [ + { + "command": "pyrsia.docker.replace-images", + "when": "view == pyrsia.node-integrations && viewItem == pyrsia.docker", + "group": "pyrsia-integration" + }, + { + "command": "pyrsia.node-config.update-url", + "when": "view == pyrsia.node-config", + "group": "pyrsia-config" + }, + { + "command": "pyrsia.docker.update-config", + "when": "view == pyrsia.node-integrations && viewItem == pyrsia.docker.config-file", + "group": "pyrsia-integration" + }, + { + "command": "pyrsia.docker.request-build", + "when": "view == pyrsia.node-integrations && viewItem == pyrsia.docker.not-pyrsia", + "group": "pyrsia-integration" + }, + { + "command": "pyrsia.docker.open-trans-log", + "when": "view == pyrsia.node-integrations && viewItem == pyrsia.docker.is-pyrsia", + "group": "pyrsia-integration" + } + ] + } + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile && npm run lint", + "lint": "eslint src --ext ts", + "test": "node ./out/test/runTest.js" + }, + "devDependencies": { + "@types/dockerode": "^3.3.14", + "@types/glob": "^8.0.0", + "@types/mocha": "^10.0.1", + "@types/node": "16.x", + "@types/vscode": "^1.73.1", + "@typescript-eslint/eslint-plugin": "^5.47.0", + "@typescript-eslint/parser": "^5.47.0", + "@vscode/test-electron": "^2.2.0", + "eslint": "^8.30.0", + "glob": "^8.0.3", + "mocha": "^10.1.0", + "typescript": "^4.9.4" + }, + "dependencies": { + "@vscode/webview-ui-toolkit": "^1.2.0", + "axios": "^1.2.1", + "dockerode": "3.3.4", + "fs": "^0.0.1-security", + "os": "^0.1.2" + } +} diff --git a/pre-commit.sh b/pre-commit.sh new file mode 100755 index 0000000..1504210 --- /dev/null +++ b/pre-commit.sh @@ -0,0 +1,11 @@ +echo "Pre commit check." +echo "πŸ‘‹ Removing 'node_modules' and 'out' folder." +rm -rf ./node_modules ./out +echo "πŸ‘‹ Compiling the project" +npm install +npm run compile +echo "πŸ‘‹ Running the linter." +npm run lint +echo "πŸ‘‹ Running the tests." +npm run test +echo "Pre commit completed." diff --git a/resources/images/docker_small_dark.svg b/resources/images/docker_small_dark.svg new file mode 100644 index 0000000..4ebd26e --- /dev/null +++ b/resources/images/docker_small_dark.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/resources/images/pyrsia_white.svg b/resources/images/pyrsia_white.svg new file mode 100644 index 0000000..c33a89f --- /dev/null +++ b/resources/images/pyrsia_white.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/api/Integration.ts b/src/api/Integration.ts new file mode 100644 index 0000000..2d0fe5a --- /dev/null +++ b/src/api/Integration.ts @@ -0,0 +1,44 @@ +// https://github.com/xojs/eslint-config-xo-typescript/issues/43 +/* eslint-disable @typescript-eslint/naming-convention */ + +import * as vscode from 'vscode'; + +// Integration events +export enum Event { + IntegrationModelUpdate, + NodeConfigurationUpdate, +} + +/** +* Integration interface (e.g. DockerIntegration.ts, maven, etc). +* It allows plugging a new tree into and commands the integration view. +*/ +export interface Integration { + + /** + * Get a tree item for the provided tree item ID. + * @param {string} treeItemId tree item id + * @returns {IntegrationTreeItem} integration tree item + */ + getTreeItem(treeItemId: string): IntegrationTreeItem | undefined; + + /** + * Returns an array of children ids (treeItemId). + * @param {string} parentId + * @returns {string[]} array of children IDs (treeItemId) + */ + getTreeItemChildren(parentId?: string): string[]; + + /** + * This method is called by the external source and notifies the integration about a specific event. + * @param {Integration.Event} event + */ + update(event: Event): void; +} + +/** +* Integration Tree Item class +*/ +export abstract class IntegrationTreeItem extends vscode.TreeItem { + abstract update(context: never): void; +} diff --git a/src/api/NodeConfig.ts b/src/api/NodeConfig.ts new file mode 100644 index 0000000..11e0a6c --- /dev/null +++ b/src/api/NodeConfig.ts @@ -0,0 +1,38 @@ + +/** + * This interface is used to define most of the configs related to the Pyrsia node. + * There is on implementation of this interface that can be used as example (NodeConfigImpl.ts) + */ +export interface NodeConfig { + + get defaultUrl(): URL; + + /** + * Getter, provides the node supported protocol (e.g http || https) + * @returns {string} protocol + */ + get protocol(): string; + + /** + * Getter, returns the host name as string (e.g 'localhost:7888'). + * @returns {string} host + */ + get host(): string; + + /** + * Getter, returns Pyrsia nodes's URL. + * @returns {URL} url + */ + get url(): URL; + + /** + * Setter, used to set node's url. + * @param {URL | string | undefined} url + */ + set url(nodeUrl: URL | string | undefined); + + /** + * Getter, returns the hostname but also always includes the supported protocol (e.g. http://localhost:7888) + */ + get hostWithProtocol(): string; +} diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..ab85913 --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,31 @@ +import * as vscode from "vscode"; +import { NodeConfigView } from "./views/NodeConfigView"; +// import { NodeStatusViewProvider } from "./webviews/NodeStatusView"; +import { IntegrationsView as IntegrationsView } from "./views/IntegrationsView"; +import { Util } from "./utilities/Util"; +import { HelpView } from "./views/HelpView"; +import { Integration } from "./api/Integration"; +import { DockerIntegration } from "./integrations/DockerIntegration"; + +export const activate = (context: vscode.ExtensionContext) => { + // Init the extension utils + Util.init(context); + + // Create docker integration + const dockerIntegration: Integration = new DockerIntegration(context); + + // Create the node config view + const nodeConfigView = new NodeConfigView(context); + nodeConfigView.addIntegration(dockerIntegration); + + // Create the integrations view + const integrationView = new IntegrationsView(context); + integrationView.addIntegration(dockerIntegration); + + // Create Help view + new HelpView(context); +}; + +export const deactivate = () => { + console.debug("Pyrsia extension deactivated"); +}; diff --git a/src/integrations/DockerIntegration.ts b/src/integrations/DockerIntegration.ts new file mode 100644 index 0000000..0ecb71b --- /dev/null +++ b/src/integrations/DockerIntegration.ts @@ -0,0 +1,571 @@ +/* eslint-disable @typescript-eslint/member-ordering */ +import * as os from "os"; +import { Event, Integration, IntegrationTreeItem } from "../api/Integration"; +import * as path from 'path'; +import { Util } from "../utilities/Util"; +import * as vscode from "vscode"; +import * as client from "../utilities/pyrsiaClient"; +import { IntegrationsView } from "../views/IntegrationsView"; + +/** + * Implements Docker support for Pyrsia. + */ +export class DockerIntegration implements Integration { + //dialog options + static readonly confirmOption = "Yes"; + static readonly cancelOption = "No"; + static readonly closeOption = "Close"; + + // command Ids + static readonly updateDockerConfCommandId = "pyrsia.docker.update-config"; // NOI18N + static readonly reloadDockerImagesCommandId = "pyrsia.docker.replace-images"; // NOI18N + private static readonly openDockerTransparencyLog = "pyrsia.docker.open-trans-log"; // NOI18N + private static readonly integrationId: string = "pyrsia.docker"; // NOI18N + private static readonly requestBuildId: string = "pyrsia.docker.request-build"; // NOI18N + + // context values + static readonly imageNotPyrsiaContextValue = "pyrsia.docker.not-pyrsia"; // NOI18N + static readonly imagePyrsiaContextValue = "pyrsia.docker.is-pyrsia"; // NOI18N + static readonly imageUpdatingContextValue = "pyrsia.docker.updating"; // NOI18N + + // tree item ids + static readonly configFileItemIdPrefix: string = "pyrsia.docker.config-file"; + private static readonly imageItemIdPrefix: string = "pyrsia.docker.docker-image"; + private static readonly configItemId: string = "pyrsia.docker.configs"; + private static readonly imagesItemId: string = "pyrsia.docker.images"; + + // pre defined docker config files (this is where we look for the docker config) + private static dockerConfigPathsMap: Map = new Map(); + // Used in the docker configuration logic (the property we have to update) + private static readonly registryMirrorsName = "registry-mirrors"; + + // 'static' tree items props + private static readonly mainTreeItemName = "Docker"; + private static readonly configTreeItemName = "Configuration"; + private static readonly imagesTreeItemName = "Images"; + private static readonly warningIconPath: vscode.ThemeIcon = new vscode.ThemeIcon("warning"); // NOI18N + private readonly mainTreeItemIconPath: { light: string | vscode.Uri; dark: string | vscode.Uri; }; + + // this is where we store all of the tree items + private readonly treeItems: Map = new Map(); + + static { + // TODO add support for windows! + DockerIntegration.dockerConfigPathsMap.set(path.join(os.homedir(), ".docker"), "daemon.json"); + } + + constructor(context: vscode.ExtensionContext) { + // get the icon info for the main tree item (Docker) + this.mainTreeItemIconPath = { + dark: path.join(Util.getResourceImagePath(), "docker_small_dark.svg"), + light: path.join(Util.getResourceImagePath(), "docker_small_dark.svg") //TODO create the "light" icon + }; + + // create and add the "non-dynamic" docker tree items. + this.createBaseTreeItems(); + + // register the docker commands + this.registerCommands(context); + } + + /** + * Returns the image tree item ID (only docker images) + * @param {string} imageName - docker image name (tags) + * @returns {string} - tree item ID + */ + private static getTreeItemImageId(imageName: string): string { + return `${DockerIntegration.imageItemIdPrefix}.${imageName}`; + } + + /** + * This function replaces the local docker images and pulls them again, + * preferable from Pyrsia if properly configured and if available in Pyrsia. + * @return {Promise} void + */ + async replaceImagesWithPyrsia(): Promise { + // TODO This logic should check two conditions and fail if not met + // 1) If docker is configured to use Pyrsia node + // 2) If the node is listed as docker's proxy (registry) + + // get the local docker images + const dockerClient = Util.getDockerClient(); + const images = await dockerClient.listImages(); + const allContainers = await dockerClient.listContainers({ "all": true }); + for (const imageInfo of images) { + const imageName = imageInfo.RepoTags?.join(); + // no image tags? => skip it + if (!imageName) { + continue; + } + // only update the images which are available in Pyrsia + // eslint-disable-next-line no-await-in-loop + const transImageLog: [] = await client.getDockerTransparencyLog(imageName); + if (transImageLog.length > 0) { + // remove the old images first + try { + // get all Pyrsia images relevant containers + const containers = allContainers.filter((container) => { + return container.Image === imageName; + }); + + // if the image has containers go to hell (skip it and warn the user) + if (containers.length > 0) { + vscode.window.showErrorMessage( + `Reloading the '${imageName}' docker image + failed because it has container(s) attached, please remove the container(s) and try again.`, + DockerIntegration.closeOption + ); + continue; + } + + // delete the image first before pulling + dockerClient.getImage(imageInfo.Id).remove({ force: true }, (error) => { + if (error) { + // something went wrong, warn the user then go to the next image + vscode.window.showErrorMessage( + `Reloading the '${imageName}' docker image + failed, Error: ${error}`, + DockerIntegration.closeOption + ); + return; + } + // pull the deleted image and show the progress (icons in the Integrations view) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + dockerClient.pull(imageName, (err: string, stream: any) => { + console.debug(err); + + // this one is executed when the pulling is done + const onFinished = (error_: unknown, output: unknown) => { + if (error_) { + console.error(error_); + } + console.debug(output); + // delete the tree item representing the image, it will be recreated on the next update + this.treeItems.delete(DockerIntegration.getTreeItemImageId(imageName)); + // request the view (UI) update + IntegrationsView.requestIntegrationsUpdate(); + }; + + // this method is periodically called as the image is being pulled + const onProgress = (event: unknown) => { + console.debug(event); + const treeItem = this.treeItems.get(DockerIntegration.getTreeItemImageId(imageName)); + if (treeItem) { + treeItem.label = `Pulling '${imageName}'`; + treeItem.iconPath = DockerImageTreeItem.iconPathPullDocker; + treeItem.contextValue = DockerIntegration.imageUpdatingContextValue; + } + // request the model the view (UI) update + this.updateModel(true); + IntegrationsView.requestIntegrationsViewUpdate(); + }; + // request the view (UI) update + dockerClient.modem.followProgress(stream, onFinished, onProgress); + }); + }); + } catch (err) { + // pulling unsuccessfully, show the error (only in the debug mode) + Util.debugMessage(`Couldn't replace image: ${imageInfo.Labels}, error: ${err}`); + // request the view (UI) update + IntegrationsView.requestIntegrationsUpdate(); + } + } + } + } + + getTreeItemChildren(parentId?: string | undefined): string[] { + let children: string[] = []; + switch (parentId) { + case DockerIntegration.integrationId: + children.push(DockerIntegration.configItemId, DockerIntegration.imagesItemId); + break; + case DockerIntegration.configItemId: + // eslint-disable-next-line no-case-declarations + const configItems = [... this.treeItems].map(([, value]) => { + return value.id; + }).filter(id => id?.includes(DockerIntegration.configFileItemIdPrefix)); + children = children.concat((configItems as string[])); + break; + case DockerIntegration.imagesItemId: + // eslint-disable-next-line no-case-declarations + const imageItems = [... this.treeItems].map(([, value]) => { + return value.id; + }).filter(id => id?.includes(DockerIntegration.imageItemIdPrefix)); + children = children.concat((imageItems as string[])); + break; + default: + children.push(DockerIntegration.integrationId); + break; + } + + return children.sort(); + } + + getTreeItem(treeItemId: string): IntegrationTreeItem | undefined { + return this.treeItems.get(treeItemId); + } + + async update(event: Event): Promise { + switch (event) { + case Event.IntegrationModelUpdate: { + await this.updateModel(false); + break; + } + case Event.NodeConfigurationUpdate: { + this.treeItems.forEach((treeItem: vscode.TreeItem) => { + if (treeItem instanceof DockerConfigTreeItem) { + vscode.commands.executeCommand(DockerIntegration.updateDockerConfCommandId, treeItem.confFilePath); + } + }); + break; + } + default:{ + this.updateModel(false); + } + } + IntegrationsView.requestIntegrationsViewUpdate(); + } + + private async updateModel(pullingInProgress: boolean) { + // check if the docker and node is up + let isDockerUp = true; + try { + await Util.getDockerClient().ping(); + } catch (error) { + isDockerUp = false; + } + const isPyrsiaNodeUp = await client.isNodeHealthy(); + // update the Docker tree item in case Docker or node is down + const dockerTreeItem = this.treeItems.get(DockerIntegration.integrationId); + if (dockerTreeItem && (!isDockerUp || !isPyrsiaNodeUp)) { + // create warning tree item and hide the rest of the tree items + dockerTreeItem.label = `${DockerIntegration.mainTreeItemName} (Pyrsia Node or Docker is unavailable)`; + dockerTreeItem.tooltip = "Please make sure that Docker service and Pyrsia node is up and configured"; + dockerTreeItem.iconPath = DockerIntegration.warningIconPath; + dockerTreeItem.collapsibleState = vscode.TreeItemCollapsibleState.None; + dockerTreeItem.command = undefined; + dockerTreeItem.contextValue = undefined; + } else { + // recreate the docker tree item + this.createDockerTreeItem(); + + // find the docker conf file(s) (macos, linux). TODO Windows + for (const confPath of DockerIntegration.dockerConfigPathsMap.keys()) { + const fileName = DockerIntegration.dockerConfigPathsMap.get(confPath); + if (!fileName) { + throw new Error("Configuration file name cannot be null"); + } + Util.findFile(confPath, fileName).then((confFilePath) => { + if (confFilePath) { + const id = `${DockerIntegration.configFileItemIdPrefix}.${confFilePath}`; + const label = `${confFilePath}`; + this.treeItems.set( + id, + new DockerConfigTreeItem(label, id, confFilePath, vscode.TreeItemCollapsibleState.None) + ); + } else { + console.debug(`No configuration for 'Docker' - ${path.join(confPath, fileName)}`); + } + }); + } + + // get the local docker images + const dockerClient = Util.getDockerClient(); + const images = await dockerClient.listImages(); + const currentImages: string[] = []; + + for (const image of images) { + const imageName = image.RepoTags?.join(); + if (imageName?.startsWith("")) { + continue; + } + // no image tags? => skip the image + if (!imageName) { + continue; + } + const id = DockerIntegration.getTreeItemImageId(imageName); + const imageItem = new DockerImageTreeItem( + id, + imageName + ); + currentImages.push(id); + // check if image exists in pyrsia + // eslint-disable-next-line no-await-in-loop + const transImageLog: [] = await client.getDockerTransparencyLog(imageName); + imageItem.update({ pyrsia: transImageLog.length > 0 }); + + // add item to the tree items map + this.treeItems.set(id, imageItem); + } + + // remove deleted images + if (!pullingInProgress) { + for (const key of this.treeItems.keys()) { + // skip non image tree items + if (!key.startsWith(DockerIntegration.imageItemIdPrefix)) { + continue; + } + if (!currentImages.includes(key)) { + this.treeItems.delete(key); + } + } + } + IntegrationsView.requestIntegrationsViewUpdate(); + } + } + + private registerCommands(context: vscode.ExtensionContext) { + // docker command to open and update configuration editor command for the docker integration + const openDockerUpdateConfFile = vscode.commands.registerCommand( + DockerIntegration.updateDockerConfCommandId, + (args: DockerConfigTreeItem | string) => { + let confFilePath; + if (args instanceof DockerConfigTreeItem) { // arg as tree item + // eslint-disable-next-line prefer-destructuring + confFilePath = args.confFilePath; + } else if (typeof args === "string") { // arg as view item name (tree item name) + const treeItem = this.treeItems.get(args); + if (treeItem instanceof DockerConfigTreeItem) { + confFilePath = treeItem?.confFilePath; + } else { + console.error(`Docker update command - tree item not found ${args}`); + } + } else { + console.error(`Docker update command - tree item not found ${args}`); + } + const setting: vscode.Uri = vscode.Uri.parse(`${confFilePath}`); + vscode.workspace.openTextDocument(setting).then((textDocument: vscode.TextDocument) => { + // Get the docker config as JSON object + const dockerConfigJson = JSON.parse(textDocument.getText()); + // Get the current node configuration + const host = Util.getNodeConfig().hostWithProtocol; + // Check if the docker config has to be updated. + let registryMirrors: string[] = dockerConfigJson[DockerIntegration.registryMirrorsName]; + let updateConfig = false; + if (registryMirrors) { + updateConfig = !!registryMirrors.find((mirror: string) => { + return mirror.includes(host); + }); + } + vscode.window.showTextDocument(textDocument, 1, false).then(async (textEditor) => { + if (updateConfig) { + // the docker config doesn't have to be updated, exit + return; + } + const confirmOption = "Yes"; + const cancelOption = "No"; + const result = await vscode.window.showInformationMessage( + "Add Pyrsia to the Docker configuration?", + confirmOption, + cancelOption + ); + + if (result === confirmOption) { + textEditor.edit(edit => { + if (!registryMirrors) { // no mirrors found, add one for the pyrsia node + registryMirrors = []; + dockerConfigJson[DockerIntegration.registryMirrorsName] = registryMirrors; + } + // update document only when the docker config was updated + registryMirrors.push(host); + const updateDockerConfText = JSON.stringify(dockerConfigJson, null, 2); + edit.replace( + new vscode.Range( + textDocument.lineAt(0).range.start, + textDocument.lineAt(textDocument.lineCount - 1).range.end + ), + updateDockerConfText + ); + // select the changes + const hostStartLocation = updateDockerConfText.lastIndexOf(host); + textEditor.selection = new vscode.Selection( + new vscode.Position(hostStartLocation, hostStartLocation), + new vscode.Position(hostStartLocation, hostStartLocation + host.length) + ); + textDocument.save(); + vscode.window.showWarningMessage("Please restart Docker to apply the configuration changes."); + }); + } + }); + }, (error: unknown) => { + console.error(error); + }); + } + ); + + // docker command to open and update configuration editor command for the docker integration + const replaceDockerWithPyrsiaImages = vscode.commands.registerCommand( + DockerIntegration.reloadDockerImagesCommandId, + async () => { + const result = await vscode.window.showInformationMessage( + "πŸ‘‹ This operation will attempt to replace the local Docker images with images hosted by Pyrsia? Would you like to continue?", + DockerIntegration.confirmOption, + DockerIntegration.cancelOption + ); + + if (result === DockerIntegration.confirmOption) { + this.replaceImagesWithPyrsia(); + } + } + ); + + // request docker image build + const requestDockerImageBuild = vscode.commands.registerCommand( + DockerIntegration.requestBuildId, + async (id: string) => { + const treeItem = this.treeItems.get(id); + if (treeItem && typeof treeItem.label === "string") { + // ask if request the build + const result = await vscode.window.showInformationMessage( + `πŸ‘‹ Are you sure you'd like to add '${treeItem.label}' to Pyrsia?`, + DockerIntegration.confirmOption, + DockerIntegration.cancelOption + ); + + if (result === DockerIntegration.confirmOption) { + const buildId = await client.requestDockerBuild(treeItem.label); + let message; + if (buildId) { + message = `A request to add '${treeItem.label}' was successful, ID: ${buildId}`; + } else { + message = `A request to add '${treeItem.label}' was unsuccessful.`; + } + // show the result message + vscode.window.showInformationMessage(message); + } + } + } + ); + + // open docker image transparency log command + const openDockerTransparencyLog = vscode.commands.registerCommand( + DockerIntegration.openDockerTransparencyLog, + async (id: string) => { + const treeItem = this.treeItems.get(id); + if (treeItem instanceof DockerImageTreeItem) { + const transLogs: string[] = await client.getDockerTransparencyLog(treeItem.label); + if (!transLogs) { + return; + } + // parse and format the logs + const formattedTransLogsJson = JSON.stringify({ [treeItem.label]: transLogs }, null, 2); + // open and create a new editor and insert the transparency log + const textDocument = await vscode.workspace.openTextDocument({ content: formattedTransLogsJson, language: "json" }); + await vscode.window.showTextDocument(textDocument); + } else { + console.error(`Docker update command - tree item not found ${id}`); + return; + } + } + ); + + // subscribe the commands + context.subscriptions.push( + openDockerUpdateConfFile, // update docker file command + replaceDockerWithPyrsiaImages, // replace docker images with pyrsia images + requestDockerImageBuild, // request docker image build + openDockerTransparencyLog // opens docker image transparency log in editor + ); + } + + private createDockerTreeItem() { + // create "Docker" tree item + const dockerTreeItem = new DockerTreeItem(DockerIntegration.integrationId, "Docker", this.mainTreeItemIconPath); + dockerTreeItem.contextValue = DockerIntegration.integrationId; + this.treeItems.set(DockerIntegration.integrationId, dockerTreeItem); + } + + private createBaseTreeItems() { + // create "Docker" tree item + this.createDockerTreeItem(); + + // create Docker "Configuration" tree item + this.treeItems.set( + DockerIntegration.configItemId, + new DockerTreeItem( + DockerIntegration.configItemId, + DockerIntegration.configTreeItemName, + new vscode.ThemeIcon("gear") // NOI18N + ) + ); + + // create docker "Images" tree item + const imagesTreeItem = new DockerTreeItem( + DockerIntegration.imagesItemId, + DockerIntegration.imagesTreeItemName, + new vscode.ThemeIcon("folder-library") // NOI18N + ); + imagesTreeItem.contextValue = DockerIntegration.integrationId; + this.treeItems.set( + DockerIntegration.imagesItemId, + imagesTreeItem + ); + } +} + +// Docker Tree Item which represents the Docker configuration files +class DockerConfigTreeItem extends IntegrationTreeItem { + // icon (path) + private static iconPath: vscode.ThemeIcon = new vscode.ThemeIcon("go-to-file"); // NOI18N + + constructor( + public label: string, + public readonly id: string, + public readonly confFilePath: string, + public readonly collapsableState: vscode.TreeItemCollapsibleState + ) { + super(label, collapsableState); + + this.command = { + arguments: [this], + command: DockerIntegration.updateDockerConfCommandId, + title: "Open Docker Configuration File" + }; + + this.iconPath = DockerConfigTreeItem.iconPath; + this.contextValue = DockerIntegration.configFileItemIdPrefix; + } + + update(): void { + throw new Error("Method not implemented."); + } +} + +class DockerTreeItem extends IntegrationTreeItem { + + constructor( + public readonly id: string, + public readonly label: string, + public readonly iconPath?: { light: string | vscode.Uri; dark: string | vscode.Uri; } | vscode.ThemeIcon | undefined + ) { + super(label, vscode.TreeItemCollapsibleState.Expanded); + } + + update(): void { + console.debug("Nothing to update in Docker integration tree item"); + } +} + +class DockerImageTreeItem extends IntegrationTreeItem { + static readonly iconPathPullDocker: vscode.ThemeIcon = new vscode.ThemeIcon("sync"); // NOI18N + private static readonly defaultIconPath: vscode.ThemeIcon = new vscode.ThemeIcon("archive"); // NOI18N + readonly pyrsiaIconPath: { dark: string, light: string }; + + constructor( + public readonly id: string, + public readonly label: string + ) { + super(label, vscode.TreeItemCollapsibleState.None); + this.iconPath = DockerImageTreeItem.defaultIconPath; + this.pyrsiaIconPath = { + dark: path.join(Util.getResourceImagePath(), "pyrsia_white.svg"), //TODO update to dark + light: path.join(Util.getResourceImagePath(), "pyrsia_white.svg") //TODO update to light + }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + update(context: any): void { + this.iconPath = context.pyrsia ? this.pyrsiaIconPath : DockerImageTreeItem.defaultIconPath; + this.contextValue = context.pyrsia ? DockerIntegration.imagePyrsiaContextValue : DockerIntegration.imageNotPyrsiaContextValue; + } +} diff --git a/src/test/runTest.ts b/src/test/runTest.ts new file mode 100644 index 0000000..be466d0 --- /dev/null +++ b/src/test/runTest.ts @@ -0,0 +1,24 @@ +import * as path from 'path'; + +import { runTests } from '@vscode/test-electron'; + +const main = async () => { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../'); + + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, './suite/index'); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + console.error('Failed to run tests'); + // eslint-disable-next-line no-process-exit + process.exit(1); + } +}; + +main(); diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts new file mode 100644 index 0000000..65c1b8e --- /dev/null +++ b/src/test/suite/extension.test.ts @@ -0,0 +1,31 @@ +import * as assert from "assert"; +import * as vscode from "vscode"; +import { NodeConfig } from "../../api/NodeConfig"; +import { Util } from "../../utilities/Util"; + +// TODO more tests! +suite("Pyrsia Extension Test Suite", () => { + vscode.window.showInformationMessage("Start all tests."); + + test("Test if the extension is activated", () => { + const extension = vscode.extensions.getExtension("undefined_publisher.pyrsia-extension"); + assert.equal(extension?.isActive, true); + }); + + test("Test NodeConfig", () => { + const nodeConfig: NodeConfig = Util.getNodeConfig(); + // check if the config is created + assert(nodeConfig); + // check if has a value + assert(nodeConfig.url); + // set a new value + nodeConfig.url = nodeConfig.defaultUrl; + // check is the node url was correctly assigned + assert.equal(nodeConfig.url, nodeConfig.defaultUrl); + // check host + assert.equal(nodeConfig.host, nodeConfig.defaultUrl.href); + // check host with protocol + assert.equal(nodeConfig.hostWithProtocol, `${nodeConfig.protocol}://${nodeConfig.defaultUrl.href}`); + }); + +}); diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts new file mode 100644 index 0000000..66acd05 --- /dev/null +++ b/src/test/suite/index.ts @@ -0,0 +1,40 @@ +import * as path from 'path'; +import * as Mocha from 'mocha'; +import * as glob from 'glob'; + +export const run = (): Promise => { + // Create the mocha test + const mocha = new Mocha({ + color: true, + ui: 'tdd' + }); + + const testsRoot = path.resolve(__dirname, '..'); + + return new Promise((callback, er) => { + glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { + if (err) { + return er(err); + } + + // Add files to the test suite + files.forEach(file => mocha.addFile(path.resolve(testsRoot, file))); + + try { + // Run the mocha test + mocha.run(failures => { + if (failures > 0) { + er(new Error(`${failures} tests failed.`)); + } else { + // eslint-disable-next-line callback-return + callback(); + } + }); + } catch (error) { + console.error(error); + er(error); + } + return null; + }); + }); +}; diff --git a/src/utilities/Util.ts b/src/utilities/Util.ts new file mode 100644 index 0000000..86796ca --- /dev/null +++ b/src/utilities/Util.ts @@ -0,0 +1,177 @@ +import path = require("path"); +import * as DockerClient from "dockerode"; +import * as vscode from "vscode"; +import { NodeConfig } from "../api/NodeConfig"; +import { readdir } from "fs/promises"; + +/** + * Utility static method (don't create instances) + */ +export class Util { + private static resourcePath: string; + private static config: NodeConfig; + private static dockerClient: DockerClient; + + /** + * It's called once to pass the init values. + * @param {vscode.ExtensionContext} context - extension context + * @returns {void} + */ + public static init(context: vscode.ExtensionContext): Util { + if (this.config) { + throw new Error("Utils class is already initialized"); + } + // set the resource path + Util.resourcePath = context.asAbsolutePath(path.join('resources')); // NOI18N + // load the configuration from the context (context is used to store the node configuration - e.g URL) + this.config = new NodeConfigImpl(context.workspaceState); + + return this; + } + + /** + * Get the node configuration. + * @returns {NodeConfig} returns the node config + */ + public static getNodeConfig(): NodeConfig { + return Util.config; + } + + /** + * Returns the resource path. + * @returns {string} resource path folder path + */ + public static getResourcePath(): string { + return Util.resourcePath; + } + + /** + * Returns the image resource path folder + * @returns {string} image resource folder path + */ + public static getResourceImagePath(): string { + return path.join(Util.resourcePath, "images"); // NOI18N + } + + /** + * Returns docker client, at this point we only support '/var/run/docker.sock'. + * @returns {DockerClient} docker client + */ + public static getDockerClient(): DockerClient { + if (!this.dockerClient) { + //TODO The docker client should be configurable but for now we only support Docker Desktop. + const dockerConfig: DockerClient.DockerOptions = { socketPath: '/var/run/docker.sock' }; // NOI18N + this.dockerClient = new DockerClient(dockerConfig); + } + + return this.dockerClient; + } + + /** + * Checks if in the debug mode. + * @returns {boolean} Boolean true if in the debug mode + */ + public static isDebugMode(): boolean { + return process.env.VSCODE_DEBUG_MODE === "true"; + } + + /** + * It shows an error message (IDE notification). + * @param {string} message error message + * @returns {void} + */ + public static debugMessage(message: string): void { + if (this.isDebugMode()) { + vscode.window.showErrorMessage(message); + } + console.debug(message); + } + + /** + * Sleeps for the given amount of time. + * @param {number} milliseconds sf + * @returns {Promise} Promise + */ + public static sleep(milliseconds: number): Promise { + return new Promise((resolve) => setTimeout(resolve, milliseconds)); + } + + /** + * Searches for a file in the given dir recursively. + * @async + * @param {string} dir path + * @param {string} fileName searched filename + * @returns {string} file path | unknown + */ + public static async findFile(dir: string, fileName: string): Promise { + const dirFileNames = await readdir(dir); + let matchedFile: string | undefined = undefined; + for (const dirFileName of dirFileNames) { + if (dirFileName === fileName) { + matchedFile = path.join(dir, dirFileName); + break; + } + } + + return matchedFile; + } +} + +/** + * Private NodeConfig implementation. + */ +class NodeConfigImpl implements NodeConfig { + // the node supported protocol + private static readonly protocol = "http"; // NOI18N + // default node URL + private static readonly defaultNodeUrl = new URL("localhost:7888"); // NOI18N + // the configuration ket, it uses to store configuration in context.workspaceState + private static readonly nodeUrlKey: string = "PYRSIA_NODE_URL_KEY"; // NOI18N + + private nodeUrl: URL; + private workspaceState: vscode.Memento; + + constructor(workspaceState: vscode.Memento) { + this.workspaceState = workspaceState; + const nodeUrl: string | undefined = workspaceState.get(NodeConfigImpl.nodeUrlKey); + this.url = !nodeUrl ? this.defaultUrl : new URL(nodeUrl); + } + + get defaultUrl(): URL { + return NodeConfigImpl.defaultNodeUrl; + } + + get hostWithProtocol(): string { + let host = this.nodeUrl.href; + if (!host.toLocaleLowerCase().startsWith(NodeConfigImpl.protocol)) { + host = `${NodeConfigImpl.protocol}://${host}`; + } + + return host; + } + + get protocol(): string { + return NodeConfigImpl.protocol; + } + + get host(): string { + return this.nodeUrl.href; + } + + get url(): URL { + return this.nodeUrl; + } + + set url(nodeUrl: URL | string | undefined) { + if (!nodeUrl) { + console.warn(`The node config wasn't updated because the provided URL is ${nodeUrl}`); // NOI18N + return; + } + if (typeof nodeUrl === "string" ) { // NOI18N + this.nodeUrl = new URL(nodeUrl); + } else { + this.nodeUrl = nodeUrl || NodeConfigImpl.defaultNodeUrl; + } + this.workspaceState.update(NodeConfigImpl.nodeUrlKey, this.nodeUrl); + } +} diff --git a/src/utilities/pyrsiaClient.ts b/src/utilities/pyrsiaClient.ts new file mode 100644 index 0000000..a5441d4 --- /dev/null +++ b/src/utilities/pyrsiaClient.ts @@ -0,0 +1,123 @@ +import axios from "axios"; +import { Util } from "./Util"; + +// Methods to get various info from a Pyrsia node + +type PingResponse = { + data: string[]; +}; + +type StatusResponse = { + data: string[]; +}; + +type TransparencyLogResponse = { + data: string[]; +}; + +/** + * Checks if Pyrsia node is up + * @returns {Promise} 'true' if Pyrsia node is up + */ +export const isNodeHealthy = async (): Promise => { + console.debug('Check node health'); + const nodeUrl = `${Util.getNodeConfig().hostWithProtocol}/v2`; // NOI18N + let status; + try { + ({ status } = await axios.get( + nodeUrl, + { + headers: { + accept: 'application/json' // NOI18N + } + } + )); + } catch (error) { + console.error(error); + } + + return status === 200; +}; + +/** + * Returns Pyrsia node status. + * @returns {Promise} Returns the node status + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const getStatus = async (): Promise => { + console.debug('Get node status'); + const nodeUrl = `${Util.getNodeConfig().hostWithProtocol}/status`; + let data; + try { + ({ data } = await axios.get( + nodeUrl, + { + headers: { + accept: 'application/json' + } + } + )); + } catch (error) { + console.error(error); + } + + return data; +}; + +/** + * Returns Pyrsia Transparency Log. + * @param {string} imageName - docker image name + * @returns {Promise<[]>} docker transparency log + */ +export const getDockerTransparencyLog = async (imageName: string): Promise<[]> => { + console.debug(`Get docker image transparency log info for ${imageName}`); + const nodeUrl = `${Util.getNodeConfig().hostWithProtocol}/inspect/docker`; + let data; + + try { + ({ data } = await axios.post( + nodeUrl, + { + image: imageName + } + )); + } catch (error) { + throw new Error("getDockerTransparencyLog error" + error); + } + + return (data as unknown as []); +}; + +/** + * Request docker build (adds a new docker image to Pyrsia) + * @param {string} imageName docker image name (tags) + * @returns {Promise[]} - the build info (mostly the build ID) + */ +export const requestDockerBuild = async (imageName: string): Promise<[]> => { + console.debug(`Request build for docker image: ${imageName}`); + const nodeUrl = `${Util.getNodeConfig().hostWithProtocol}/build/docker`; + let data; + try { + ({ data } = await axios.post( + nodeUrl, + { + image: imageName + } + )); + } catch (error) { + throw new Error("requestDockerBuild error" + error); + } + + return (data as unknown as []); +}; + +/** + * Returns number of connected peers. + * @returns {Pyrsia} number of peers + */ +export const getPeers = async (): Promise => { + console.debug('Get node peers'); + const data = await getStatus(); + + return data ? data.peers_count : "0"; +}; diff --git a/src/views/HelpView.ts b/src/views/HelpView.ts new file mode 100644 index 0000000..5277db3 --- /dev/null +++ b/src/views/HelpView.ts @@ -0,0 +1,154 @@ +// https://github.com/xojs/eslint-config-xo-typescript/issues/43 +/* eslint-disable @typescript-eslint/naming-convention */ +import * as vscode from 'vscode'; + +// Help list enum +enum HelpProperty { + Install = "install", + Tutorials = "tutorials", + Overview = "overview", + Github = "github", + Issue = "issue", +} + +/** + * Help Util class, don't create instances of this class. + * TODO Do we need this one? + */ +export class HelpUtil { + public static readonly helpCommandId = "pyrsia.openHelpLink"; // NOI18N + public static readonly quickStartUrl = "https://pyrsia.io/docs/tutorials/quick-installation/"; // NOI18N +} + +/** + * Pyrsia Help view. + */ +export class HelpView { + private static readonly viewType: string = "pyrsia.help"; // NOI18N + private readonly treeViewProvider: HelpTreeProvider; + + constructor(context: vscode.ExtensionContext) { + this.treeViewProvider = new HelpTreeProvider(); + const view = vscode.window.createTreeView( + HelpView.viewType, + { showCollapseAll: true, treeDataProvider: this.treeViewProvider } + ); + vscode.window.registerTreeDataProvider(HelpView.viewType, this.treeViewProvider); + // create the open external help link command + const openHelpLink = vscode.commands.registerCommand(HelpUtil.helpCommandId, (helpUrl: string) => { + console.debug(`Open ${helpUrl} using '${HelpUtil.helpCommandId}'`); // NOI18N + vscode.env.openExternal(vscode.Uri.parse(helpUrl)); + }); + // register the open external help link command + context.subscriptions.push(openHelpLink); + // register the help view + context.subscriptions.push(view); + } +} + +/** + * Help tree provider. + */ +class HelpTreeProvider implements vscode.TreeDataProvider { + private treeItems: Map = new Map(); + + getTreeItem(id: string): vscode.TreeItem | Thenable { + const treeItem = this.treeItems.get(id); + if (!treeItem) { + throw new Error(`Tree item ${id} doesn't exist.`); + } + + return treeItem; + } + + getChildren(parentId?: string | undefined): vscode.ProviderResult { + let children: string[] = []; + if (!parentId) { // Create all tree Items for the tree + for (const nodeProperty in HelpProperty) { // TODO Why nodeProperty is 'string' type? Investigate + const treeItem = this.treeItems.get(nodeProperty.toLowerCase()); + if (!treeItem) { + // TODO Why I have to do this conversion in TS? Shouldn't 'nodeProperty' be the enum type? + const enumType = HelpProperty[nodeProperty as keyof typeof HelpProperty]; + // TODO Why I have to do this conversion in TS? Shouldn't 'nodeProperty' be the enum type? + this.treeItems.set(nodeProperty.toLocaleLowerCase(), HelpTreeItem.create(enumType)); + } + } + children = [... this.treeItems].map(([, value]) => { + return value.id; + }).filter(value => value !== ""); + } else { + const childId = HelpTreeItem.getChildrenId(parentId); + const treeItem: HelpTreeItem = this.treeItems.get(childId) as HelpTreeItem; + children = [treeItem.id]; + } + + return children; + } +} + +/** + * Help Tree Item + */ +class HelpTreeItem extends vscode.TreeItem { + + private static readonly properties = { + [HelpProperty.Install.toLowerCase()]: { + iconPath: new vscode.ThemeIcon("getting-started-beginner"), // NOI18N + id: "install", // NOI18N + name: "Pyrsia Quick Installation", + url: HelpUtil.quickStartUrl + }, + [HelpProperty.Overview.toLowerCase()]: { + iconPath: new vscode.ThemeIcon("open-editors-view-icon"), // NOI18N + id: "overview", // NOI18N + name: "Read Pyrsia Documentation", + url: "https://pyrsia.io/docs/" // NOI18N + }, + [HelpProperty.Tutorials.toLowerCase()]: { + iconPath: new vscode.ThemeIcon("play-circle"), // NOI18N + id: "tutorials", // NOI18N + name: "Watch Pyrsia Tutorials", + url: "https://www.youtube.com/@pyrsiaoss/playlists" // NOI18N + }, + [HelpProperty.Github.toLowerCase()]: { + iconPath: new vscode.ThemeIcon("github"), // NOI18N + id: "github", // NOI18N + name: "Get Involved", + url: "https://github.com/pyrsia" // NOI18N + }, + [HelpProperty.Issue.toLowerCase()]: { + iconPath: new vscode.ThemeIcon("remote-explorer-report-issues"), // NOI18N + id: "issue", // NOI18N + name: "Report Issue", + url: "https://github.com/pyrsia/pyrsia/issues" // NOI18N + } + }; + + constructor( + public label: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly id: string, + public readonly iconPath: vscode.ThemeIcon, + readonly helpUrl: string + ) { + super(label, collapsibleState); + this.command = { arguments: [helpUrl], command: HelpUtil.helpCommandId, title: "Open Pyrsia Help" }; + this.tooltip = this.label; + } + + static create(nodeProperty: HelpProperty): HelpTreeItem { + const property = this.properties[nodeProperty]; + const collapsibleState = vscode.TreeItemCollapsibleState.None; + return new HelpTreeItem( + property.name, + collapsibleState, + property.id, + property.iconPath, + property.url + ); + } + + static getChildrenId(parentId: string) { + return `${parentId}value`; + } +} diff --git a/src/views/IntegrationsView.ts b/src/views/IntegrationsView.ts new file mode 100644 index 0000000..621fd6a --- /dev/null +++ b/src/views/IntegrationsView.ts @@ -0,0 +1,151 @@ +/* eslint-disable @typescript-eslint/member-ordering */ +// https://github.com/xojs/eslint-config-xo-typescript/issues/43 +/* eslint-disable @typescript-eslint/naming-convention */ +import * as vscode from 'vscode'; +import { Integration, IntegrationTreeItem, Event } from '../api/Integration'; // NOI18N + +/** + * Integrations view. + */ +export class IntegrationsView { + private static readonly refreshIntegrationModelCommandId: string = "pyrsia.integrations.model.update"; // NOI18N + private static readonly refreshIntegrationViewCommandId: string = "pyrsia.integrations.view.update"; // NOI18N + private static readonly refreshIntegrationCommandId: string = "pyrsia.integrations.update"; // NOI18N + private static readonly viewType: string = "pyrsia.node-integrations"; // NOI18N + + private readonly treeViewProvider: IntegrationsTreeProvider; + private readonly _view?: vscode.TreeView; + + constructor(context: vscode.ExtensionContext) { + this.treeViewProvider = new IntegrationsTreeProvider(); + this._view = vscode.window.createTreeView( + IntegrationsView.viewType, + { showCollapseAll: true, treeDataProvider: this.treeViewProvider } + ); + this.treeViewProvider.update(); + + // register update model command + vscode.commands.registerCommand(IntegrationsView.refreshIntegrationModelCommandId, () => { + this.treeViewProvider.update(); + }); + + // register update view command + vscode.commands.registerCommand(IntegrationsView.refreshIntegrationViewCommandId, () => { + this.treeViewProvider.updateTreeView(); + }); + + // register command that update both - mode and view + vscode.commands.registerCommand(IntegrationsView.refreshIntegrationCommandId, () => { + this.treeViewProvider.update(); + this.treeViewProvider.updateTreeView(); + }); + + // triggered the update when view is shown + this._view.onDidChangeVisibility(() => { + this.treeViewProvider.update(); + }); + + // triggered the update on the selection change + this._view.onDidChangeSelection(() => { + this.treeViewProvider.update(); + }); + + context.subscriptions.push(this._view); + } + + /** + * Requests update view. + * @returns {void} + */ + static requestIntegrationsViewUpdate() { + vscode.commands.executeCommand(this.refreshIntegrationViewCommandId); + } + + /** + * Requests update view and model. + * @returns {void} + */ + static requestIntegrationsUpdate() { + vscode.commands.executeCommand(this.refreshIntegrationCommandId); + } + + /** + * Add an integration instance for the Integrations view (e.g. DockerIntegration, etc) + * @param {Integration} integration (e.g DockerIntegration) + * @returns {void} + */ + addIntegration(integration: Integration) { + this.treeViewProvider.addIntegration(integration); + } + + /** + * Triggers the tree provider update. + * @returns {Promise} void + */ + async update(): Promise { + if (this.treeViewProvider) { + await this.treeViewProvider.update(); + } + } +} + +/** + * Integration view tree provider. + */ +class IntegrationsTreeProvider implements vscode.TreeDataProvider { + // on change tree data + private _onDidChangeTreeData: vscode.EventEmitter = + new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + // integrations (e.g. DockerIntegration) + private readonly integrations: Set = new Set(); + + addIntegration(integration: Integration): void { + this.integrations.add(integration); + } + + getTreeItem(id: string): vscode.TreeItem | Thenable { + let treeItem: IntegrationTreeItem | undefined; + + for (const integration of this.integrations.values()) { + treeItem = integration.getTreeItem(id); + } + + if (!treeItem) { + throw new Error(`Tree item ${id} doesn't exist.`); + } + + return treeItem; + } + + getChildren(parentId?: string | undefined): vscode.ProviderResult { + console.debug(parentId); + let children: string[] = []; + for (const integration of this.integrations) { + children = children.concat(integration.getTreeItemChildren(parentId)); + } + + return children; + } + + update(): void { + for (const integration of this.integrations) { + integration.update(Event.IntegrationModelUpdate); + } + } + + resolveTreeItem?( + item: vscode.TreeItem, + element: string, + token: vscode.CancellationToken + ): vscode.ProviderResult { + console.debug(element); + console.debug(token); + return item; + } + + // fires the tree view update (UI) + updateTreeView(): void { + this._onDidChangeTreeData.fire(undefined); + } +} diff --git a/src/views/NodeConfigView.ts b/src/views/NodeConfigView.ts new file mode 100644 index 0000000..8092727 --- /dev/null +++ b/src/views/NodeConfigView.ts @@ -0,0 +1,315 @@ +// https://github.com/xojs/eslint-config-xo-typescript/issues/43 +/* eslint-disable @typescript-eslint/naming-convention */ +import * as vscode from "vscode"; +import * as client from "../utilities/pyrsiaClient"; +import { HelpUtil } from "./HelpView"; +import { Event, Integration } from "../api/Integration"; +import { IntegrationsView } from "./IntegrationsView"; +import { Util } from "../utilities/Util"; + +enum NodeConfigProperty { + Status = "status", // NOI18N + Peers = "peers", // NOI18N + WarningConnection = "warningconnection", // NOI18N + WarningUpdateNode = "warningupdatenode", // NOI18N +} + +/** + * Node Config view. + */ +export class NodeConfigView { + // Ids (view, commands) + public static readonly configNodeCommandId = "pyrsia.node-config.update-url"; // NOI18N + private static readonly viewType: string = "pyrsia.node-config"; // NOI18N + private static readonly updateViewCommandId = "pyrsia.node-config.update-view"; // NOI18N + + private readonly treeViewProvider: NodeConfigTreeProvider; + private readonly view: vscode.TreeView; + private readonly integrations: Set = new Set(); + + constructor(context: vscode.ExtensionContext) { + // create the view provider + this.treeViewProvider = new NodeConfigTreeProvider(); + // create the tree view + this.view = vscode.window.createTreeView( + NodeConfigView.viewType, + { showCollapseAll: true, treeDataProvider: this.treeViewProvider } + ); + // register the view provider + vscode.window.registerTreeDataProvider(NodeConfigView.viewType, this.treeViewProvider); + // subscribe the node config view + context.subscriptions.push(this.view); + // register the update view node (responsible for the view update on certain events) + vscode.commands.registerCommand(NodeConfigView.updateViewCommandId, () => { + this.update(); + this.notifyNodeConfigUpdated(); + }); + // update the view (UI/model) on certain view events + this.view.onDidChangeVisibility(() => { + this.update(); + this.treeViewProvider.update(); + }); + + // Add a command to update the Pyrsia node configuration (actually just URL) + const configureNodeCommand = vscode.commands.registerCommand( + NodeConfigView.configNodeCommandId, + async () => { + // the update node url input box + const options: vscode.InputBoxOptions = { + prompt: "Update the Pyrsia node address (e.g. localhost:7888)", + validateInput(value) { + let errorMessage: string | undefined; + console.debug(`Node configuration input: ${value}`); + if (!value.toLocaleLowerCase().startsWith(Util.getNodeConfig.prototype)) { + value = `${Util.getNodeConfig().protocol}://${value}`; + } + try { + new URL(value); + } catch (error) { + errorMessage = + "Incorrect Pyrsia node address, please provide a correct address (e.g localhost:7888)"; + } + + return errorMessage; + }, + value: Util.getNodeConfig().host + }; + + // show the url input box so the user can provide a new node address + const newNodeAddress: string | undefined = await vscode.window.showInputBox(options); + Util.getNodeConfig().url = newNodeAddress; + // update the view and the dependencies + this.update(); + // notify the integrations about the change + this.notifyNodeConfigUpdated(); + IntegrationsView.requestIntegrationsUpdate(); + } + ); + + context.subscriptions.push(configureNodeCommand); + + // trigger data and UI updates for the first time + setTimeout(() => { + this.update(); + }, 1000); + + // update the UI every minute + setInterval(() => { + this.update(); + }, 60000); + } + + // update the view + public update(): void { + this.treeViewProvider.update(); + client.isNodeHealthy().then((healthy) => { + healthy ? this.view.title = "NODE STATUS 🟩" : this.view.title = "NODE STATUS πŸŸ₯"; + }); + } + + // adds integration (mostly so there is a way to notify them about the changes) + public addIntegration(integration: Integration): void { + this.integrations.add(integration); + } + + // notify the integrations (e.g. Docker) about the changes + private notifyNodeConfigUpdated() { + for (const integration of this.integrations) { + integration.update(Event.NodeConfigurationUpdate); + integration.update(Event.IntegrationModelUpdate); + } + } +} + +// Tree data provider for the node config +class NodeConfigTreeProvider implements vscode.TreeDataProvider { + // update the tree on changes + private _onDidChangeTreeData: vscode.EventEmitter = + new vscode.EventEmitter(); + // eslint-disable-next-line @typescript-eslint/member-ordering + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + // tree items + private readonly treeItems: Map; + + constructor() { + this.treeItems = new Map(); + for (const nodeProperty in NodeConfigProperty) { + const treeItem = this.treeItems.get(nodeProperty.toLowerCase()); + if (!treeItem) { + // TODO Why I have to do this conversion in TS? Shouldn't 'nodeProperty' be the enum type? + const enumType = NodeConfigProperty[nodeProperty as keyof typeof NodeConfigProperty]; + // TODO Why I have to do this conversion in TS? Shouldn't 'nodeProperty' be the enum type? + this.treeItems.set(nodeProperty.toLocaleLowerCase(), NodeTreeItem.create(enumType)); + } + } + } + + // update the tree data + update() { + for (const nodeProperty in NodeConfigProperty) { + const treeItem = this.treeItems.get(nodeProperty.toLocaleLowerCase()); + if (treeItem) { + treeItem.update(); + } + } + + // refresh the tree + setTimeout(() => { + this._onDidChangeTreeData.fire(); + }, 1000); + } + + getTreeItem(id: string): vscode.TreeItem | Thenable { + const treeItem = this.treeItems.get(id); + if (!treeItem) { + throw new Error(`Tree item ${id} doesn't exist.`); + } + + return treeItem; + } + + getChildren(parentId?: string | undefined): vscode.ProviderResult { + let children: string[] = []; + if (!parentId) { + children = [... this.treeItems].map(([, value]) => { + return value.isRoot() ? value.id : ""; + }).filter(value => value !== ""); + } else { + const childId = NodeTreeItem.getChildrenId(parentId); + const treeItem: NodeTreeItem = this.treeItems.get(childId) as NodeTreeItem; + children = [treeItem.id]; + } + + return children; + } +} + +/** + * Node config tree item + */ +class NodeTreeItem extends vscode.TreeItem { + // tree item icons + private static readonly emptyIcon = new vscode.ThemeIcon("non-icon"); // NOI18N + private static readonly rightArrowIcon = new vscode.ThemeIcon("arrow-right"); // NOI18N + private static readonly cloudIcon = new vscode.ThemeIcon("cloud"); // NOI18N + private static readonly brokenConnectionIcon = new vscode.ThemeIcon("alert"); // NOI18N + private static readonly peersCountIcon = new vscode.ThemeIcon("extensions-install-count"); // NOI18N + + // Tree item properties and the logic to update it. + private static readonly properties = { + [NodeConfigProperty.Status.toLowerCase()]: { + iconPath: NodeTreeItem.cloudIcon, + id: "status", // NOI18N + listener: { + onUpdate: async (treeItem: NodeTreeItem) => { + const healthy: boolean = await client.isNodeHealthy(); + const { host } = Util.getNodeConfig(); + const status: string = healthy ? `Connected to Pyrsia node '${host}'` : `Failed connecting to Pyrsia node: '${host}'`; + treeItem.label = status; + treeItem.iconPath = healthy ? NodeTreeItem.cloudIcon : NodeTreeItem.brokenConnectionIcon; + treeItem.command = { command: NodeConfigView.configNodeCommandId, title: "Configure Pyrsia Node" }; + } + }, + name: "Status", + root: true + }, + [NodeConfigProperty.Peers.toLowerCase()]: { + iconPath: NodeTreeItem.peersCountIcon, + id: "peers", // NOI18N + listener: { + onUpdate: async (treeItem: NodeTreeItem) => { + const health = await client.isNodeHealthy(); + if (health) { + const peers = await client.getPeers(); + const { name } = NodeTreeItem.properties[NodeConfigProperty.Peers.toLowerCase()]; + treeItem.label = `${name}: ${peers.toString()}`; + treeItem.iconPath = NodeTreeItem.peersCountIcon; + } else { // don't show the item content is connection is broken + treeItem.label = ""; + treeItem.iconPath = NodeTreeItem.emptyIcon; + } + } + }, + name: "Node peers", + root: true + }, + [NodeConfigProperty.WarningConnection.toLowerCase()]: { + iconPath: NodeTreeItem.emptyIcon, + id: "warningconnection", // NOI18N + listener: { + onUpdate: async (treeItem: NodeTreeItem) => { + const healthy: boolean = await client.isNodeHealthy(); + treeItem.label = healthy ? "" : "πŸ‘‹ Read how to install and configure Pyrsia"; + treeItem.iconPath = healthy ? NodeTreeItem.emptyIcon : NodeTreeItem.rightArrowIcon; + treeItem.command = healthy ? undefined : { + arguments: [HelpUtil.quickStartUrl], + command: HelpUtil.helpCommandId, + title: "Open Pyrsia Help" + }; + } + }, + name: "", + root: true + }, + [NodeConfigProperty.WarningUpdateNode.toLowerCase()]: { + iconPath: NodeTreeItem.emptyIcon, + id: "warningupdatenode", // NOI18N + listener: { + onUpdate: async (treeItem: NodeTreeItem) => { + const healthy: boolean = await client.isNodeHealthy(); + treeItem.label = healthy ? "" : "πŸ‘‹ Update Pyrsia node configuration"; + treeItem.command = healthy ? undefined : { + command: NodeConfigView.configNodeCommandId, + title: "Configure Pyrsia Node" + }; + } + }, + name: "", + root: true + } + }; + + constructor( + public label: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly id: string, + public readonly root: boolean, + private readonly listener: NodeConfigListener, + public iconPath: vscode.ThemeIcon + ) { + super(label, collapsibleState); + this.tooltip = this.label; + } + + public static create(nodeProperty: NodeConfigProperty): NodeTreeItem { + const property = this.properties[nodeProperty]; + const collapsibleState = vscode.TreeItemCollapsibleState.None; + return new NodeTreeItem( + "Connecting to Pyrsia...", + collapsibleState, + property.id, + property.root, + property.listener, + property.iconPath + ); + } + + public static getChildrenId(parentId: string) { + return `${parentId}value`; + } + + public update() { + this.listener.onUpdate(this); + } + + public isRoot(): boolean { + return this.root; + } +} + +/** + * Node Config Tree Item update interface + */ +interface NodeConfigListener { + onUpdate(treeItem: NodeTreeItem): void; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..52eb29d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "strictPropertyInitialization": false, + "lib": [ + "ES2020" + ], + "sourceMap": true, + "rootDir": "src", + "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. */ + "forceConsistentCasingInFileNames": true, + } +}