├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── release-version.yml │ └── vscode-extension-ci.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── media ├── critical.svg ├── high.svg ├── informational.svg ├── low.svg ├── medium.svg ├── spectral-demo.gif ├── spectral.png └── spectral.svg ├── package.json ├── resources └── images │ └── spectral.svg ├── spectral.config.json ├── src ├── common │ ├── configuration.ts │ ├── constants.ts │ ├── persistence-context.ts │ ├── spectral-utils.ts │ ├── types.ts │ ├── utils.ts │ └── vs-code.ts ├── extension.ts ├── services │ ├── analytics-service.ts │ ├── context-service.ts │ ├── logger-service.ts │ ├── secret-storage-service.ts │ └── spectral-agent-service.ts ├── spectral │ ├── commands.ts │ ├── extension.ts │ ├── results-items.ts │ ├── results-view-decorations.ts │ └── results-view.ts └── test │ ├── mocks │ └── scan-results.mock.ts │ └── unit │ └── services │ └── spectral-agent-service.test.ts ├── tsconfig.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "parser": "@typescript-eslint/parser" 5 | }, 6 | "extends": [ 7 | "prettier" 8 | ], 9 | "plugins": [ 10 | "@typescript-eslint", 11 | "prettier" 12 | ], 13 | "rules": { 14 | "arrow-parens": 2, 15 | "no-param-reassign": 0, 16 | "@typescript-eslint/ban-ts-ignore": 0, 17 | "import/extensions": 0, 18 | "@typescript-eslint/camelcase": 0, 19 | "@typescript-eslint/no-var-requires": 0, 20 | "@typescript-eslint/type-annotation-spacing": 0, 21 | "import/prefer-default-export": 0, 22 | "class-methods-use-this": 0, 23 | "@typescript-eslint/explicit-member-accessibility": 0, 24 | "@typescript-eslint/explicit-function-return-type": 0, 25 | "@typescript-eslint/member-delimiter-style": 0, 26 | "@typescript-eslint/no-unused-vars": [ 27 | 2, 28 | { 29 | "argsIgnorePattern": "^_", 30 | "varsIgnorePattern": "^_" 31 | } 32 | ], 33 | "@typescript-eslint/indent": 0, 34 | "space-before-function-paren": 0, 35 | "comma-dangle": 0, 36 | "prettier/prettier": [ 37 | 2, 38 | { 39 | "semi": false, 40 | "singleQuote": true, 41 | "arrowParens": "always", 42 | "trailingComma": "es5", 43 | "endOfLine": "auto" 44 | } 45 | ] 46 | }, 47 | "env": { 48 | "es6": true 49 | } 50 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Expected Behavior 11 | 12 | 13 | ## Current Behavior 14 | 15 | 16 | ## Possible Solution 17 | 18 | 19 | ## Steps to Reproduce 20 | 21 | 1. 22 | 2. 23 | 3. 24 | 4. 25 | 26 | ## Context 27 | 28 | 29 | ## Specifications 30 | - Version: `$ vs code extension version` 31 | - Platform: 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Feature Request 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | 14 | 15 | **Describe the solution you'd like** 16 | 17 | 18 | **Describe alternatives you've considered** 19 | 20 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Related Issues 2 | 3 | 4 | ## Description 5 | 6 | 7 | ## Motivation and Context 8 | 9 | 10 | 11 | ## How Has This Been Tested? 12 | 13 | 14 | 15 | 16 | # Checklist 17 | - [ ] Tests 18 | - [ ] Documentation 19 | - [ ] Linting 20 | - [ ] Change log -------------------------------------------------------------------------------- /.github/workflows/release-version.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | custom_tag: 7 | description: "If not patch specifiy release tag" 8 | required: false 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | name: Publish extension 14 | outputs: 15 | new-version: ${{ steps.patched-tag.outputs.new_version }} 16 | new-tag: ${{ steps.patched-tag.outputs.new_tag }} 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: actions/setup-node@v1 20 | with: 21 | node-version: 16 22 | 23 | - name: Install dependencies 24 | run: yarn 25 | 26 | - name: Bump version 27 | id: patched-tag 28 | uses: mathieudutour/github-tag-action@v6.0 29 | with: 30 | github_token: ${{ secrets.GITHUB_TOKEN }} 31 | release_branches: main 32 | default_bump: patch 33 | custom_tag: ${{ github.event.inputs.custom_tag }} 34 | 35 | - name: Update package.json version 36 | run: jq '.version = "${{ steps.patched-tag.outputs.new_version }}"' package.json > tmp.package.json && mv tmp.package.json package.json 37 | 38 | - name: Add Credentials 39 | uses: cschleiden/replace-tokens@v1 40 | with: 41 | tokenPrefix: '{' 42 | tokenSuffix: '}' 43 | files: 'spectral.config.json' 44 | env: 45 | BUILD_MIX_PANEL_KEY: ${{ secrets.MIX_PANEL_KEY }} 46 | 47 | - name: Build 48 | run: yarn vscode:prepublish 49 | 50 | - name: Setup VSCE 51 | run: yarn global add @vscode/vsce@2.21.0 52 | 53 | - name: Publish to Marketplace 54 | run: vsce publish -p ${{ secrets.MARKETPLACE_TOKEN }} 55 | 56 | release: 57 | runs-on: ubuntu-latest 58 | needs: publish 59 | environment: Stable 60 | name: Release 61 | steps: 62 | - name: Fetch sources 63 | uses: actions/checkout@v2 64 | 65 | - name: Install dependencies 66 | run: yarn 67 | 68 | - name: Setup VSCE 69 | run: yarn global add vsce@2.11.0 70 | 71 | - name: Package VSIX 72 | run: echo y | vsce package 73 | 74 | - name: Extract release notes 75 | id: extract-release-notes 76 | uses: ffurrer2/extract-release-notes@v1 77 | 78 | - name: Create release 79 | uses: softprops/action-gh-release@v1 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | with: 83 | name: ${{ needs.publish.outputs.new-tag }} 84 | tag_name: ${{ needs.publish.outputs.new-tag }} 85 | body: "${{ steps.extract-release-notes.outputs.release_notes }}" 86 | draft: false 87 | prerelease: false 88 | fail_on_unmatched_files: true 89 | files: | 90 | **/*.vsix 91 | -------------------------------------------------------------------------------- /.github/workflows/vscode-extension-ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | SPECTRAL_DSN: ${{ secrets.SPECTRAL_DSN }} 5 | 6 | on: 7 | push: 8 | jobs: 9 | spectral-scan: 10 | name: Spectral Scan 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: spectralops/spectral-github-action@v2 15 | with: 16 | spectral-dsn: ${{ secrets.SPECTRAL_DSN }} 17 | spectral-args: scan --include-tags base,iac 18 | code-analysis: 19 | runs-on: ubuntu-latest 20 | container: 21 | image: sourceguard/sourceguard-cli 22 | steps: 23 | - name: SourceGuard Scan 24 | uses: CheckPointSW/sourceguard-action@v1.0.0 25 | continue-on-error: false 26 | with: 27 | SG_CLIENT_ID: ${{ secrets.SG_CLIENT_ID }} 28 | SG_SECRET_KEY: ${{ secrets.SG_SECRET_KEY }} 29 | - name: Detect done 30 | run: echo done 31 | tests: 32 | name: Tests 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v2 36 | - run: yarn && yarn test:unit 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | node_modules/ 3 | .vscode-test/ 4 | *.vsix 5 | tmp/ 6 | yarn-error.log 7 | .dccache 8 | .DS_Store -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--extensionDevelopmentPath=${workspaceFolder}", 26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 27 | ], 28 | "outFiles": [ 29 | "${workspaceFolder}/out/test/**/*.js" 30 | ], 31 | "preLaunchTask": "${defaultBuildTask}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | node_modules/** 5 | 6 | src/** 7 | .gitignore 8 | .yarnrc 9 | tsconfig.json 10 | .eslintrc.json 11 | 12 | README.md 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # SpectralOps - Automated Code Security Change Log 2 | ## [1.1.2] 3 | 4 | - Fix Spectral installtion notification bug 5 | ## [1.1.1] 6 | 7 | - Install Spectral from the extension 8 | - Auto update Spectral 9 | ## [1.1.0] 10 | 11 | - Support user configuration for 'engines' and 'includesTags' flags 12 | - Support OSS scanning 13 | 14 | ## [1.0.18] 15 | 16 | - Remove the usage of --ok flag 17 | - Reset issues before scanning workspace folders 18 | - fix action button bug 19 | 20 | ## [1.0.14] 21 | 22 | - Support spectral yaml 23 | 24 | ## [1.0.8] 25 | 26 | - Analytics event added 27 | 28 | ## [1.0.7] 29 | 30 | - initial release 31 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the SpectralOps VS code extension 2 | 3 | SpectralOps VS code extension is open source and we love to receive contributions from our community — you! 4 | 5 | There are many ways to contribute, 6 | from writing tutorials or blog posts, 7 | improving the documentation, 8 | submitting bug reports and feature requests or writing code. 9 | feedback and ideas are always welcome. 10 | 11 | ## Code contributions 12 | 13 | If you have a bugfix or new feature that you would like to contribute, 14 | please find or open an issue about it first. 15 | Talk about what you would like to do. 16 | It may be that somebody is already working on it, 17 | or that there are particular issues that you should know about before implementing the change. 18 | 19 | ### Submitting your changes 20 | 21 | Generally, we require that you test any code you are adding or modifying. 22 | Once your changes are ready , submit for review and we will make sure will review it , your effort is much appreciated! 23 | 24 | ### Workflow 25 | 26 | All feature development and most bug fixes hit the master branch first. 27 | Pull requests should be reviewed by someone with commit access. 28 | Once approved, the author of the pull request, 29 | or reviewer if the author does not have commit access, 30 | should "Squash and merge". 31 | 32 | ## Run extension and debug 33 | 34 | Clone the repository, then run `yarn` in the directory. 35 | 36 | - Open repository directory in VS Code run `yarn esbuild` and press `F5` to run extension in a new VS Code window. 37 | - This allows extension debugging within VS Code. 38 | - You can find output from your extension in the debug console and output channel. 39 | 40 | Please install all recommended extension that are suggested by VS Code when first opening the cloned directory. You can also do install them manually with the list of extensions defined in `.vscode/extensions.json`. This will ensure consistent formatting with the current codebase. 41 | 42 | ## Make changes 43 | 44 | Code changes require extension reload when run in debug. 45 | 46 | - You can relaunch the extension from the debug toolbar after changing code. 47 | - You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. 48 | 49 | ## Run tests and debug 50 | 51 | - Unit tests 52 | 53 | - Run `yarn test:unit` for a single execution. 54 | - Make sure to re-run the command to pick up new files, if new `**.test.ts` is added. 55 | 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 SpectralOps 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |
4 |

5 | 6 | SpectralOps logo 7 | 8 |

9 |

10 | 11 | 12 |

13 |

14 | 15 | # Spectral VS Code extension 16 | 17 | The Spectral VS Code extension is a tool for developers that want to catch security issues (such as credentials, tokens and IaC misconfigurations) while still coding. 18 | 19 |

20 | 21 |

22 | 23 | ## What is Spectral? 24 | 25 | Monitor, classify, and protect your code, assets, and infrastructure for exposed API keys, tokens, credentials, and high-risk IaC security misconfigurations simply, without noise. Spectral comes with an industry's leading detector coverage with over 2500 different detectors built-in, including machine learning based detectors. 26 | 27 | What this means in the context of a developer, working in VS Code, is that while you write your code, we're actively scanning it to make sure you don't accidentally enter sensitive data, which can be used against you if breached. 28 | 29 | Spectral scans your code locally, sending only metadata back to our servers. No actual data (like credentials or tokens we may find) is transmitted outside your computer. This ensures we're never going to be a part of a supply-chain attack. 30 | 31 | Read more about our mission statement [here](https://spectralops.io/). 32 | 33 | ## Install the extension 34 | 35 | After you've installed the extension, you'll see a new icon in the activity bar. 36 | 37 | First, you'll now need to download Spectral binary. Additionally, you'll need to fill in your Spectral DSN. The extension will guide you through those steps - read on to learn more. 38 | 39 | ## Configuration 40 | 41 | - Sign up and get your SpectralOps account [here.](https://get.spectralops.io/signup) If you already have an account, sign in and do the next step. 42 | - From Settings -> Organization, copy your DSN. 43 | - Set your DSN in the SpectralOps extension. 44 | - In the extension configuration set the engines you would like to run and tags to include. 45 | 46 | ## Usage 47 | 48 | - Open a workspace you wish to scan with the SpectralOps extension. 49 | - Click Scan now 50 | - Scan results should appear in the SpectralOps extension and your editor. 51 | 52 | #### Spectral DSN 53 | 54 | The Spectral DSN (Data Source Name) is your personal key to communicate with Spectral. While the extension does not transmit data to our servers, you still need a DSN for Spectral to operate. 55 | 56 | #### Spectral binary 57 | 58 | This extension requires the Spectral binary to be present and available. You can install it from the extension or by following the instructions in our docs. The extension will automatically update Spectral agent, if you wish to disable it you can do it from the extension configuration. 59 | 60 | ### How to Contribute 61 | 62 | We welcome [issues](https://github.com/SpectralOps/vscode-extension/issues) to and [pull requests](https://github.com/SpectralOps/vscode-extension/pulls) against this repository! 63 | 64 | ## License 65 | 66 | This project is licensed under the MIT License. See [LICENSE](LICENSE) for further details. 67 | 68 | 69 | -------------------------------------------------------------------------------- /media/critical.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /media/high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /media/informational.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /media/low.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /media/medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /media/spectral-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpectralOps/vscode-extension/f4e1ed7b1f363b8bcbfc9db69ef88b618a36c0f9/media/spectral-demo.gif -------------------------------------------------------------------------------- /media/spectral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpectralOps/vscode-extension/f4e1ed7b1f363b8bcbfc9db69ef88b618a36c0f9/media/spectral.png -------------------------------------------------------------------------------- /media/spectral.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spectral-checkpoint-vscode-extension", 3 | "displayName": "SpectralOps - A CheckPoint Solution", 4 | "description": "Monitor your code for exposed API keys, tokens, credentials, and high-risk security misconfigurations", 5 | "version": "1.1.2", 6 | "publisher": "CheckPoint", 7 | "icon": "media/spectral.png", 8 | "homepage": "https://spectralops.io/", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/SpectralOps/vscode-extension" 12 | }, 13 | "engines": { 14 | "vscode": "^1.59.0" 15 | }, 16 | "license": "MIT", 17 | "categories": [ 18 | "Linters", 19 | "Programming Languages", 20 | "Other" 21 | ], 22 | "keywords": [ 23 | "Code Analysis", 24 | "JavaScript", 25 | "Java", 26 | "TypeScript", 27 | "ts", 28 | "Vue", 29 | "Angular", 30 | "React", 31 | "Static Code Analysis", 32 | "Code security", 33 | "Python" 34 | ], 35 | "activationEvents": [ 36 | "onStartupFinished" 37 | ], 38 | "main": "./out/extension.js", 39 | "types": "./out/extension.d.ts", 40 | "contributes": { 41 | "commands": [ 42 | { 43 | "command": "spectral.scan", 44 | "title": "Spectral scan", 45 | "icon": "$(refresh)", 46 | "category": "Spectral" 47 | }, 48 | { 49 | "command": "spectral.install", 50 | "title": "Install Spectral" 51 | }, 52 | { 53 | "command": "spectral.showOutput", 54 | "title": "Show Output Channel" 55 | }, 56 | { 57 | "command": "spectral.setDsn", 58 | "title": "Spectral Set DSN", 59 | "icon": "$(settings-gear)", 60 | "category": "Spectral" 61 | } 62 | ], 63 | "viewsContainers": { 64 | "activitybar": [ 65 | { 66 | "id": "spectral", 67 | "title": "Spectral", 68 | "icon": "media/spectral.svg" 69 | } 70 | ] 71 | }, 72 | "views": { 73 | "spectral": [ 74 | { 75 | "id": "spectral.views.welcome", 76 | "name": "Spectral", 77 | "when": "spectral:preScan && !spectral:scanState" 78 | }, 79 | { 80 | "id": "spectral.views.scanState", 81 | "name": "", 82 | "when": "spectral:scanState && spectral:scanState == 'failed' || spectral:scanState == 'inProgress' && spectral:preScan" 83 | }, 84 | { 85 | "id": "spectral.views.secrets", 86 | "name": "Secrets", 87 | "when": "spectral:scanState == 'success' || spectral:scanState == 'inProgress' && !spectral:preScan" 88 | }, 89 | { 90 | "id": "spectral.views.iac", 91 | "name": "IaC", 92 | "when": "spectral:scanState == 'success' || spectral:scanState == 'inProgress' && !spectral:preScan" 93 | }, 94 | { 95 | "id": "spectral.views.oss", 96 | "name": "Open Source", 97 | "when": "spectral:scanState == 'success' || spectral:scanState == 'inProgress' && !spectral:preScan" 98 | } 99 | ] 100 | }, 101 | "viewsWelcome": [ 102 | { 103 | "view": "spectral.views.welcome", 104 | "contents": "Welcome to Spectral for Visual Studio Code. \nOpen a workspace or a folder to start scanning by clicking on the button below to perform a scan on your workspace. \n[Scan now](command:spectral.scan)\n", 105 | "when": "spectral:hasSpectralInstalled && spectral:hasDsn || spectral:hasLicense" 106 | }, 107 | { 108 | "view": "spectral.views.welcome", 109 | "contents": "Welcome to Spectral for Visual Studio Code. We noticed that Spectral is not installed on your machine. \n[Install Spectral](command:spectral.install)\n \nYou can also refer to our docs for more details on [how to install Spectral manually](https://guides.spectralops.io/docs/how-to-get-started)", 110 | "when": "!spectral:hasSpectralInstalled" 111 | }, 112 | { 113 | "view": "spectral.views.welcome", 114 | "contents": "Welcome to Spectral for Visual Studio Code.\n Grab your Spectral DSN from SpectralOps platform and set it here: \n [Set DSN](command:spectral.setDsn)\n", 115 | "when": "!spectral:hasDsn && !spectral:hasLicense && spectral:hasSpectralInstalled" 116 | }, 117 | { 118 | "view": "spectral.views.scanState", 119 | "contents": "Something went wrong, please refer to output for more details. \n [Show Output Channel](command:spectral.showOutput)\n", 120 | "when": "spectral:scanState == 'failed'" 121 | }, 122 | { 123 | "view": "spectral.views.scanState", 124 | "contents": "Spectral is performing a scan on your workspace", 125 | "when": "spectral:scanState == 'inProgress' && spectral:preScan" 126 | } 127 | ], 128 | "menus": { 129 | "view/title": [ 130 | { 131 | "command": "spectral.scan", 132 | "when": "view == spectral.views.secrets || view == spectral.views.scanState", 133 | "group": "navigation" 134 | }, 135 | { 136 | "command": "spectral.setDsn", 137 | "when": "view == spectral.views.secrets || view == spectral.views.welcome || view == spectral.views.scanState && spectral:hasSpectralInstalled || spectral:hasLicense", 138 | "group": "navigation" 139 | } 140 | ], 141 | "commandPalette": [ 142 | { 143 | "command": "spectral.scan", 144 | "when": "spectral:hasSpectralInstalled && spectral:hasDsn || spectral:hasLicense" 145 | }, 146 | { 147 | "command": "spectral.setDsn", 148 | "when": "spectral:hasSpectralInstalled" 149 | } 150 | ] 151 | }, 152 | "configuration": { 153 | "type": "object", 154 | "title": "Spectral", 155 | "properties": { 156 | "spectral.scan.engines.useSecretsEngine": { 157 | "type": "boolean", 158 | "default": true, 159 | "description": "Scan for secrets", 160 | "scope": "window" 161 | }, 162 | "spectral.scan.engines.useIacEngine": { 163 | "type": "boolean", 164 | "default": false, 165 | "description": "Scan for infrastructure as code", 166 | "scope": "window" 167 | }, 168 | "spectral.scan.engines.useOssEngine": { 169 | "type": "boolean", 170 | "default": false, 171 | "description": "Scan open source packages", 172 | "scope": "window" 173 | }, 174 | "spectral.scan.includeTags": { 175 | "type": "array", 176 | "items": { 177 | "type": "string" 178 | }, 179 | "maxItems": 100, 180 | "default": [], 181 | "description": "Scan include tags", 182 | "scope": "window" 183 | }, 184 | "spectral.install.autoUpdate": { 185 | "type": "boolean", 186 | "default": true, 187 | "description": "Auto update Spectral agent", 188 | "scope": "window" 189 | } 190 | } 191 | } 192 | }, 193 | "scripts": { 194 | "vscode:prepublish": "yarn esbuild-base --minify", 195 | "esbuild-base": "rimraf out && esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node", 196 | "esbuild": "yarn esbuild-base --sourcemap", 197 | "esbuild-watch": "yarn esbuild-base --sourcemap --watch", 198 | "build": "rimraf out && tsc -p ./", 199 | "pretest": "yarn run build && yarn run lint", 200 | "lint": "eslint src --ext ts", 201 | "test:unit": "yarn build && mocha --ui tdd -c 'out/src/test/unit/**/*.test.js'", 202 | "test:integration": "node ./out/test/integration/runTest.js" 203 | }, 204 | "devDependencies": { 205 | "@types/glob": "^7.1.3", 206 | "@types/lodash": "^4.14.184", 207 | "@types/mocha": "8.2.2", 208 | "@types/node": "14.x", 209 | "@types/vscode": "^1.59.0", 210 | "@typescript-eslint/eslint-plugin": "^5.37.0", 211 | "@typescript-eslint/parser": "^4.26.0", 212 | "esbuild": "^0.15.7", 213 | "eslint": "^8.44.0", 214 | "eslint-config-prettier": "^8.5.0", 215 | "eslint-plugin-prettier": "^4.2.1", 216 | "glob": "^7.1.7", 217 | "mocha": "^10.0.0", 218 | "prettier": "^2.7.1", 219 | "rimraf": "^3.0.2", 220 | "typescript": "^4.3.2" 221 | }, 222 | "dependencies": { 223 | "lodash": "^4.17.21", 224 | "mixpanel": "^0.17.0" 225 | } 226 | } -------------------------------------------------------------------------------- /resources/images/spectral.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /spectral.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mixPanelKey": "{BUILD_MIX_PANEL_KEY}" 3 | } -------------------------------------------------------------------------------- /src/common/configuration.ts: -------------------------------------------------------------------------------- 1 | import { WorkspaceConfiguration } from 'vscode' 2 | import { 3 | AUTO_UPDATE_SETTINGS, 4 | CONFIGURATION_IDENTIFIER, 5 | INCLUDE_TAGS_SETTING, 6 | ScanEngine, 7 | USE_IAC_ENGINE_SETTING, 8 | USE_OSS_ENGINE_SETTING, 9 | USE_SECRET_ENGINE_SETTING, 10 | } from './constants' 11 | 12 | export class Configuration { 13 | private extensionConfig: WorkspaceConfiguration 14 | static instance: Configuration 15 | constructor(workspace) { 16 | this.extensionConfig = workspace.getConfiguration(CONFIGURATION_IDENTIFIER) 17 | } 18 | 19 | public static getInstance(workspace?) { 20 | if (!this.instance) { 21 | this.instance = new Configuration(workspace) 22 | } 23 | return this.instance 24 | } 25 | 26 | get engines() { 27 | const engines = [] 28 | const useSecretsEngine = this.extensionConfig.get( 29 | USE_SECRET_ENGINE_SETTING 30 | ) 31 | if (useSecretsEngine) { 32 | engines.push(ScanEngine.secrets) 33 | } 34 | const useIacEngine = this.extensionConfig.get( 35 | USE_IAC_ENGINE_SETTING 36 | ) 37 | if (useIacEngine) { 38 | engines.push(ScanEngine.iac) 39 | } 40 | const useOssEngine = this.extensionConfig.get( 41 | USE_OSS_ENGINE_SETTING 42 | ) 43 | if (useOssEngine) { 44 | engines.push(ScanEngine.oss) 45 | } 46 | return engines.join(',') 47 | } 48 | 49 | get includeTags() { 50 | return this.extensionConfig 51 | .get>(INCLUDE_TAGS_SETTING, []) 52 | .join(',') 53 | } 54 | 55 | get isAutoUpdateEnabled() { 56 | return this.extensionConfig.get(AUTO_UPDATE_SETTINGS) 57 | } 58 | 59 | public updateConfiguration = (workspace) => { 60 | const extensionConfig = workspace.getConfiguration(CONFIGURATION_IDENTIFIER) 61 | this.extensionConfig = extensionConfig 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/common/constants.ts: -------------------------------------------------------------------------------- 1 | export const SPECTRAL_SCAN = 'spectral.scan' 2 | export const SPECTRAL_INSTALL = 'spectral.install' 3 | export const SPECTRAL_SHOW_OUTPUT = 'spectral.showOutput' 4 | export const SPECTRAL_SET_DSN = 'spectral.setDsn' 5 | export const CONTEXT_PREFIX = 'spectral:' 6 | export const HAS_SPECTRAL_INSTALLED = 'hasSpectralInstalled' 7 | export const HAS_DSN = 'hasDsn' 8 | export const HAS_LICENSE = 'hasLicense' 9 | export const SCAN_STATE = 'scanState' 10 | export const ENABLE_INSTALL_AGENT = 'enableInstallAgent' 11 | export const PRE_SCAN = 'preScan' 12 | export const SPECTRAL_VIEW_SECRETS = 'spectral.views.secrets' 13 | export const SPECTRAL_VIEW_IAC = 'spectral.views.iac' 14 | export const SPECTRAL_VIEW_OSS = 'spectral.views.oss' 15 | 16 | export enum ScanState { 17 | preScan = 'preScan', 18 | inProgress = 'inProgress', 19 | success = 'success', 20 | failed = 'failed', 21 | } 22 | 23 | export enum FindingSeverity { 24 | error = 'error', 25 | warning = 'warning', 26 | info = 'info', 27 | critical = 'critical', 28 | high = 'high', 29 | medium = 'medium', 30 | low = 'low', 31 | informational = 'informational', 32 | } 33 | 34 | export enum FindingSeverityLevel { 35 | critical = 1, 36 | high = 2, 37 | medium = 3, 38 | low = 4, 39 | informational = 5, 40 | } 41 | 42 | export enum FindingType { 43 | secret = 'secret', 44 | iac = 'iac', 45 | oss = 'oss', 46 | } 47 | 48 | export enum ScanEngine { 49 | secrets = 'secrets', 50 | iac = 'iac', 51 | oss = 'oss', 52 | } 53 | 54 | export const severityMapping = { 55 | [FindingSeverity.error]: FindingSeverity.high, 56 | [FindingSeverity.warning]: FindingSeverity.medium, 57 | [FindingSeverity.info]: FindingSeverity.informational, 58 | } 59 | 60 | export const SPECTRAL_FOLDER = `${process.env.HOME}/.spectral` 61 | export const SPECTRAL_DSN = 'SPECTRAL_DSN' 62 | export const FINDING_POSITION_LINE_INDEX = 0 63 | export const FINDING_POSITION_COL_INDEX = 1 64 | export const SPECTRAL_BASE_URL = 'https://get.spectralops.io' 65 | export const PLAYBOOKS_URL = `${SPECTRAL_BASE_URL}/api/v1/issues/playbooks` 66 | export const AGENT_LAST_UPDATE_DATE = 'spectral.agentLastUpdateDate' 67 | export const CONFIGURATION_IDENTIFIER = 'spectral' 68 | export const USE_IAC_ENGINE_SETTING = 'scan.engines.useIacEngine' 69 | export const USE_OSS_ENGINE_SETTING = 'scan.engines.useOssEngine' 70 | export const USE_SECRET_ENGINE_SETTING = 'scan.engines.useSecretsEngine' 71 | export const INCLUDE_TAGS_SETTING = 'scan.includeTags' 72 | export const AUTO_UPDATE_SETTINGS = 'install.autoUpdate' 73 | -------------------------------------------------------------------------------- /src/common/persistence-context.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | 3 | export class PersistenceContext { 4 | private context?: vscode.ExtensionContext 5 | private static instance: PersistenceContext 6 | 7 | public static getInstance() { 8 | if (!this.instance) { 9 | this.instance = new PersistenceContext() 10 | } 11 | 12 | return this.instance 13 | } 14 | 15 | setContext(context: vscode.ExtensionContext): void { 16 | this.context = context 17 | } 18 | 19 | getGlobalStateValue(key: string): T | undefined { 20 | return this.acquireContext().globalState.get(key) 21 | } 22 | 23 | updateGlobalStateValue(key: string, value: unknown): Thenable { 24 | return this.acquireContext().globalState.update(key, value) 25 | } 26 | 27 | private acquireContext(): vscode.ExtensionContext { 28 | if (!this.context) throw new Error('VS Code extension context not set.') 29 | return this.context 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/common/spectral-utils.ts: -------------------------------------------------------------------------------- 1 | import { Configuration } from './configuration' 2 | import { AGENT_LAST_UPDATE_DATE } from './constants' 3 | import { PersistenceContext } from './persistence-context' 4 | 5 | export const shouldUpdateSpectralAgent = (): boolean => { 6 | return ( 7 | isOverUpdateThreshold() && Configuration.getInstance().isAutoUpdateEnabled 8 | ) 9 | } 10 | 11 | const isOverUpdateThreshold = (): boolean => { 12 | const lastUpdateDate = 13 | PersistenceContext.getInstance().getGlobalStateValue( 14 | AGENT_LAST_UPDATE_DATE 15 | ) 16 | if (!lastUpdateDate) { 17 | return true 18 | } 19 | const oneWeekInMs = 7 * 24 * 3600 * 1000 20 | return Date.now() - lastUpdateDate > oneWeekInMs 21 | } 22 | -------------------------------------------------------------------------------- /src/common/types.ts: -------------------------------------------------------------------------------- 1 | import { FindingSeverity, FindingType } from './constants' 2 | 3 | export type LogLevel = 'Info' | 'Warn' | 'Error' | 'Debug' 4 | export type Findings = Record 5 | 6 | export type FindingsTypeResults = Record> 7 | 8 | export type FindingsAggregations = Record 9 | 10 | export type ScanResult = { 11 | items: Array 12 | } 13 | 14 | export interface ScanFinding { 15 | finding: string 16 | rule: Rule 17 | position: FindingPosition 18 | metadata: FindingMetadata 19 | } 20 | 21 | export interface ScanFindingView extends ScanFinding { 22 | rootPath: string 23 | labelDisplayName: string 24 | } 25 | 26 | type FindingMetadata = { 27 | tags: Array 28 | } 29 | 30 | export type Rule = { 31 | id: string 32 | name: string 33 | description: string 34 | severity: FindingSeverity 35 | } 36 | 37 | type FindingPosition = { 38 | start: Array 39 | end: Array 40 | } 41 | -------------------------------------------------------------------------------- /src/common/utils.ts: -------------------------------------------------------------------------------- 1 | export const formatWindowsPath = (path: string): string => { 2 | const stringToReplace = new RegExp(/\\/, 'g') 3 | return `/${path.replace(stringToReplace, '/')}` 4 | } 5 | 6 | export const isWindows = (): boolean => process.platform === 'win32' 7 | -------------------------------------------------------------------------------- /src/common/vs-code.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Diagnostic, 3 | DiagnosticSeverity, 4 | DiagnosticCollection, 5 | languages, 6 | DecorationRenderOptions, 7 | TextEditorDecorationType, 8 | window, 9 | TreeDataProvider, 10 | TreeView, 11 | StatusBarItem, 12 | StatusBarAlignment, 13 | Uri, 14 | workspace, 15 | Range, 16 | commands, 17 | } from 'vscode' 18 | import { 19 | CONTEXT_PREFIX, 20 | FindingSeverity, 21 | FINDING_POSITION_COL_INDEX, 22 | FINDING_POSITION_LINE_INDEX, 23 | PLAYBOOKS_URL, 24 | } from './constants' 25 | import { Findings, ScanFinding, ScanFindingView } from './types' 26 | 27 | export const setContext = (key: string, value: any): void => { 28 | commands.executeCommand('setContext', `${CONTEXT_PREFIX}${key}`, value) 29 | } 30 | 31 | export const getActiveTextEditor = () => window.activeTextEditor 32 | 33 | export const createTextDecoration = ( 34 | options: DecorationRenderOptions 35 | ): TextEditorDecorationType => window.createTextEditorDecorationType(options) 36 | 37 | export const ShowNotificationMessage = ({ 38 | messageType, 39 | messageText, 40 | items = [], 41 | }: { 42 | messageType: 'error' | 'warning' | 'info' 43 | messageText: string 44 | items?: Array 45 | }): Thenable => { 46 | switch (messageType) { 47 | case 'error': 48 | return window.showErrorMessage(messageText, ...items) 49 | case 'warning': 50 | return window.showWarningMessage(messageText, ...items) 51 | case 'info': 52 | return window.showInformationMessage(messageText, ...items) 53 | default: 54 | return window.showInformationMessage(messageText, ...items) 55 | } 56 | } 57 | 58 | type TreeOptions = { 59 | treeDataProvider: TreeDataProvider 60 | } 61 | 62 | export const createTree = ({ 63 | viewId, 64 | options, 65 | }: { 66 | viewId: string 67 | options: TreeOptions 68 | }): TreeView => { 69 | return window.createTreeView(viewId, options) 70 | } 71 | 72 | type StatusBarSettings = { 73 | location: 'right' | 'left' 74 | //The larger the number the more left the item will be 75 | priority: number 76 | text: string 77 | tooltip: string 78 | } 79 | 80 | export const createStatusBarItem = ( 81 | settings: StatusBarSettings 82 | ): StatusBarItem => { 83 | const item = window.createStatusBarItem( 84 | settings.location == 'right' 85 | ? StatusBarAlignment.Right 86 | : StatusBarAlignment.Left, 87 | settings.priority 88 | ) 89 | 90 | item.text = settings.text 91 | if (settings.tooltip) { 92 | item.tooltip = settings.tooltip 93 | } 94 | return item 95 | } 96 | 97 | export const runWithLoaderOnView = async ({ 98 | viewId, 99 | action, 100 | }: { 101 | viewId: string 102 | action: () => Promise 103 | }) => { 104 | await window.withProgress( 105 | { 106 | location: { viewId }, 107 | }, 108 | async () => { 109 | await action() 110 | } 111 | ) 112 | } 113 | 114 | type InputBoxOptions = { 115 | password: boolean 116 | placeHolder: string 117 | title: string 118 | } 119 | 120 | export const showInputBox = ( 121 | options: InputBoxOptions, 122 | task: (value) => Promise, 123 | inputValidation?: (value) => boolean, 124 | validationMessage?: string 125 | ) => { 126 | window 127 | .showInputBox({ 128 | password: options.password, 129 | placeHolder: options.placeHolder, 130 | title: options.title, 131 | validateInput(value) { 132 | if (inputValidation) { 133 | const isValid = inputValidation(value) 134 | if (!isValid) { 135 | return validationMessage 136 | } 137 | } 138 | return null 139 | }, 140 | }) 141 | .then(async (value) => { 142 | if (value) { 143 | await task(value) 144 | } 145 | }) 146 | } 147 | 148 | const severityToDiagnosticSeverity = { 149 | [FindingSeverity.critical]: DiagnosticSeverity.Error, 150 | [FindingSeverity.high]: DiagnosticSeverity.Error, 151 | [FindingSeverity.medium]: DiagnosticSeverity.Warning, 152 | [FindingSeverity.low]: DiagnosticSeverity.Warning, 153 | [FindingSeverity.informational]: DiagnosticSeverity.Information, 154 | } 155 | 156 | export const appendFindingsToProblemsChannel = ( 157 | findings: Findings 158 | ): DiagnosticCollection => { 159 | const collection = languages.createDiagnosticCollection('Spectral') 160 | 161 | const diagnosticItemsPerFile = Object.values(findings).reduce( 162 | (acc: Record, findingTypeItems) => { 163 | Object.entries(findingTypeItems).forEach( 164 | ([filePath, items]: [string, ScanFindingView[]]) => { 165 | const diagnosticItems = items.map((item: ScanFindingView) => { 166 | const diagnostic = createDiagnosticItem(item) 167 | diagnostic.code = { 168 | value: `Spectral ${item.rule.id}`, 169 | target: Uri.parse(`${PLAYBOOKS_URL}/${item.rule.id}`), 170 | } 171 | return diagnostic 172 | }) 173 | 174 | if (acc[filePath]) { 175 | acc[filePath] = [...acc[filePath], ...diagnosticItems] 176 | } else { 177 | acc[filePath] = diagnosticItems 178 | } 179 | } 180 | ) 181 | return acc 182 | }, 183 | {} 184 | ) 185 | 186 | const diagnosticCollection: Array<[Uri, Array]> = Object.entries( 187 | diagnosticItemsPerFile 188 | ).map(([filePath, diagnosticItems]) => [Uri.file(filePath), diagnosticItems]) 189 | 190 | collection.set(diagnosticCollection) 191 | return collection 192 | } 193 | 194 | const createDiagnosticItem = (finding: ScanFindingView): Diagnostic => { 195 | return new Diagnostic( 196 | getFindingRange(finding), 197 | `${finding.rule.name} - ${finding.rule.description}`, 198 | severityToDiagnosticSeverity[finding.rule.severity] 199 | ) 200 | } 201 | 202 | export const getWorkspaceFolders = (): Array => 203 | workspace.workspaceFolders?.map((folder) => folder.uri.fsPath) || [] 204 | 205 | export const getFindingRange = (finding: ScanFinding): Range => { 206 | const startRow = finding.position?.start[FINDING_POSITION_LINE_INDEX] || 0 207 | const startCol = finding.position?.start[FINDING_POSITION_COL_INDEX] || 0 208 | const endRow = finding.position?.end[FINDING_POSITION_LINE_INDEX] || 0 209 | const endCol = finding.position?.end[FINDING_POSITION_COL_INDEX] || 0 210 | const startLine = startRow > 0 ? startRow - 1 : 1 211 | const startChar = startCol <= 1 ? startCol : startCol - 1 212 | const endLine = endRow > 0 ? endRow - 1 : 1 213 | const endChar = endCol <= 1 ? endCol + 3 : endCol - 1 214 | return finding.position 215 | ? new Range(startLine, startChar, endLine, endChar) 216 | : null 217 | } 218 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | import { SpectralExtension } from './spectral/extension' 3 | 4 | const extension = new SpectralExtension() 5 | 6 | export function activate(context: vscode.ExtensionContext): void { 7 | console.log('Activating Spectral extension') 8 | extension.activate(context) 9 | } 10 | export function deactivate(): void { 11 | console.log('Deactivating Spectral extension') 12 | } 13 | -------------------------------------------------------------------------------- /src/services/analytics-service.ts: -------------------------------------------------------------------------------- 1 | import { LoggerService } from './logger-service' 2 | import Mixpanel from 'mixpanel' 3 | import spectralConfig from '../../spectral.config.json' 4 | 5 | export class AnalyticsService { 6 | private static mixpanel: Mixpanel.Mixpanel 7 | static init(): void { 8 | const logger = LoggerService.getInstance() 9 | try { 10 | if (!spectralConfig.mixPanelKey) { 11 | logger.warn('Analytics service not initialized - key not provided') 12 | return 13 | } 14 | 15 | this.mixpanel = Mixpanel.init(spectralConfig.mixPanelKey) 16 | } catch (error) { 17 | logger.warn(`Analytics service not initialized - ${error}`) 18 | } 19 | } 20 | 21 | static track(eventName: string, eventProperties: any = null): void { 22 | const logger = LoggerService.getInstance() 23 | try { 24 | this.mixpanel.track(eventName, eventProperties) 25 | } catch (error) { 26 | logger.warn(`Analytics service event not tracked - ${error}`) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/services/context-service.ts: -------------------------------------------------------------------------------- 1 | import { SCAN_STATE } from '../common/constants' 2 | import { setContext } from '../common/vs-code' 3 | 4 | export class ContextService { 5 | private readonly viewContext: { [key: string]: any } 6 | private static instance: ContextService 7 | constructor() { 8 | this.viewContext = {} 9 | } 10 | 11 | public static getInstance() { 12 | if (!this.instance) { 13 | this.instance = new ContextService() 14 | } 15 | 16 | return this.instance 17 | } 18 | 19 | public getContext(key: string): any { 20 | return this.viewContext[key] 21 | } 22 | 23 | public setContext(key: string, value: any): void { 24 | this.viewContext[key] = value 25 | setContext(key, value) 26 | } 27 | 28 | get scanState(): string { 29 | return this.viewContext[SCAN_STATE] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/services/logger-service.ts: -------------------------------------------------------------------------------- 1 | import { OutputChannel, window } from 'vscode' 2 | import { LogLevel } from '../common/types' 3 | 4 | export class LoggerService { 5 | private output: OutputChannel 6 | private static instance: LoggerService 7 | 8 | public static getInstance() { 9 | if (!this.instance) { 10 | this.instance = new LoggerService() 11 | } 12 | 13 | return this.instance 14 | } 15 | 16 | constructor() { 17 | this.output = window.createOutputChannel('Spectral') 18 | } 19 | 20 | info(message: string): void { 21 | this.log('Info', message) 22 | } 23 | 24 | warn(message: string): void { 25 | this.log('Warn', message) 26 | } 27 | 28 | error(message: string): void { 29 | this.log('Error', message) 30 | } 31 | 32 | debug(message: string): void { 33 | this.log('Debug', message) 34 | } 35 | 36 | log(level: LogLevel, message: string): void { 37 | if (level == 'Debug') { 38 | return console.log(message) 39 | } 40 | 41 | this.output.appendLine(`[${level}] ${message}`) 42 | } 43 | 44 | showOutput() { 45 | this.output.show() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/services/secret-storage-service.ts: -------------------------------------------------------------------------------- 1 | import { SecretStorage, ExtensionContext } from 'vscode' 2 | 3 | export default class SecretStorageService { 4 | private static instance: SecretStorageService 5 | constructor(private secretStorage: SecretStorage) {} 6 | static init(context: ExtensionContext): void { 7 | SecretStorageService.instance = new SecretStorageService(context.secrets) 8 | } 9 | 10 | static getInstance(): SecretStorageService { 11 | return SecretStorageService.instance 12 | } 13 | 14 | get(key: string): Promise { 15 | return this.secretStorage.get(key) as Promise 16 | } 17 | 18 | store(key: string, value: string): void { 19 | this.secretStorage.store(key, value) as Promise 20 | } 21 | 22 | delete(key: string): Promise { 23 | return this.secretStorage.delete(key) as Promise 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/services/spectral-agent-service.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from 'child_process' 2 | import { readFileSync, unlinkSync } from 'fs' 3 | import isEmpty from 'lodash/isEmpty' 4 | import path from 'path' 5 | import { 6 | FindingSeverity, 7 | FindingType, 8 | severityMapping, 9 | SPECTRAL_BASE_URL, 10 | SPECTRAL_DSN, 11 | SPECTRAL_FOLDER, 12 | } from '../common/constants' 13 | import { 14 | Findings, 15 | FindingsAggregations, 16 | ScanFindingView, 17 | ScanResult, 18 | } from '../common/types' 19 | import { formatWindowsPath, isWindows } from '../common/utils' 20 | import SecretStorageService from './secret-storage-service' 21 | import { Configuration } from '../common/configuration' 22 | 23 | export class SpectralAgentService { 24 | public findings: Findings 25 | public findingsAggregations: FindingsAggregations 26 | 27 | constructor() { 28 | this.resetFindings() 29 | } 30 | 31 | public installSpectral(): Promise { 32 | return new Promise((resolve, reject) => { 33 | let child 34 | let command 35 | if (isWindows()) { 36 | command = `iwr ${SPECTRAL_BASE_URL}/latest/ps1 -useb | iex` 37 | child = spawn('powershell.exe', [command]) 38 | } else { 39 | command = `curl -L '${SPECTRAL_BASE_URL}/latest/x/sh' | sh` 40 | child = spawn('/bin/sh', ['-c', command]) 41 | } 42 | 43 | child.stderr.setEncoding('utf8') 44 | const stdOut: string[] = [] 45 | const stderrChunks: string[] = [] 46 | child.stdout.on('data', (data) => { 47 | stdOut.push(data.toString('utf8')) 48 | }) 49 | child.stderr.on('data', (chunk) => { 50 | return stderrChunks.push(chunk) 51 | }) 52 | child.on('error', async (err) => { 53 | reject(err) 54 | }) 55 | child.on('close', (code) => { 56 | if (!isEmpty(stderrChunks) && code !== 0) { 57 | const error = stderrChunks.join('') 58 | return reject(error) 59 | } 60 | return resolve('') 61 | }) 62 | }) 63 | } 64 | 65 | public checkForSpectralBinary(): Promise { 66 | return new Promise((resolve) => { 67 | const child = spawn(this.getSpectralPath(), ['--nobanners', 'version'], { 68 | cwd: process.env.home, 69 | }) 70 | child.on('error', () => { 71 | resolve(false) 72 | }) 73 | child.on('close', (code) => { 74 | return resolve(code === 0) 75 | }) 76 | }) 77 | } 78 | 79 | public scan(scanPath: string): Promise { 80 | return new Promise(async (resolve, reject) => { 81 | const dsn = await SecretStorageService.getInstance().get(SPECTRAL_DSN) 82 | const outputFileName = 'output.txt' 83 | const spectralArgs = [ 84 | '--nobanners', 85 | 'scan', 86 | '--nosend', 87 | '--internal-output', 88 | outputFileName, 89 | ] 90 | const options: any = { 91 | cwd: scanPath, 92 | } 93 | const engines = Configuration.getInstance().engines 94 | if (!isEmpty(engines)) { 95 | spectralArgs.push('--engines', engines) 96 | } 97 | const tags = Configuration.getInstance().includeTags 98 | if (!isEmpty(tags)) { 99 | spectralArgs.push('--include-tags', tags) 100 | } 101 | if (isWindows()) { 102 | spectralArgs.push('--dsn', dsn) 103 | } else { 104 | options.env = { ...process.env, [SPECTRAL_DSN]: dsn } 105 | } 106 | 107 | const child = spawn(this.getSpectralPath(), spectralArgs, options) 108 | 109 | child.stderr.setEncoding('utf8') 110 | const stderrChunks: string[] = [] 111 | const stdOut: string[] = [] 112 | child.stdout.on('data', (data) => { 113 | stdOut.push(data.toString('utf8')) 114 | }) 115 | child.stderr.on('data', (chunk) => { 116 | return stderrChunks.push(chunk) 117 | }) 118 | child.on('error', async (err) => { 119 | return reject(err) 120 | }) 121 | child.on('close', async () => { 122 | if (!isEmpty(stderrChunks)) { 123 | const agentError = stderrChunks.join('') 124 | return reject(agentError) 125 | } 126 | try { 127 | const filePath = path.join(scanPath, outputFileName) 128 | const result = readFileSync(filePath, { encoding: 'utf8' }) 129 | unlinkSync(filePath) 130 | const jsonOutput = JSON.parse(result) 131 | return resolve(jsonOutput) 132 | } catch (err) { 133 | return reject(err) 134 | } 135 | }) 136 | }) 137 | } 138 | 139 | public processResults(results: ScanResult, folderPath: string) { 140 | results?.items?.forEach((item: ScanFindingView) => { 141 | this.processFindingItem({ item, folderPath: folderPath.toLowerCase() }) 142 | }) 143 | } 144 | 145 | public resetFindings() { 146 | this.findings = Object.values(FindingType).reduce((acc, key) => { 147 | acc[key] = {} 148 | return acc 149 | }, {} as Findings) 150 | this.findingsAggregations = Object.values(FindingType).reduce( 151 | (acc, key) => { 152 | acc[key] = 0 153 | return acc 154 | }, 155 | {} as FindingsAggregations 156 | ) 157 | } 158 | 159 | private processFindingItem({ 160 | item, 161 | folderPath, 162 | }: { 163 | item: ScanFindingView 164 | folderPath: string 165 | }): void { 166 | item.rootPath = folderPath 167 | 168 | if (isWindows()) { 169 | item.rootPath = formatWindowsPath(folderPath) 170 | item.finding = formatWindowsPath(item.finding.replace('\\\\?\\', '')) 171 | } 172 | 173 | item.rule.severity = this.mapToNewSeverity(item.rule.severity) 174 | item.finding = item.finding.toLowerCase() 175 | item.labelDisplayName = item.rule.name 176 | 177 | this.findingsAggregations[item.rule.severity] += 1 178 | const isIac = item.metadata.tags.includes(FindingType.iac) 179 | const isOss = item.metadata.tags.includes(FindingType.oss) 180 | 181 | let findingTypeResults 182 | if (isIac) { 183 | findingTypeResults = this.findings[FindingType.iac] 184 | this.findingsAggregations[FindingType.iac] += 1 185 | } else if (isOss) { 186 | item.labelDisplayName = `${item.rule.name} - ${item.rule.id}` 187 | findingTypeResults = this.findings[FindingType.oss] 188 | this.findingsAggregations[FindingType.oss] += 1 189 | } else { 190 | findingTypeResults = this.findings[FindingType.secret] 191 | this.findingsAggregations[FindingType.secret] += 1 192 | } 193 | 194 | if (!findingTypeResults[item.finding]) { 195 | findingTypeResults[item.finding] = [] 196 | } 197 | 198 | findingTypeResults[item.finding].push(item) 199 | } 200 | 201 | private getSpectralPath(): string { 202 | if (isWindows()) { 203 | return 'spectral' 204 | } 205 | 206 | return `${SPECTRAL_FOLDER}/spectral` 207 | } 208 | 209 | private mapToNewSeverity(itemSeverity: FindingSeverity): FindingSeverity { 210 | if (severityMapping[itemSeverity]) { 211 | return severityMapping[itemSeverity] 212 | } else { 213 | return itemSeverity 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/spectral/commands.ts: -------------------------------------------------------------------------------- 1 | import { ContextService } from '../services/context-service' 2 | import { SpectralAgentService } from '../services/spectral-agent-service' 3 | import { updateFindingsDecorations } from './results-view-decorations' 4 | import { 5 | AGENT_LAST_UPDATE_DATE, 6 | ENABLE_INSTALL_AGENT, 7 | HAS_DSN, 8 | HAS_SPECTRAL_INSTALLED, 9 | PRE_SCAN, 10 | SCAN_STATE, 11 | } from '../common/constants' 12 | import { SPECTRAL_VIEW_SECRETS } from '../common/constants' 13 | import { 14 | createStatusBarItem, 15 | getActiveTextEditor, 16 | runWithLoaderOnView, 17 | showInputBox, 18 | ShowNotificationMessage, 19 | } from '../common/vs-code' 20 | import { ScanResult } from '../common/types' 21 | import { ScanState, SPECTRAL_DSN } from '../common/constants' 22 | import { LoggerService } from '../services/logger-service' 23 | import { ResultsView } from './results-view' 24 | import SecretStorageService from '../services/secret-storage-service' 25 | import { AnalyticsService } from '../services/analytics-service' 26 | import { PersistenceContext } from '../common/persistence-context' 27 | 28 | export const setDsn = () => { 29 | showInputBox( 30 | { 31 | password: true, 32 | placeHolder: 'https://spu-XXXXXXXXXXXXX@XXXXXX', 33 | title: 'Spectral DSN', 34 | }, 35 | storeDsn, 36 | (value: string) => { 37 | const regex = 38 | /^https:\/\/spu-[a-zA-Z0-9]{32}@([a-zA-Z0-9_-]+\.)+[a-zA-Z]+$/ 39 | return regex.test(value) 40 | }, 41 | 'DSN structure is invalid' 42 | ) 43 | } 44 | 45 | export const scanWorkSpaceFolders = async ({ 46 | foldersPath, 47 | spectralAgentService, 48 | resultsView, 49 | }: { 50 | foldersPath: Array 51 | spectralAgentService: SpectralAgentService 52 | resultsView: ResultsView 53 | }): Promise => { 54 | const contextService = ContextService.getInstance() 55 | const logger = LoggerService.getInstance() 56 | const inProgressStatusBarItem = createStatusBarItem({ 57 | location: 'right', 58 | priority: 1, 59 | text: `$(loading~spin)`, 60 | tooltip: 'Spectral is scanning', 61 | }) 62 | contextService.setContext(SCAN_STATE, ScanState.inProgress) 63 | logger.debug('Scan start') 64 | AnalyticsService.track('vscode-scan') 65 | inProgressStatusBarItem.show() 66 | try { 67 | spectralAgentService.resetFindings() 68 | await runWithLoaderOnView({ 69 | viewId: SPECTRAL_VIEW_SECRETS, 70 | action: () => 71 | scanWorkspaces({ spectralAgentService, foldersPath, logger }), 72 | }) 73 | } catch (error) { 74 | contextService.setContext(SCAN_STATE, 'failed') 75 | inProgressStatusBarItem.dispose() 76 | logger.error(error) 77 | ShowNotificationMessage({ 78 | messageType: 'error', 79 | messageText: `Scan failed`, 80 | items: ['See output'], 81 | }).then(() => logger.showOutput()) 82 | return 83 | } 84 | 85 | contextService.setContext(SCAN_STATE, ScanState.success) 86 | logger.debug('Scan finished') 87 | contextService.setContext(PRE_SCAN, false) 88 | 89 | inProgressStatusBarItem.dispose() 90 | 91 | resultsView.refresh({ 92 | findings: spectralAgentService.findings, 93 | findingsAggregations: spectralAgentService.findingsAggregations, 94 | }) 95 | 96 | updateFindingsDecorations( 97 | getActiveTextEditor(), 98 | spectralAgentService.findings 99 | ) 100 | } 101 | 102 | export const setupSpectral = async ( 103 | spectralAgentService: SpectralAgentService 104 | ): Promise => { 105 | const inProgressStatusBarItem = createStatusBarItem({ 106 | location: 'right', 107 | priority: 1, 108 | text: `$(loading~spin)`, 109 | tooltip: 'Installing Spectral', 110 | }) 111 | const contextService = ContextService.getInstance() 112 | try { 113 | contextService.setContext(ENABLE_INSTALL_AGENT, false) 114 | inProgressStatusBarItem.show() 115 | ShowNotificationMessage({ 116 | messageType: 'info', 117 | messageText: `Spectral installation is in progress`, 118 | }) 119 | await spectralAgentService.installSpectral() 120 | inProgressStatusBarItem.dispose() 121 | contextService.setContext(HAS_SPECTRAL_INSTALLED, true) 122 | const extensionContext = PersistenceContext.getInstance() 123 | extensionContext.updateGlobalStateValue(AGENT_LAST_UPDATE_DATE, Date.now()) 124 | } catch (error) { 125 | inProgressStatusBarItem.dispose() 126 | const logger = LoggerService.getInstance() 127 | logger.error(error) 128 | ShowNotificationMessage({ 129 | messageType: 'error', 130 | messageText: `Spectral installation failed`, 131 | items: ['See output'], 132 | }).then(() => logger.showOutput()) 133 | } 134 | ShowNotificationMessage({ 135 | messageType: 'info', 136 | messageText: `Spectral installation completed`, 137 | }) 138 | } 139 | 140 | const scanWorkspaces = async ({ 141 | spectralAgentService, 142 | foldersPath, 143 | logger, 144 | }: { 145 | spectralAgentService: SpectralAgentService 146 | foldersPath: Array 147 | logger: LoggerService 148 | }) => { 149 | await Promise.all( 150 | foldersPath.map(async (folderPath) => { 151 | logger.debug(`Scanning workspace folder: ${folderPath}`) 152 | const results: ScanResult = await spectralAgentService.scan(folderPath) 153 | logger.debug(`Found: ${results.items.length} findings`) 154 | spectralAgentService.processResults(results, folderPath) 155 | }) 156 | ) 157 | } 158 | 159 | const storeDsn = async (dsn: string): Promise => { 160 | const secretStorageService = SecretStorageService.getInstance() 161 | secretStorageService.store(SPECTRAL_DSN, dsn) 162 | const contextService = ContextService.getInstance() 163 | contextService.setContext(HAS_DSN, true) 164 | } 165 | -------------------------------------------------------------------------------- /src/spectral/extension.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ExtensionContext, 3 | commands, 4 | window, 5 | workspace, 6 | WorkspaceFoldersChangeEvent, 7 | TextEditor, 8 | ConfigurationChangeEvent, 9 | } from 'vscode' 10 | import { 11 | ENABLE_INSTALL_AGENT, 12 | SPECTRAL_INSTALL, 13 | CONFIGURATION_IDENTIFIER, 14 | INCLUDE_TAGS_SETTING, 15 | SPECTRAL_SCAN, 16 | SPECTRAL_SET_DSN, 17 | SPECTRAL_SHOW_OUTPUT, 18 | USE_IAC_ENGINE_SETTING, 19 | USE_OSS_ENGINE_SETTING, 20 | USE_SECRET_ENGINE_SETTING, 21 | } from '../common/constants' 22 | import { 23 | HAS_DSN, 24 | PRE_SCAN, 25 | HAS_SPECTRAL_INSTALLED, 26 | HAS_LICENSE, 27 | } from '../common/constants' 28 | import { ContextService } from '../services/context-service' 29 | import { SpectralAgentService } from '../services/spectral-agent-service' 30 | import { scanWorkSpaceFolders, setDsn, setupSpectral } from './commands' 31 | import concat from 'lodash/concat' 32 | import pullAll from 'lodash/pullAll' 33 | import { updateFindingsDecorations } from './results-view-decorations' 34 | import { SPECTRAL_DSN, SPECTRAL_FOLDER } from '../common/constants' 35 | import { accessSync } from 'fs' 36 | import path from 'path' 37 | import { LoggerService } from '../services/logger-service' 38 | import { ResultsView } from './results-view' 39 | import SecretStorageService from '../services/secret-storage-service' 40 | import { getWorkspaceFolders } from '../common/vs-code' 41 | import { AnalyticsService } from '../services/analytics-service' 42 | import { PersistenceContext } from '../common/persistence-context' 43 | import { Configuration } from '../common/configuration' 44 | import { shouldUpdateSpectralAgent } from '../common/spectral-utils' 45 | 46 | export class SpectralExtension { 47 | private workspaceFolders: Array = getWorkspaceFolders() 48 | private readonly contextService: ContextService 49 | private readonly persistenceContext: PersistenceContext 50 | private readonly logger: LoggerService 51 | private readonly configuration: Configuration 52 | private readonly spectralAgentService: SpectralAgentService 53 | private readonly resultsView: ResultsView 54 | 55 | constructor() { 56 | this.contextService = ContextService.getInstance() 57 | this.logger = LoggerService.getInstance() 58 | this.persistenceContext = PersistenceContext.getInstance() 59 | this.configuration = Configuration.getInstance(workspace) 60 | this.spectralAgentService = new SpectralAgentService() 61 | this.resultsView = new ResultsView() 62 | } 63 | 64 | public async activate(vsCodeContext: ExtensionContext): Promise { 65 | try { 66 | this.persistenceContext.setContext(vsCodeContext) 67 | this.initializeExtension(vsCodeContext) 68 | const isSpectralInstalled = 69 | await this.spectralAgentService.checkForSpectralBinary() 70 | 71 | this.setSpectralInstallationContext(isSpectralInstalled) 72 | if (isSpectralInstalled && shouldUpdateSpectralAgent()) { 73 | await setupSpectral(this.spectralAgentService) 74 | } 75 | AnalyticsService.init() 76 | } catch (error) { 77 | this.logger.error(error) 78 | } 79 | } 80 | 81 | private async initializeExtension( 82 | vsCodeContext: ExtensionContext 83 | ): Promise { 84 | SecretStorageService.init(vsCodeContext) 85 | this.contextService.setContext(PRE_SCAN, true) 86 | this.registerCommands(vsCodeContext) 87 | this.registerEvents() 88 | } 89 | 90 | public async setSpectralInstallationContext( 91 | isSpectralInstalled: Boolean 92 | ): Promise { 93 | this.contextService.setContext(ENABLE_INSTALL_AGENT, true) 94 | if (!isSpectralInstalled) { 95 | this.contextService.setContext(HAS_SPECTRAL_INSTALLED, false) 96 | return 97 | } 98 | this.contextService.setContext(HAS_SPECTRAL_INSTALLED, true) 99 | const secretStorageService = SecretStorageService.getInstance() 100 | const dsn = await secretStorageService.get(SPECTRAL_DSN) 101 | if (!dsn) { 102 | this.contextService.setContext(HAS_DSN, false) 103 | } else { 104 | this.contextService.setContext(HAS_DSN, true) 105 | } 106 | try { 107 | accessSync(path.join(`${SPECTRAL_FOLDER}/license`)) 108 | this.contextService.setContext(HAS_LICENSE, true) 109 | } catch (error) { 110 | this.contextService.setContext(HAS_LICENSE, false) 111 | } 112 | } 113 | 114 | private registerCommands(vsCodeContext: ExtensionContext): void { 115 | vsCodeContext.subscriptions.push( 116 | commands.registerCommand(SPECTRAL_INSTALL, () => 117 | setupSpectral(this.spectralAgentService) 118 | ), 119 | commands.registerCommand(SPECTRAL_SCAN, () => 120 | scanWorkSpaceFolders({ 121 | foldersPath: this.workspaceFolders, 122 | spectralAgentService: this.spectralAgentService, 123 | resultsView: this.resultsView, 124 | }) 125 | ), 126 | commands.registerCommand(SPECTRAL_SHOW_OUTPUT, () => 127 | this.logger.showOutput() 128 | ), 129 | commands.registerCommand(SPECTRAL_SET_DSN, async () => { 130 | setDsn() 131 | }) 132 | ) 133 | } 134 | 135 | private registerEvents(): void { 136 | workspace.onDidChangeWorkspaceFolders( 137 | (workspaceFoldersChangeEvent: WorkspaceFoldersChangeEvent) => { 138 | this.workspaceFolders = concat( 139 | this.workspaceFolders, 140 | workspaceFoldersChangeEvent.added.map((folder) => folder.uri.path) 141 | ) 142 | pullAll( 143 | this.workspaceFolders, 144 | workspaceFoldersChangeEvent.removed.map((folder) => folder.uri.path) 145 | ) 146 | } 147 | ) 148 | window.onDidChangeActiveTextEditor((event: TextEditor | undefined) => { 149 | updateFindingsDecorations(event, this.spectralAgentService.findings) 150 | }) 151 | workspace.onDidChangeConfiguration( 152 | async (event: ConfigurationChangeEvent): Promise => { 153 | const changed = [ 154 | `${CONFIGURATION_IDENTIFIER}.${USE_SECRET_ENGINE_SETTING}`, 155 | `${CONFIGURATION_IDENTIFIER}.${USE_IAC_ENGINE_SETTING}`, 156 | `${CONFIGURATION_IDENTIFIER}.${USE_OSS_ENGINE_SETTING}`, 157 | `${CONFIGURATION_IDENTIFIER}.${INCLUDE_TAGS_SETTING}`, 158 | ].find((setting) => event.affectsConfiguration(setting)) 159 | if (changed) { 160 | workspace.getConfiguration(CONFIGURATION_IDENTIFIER) 161 | this.configuration.updateConfiguration(workspace) 162 | } 163 | } 164 | ) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/spectral/results-items.ts: -------------------------------------------------------------------------------- 1 | import { 2 | TreeItem, 3 | TreeDataProvider, 4 | ProviderResult, 5 | TreeItemCollapsibleState, 6 | Uri, 7 | ThemeIcon, 8 | } from 'vscode' 9 | import L from 'lodash' 10 | import { basename, dirname, join } from 'path' 11 | import isEmpty from 'lodash/isEmpty' 12 | import { getFindingRange } from '../common/vs-code' 13 | import { FindingsTypeResults, ScanFindingView } from '../common/types' 14 | import { FindingSeverity, FindingSeverityLevel } from '../common/constants' 15 | 16 | export class FindingsProvider implements TreeDataProvider { 17 | private readonly findings: FindingsTypeResults 18 | constructor(findings: FindingsTypeResults) { 19 | this.findings = findings 20 | } 21 | getTreeItem(element: TreeItem): TreeItem | Thenable { 22 | return element 23 | } 24 | getChildren(element?: TreeItem): ProviderResult { 25 | if (!element) { 26 | if (!isEmpty(this.findings)) { 27 | return Promise.resolve(new WorkspaceItem(this.findings).fileNodes) 28 | } else { 29 | return Promise.resolve([ 30 | new EmptyItem('Great job! no issues were found.'), 31 | ]) 32 | } 33 | } else { 34 | return Promise.resolve((element).findingNodes) 35 | } 36 | } 37 | } 38 | 39 | export class WorkspaceItem extends TreeItem { 40 | fileNodes: Array 41 | constructor(findings: FindingsTypeResults) { 42 | super('', TreeItemCollapsibleState.Collapsed) 43 | this.fileNodes = this.getFilesNodes(findings) 44 | } 45 | 46 | private getFilesNodes(findings: FindingsTypeResults) { 47 | const orderedFindings = L(findings) 48 | .toPairs() 49 | .orderBy([0], ['asc']) 50 | .fromPairs() 51 | .value() 52 | return Object.entries(orderedFindings).map( 53 | ([filePath, fileFindings]: [string, Array]) => { 54 | const rootPath = fileFindings[0].rootPath 55 | const path = filePath.startsWith(rootPath) 56 | ? filePath.substring(rootPath.length) 57 | : filePath 58 | return new FileItem(path, fileFindings) 59 | } 60 | ) 61 | } 62 | } 63 | 64 | export class FindingItem extends TreeItem { 65 | private iconsPath = { 66 | [FindingSeverity.critical]: join( 67 | __filename, 68 | '..', 69 | '..', 70 | 'media', 71 | 'critical.svg' 72 | ), 73 | [FindingSeverity.high]: join(__filename, '..', '..', 'media', 'high.svg'), 74 | [FindingSeverity.medium]: join( 75 | __filename, 76 | '..', 77 | '..', 78 | 'media', 79 | 'medium.svg' 80 | ), 81 | [FindingSeverity.low]: join(__filename, '..', '..', 'media', 'low.svg'), 82 | [FindingSeverity.informational]: join( 83 | __filename, 84 | '..', 85 | '..', 86 | 'media', 87 | 'informational.svg' 88 | ), 89 | } 90 | constructor(finding: ScanFindingView) { 91 | super(finding.labelDisplayName, TreeItemCollapsibleState.None) 92 | const findingPosition = getFindingRange(finding) 93 | this.tooltip = L.capitalize(finding.rule.severity) 94 | this.iconPath = { 95 | light: this.iconsPath[finding.rule.severity], 96 | dark: this.iconsPath[finding.rule.severity], 97 | } 98 | this.command = { 99 | title: 'Open', 100 | command: 'vscode.open', 101 | arguments: [Uri.parse(finding.finding), { selection: findingPosition }], 102 | } 103 | } 104 | } 105 | 106 | export class FileItem extends TreeItem { 107 | findingNodes: Array 108 | 109 | constructor(filePath: string, findings: Array) { 110 | super(basename(filePath), TreeItemCollapsibleState.Collapsed) 111 | this.resourceUri = Uri.file(filePath) 112 | this.iconPath = ThemeIcon.File 113 | this.description = dirname(filePath).substring(1) 114 | this.findingNodes = findings 115 | .sort((a, b) => { 116 | if ( 117 | FindingSeverityLevel[a.rule.severity] < 118 | FindingSeverityLevel[b.rule.severity] 119 | ) { 120 | return -1 121 | } 122 | if ( 123 | FindingSeverityLevel[a.rule.severity] > 124 | FindingSeverityLevel[b.rule.severity] 125 | ) { 126 | return 1 127 | } 128 | return 0 129 | }) 130 | .map((finding) => new FindingItem(finding)) 131 | } 132 | } 133 | 134 | export class EmptyItem extends TreeItem { 135 | constructor(message: string) { 136 | super(message, TreeItemCollapsibleState.None) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/spectral/results-view-decorations.ts: -------------------------------------------------------------------------------- 1 | import { TextEditor, TextEditorDecorationType, OverviewRulerLane } from 'vscode' 2 | import { Findings, ScanFinding } from '../common/types' 3 | import { formatWindowsPath, isWindows } from '../common/utils' 4 | import { FindingSeverity, FindingType } from '../common/constants' 5 | import { createTextDecoration, getFindingRange } from '../common/vs-code' 6 | import concat from 'lodash/concat' 7 | import compact from 'lodash/compact' 8 | import isEmpty from 'lodash/isEmpty' 9 | 10 | export const updateFindingsDecorations = ( 11 | editor: TextEditor | undefined, 12 | findings: Findings 13 | ) => { 14 | if (!editor) { 15 | return 16 | } 17 | 18 | const docFilePath = isWindows() 19 | ? formatWindowsPath(editor.document.fileName.toLowerCase()) 20 | : editor.document.fileName.toLowerCase() 21 | 22 | const fileFindings: any = concat( 23 | findings[FindingType.secret][docFilePath], 24 | findings[FindingType.iac][docFilePath] 25 | ) 26 | 27 | if (isEmpty(fileFindings)) { 28 | // clean all decorations from the current file, if any 29 | Object.keys(severityDecorationsColors).forEach( 30 | (severity: FindingSeverity) => { 31 | editor.setDecorations(createDecorationTypeBySeverity(severity), []) 32 | } 33 | ) 34 | return 35 | } 36 | 37 | Object.keys(severityDecorationsColors).forEach( 38 | (severity: FindingSeverity) => { 39 | decorateBySeverity({ 40 | editor, 41 | severity, 42 | fileFindings: compact(fileFindings), 43 | }) 44 | } 45 | ) 46 | } 47 | 48 | const decorateBySeverity = ({ 49 | editor, 50 | severity, 51 | fileFindings, 52 | }: { 53 | editor: TextEditor 54 | severity: FindingSeverity 55 | fileFindings: Array 56 | }): void => { 57 | const findingsBySeverity: any = fileFindings.filter( 58 | (finding) => finding.rule.severity === severity 59 | ) 60 | 61 | if (isEmpty(findingsBySeverity)) { 62 | return 63 | } 64 | 65 | const rangesToDecorate = findingsBySeverity 66 | .map((finding: ScanFinding) => { 67 | const findingPosition = getFindingRange(finding) 68 | return findingPosition 69 | ? { 70 | range: findingPosition, 71 | } 72 | : false 73 | }) 74 | .filter(Boolean) 75 | editor.setDecorations( 76 | createDecorationTypeBySeverity(severity), 77 | rangesToDecorate 78 | ) 79 | } 80 | 81 | const severityDecorationsColors = { 82 | [FindingSeverity.critical]: '#f24e4e', 83 | [FindingSeverity.high]: '#ff8000', 84 | [FindingSeverity.medium]: '#ffb72d', 85 | [FindingSeverity.low]: '#ffdb66', 86 | [FindingSeverity.informational]: '#80c3ff', 87 | } 88 | 89 | const createDecorationTypeBySeverity = ( 90 | severity: FindingSeverity 91 | ): TextEditorDecorationType => 92 | createTextDecoration({ 93 | cursor: 'pointer', 94 | borderWidth: '0px 0px 1px 0px', 95 | borderStyle: 'solid', 96 | overviewRulerColor: severityDecorationsColors[severity], 97 | overviewRulerLane: OverviewRulerLane.Right, 98 | light: { 99 | borderColor: severityDecorationsColors[severity], 100 | }, 101 | dark: { 102 | borderColor: severityDecorationsColors[severity], 103 | }, 104 | }) 105 | -------------------------------------------------------------------------------- /src/spectral/results-view.ts: -------------------------------------------------------------------------------- 1 | import { DiagnosticCollection, TreeItem } from 'vscode' 2 | import gt from 'lodash/gt' 3 | import { 4 | appendFindingsToProblemsChannel, 5 | createTree, 6 | ShowNotificationMessage, 7 | } from '../common/vs-code' 8 | import { 9 | Findings, 10 | FindingsAggregations, 11 | FindingsTypeResults, 12 | } from '../common/types' 13 | import { 14 | SPECTRAL_VIEW_IAC, 15 | SPECTRAL_VIEW_OSS, 16 | SPECTRAL_VIEW_SECRETS, 17 | } from '../common/constants' 18 | import { FindingsProvider } from './results-items' 19 | 20 | export class ResultsView { 21 | private problemsCollection: DiagnosticCollection 22 | 23 | public refresh({ 24 | findings, 25 | findingsAggregations, 26 | }: { 27 | findings: Findings 28 | findingsAggregations: FindingsAggregations 29 | }) { 30 | this.createResultsViews({ 31 | results: findings.secret, 32 | viewId: SPECTRAL_VIEW_SECRETS, 33 | viewTitle: `Secrets (${findingsAggregations.secret})`, 34 | }) 35 | 36 | this.createResultsViews({ 37 | results: findings.iac, 38 | viewId: SPECTRAL_VIEW_IAC, 39 | viewTitle: `IaC (${findingsAggregations.iac})`, 40 | }) 41 | 42 | this.createResultsViews({ 43 | results: findings.oss, 44 | viewId: SPECTRAL_VIEW_OSS, 45 | viewTitle: `Open Source (${findingsAggregations.oss})`, 46 | }) 47 | 48 | if (this.problemsCollection) { 49 | this.problemsCollection.clear() 50 | } 51 | this.problemsCollection = appendFindingsToProblemsChannel(findings) 52 | 53 | this.showResultNotification(findingsAggregations) 54 | } 55 | 56 | private createResultsViews({ 57 | results, 58 | viewId, 59 | viewTitle, 60 | }: { 61 | results: FindingsTypeResults 62 | viewId: string 63 | viewTitle: string 64 | }): void { 65 | const findingsTree = createTree({ 66 | viewId: viewId, 67 | options: { 68 | treeDataProvider: new FindingsProvider(results), 69 | }, 70 | }) 71 | 72 | findingsTree.title = viewTitle 73 | } 74 | 75 | private showResultNotification( 76 | findingsAggregations: FindingsAggregations 77 | ): void { 78 | if (gt(findingsAggregations.iac, 0) || gt(findingsAggregations.secret, 0)) { 79 | const totalFindings = 80 | findingsAggregations.iac + findingsAggregations.secret 81 | 82 | ShowNotificationMessage({ 83 | messageType: 'info', 84 | messageText: `Scan finished. Found ${totalFindings} issue${ 85 | totalFindings > 1 ? 's' : '' 86 | }`, 87 | }) 88 | } else { 89 | ShowNotificationMessage({ 90 | messageType: 'info', 91 | messageText: 'Great job! No issue were found', 92 | }) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/mocks/scan-results.mock.ts: -------------------------------------------------------------------------------- 1 | import { concat } from 'lodash' 2 | import { FindingSeverity } from '../../common/constants' 3 | import { ScanResult } from '../../common/types' 4 | 5 | export const scanSecretsSingleFinding: ScanResult = { 6 | items: [ 7 | { 8 | finding: '/Users/testUser/git/vscode-extension/src/common/state.ts', 9 | position: { 10 | start: [1, 5], 11 | end: [1, 2], 12 | }, 13 | rule: { 14 | id: 'CLD001', 15 | name: 'Visible AWS Key', 16 | severity: FindingSeverity.error, 17 | description: 'Found a visible AWS Key', 18 | }, 19 | metadata: { 20 | tags: ['base', 'amazon'], 21 | }, 22 | }, 23 | ], 24 | } 25 | 26 | export const scanIacSingleFinding: ScanResult = { 27 | items: [ 28 | { 29 | finding: '/Users/testUser/git/vscode-extension/src/common/state.ts', 30 | position: { 31 | start: [1, 5], 32 | end: [1, 2], 33 | }, 34 | rule: { 35 | id: 'AZURE001', 36 | name: 'Missing email account admins for security alerts', 37 | severity: FindingSeverity.warning, 38 | description: 'Missing email account admins for security alerts', 39 | }, 40 | metadata: { 41 | tags: ['azure', 'iac'], 42 | }, 43 | }, 44 | ], 45 | } 46 | 47 | export const scanOssSingleFinding: ScanResult = { 48 | items: [ 49 | { 50 | finding: '/Users/testUser/git/vscode-extension/package-lock.json', 51 | position: { 52 | start: [1, 1], 53 | end: [1, 1], 54 | }, 55 | rule: { 56 | id: 'CVE-2018-20834', 57 | name: 'CVE-2018-20834', 58 | severity: FindingSeverity.high, 59 | description: 60 | 'A vulnerability was found in node-tar before version 4.4.2 (excluding version 2.2.2). An Arbitrary File Overwrite issue exists when extracting a tarball containing a hardlink to a file that already exists on the system, in conjunction with a later plain file with the same name as the hardlink. This plain file content replaces the existing file content. A patch has been applied to node-tar v2.2.2).', 61 | }, 62 | metadata: { 63 | tags: ['oss'], 64 | }, 65 | }, 66 | ], 67 | } 68 | 69 | export const scanSingleFindingEachType: ScanResult = { 70 | items: concat( 71 | scanSecretsSingleFinding.items, 72 | scanIacSingleFinding.items, 73 | scanOssSingleFinding.items 74 | ), 75 | } 76 | 77 | export const scanMultipleSecrets: ScanResult = { 78 | items: [ 79 | { 80 | finding: '/Users/testUser/git/vscode-extension/src/common/state.ts', 81 | position: { 82 | start: [1, 5], 83 | end: [1, 2], 84 | }, 85 | rule: { 86 | id: 'CLD001', 87 | name: 'Visible AWS Key', 88 | severity: FindingSeverity.error, 89 | description: 'Found a visible AWS Key', 90 | }, 91 | metadata: { 92 | tags: ['base', 'amazon'], 93 | }, 94 | }, 95 | { 96 | finding: 97 | '/Users/testUser/git/vscode-extension/src/services/context-service.ts', 98 | position: { 99 | start: [1, 5], 100 | end: [1, 2], 101 | }, 102 | rule: { 103 | id: 'CLD001', 104 | name: 'Visible AWS Key', 105 | severity: FindingSeverity.error, 106 | description: 'Found a visible AWS Key', 107 | }, 108 | metadata: { 109 | tags: ['base', 'amazon'], 110 | }, 111 | }, 112 | ], 113 | } 114 | -------------------------------------------------------------------------------- /src/test/unit/services/spectral-agent-service.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | import { SpectralAgentService } from '../../../services/spectral-agent-service' 3 | import { 4 | scanIacSingleFinding, 5 | scanMultipleSecrets, 6 | scanOssSingleFinding, 7 | scanSecretsSingleFinding, 8 | scanSingleFindingEachType, 9 | } from '../../mocks/scan-results.mock' 10 | 11 | suite('Spectral agent service', () => { 12 | test('[ok] - process only secrets - aggregations should be as items length', () => { 13 | const spectralAgentService = new SpectralAgentService() 14 | spectralAgentService.processResults(scanSecretsSingleFinding, 'somePath') 15 | assert.strictEqual( 16 | spectralAgentService.findingsAggregations.secret, 17 | scanSecretsSingleFinding.items.length 18 | ) 19 | }) 20 | 21 | test('[ok] - process only iac - aggregations should be as items length', () => { 22 | const spectralAgentService = new SpectralAgentService() 23 | spectralAgentService.processResults(scanIacSingleFinding, 'somePath') 24 | assert.strictEqual( 25 | spectralAgentService.findingsAggregations.iac, 26 | scanIacSingleFinding.items.length 27 | ) 28 | }) 29 | 30 | test('[ok] - process only oss - aggregations should be as items length', () => { 31 | const spectralAgentService = new SpectralAgentService() 32 | spectralAgentService.processResults(scanOssSingleFinding, 'somePath') 33 | assert.strictEqual( 34 | spectralAgentService.findingsAggregations.oss, 35 | scanOssSingleFinding.items.length 36 | ) 37 | }) 38 | 39 | test('[ok] - process both finding types - aggregations should be as items length', () => { 40 | const spectralAgentService = new SpectralAgentService() 41 | spectralAgentService.processResults(scanSingleFindingEachType, 'somePath') 42 | const expectedIacItems = 1 43 | const expectedOssItems = 1 44 | const expectedSecretsItems = 1 45 | assert.strictEqual( 46 | spectralAgentService.findingsAggregations.iac, 47 | expectedIacItems, 48 | 'iac validation failed' 49 | ) 50 | assert.strictEqual( 51 | spectralAgentService.findingsAggregations.oss, 52 | expectedOssItems, 53 | 'oss validation failed' 54 | ) 55 | assert.strictEqual( 56 | spectralAgentService.findingsAggregations.secret, 57 | expectedSecretsItems, 58 | 'secrets validation failed' 59 | ) 60 | }) 61 | 62 | test('[ok] - process finding - findings should be aggregated by the finding path', () => { 63 | const spectralAgentService = new SpectralAgentService() 64 | spectralAgentService.processResults(scanSecretsSingleFinding, 'somePath') 65 | const findingKey = Object.keys(spectralAgentService.findings.secret)[0] 66 | assert.strictEqual(findingKey, scanSecretsSingleFinding.items[0].finding) 67 | }) 68 | 69 | test('[ok] - process finding - different findings should be aggregated by different finding path', () => { 70 | const spectralAgentService = new SpectralAgentService() 71 | spectralAgentService.processResults(scanMultipleSecrets, 'somePath') 72 | const expectedKeys = 2 73 | const actualKeys = Object.keys(spectralAgentService.findings.secret).length 74 | assert.strictEqual(actualKeys, expectedKeys) 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noImplicitAny": false, 4 | "resolveJsonModule": true, 5 | "esModuleInterop": true, 6 | "module": "commonjs", 7 | "target": "es2017", 8 | "outDir": "out", 9 | "sourceMap": true, 10 | }, 11 | "exclude": [ 12 | "node_modules", 13 | ".vscode-test", 14 | ] 15 | } -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@aashutoshrathi/word-wrap@^1.2.3": 6 | version "1.2.6" 7 | resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" 8 | integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== 9 | 10 | "@esbuild/linux-loong64@0.15.7": 11 | version "0.15.7" 12 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz#1ec4af4a16c554cbd402cc557ccdd874e3f7be53" 13 | integrity sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw== 14 | 15 | "@eslint-community/eslint-utils@^4.2.0": 16 | version "4.4.0" 17 | resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" 18 | integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== 19 | dependencies: 20 | eslint-visitor-keys "^3.3.0" 21 | 22 | "@eslint-community/regexpp@^4.4.0": 23 | version "4.5.1" 24 | resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" 25 | integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== 26 | 27 | "@eslint/eslintrc@^2.1.0": 28 | version "2.1.0" 29 | resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" 30 | integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== 31 | dependencies: 32 | ajv "^6.12.4" 33 | debug "^4.3.2" 34 | espree "^9.6.0" 35 | globals "^13.19.0" 36 | ignore "^5.2.0" 37 | import-fresh "^3.2.1" 38 | js-yaml "^4.1.0" 39 | minimatch "^3.1.2" 40 | strip-json-comments "^3.1.1" 41 | 42 | "@eslint/js@8.44.0": 43 | version "8.44.0" 44 | resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" 45 | integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== 46 | 47 | "@humanwhocodes/config-array@^0.11.10": 48 | version "0.11.10" 49 | resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" 50 | integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== 51 | dependencies: 52 | "@humanwhocodes/object-schema" "^1.2.1" 53 | debug "^4.1.1" 54 | minimatch "^3.0.5" 55 | 56 | "@humanwhocodes/module-importer@^1.0.1": 57 | version "1.0.1" 58 | resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" 59 | integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== 60 | 61 | "@humanwhocodes/object-schema@^1.2.1": 62 | version "1.2.1" 63 | resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" 64 | integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== 65 | 66 | "@nodelib/fs.scandir@2.1.5": 67 | version "2.1.5" 68 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" 69 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 70 | dependencies: 71 | "@nodelib/fs.stat" "2.0.5" 72 | run-parallel "^1.1.9" 73 | 74 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 75 | version "2.0.5" 76 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" 77 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 78 | 79 | "@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": 80 | version "1.2.8" 81 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" 82 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 83 | dependencies: 84 | "@nodelib/fs.scandir" "2.1.5" 85 | fastq "^1.6.0" 86 | 87 | "@types/glob@^7.1.3": 88 | version "7.1.4" 89 | resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672" 90 | integrity sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA== 91 | dependencies: 92 | "@types/minimatch" "*" 93 | "@types/node" "*" 94 | 95 | "@types/json-schema@^7.0.9": 96 | version "7.0.11" 97 | resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" 98 | integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== 99 | 100 | "@types/lodash@^4.14.184": 101 | version "4.14.184" 102 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.184.tgz#23f96cd2a21a28e106dc24d825d4aa966de7a9fe" 103 | integrity sha512-RoZphVtHbxPZizt4IcILciSWiC6dcn+eZ8oX9IWEYfDMcocdd42f7NPI6fQj+6zI8y4E0L7gu2pcZKLGTRaV9Q== 104 | 105 | "@types/minimatch@*": 106 | version "3.0.5" 107 | resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" 108 | integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== 109 | 110 | "@types/mocha@8.2.2": 111 | version "8.2.2" 112 | resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.2.tgz#91daa226eb8c2ff261e6a8cbf8c7304641e095e0" 113 | integrity sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw== 114 | 115 | "@types/node@*": 116 | version "16.6.1" 117 | resolved "https://registry.yarnpkg.com/@types/node/-/node-16.6.1.tgz#aee62c7b966f55fc66c7b6dfa1d58db2a616da61" 118 | integrity sha512-Sr7BhXEAer9xyGuCN3Ek9eg9xPviCF2gfu9kTfuU2HkTVAMYSDeX40fvpmo72n5nansg3nsBjuQBrsS28r+NUw== 119 | 120 | "@types/node@14.x": 121 | version "14.17.9" 122 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.9.tgz#b97c057e6138adb7b720df2bd0264b03c9f504fd" 123 | integrity sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g== 124 | 125 | "@types/vscode@^1.59.0": 126 | version "1.59.0" 127 | resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.59.0.tgz#11c93f5016926126bf30b47b9ece3bd617eeef31" 128 | integrity sha512-Zg38rusx2nU6gy6QdF7v4iqgxNfxzlBlDhrRCjOiPQp+sfaNrp3f9J6OHIhpGNN1oOAca4+9Hq0+8u3jwzPMlQ== 129 | 130 | "@typescript-eslint/eslint-plugin@^5.37.0": 131 | version "5.37.0" 132 | resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.37.0.tgz#5ccdd5d9004120f28fc6e717fb4b5c9bddcfbc04" 133 | integrity sha512-Fde6W0IafXktz1UlnhGkrrmnnGpAo1kyX7dnyHHVrmwJOn72Oqm3eYtddrpOwwel2W8PAK9F3pIL5S+lfoM0og== 134 | dependencies: 135 | "@typescript-eslint/scope-manager" "5.37.0" 136 | "@typescript-eslint/type-utils" "5.37.0" 137 | "@typescript-eslint/utils" "5.37.0" 138 | debug "^4.3.4" 139 | functional-red-black-tree "^1.0.1" 140 | ignore "^5.2.0" 141 | regexpp "^3.2.0" 142 | semver "^7.3.7" 143 | tsutils "^3.21.0" 144 | 145 | "@typescript-eslint/parser@^4.26.0": 146 | version "4.29.1" 147 | resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.29.1.tgz#17dfbb45c9032ffa0fe15881d20fbc2a4bdeb02d" 148 | integrity sha512-3fL5iN20hzX3Q4OkG7QEPFjZV2qsVGiDhEwwh+EkmE/w7oteiOvUNzmpu5eSwGJX/anCryONltJ3WDmAzAoCMg== 149 | dependencies: 150 | "@typescript-eslint/scope-manager" "4.29.1" 151 | "@typescript-eslint/types" "4.29.1" 152 | "@typescript-eslint/typescript-estree" "4.29.1" 153 | debug "^4.3.1" 154 | 155 | "@typescript-eslint/scope-manager@4.29.1": 156 | version "4.29.1" 157 | resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.1.tgz#f25da25bc6512812efa2ce5ebd36619d68e61358" 158 | integrity sha512-Hzv/uZOa9zrD/W5mftZa54Jd5Fed3tL6b4HeaOpwVSabJK8CJ+2MkDasnX/XK4rqP5ZTWngK1ZDeCi6EnxPQ7A== 159 | dependencies: 160 | "@typescript-eslint/types" "4.29.1" 161 | "@typescript-eslint/visitor-keys" "4.29.1" 162 | 163 | "@typescript-eslint/scope-manager@5.37.0": 164 | version "5.37.0" 165 | resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.37.0.tgz#044980e4f1516a774a418dafe701a483a6c9f9ca" 166 | integrity sha512-F67MqrmSXGd/eZnujjtkPgBQzgespu/iCZ+54Ok9X5tALb9L2v3G+QBSoWkXG0p3lcTJsL+iXz5eLUEdSiJU9Q== 167 | dependencies: 168 | "@typescript-eslint/types" "5.37.0" 169 | "@typescript-eslint/visitor-keys" "5.37.0" 170 | 171 | "@typescript-eslint/type-utils@5.37.0": 172 | version "5.37.0" 173 | resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.37.0.tgz#43ed2f567ada49d7e33a6e4b6f9babd060445fe5" 174 | integrity sha512-BSx/O0Z0SXOF5tY0bNTBcDEKz2Ec20GVYvq/H/XNKiUorUFilH7NPbFUuiiyzWaSdN3PA8JV0OvYx0gH/5aFAQ== 175 | dependencies: 176 | "@typescript-eslint/typescript-estree" "5.37.0" 177 | "@typescript-eslint/utils" "5.37.0" 178 | debug "^4.3.4" 179 | tsutils "^3.21.0" 180 | 181 | "@typescript-eslint/types@4.29.1": 182 | version "4.29.1" 183 | resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.1.tgz#94cce6cf7cc83451df03339cda99d326be2feaf5" 184 | integrity sha512-Jj2yu78IRfw4nlaLtKjVaGaxh/6FhofmQ/j8v3NXmAiKafbIqtAPnKYrf0sbGjKdj0hS316J8WhnGnErbJ4RCA== 185 | 186 | "@typescript-eslint/types@5.37.0": 187 | version "5.37.0" 188 | resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.37.0.tgz#09e4870a5f3af7af3f84e08d792644a87d232261" 189 | integrity sha512-3frIJiTa5+tCb2iqR/bf7XwU20lnU05r/sgPJnRpwvfZaqCJBrl8Q/mw9vr3NrNdB/XtVyMA0eppRMMBqdJ1bA== 190 | 191 | "@typescript-eslint/typescript-estree@4.29.1": 192 | version "4.29.1" 193 | resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.1.tgz#7b32a25ff8e51f2671ccc6b26cdbee3b1e6c5e7f" 194 | integrity sha512-lIkkrR9E4lwZkzPiRDNq0xdC3f2iVCUjw/7WPJ4S2Sl6C3nRWkeE1YXCQ0+KsiaQRbpY16jNaokdWnm9aUIsfw== 195 | dependencies: 196 | "@typescript-eslint/types" "4.29.1" 197 | "@typescript-eslint/visitor-keys" "4.29.1" 198 | debug "^4.3.1" 199 | globby "^11.0.3" 200 | is-glob "^4.0.1" 201 | semver "^7.3.5" 202 | tsutils "^3.21.0" 203 | 204 | "@typescript-eslint/typescript-estree@5.37.0": 205 | version "5.37.0" 206 | resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.37.0.tgz#956dcf5c98363bcb97bdd5463a0a86072ff79355" 207 | integrity sha512-JkFoFIt/cx59iqEDSgIGnQpCTRv96MQnXCYvJi7QhBC24uyuzbD8wVbajMB1b9x4I0octYFJ3OwjAwNqk1AjDA== 208 | dependencies: 209 | "@typescript-eslint/types" "5.37.0" 210 | "@typescript-eslint/visitor-keys" "5.37.0" 211 | debug "^4.3.4" 212 | globby "^11.1.0" 213 | is-glob "^4.0.3" 214 | semver "^7.3.7" 215 | tsutils "^3.21.0" 216 | 217 | "@typescript-eslint/utils@5.37.0": 218 | version "5.37.0" 219 | resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.37.0.tgz#7784cb8e91390c4f90ccaffd24a0cf9874df81b2" 220 | integrity sha512-jUEJoQrWbZhmikbcWSMDuUSxEE7ID2W/QCV/uz10WtQqfOuKZUqFGjqLJ+qhDd17rjgp+QJPqTdPIBWwoob2NQ== 221 | dependencies: 222 | "@types/json-schema" "^7.0.9" 223 | "@typescript-eslint/scope-manager" "5.37.0" 224 | "@typescript-eslint/types" "5.37.0" 225 | "@typescript-eslint/typescript-estree" "5.37.0" 226 | eslint-scope "^5.1.1" 227 | eslint-utils "^3.0.0" 228 | 229 | "@typescript-eslint/visitor-keys@4.29.1": 230 | version "4.29.1" 231 | resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.1.tgz#0615be8b55721f5e854f3ee99f1a714f2d093e5d" 232 | integrity sha512-zLqtjMoXvgdZY/PG6gqA73V8BjqPs4af1v2kiiETBObp+uC6gRYnJLmJHxC0QyUrrHDLJPIWNYxoBV3wbcRlag== 233 | dependencies: 234 | "@typescript-eslint/types" "4.29.1" 235 | eslint-visitor-keys "^2.0.0" 236 | 237 | "@typescript-eslint/visitor-keys@5.37.0": 238 | version "5.37.0" 239 | resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.37.0.tgz#7b72dd343295ea11e89b624995abc7103c554eee" 240 | integrity sha512-Hp7rT4cENBPIzMwrlehLW/28EVCOcE9U1Z1BQTc8EA8v5qpr7GRGuG+U58V5tTY48zvUOA3KHvw3rA8tY9fbdA== 241 | dependencies: 242 | "@typescript-eslint/types" "5.37.0" 243 | eslint-visitor-keys "^3.3.0" 244 | 245 | "@ungap/promise-all-settled@1.1.2": 246 | version "1.1.2" 247 | resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" 248 | integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== 249 | 250 | acorn-jsx@^5.3.2: 251 | version "5.3.2" 252 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" 253 | integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== 254 | 255 | acorn@^8.9.0: 256 | version "8.10.0" 257 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" 258 | integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== 259 | 260 | agent-base@6: 261 | version "6.0.2" 262 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" 263 | integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== 264 | dependencies: 265 | debug "4" 266 | 267 | ajv@^6.10.0, ajv@^6.12.4: 268 | version "6.12.6" 269 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" 270 | integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== 271 | dependencies: 272 | fast-deep-equal "^3.1.1" 273 | fast-json-stable-stringify "^2.0.0" 274 | json-schema-traverse "^0.4.1" 275 | uri-js "^4.2.2" 276 | 277 | ansi-colors@4.1.1: 278 | version "4.1.1" 279 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" 280 | integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== 281 | 282 | ansi-regex@^5.0.0, ansi-regex@^5.0.1: 283 | version "5.0.1" 284 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 285 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 286 | 287 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 288 | version "4.3.0" 289 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 290 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 291 | dependencies: 292 | color-convert "^2.0.1" 293 | 294 | anymatch@~3.1.2: 295 | version "3.1.2" 296 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" 297 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 298 | dependencies: 299 | normalize-path "^3.0.0" 300 | picomatch "^2.0.4" 301 | 302 | argparse@^2.0.1: 303 | version "2.0.1" 304 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 305 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 306 | 307 | array-union@^2.1.0: 308 | version "2.1.0" 309 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" 310 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== 311 | 312 | balanced-match@^1.0.0: 313 | version "1.0.2" 314 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 315 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 316 | 317 | binary-extensions@^2.0.0: 318 | version "2.2.0" 319 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 320 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 321 | 322 | brace-expansion@^1.1.7: 323 | version "1.1.11" 324 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 325 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 326 | dependencies: 327 | balanced-match "^1.0.0" 328 | concat-map "0.0.1" 329 | 330 | brace-expansion@^2.0.1: 331 | version "2.0.1" 332 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 333 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 334 | dependencies: 335 | balanced-match "^1.0.0" 336 | 337 | braces@^3.0.1, braces@~3.0.2: 338 | version "3.0.2" 339 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 340 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 341 | dependencies: 342 | fill-range "^7.0.1" 343 | 344 | browser-stdout@1.3.1: 345 | version "1.3.1" 346 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 347 | integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== 348 | 349 | callsites@^3.0.0: 350 | version "3.1.0" 351 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 352 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 353 | 354 | camelcase@^6.0.0: 355 | version "6.3.0" 356 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" 357 | integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== 358 | 359 | chalk@^4.0.0, chalk@^4.1.0: 360 | version "4.1.2" 361 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 362 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 363 | dependencies: 364 | ansi-styles "^4.1.0" 365 | supports-color "^7.1.0" 366 | 367 | chokidar@3.5.3: 368 | version "3.5.3" 369 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 370 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 371 | dependencies: 372 | anymatch "~3.1.2" 373 | braces "~3.0.2" 374 | glob-parent "~5.1.2" 375 | is-binary-path "~2.1.0" 376 | is-glob "~4.0.1" 377 | normalize-path "~3.0.0" 378 | readdirp "~3.6.0" 379 | optionalDependencies: 380 | fsevents "~2.3.2" 381 | 382 | cliui@^7.0.2: 383 | version "7.0.4" 384 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" 385 | integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== 386 | dependencies: 387 | string-width "^4.2.0" 388 | strip-ansi "^6.0.0" 389 | wrap-ansi "^7.0.0" 390 | 391 | color-convert@^2.0.1: 392 | version "2.0.1" 393 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 394 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 395 | dependencies: 396 | color-name "~1.1.4" 397 | 398 | color-name@~1.1.4: 399 | version "1.1.4" 400 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 401 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 402 | 403 | concat-map@0.0.1: 404 | version "0.0.1" 405 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 406 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 407 | 408 | cross-spawn@^7.0.2: 409 | version "7.0.3" 410 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 411 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 412 | dependencies: 413 | path-key "^3.1.0" 414 | shebang-command "^2.0.0" 415 | which "^2.0.1" 416 | 417 | debug@4, debug@4.3.4, debug@^4.3.2, debug@^4.3.4: 418 | version "4.3.4" 419 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 420 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 421 | dependencies: 422 | ms "2.1.2" 423 | 424 | debug@^4.1.1, debug@^4.3.1: 425 | version "4.3.2" 426 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" 427 | integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== 428 | dependencies: 429 | ms "2.1.2" 430 | 431 | decamelize@^4.0.0: 432 | version "4.0.0" 433 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" 434 | integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== 435 | 436 | deep-is@^0.1.3: 437 | version "0.1.3" 438 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 439 | integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= 440 | 441 | diff@5.0.0: 442 | version "5.0.0" 443 | resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" 444 | integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== 445 | 446 | dir-glob@^3.0.1: 447 | version "3.0.1" 448 | resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" 449 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 450 | dependencies: 451 | path-type "^4.0.0" 452 | 453 | doctrine@^3.0.0: 454 | version "3.0.0" 455 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" 456 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 457 | dependencies: 458 | esutils "^2.0.2" 459 | 460 | emoji-regex@^8.0.0: 461 | version "8.0.0" 462 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 463 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 464 | 465 | esbuild-android-64@0.15.7: 466 | version "0.15.7" 467 | resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz#a521604d8c4c6befc7affedc897df8ccde189bea" 468 | integrity sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w== 469 | 470 | esbuild-android-arm64@0.15.7: 471 | version "0.15.7" 472 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz#307b81f1088bf1e81dfe5f3d1d63a2d2a2e3e68e" 473 | integrity sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ== 474 | 475 | esbuild-darwin-64@0.15.7: 476 | version "0.15.7" 477 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz#270117b0c4ec6bcbc5cf3a297a7d11954f007e11" 478 | integrity sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg== 479 | 480 | esbuild-darwin-arm64@0.15.7: 481 | version "0.15.7" 482 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz#97851eacd11dacb7719713602e3319e16202fc77" 483 | integrity sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ== 484 | 485 | esbuild-freebsd-64@0.15.7: 486 | version "0.15.7" 487 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz#1de15ffaf5ae916aa925800aa6d02579960dd8c4" 488 | integrity sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ== 489 | 490 | esbuild-freebsd-arm64@0.15.7: 491 | version "0.15.7" 492 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz#0f160dbf5c9a31a1d8dd87acbbcb1a04b7031594" 493 | integrity sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q== 494 | 495 | esbuild-linux-32@0.15.7: 496 | version "0.15.7" 497 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz#422eb853370a5e40bdce8b39525380de11ccadec" 498 | integrity sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg== 499 | 500 | esbuild-linux-64@0.15.7: 501 | version "0.15.7" 502 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz#f89c468453bb3194b14f19dc32e0b99612e81d2b" 503 | integrity sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ== 504 | 505 | esbuild-linux-arm64@0.15.7: 506 | version "0.15.7" 507 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz#68a79d6eb5e032efb9168a0f340ccfd33d6350a1" 508 | integrity sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw== 509 | 510 | esbuild-linux-arm@0.15.7: 511 | version "0.15.7" 512 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz#2b7c784d0b3339878013dfa82bf5eaf82c7ce7d3" 513 | integrity sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ== 514 | 515 | esbuild-linux-mips64le@0.15.7: 516 | version "0.15.7" 517 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz#bb8330a50b14aa84673816cb63cc6c8b9beb62cc" 518 | integrity sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw== 519 | 520 | esbuild-linux-ppc64le@0.15.7: 521 | version "0.15.7" 522 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz#52544e7fa992811eb996674090d0bc41f067a14b" 523 | integrity sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw== 524 | 525 | esbuild-linux-riscv64@0.15.7: 526 | version "0.15.7" 527 | resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz#a43ae60697992b957e454cbb622f7ee5297e8159" 528 | integrity sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g== 529 | 530 | esbuild-linux-s390x@0.15.7: 531 | version "0.15.7" 532 | resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz#8c76a125dd10a84c166294d77416caaf5e1c7b64" 533 | integrity sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ== 534 | 535 | esbuild-netbsd-64@0.15.7: 536 | version "0.15.7" 537 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz#19b2e75449d7d9c32b5d8a222bac2f1e0c3b08fd" 538 | integrity sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ== 539 | 540 | esbuild-openbsd-64@0.15.7: 541 | version "0.15.7" 542 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz#1357b2bf72fd037d9150e751420a1fe4c8618ad7" 543 | integrity sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ== 544 | 545 | esbuild-sunos-64@0.15.7: 546 | version "0.15.7" 547 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz#87ab2c604592a9c3c763e72969da0d72bcde91d2" 548 | integrity sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag== 549 | 550 | esbuild-windows-32@0.15.7: 551 | version "0.15.7" 552 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz#c81e688c0457665a8d463a669e5bf60870323e99" 553 | integrity sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA== 554 | 555 | esbuild-windows-64@0.15.7: 556 | version "0.15.7" 557 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz#2421d1ae34b0561a9d6767346b381961266c4eff" 558 | integrity sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q== 559 | 560 | esbuild-windows-arm64@0.15.7: 561 | version "0.15.7" 562 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz#7d5e9e060a7b454cb2f57f84a3f3c23c8f30b7d2" 563 | integrity sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw== 564 | 565 | esbuild@^0.15.7: 566 | version "0.15.7" 567 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.7.tgz#8a1f1aff58671a3199dd24df95314122fc1ddee8" 568 | integrity sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw== 569 | optionalDependencies: 570 | "@esbuild/linux-loong64" "0.15.7" 571 | esbuild-android-64 "0.15.7" 572 | esbuild-android-arm64 "0.15.7" 573 | esbuild-darwin-64 "0.15.7" 574 | esbuild-darwin-arm64 "0.15.7" 575 | esbuild-freebsd-64 "0.15.7" 576 | esbuild-freebsd-arm64 "0.15.7" 577 | esbuild-linux-32 "0.15.7" 578 | esbuild-linux-64 "0.15.7" 579 | esbuild-linux-arm "0.15.7" 580 | esbuild-linux-arm64 "0.15.7" 581 | esbuild-linux-mips64le "0.15.7" 582 | esbuild-linux-ppc64le "0.15.7" 583 | esbuild-linux-riscv64 "0.15.7" 584 | esbuild-linux-s390x "0.15.7" 585 | esbuild-netbsd-64 "0.15.7" 586 | esbuild-openbsd-64 "0.15.7" 587 | esbuild-sunos-64 "0.15.7" 588 | esbuild-windows-32 "0.15.7" 589 | esbuild-windows-64 "0.15.7" 590 | esbuild-windows-arm64 "0.15.7" 591 | 592 | escalade@^3.1.1: 593 | version "3.1.1" 594 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 595 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 596 | 597 | escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: 598 | version "4.0.0" 599 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 600 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 601 | 602 | eslint-config-prettier@^8.5.0: 603 | version "8.5.0" 604 | resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" 605 | integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== 606 | 607 | eslint-plugin-prettier@^4.2.1: 608 | version "4.2.1" 609 | resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" 610 | integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== 611 | dependencies: 612 | prettier-linter-helpers "^1.0.0" 613 | 614 | eslint-scope@^5.1.1: 615 | version "5.1.1" 616 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" 617 | integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== 618 | dependencies: 619 | esrecurse "^4.3.0" 620 | estraverse "^4.1.1" 621 | 622 | eslint-scope@^7.2.0: 623 | version "7.2.0" 624 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" 625 | integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== 626 | dependencies: 627 | esrecurse "^4.3.0" 628 | estraverse "^5.2.0" 629 | 630 | eslint-utils@^3.0.0: 631 | version "3.0.0" 632 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" 633 | integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== 634 | dependencies: 635 | eslint-visitor-keys "^2.0.0" 636 | 637 | eslint-visitor-keys@^2.0.0: 638 | version "2.1.0" 639 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" 640 | integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== 641 | 642 | eslint-visitor-keys@^3.3.0: 643 | version "3.3.0" 644 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" 645 | integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== 646 | 647 | eslint-visitor-keys@^3.4.1: 648 | version "3.4.1" 649 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" 650 | integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== 651 | 652 | eslint@^8.44.0: 653 | version "8.44.0" 654 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" 655 | integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== 656 | dependencies: 657 | "@eslint-community/eslint-utils" "^4.2.0" 658 | "@eslint-community/regexpp" "^4.4.0" 659 | "@eslint/eslintrc" "^2.1.0" 660 | "@eslint/js" "8.44.0" 661 | "@humanwhocodes/config-array" "^0.11.10" 662 | "@humanwhocodes/module-importer" "^1.0.1" 663 | "@nodelib/fs.walk" "^1.2.8" 664 | ajv "^6.10.0" 665 | chalk "^4.0.0" 666 | cross-spawn "^7.0.2" 667 | debug "^4.3.2" 668 | doctrine "^3.0.0" 669 | escape-string-regexp "^4.0.0" 670 | eslint-scope "^7.2.0" 671 | eslint-visitor-keys "^3.4.1" 672 | espree "^9.6.0" 673 | esquery "^1.4.2" 674 | esutils "^2.0.2" 675 | fast-deep-equal "^3.1.3" 676 | file-entry-cache "^6.0.1" 677 | find-up "^5.0.0" 678 | glob-parent "^6.0.2" 679 | globals "^13.19.0" 680 | graphemer "^1.4.0" 681 | ignore "^5.2.0" 682 | import-fresh "^3.0.0" 683 | imurmurhash "^0.1.4" 684 | is-glob "^4.0.0" 685 | is-path-inside "^3.0.3" 686 | js-yaml "^4.1.0" 687 | json-stable-stringify-without-jsonify "^1.0.1" 688 | levn "^0.4.1" 689 | lodash.merge "^4.6.2" 690 | minimatch "^3.1.2" 691 | natural-compare "^1.4.0" 692 | optionator "^0.9.3" 693 | strip-ansi "^6.0.1" 694 | strip-json-comments "^3.1.0" 695 | text-table "^0.2.0" 696 | 697 | espree@^9.6.0: 698 | version "9.6.0" 699 | resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" 700 | integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== 701 | dependencies: 702 | acorn "^8.9.0" 703 | acorn-jsx "^5.3.2" 704 | eslint-visitor-keys "^3.4.1" 705 | 706 | esquery@^1.4.2: 707 | version "1.5.0" 708 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" 709 | integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== 710 | dependencies: 711 | estraverse "^5.1.0" 712 | 713 | esrecurse@^4.3.0: 714 | version "4.3.0" 715 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" 716 | integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== 717 | dependencies: 718 | estraverse "^5.2.0" 719 | 720 | estraverse@^4.1.1: 721 | version "4.3.0" 722 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 723 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 724 | 725 | estraverse@^5.1.0, estraverse@^5.2.0: 726 | version "5.2.0" 727 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" 728 | integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== 729 | 730 | esutils@^2.0.2: 731 | version "2.0.3" 732 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 733 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 734 | 735 | fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: 736 | version "3.1.3" 737 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 738 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 739 | 740 | fast-diff@^1.1.2: 741 | version "1.2.0" 742 | resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" 743 | integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== 744 | 745 | fast-glob@^3.1.1: 746 | version "3.2.7" 747 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" 748 | integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== 749 | dependencies: 750 | "@nodelib/fs.stat" "^2.0.2" 751 | "@nodelib/fs.walk" "^1.2.3" 752 | glob-parent "^5.1.2" 753 | merge2 "^1.3.0" 754 | micromatch "^4.0.4" 755 | 756 | fast-glob@^3.2.9: 757 | version "3.2.12" 758 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" 759 | integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== 760 | dependencies: 761 | "@nodelib/fs.stat" "^2.0.2" 762 | "@nodelib/fs.walk" "^1.2.3" 763 | glob-parent "^5.1.2" 764 | merge2 "^1.3.0" 765 | micromatch "^4.0.4" 766 | 767 | fast-json-stable-stringify@^2.0.0: 768 | version "2.1.0" 769 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 770 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 771 | 772 | fast-levenshtein@^2.0.6: 773 | version "2.0.6" 774 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 775 | integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= 776 | 777 | fastq@^1.6.0: 778 | version "1.11.1" 779 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" 780 | integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== 781 | dependencies: 782 | reusify "^1.0.4" 783 | 784 | file-entry-cache@^6.0.1: 785 | version "6.0.1" 786 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" 787 | integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== 788 | dependencies: 789 | flat-cache "^3.0.4" 790 | 791 | fill-range@^7.0.1: 792 | version "7.0.1" 793 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 794 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 795 | dependencies: 796 | to-regex-range "^5.0.1" 797 | 798 | find-up@5.0.0, find-up@^5.0.0: 799 | version "5.0.0" 800 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" 801 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 802 | dependencies: 803 | locate-path "^6.0.0" 804 | path-exists "^4.0.0" 805 | 806 | flat-cache@^3.0.4: 807 | version "3.0.4" 808 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" 809 | integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== 810 | dependencies: 811 | flatted "^3.1.0" 812 | rimraf "^3.0.2" 813 | 814 | flat@^5.0.2: 815 | version "5.0.2" 816 | resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" 817 | integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== 818 | 819 | flatted@^3.1.0: 820 | version "3.2.2" 821 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" 822 | integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== 823 | 824 | fs.realpath@^1.0.0: 825 | version "1.0.0" 826 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 827 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 828 | 829 | fsevents@~2.3.2: 830 | version "2.3.2" 831 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 832 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 833 | 834 | functional-red-black-tree@^1.0.1: 835 | version "1.0.1" 836 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 837 | integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= 838 | 839 | get-caller-file@^2.0.5: 840 | version "2.0.5" 841 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 842 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 843 | 844 | glob-parent@^5.1.2, glob-parent@~5.1.2: 845 | version "5.1.2" 846 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 847 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 848 | dependencies: 849 | is-glob "^4.0.1" 850 | 851 | glob-parent@^6.0.2: 852 | version "6.0.2" 853 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" 854 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 855 | dependencies: 856 | is-glob "^4.0.3" 857 | 858 | glob@7.2.0: 859 | version "7.2.0" 860 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" 861 | integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== 862 | dependencies: 863 | fs.realpath "^1.0.0" 864 | inflight "^1.0.4" 865 | inherits "2" 866 | minimatch "^3.0.4" 867 | once "^1.3.0" 868 | path-is-absolute "^1.0.0" 869 | 870 | glob@^7.1.3, glob@^7.1.7: 871 | version "7.1.7" 872 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" 873 | integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== 874 | dependencies: 875 | fs.realpath "^1.0.0" 876 | inflight "^1.0.4" 877 | inherits "2" 878 | minimatch "^3.0.4" 879 | once "^1.3.0" 880 | path-is-absolute "^1.0.0" 881 | 882 | globals@^13.19.0: 883 | version "13.20.0" 884 | resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" 885 | integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== 886 | dependencies: 887 | type-fest "^0.20.2" 888 | 889 | globby@^11.0.3: 890 | version "11.0.4" 891 | resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" 892 | integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== 893 | dependencies: 894 | array-union "^2.1.0" 895 | dir-glob "^3.0.1" 896 | fast-glob "^3.1.1" 897 | ignore "^5.1.4" 898 | merge2 "^1.3.0" 899 | slash "^3.0.0" 900 | 901 | globby@^11.1.0: 902 | version "11.1.0" 903 | resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" 904 | integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== 905 | dependencies: 906 | array-union "^2.1.0" 907 | dir-glob "^3.0.1" 908 | fast-glob "^3.2.9" 909 | ignore "^5.2.0" 910 | merge2 "^1.4.1" 911 | slash "^3.0.0" 912 | 913 | graphemer@^1.4.0: 914 | version "1.4.0" 915 | resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" 916 | integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== 917 | 918 | has-flag@^4.0.0: 919 | version "4.0.0" 920 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 921 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 922 | 923 | he@1.2.0: 924 | version "1.2.0" 925 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 926 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 927 | 928 | https-proxy-agent@5.0.0: 929 | version "5.0.0" 930 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" 931 | integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== 932 | dependencies: 933 | agent-base "6" 934 | debug "4" 935 | 936 | ignore@^5.1.4: 937 | version "5.1.8" 938 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" 939 | integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== 940 | 941 | ignore@^5.2.0: 942 | version "5.2.0" 943 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" 944 | integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== 945 | 946 | import-fresh@^3.0.0, import-fresh@^3.2.1: 947 | version "3.3.0" 948 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" 949 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 950 | dependencies: 951 | parent-module "^1.0.0" 952 | resolve-from "^4.0.0" 953 | 954 | imurmurhash@^0.1.4: 955 | version "0.1.4" 956 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 957 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= 958 | 959 | inflight@^1.0.4: 960 | version "1.0.6" 961 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 962 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 963 | dependencies: 964 | once "^1.3.0" 965 | wrappy "1" 966 | 967 | inherits@2: 968 | version "2.0.4" 969 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 970 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 971 | 972 | is-binary-path@~2.1.0: 973 | version "2.1.0" 974 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 975 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 976 | dependencies: 977 | binary-extensions "^2.0.0" 978 | 979 | is-extglob@^2.1.1: 980 | version "2.1.1" 981 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 982 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 983 | 984 | is-fullwidth-code-point@^3.0.0: 985 | version "3.0.0" 986 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 987 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 988 | 989 | is-glob@^4.0.0, is-glob@^4.0.1: 990 | version "4.0.1" 991 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" 992 | integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== 993 | dependencies: 994 | is-extglob "^2.1.1" 995 | 996 | is-glob@^4.0.3, is-glob@~4.0.1: 997 | version "4.0.3" 998 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 999 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 1000 | dependencies: 1001 | is-extglob "^2.1.1" 1002 | 1003 | is-number@^7.0.0: 1004 | version "7.0.0" 1005 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 1006 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1007 | 1008 | is-path-inside@^3.0.3: 1009 | version "3.0.3" 1010 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" 1011 | integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== 1012 | 1013 | is-plain-obj@^2.1.0: 1014 | version "2.1.0" 1015 | resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" 1016 | integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== 1017 | 1018 | is-unicode-supported@^0.1.0: 1019 | version "0.1.0" 1020 | resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" 1021 | integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== 1022 | 1023 | isexe@^2.0.0: 1024 | version "2.0.0" 1025 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 1026 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 1027 | 1028 | js-yaml@4.1.0, js-yaml@^4.1.0: 1029 | version "4.1.0" 1030 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 1031 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 1032 | dependencies: 1033 | argparse "^2.0.1" 1034 | 1035 | json-schema-traverse@^0.4.1: 1036 | version "0.4.1" 1037 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 1038 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 1039 | 1040 | json-stable-stringify-without-jsonify@^1.0.1: 1041 | version "1.0.1" 1042 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 1043 | integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= 1044 | 1045 | levn@^0.4.1: 1046 | version "0.4.1" 1047 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" 1048 | integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== 1049 | dependencies: 1050 | prelude-ls "^1.2.1" 1051 | type-check "~0.4.0" 1052 | 1053 | locate-path@^6.0.0: 1054 | version "6.0.0" 1055 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" 1056 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 1057 | dependencies: 1058 | p-locate "^5.0.0" 1059 | 1060 | lodash.merge@^4.6.2: 1061 | version "4.6.2" 1062 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 1063 | integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 1064 | 1065 | lodash@^4.17.21: 1066 | version "4.17.21" 1067 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 1068 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 1069 | 1070 | log-symbols@4.1.0: 1071 | version "4.1.0" 1072 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" 1073 | integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== 1074 | dependencies: 1075 | chalk "^4.1.0" 1076 | is-unicode-supported "^0.1.0" 1077 | 1078 | lru-cache@^6.0.0: 1079 | version "6.0.0" 1080 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 1081 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 1082 | dependencies: 1083 | yallist "^4.0.0" 1084 | 1085 | merge2@^1.3.0, merge2@^1.4.1: 1086 | version "1.4.1" 1087 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 1088 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 1089 | 1090 | micromatch@^4.0.4: 1091 | version "4.0.4" 1092 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" 1093 | integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== 1094 | dependencies: 1095 | braces "^3.0.1" 1096 | picomatch "^2.2.3" 1097 | 1098 | minimatch@5.0.1: 1099 | version "5.0.1" 1100 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" 1101 | integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== 1102 | dependencies: 1103 | brace-expansion "^2.0.1" 1104 | 1105 | minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: 1106 | version "3.1.2" 1107 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 1108 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 1109 | dependencies: 1110 | brace-expansion "^1.1.7" 1111 | 1112 | mixpanel@^0.17.0: 1113 | version "0.17.0" 1114 | resolved "https://registry.yarnpkg.com/mixpanel/-/mixpanel-0.17.0.tgz#ec57b068598c620cf039a5e504fb37c97ebfe8ce" 1115 | integrity sha512-DY5WeOy/hmkPrNiiZugJpWR0iMuOwuj1a3u0bgwB2eUFRV6oIew/pIahhpawdbNjb+Bye4a8ID3gefeNPvL81g== 1116 | dependencies: 1117 | https-proxy-agent "5.0.0" 1118 | 1119 | mocha@^10.0.0: 1120 | version "10.0.0" 1121 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9" 1122 | integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA== 1123 | dependencies: 1124 | "@ungap/promise-all-settled" "1.1.2" 1125 | ansi-colors "4.1.1" 1126 | browser-stdout "1.3.1" 1127 | chokidar "3.5.3" 1128 | debug "4.3.4" 1129 | diff "5.0.0" 1130 | escape-string-regexp "4.0.0" 1131 | find-up "5.0.0" 1132 | glob "7.2.0" 1133 | he "1.2.0" 1134 | js-yaml "4.1.0" 1135 | log-symbols "4.1.0" 1136 | minimatch "5.0.1" 1137 | ms "2.1.3" 1138 | nanoid "3.3.3" 1139 | serialize-javascript "6.0.0" 1140 | strip-json-comments "3.1.1" 1141 | supports-color "8.1.1" 1142 | workerpool "6.2.1" 1143 | yargs "16.2.0" 1144 | yargs-parser "20.2.4" 1145 | yargs-unparser "2.0.0" 1146 | 1147 | ms@2.1.2: 1148 | version "2.1.2" 1149 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 1150 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1151 | 1152 | ms@2.1.3: 1153 | version "2.1.3" 1154 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 1155 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 1156 | 1157 | nanoid@3.3.3: 1158 | version "3.3.3" 1159 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" 1160 | integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== 1161 | 1162 | natural-compare@^1.4.0: 1163 | version "1.4.0" 1164 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 1165 | integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= 1166 | 1167 | normalize-path@^3.0.0, normalize-path@~3.0.0: 1168 | version "3.0.0" 1169 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 1170 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 1171 | 1172 | once@^1.3.0: 1173 | version "1.4.0" 1174 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1175 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 1176 | dependencies: 1177 | wrappy "1" 1178 | 1179 | optionator@^0.9.3: 1180 | version "0.9.3" 1181 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" 1182 | integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== 1183 | dependencies: 1184 | "@aashutoshrathi/word-wrap" "^1.2.3" 1185 | deep-is "^0.1.3" 1186 | fast-levenshtein "^2.0.6" 1187 | levn "^0.4.1" 1188 | prelude-ls "^1.2.1" 1189 | type-check "^0.4.0" 1190 | 1191 | p-limit@^3.0.2: 1192 | version "3.1.0" 1193 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" 1194 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 1195 | dependencies: 1196 | yocto-queue "^0.1.0" 1197 | 1198 | p-locate@^5.0.0: 1199 | version "5.0.0" 1200 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" 1201 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 1202 | dependencies: 1203 | p-limit "^3.0.2" 1204 | 1205 | parent-module@^1.0.0: 1206 | version "1.0.1" 1207 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 1208 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1209 | dependencies: 1210 | callsites "^3.0.0" 1211 | 1212 | path-exists@^4.0.0: 1213 | version "4.0.0" 1214 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 1215 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 1216 | 1217 | path-is-absolute@^1.0.0: 1218 | version "1.0.1" 1219 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1220 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 1221 | 1222 | path-key@^3.1.0: 1223 | version "3.1.1" 1224 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 1225 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 1226 | 1227 | path-type@^4.0.0: 1228 | version "4.0.0" 1229 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" 1230 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 1231 | 1232 | picomatch@^2.0.4, picomatch@^2.2.1: 1233 | version "2.3.1" 1234 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 1235 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1236 | 1237 | picomatch@^2.2.3: 1238 | version "2.3.0" 1239 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" 1240 | integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== 1241 | 1242 | prelude-ls@^1.2.1: 1243 | version "1.2.1" 1244 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" 1245 | integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== 1246 | 1247 | prettier-linter-helpers@^1.0.0: 1248 | version "1.0.0" 1249 | resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" 1250 | integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== 1251 | dependencies: 1252 | fast-diff "^1.1.2" 1253 | 1254 | prettier@^2.7.1: 1255 | version "2.7.1" 1256 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" 1257 | integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== 1258 | 1259 | punycode@^2.1.0: 1260 | version "2.1.1" 1261 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 1262 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 1263 | 1264 | queue-microtask@^1.2.2: 1265 | version "1.2.3" 1266 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" 1267 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 1268 | 1269 | randombytes@^2.1.0: 1270 | version "2.1.0" 1271 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" 1272 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== 1273 | dependencies: 1274 | safe-buffer "^5.1.0" 1275 | 1276 | readdirp@~3.6.0: 1277 | version "3.6.0" 1278 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 1279 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 1280 | dependencies: 1281 | picomatch "^2.2.1" 1282 | 1283 | regexpp@^3.2.0: 1284 | version "3.2.0" 1285 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" 1286 | integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== 1287 | 1288 | require-directory@^2.1.1: 1289 | version "2.1.1" 1290 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 1291 | integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== 1292 | 1293 | resolve-from@^4.0.0: 1294 | version "4.0.0" 1295 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 1296 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1297 | 1298 | reusify@^1.0.4: 1299 | version "1.0.4" 1300 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" 1301 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1302 | 1303 | rimraf@^3.0.2: 1304 | version "3.0.2" 1305 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" 1306 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 1307 | dependencies: 1308 | glob "^7.1.3" 1309 | 1310 | run-parallel@^1.1.9: 1311 | version "1.2.0" 1312 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" 1313 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1314 | dependencies: 1315 | queue-microtask "^1.2.2" 1316 | 1317 | safe-buffer@^5.1.0: 1318 | version "5.2.1" 1319 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 1320 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1321 | 1322 | semver@^7.3.5: 1323 | version "7.3.5" 1324 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" 1325 | integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== 1326 | dependencies: 1327 | lru-cache "^6.0.0" 1328 | 1329 | semver@^7.3.7: 1330 | version "7.3.7" 1331 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" 1332 | integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== 1333 | dependencies: 1334 | lru-cache "^6.0.0" 1335 | 1336 | serialize-javascript@6.0.0: 1337 | version "6.0.0" 1338 | resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" 1339 | integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== 1340 | dependencies: 1341 | randombytes "^2.1.0" 1342 | 1343 | shebang-command@^2.0.0: 1344 | version "2.0.0" 1345 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 1346 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 1347 | dependencies: 1348 | shebang-regex "^3.0.0" 1349 | 1350 | shebang-regex@^3.0.0: 1351 | version "3.0.0" 1352 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 1353 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 1354 | 1355 | slash@^3.0.0: 1356 | version "3.0.0" 1357 | resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" 1358 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 1359 | 1360 | string-width@^4.1.0, string-width@^4.2.0: 1361 | version "4.2.3" 1362 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 1363 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 1364 | dependencies: 1365 | emoji-regex "^8.0.0" 1366 | is-fullwidth-code-point "^3.0.0" 1367 | strip-ansi "^6.0.1" 1368 | 1369 | strip-ansi@^6.0.0: 1370 | version "6.0.0" 1371 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 1372 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 1373 | dependencies: 1374 | ansi-regex "^5.0.0" 1375 | 1376 | strip-ansi@^6.0.1: 1377 | version "6.0.1" 1378 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1379 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1380 | dependencies: 1381 | ansi-regex "^5.0.1" 1382 | 1383 | strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: 1384 | version "3.1.1" 1385 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 1386 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 1387 | 1388 | supports-color@8.1.1: 1389 | version "8.1.1" 1390 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" 1391 | integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== 1392 | dependencies: 1393 | has-flag "^4.0.0" 1394 | 1395 | supports-color@^7.1.0: 1396 | version "7.2.0" 1397 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 1398 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1399 | dependencies: 1400 | has-flag "^4.0.0" 1401 | 1402 | text-table@^0.2.0: 1403 | version "0.2.0" 1404 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1405 | integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= 1406 | 1407 | to-regex-range@^5.0.1: 1408 | version "5.0.1" 1409 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1410 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1411 | dependencies: 1412 | is-number "^7.0.0" 1413 | 1414 | tslib@^1.8.1: 1415 | version "1.14.1" 1416 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" 1417 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 1418 | 1419 | tsutils@^3.21.0: 1420 | version "3.21.0" 1421 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" 1422 | integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== 1423 | dependencies: 1424 | tslib "^1.8.1" 1425 | 1426 | type-check@^0.4.0, type-check@~0.4.0: 1427 | version "0.4.0" 1428 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" 1429 | integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== 1430 | dependencies: 1431 | prelude-ls "^1.2.1" 1432 | 1433 | type-fest@^0.20.2: 1434 | version "0.20.2" 1435 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" 1436 | integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== 1437 | 1438 | typescript@^4.3.2: 1439 | version "4.3.5" 1440 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" 1441 | integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== 1442 | 1443 | uri-js@^4.2.2: 1444 | version "4.4.1" 1445 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" 1446 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 1447 | dependencies: 1448 | punycode "^2.1.0" 1449 | 1450 | which@^2.0.1: 1451 | version "2.0.2" 1452 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 1453 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1454 | dependencies: 1455 | isexe "^2.0.0" 1456 | 1457 | workerpool@6.2.1: 1458 | version "6.2.1" 1459 | resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" 1460 | integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== 1461 | 1462 | wrap-ansi@^7.0.0: 1463 | version "7.0.0" 1464 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 1465 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 1466 | dependencies: 1467 | ansi-styles "^4.0.0" 1468 | string-width "^4.1.0" 1469 | strip-ansi "^6.0.0" 1470 | 1471 | wrappy@1: 1472 | version "1.0.2" 1473 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1474 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1475 | 1476 | y18n@^5.0.5: 1477 | version "5.0.8" 1478 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" 1479 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 1480 | 1481 | yallist@^4.0.0: 1482 | version "4.0.0" 1483 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1484 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1485 | 1486 | yargs-parser@20.2.4: 1487 | version "20.2.4" 1488 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" 1489 | integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== 1490 | 1491 | yargs-parser@^20.2.2: 1492 | version "20.2.9" 1493 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" 1494 | integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== 1495 | 1496 | yargs-unparser@2.0.0: 1497 | version "2.0.0" 1498 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" 1499 | integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== 1500 | dependencies: 1501 | camelcase "^6.0.0" 1502 | decamelize "^4.0.0" 1503 | flat "^5.0.2" 1504 | is-plain-obj "^2.1.0" 1505 | 1506 | yargs@16.2.0: 1507 | version "16.2.0" 1508 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" 1509 | integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== 1510 | dependencies: 1511 | cliui "^7.0.2" 1512 | escalade "^3.1.1" 1513 | get-caller-file "^2.0.5" 1514 | require-directory "^2.1.1" 1515 | string-width "^4.2.0" 1516 | y18n "^5.0.5" 1517 | yargs-parser "^20.2.2" 1518 | 1519 | yocto-queue@^0.1.0: 1520 | version "0.1.0" 1521 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" 1522 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 1523 | --------------------------------------------------------------------------------