├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ └── release.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── docs └── images │ └── open-remote-ssh.gif ├── package.json ├── resources └── icon.png ├── src ├── authResolver.ts ├── commands.ts ├── common │ ├── disposable.ts │ ├── files.ts │ ├── logger.ts │ ├── platform.ts │ └── ports.ts ├── extension.ts ├── hostTreeView.ts ├── remoteLocationHistory.ts ├── serverConfig.ts ├── serverSetup.ts └── ssh │ ├── hostfile.ts │ ├── identityFiles.ts │ ├── sshConfig.ts │ ├── sshConnection.ts │ └── sshDestination.ts ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | out/ 3 | *.d.ts 4 | *.js 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | }, 7 | "plugins": [ 8 | "@typescript-eslint/eslint-plugin", 9 | "jsdoc" 10 | ], 11 | "rules": { 12 | "constructor-super": "warn", 13 | "curly": "warn", 14 | "eqeqeq": "warn", 15 | "no-buffer-constructor": "warn", 16 | "no-caller": "warn", 17 | "no-case-declarations": "warn", 18 | "no-debugger": "warn", 19 | "no-duplicate-case": "warn", 20 | "no-duplicate-imports": "warn", 21 | "no-eval": "warn", 22 | "no-async-promise-executor": "warn", 23 | "no-extra-semi": "warn", 24 | "no-new-wrappers": "warn", 25 | "no-redeclare": "off", 26 | "no-sparse-arrays": "warn", 27 | "no-throw-literal": "warn", 28 | "no-unsafe-finally": "warn", 29 | "no-unused-labels": "warn", 30 | "no-restricted-globals": [ 31 | "warn", 32 | "name", 33 | "length", 34 | "event", 35 | "closed", 36 | "external", 37 | "status", 38 | "origin", 39 | "orientation", 40 | "context" 41 | ], // non-complete list of globals that are easy to access unintentionally 42 | "no-var": "warn", 43 | "jsdoc/no-types": "warn", 44 | "semi": "off", 45 | "@typescript-eslint/semi": "warn", 46 | "@typescript-eslint/member-delimiter-style": "warn", 47 | "@typescript-eslint/naming-convention": [ 48 | "warn", 49 | { 50 | "selector": "class", 51 | "format": [ 52 | "PascalCase" 53 | ] 54 | } 55 | ], 56 | "quotes": "off", 57 | "@typescript-eslint/quotes": [ 58 | "warn", 59 | "single", 60 | { 61 | "allowTemplateLiterals": true 62 | } 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | release: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: 20 14 | 15 | - name: Install dependencies 16 | run: | 17 | yarn install --frozen-lockfile 18 | 19 | - name: Package extension 20 | id: package_vsix 21 | run: | 22 | yarn package 23 | echo ::set-output name=vsix_path::$(ls *.vsix) 24 | 25 | version=$(jq --raw-output '.version' package.json) 26 | echo ::set-output name=version::$version 27 | 28 | - name: Ensure new version 29 | run: | 30 | latest_tag=$(curl --silent -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/jeanp413/open-remote-ssh/releases/latest | jq --raw-output '.tag_name') 31 | if [ "$latest_tag" = "v${{ steps.package_vsix.outputs.version }}" ]; then 32 | exit 1 33 | fi 34 | 35 | - name: Create GitHub release 36 | uses: softprops/action-gh-release@v1 37 | with: 38 | tag_name: v${{ steps.package_vsix.outputs.version }} 39 | token: ${{ secrets.GITHUB_TOKEN }} 40 | files: ${{ steps.package_vsix.outputs.vsix_path }} 41 | 42 | # - name: Publish to MS Marketplace 43 | # run: | 44 | # npx vsce publish --packagePath ${{ steps.package_vsix.outputs.vsix_path }} 45 | # env: 46 | # VSCE_PAT: ${{ secrets.VSCE_PAT }} 47 | 48 | - name: Publish to Open VSX Registry 49 | run: | 50 | npx ovsx publish --packagePath ${{ steps.package_vsix.outputs.vsix_path }} 51 | env: 52 | OVSX_PAT: ${{ secrets.OPEN_VSX_PAT }} 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | out/ 3 | .vscode-test/ 4 | *.vsix 5 | .DS_Store 6 | package.insiders.json 7 | vscode.d.ts 8 | vscode.proposed.*.d.ts 9 | *.ignore.ts 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceFolder}" 11 | ], 12 | "outFiles": [ 13 | "${workspaceFolder}/out/**/*.js" 14 | ], 15 | "preLaunchTask": "npm: watch" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "watch", 7 | "problemMatcher": "$tsc-watch", 8 | "isBackground": true, 9 | "presentation": { 10 | "reveal": "never" 11 | }, 12 | "group": { 13 | "kind": "build", 14 | "isDefault": true 15 | } 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .github/** 2 | .vscode/** 3 | node_modules/** 4 | src/** 5 | build/** 6 | docs/** 7 | .gitignore 8 | tsconfig.json 9 | yarn.lock 10 | .eslintignore 11 | .eslintrc.json 12 | .gitpod.yml 13 | webpack.config.js 14 | package.insiders.json 15 | vscode.d.ts 16 | vscode.proposed.*.d.ts 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.4 2 | 3 | Update vscode-core to 1.97.2 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > Base on [Open Remote - SSH v0.0.48](https://github.com/jeanp413/open-remote-ssh). 2 | 3 | # Open Remote - SSH 4 | 5 | ![Open Remote SSH](https://raw.githubusercontent.com/jeanp413/open-remote-ssh/master/docs/images/open-remote-ssh.gif) 6 | 7 | ## SSH Host Requirements 8 | 9 | You can connect to a running SSH server on the following platforms. 10 | 11 | **Supported**: 12 | 13 | - x86_64 Debian 8+, Ubuntu 16.04+, CentOS / RHEL 7+ Linux. 14 | - ARMv7l (AArch32) Raspbian Stretch/9+ (32-bit). 15 | - ARMv8l (AArch64) Ubuntu 18.04+ (64-bit). 16 | - macOS 10.14+ (Mojave) 17 | - Windows 10+ 18 | - FreeBSD 13 (Requires manual remote-extension-host installation) 19 | - DragonFlyBSD (Requires manual remote-extension-host installation) 20 | 21 | ## Requirements 22 | 23 | **Activation** 24 | 25 | > NOTE: Not needed in VSCodium since version 1.75 26 | 27 | Enable the extension in your `argv.json` 28 | 29 | ```json 30 | { 31 | ... 32 | "enable-proposed-api": [ 33 | ..., 34 | "labring.open-remote-ssh-for-trae", 35 | ] 36 | ... 37 | } 38 | ``` 39 | 40 | which you can open by running the `Preferences: Configure Runtime Arguments` command. 41 | The file is located in `~/.vscode-oss/argv.json`. 42 | 43 | **Alpine linux** 44 | 45 | When running on alpine linux, the packages `libstdc++` and `bash` are necessary and can be installed via 46 | running 47 | 48 | ```bash 49 | sudo apk add bash libstdc++ 50 | ``` 51 | 52 | ## SSH configuration file 53 | 54 | [OpenSSH](https://www.openssh.com/) supports using a [configuration file](https://linuxize.com/post/using-the-ssh-config-file/) to store all your different SSH connections. To use an SSH config file, run the `Remote-SSH: Open SSH Configuration File...` command. 55 | -------------------------------------------------------------------------------- /docs/images/open-remote-ssh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labring/open-remote-ssh/197157aa2db115ccab059557c8f9b416e75bf84e/docs/images/open-remote-ssh.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-remote-ssh-for-trae", 3 | "displayName": "Remote - SSH for Trae", 4 | "description": "Use any remote machine with a SSH server as your development environment.", 5 | "version": "0.0.4", 6 | "publisher": "labring", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/labring/open-remote-ssh.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/labring/open-remote-ssh/issues" 13 | }, 14 | "icon": "resources/icon.png", 15 | "engines": { 16 | "vscode": "^1.70.2" 17 | }, 18 | "extensionKind": [ 19 | "ui" 20 | ], 21 | "enabledApiProposals": [ 22 | "resolvers", 23 | "contribViewsRemote" 24 | ], 25 | "keywords": [ 26 | "remote development", 27 | "remote", 28 | "ssh" 29 | ], 30 | "api": "none", 31 | "activationEvents": [ 32 | "onCommand:openremotessh.openEmptyWindow", 33 | "onCommand:openremotessh.openEmptyWindowInCurrentWindow", 34 | "onCommand:openremotessh.openConfigFile", 35 | "onCommand:openremotessh.showLog", 36 | "onResolveRemoteAuthority:ssh-remote", 37 | "onView:sshHosts" 38 | ], 39 | "main": "./out/extension.js", 40 | "contributes": { 41 | "configuration": { 42 | "title": "Remote - SSH", 43 | "properties": { 44 | "remote.SSH.configFile": { 45 | "type": "string", 46 | "description": "The absolute file path to a custom SSH config file.", 47 | "default": "", 48 | "scope": "application" 49 | }, 50 | "remote.SSH.connectTimeout": { 51 | "type": "number", 52 | "description": "Specifies the timeout in seconds used for the SSH command that connects to the remote.", 53 | "default": 60, 54 | "scope": "application", 55 | "minimum": 1 56 | }, 57 | "remote.SSH.defaultExtensions": { 58 | "type": "array", 59 | "items": { 60 | "type": "string" 61 | }, 62 | "description": "List of extensions that should be installed automatically on all SSH hosts.", 63 | "scope": "application" 64 | }, 65 | "remote.SSH.enableDynamicForwarding": { 66 | "type": "boolean", 67 | "description": "Whether to use SSH dynamic forwarding to allow setting up new port tunnels over an existing SSH connection.", 68 | "scope": "application", 69 | "default": true 70 | }, 71 | "remote.SSH.enableAgentForwarding": { 72 | "type": "boolean", 73 | "markdownDescription": "Enable fixing the remote environment so that the SSH config option `ForwardAgent` will take effect as expected from VS Code's remote extension host.", 74 | "scope": "application", 75 | "default": true 76 | }, 77 | "remote.SSH.serverDownloadUrlTemplate": { 78 | "type": "string", 79 | "description": "The URL from where the vscode server will be downloaded. You can use the following variables and they will be replaced dynamically:\n- ${quality}: vscode server quality, e.g. stable or insiders\n- ${version}: vscode server version, e.g. 1.69.0\n- ${commit}: vscode server release commit\n- ${arch}: vscode server arch, e.g. x64, armhf, arm64\n- ${release}: release number, vscodium only https://github.com/VSCodium/vscodium/pull/1192", 80 | "scope": "application", 81 | "default": "https://github.com/VSCodium/vscodium/releases/download/${version}.${release}/vscodium-reh-${os}-${arch}-${version}.${release}.tar.gz" 82 | }, 83 | "remote.SSH.remotePlatform": { 84 | "type": "object", 85 | "description": "A map of the remote hostname to the platform for that remote. Valid values: linux, macos, windows.", 86 | "scope": "application", 87 | "default": {}, 88 | "additionalProperties": { 89 | "type": "string", 90 | "enum": [ 91 | "linux", 92 | "macos", 93 | "windows" 94 | ] 95 | } 96 | }, 97 | "remote.SSH.remoteServerListenOnSocket": { 98 | "type": "boolean", 99 | "description": "When true, the remote vscode server will listen on a socket path instead of opening a port. Only valid for Linux and macOS remotes. Requires `AllowStreamLocalForwarding` to be enabled for the SSH server.", 100 | "default": false 101 | }, 102 | "remote.SSH.experimental.serverBinaryName": { 103 | "type": "string", 104 | "description": "**Experimental:** The name of the server binary, use this **only if** you are using a client without a corresponding server release", 105 | "scope": "application", 106 | "default": "" 107 | } 108 | } 109 | }, 110 | "views": { 111 | "remote": [ 112 | { 113 | "id": "sshHosts", 114 | "name": "SSH Targets", 115 | "group": "targets@1", 116 | "remoteName": "ssh-remote" 117 | } 118 | ] 119 | }, 120 | "commands": [ 121 | { 122 | "command": "openremotessh.openEmptyWindow", 123 | "title": "Connect to Host...", 124 | "category": "Remote-SSH" 125 | }, 126 | { 127 | "command": "openremotessh.openEmptyWindowInCurrentWindow", 128 | "title": "Connect Current Window to Host...", 129 | "category": "Remote-SSH" 130 | }, 131 | { 132 | "command": "openremotessh.openConfigFile", 133 | "title": "Open SSH Configuration File...", 134 | "category": "Remote-SSH" 135 | }, 136 | { 137 | "command": "openremotessh.showLog", 138 | "title": "Show Log", 139 | "category": "Remote-SSH" 140 | }, 141 | { 142 | "command": "openremotessh.explorer.emptyWindowInNewWindow", 143 | "title": "Connect to Host in New Window", 144 | "icon": "$(empty-window)" 145 | }, 146 | { 147 | "command": "openremotessh.explorer.emptyWindowInCurrentWindow", 148 | "title": "Connect to Host in Current Window" 149 | }, 150 | { 151 | "command": "openremotessh.explorer.reopenFolderInCurrentWindow", 152 | "title": "Open on SSH Host in Current Window" 153 | }, 154 | { 155 | "command": "openremotessh.explorer.reopenFolderInNewWindow", 156 | "title": "Open on SSH Host in New Window", 157 | "icon": "$(folder-opened)" 158 | }, 159 | { 160 | "command": "openremotessh.explorer.deleteFolderHistoryItem", 161 | "title": "Remove From Recent List", 162 | "icon": "$(x)" 163 | }, 164 | { 165 | "command": "openremotessh.explorer.refresh", 166 | "title": "Refresh", 167 | "icon": "$(refresh)" 168 | }, 169 | { 170 | "command": "openremotessh.explorer.configure", 171 | "title": "Configure", 172 | "icon": "$(gear)" 173 | }, 174 | { 175 | "command": "openremotessh.explorer.add", 176 | "title": "Add New", 177 | "icon": "$(plus)" 178 | } 179 | ], 180 | "resourceLabelFormatters": [ 181 | { 182 | "scheme": "vscode-remote", 183 | "authority": "ssh-remote+*", 184 | "formatting": { 185 | "label": "${path}", 186 | "separator": "/", 187 | "tildify": true, 188 | "workspaceSuffix": "SSH" 189 | } 190 | } 191 | ], 192 | "menus": { 193 | "statusBar/remoteIndicator": [ 194 | { 195 | "command": "openremotessh.openEmptyWindow", 196 | "when": "remoteName =~ /^ssh-remote$/ && remoteConnectionState == connected", 197 | "group": "remote_20_ssh_1general@1" 198 | }, 199 | { 200 | "command": "openremotessh.openEmptyWindowInCurrentWindow", 201 | "when": "remoteName =~ /^ssh-remote$/ && remoteConnectionState == connected", 202 | "group": "remote_20_ssh_1general@2" 203 | }, 204 | { 205 | "command": "openremotessh.openConfigFile", 206 | "when": "remoteName =~ /^ssh-remote$/ && remoteConnectionState == connected", 207 | "group": "remote_20_ssh_1general@3" 208 | }, 209 | { 210 | "command": "openremotessh.showLog", 211 | "when": "remoteName =~ /^ssh-remote$/ && remoteConnectionState == connected", 212 | "group": "remote_20_ssh_1general@4" 213 | }, 214 | { 215 | "command": "openremotessh.openEmptyWindow", 216 | "when": "remoteConnectionState == disconnected", 217 | "group": "remote_20_ssh_3local@1" 218 | }, 219 | { 220 | "command": "openremotessh.openEmptyWindowInCurrentWindow", 221 | "when": "remoteConnectionState == disconnected", 222 | "group": "remote_20_ssh_3local@2" 223 | }, 224 | { 225 | "command": "openremotessh.openConfigFile", 226 | "when": "remoteConnectionState == disconnected", 227 | "group": "remote_20_ssh_3local@3" 228 | }, 229 | { 230 | "command": "openremotessh.openEmptyWindow", 231 | "when": "!remoteName && !virtualWorkspace", 232 | "group": "remote_20_ssh_3local@5" 233 | }, 234 | { 235 | "command": "openremotessh.openEmptyWindowInCurrentWindow", 236 | "when": "!remoteName && !virtualWorkspace", 237 | "group": "remote_20_ssh_3local@6" 238 | }, 239 | { 240 | "command": "openremotessh.openConfigFile", 241 | "when": "!remoteName && !virtualWorkspace", 242 | "group": "remote_20_ssh_3local@7" 243 | } 244 | ], 245 | "commandPalette": [ 246 | { 247 | "command": "openremotessh.explorer.refresh", 248 | "when": "false" 249 | }, 250 | { 251 | "command": "openremotessh.explorer.configure", 252 | "when": "false" 253 | }, 254 | { 255 | "command": "openremotessh.explorer.add", 256 | "when": "false" 257 | }, 258 | { 259 | "command": "openremotessh.explorer.emptyWindowInNewWindow", 260 | "when": "false" 261 | }, 262 | { 263 | "command": "openremotessh.explorer.emptyWindowInCurrentWindow", 264 | "when": "false" 265 | }, 266 | { 267 | "command": "openremotessh.explorer.reopenFolderInCurrentWindow", 268 | "when": "false" 269 | }, 270 | { 271 | "command": "openremotessh.explorer.reopenFolderInNewWindow", 272 | "when": "false" 273 | }, 274 | { 275 | "command": "openremotessh.explorer.deleteFolderHistoryItem", 276 | "when": "false" 277 | } 278 | ], 279 | "view/title": [ 280 | { 281 | "command": "openremotessh.explorer.add", 282 | "when": "view == sshHosts", 283 | "group": "navigation" 284 | }, 285 | { 286 | "command": "openremotessh.explorer.configure", 287 | "when": "view == sshHosts", 288 | "group": "navigation" 289 | }, 290 | { 291 | "command": "openremotessh.explorer.refresh", 292 | "when": "view == sshHosts", 293 | "group": "navigation" 294 | } 295 | ], 296 | "view/item/context": [ 297 | { 298 | "command": "openremotessh.explorer.emptyWindowInNewWindow", 299 | "when": "viewItem =~ /^openremotessh.explorer.host$/", 300 | "group": "inline@1" 301 | }, 302 | { 303 | "command": "openremotessh.explorer.emptyWindowInNewWindow", 304 | "when": "viewItem =~ /^openremotessh.explorer.host$/", 305 | "group": "navigation@2" 306 | }, 307 | { 308 | "command": "openremotessh.explorer.emptyWindowInCurrentWindow", 309 | "when": "viewItem =~ /^openremotessh.explorer.host$/", 310 | "group": "navigation@1" 311 | }, 312 | { 313 | "command": "openremotessh.explorer.reopenFolderInNewWindow", 314 | "when": "viewItem == openremotessh.explorer.folder", 315 | "group": "inline@1" 316 | }, 317 | { 318 | "command": "openremotessh.explorer.reopenFolderInNewWindow", 319 | "when": "viewItem == openremotessh.explorer.folder", 320 | "group": "navigation@2" 321 | }, 322 | { 323 | "command": "openremotessh.explorer.reopenFolderInCurrentWindow", 324 | "when": "viewItem == openremotessh.explorer.folder", 325 | "group": "navigation@1" 326 | }, 327 | { 328 | "command": "openremotessh.explorer.deleteFolderHistoryItem", 329 | "when": "viewItem =~ /^openremotessh.explorer.folder/", 330 | "group": "navigation@3" 331 | }, 332 | { 333 | "command": "openremotessh.explorer.deleteFolderHistoryItem", 334 | "when": "viewItem =~ /^openremotessh.explorer.folder/", 335 | "group": "inline@2" 336 | } 337 | ] 338 | } 339 | }, 340 | "scripts": { 341 | "postinstall": "yarn update-dts", 342 | "vscode:prepublish": "webpack --mode production", 343 | "webpack": "webpack --mode development", 344 | "compile": "tsc -b", 345 | "watch": "tsc -b -w", 346 | "package": "npx vsce package --yarn", 347 | "update-dts": "npx vscode-dts dev && npx vscode-dts main" 348 | }, 349 | "devDependencies": { 350 | "@types/node": "^16.11.0", 351 | "@types/ssh2": "^0.5.52", 352 | "@types/ssh2-streams": "0.1.12", 353 | "@types/webpack": "^5.28.0", 354 | "@typescript-eslint/eslint-plugin": "^5.19.0", 355 | "@typescript-eslint/parser": "^5.19.0", 356 | "eslint": "^8.13.0", 357 | "eslint-plugin-jsdoc": "^19.1.0", 358 | "ts-loader": "^9.2.7", 359 | "typescript": "^4.6.3", 360 | "webpack": "^5.42.0", 361 | "webpack-cli": "^4.7.2" 362 | }, 363 | "dependencies": { 364 | "glob": "^9.3.1", 365 | "simple-socks": "git+https://github.com/jeanp413/simple-socks#main", 366 | "socks": "^2.5.0", 367 | "@jeanp413/ssh-config": "^4.3.1", 368 | "ssh2": "git+https://github.com/jeanp413/ssh2#master" 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labring/open-remote-ssh/197157aa2db115ccab059557c8f9b416e75bf84e/resources/icon.png -------------------------------------------------------------------------------- /src/authResolver.ts: -------------------------------------------------------------------------------- 1 | import * as cp from 'child_process'; 2 | import * as fs from 'fs'; 3 | import * as net from 'net'; 4 | import * as stream from 'stream'; 5 | import { SocksClient, SocksClientOptions } from 'socks'; 6 | import * as vscode from 'vscode'; 7 | import * as ssh2 from 'ssh2'; 8 | import type { ParsedKey } from 'ssh2-streams'; 9 | import Log from './common/logger'; 10 | import SSHDestination from './ssh/sshDestination'; 11 | import SSHConnection, { SSHTunnelConfig } from './ssh/sshConnection'; 12 | import SSHConfiguration from './ssh/sshConfig'; 13 | import { gatherIdentityFiles } from './ssh/identityFiles'; 14 | import { untildify, exists as fileExists } from './common/files'; 15 | import { findRandomPort } from './common/ports'; 16 | import { disposeAll } from './common/disposable'; 17 | import { installCodeServer, ServerInstallError } from './serverSetup'; 18 | import { isWindows } from './common/platform'; 19 | import * as os from 'os'; 20 | 21 | const PASSWORD_RETRY_COUNT = 3; 22 | const PASSPHRASE_RETRY_COUNT = 3; 23 | 24 | export const REMOTE_SSH_AUTHORITY = 'ssh-remote'; 25 | 26 | export function getRemoteAuthority(host: string) { 27 | return `${REMOTE_SSH_AUTHORITY}+${host}`; 28 | } 29 | 30 | class TunnelInfo implements vscode.Disposable { 31 | constructor( 32 | readonly localPort: number, 33 | readonly remotePortOrSocketPath: number | string, 34 | private disposables: vscode.Disposable[] 35 | ) { 36 | } 37 | 38 | dispose() { 39 | disposeAll(this.disposables); 40 | } 41 | } 42 | 43 | interface SSHKey { 44 | filename: string; 45 | parsedKey: ParsedKey; 46 | fingerprint: string; 47 | agentSupport?: boolean; 48 | isPrivate?: boolean; 49 | } 50 | 51 | export class RemoteSSHResolver implements vscode.RemoteAuthorityResolver, vscode.Disposable { 52 | 53 | private proxyConnections: SSHConnection[] = []; 54 | private sshConnection: SSHConnection | undefined; 55 | private sshAgentSock: string | undefined; 56 | private proxyCommandProcess: cp.ChildProcessWithoutNullStreams | undefined; 57 | 58 | private socksTunnel: SSHTunnelConfig | undefined; 59 | private tunnels: TunnelInfo[] = []; 60 | 61 | private labelFormatterDisposable: vscode.Disposable | undefined; 62 | 63 | constructor( 64 | readonly context: vscode.ExtensionContext, 65 | readonly logger: Log 66 | ) { 67 | } 68 | 69 | resolve(authority: string, context: vscode.RemoteAuthorityResolverContext): Thenable { 70 | const [type, dest] = authority.split('+'); 71 | if (type !== REMOTE_SSH_AUTHORITY) { 72 | throw new Error(`Invalid authority type for SSH resolver: ${type}`); 73 | } 74 | 75 | this.logger.info(`Resolving ssh remote authority '${authority}' (attemp #${context.resolveAttempt})`); 76 | 77 | const sshDest = SSHDestination.parseEncoded(dest); 78 | 79 | // It looks like default values are not loaded yet when resolving a remote, 80 | // so let's hardcode the default values here 81 | const remoteSSHconfig = vscode.workspace.getConfiguration('remote.SSH'); 82 | const enableDynamicForwarding = remoteSSHconfig.get('enableDynamicForwarding', true)!; 83 | const enableAgentForwarding = remoteSSHconfig.get('enableAgentForwarding', true)!; 84 | const serverDownloadUrlTemplate = remoteSSHconfig.get('serverDownloadUrlTemplate'); 85 | const defaultExtensions = remoteSSHconfig.get('defaultExtensions', []); 86 | const remotePlatformMap = remoteSSHconfig.get>('remotePlatform', {}); 87 | const remoteServerListenOnSocket = remoteSSHconfig.get('remoteServerListenOnSocket', false)!; 88 | const connectTimeout = remoteSSHconfig.get('connectTimeout', 60)!; 89 | 90 | return vscode.window.withProgress({ 91 | title: `Setting up SSH Host ${sshDest.hostname}`, 92 | location: vscode.ProgressLocation.Notification, 93 | cancellable: false 94 | }, async () => { 95 | try { 96 | const sshconfig = await SSHConfiguration.loadFromFS(); 97 | const sshHostConfig = sshconfig.getHostConfiguration(sshDest.hostname); 98 | const sshHostName = sshHostConfig['HostName'] ? sshHostConfig['HostName'].replace('%h', sshDest.hostname) : sshDest.hostname; 99 | const sshUser = sshHostConfig['User'] || sshDest.user || os.userInfo().username || ''; // https://github.com/openssh/openssh-portable/blob/5ec5504f1d328d5bfa64280cd617c3efec4f78f3/sshconnect.c#L1561-L1562 100 | const sshPort = sshHostConfig['Port'] ? parseInt(sshHostConfig['Port'], 10) : (sshDest.port || 22); 101 | 102 | this.sshAgentSock = sshHostConfig['IdentityAgent'] || process.env['SSH_AUTH_SOCK'] || (isWindows ? '\\\\.\\pipe\\openssh-ssh-agent' : undefined); 103 | this.sshAgentSock = this.sshAgentSock ? untildify(this.sshAgentSock) : undefined; 104 | const agentForward = enableAgentForwarding && (sshHostConfig['ForwardAgent'] || 'no').toLowerCase() === 'yes'; 105 | const agent = agentForward && this.sshAgentSock ? new ssh2.OpenSSHAgent(this.sshAgentSock) : undefined; 106 | 107 | const preferredAuthentications = sshHostConfig['PreferredAuthentications'] ? sshHostConfig['PreferredAuthentications'].split(',').map(s => s.trim()) : ['publickey', 'password', 'keyboard-interactive']; 108 | 109 | const identityFiles: string[] = (sshHostConfig['IdentityFile'] as unknown as string[]) || []; 110 | const identitiesOnly = (sshHostConfig['IdentitiesOnly'] || 'no').toLowerCase() === 'yes'; 111 | const identityKeys = await gatherIdentityFiles(identityFiles, this.sshAgentSock, identitiesOnly, this.logger); 112 | 113 | // Create proxy jump connections if any 114 | let proxyStream: ssh2.ClientChannel | stream.Duplex | undefined; 115 | if (sshHostConfig['ProxyJump']) { 116 | const proxyJumps = sshHostConfig['ProxyJump'].split(',').filter(i => !!i.trim()) 117 | .map(i => { 118 | const proxy = SSHDestination.parse(i); 119 | const proxyHostConfig = sshconfig.getHostConfiguration(proxy.hostname); 120 | return [proxy, proxyHostConfig] as [SSHDestination, Record]; 121 | }); 122 | for (let i = 0; i < proxyJumps.length; i++) { 123 | const [proxy, proxyHostConfig] = proxyJumps[i]; 124 | const proxyHostName = proxyHostConfig['HostName'] || proxy.hostname; 125 | const proxyUser = proxyHostConfig['User'] || proxy.user || sshUser; 126 | const proxyPort = proxyHostConfig['Port'] ? parseInt(proxyHostConfig['Port'], 10) : (proxy.port || sshPort); 127 | 128 | const proxyAgentForward = enableAgentForwarding && (proxyHostConfig['ForwardAgent'] || 'no').toLowerCase() === 'yes'; 129 | const proxyAgent = proxyAgentForward && this.sshAgentSock ? new ssh2.OpenSSHAgent(this.sshAgentSock) : undefined; 130 | 131 | const proxyIdentityFiles: string[] = (proxyHostConfig['IdentityFile'] as unknown as string[]) || []; 132 | const proxyIdentitiesOnly = (proxyHostConfig['IdentitiesOnly'] || 'no').toLowerCase() === 'yes'; 133 | const proxyIdentityKeys = await gatherIdentityFiles(proxyIdentityFiles, this.sshAgentSock, proxyIdentitiesOnly, this.logger); 134 | 135 | const proxyAuthHandler = this.getSSHAuthHandler(proxyUser, proxyHostName, proxyIdentityKeys, preferredAuthentications); 136 | const proxyConnection = new SSHConnection({ 137 | host: !proxyStream ? proxyHostName : undefined, 138 | port: !proxyStream ? proxyPort : undefined, 139 | sock: proxyStream, 140 | username: proxyUser, 141 | readyTimeout: connectTimeout * 1000, 142 | strictVendor: false, 143 | agentForward: proxyAgentForward, 144 | agent: proxyAgent, 145 | authHandler: (arg0, arg1, arg2) => (proxyAuthHandler(arg0, arg1, arg2), undefined) 146 | }); 147 | this.proxyConnections.push(proxyConnection); 148 | 149 | const nextProxyJump = i < proxyJumps.length - 1 ? proxyJumps[i + 1] : undefined; 150 | const destIP = nextProxyJump ? (nextProxyJump[1]['HostName'] || nextProxyJump[0].hostname) : sshHostName; 151 | const destPort = nextProxyJump ? ((nextProxyJump[1]['Port'] && parseInt(nextProxyJump[1]['Port'], 10)) || nextProxyJump[0].port || 22) : sshPort; 152 | proxyStream = await proxyConnection.forwardOut('127.0.0.1', 0, destIP, destPort); 153 | } 154 | } else if (sshHostConfig['ProxyCommand']) { 155 | let proxyArgs = (sshHostConfig['ProxyCommand'] as unknown as string[]) 156 | .map((arg) => arg.replace('%h', sshHostName).replace('%n', sshDest.hostname).replace('%p', sshPort.toString()).replace('%r', sshUser)); 157 | let proxyCommand = proxyArgs.shift()!; 158 | 159 | let options = {}; 160 | if (isWindows && /\.(bat|cmd)$/.test(proxyCommand)) { 161 | proxyCommand = `"${proxyCommand}"`; 162 | proxyArgs = proxyArgs.map((arg) => arg.includes(' ') ? `"${arg}"` : arg); 163 | options = { shell: true, windowsHide: true, windowsVerbatimArguments: true }; 164 | } 165 | 166 | this.logger.trace(`Spawning ProxyCommand: ${proxyCommand} ${proxyArgs.join(' ')}`); 167 | 168 | const child = cp.spawn(proxyCommand, proxyArgs, options); 169 | proxyStream = stream.Duplex.from({ readable: child.stdout, writable: child.stdin }); 170 | this.proxyCommandProcess = child; 171 | } 172 | 173 | // Create final shh connection 174 | const sshAuthHandler = this.getSSHAuthHandler(sshUser, sshHostName, identityKeys, preferredAuthentications); 175 | 176 | this.sshConnection = new SSHConnection({ 177 | host: !proxyStream ? sshHostName : undefined, 178 | port: !proxyStream ? sshPort : undefined, 179 | sock: proxyStream, 180 | username: sshUser, 181 | readyTimeout: connectTimeout * 1000, 182 | strictVendor: false, 183 | agentForward, 184 | agent, 185 | authHandler: (arg0, arg1, arg2) => (sshAuthHandler(arg0, arg1, arg2), undefined), 186 | }); 187 | await this.sshConnection.connect(); 188 | 189 | const envVariables: Record = {}; 190 | if (agentForward) { 191 | envVariables['SSH_AUTH_SOCK'] = null; 192 | } 193 | 194 | const installResult = await installCodeServer(this.sshConnection, serverDownloadUrlTemplate, defaultExtensions, Object.keys(envVariables), remotePlatformMap[sshDest.hostname], remoteServerListenOnSocket, this.logger); 195 | 196 | for (const key of Object.keys(envVariables)) { 197 | if (installResult[key] !== undefined) { 198 | envVariables[key] = installResult[key]; 199 | } 200 | } 201 | 202 | // Update terminal env variables 203 | this.context.environmentVariableCollection.persistent = false; 204 | for (const [key, value] of Object.entries(envVariables)) { 205 | if (value) { 206 | this.context.environmentVariableCollection.replace(key, value); 207 | } 208 | } 209 | 210 | if (enableDynamicForwarding) { 211 | const socksPort = await findRandomPort(); 212 | this.socksTunnel = await this.sshConnection!.addTunnel({ 213 | name: `ssh_tunnel_socks_${socksPort}`, 214 | localPort: socksPort, 215 | socks: true 216 | }); 217 | } 218 | 219 | const tunnelConfig = await this.openTunnel(0, installResult.listeningOn); 220 | this.tunnels.push(tunnelConfig); 221 | 222 | // Enable ports view 223 | vscode.commands.executeCommand('setContext', 'forwardedPortsViewEnabled', true); 224 | 225 | this.labelFormatterDisposable?.dispose(); 226 | this.labelFormatterDisposable = vscode.workspace.registerResourceLabelFormatter({ 227 | scheme: 'vscode-remote', 228 | authority: `${REMOTE_SSH_AUTHORITY}+*`, 229 | formatting: { 230 | label: '${path}', 231 | separator: '/', 232 | tildify: true, 233 | workspaceSuffix: `SSH: ${sshDest.hostname}` + (sshDest.port && sshDest.port !== 22 ? `:${sshDest.port}` : '') 234 | } 235 | }); 236 | 237 | const resolvedResult: vscode.ResolverResult = new vscode.ResolvedAuthority('127.0.0.1', tunnelConfig.localPort, installResult.connectionToken); 238 | resolvedResult.extensionHostEnv = envVariables; 239 | return resolvedResult; 240 | } catch (e: unknown) { 241 | this.logger.error(`Error resolving authority`, e); 242 | 243 | // Initial connection 244 | if (context.resolveAttempt === 1) { 245 | this.logger.show(); 246 | 247 | const closeRemote = 'Close Remote'; 248 | const retry = 'Retry'; 249 | const result = await vscode.window.showErrorMessage(`Could not establish connection to "${sshDest.hostname}"`, { modal: true }, closeRemote, retry); 250 | if (result === closeRemote) { 251 | await vscode.commands.executeCommand('workbench.action.remote.close'); 252 | } else if (result === retry) { 253 | await vscode.commands.executeCommand('workbench.action.reloadWindow'); 254 | } 255 | } 256 | 257 | if (e instanceof ServerInstallError || !(e instanceof Error)) { 258 | throw vscode.RemoteAuthorityResolverError.NotAvailable(e instanceof Error ? e.message : String(e)); 259 | } else { 260 | throw vscode.RemoteAuthorityResolverError.TemporarilyNotAvailable(e.message); 261 | } 262 | } 263 | }); 264 | } 265 | 266 | private async openTunnel(localPort: number, remotePortOrSocketPath: number | string) { 267 | localPort = localPort > 0 ? localPort : await findRandomPort(); 268 | 269 | const disposables: vscode.Disposable[] = []; 270 | const remotePort = typeof remotePortOrSocketPath === 'number' ? remotePortOrSocketPath : undefined; 271 | const remoteSocketPath = typeof remotePortOrSocketPath === 'string' ? remotePortOrSocketPath : undefined; 272 | if (this.socksTunnel && remotePort) { 273 | const forwardingServer = await new Promise((resolve, reject) => { 274 | this.logger.trace(`Creating forwarding server ${localPort}(local) => ${this.socksTunnel!.localPort!}(socks) => ${remotePort}(remote)`); 275 | const socksOptions: SocksClientOptions = { 276 | proxy: { 277 | host: '127.0.0.1', 278 | port: this.socksTunnel!.localPort!, 279 | type: 5 280 | }, 281 | command: 'connect', 282 | destination: { 283 | host: '127.0.0.1', 284 | port: remotePort 285 | } 286 | }; 287 | const server: net.Server = net.createServer() 288 | .on('error', reject) 289 | .on('connection', async (socket: net.Socket) => { 290 | try { 291 | const socksConn = await SocksClient.createConnection(socksOptions); 292 | socket.pipe(socksConn.socket); 293 | socksConn.socket.pipe(socket); 294 | } catch (error) { 295 | this.logger.error(`Error while creating SOCKS connection`, error); 296 | } 297 | }) 298 | .on('listening', () => resolve(server)) 299 | .listen(localPort); 300 | }); 301 | disposables.push({ 302 | dispose: () => forwardingServer.close(() => { 303 | this.logger.trace(`SOCKS forwading server closed`); 304 | }), 305 | }); 306 | } else { 307 | this.logger.trace(`Opening tunnel ${localPort}(local) => ${remotePortOrSocketPath}(remote)`); 308 | const tunnelConfig = await this.sshConnection!.addTunnel({ 309 | name: `ssh_tunnel_${localPort}_${remotePortOrSocketPath}`, 310 | remoteAddr: '127.0.0.1', 311 | remotePort, 312 | remoteSocketPath, 313 | localPort 314 | }); 315 | disposables.push({ 316 | dispose: () => { 317 | this.sshConnection?.closeTunnel(tunnelConfig.name); 318 | this.logger.trace(`Tunnel ${tunnelConfig.name} closed`); 319 | } 320 | }); 321 | } 322 | 323 | return new TunnelInfo(localPort, remotePortOrSocketPath, disposables); 324 | } 325 | 326 | private getSSHAuthHandler(sshUser: string, sshHostName: string, identityKeys: SSHKey[], preferredAuthentications: string[]) { 327 | let passwordRetryCount = PASSWORD_RETRY_COUNT; 328 | let keyboardRetryCount = PASSWORD_RETRY_COUNT; 329 | identityKeys = identityKeys.slice(); 330 | return async (methodsLeft: string[] | null, _partialSuccess: boolean | null, callback: (nextAuth: ssh2.AuthHandlerResult) => void) => { 331 | if (methodsLeft === null) { 332 | this.logger.info(`Trying no-auth authentication`); 333 | 334 | return callback({ 335 | type: 'none', 336 | username: sshUser, 337 | }); 338 | } 339 | if (methodsLeft.includes('publickey') && identityKeys.length && preferredAuthentications.includes('publickey')) { 340 | const identityKey = identityKeys.shift()!; 341 | 342 | this.logger.info(`Trying publickey authentication: ${identityKey.filename} ${identityKey.parsedKey.type} SHA256:${identityKey.fingerprint}`); 343 | 344 | if (identityKey.agentSupport) { 345 | return callback({ 346 | type: 'agent', 347 | username: sshUser, 348 | agent: new class extends ssh2.OpenSSHAgent { 349 | // Only return the current key 350 | override getIdentities(callback: (err: Error | undefined, publicKeys?: ParsedKey[]) => void): void { 351 | callback(undefined, [identityKey.parsedKey]); 352 | } 353 | }(this.sshAgentSock!) 354 | }); 355 | } 356 | if (identityKey.isPrivate) { 357 | return callback({ 358 | type: 'publickey', 359 | username: sshUser, 360 | key: identityKey.parsedKey 361 | }); 362 | } 363 | if (!await fileExists(identityKey.filename)) { 364 | // Try next identity file 365 | return callback(null as any); 366 | } 367 | 368 | const keyBuffer = await fs.promises.readFile(identityKey.filename); 369 | let result = ssh2.utils.parseKey(keyBuffer); // First try without passphrase 370 | if (result instanceof Error && result.message === 'Encrypted private OpenSSH key detected, but no passphrase given') { 371 | let passphraseRetryCount = PASSPHRASE_RETRY_COUNT; 372 | while (result instanceof Error && passphraseRetryCount > 0) { 373 | const passphrase = await vscode.window.showInputBox({ 374 | title: `Enter passphrase for ${identityKey.filename}`, 375 | password: true, 376 | ignoreFocusOut: true 377 | }); 378 | if (!passphrase) { 379 | break; 380 | } 381 | result = ssh2.utils.parseKey(keyBuffer, passphrase); 382 | passphraseRetryCount--; 383 | } 384 | } 385 | if (!result || result instanceof Error) { 386 | // Try next identity file 387 | return callback(null as any); 388 | } 389 | 390 | const key = Array.isArray(result) ? result[0] : result; 391 | return callback({ 392 | type: 'publickey', 393 | username: sshUser, 394 | key 395 | }); 396 | } 397 | if (methodsLeft.includes('password') && passwordRetryCount > 0 && preferredAuthentications.includes('password')) { 398 | if (passwordRetryCount === PASSWORD_RETRY_COUNT) { 399 | this.logger.info(`Trying password authentication`); 400 | } 401 | 402 | const password = await vscode.window.showInputBox({ 403 | title: `Enter password for ${sshUser}@${sshHostName}`, 404 | password: true, 405 | ignoreFocusOut: true 406 | }); 407 | passwordRetryCount--; 408 | 409 | return callback(password 410 | ? { 411 | type: 'password', 412 | username: sshUser, 413 | password 414 | } 415 | : false); 416 | } 417 | if (methodsLeft.includes('keyboard-interactive') && keyboardRetryCount > 0 && preferredAuthentications.includes('keyboard-interactive')) { 418 | if (keyboardRetryCount === PASSWORD_RETRY_COUNT) { 419 | this.logger.info(`Trying keyboard-interactive authentication`); 420 | } 421 | 422 | return callback({ 423 | type: 'keyboard-interactive', 424 | username: sshUser, 425 | prompt: async (_name, _instructions, _instructionsLang, prompts, finish) => { 426 | const responses: string[] = []; 427 | for (const prompt of prompts) { 428 | const response = await vscode.window.showInputBox({ 429 | title: `(${sshUser}@${sshHostName}) ${prompt.prompt}`, 430 | password: !prompt.echo, 431 | ignoreFocusOut: true 432 | }); 433 | if (response === undefined) { 434 | keyboardRetryCount = 0; 435 | break; 436 | } 437 | responses.push(response); 438 | } 439 | keyboardRetryCount--; 440 | finish(responses); 441 | } 442 | }); 443 | } 444 | 445 | callback(false); 446 | }; 447 | } 448 | 449 | dispose() { 450 | disposeAll(this.tunnels); 451 | // If there's proxy connections then just close the parent connection 452 | if (this.proxyConnections.length) { 453 | this.proxyConnections[0].close(); 454 | } else { 455 | this.sshConnection?.close(); 456 | } 457 | this.proxyCommandProcess?.kill(); 458 | this.labelFormatterDisposable?.dispose(); 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /src/commands.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as fs from 'fs'; 3 | import { getRemoteAuthority } from './authResolver'; 4 | import { getSSHConfigPath } from './ssh/sshConfig'; 5 | import { exists as fileExists } from './common/files'; 6 | import SSHDestination from './ssh/sshDestination'; 7 | 8 | export async function promptOpenRemoteSSHWindow(reuseWindow: boolean) { 9 | const host = await vscode.window.showInputBox({ 10 | title: 'Enter [user@]hostname[:port]' 11 | }); 12 | 13 | if (!host) { 14 | return; 15 | } 16 | 17 | const sshDest = new SSHDestination(host); 18 | openRemoteSSHWindow(sshDest.toEncodedString(), reuseWindow); 19 | } 20 | 21 | export function openRemoteSSHWindow(host: string, reuseWindow: boolean) { 22 | vscode.commands.executeCommand('vscode.newWindow', { remoteAuthority: getRemoteAuthority(host), reuseWindow }); 23 | } 24 | 25 | export function openRemoteSSHLocationWindow(host: string, path: string, reuseWindow: boolean) { 26 | vscode.commands.executeCommand('vscode.openFolder', vscode.Uri.from({ scheme: 'vscode-remote', authority: getRemoteAuthority(host), path }), { forceNewWindow: !reuseWindow }); 27 | } 28 | 29 | export async function addNewHost() { 30 | const sshConfigPath = getSSHConfigPath(); 31 | if (!await fileExists(sshConfigPath)) { 32 | await fs.promises.appendFile(sshConfigPath, ''); 33 | } 34 | 35 | await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(sshConfigPath), { preview: false }); 36 | 37 | const textEditor = vscode.window.activeTextEditor; 38 | if (textEditor?.document.uri.fsPath !== sshConfigPath) { 39 | return; 40 | } 41 | 42 | const textDocument = textEditor.document; 43 | const lastLine = textDocument.lineAt(textDocument.lineCount - 1); 44 | 45 | if (!lastLine.isEmptyOrWhitespace) { 46 | await textEditor.edit((editBuilder: vscode.TextEditorEdit) => { 47 | editBuilder.insert(lastLine.range.end, '\n'); 48 | }); 49 | } 50 | 51 | let snippet = '\nHost ${1:dev}\n\tHostName ${2:dev.example.com}\n\tUser ${3:john}'; 52 | await textEditor.insertSnippet( 53 | new vscode.SnippetString(snippet), 54 | new vscode.Position(textDocument.lineCount, 0) 55 | ); 56 | } 57 | 58 | export async function openSSHConfigFile() { 59 | const sshConfigPath = getSSHConfigPath(); 60 | if (!await fileExists(sshConfigPath)) { 61 | await fs.promises.appendFile(sshConfigPath, ''); 62 | } 63 | vscode.commands.executeCommand('vscode.open', vscode.Uri.file(sshConfigPath)); 64 | } 65 | -------------------------------------------------------------------------------- /src/common/disposable.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | export function disposeAll(disposables: vscode.Disposable[]): void { 4 | while (disposables.length) { 5 | const item = disposables.pop(); 6 | if (item) { 7 | item.dispose(); 8 | } 9 | } 10 | } 11 | 12 | export abstract class Disposable { 13 | private _isDisposed = false; 14 | 15 | protected _disposables: vscode.Disposable[] = []; 16 | 17 | public dispose(): any { 18 | if (this._isDisposed) { 19 | return; 20 | } 21 | this._isDisposed = true; 22 | disposeAll(this._disposables); 23 | } 24 | 25 | protected _register(value: T): T { 26 | if (this._isDisposed) { 27 | value.dispose(); 28 | } else { 29 | this._disposables.push(value); 30 | } 31 | return value; 32 | } 33 | 34 | protected get isDisposed(): boolean { 35 | return this._isDisposed; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/common/files.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as os from 'os'; 3 | 4 | const homeDir = os.homedir(); 5 | 6 | export async function exists(path: string) { 7 | try { 8 | await fs.promises.access(path); 9 | return true; 10 | } catch { 11 | return false; 12 | } 13 | } 14 | 15 | export function untildify(path: string){ 16 | return path.replace(/^~(?=$|\/|\\)/, homeDir); 17 | } 18 | 19 | export function normalizeToSlash(path: string) { 20 | return path.replace(/\\/g, '/'); 21 | } 22 | -------------------------------------------------------------------------------- /src/common/logger.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | type LogLevel = 'Trace' | 'Info' | 'Error'; 4 | 5 | export default class Log { 6 | private output: vscode.OutputChannel; 7 | 8 | constructor(name: string) { 9 | this.output = vscode.window.createOutputChannel(name); 10 | } 11 | 12 | private data2String(data: any): string { 13 | if (data instanceof Error) { 14 | return data.stack || data.message; 15 | } 16 | if (data.success === false && data.message) { 17 | return data.message; 18 | } 19 | return data.toString(); 20 | } 21 | 22 | public trace(message: string, data?: any): void { 23 | this.logLevel('Trace', message, data); 24 | } 25 | 26 | public info(message: string, data?: any): void { 27 | this.logLevel('Info', message, data); 28 | } 29 | 30 | public error(message: string, data?: any): void { 31 | this.logLevel('Error', message, data); 32 | } 33 | 34 | public logLevel(level: LogLevel, message: string, data?: any): void { 35 | this.output.appendLine(`[${level} - ${this.now()}] ${message}`); 36 | if (data) { 37 | this.output.appendLine(this.data2String(data)); 38 | } 39 | } 40 | 41 | private now(): string { 42 | const now = new Date(); 43 | return padLeft(now.getUTCHours() + '', 2, '0') 44 | + ':' + padLeft(now.getMinutes() + '', 2, '0') 45 | + ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds(); 46 | } 47 | 48 | public show() { 49 | this.output.show(); 50 | } 51 | 52 | public dispose() { 53 | this.output.dispose(); 54 | } 55 | } 56 | 57 | function padLeft(s: string, n: number, pad = ' ') { 58 | return pad.repeat(Math.max(0, n - s.length)) + s; 59 | } 60 | -------------------------------------------------------------------------------- /src/common/platform.ts: -------------------------------------------------------------------------------- 1 | export const isWindows = process.platform === 'win32'; 2 | export const isMacintosh = process.platform === 'darwin'; 3 | export const isLinux = process.platform === 'linux'; 4 | -------------------------------------------------------------------------------- /src/common/ports.ts: -------------------------------------------------------------------------------- 1 | import * as net from 'net'; 2 | 3 | /** 4 | * Finds a random unused port assigned by the operating system. Will reject in case no free port can be found. 5 | */ 6 | export function findRandomPort(): Promise { 7 | return new Promise((resolve, reject) => { 8 | const server = net.createServer({ pauseOnConnect: true }); 9 | server.on('error', reject); 10 | server.on('listening', () => { 11 | const port = (server.address() as net.AddressInfo).port; 12 | server.close(() => resolve(port)); 13 | }); 14 | server.listen(0, '127.0.0.1'); 15 | }); 16 | } 17 | 18 | /** 19 | * Given a start point and a max number of retries, will find a port that 20 | * is openable. Will return 0 in case no free port can be found. 21 | */ 22 | export function findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise { 23 | let done = false; 24 | 25 | return new Promise(resolve => { 26 | const timeoutHandle = setTimeout(() => { 27 | if (!done) { 28 | done = true; 29 | return resolve(0); 30 | } 31 | }, timeout); 32 | 33 | doFindFreePort(startPort, giveUpAfter, stride, (port) => { 34 | if (!done) { 35 | done = true; 36 | clearTimeout(timeoutHandle); 37 | return resolve(port); 38 | } 39 | }); 40 | }); 41 | } 42 | 43 | function doFindFreePort(startPort: number, giveUpAfter: number, stride: number, clb: (port: number) => void): void { 44 | if (giveUpAfter === 0) { 45 | return clb(0); 46 | } 47 | 48 | const client = new net.Socket(); 49 | 50 | // If we can connect to the port it means the port is already taken so we continue searching 51 | client.once('connect', () => { 52 | dispose(client); 53 | 54 | return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb); 55 | }); 56 | 57 | client.once('data', () => { 58 | // this listener is required since node.js 8.x 59 | }); 60 | 61 | client.once('error', (err: Error & { code?: string }) => { 62 | dispose(client); 63 | 64 | // If we receive any non ECONNREFUSED error, it means the port is used but we cannot connect 65 | if (err.code !== 'ECONNREFUSED') { 66 | return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb); 67 | } 68 | 69 | // Otherwise it means the port is free to use! 70 | return clb(startPort); 71 | }); 72 | 73 | client.connect(startPort, '127.0.0.1'); 74 | } 75 | 76 | /** 77 | * Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener. 78 | */ 79 | export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number): Promise { 80 | let resolved = false; 81 | let timeoutHandle: NodeJS.Timeout | undefined = undefined; 82 | let countTried = 1; 83 | const server = net.createServer({ pauseOnConnect: true }); 84 | function doResolve(port: number, resolve: (port: number) => void) { 85 | if (!resolved) { 86 | resolved = true; 87 | server.removeAllListeners(); 88 | server.close(); 89 | if (timeoutHandle) { 90 | clearTimeout(timeoutHandle); 91 | } 92 | resolve(port); 93 | } 94 | } 95 | return new Promise(resolve => { 96 | timeoutHandle = setTimeout(() => { 97 | doResolve(0, resolve); 98 | }, timeout); 99 | 100 | server.on('listening', () => { 101 | doResolve(startPort, resolve); 102 | }); 103 | server.on('error', err => { 104 | if (err && ((err).code === 'EADDRINUSE' || (err).code === 'EACCES') && (countTried < giveUpAfter)) { 105 | startPort++; 106 | countTried++; 107 | server.listen(startPort, '127.0.0.1'); 108 | } else { 109 | doResolve(0, resolve); 110 | } 111 | }); 112 | server.on('close', () => { 113 | doResolve(0, resolve); 114 | }); 115 | server.listen(startPort, '127.0.0.1'); 116 | }); 117 | } 118 | 119 | function dispose(socket: net.Socket): void { 120 | try { 121 | socket.removeAllListeners('connect'); 122 | socket.removeAllListeners('error'); 123 | socket.end(); 124 | socket.destroy(); 125 | socket.unref(); 126 | } catch (error) { 127 | console.error(error); // otherwise this error would get lost in the callback chain 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import Log from './common/logger'; 3 | import { RemoteSSHResolver, REMOTE_SSH_AUTHORITY } from './authResolver'; 4 | import { openSSHConfigFile, promptOpenRemoteSSHWindow } from './commands'; 5 | import { HostTreeDataProvider } from './hostTreeView'; 6 | import { getRemoteWorkspaceLocationData, RemoteLocationHistory } from './remoteLocationHistory'; 7 | 8 | export async function activate(context: vscode.ExtensionContext) { 9 | const logger = new Log('Remote - SSH'); 10 | context.subscriptions.push(logger); 11 | 12 | const remoteSSHResolver = new RemoteSSHResolver(context, logger); 13 | context.subscriptions.push(vscode.workspace.registerRemoteAuthorityResolver(REMOTE_SSH_AUTHORITY, remoteSSHResolver)); 14 | context.subscriptions.push(remoteSSHResolver); 15 | 16 | const locationHistory = new RemoteLocationHistory(context); 17 | const locationData = getRemoteWorkspaceLocationData(); 18 | if (locationData) { 19 | await locationHistory.addLocation(locationData[0], locationData[1]); 20 | } 21 | 22 | const hostTreeDataProvider = new HostTreeDataProvider(locationHistory); 23 | context.subscriptions.push(vscode.window.createTreeView('sshHosts', { treeDataProvider: hostTreeDataProvider })); 24 | context.subscriptions.push(hostTreeDataProvider); 25 | 26 | context.subscriptions.push(vscode.commands.registerCommand('openremotessh.openEmptyWindow', () => promptOpenRemoteSSHWindow(false))); 27 | context.subscriptions.push(vscode.commands.registerCommand('openremotessh.openEmptyWindowInCurrentWindow', () => promptOpenRemoteSSHWindow(true))); 28 | context.subscriptions.push(vscode.commands.registerCommand('openremotessh.openConfigFile', () => openSSHConfigFile())); 29 | context.subscriptions.push(vscode.commands.registerCommand('openremotessh.showLog', () => logger.show())); 30 | } 31 | 32 | export function deactivate() { 33 | } 34 | -------------------------------------------------------------------------------- /src/hostTreeView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as path from 'path'; 3 | import SSHConfiguration, { getSSHConfigPath } from './ssh/sshConfig'; 4 | import { RemoteLocationHistory } from './remoteLocationHistory'; 5 | import { Disposable } from './common/disposable'; 6 | import { addNewHost, openRemoteSSHLocationWindow, openRemoteSSHWindow, openSSHConfigFile } from './commands'; 7 | import SSHDestination from './ssh/sshDestination'; 8 | 9 | class HostItem { 10 | constructor( 11 | public hostname: string, 12 | public locations: string[] 13 | ) { 14 | } 15 | } 16 | 17 | class HostLocationItem { 18 | constructor( 19 | public path: string, 20 | public hostname: string 21 | ) { 22 | } 23 | } 24 | 25 | type DataTreeItem = HostItem | HostLocationItem; 26 | 27 | export class HostTreeDataProvider extends Disposable implements vscode.TreeDataProvider { 28 | 29 | private readonly _onDidChangeTreeData = this._register(new vscode.EventEmitter()); 30 | public readonly onDidChangeTreeData = this._onDidChangeTreeData.event; 31 | 32 | constructor( 33 | private locationHistory: RemoteLocationHistory 34 | ) { 35 | super(); 36 | 37 | this._register(vscode.commands.registerCommand('openremotessh.explorer.add', () => addNewHost())); 38 | this._register(vscode.commands.registerCommand('openremotessh.explorer.configure', () => openSSHConfigFile())); 39 | this._register(vscode.commands.registerCommand('openremotessh.explorer.refresh', () => this.refresh())); 40 | this._register(vscode.commands.registerCommand('openremotessh.explorer.emptyWindowInNewWindow', e => this.openRemoteSSHWindow(e, false))); 41 | this._register(vscode.commands.registerCommand('openremotessh.explorer.emptyWindowInCurrentWindow', e => this.openRemoteSSHWindow(e, true))); 42 | this._register(vscode.commands.registerCommand('openremotessh.explorer.reopenFolderInNewWindow', e => this.openRemoteSSHLocationWindow(e, false))); 43 | this._register(vscode.commands.registerCommand('openremotessh.explorer.reopenFolderInCurrentWindow', e => this.openRemoteSSHLocationWindow(e, true))); 44 | this._register(vscode.commands.registerCommand('openremotessh.explorer.deleteFolderHistoryItem', e => this.deleteHostLocation(e))); 45 | 46 | this._register(vscode.workspace.onDidChangeConfiguration(e => { 47 | if (e.affectsConfiguration('remote.SSH.configFile')) { 48 | this.refresh(); 49 | } 50 | })); 51 | this._register(vscode.workspace.onDidSaveTextDocument(e => { 52 | if (e.uri.fsPath === getSSHConfigPath()) { 53 | this.refresh(); 54 | } 55 | })); 56 | } 57 | 58 | getTreeItem(element: DataTreeItem): vscode.TreeItem { 59 | if (element instanceof HostLocationItem) { 60 | const label = path.posix.basename(element.path).replace(/\.code-workspace$/, ' (Workspace)'); 61 | const treeItem = new vscode.TreeItem(label); 62 | treeItem.description = path.posix.dirname(element.path); 63 | treeItem.iconPath = new vscode.ThemeIcon('folder'); 64 | treeItem.contextValue = 'openremotessh.explorer.folder'; 65 | return treeItem; 66 | } 67 | 68 | const treeItem = new vscode.TreeItem(element.hostname); 69 | treeItem.collapsibleState = element.locations.length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None; 70 | treeItem.iconPath = new vscode.ThemeIcon('vm'); 71 | treeItem.contextValue = 'openremotessh.explorer.host'; 72 | return treeItem; 73 | } 74 | 75 | async getChildren(element?: HostItem): Promise { 76 | if (!element) { 77 | const sshConfigFile = await SSHConfiguration.loadFromFS(); 78 | const hosts = sshConfigFile.getAllConfiguredHosts(); 79 | return hosts.map(hostname => new HostItem(hostname, this.locationHistory.getHistory(hostname))); 80 | } 81 | if (element instanceof HostItem) { 82 | return element.locations.map(location => new HostLocationItem(location, element.hostname)); 83 | } 84 | return []; 85 | } 86 | 87 | private refresh() { 88 | this._onDidChangeTreeData.fire(); 89 | } 90 | 91 | private async deleteHostLocation(element: HostLocationItem) { 92 | await this.locationHistory.removeLocation(element.hostname, element.path); 93 | this.refresh(); 94 | } 95 | 96 | private async openRemoteSSHWindow(element: HostItem, reuseWindow: boolean) { 97 | const sshDest = new SSHDestination(element.hostname); 98 | openRemoteSSHWindow(sshDest.toEncodedString(), reuseWindow); 99 | } 100 | 101 | private async openRemoteSSHLocationWindow(element: HostLocationItem, reuseWindow: boolean) { 102 | const sshDest = new SSHDestination(element.hostname); 103 | openRemoteSSHLocationWindow(sshDest.toEncodedString(), element.path, reuseWindow); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/remoteLocationHistory.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { REMOTE_SSH_AUTHORITY } from './authResolver'; 3 | import SSHDestination from './ssh/sshDestination'; 4 | 5 | export class RemoteLocationHistory { 6 | private static STORAGE_KEY = 'remoteLocationHistory_v0'; 7 | 8 | private remoteLocationHistory: Record = {}; 9 | 10 | constructor(private context: vscode.ExtensionContext) { 11 | // context.globalState.update(RemoteLocationHistory.STORAGE_KEY, undefined); 12 | this.remoteLocationHistory = context.globalState.get(RemoteLocationHistory.STORAGE_KEY) || {}; 13 | } 14 | 15 | getHistory(host: string): string[] { 16 | return this.remoteLocationHistory[host] || []; 17 | } 18 | 19 | async addLocation(host: string, path: string) { 20 | let hostLocations = this.remoteLocationHistory[host] || []; 21 | if (!hostLocations.includes(path)) { 22 | hostLocations.unshift(path); 23 | this.remoteLocationHistory[host] = hostLocations; 24 | 25 | await this.context.globalState.update(RemoteLocationHistory.STORAGE_KEY, this.remoteLocationHistory); 26 | } 27 | } 28 | 29 | async removeLocation(host: string, path: string) { 30 | let hostLocations = this.remoteLocationHistory[host] || []; 31 | hostLocations = hostLocations.filter(l => l !== path); 32 | this.remoteLocationHistory[host] = hostLocations; 33 | 34 | await this.context.globalState.update(RemoteLocationHistory.STORAGE_KEY, this.remoteLocationHistory); 35 | } 36 | } 37 | 38 | export function getRemoteWorkspaceLocationData(): [string, string] | undefined { 39 | let location = vscode.workspace.workspaceFile; 40 | if (location && location.scheme === 'vscode-remote' && location.authority.startsWith(REMOTE_SSH_AUTHORITY) && location.path.endsWith('.code-workspace')) { 41 | const [, host] = location.authority.split('+'); 42 | const sshDest = SSHDestination.parseEncoded(host); 43 | return [sshDest.hostname, location.path]; 44 | } 45 | 46 | location = vscode.workspace.workspaceFolders?.[0].uri; 47 | if (location && location.scheme === 'vscode-remote' && location.authority.startsWith(REMOTE_SSH_AUTHORITY)) { 48 | const [, host] = location.authority.split('+'); 49 | const sshDest = SSHDestination.parseEncoded(host); 50 | return [sshDest.hostname, location.path]; 51 | } 52 | 53 | return undefined; 54 | } 55 | -------------------------------------------------------------------------------- /src/serverConfig.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | let vscodeProductJson: any; 6 | async function getVSCodeProductJson() { 7 | if (!vscodeProductJson) { 8 | const productJsonStr = await fs.promises.readFile(path.join(vscode.env.appRoot, 'product.json'), 'utf8'); 9 | vscodeProductJson = JSON.parse(productJsonStr); 10 | } 11 | 12 | return vscodeProductJson; 13 | } 14 | 15 | export interface IServerConfig { 16 | version: string; 17 | commit: string; 18 | quality: string; 19 | release?: string; // vscodium-like specific 20 | serverApplicationName: string; 21 | serverDataFolderName: string; 22 | serverDownloadUrlTemplate?: string; // vscodium-like specific 23 | } 24 | 25 | export async function getVSCodeServerConfig(): Promise { 26 | const productJson = await getVSCodeProductJson(); 27 | 28 | const customServerBinaryName = vscode.workspace.getConfiguration('remote.SSH.experimental').get('serverBinaryName', ''); 29 | 30 | return { 31 | version: vscode.version.replace('-insider',''), 32 | commit: productJson.commit, 33 | quality: productJson.quality, 34 | release: productJson.release, 35 | serverApplicationName: customServerBinaryName || productJson.serverApplicationName, 36 | serverDataFolderName: productJson.serverDataFolderName, 37 | serverDownloadUrlTemplate: productJson.serverDownloadUrlTemplate 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/serverSetup.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto'; 2 | import Log from './common/logger'; 3 | import { getVSCodeServerConfig } from './serverConfig'; 4 | import SSHConnection from './ssh/sshConnection'; 5 | 6 | export interface ServerInstallOptions { 7 | id: string; 8 | quality: string; 9 | commit: string; 10 | version: string; 11 | release?: string; // vscodium specific 12 | extensionIds: string[]; 13 | envVariables: string[]; 14 | useSocketPath: boolean; 15 | serverApplicationName: string; 16 | serverDataFolderName: string; 17 | serverDownloadUrlTemplate: string; 18 | } 19 | 20 | export interface ServerInstallResult { 21 | exitCode: number; 22 | listeningOn: number | string; 23 | connectionToken: string; 24 | logFile: string; 25 | osReleaseId: string; 26 | arch: string; 27 | platform: string; 28 | tmpDir: string; 29 | [key: string]: any; 30 | } 31 | 32 | export class ServerInstallError extends Error { 33 | constructor(message: string) { 34 | super(message); 35 | } 36 | } 37 | 38 | const DEFAULT_DOWNLOAD_URL_TEMPLATE = 'https://github.com/VSCodium/vscodium/releases/download/${version}.${release}/vscodium-reh-${os}-${arch}-${version}.${release}.tar.gz'; 39 | 40 | const VERSION_RELEASE_MAP: { [key: string]: string } = { 41 | '1.95.3': '24321', 42 | '1.96.4': '25026', 43 | '1.97.2': '25045' 44 | }; 45 | 46 | export async function installCodeServer(conn: SSHConnection, serverDownloadUrlTemplate: string | undefined, extensionIds: string[], envVariables: string[], platform: string | undefined, useSocketPath: boolean, logger: Log): Promise { 47 | let shell = 'powershell'; 48 | 49 | // detect platform and shell for windows 50 | if (!platform || platform === 'windows') { 51 | const result = await conn.exec('uname -s'); 52 | 53 | if (result.stdout) { 54 | if (result.stdout.includes('windows32')) { 55 | platform = 'windows'; 56 | } else if (result.stdout.includes('MINGW64')) { 57 | platform = 'windows'; 58 | shell = 'bash'; 59 | } 60 | } else if (result.stderr) { 61 | if (result.stderr.includes('FullyQualifiedErrorId : CommandNotFoundException')) { 62 | platform = 'windows'; 63 | } 64 | 65 | if (result.stderr.includes('is not recognized as an internal or external command')) { 66 | platform = 'windows'; 67 | shell = 'cmd'; 68 | } 69 | } 70 | 71 | if (platform) { 72 | logger.trace(`Detected platform: ${platform}, ${shell}`); 73 | } 74 | } 75 | 76 | const scriptId = crypto.randomBytes(12).toString('hex'); 77 | 78 | const vscodeServerConfig = await getVSCodeServerConfig(); 79 | const installOptions: ServerInstallOptions = { 80 | id: scriptId, 81 | version: vscodeServerConfig.version, 82 | commit: vscodeServerConfig.commit, 83 | quality: vscodeServerConfig.quality, 84 | release: vscodeServerConfig.release, 85 | extensionIds, 86 | envVariables, 87 | useSocketPath, 88 | serverApplicationName: vscodeServerConfig.serverApplicationName, 89 | serverDataFolderName: vscodeServerConfig.serverDataFolderName, 90 | serverDownloadUrlTemplate: serverDownloadUrlTemplate ?? vscodeServerConfig.serverDownloadUrlTemplate ?? DEFAULT_DOWNLOAD_URL_TEMPLATE, 91 | }; 92 | 93 | let commandOutput: { stdout: string; stderr: string }; 94 | if (platform === 'windows') { 95 | const installServerScript = generatePowerShellInstallScript(installOptions); 96 | 97 | logger.trace('Server install command:', installServerScript); 98 | 99 | const installDir = `$HOME\\${vscodeServerConfig.serverDataFolderName}\\install`; 100 | const installScript = `${installDir}\\${vscodeServerConfig.commit}.ps1`; 101 | const endRegex = new RegExp(`${scriptId}: end`); 102 | // investigate if it's possible to use `-EncodedCommand` flag 103 | // https://devblogs.microsoft.com/powershell/invoking-powershell-with-complex-expressions-using-scriptblocks/ 104 | let command = ''; 105 | if (shell === 'powershell') { 106 | command = `md -Force ${installDir}; echo @'\n${installServerScript}\n'@ | Set-Content ${installScript}; powershell -ExecutionPolicy ByPass -File "${installScript}"`; 107 | } else if (shell === 'bash') { 108 | command = `mkdir -p ${installDir.replace(/\\/g, '/')} && echo '\n${installServerScript.replace(/'/g, '\'"\'"\'')}\n' > ${installScript.replace(/\\/g, '/')} && powershell -ExecutionPolicy ByPass -File "${installScript}"`; 109 | } else if (shell === 'cmd') { 110 | const script = installServerScript.trim() 111 | // remove comments 112 | .replace(/^#.*$/gm, '') 113 | // remove empty lines 114 | .replace(/\n{2,}/gm, '\n') 115 | // remove leading spaces 116 | .replace(/^\s*/gm, '') 117 | // escape double quotes (from powershell/cmd) 118 | .replace(/"/g, '"""') 119 | // escape single quotes (from cmd) 120 | .replace(/'/g, `''`) 121 | // escape redirect (from cmd) 122 | .replace(/>/g, `^>`) 123 | // escape new lines (from powershell/cmd) 124 | .replace(/\n/g, '\'`n\''); 125 | 126 | command = `powershell "md -Force ${installDir}" && powershell "echo '${script}'" > ${installScript.replace('$HOME', '%USERPROFILE%')} && powershell -ExecutionPolicy ByPass -File "${installScript.replace('$HOME', '%USERPROFILE%')}"`; 127 | 128 | logger.trace('Command length (8191 max):', command.length); 129 | 130 | if (command.length > 8191) { 131 | throw new ServerInstallError(`Command line too long`); 132 | } 133 | } else { 134 | throw new ServerInstallError(`Not supported shell: ${shell}`); 135 | } 136 | 137 | commandOutput = await conn.execPartial(command, (stdout: string) => endRegex.test(stdout)); 138 | } else { 139 | const installServerScript = generateBashInstallScript(installOptions); 140 | 141 | logger.trace('Server install command:', installServerScript); 142 | // Fish shell does not support heredoc so let's workaround it using -c option, 143 | // also replace single quotes (') within the script with ('\'') as there's no quoting within single quotes, see https://unix.stackexchange.com/a/24676 144 | commandOutput = await conn.exec(`bash -c '${installServerScript.replace(/'/g, `'\\''`)}'`); 145 | } 146 | 147 | if (commandOutput.stderr) { 148 | logger.trace('Server install command stderr:', commandOutput.stderr); 149 | } 150 | logger.trace('Server install command stdout:', commandOutput.stdout); 151 | 152 | const resultMap = parseServerInstallOutput(commandOutput.stdout, scriptId); 153 | if (!resultMap) { 154 | throw new ServerInstallError(`Failed parsing install script output`); 155 | } 156 | 157 | const exitCode = parseInt(resultMap.exitCode, 10); 158 | if (exitCode !== 0) { 159 | throw new ServerInstallError(`Couldn't install vscode server on remote server, install script returned non-zero exit status`); 160 | } 161 | 162 | const listeningOn = resultMap.listeningOn.match(/^\d+$/) 163 | ? parseInt(resultMap.listeningOn, 10) 164 | : resultMap.listeningOn; 165 | 166 | const remoteEnvVars = Object.fromEntries(Object.entries(resultMap).filter(([key,]) => envVariables.includes(key))); 167 | 168 | return { 169 | exitCode, 170 | listeningOn, 171 | connectionToken: resultMap.connectionToken, 172 | logFile: resultMap.logFile, 173 | osReleaseId: resultMap.osReleaseId, 174 | arch: resultMap.arch, 175 | platform: resultMap.platform, 176 | tmpDir: resultMap.tmpDir, 177 | ...remoteEnvVars 178 | }; 179 | } 180 | 181 | function parseServerInstallOutput(str: string, scriptId: string): { [k: string]: string } | undefined { 182 | const startResultStr = `${scriptId}: start`; 183 | const endResultStr = `${scriptId}: end`; 184 | 185 | const startResultIdx = str.indexOf(startResultStr); 186 | if (startResultIdx < 0) { 187 | return undefined; 188 | } 189 | 190 | const endResultIdx = str.indexOf(endResultStr, startResultIdx + startResultStr.length); 191 | if (endResultIdx < 0) { 192 | return undefined; 193 | } 194 | 195 | const installResult = str.substring(startResultIdx + startResultStr.length, endResultIdx); 196 | 197 | const resultMap: { [k: string]: string } = {}; 198 | const resultArr = installResult.split(/\r?\n/); 199 | for (const line of resultArr) { 200 | const [key, value] = line.split('=='); 201 | resultMap[key] = value; 202 | } 203 | 204 | return resultMap; 205 | } 206 | 207 | function generateBashInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate }: ServerInstallOptions) { 208 | const extensions = extensionIds.map(id => '--install-extension ' + id).join(' '); 209 | return ` 210 | # Server installation script 211 | 212 | TMP_DIR="\${XDG_RUNTIME_DIR:-"/tmp"}" 213 | 214 | DISTRO_VERSION="${version}" 215 | DISTRO_COMMIT="${commit}" 216 | DISTRO_QUALITY="${quality}" 217 | DISTRO_VSCODIUM_RELEASE="${release ?? VERSION_RELEASE_MAP[version] ?? ''}" 218 | 219 | SERVER_APP_NAME="${serverApplicationName}" 220 | SERVER_INITIAL_EXTENSIONS="${extensions}" 221 | SERVER_LISTEN_FLAG="${useSocketPath ? `--socket-path="$TMP_DIR/vscode-server-sock-${crypto.randomUUID()}"` : '--port=0'}" 222 | SERVER_DATA_DIR="$HOME/${serverDataFolderName}" 223 | SERVER_DIR="$SERVER_DATA_DIR/bin/$DISTRO_COMMIT" 224 | SERVER_SCRIPT="$SERVER_DIR/bin/$SERVER_APP_NAME" 225 | SERVER_LOGFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.log" 226 | SERVER_PIDFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.pid" 227 | SERVER_TOKENFILE="$SERVER_DATA_DIR/.$DISTRO_COMMIT.token" 228 | SERVER_ARCH= 229 | SERVER_CONNECTION_TOKEN= 230 | SERVER_DOWNLOAD_URL= 231 | 232 | LISTENING_ON= 233 | OS_RELEASE_ID= 234 | ARCH= 235 | PLATFORM= 236 | 237 | # Mimic output from logs of remote-ssh extension 238 | print_install_results_and_exit() { 239 | echo "${id}: start" 240 | echo "exitCode==$1==" 241 | echo "listeningOn==$LISTENING_ON==" 242 | echo "connectionToken==$SERVER_CONNECTION_TOKEN==" 243 | echo "logFile==$SERVER_LOGFILE==" 244 | echo "osReleaseId==$OS_RELEASE_ID==" 245 | echo "arch==$ARCH==" 246 | echo "platform==$PLATFORM==" 247 | echo "tmpDir==$TMP_DIR==" 248 | ${envVariables.map(envVar => `echo "${envVar}==$${envVar}=="`).join('\n')} 249 | echo "${id}: end" 250 | exit 0 251 | } 252 | 253 | # Check if platform is supported 254 | KERNEL="$(uname -s)" 255 | case $KERNEL in 256 | Darwin) 257 | PLATFORM="darwin" 258 | ;; 259 | Linux) 260 | PLATFORM="linux" 261 | ;; 262 | FreeBSD) 263 | PLATFORM="freebsd" 264 | ;; 265 | DragonFly) 266 | PLATFORM="dragonfly" 267 | ;; 268 | *) 269 | echo "Error platform not supported: $KERNEL" 270 | print_install_results_and_exit 1 271 | ;; 272 | esac 273 | 274 | # Check machine architecture 275 | ARCH="$(uname -m)" 276 | case $ARCH in 277 | x86_64 | amd64) 278 | SERVER_ARCH="x64" 279 | ;; 280 | armv7l | armv8l) 281 | SERVER_ARCH="armhf" 282 | ;; 283 | arm64 | aarch64) 284 | SERVER_ARCH="arm64" 285 | ;; 286 | ppc64le) 287 | SERVER_ARCH="ppc64le" 288 | ;; 289 | riscv64) 290 | SERVER_ARCH="riscv64" 291 | ;; 292 | loongarch64) 293 | SERVER_ARCH="loong64" 294 | ;; 295 | s390x) 296 | SERVER_ARCH="s390x" 297 | ;; 298 | *) 299 | echo "Error architecture not supported: $ARCH" 300 | print_install_results_and_exit 1 301 | ;; 302 | esac 303 | 304 | # https://www.freedesktop.org/software/systemd/man/os-release.html 305 | OS_RELEASE_ID="$(grep -i '^ID=' /etc/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')" 306 | if [[ -z $OS_RELEASE_ID ]]; then 307 | OS_RELEASE_ID="$(grep -i '^ID=' /usr/lib/os-release 2>/dev/null | sed 's/^ID=//gi' | sed 's/"//g')" 308 | if [[ -z $OS_RELEASE_ID ]]; then 309 | OS_RELEASE_ID="unknown" 310 | fi 311 | fi 312 | 313 | # Create installation folder 314 | if [[ ! -d $SERVER_DIR ]]; then 315 | mkdir -p $SERVER_DIR 316 | if (( $? > 0 )); then 317 | echo "Error creating server install directory" 318 | print_install_results_and_exit 1 319 | fi 320 | fi 321 | 322 | # adjust platform for vscodium download, if needed 323 | if [[ $OS_RELEASE_ID = alpine ]]; then 324 | PLATFORM=$OS_RELEASE_ID 325 | fi 326 | 327 | SERVER_DOWNLOAD_URL="$(echo "${serverDownloadUrlTemplate.replace(/\$\{/g, '\\${')}" | sed "s/\\\${quality}/$DISTRO_QUALITY/g" | sed "s/\\\${version}/$DISTRO_VERSION/g" | sed "s/\\\${commit}/$DISTRO_COMMIT/g" | sed "s/\\\${os}/$PLATFORM/g" | sed "s/\\\${arch}/$SERVER_ARCH/g" | sed "s/\\\${release}/$DISTRO_VSCODIUM_RELEASE/g")" 328 | 329 | # Check if server script is already installed 330 | if [[ ! -f $SERVER_SCRIPT ]]; then 331 | case "$PLATFORM" in 332 | darwin | linux | alpine ) 333 | ;; 334 | *) 335 | echo "Error '$PLATFORM' needs manual installation of remote extension host" 336 | print_install_results_and_exit 1 337 | ;; 338 | esac 339 | 340 | pushd $SERVER_DIR > /dev/null 341 | 342 | if [[ ! -z $(which wget) ]]; then 343 | wget --tries=3 --timeout=10 --continue --no-verbose -O vscode-server.tar.gz $SERVER_DOWNLOAD_URL 344 | elif [[ ! -z $(which curl) ]]; then 345 | curl --retry 3 --connect-timeout 10 --location --show-error --silent --output vscode-server.tar.gz $SERVER_DOWNLOAD_URL 346 | else 347 | echo "Error no tool to download server binary" 348 | print_install_results_and_exit 1 349 | fi 350 | 351 | if (( $? > 0 )); then 352 | echo "Error downloading server from $SERVER_DOWNLOAD_URL" 353 | print_install_results_and_exit 1 354 | fi 355 | 356 | tar -xf vscode-server.tar.gz --strip-components 1 357 | if (( $? > 0 )); then 358 | echo "Error while extracting server contents" 359 | print_install_results_and_exit 1 360 | fi 361 | 362 | # Get old commit from product.json and replace with new commit 363 | if [[ -f "$SERVER_DIR/product.json" ]]; then 364 | OLD_COMMIT=$(grep -o '"commit": *"[^"]*"' "$SERVER_DIR/product.json" | cut -d'"' -f4) 365 | if [[ ! -z "$OLD_COMMIT" ]]; then 366 | find "$SERVER_DIR" -type f -exec sed -i "s/$OLD_COMMIT/$DISTRO_COMMIT/g" {} + 367 | fi 368 | fi 369 | 370 | # Rename all files containing 'codium' in bin directory 371 | find "$SERVER_DIR/bin" -type f -name "*codium*" | while read file; do 372 | new_name=$(echo "$file" | sed 's/codium/trae/g') 373 | mv "$file" "$new_name" 374 | done 375 | 376 | if [[ ! -f $SERVER_SCRIPT ]]; then 377 | echo "Error server contents are corrupted" 378 | print_install_results_and_exit 1 379 | fi 380 | 381 | rm -f vscode-server.tar.gz 382 | 383 | popd > /dev/null 384 | else 385 | echo "Server script already installed in $SERVER_SCRIPT" 386 | fi 387 | 388 | # Try to find if server is already running 389 | if [[ -f $SERVER_PIDFILE ]]; then 390 | SERVER_PID="$(cat $SERVER_PIDFILE)" 391 | SERVER_RUNNING_PROCESS="$(ps -o pid,args -p $SERVER_PID | grep $SERVER_SCRIPT)" 392 | else 393 | SERVER_RUNNING_PROCESS="$(ps -o pid,args -A | grep $SERVER_SCRIPT | grep -v grep)" 394 | fi 395 | 396 | if [[ -z $SERVER_RUNNING_PROCESS ]]; then 397 | if [[ -f $SERVER_LOGFILE ]]; then 398 | rm $SERVER_LOGFILE 399 | fi 400 | if [[ -f $SERVER_TOKENFILE ]]; then 401 | rm $SERVER_TOKENFILE 402 | fi 403 | 404 | touch $SERVER_TOKENFILE 405 | chmod 600 $SERVER_TOKENFILE 406 | SERVER_CONNECTION_TOKEN="${crypto.randomUUID()}" 407 | echo $SERVER_CONNECTION_TOKEN > $SERVER_TOKENFILE 408 | 409 | $SERVER_SCRIPT --start-server --host=127.0.0.1 $SERVER_LISTEN_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file $SERVER_TOKENFILE --telemetry-level off --enable-remote-auto-shutdown --accept-server-license-terms &> $SERVER_LOGFILE & 410 | echo $! > $SERVER_PIDFILE 411 | else 412 | echo "Server script is already running $SERVER_SCRIPT" 413 | fi 414 | 415 | if [[ -f $SERVER_TOKENFILE ]]; then 416 | SERVER_CONNECTION_TOKEN="$(cat $SERVER_TOKENFILE)" 417 | else 418 | echo "Error server token file not found $SERVER_TOKENFILE" 419 | print_install_results_and_exit 1 420 | fi 421 | 422 | if [[ -f $SERVER_LOGFILE ]]; then 423 | for i in {1..5}; do 424 | LISTENING_ON="$(cat $SERVER_LOGFILE | grep -E 'Extension host agent listening on .+' | sed 's/Extension host agent listening on //')" 425 | if [[ -n $LISTENING_ON ]]; then 426 | break 427 | fi 428 | sleep 0.5 429 | done 430 | 431 | if [[ -z $LISTENING_ON ]]; then 432 | echo "Error server did not start successfully" 433 | print_install_results_and_exit 1 434 | fi 435 | else 436 | echo "Error server log file not found $SERVER_LOGFILE" 437 | print_install_results_and_exit 1 438 | fi 439 | 440 | # Finish server setup 441 | print_install_results_and_exit 0 442 | `; 443 | } 444 | 445 | function generatePowerShellInstallScript({ id, quality, version, commit, release, extensionIds, envVariables, useSocketPath, serverApplicationName, serverDataFolderName, serverDownloadUrlTemplate }: ServerInstallOptions) { 446 | const extensions = extensionIds.map(id => '--install-extension ' + id).join(' '); 447 | const downloadUrl = serverDownloadUrlTemplate 448 | .replace(/\$\{quality\}/g, quality) 449 | .replace(/\$\{version\}/g, version) 450 | .replace(/\$\{commit\}/g, commit) 451 | .replace(/\$\{os\}/g, 'win32') 452 | .replace(/\$\{arch\}/g, 'x64') 453 | .replace(/\$\{release\}/g, release ?? VERSION_RELEASE_MAP[version] ?? ''); 454 | 455 | return ` 456 | # Server installation script 457 | 458 | $TMP_DIR="$env:TEMP\\$([System.IO.Path]::GetRandomFileName())" 459 | $ProgressPreference = "SilentlyContinue" 460 | 461 | $DISTRO_VERSION="${version}" 462 | $DISTRO_COMMIT="${commit}" 463 | $DISTRO_QUALITY="${quality}" 464 | $DISTRO_VSCODIUM_RELEASE="${release ?? VERSION_RELEASE_MAP[version] ?? ''}" 465 | 466 | $SERVER_APP_NAME="${serverApplicationName}" 467 | $SERVER_INITIAL_EXTENSIONS="${extensions}" 468 | $SERVER_LISTEN_FLAG="${useSocketPath ? `--socket-path="$TMP_DIR/vscode-server-sock-${crypto.randomUUID()}"` : '--port=0'}" 469 | $SERVER_DATA_DIR="$(Resolve-Path ~)\\${serverDataFolderName}" 470 | $SERVER_DIR="$SERVER_DATA_DIR\\bin\\$DISTRO_COMMIT" 471 | $SERVER_SCRIPT="$SERVER_DIR\\bin\\$SERVER_APP_NAME.cmd" 472 | $SERVER_LOGFILE="$SERVER_DATA_DIR\\.$DISTRO_COMMIT.log" 473 | $SERVER_PIDFILE="$SERVER_DATA_DIR\\.$DISTRO_COMMIT.pid" 474 | $SERVER_TOKENFILE="$SERVER_DATA_DIR\\.$DISTRO_COMMIT.token" 475 | $SERVER_ARCH= 476 | $SERVER_CONNECTION_TOKEN= 477 | $SERVER_DOWNLOAD_URL= 478 | 479 | $LISTENING_ON= 480 | $OS_RELEASE_ID= 481 | $ARCH= 482 | $PLATFORM="win32" 483 | 484 | function printInstallResults($code) { 485 | "${id}: start" 486 | "exitCode==$code==" 487 | "listeningOn==$LISTENING_ON==" 488 | "connectionToken==$SERVER_CONNECTION_TOKEN==" 489 | "logFile==$SERVER_LOGFILE==" 490 | "osReleaseId==$OS_RELEASE_ID==" 491 | "arch==$ARCH==" 492 | "platform==$PLATFORM==" 493 | "tmpDir==$TMP_DIR==" 494 | ${envVariables.map(envVar => `"${envVar}==$${envVar}=="`).join('\n')} 495 | "${id}: end" 496 | } 497 | 498 | # Check machine architecture 499 | $ARCH=$env:PROCESSOR_ARCHITECTURE 500 | # Use x64 version for ARM64, as it's not yet available. 501 | if(($ARCH -eq "AMD64") -or ($ARCH -eq "IA64") -or ($ARCH -eq "ARM64")) { 502 | $SERVER_ARCH="x64" 503 | } 504 | else { 505 | "Error architecture not supported: $ARCH" 506 | printInstallResults 1 507 | exit 0 508 | } 509 | 510 | # Create installation folder 511 | if(!(Test-Path $SERVER_DIR)) { 512 | try { 513 | ni -it d $SERVER_DIR -f -ea si 514 | } catch { 515 | "Error creating server install directory - $($_.ToString())" 516 | exit 1 517 | } 518 | 519 | if(!(Test-Path $SERVER_DIR)) { 520 | "Error creating server install directory" 521 | exit 1 522 | } 523 | } 524 | 525 | cd $SERVER_DIR 526 | 527 | # Check if server script is already installed 528 | if(!(Test-Path $SERVER_SCRIPT)) { 529 | del vscode-server.tar.gz 530 | 531 | $REQUEST_ARGUMENTS = @{ 532 | Uri="${downloadUrl}" 533 | TimeoutSec=20 534 | OutFile="vscode-server.tar.gz" 535 | UseBasicParsing=$True 536 | } 537 | 538 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 539 | 540 | Invoke-RestMethod @REQUEST_ARGUMENTS 541 | 542 | if(Test-Path "vscode-server.tar.gz") { 543 | tar -xf vscode-server.tar.gz --strip-components 1 544 | 545 | # Get old commit from product.json and replace with new commit 546 | if(Test-Path "$SERVER_DIR\\product.json") { 547 | $OLD_COMMIT = (Select-String -Path "$SERVER_DIR\\product.json" -Pattern '"commit": *"[^"]*"').Matches.Value -replace '"commit": *"|"','' 548 | if($OLD_COMMIT) { 549 | Get-ChildItem -Path $SERVER_DIR -Recurse -File | ForEach-Object { 550 | (Get-Content $_.FullName) -replace $OLD_COMMIT,$DISTRO_COMMIT | Set-Content $_.FullName 551 | } 552 | } 553 | } 554 | 555 | # Rename all files containing 'codium' in bin directory 556 | Get-ChildItem -Path "$SERVER_DIR\\bin" -Filter "*codium*" | ForEach-Object { 557 | $newName = $_.Name -replace 'codium','trae' 558 | Rename-Item -Path $_.FullName -NewName $newName 559 | } 560 | 561 | del vscode-server.tar.gz 562 | } 563 | 564 | if(!(Test-Path $SERVER_SCRIPT)) { 565 | "Error while installing the server binary" 566 | exit 1 567 | } 568 | } 569 | else { 570 | "Server script already installed in $SERVER_SCRIPT" 571 | } 572 | 573 | # Try to find if server is already running 574 | if(Get-Process node -ErrorAction SilentlyContinue | Where-Object Path -Like "$SERVER_DIR\\*") { 575 | echo "Server script is already running $SERVER_SCRIPT" 576 | } 577 | else { 578 | if(Test-Path $SERVER_LOGFILE) { 579 | del $SERVER_LOGFILE 580 | } 581 | if(Test-Path $SERVER_PIDFILE) { 582 | del $SERVER_PIDFILE 583 | } 584 | if(Test-Path $SERVER_TOKENFILE) { 585 | del $SERVER_TOKENFILE 586 | } 587 | 588 | $SERVER_CONNECTION_TOKEN="${crypto.randomUUID()}" 589 | [System.IO.File]::WriteAllLines($SERVER_TOKENFILE, $SERVER_CONNECTION_TOKEN) 590 | 591 | $SCRIPT_ARGUMENTS="--start-server --host=127.0.0.1 $SERVER_LISTEN_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file $SERVER_TOKENFILE --telemetry-level off --enable-remote-auto-shutdown --accept-server-license-terms *> '$SERVER_LOGFILE'" 592 | 593 | $START_ARGUMENTS = @{ 594 | FilePath = "powershell.exe" 595 | WindowStyle = "hidden" 596 | ArgumentList = @( 597 | "-ExecutionPolicy", "Unrestricted", "-NoLogo", "-NoProfile", "-NonInteractive", "-c", "$SERVER_SCRIPT $SCRIPT_ARGUMENTS" 598 | ) 599 | PassThru = $True 600 | } 601 | 602 | $SERVER_ID = (start @START_ARGUMENTS).ID 603 | 604 | if($SERVER_ID) { 605 | [System.IO.File]::WriteAllLines($SERVER_PIDFILE, $SERVER_ID) 606 | } 607 | } 608 | 609 | if(Test-Path $SERVER_TOKENFILE) { 610 | $SERVER_CONNECTION_TOKEN="$(cat $SERVER_TOKENFILE)" 611 | } 612 | else { 613 | "Error server token file not found $SERVER_TOKENFILE" 614 | printInstallResults 1 615 | exit 0 616 | } 617 | 618 | sleep -Milliseconds 500 619 | 620 | $SELECT_ARGUMENTS = @{ 621 | Path = $SERVER_LOGFILE 622 | Pattern = "Extension host agent listening on (\\d+)" 623 | } 624 | 625 | for($I = 1; $I -le 5; $I++) { 626 | if(Test-Path $SERVER_LOGFILE) { 627 | $GROUPS = (Select-String @SELECT_ARGUMENTS).Matches.Groups 628 | 629 | if($GROUPS) { 630 | $LISTENING_ON = $GROUPS[1].Value 631 | break 632 | } 633 | } 634 | 635 | sleep -Milliseconds 500 636 | } 637 | 638 | if(!(Test-Path $SERVER_LOGFILE)) { 639 | "Error server log file not found $SERVER_LOGFILE" 640 | printInstallResults 1 641 | exit 0 642 | } 643 | 644 | # Finish server setup 645 | printInstallResults 0 646 | 647 | if($SERVER_ID) { 648 | while($True) { 649 | if(!(gps -Id $SERVER_ID)) { 650 | "server died, exit" 651 | exit 0 652 | } 653 | 654 | sleep 30 655 | } 656 | } 657 | `; 658 | } 659 | -------------------------------------------------------------------------------- /src/ssh/hostfile.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import * as crypto from 'crypto'; 5 | import { exists as folderExists } from '../common/files'; 6 | 7 | const PATH_SSH_USER_DIR = path.join(os.homedir(), '.ssh'); 8 | const KNOW_HOST_FILE = path.join(PATH_SSH_USER_DIR, 'known_hosts'); 9 | const HASH_MAGIC = '|1|'; 10 | const HASH_DELIM = '|'; 11 | 12 | export async function checkNewHostInHostkeys(host: string): Promise { 13 | const fileContent = await fs.promises.readFile(KNOW_HOST_FILE, { encoding: 'utf8' }); 14 | const lines = fileContent.split(/\r?\n/); 15 | for (let line of lines) { 16 | line = line.trim(); 17 | if (!line.startsWith(HASH_MAGIC)) { 18 | continue; 19 | } 20 | 21 | const [hostEncripted_] = line.split(' '); 22 | const [salt_, hostHash_] = hostEncripted_.substring(HASH_MAGIC.length).split(HASH_DELIM); 23 | const hostHash = crypto.createHmac('sha1', Buffer.from(salt_, 'base64')).update(host).digest(); 24 | if (hostHash.toString('base64') === hostHash_) { 25 | return false; 26 | } 27 | } 28 | 29 | return true; 30 | } 31 | 32 | export async function addHostToHostFile(host: string, hostKey: Buffer, type: string): Promise { 33 | if (!folderExists(PATH_SSH_USER_DIR)) { 34 | await fs.promises.mkdir(PATH_SSH_USER_DIR, 0o700); 35 | } 36 | 37 | const salt = crypto.randomBytes(20); 38 | const hostHash = crypto.createHmac('sha1', salt).update(host).digest(); 39 | 40 | const entry = `${HASH_MAGIC}${salt.toString('base64')}${HASH_DELIM}${hostHash.toString('base64')} ${type} ${hostKey.toString('base64')}\n`; 41 | await fs.promises.appendFile(KNOW_HOST_FILE, entry); 42 | } 43 | -------------------------------------------------------------------------------- /src/ssh/identityFiles.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as os from 'os'; 3 | import * as path from 'path'; 4 | import * as crypto from 'crypto'; 5 | import type { ParsedKey } from 'ssh2-streams'; 6 | import * as ssh2 from 'ssh2'; 7 | import { untildify, exists as fileExists } from '../common/files'; 8 | import Log from '../common/logger'; 9 | 10 | const homeDir = os.homedir(); 11 | const PATH_SSH_CLIENT_ID_DSA = path.join(homeDir, '.ssh', '/id_dsa'); 12 | const PATH_SSH_CLIENT_ID_ECDSA = path.join(homeDir, '.ssh', '/id_ecdsa'); 13 | const PATH_SSH_CLIENT_ID_RSA = path.join(homeDir, '.ssh', '/id_rsa'); 14 | const PATH_SSH_CLIENT_ID_ED25519 = path.join(homeDir, '.ssh', '/id_ed25519'); 15 | const PATH_SSH_CLIENT_ID_XMSS = path.join(homeDir, '.ssh', '/id_xmss'); 16 | const PATH_SSH_CLIENT_ID_ECDSA_SK = path.join(homeDir, '.ssh', '/id_ecdsa_sk'); 17 | const PATH_SSH_CLIENT_ID_ED25519_SK = path.join(homeDir, '.ssh', '/id_ed25519_sk'); 18 | 19 | const DEFAULT_IDENTITY_FILES: string[] = [ 20 | PATH_SSH_CLIENT_ID_RSA, 21 | PATH_SSH_CLIENT_ID_ECDSA, 22 | PATH_SSH_CLIENT_ID_ECDSA_SK, 23 | PATH_SSH_CLIENT_ID_ED25519, 24 | PATH_SSH_CLIENT_ID_ED25519_SK, 25 | PATH_SSH_CLIENT_ID_XMSS, 26 | PATH_SSH_CLIENT_ID_DSA, 27 | ]; 28 | 29 | export interface SSHKey { 30 | filename: string; 31 | parsedKey: ParsedKey; 32 | fingerprint: string; 33 | agentSupport?: boolean; 34 | isPrivate?: boolean; 35 | } 36 | 37 | // From https://github.com/openssh/openssh-portable/blob/acb2059febaddd71ee06c2ebf63dcf211d9ab9f2/sshconnect2.c#L1689-L1690 38 | export async function gatherIdentityFiles(identityFiles: string[], sshAgentSock: string | undefined, identitiesOnly: boolean, logger: Log) { 39 | identityFiles = identityFiles.map(untildify).map(i => i.replace(/\.pub$/, '')); 40 | if (identityFiles.length === 0) { 41 | identityFiles.push(...DEFAULT_IDENTITY_FILES); 42 | } 43 | 44 | const identityFileContentsResult = await Promise.allSettled(identityFiles.map(async keyPath => { 45 | keyPath = await fileExists(keyPath + '.pub') ? keyPath + '.pub' : keyPath; 46 | return fs.promises.readFile(keyPath); 47 | })); 48 | const fileKeys: SSHKey[] = identityFileContentsResult.map((result, i) => { 49 | if (result.status === 'rejected') { 50 | return undefined; 51 | } 52 | 53 | const parsedResult = ssh2.utils.parseKey(result.value); 54 | if (parsedResult instanceof Error || !parsedResult) { 55 | logger.error(`Error while parsing SSH public key ${identityFiles[i]}:`, parsedResult); 56 | return undefined; 57 | } 58 | 59 | const parsedKey = Array.isArray(parsedResult) ? parsedResult[0] : parsedResult; 60 | const fingerprint = crypto.createHash('sha256').update(parsedKey.getPublicSSH()).digest('base64'); 61 | 62 | return { 63 | filename: identityFiles[i], 64 | parsedKey, 65 | fingerprint 66 | }; 67 | }).filter((v: T | undefined): v is T => !!v); 68 | 69 | let sshAgentParsedKeys: ParsedKey[] = []; 70 | try { 71 | if (!sshAgentSock) { 72 | throw new Error(`SSH_AUTH_SOCK environment variable not defined`); 73 | } 74 | 75 | sshAgentParsedKeys = await new Promise((resolve, reject) => { 76 | const sshAgent = new ssh2.OpenSSHAgent(sshAgentSock); 77 | sshAgent.getIdentities((err, publicKeys) => { 78 | if (err) { 79 | reject(err); 80 | } else { 81 | resolve(publicKeys || []); 82 | } 83 | }); 84 | }); 85 | } catch (e) { 86 | logger.error(`Couldn't get identities from OpenSSH agent`, e); 87 | } 88 | 89 | const sshAgentKeys: SSHKey[] = sshAgentParsedKeys.map(parsedKey => { 90 | const fingerprint = crypto.createHash('sha256').update(parsedKey.getPublicSSH()).digest('base64'); 91 | return { 92 | filename: parsedKey.comment, 93 | parsedKey, 94 | fingerprint, 95 | agentSupport: true 96 | }; 97 | }); 98 | 99 | const agentKeys: SSHKey[] = []; 100 | const preferredIdentityKeys: SSHKey[] = []; 101 | for (const agentKey of sshAgentKeys) { 102 | const foundIdx = fileKeys.findIndex(k => agentKey.parsedKey.type === k.parsedKey.type && agentKey.fingerprint === k.fingerprint); 103 | if (foundIdx >= 0) { 104 | preferredIdentityKeys.push({ ...fileKeys[foundIdx], agentSupport: true }); 105 | fileKeys.splice(foundIdx, 1); 106 | } else if (!identitiesOnly) { 107 | agentKeys.push(agentKey); 108 | } 109 | } 110 | preferredIdentityKeys.push(...agentKeys); 111 | preferredIdentityKeys.push(...fileKeys); 112 | 113 | logger.trace(`Identity keys:`, preferredIdentityKeys.length ? preferredIdentityKeys.map(k => `${k.filename} ${k.parsedKey.type} SHA256:${k.fingerprint}`).join('\n') : 'None'); 114 | 115 | return preferredIdentityKeys; 116 | } 117 | -------------------------------------------------------------------------------- /src/ssh/sshConfig.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import SSHConfig, { Directive, Line, Section } from '@jeanp413/ssh-config'; 5 | import * as vscode from 'vscode'; 6 | import { exists as fileExists, normalizeToSlash, untildify } from '../common/files'; 7 | import { isWindows } from '../common/platform'; 8 | import { glob } from 'glob'; 9 | 10 | const systemSSHConfig = isWindows ? path.resolve(process.env.ALLUSERSPROFILE || 'C:\\ProgramData', 'ssh\\ssh_config') : '/etc/ssh/ssh_config'; 11 | const defaultSSHConfigPath = path.resolve(os.homedir(), '.ssh/config'); 12 | 13 | export function getSSHConfigPath() { 14 | const sshConfigPath = vscode.workspace.getConfiguration('remote.SSH').get('configFile'); 15 | return sshConfigPath ? untildify(sshConfigPath) : defaultSSHConfigPath; 16 | } 17 | 18 | function isDirective(line: Line): line is Directive { 19 | return line.type === SSHConfig.DIRECTIVE; 20 | } 21 | 22 | function isHostSection(line: Line): line is Section { 23 | return isDirective(line) && line.param === 'Host' && !!line.value && !!(line as Section).config; 24 | } 25 | 26 | function isIncludeDirective(line: Line): line is Section { 27 | return isDirective(line) && line.param === 'Include' && !!line.value; 28 | } 29 | 30 | const SSH_CONFIG_PROPERTIES: Record = { 31 | 'host': 'Host', 32 | 'hostname': 'HostName', 33 | 'user': 'User', 34 | 'port': 'Port', 35 | 'identityagent': 'IdentityAgent', 36 | 'identitiesonly': 'IdentitiesOnly', 37 | 'identityfile': 'IdentityFile', 38 | 'forwardagent': 'ForwardAgent', 39 | 'preferredauthentications': 'PreferredAuthentications', 40 | 'proxyjump': 'ProxyJump', 41 | 'proxycommand': 'ProxyCommand', 42 | 'include': 'Include', 43 | }; 44 | 45 | function normalizeProp(prop: Directive) { 46 | prop.param = SSH_CONFIG_PROPERTIES[prop.param.toLowerCase()] || prop.param; 47 | } 48 | 49 | function normalizeSSHConfig(config: SSHConfig) { 50 | for (const line of config) { 51 | if (isDirective(line)) { 52 | normalizeProp(line); 53 | } 54 | if (isHostSection(line)) { 55 | normalizeSSHConfig(line.config); 56 | } 57 | } 58 | return config; 59 | } 60 | 61 | async function parseSSHConfigFromFile(filePath: string, userConfig: boolean) { 62 | let content = ''; 63 | if (await fileExists(filePath)) { 64 | content = (await fs.promises.readFile(filePath, 'utf8')).trim(); 65 | } 66 | const config = normalizeSSHConfig(SSHConfig.parse(content)); 67 | 68 | const includedConfigs: [number, SSHConfig[]][] = []; 69 | for (let i = 0; i < config.length; i++) { 70 | const line = config[i]; 71 | if (isIncludeDirective(line)) { 72 | const values = (line.value as string).split(',').map(s => s.trim()); 73 | const configs: SSHConfig[] = []; 74 | for (const value of values) { 75 | const includePaths = await glob(normalizeToSlash(untildify(value)), { 76 | absolute: true, 77 | cwd: normalizeToSlash(path.dirname(userConfig ? defaultSSHConfigPath : systemSSHConfig)) 78 | }); 79 | for (const p of includePaths) { 80 | configs.push(await parseSSHConfigFromFile(p, userConfig)); 81 | } 82 | } 83 | includedConfigs.push([i, configs]); 84 | } 85 | } 86 | for (const [idx, includeConfigs] of includedConfigs.reverse()) { 87 | config.splice(idx, 1, ...includeConfigs.flat()); 88 | } 89 | 90 | return config; 91 | } 92 | 93 | export default class SSHConfiguration { 94 | 95 | static async loadFromFS(): Promise { 96 | const config = await parseSSHConfigFromFile(getSSHConfigPath(), true); 97 | config.push(...await parseSSHConfigFromFile(systemSSHConfig, false)); 98 | 99 | return new SSHConfiguration(config); 100 | } 101 | 102 | constructor(private sshConfig: SSHConfig) { 103 | } 104 | 105 | getAllConfiguredHosts(): string[] { 106 | const hosts = new Set(); 107 | for (const line of this.sshConfig) { 108 | if (isHostSection(line)) { 109 | const value = Array.isArray(line.value) ? line.value[0] : line.value; 110 | const isPattern = /^!/.test(value) || /[?*]/.test(value); 111 | if (!isPattern) { 112 | hosts.add(value); 113 | } 114 | } 115 | } 116 | 117 | return [...hosts.keys()]; 118 | } 119 | 120 | getHostConfiguration(host: string): Record { 121 | // Only a few directives return an array 122 | // https://github.com/jeanp413/ssh-config/blob/8d187bb8f1d83a51ff2b1d127e6b6269d24092b5/src/ssh-config.ts#L9C1-L9C118 123 | return this.sshConfig.compute(host) as Record; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/ssh/sshConnection.ts: -------------------------------------------------------------------------------- 1 | // From https://github.com/sanketbajoria/ssh2-promise 2 | 3 | import { EventEmitter } from 'events'; 4 | import * as net from 'net'; 5 | import * as fs from 'fs'; 6 | import * as stream from 'stream'; 7 | import { Client, ClientChannel, ClientErrorExtensions, ExecOptions, ShellOptions, ConnectConfig } from 'ssh2'; 8 | import { Server } from 'net'; 9 | import { SocksConnectionInfo, createServer as createSocksServer } from 'simple-socks'; 10 | 11 | export interface SSHConnectConfig extends ConnectConfig { 12 | /** Optional Unique ID attached to ssh connection. */ 13 | uniqueId?: string; 14 | /** Automatic retry to connect, after disconnect. Default true */ 15 | reconnect?: boolean; 16 | /** Number of reconnect retry, after disconnect. Default 10 */ 17 | reconnectTries?: number; 18 | /** Delay after which reconnect should be done. Default 5000ms */ 19 | reconnectDelay?: number; 20 | /** Path to private key */ 21 | identity?: string | Buffer; 22 | } 23 | 24 | export interface SSHTunnelConfig { 25 | /** Remote Address to connect */ 26 | remoteAddr?: string; 27 | /** Local port to bind to. By default, it will bind to a random port, if not passed */ 28 | localPort?: number; 29 | /** Remote Port to connect */ 30 | remotePort?: number; 31 | /** Remote socket path to connect */ 32 | remoteSocketPath?: string; 33 | socks?: boolean; 34 | /** Unique name */ 35 | name?: string; 36 | } 37 | 38 | const defaultOptions: Partial = { 39 | reconnect: false, 40 | port: 22, 41 | reconnectTries: 3, 42 | reconnectDelay: 5000 43 | }; 44 | 45 | const SSHConstants = { 46 | 'CHANNEL': { 47 | SSH: 'ssh', 48 | TUNNEL: 'tunnel', 49 | X11: 'x11' 50 | }, 51 | 'STATUS': { 52 | BEFORECONNECT: 'beforeconnect', 53 | CONNECT: 'connect', 54 | BEFOREDISCONNECT: 'beforedisconnect', 55 | DISCONNECT: 'disconnect' 56 | } 57 | }; 58 | 59 | export default class SSHConnection extends EventEmitter { 60 | public config: SSHConnectConfig; 61 | 62 | private activeTunnels: { [index: string]: SSHTunnelConfig & { server: Server } } = {}; 63 | private __$connectPromise: Promise | null = null; 64 | private __retries: number = 0; 65 | private __err: Error & ClientErrorExtensions & { code?: string } | null = null; 66 | private sshConnection: Client | null = null; 67 | 68 | constructor(options: SSHConnectConfig) { 69 | super(); 70 | this.config = Object.assign({}, defaultOptions, options); 71 | this.config.uniqueId = this.config.uniqueId || `${this.config.username}@${this.config.host}`; 72 | } 73 | 74 | /** 75 | * Emit message on this channel 76 | */ 77 | override emit(channel: string, status: string, payload?: any): boolean { 78 | super.emit(channel, status, this, payload); 79 | return super.emit(`${channel}:${status}`, this, payload); 80 | } 81 | 82 | /** 83 | * Get shell socket 84 | */ 85 | shell(options: ShellOptions = {}): Promise { 86 | return this.connect().then(() => { 87 | return new Promise((resolve, reject) => { 88 | this.sshConnection!.shell(options, (err, stream) => err ? reject(err) : resolve(stream)); 89 | }); 90 | }); 91 | } 92 | 93 | /** 94 | * Exec a command 95 | */ 96 | exec(cmd: string, params?: Array, options: ExecOptions = {}): Promise<{ stdout: string; stderr: string }> { 97 | cmd += (Array.isArray(params) ? (' ' + params.join(' ')) : ''); 98 | return this.connect().then(() => { 99 | return new Promise((resolve, reject) => { 100 | this.sshConnection!.exec(cmd, options, (err, stream) => { 101 | if (err) { 102 | return reject(err); 103 | } 104 | let stdout = ''; 105 | let stderr = ''; 106 | stream.on('close', function () { 107 | return resolve({ stdout, stderr }); 108 | }).on('data', function (data: Buffer | string) { 109 | stdout += data.toString(); 110 | }).stderr.on('data', function (data: Buffer | string) { 111 | stderr += data.toString(); 112 | }); 113 | }); 114 | }); 115 | }); 116 | } 117 | 118 | /** 119 | * Exec a command 120 | */ 121 | execPartial(cmd: string, tester: (stdout: string, stderr: string) => boolean, params?: Array, options: ExecOptions = {}): Promise<{ stdout: string; stderr: string }> { 122 | cmd += (Array.isArray(params) ? (' ' + params.join(' ')) : ''); 123 | return this.connect().then(() => { 124 | return new Promise((resolve, reject) => { 125 | this.sshConnection!.exec(cmd, options, (err, stream) => { 126 | if (err) { 127 | return reject(err); 128 | } 129 | let stdout = ''; 130 | let stderr = ''; 131 | let resolved = false; 132 | stream.on('close', function () { 133 | if (!resolved) { 134 | return resolve({ stdout, stderr }); 135 | } 136 | }).on('data', function (data: Buffer | string) { 137 | stdout += data.toString(); 138 | 139 | if (tester(stdout, stderr)) { 140 | resolved = true; 141 | 142 | return resolve({ stdout, stderr }); 143 | } 144 | }).stderr.on('data', function (data: Buffer | string) { 145 | stderr += data.toString(); 146 | 147 | if (tester(stdout, stderr)) { 148 | resolved = true; 149 | 150 | return resolve({ stdout, stderr }); 151 | } 152 | }); 153 | }); 154 | }); 155 | }); 156 | } 157 | 158 | /** 159 | * Forward out 160 | */ 161 | forwardOut(srcIP: string, srcPort: number, destIP: string, destPort: number): Promise { 162 | return this.connect().then(() => { 163 | return new Promise((resolve, reject) => { 164 | this.sshConnection!.forwardOut(srcIP, srcPort, destIP, destPort, (err, stream) => { 165 | if (err) { 166 | return reject(err); 167 | } 168 | resolve(stream); 169 | }); 170 | }); 171 | }); 172 | } 173 | 174 | /** 175 | * Get a Socks Port 176 | */ 177 | getSocksPort(localPort: number): Promise { 178 | return this.addTunnel({ name: '__socksServer', socks: true, localPort: localPort }).then((tunnel) => { 179 | return tunnel.localPort!; 180 | }); 181 | } 182 | 183 | /** 184 | * Close SSH Connection 185 | */ 186 | close(): Promise { 187 | this.emit(SSHConstants.CHANNEL.SSH, SSHConstants.STATUS.BEFOREDISCONNECT); 188 | return this.closeTunnel().then(() => { 189 | if (this.sshConnection) { 190 | this.sshConnection.end(); 191 | this.emit(SSHConstants.CHANNEL.SSH, SSHConstants.STATUS.DISCONNECT); 192 | } 193 | }); 194 | } 195 | 196 | /** 197 | * Connect the SSH Connection 198 | */ 199 | connect(c?: SSHConnectConfig): Promise { 200 | this.config = Object.assign(this.config, c); 201 | ++this.__retries; 202 | 203 | if (this.__$connectPromise) { 204 | return this.__$connectPromise; 205 | } 206 | 207 | this.__$connectPromise = new Promise((resolve, reject) => { 208 | this.emit(SSHConstants.CHANNEL.SSH, SSHConstants.STATUS.BEFORECONNECT); 209 | if (!this.config || typeof this.config === 'function' || !(this.config.host || this.config.sock) || !this.config.username) { 210 | reject(`Invalid SSH connection configuration host/username can't be empty`); 211 | this.__$connectPromise = null; 212 | return; 213 | } 214 | 215 | if (this.config.identity) { 216 | if (fs.existsSync(this.config.identity)) { 217 | this.config.privateKey = fs.readFileSync(this.config.identity); 218 | } 219 | delete this.config.identity; 220 | } 221 | 222 | //Start ssh server connection 223 | this.sshConnection = new Client(); 224 | this.sshConnection.on('ready', (err: Error & ClientErrorExtensions) => { 225 | if (err) { 226 | this.emit(SSHConstants.CHANNEL.SSH, SSHConstants.STATUS.DISCONNECT, { err: err }); 227 | this.__$connectPromise = null; 228 | return reject(err); 229 | } 230 | this.emit(SSHConstants.CHANNEL.SSH, SSHConstants.STATUS.CONNECT); 231 | this.__retries = 0; 232 | this.__err = null; 233 | resolve(this); 234 | }).on('error', (err) => { 235 | this.emit(SSHConstants.CHANNEL.SSH, SSHConstants.STATUS.DISCONNECT, { err: err }); 236 | this.__err = err; 237 | }).on('close', () => { 238 | this.emit(SSHConstants.CHANNEL.SSH, SSHConstants.STATUS.DISCONNECT, { err: this.__err }); 239 | if (this.config.reconnect && this.__retries <= this.config.reconnectTries! && this.__err && this.__err.level !== 'client-authentication' && this.__err.code !== 'ENOTFOUND') { 240 | setTimeout(() => { 241 | this.__$connectPromise = null; 242 | resolve(this.connect()); 243 | }, this.config.reconnectDelay); 244 | } else { 245 | reject(this.__err); 246 | } 247 | }).connect(this.config); 248 | }); 249 | return this.__$connectPromise; 250 | } 251 | 252 | /** 253 | * Get existing tunnel by name 254 | */ 255 | getTunnel(name: string) { 256 | return this.activeTunnels[name]; 257 | } 258 | 259 | /** 260 | * Add new tunnel if not exist 261 | */ 262 | addTunnel(SSHTunnelConfig: SSHTunnelConfig): Promise { 263 | SSHTunnelConfig.name = SSHTunnelConfig.name || `${SSHTunnelConfig.remoteAddr}@${SSHTunnelConfig.remotePort || SSHTunnelConfig.remoteSocketPath}`; 264 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.BEFORECONNECT, { SSHTunnelConfig: SSHTunnelConfig }); 265 | if (this.getTunnel(SSHTunnelConfig.name)) { 266 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.CONNECT, { SSHTunnelConfig: SSHTunnelConfig }); 267 | return Promise.resolve(this.getTunnel(SSHTunnelConfig.name)); 268 | } else { 269 | return new Promise((resolve, reject) => { 270 | let server: net.Server; 271 | if (SSHTunnelConfig.socks) { 272 | server = createSocksServer({ 273 | connectionFilter: (destination: SocksConnectionInfo, origin: SocksConnectionInfo, callback: (err?: any, dest?: stream.Duplex) => void) => { 274 | this.connect().then(() => { 275 | this.sshConnection!.forwardOut( 276 | origin.address, 277 | origin.port, 278 | destination.address, 279 | destination.port, 280 | (err, stream) => { 281 | if (err) { 282 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.DISCONNECT, { SSHTunnelConfig: SSHTunnelConfig, err: err }); 283 | return callback(err); 284 | } 285 | return callback(null, stream); 286 | }); 287 | }); 288 | } 289 | }).on('proxyError', (err: any) => { 290 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.DISCONNECT, { SSHTunnelConfig: SSHTunnelConfig, err: err }); 291 | }); 292 | } else { 293 | server = net.createServer() 294 | .on('connection', (socket) => { 295 | this.connect().then(() => { 296 | if (SSHTunnelConfig.remotePort) { 297 | this.sshConnection!.forwardOut('127.0.0.1', 0, SSHTunnelConfig.remoteAddr!, SSHTunnelConfig.remotePort!, (err, stream) => { 298 | if (err) { 299 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.DISCONNECT, { SSHTunnelConfig: SSHTunnelConfig, err: err }); 300 | return; 301 | } 302 | stream.pipe(socket); 303 | socket.pipe(stream); 304 | }); 305 | } else { 306 | this.sshConnection!.openssh_forwardOutStreamLocal(SSHTunnelConfig.remoteSocketPath!, (err, stream) => { 307 | if (err) { 308 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.DISCONNECT, { SSHTunnelConfig: SSHTunnelConfig, err: err }); 309 | return; 310 | } 311 | stream.pipe(socket); 312 | socket.pipe(stream); 313 | }); 314 | } 315 | }); 316 | }); 317 | } 318 | 319 | SSHTunnelConfig.localPort = SSHTunnelConfig.localPort || 0; 320 | server.on('listening', () => { 321 | SSHTunnelConfig.localPort = (server.address() as net.AddressInfo).port; 322 | this.activeTunnels[SSHTunnelConfig.name!] = Object.assign({}, { server }, SSHTunnelConfig); 323 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.CONNECT, { SSHTunnelConfig: SSHTunnelConfig }); 324 | resolve(this.activeTunnels[SSHTunnelConfig.name!]); 325 | }).on('error', (err: any) => { 326 | this.emit(SSHConstants.CHANNEL.TUNNEL, SSHConstants.STATUS.DISCONNECT, { SSHTunnelConfig: SSHTunnelConfig, err: err }); 327 | server.close(); 328 | reject(err); 329 | delete this.activeTunnels[SSHTunnelConfig.name!]; 330 | }).listen(SSHTunnelConfig.localPort); 331 | }); 332 | } 333 | } 334 | 335 | /** 336 | * Close the tunnel 337 | */ 338 | closeTunnel(name?: string): Promise { 339 | if (name && this.activeTunnels[name]) { 340 | return new Promise((resolve) => { 341 | const tunnel = this.activeTunnels[name]; 342 | this.emit( 343 | SSHConstants.CHANNEL.TUNNEL, 344 | SSHConstants.STATUS.BEFOREDISCONNECT, 345 | { SSHTunnelConfig: tunnel } 346 | ); 347 | tunnel.server.close(() => { 348 | this.emit( 349 | SSHConstants.CHANNEL.TUNNEL, 350 | SSHConstants.STATUS.DISCONNECT, 351 | { SSHTunnelConfig: this.activeTunnels[name] } 352 | ); 353 | delete this.activeTunnels[name]; 354 | resolve(); 355 | }); 356 | }); 357 | } else if (!name) { 358 | const tunnels = Object.keys(this.activeTunnels).map((key) => this.closeTunnel(key)); 359 | return Promise.all(tunnels).then(() => { }); 360 | } 361 | 362 | return Promise.resolve(); 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/ssh/sshDestination.ts: -------------------------------------------------------------------------------- 1 | export default class SSHDestination { 2 | constructor( 3 | public readonly hostname: string, 4 | public readonly user?: string, 5 | public readonly port?: number 6 | ) { 7 | } 8 | 9 | static parse(dest: string): SSHDestination { 10 | let user: string | undefined; 11 | const atPos = dest.lastIndexOf('@'); 12 | if (atPos !== -1) { 13 | user = dest.substring(0, atPos); 14 | } 15 | 16 | let port: number | undefined; 17 | const colonPos = dest.lastIndexOf(':'); 18 | if (colonPos !== -1) { 19 | port = parseInt(dest.substring(colonPos + 1), 10); 20 | } 21 | 22 | const start = atPos !== -1 ? atPos + 1 : 0; 23 | const end = colonPos !== -1 ? colonPos : dest.length; 24 | const hostname = dest.substring(start, end); 25 | 26 | return new SSHDestination(hostname, user, port); 27 | } 28 | 29 | toString(): string { 30 | let result = this.hostname; 31 | if (this.user) { 32 | result = `${this.user}@` + result; 33 | } 34 | if (this.port) { 35 | result = result + `:${this.port}`; 36 | } 37 | return result; 38 | } 39 | 40 | // vscode.uri implementation lowercases the authority, so when reopen or restore 41 | // a remote session from the recently openend list the connection fails 42 | static parseEncoded(dest: string): SSHDestination { 43 | try { 44 | const data = JSON.parse(Buffer.from(dest, 'hex').toString()); 45 | return new SSHDestination(data.hostName, data.user, data.port); 46 | } catch { 47 | } 48 | return SSHDestination.parse(dest.replace(/\\x([0-9a-f]{2})/g, (_, charCode) => String.fromCharCode(parseInt(charCode, 16)))); 49 | } 50 | 51 | toEncodedString(): string { 52 | return this.toString().replace(/[A-Z]/g, (ch) => `\\x${ch.charCodeAt(0).toString(16).toLowerCase()}`); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": [ 5 | "ES2016", 6 | "ES2017.Object", 7 | "ES2017.String", 8 | "ES2017.Intl", 9 | "ES2017.TypedArrays", 10 | "ES2018.AsyncIterable", 11 | "ES2018.AsyncGenerator", 12 | "ES2018.Promise", 13 | "ES2018.Regexp", 14 | "ES2018.Intl", 15 | "ES2019.Array", 16 | "ES2019.Object", 17 | "ES2019.String", 18 | "ES2019.Symbol", 19 | "ES2020.BigInt", 20 | "ES2020.Promise", 21 | "ES2020.String", 22 | "ES2020.Symbol.WellKnown", 23 | "ES2020.Intl", 24 | ], 25 | "module": "commonjs", 26 | "strict": true, 27 | "outDir": "./out", 28 | "sourceMap": true, 29 | "exactOptionalPropertyTypes": false, 30 | "useUnknownInCatchVariables": false, 31 | "alwaysStrict": true, 32 | "noImplicitAny": true, 33 | "noImplicitReturns": true, 34 | "noImplicitOverride": true, 35 | "noUnusedLocals": true, 36 | "noUnusedParameters": true, 37 | "forceConsistentCasingInFileNames": true, 38 | "esModuleInterop": true 39 | }, 40 | "exclude": [ 41 | "node_modules", 42 | ".vscode-test" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | const webpack = require('webpack'); 7 | 8 | /**@type {import('webpack').Configuration}*/ 9 | const config = { 10 | target: 'node', 11 | entry: './src/extension.ts', 12 | output: { 13 | path: path.resolve(__dirname, 'out'), 14 | filename: 'extension.js', 15 | libraryTarget: "commonjs2", 16 | devtoolModuleFilenameTemplate: "../[resource-path]", 17 | }, 18 | devtool: 'source-map', 19 | externals: { 20 | vscode: "commonjs vscode", 21 | bufferutil: "bufferutil", 22 | "utf-8-validate": "utf-8-validate", 23 | }, 24 | resolve: { 25 | extensions: ['.ts', '.js'] 26 | }, 27 | module: { 28 | rules: [{ 29 | test: /\.ts$/, 30 | exclude: /node_modules/, 31 | use: [{ 32 | loader: 'ts-loader' 33 | }] 34 | }] 35 | }, 36 | plugins: [ 37 | new webpack.IgnorePlugin({ 38 | resourceRegExp: /crypto\/build\/Release\/sshcrypto\.node$/, 39 | }), 40 | new webpack.IgnorePlugin({ 41 | resourceRegExp: /cpu-features/, 42 | }) 43 | ] 44 | } 45 | 46 | module.exports = (_env, argv) => { 47 | if (argv.mode === 'production') { 48 | config.devtool = false; 49 | } 50 | 51 | return config; 52 | }; 53 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/runtime-corejs3@^7.16.8": 6 | version "7.18.3" 7 | resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz#52f0241a31e0ec61a6187530af6227c2846bd60c" 8 | integrity sha512-l4ddFwrc9rnR+EJsHsh+TJ4A35YqQz/UqcjtlX2ov53hlJYG5CxtQmNZxyajwDVmCxwy++rtvGU5HazCK4W41Q== 9 | dependencies: 10 | core-js-pure "^3.20.2" 11 | regenerator-runtime "^0.13.4" 12 | 13 | "@discoveryjs/json-ext@^0.5.0": 14 | version "0.5.7" 15 | resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" 16 | integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== 17 | 18 | "@eslint/eslintrc@^1.3.0": 19 | version "1.3.0" 20 | resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" 21 | integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== 22 | dependencies: 23 | ajv "^6.12.4" 24 | debug "^4.3.2" 25 | espree "^9.3.2" 26 | globals "^13.15.0" 27 | ignore "^5.2.0" 28 | import-fresh "^3.2.1" 29 | js-yaml "^4.1.0" 30 | minimatch "^3.1.2" 31 | strip-json-comments "^3.1.1" 32 | 33 | "@humanwhocodes/config-array@^0.9.2": 34 | version "0.9.5" 35 | resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" 36 | integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== 37 | dependencies: 38 | "@humanwhocodes/object-schema" "^1.2.1" 39 | debug "^4.1.1" 40 | minimatch "^3.0.4" 41 | 42 | "@humanwhocodes/object-schema@^1.2.1": 43 | version "1.2.1" 44 | resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" 45 | integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== 46 | 47 | "@jeanp413/ssh-config@^4.3.1": 48 | version "4.3.1" 49 | resolved "https://registry.yarnpkg.com/@jeanp413/ssh-config/-/ssh-config-4.3.1.tgz#08055a9ea3a42b6557115e658d336c00b14d1313" 50 | integrity sha512-x0EaWRdjs5sPDNmYr11wVB1GdwWQgRekc7SbueuO5FK7YZUav98qZKtZZU5iSDKyxJkooCs3rgVizB1wIWrF7g== 51 | 52 | "@jridgewell/gen-mapping@^0.3.0": 53 | version "0.3.2" 54 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" 55 | integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== 56 | dependencies: 57 | "@jridgewell/set-array" "^1.0.1" 58 | "@jridgewell/sourcemap-codec" "^1.4.10" 59 | "@jridgewell/trace-mapping" "^0.3.9" 60 | 61 | "@jridgewell/resolve-uri@^3.0.3": 62 | version "3.0.8" 63 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz#687cc2bbf243f4e9a868ecf2262318e2658873a1" 64 | integrity sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w== 65 | 66 | "@jridgewell/set-array@^1.0.1": 67 | version "1.1.2" 68 | resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" 69 | integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== 70 | 71 | "@jridgewell/source-map@^0.3.2": 72 | version "0.3.2" 73 | resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" 74 | integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== 75 | dependencies: 76 | "@jridgewell/gen-mapping" "^0.3.0" 77 | "@jridgewell/trace-mapping" "^0.3.9" 78 | 79 | "@jridgewell/sourcemap-codec@^1.4.10": 80 | version "1.4.14" 81 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" 82 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 83 | 84 | "@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": 85 | version "0.3.14" 86 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" 87 | integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== 88 | dependencies: 89 | "@jridgewell/resolve-uri" "^3.0.3" 90 | "@jridgewell/sourcemap-codec" "^1.4.10" 91 | 92 | "@nodelib/fs.scandir@2.1.5": 93 | version "2.1.5" 94 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" 95 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 96 | dependencies: 97 | "@nodelib/fs.stat" "2.0.5" 98 | run-parallel "^1.1.9" 99 | 100 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 101 | version "2.0.5" 102 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" 103 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 104 | 105 | "@nodelib/fs.walk@^1.2.3": 106 | version "1.2.8" 107 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" 108 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 109 | dependencies: 110 | "@nodelib/fs.scandir" "2.1.5" 111 | fastq "^1.6.0" 112 | 113 | "@types/eslint-scope@^3.7.3": 114 | version "3.7.3" 115 | resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" 116 | integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== 117 | dependencies: 118 | "@types/eslint" "*" 119 | "@types/estree" "*" 120 | 121 | "@types/eslint@*": 122 | version "8.4.3" 123 | resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.3.tgz#5c92815a3838b1985c90034cd85f26f59d9d0ece" 124 | integrity sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw== 125 | dependencies: 126 | "@types/estree" "*" 127 | "@types/json-schema" "*" 128 | 129 | "@types/estree@*": 130 | version "0.0.52" 131 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.52.tgz#7f1f57ad5b741f3d5b210d3b1f145640d89bf8fe" 132 | integrity sha512-BZWrtCU0bMVAIliIV+HJO1f1PR41M7NKjfxrFJwwhKI1KwhwOxYw1SXg9ao+CIMt774nFuGiG6eU+udtbEI9oQ== 133 | 134 | "@types/estree@^0.0.51": 135 | version "0.0.51" 136 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" 137 | integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== 138 | 139 | "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": 140 | version "7.0.11" 141 | resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" 142 | integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== 143 | 144 | "@types/node@*": 145 | version "18.0.0" 146 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" 147 | integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== 148 | 149 | "@types/node@^16.11.0": 150 | version "16.11.41" 151 | resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.41.tgz#88eb485b1bfdb4c224d878b7832239536aa2f813" 152 | integrity sha512-mqoYK2TnVjdkGk8qXAVGc/x9nSaTpSrFaGFm43BUH3IdoBV0nta6hYaGmdOvIMlbHJbUEVen3gvwpwovAZKNdQ== 153 | 154 | "@types/ssh2-streams@*": 155 | version "0.1.9" 156 | resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.9.tgz#8ca51b26f08750a780f82ee75ff18d7160c07a87" 157 | integrity sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg== 158 | dependencies: 159 | "@types/node" "*" 160 | 161 | "@types/ssh2-streams@0.1.12": 162 | version "0.1.12" 163 | resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz#e68795ba2bf01c76b93f9c9809e1f42f0eaaec5f" 164 | integrity sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg== 165 | dependencies: 166 | "@types/node" "*" 167 | 168 | "@types/ssh2@^0.5.52": 169 | version "0.5.52" 170 | resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741" 171 | integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg== 172 | dependencies: 173 | "@types/node" "*" 174 | "@types/ssh2-streams" "*" 175 | 176 | "@types/webpack@^5.28.0": 177 | version "5.28.0" 178 | resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-5.28.0.tgz#78dde06212f038d77e54116cfe69e88ae9ed2c03" 179 | integrity sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w== 180 | dependencies: 181 | "@types/node" "*" 182 | tapable "^2.2.0" 183 | webpack "^5" 184 | 185 | "@typescript-eslint/eslint-plugin@^5.19.0": 186 | version "5.29.0" 187 | resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz#c67794d2b0fd0b4a47f50266088acdc52a08aab6" 188 | integrity sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w== 189 | dependencies: 190 | "@typescript-eslint/scope-manager" "5.29.0" 191 | "@typescript-eslint/type-utils" "5.29.0" 192 | "@typescript-eslint/utils" "5.29.0" 193 | debug "^4.3.4" 194 | functional-red-black-tree "^1.0.1" 195 | ignore "^5.2.0" 196 | regexpp "^3.2.0" 197 | semver "^7.3.7" 198 | tsutils "^3.21.0" 199 | 200 | "@typescript-eslint/parser@^5.19.0": 201 | version "5.29.0" 202 | resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.29.0.tgz#41314b195b34d44ff38220caa55f3f93cfca43cf" 203 | integrity sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw== 204 | dependencies: 205 | "@typescript-eslint/scope-manager" "5.29.0" 206 | "@typescript-eslint/types" "5.29.0" 207 | "@typescript-eslint/typescript-estree" "5.29.0" 208 | debug "^4.3.4" 209 | 210 | "@typescript-eslint/scope-manager@5.29.0": 211 | version "5.29.0" 212 | resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz#2a6a32e3416cb133e9af8dcf54bf077a916aeed3" 213 | integrity sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA== 214 | dependencies: 215 | "@typescript-eslint/types" "5.29.0" 216 | "@typescript-eslint/visitor-keys" "5.29.0" 217 | 218 | "@typescript-eslint/type-utils@5.29.0": 219 | version "5.29.0" 220 | resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz#241918001d164044020b37d26d5b9f4e37cc3d5d" 221 | integrity sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg== 222 | dependencies: 223 | "@typescript-eslint/utils" "5.29.0" 224 | debug "^4.3.4" 225 | tsutils "^3.21.0" 226 | 227 | "@typescript-eslint/types@5.29.0": 228 | version "5.29.0" 229 | resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.29.0.tgz#7861d3d288c031703b2d97bc113696b4d8c19aab" 230 | integrity sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg== 231 | 232 | "@typescript-eslint/typescript-estree@5.29.0": 233 | version "5.29.0" 234 | resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz#e83d19aa7fd2e74616aab2f25dfbe4de4f0b5577" 235 | integrity sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ== 236 | dependencies: 237 | "@typescript-eslint/types" "5.29.0" 238 | "@typescript-eslint/visitor-keys" "5.29.0" 239 | debug "^4.3.4" 240 | globby "^11.1.0" 241 | is-glob "^4.0.3" 242 | semver "^7.3.7" 243 | tsutils "^3.21.0" 244 | 245 | "@typescript-eslint/utils@5.29.0": 246 | version "5.29.0" 247 | resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.29.0.tgz#775046effd5019667bd086bcf326acbe32cd0082" 248 | integrity sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A== 249 | dependencies: 250 | "@types/json-schema" "^7.0.9" 251 | "@typescript-eslint/scope-manager" "5.29.0" 252 | "@typescript-eslint/types" "5.29.0" 253 | "@typescript-eslint/typescript-estree" "5.29.0" 254 | eslint-scope "^5.1.1" 255 | eslint-utils "^3.0.0" 256 | 257 | "@typescript-eslint/visitor-keys@5.29.0": 258 | version "5.29.0" 259 | resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz#7a4749fa7ef5160c44a451bf060ac1dc6dfb77ee" 260 | integrity sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ== 261 | dependencies: 262 | "@typescript-eslint/types" "5.29.0" 263 | eslint-visitor-keys "^3.3.0" 264 | 265 | "@webassemblyjs/ast@1.11.1": 266 | version "1.11.1" 267 | resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" 268 | integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== 269 | dependencies: 270 | "@webassemblyjs/helper-numbers" "1.11.1" 271 | "@webassemblyjs/helper-wasm-bytecode" "1.11.1" 272 | 273 | "@webassemblyjs/floating-point-hex-parser@1.11.1": 274 | version "1.11.1" 275 | resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" 276 | integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== 277 | 278 | "@webassemblyjs/helper-api-error@1.11.1": 279 | version "1.11.1" 280 | resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" 281 | integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== 282 | 283 | "@webassemblyjs/helper-buffer@1.11.1": 284 | version "1.11.1" 285 | resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" 286 | integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== 287 | 288 | "@webassemblyjs/helper-numbers@1.11.1": 289 | version "1.11.1" 290 | resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" 291 | integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== 292 | dependencies: 293 | "@webassemblyjs/floating-point-hex-parser" "1.11.1" 294 | "@webassemblyjs/helper-api-error" "1.11.1" 295 | "@xtuc/long" "4.2.2" 296 | 297 | "@webassemblyjs/helper-wasm-bytecode@1.11.1": 298 | version "1.11.1" 299 | resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" 300 | integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== 301 | 302 | "@webassemblyjs/helper-wasm-section@1.11.1": 303 | version "1.11.1" 304 | resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" 305 | integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== 306 | dependencies: 307 | "@webassemblyjs/ast" "1.11.1" 308 | "@webassemblyjs/helper-buffer" "1.11.1" 309 | "@webassemblyjs/helper-wasm-bytecode" "1.11.1" 310 | "@webassemblyjs/wasm-gen" "1.11.1" 311 | 312 | "@webassemblyjs/ieee754@1.11.1": 313 | version "1.11.1" 314 | resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" 315 | integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== 316 | dependencies: 317 | "@xtuc/ieee754" "^1.2.0" 318 | 319 | "@webassemblyjs/leb128@1.11.1": 320 | version "1.11.1" 321 | resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" 322 | integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== 323 | dependencies: 324 | "@xtuc/long" "4.2.2" 325 | 326 | "@webassemblyjs/utf8@1.11.1": 327 | version "1.11.1" 328 | resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" 329 | integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== 330 | 331 | "@webassemblyjs/wasm-edit@1.11.1": 332 | version "1.11.1" 333 | resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" 334 | integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== 335 | dependencies: 336 | "@webassemblyjs/ast" "1.11.1" 337 | "@webassemblyjs/helper-buffer" "1.11.1" 338 | "@webassemblyjs/helper-wasm-bytecode" "1.11.1" 339 | "@webassemblyjs/helper-wasm-section" "1.11.1" 340 | "@webassemblyjs/wasm-gen" "1.11.1" 341 | "@webassemblyjs/wasm-opt" "1.11.1" 342 | "@webassemblyjs/wasm-parser" "1.11.1" 343 | "@webassemblyjs/wast-printer" "1.11.1" 344 | 345 | "@webassemblyjs/wasm-gen@1.11.1": 346 | version "1.11.1" 347 | resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" 348 | integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== 349 | dependencies: 350 | "@webassemblyjs/ast" "1.11.1" 351 | "@webassemblyjs/helper-wasm-bytecode" "1.11.1" 352 | "@webassemblyjs/ieee754" "1.11.1" 353 | "@webassemblyjs/leb128" "1.11.1" 354 | "@webassemblyjs/utf8" "1.11.1" 355 | 356 | "@webassemblyjs/wasm-opt@1.11.1": 357 | version "1.11.1" 358 | resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" 359 | integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== 360 | dependencies: 361 | "@webassemblyjs/ast" "1.11.1" 362 | "@webassemblyjs/helper-buffer" "1.11.1" 363 | "@webassemblyjs/wasm-gen" "1.11.1" 364 | "@webassemblyjs/wasm-parser" "1.11.1" 365 | 366 | "@webassemblyjs/wasm-parser@1.11.1": 367 | version "1.11.1" 368 | resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" 369 | integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== 370 | dependencies: 371 | "@webassemblyjs/ast" "1.11.1" 372 | "@webassemblyjs/helper-api-error" "1.11.1" 373 | "@webassemblyjs/helper-wasm-bytecode" "1.11.1" 374 | "@webassemblyjs/ieee754" "1.11.1" 375 | "@webassemblyjs/leb128" "1.11.1" 376 | "@webassemblyjs/utf8" "1.11.1" 377 | 378 | "@webassemblyjs/wast-printer@1.11.1": 379 | version "1.11.1" 380 | resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" 381 | integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== 382 | dependencies: 383 | "@webassemblyjs/ast" "1.11.1" 384 | "@xtuc/long" "4.2.2" 385 | 386 | "@webpack-cli/configtest@^1.2.0": 387 | version "1.2.0" 388 | resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" 389 | integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== 390 | 391 | "@webpack-cli/info@^1.5.0": 392 | version "1.5.0" 393 | resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" 394 | integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== 395 | dependencies: 396 | envinfo "^7.7.3" 397 | 398 | "@webpack-cli/serve@^1.7.0": 399 | version "1.7.0" 400 | resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" 401 | integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== 402 | 403 | "@xtuc/ieee754@^1.2.0": 404 | version "1.2.0" 405 | resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" 406 | integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== 407 | 408 | "@xtuc/long@4.2.2": 409 | version "4.2.2" 410 | resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" 411 | integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== 412 | 413 | acorn-import-assertions@^1.7.6: 414 | version "1.8.0" 415 | resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" 416 | integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== 417 | 418 | acorn-jsx@^5.3.2: 419 | version "5.3.2" 420 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" 421 | integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== 422 | 423 | acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1: 424 | version "8.7.1" 425 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" 426 | integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== 427 | 428 | ajv-keywords@^3.5.2: 429 | version "3.5.2" 430 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" 431 | integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== 432 | 433 | ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: 434 | version "6.12.6" 435 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" 436 | integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== 437 | dependencies: 438 | fast-deep-equal "^3.1.1" 439 | fast-json-stable-stringify "^2.0.0" 440 | json-schema-traverse "^0.4.1" 441 | uri-js "^4.2.2" 442 | 443 | ansi-regex@^5.0.1: 444 | version "5.0.1" 445 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 446 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 447 | 448 | ansi-styles@^4.1.0: 449 | version "4.3.0" 450 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 451 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 452 | dependencies: 453 | color-convert "^2.0.1" 454 | 455 | argparse@^2.0.1: 456 | version "2.0.1" 457 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 458 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 459 | 460 | array-union@^2.1.0: 461 | version "2.1.0" 462 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" 463 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== 464 | 465 | asn1@^0.2.6: 466 | version "0.2.6" 467 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" 468 | integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== 469 | dependencies: 470 | safer-buffer "~2.1.0" 471 | 472 | balanced-match@^1.0.0: 473 | version "1.0.2" 474 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 475 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 476 | 477 | bcrypt-pbkdf@^1.0.2: 478 | version "1.0.2" 479 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" 480 | integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== 481 | dependencies: 482 | tweetnacl "^0.14.3" 483 | 484 | binary@^0.3.0: 485 | version "0.3.0" 486 | resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" 487 | integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg== 488 | dependencies: 489 | buffers "~0.1.1" 490 | chainsaw "~0.1.0" 491 | 492 | brace-expansion@^1.1.7: 493 | version "1.1.11" 494 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 495 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 496 | dependencies: 497 | balanced-match "^1.0.0" 498 | concat-map "0.0.1" 499 | 500 | brace-expansion@^2.0.1: 501 | version "2.0.1" 502 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 503 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 504 | dependencies: 505 | balanced-match "^1.0.0" 506 | 507 | braces@^3.0.2: 508 | version "3.0.2" 509 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 510 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 511 | dependencies: 512 | fill-range "^7.0.1" 513 | 514 | browserslist@^4.14.5: 515 | version "4.21.0" 516 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.0.tgz#7ab19572361a140ecd1e023e2c1ed95edda0cefe" 517 | integrity sha512-UQxE0DIhRB5z/zDz9iA03BOfxaN2+GQdBYH/2WrSIWEUrnpzTPJbhqt+umq6r3acaPRTW1FNTkrcp0PXgtFkvA== 518 | dependencies: 519 | caniuse-lite "^1.0.30001358" 520 | electron-to-chromium "^1.4.164" 521 | node-releases "^2.0.5" 522 | update-browserslist-db "^1.0.0" 523 | 524 | buffer-from@^1.0.0: 525 | version "1.1.2" 526 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" 527 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== 528 | 529 | buffers@~0.1.1: 530 | version "0.1.1" 531 | resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" 532 | integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ== 533 | 534 | buildcheck@~0.0.6: 535 | version "0.0.6" 536 | resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" 537 | integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== 538 | 539 | callsites@^3.0.0: 540 | version "3.1.0" 541 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 542 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 543 | 544 | caniuse-lite@^1.0.30001358: 545 | version "1.0.30001359" 546 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001359.tgz#a1c1cbe1c2da9e689638813618b4219acbd4925e" 547 | integrity sha512-Xln/BAsPzEuiVLgJ2/45IaqD9jShtk3Y33anKb4+yLwQzws3+v6odKfpgES/cDEaZMLzSChpIGdbOYtH9MyuHw== 548 | 549 | chainsaw@~0.1.0: 550 | version "0.1.0" 551 | resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" 552 | integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ== 553 | dependencies: 554 | traverse ">=0.3.0 <0.4" 555 | 556 | chalk@^4.0.0, chalk@^4.1.0: 557 | version "4.1.2" 558 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 559 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 560 | dependencies: 561 | ansi-styles "^4.1.0" 562 | supports-color "^7.1.0" 563 | 564 | chrome-trace-event@^1.0.2: 565 | version "1.0.3" 566 | resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" 567 | integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== 568 | 569 | clone-deep@^4.0.1: 570 | version "4.0.1" 571 | resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" 572 | integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== 573 | dependencies: 574 | is-plain-object "^2.0.4" 575 | kind-of "^6.0.2" 576 | shallow-clone "^3.0.0" 577 | 578 | color-convert@^2.0.1: 579 | version "2.0.1" 580 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 581 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 582 | dependencies: 583 | color-name "~1.1.4" 584 | 585 | color-name@~1.1.4: 586 | version "1.1.4" 587 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 588 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 589 | 590 | colorette@^2.0.14: 591 | version "2.0.19" 592 | resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" 593 | integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== 594 | 595 | commander@^2.20.0: 596 | version "2.20.3" 597 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" 598 | integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 599 | 600 | commander@^7.0.0: 601 | version "7.2.0" 602 | resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" 603 | integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== 604 | 605 | comment-parser@^0.7.2: 606 | version "0.7.6" 607 | resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.6.tgz#0e743a53c8e646c899a1323db31f6cd337b10f12" 608 | integrity sha512-GKNxVA7/iuTnAqGADlTWX4tkhzxZKXp5fLJqKTlQLHkE65XDUKutZ3BHaJC5IGcper2tT3QRD1xr4o3jNpgXXg== 609 | 610 | concat-map@0.0.1: 611 | version "0.0.1" 612 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 613 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 614 | 615 | core-js-pure@^3.20.2: 616 | version "3.23.3" 617 | resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.3.tgz#bcd02d3d8ec68ad871ef50d5ccbb248ddb54f401" 618 | integrity sha512-XpoouuqIj4P+GWtdyV8ZO3/u4KftkeDVMfvp+308eGMhCrA3lVDSmAxO0c6GGOcmgVlaKDrgWVMo49h2ab/TDA== 619 | 620 | cpu-features@~0.0.9: 621 | version "0.0.9" 622 | resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.9.tgz#5226b92f0f1c63122b0a3eb84cb8335a4de499fc" 623 | integrity sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ== 624 | dependencies: 625 | buildcheck "~0.0.6" 626 | nan "^2.17.0" 627 | 628 | cross-spawn@^7.0.2, cross-spawn@^7.0.3: 629 | version "7.0.3" 630 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 631 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 632 | dependencies: 633 | path-key "^3.1.0" 634 | shebang-command "^2.0.0" 635 | which "^2.0.1" 636 | 637 | debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: 638 | version "4.3.4" 639 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 640 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 641 | dependencies: 642 | ms "2.1.2" 643 | 644 | deep-is@^0.1.3: 645 | version "0.1.4" 646 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" 647 | integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== 648 | 649 | dir-glob@^3.0.1: 650 | version "3.0.1" 651 | resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" 652 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 653 | dependencies: 654 | path-type "^4.0.0" 655 | 656 | doctrine@^3.0.0: 657 | version "3.0.0" 658 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" 659 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 660 | dependencies: 661 | esutils "^2.0.2" 662 | 663 | electron-to-chromium@^1.4.164: 664 | version "1.4.170" 665 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.170.tgz#0415fc489402e09bfbe1f0c99bbf4d73f31d48d4" 666 | integrity sha512-rZ8PZLhK4ORPjFqLp9aqC4/S1j4qWFsPPz13xmWdrbBkU/LlxMcok+f+6f8YnQ57MiZwKtOaW15biZZsY5Igvw== 667 | 668 | enhanced-resolve@^5.0.0, enhanced-resolve@^5.9.3: 669 | version "5.9.3" 670 | resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88" 671 | integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow== 672 | dependencies: 673 | graceful-fs "^4.2.4" 674 | tapable "^2.2.0" 675 | 676 | envinfo@^7.7.3: 677 | version "7.8.1" 678 | resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" 679 | integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== 680 | 681 | es-module-lexer@^0.9.0: 682 | version "0.9.3" 683 | resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" 684 | integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== 685 | 686 | escalade@^3.1.1: 687 | version "3.1.1" 688 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 689 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 690 | 691 | escape-string-regexp@^4.0.0: 692 | version "4.0.0" 693 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" 694 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 695 | 696 | eslint-plugin-jsdoc@^19.1.0: 697 | version "19.2.0" 698 | resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-19.2.0.tgz#f522b970878ae402b28ce62187305b33dfe2c834" 699 | integrity sha512-QdNifBFLXCDGdy+26RXxcrqzEZarFWNybCZQVqJQYEYPlxd6lm+LPkrs6mCOhaGc2wqC6zqpedBQFX8nQJuKSw== 700 | dependencies: 701 | comment-parser "^0.7.2" 702 | debug "^4.1.1" 703 | jsdoctypeparser "^6.1.0" 704 | lodash "^4.17.15" 705 | object.entries-ponyfill "^1.0.1" 706 | regextras "^0.7.0" 707 | semver "^6.3.0" 708 | spdx-expression-parse "^3.0.0" 709 | 710 | eslint-scope@5.1.1, eslint-scope@^5.1.1: 711 | version "5.1.1" 712 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" 713 | integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== 714 | dependencies: 715 | esrecurse "^4.3.0" 716 | estraverse "^4.1.1" 717 | 718 | eslint-scope@^7.1.1: 719 | version "7.1.1" 720 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" 721 | integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== 722 | dependencies: 723 | esrecurse "^4.3.0" 724 | estraverse "^5.2.0" 725 | 726 | eslint-utils@^3.0.0: 727 | version "3.0.0" 728 | resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" 729 | integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== 730 | dependencies: 731 | eslint-visitor-keys "^2.0.0" 732 | 733 | eslint-visitor-keys@^2.0.0: 734 | version "2.1.0" 735 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" 736 | integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== 737 | 738 | eslint-visitor-keys@^3.3.0: 739 | version "3.3.0" 740 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" 741 | integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== 742 | 743 | eslint@^8.13.0: 744 | version "8.18.0" 745 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.18.0.tgz#78d565d16c993d0b73968c523c0446b13da784fd" 746 | integrity sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA== 747 | dependencies: 748 | "@eslint/eslintrc" "^1.3.0" 749 | "@humanwhocodes/config-array" "^0.9.2" 750 | ajv "^6.10.0" 751 | chalk "^4.0.0" 752 | cross-spawn "^7.0.2" 753 | debug "^4.3.2" 754 | doctrine "^3.0.0" 755 | escape-string-regexp "^4.0.0" 756 | eslint-scope "^7.1.1" 757 | eslint-utils "^3.0.0" 758 | eslint-visitor-keys "^3.3.0" 759 | espree "^9.3.2" 760 | esquery "^1.4.0" 761 | esutils "^2.0.2" 762 | fast-deep-equal "^3.1.3" 763 | file-entry-cache "^6.0.1" 764 | functional-red-black-tree "^1.0.1" 765 | glob-parent "^6.0.1" 766 | globals "^13.15.0" 767 | ignore "^5.2.0" 768 | import-fresh "^3.0.0" 769 | imurmurhash "^0.1.4" 770 | is-glob "^4.0.0" 771 | js-yaml "^4.1.0" 772 | json-stable-stringify-without-jsonify "^1.0.1" 773 | levn "^0.4.1" 774 | lodash.merge "^4.6.2" 775 | minimatch "^3.1.2" 776 | natural-compare "^1.4.0" 777 | optionator "^0.9.1" 778 | regexpp "^3.2.0" 779 | strip-ansi "^6.0.1" 780 | strip-json-comments "^3.1.0" 781 | text-table "^0.2.0" 782 | v8-compile-cache "^2.0.3" 783 | 784 | espree@^9.3.2: 785 | version "9.3.2" 786 | resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" 787 | integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== 788 | dependencies: 789 | acorn "^8.7.1" 790 | acorn-jsx "^5.3.2" 791 | eslint-visitor-keys "^3.3.0" 792 | 793 | esquery@^1.4.0: 794 | version "1.4.0" 795 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" 796 | integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== 797 | dependencies: 798 | estraverse "^5.1.0" 799 | 800 | esrecurse@^4.3.0: 801 | version "4.3.0" 802 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" 803 | integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== 804 | dependencies: 805 | estraverse "^5.2.0" 806 | 807 | estraverse@^4.1.1: 808 | version "4.3.0" 809 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" 810 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 811 | 812 | estraverse@^5.1.0, estraverse@^5.2.0: 813 | version "5.3.0" 814 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" 815 | integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== 816 | 817 | esutils@^2.0.2: 818 | version "2.0.3" 819 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 820 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 821 | 822 | events@^3.2.0: 823 | version "3.3.0" 824 | resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" 825 | integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== 826 | 827 | fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: 828 | version "3.1.3" 829 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" 830 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 831 | 832 | fast-glob@^3.2.9: 833 | version "3.2.11" 834 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" 835 | integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== 836 | dependencies: 837 | "@nodelib/fs.stat" "^2.0.2" 838 | "@nodelib/fs.walk" "^1.2.3" 839 | glob-parent "^5.1.2" 840 | merge2 "^1.3.0" 841 | micromatch "^4.0.4" 842 | 843 | fast-json-stable-stringify@^2.0.0: 844 | version "2.1.0" 845 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 846 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 847 | 848 | fast-levenshtein@^2.0.6: 849 | version "2.0.6" 850 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 851 | integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== 852 | 853 | fastest-levenshtein@^1.0.12: 854 | version "1.0.12" 855 | resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" 856 | integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== 857 | 858 | fastq@^1.6.0: 859 | version "1.13.0" 860 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" 861 | integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== 862 | dependencies: 863 | reusify "^1.0.4" 864 | 865 | file-entry-cache@^6.0.1: 866 | version "6.0.1" 867 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" 868 | integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== 869 | dependencies: 870 | flat-cache "^3.0.4" 871 | 872 | fill-range@^7.0.1: 873 | version "7.0.1" 874 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 875 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 876 | dependencies: 877 | to-regex-range "^5.0.1" 878 | 879 | find-up@^4.0.0: 880 | version "4.1.0" 881 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" 882 | integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== 883 | dependencies: 884 | locate-path "^5.0.0" 885 | path-exists "^4.0.0" 886 | 887 | flat-cache@^3.0.4: 888 | version "3.0.4" 889 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" 890 | integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== 891 | dependencies: 892 | flatted "^3.1.0" 893 | rimraf "^3.0.2" 894 | 895 | flatted@^3.1.0: 896 | version "3.2.5" 897 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" 898 | integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== 899 | 900 | fs.realpath@^1.0.0: 901 | version "1.0.0" 902 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 903 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 904 | 905 | function-bind@^1.1.1: 906 | version "1.1.1" 907 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 908 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 909 | 910 | functional-red-black-tree@^1.0.1: 911 | version "1.0.1" 912 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 913 | integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== 914 | 915 | glob-parent@^5.1.2: 916 | version "5.1.2" 917 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 918 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 919 | dependencies: 920 | is-glob "^4.0.1" 921 | 922 | glob-parent@^6.0.1: 923 | version "6.0.2" 924 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" 925 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 926 | dependencies: 927 | is-glob "^4.0.3" 928 | 929 | glob-to-regexp@^0.4.1: 930 | version "0.4.1" 931 | resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" 932 | integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== 933 | 934 | glob@^7.1.3: 935 | version "7.2.3" 936 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" 937 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 938 | dependencies: 939 | fs.realpath "^1.0.0" 940 | inflight "^1.0.4" 941 | inherits "2" 942 | minimatch "^3.1.1" 943 | once "^1.3.0" 944 | path-is-absolute "^1.0.0" 945 | 946 | glob@^9.3.1: 947 | version "9.3.1" 948 | resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.1.tgz#4a15cf72479a440f77e97805b21e1b5b6e4fb18a" 949 | integrity sha512-qERvJb7IGsnkx6YYmaaGvDpf77c951hICMdWaFXyH3PlVob8sbPJJyJX0kWkiCWyXUzoy9UOTNjGg0RbD8bYIw== 950 | dependencies: 951 | fs.realpath "^1.0.0" 952 | minimatch "^7.4.1" 953 | minipass "^4.2.4" 954 | path-scurry "^1.6.1" 955 | 956 | globals@^13.15.0: 957 | version "13.15.0" 958 | resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" 959 | integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== 960 | dependencies: 961 | type-fest "^0.20.2" 962 | 963 | globby@^11.1.0: 964 | version "11.1.0" 965 | resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" 966 | integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== 967 | dependencies: 968 | array-union "^2.1.0" 969 | dir-glob "^3.0.1" 970 | fast-glob "^3.2.9" 971 | ignore "^5.2.0" 972 | merge2 "^1.4.1" 973 | slash "^3.0.0" 974 | 975 | graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: 976 | version "4.2.10" 977 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" 978 | integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== 979 | 980 | has-flag@^4.0.0: 981 | version "4.0.0" 982 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 983 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 984 | 985 | has@^1.0.3: 986 | version "1.0.3" 987 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 988 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 989 | dependencies: 990 | function-bind "^1.1.1" 991 | 992 | ignore@^5.2.0: 993 | version "5.2.0" 994 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" 995 | integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== 996 | 997 | import-fresh@^3.0.0, import-fresh@^3.2.1: 998 | version "3.3.0" 999 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" 1000 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 1001 | dependencies: 1002 | parent-module "^1.0.0" 1003 | resolve-from "^4.0.0" 1004 | 1005 | import-local@^3.0.2: 1006 | version "3.1.0" 1007 | resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" 1008 | integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== 1009 | dependencies: 1010 | pkg-dir "^4.2.0" 1011 | resolve-cwd "^3.0.0" 1012 | 1013 | imurmurhash@^0.1.4: 1014 | version "0.1.4" 1015 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 1016 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 1017 | 1018 | inflight@^1.0.4: 1019 | version "1.0.6" 1020 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 1021 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 1022 | dependencies: 1023 | once "^1.3.0" 1024 | wrappy "1" 1025 | 1026 | inherits@2: 1027 | version "2.0.4" 1028 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 1029 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 1030 | 1031 | interpret@^2.2.0: 1032 | version "2.2.0" 1033 | resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" 1034 | integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== 1035 | 1036 | ip@^1.1.5: 1037 | version "1.1.8" 1038 | resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" 1039 | integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== 1040 | 1041 | is-core-module@^2.9.0: 1042 | version "2.9.0" 1043 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" 1044 | integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== 1045 | dependencies: 1046 | has "^1.0.3" 1047 | 1048 | is-extglob@^2.1.1: 1049 | version "2.1.1" 1050 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 1051 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 1052 | 1053 | is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: 1054 | version "4.0.3" 1055 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 1056 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 1057 | dependencies: 1058 | is-extglob "^2.1.1" 1059 | 1060 | is-number@^7.0.0: 1061 | version "7.0.0" 1062 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 1063 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1064 | 1065 | is-plain-object@^2.0.4: 1066 | version "2.0.4" 1067 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 1068 | integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== 1069 | dependencies: 1070 | isobject "^3.0.1" 1071 | 1072 | isexe@^2.0.0: 1073 | version "2.0.0" 1074 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 1075 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 1076 | 1077 | isobject@^3.0.1: 1078 | version "3.0.1" 1079 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" 1080 | integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== 1081 | 1082 | jest-worker@^27.4.5: 1083 | version "27.5.1" 1084 | resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" 1085 | integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== 1086 | dependencies: 1087 | "@types/node" "*" 1088 | merge-stream "^2.0.0" 1089 | supports-color "^8.0.0" 1090 | 1091 | js-yaml@^4.1.0: 1092 | version "4.1.0" 1093 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" 1094 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 1095 | dependencies: 1096 | argparse "^2.0.1" 1097 | 1098 | jsdoctypeparser@^6.1.0: 1099 | version "6.1.0" 1100 | resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz#acfb936c26300d98f1405cb03e20b06748e512a8" 1101 | integrity sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA== 1102 | 1103 | json-parse-even-better-errors@^2.3.1: 1104 | version "2.3.1" 1105 | resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" 1106 | integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== 1107 | 1108 | json-schema-traverse@^0.4.1: 1109 | version "0.4.1" 1110 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 1111 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 1112 | 1113 | json-stable-stringify-without-jsonify@^1.0.1: 1114 | version "1.0.1" 1115 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 1116 | integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== 1117 | 1118 | kind-of@^6.0.2: 1119 | version "6.0.3" 1120 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" 1121 | integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== 1122 | 1123 | levn@^0.4.1: 1124 | version "0.4.1" 1125 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" 1126 | integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== 1127 | dependencies: 1128 | prelude-ls "^1.2.1" 1129 | type-check "~0.4.0" 1130 | 1131 | loader-runner@^4.2.0: 1132 | version "4.3.0" 1133 | resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" 1134 | integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== 1135 | 1136 | locate-path@^5.0.0: 1137 | version "5.0.0" 1138 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" 1139 | integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== 1140 | dependencies: 1141 | p-locate "^4.1.0" 1142 | 1143 | lodash.merge@^4.6.2: 1144 | version "4.6.2" 1145 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 1146 | integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 1147 | 1148 | lodash@^4.17.15: 1149 | version "4.17.21" 1150 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 1151 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 1152 | 1153 | lru-cache@^6.0.0: 1154 | version "6.0.0" 1155 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 1156 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 1157 | dependencies: 1158 | yallist "^4.0.0" 1159 | 1160 | lru-cache@^7.14.1: 1161 | version "7.18.3" 1162 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" 1163 | integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== 1164 | 1165 | merge-stream@^2.0.0: 1166 | version "2.0.0" 1167 | resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" 1168 | integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== 1169 | 1170 | merge2@^1.3.0, merge2@^1.4.1: 1171 | version "1.4.1" 1172 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 1173 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 1174 | 1175 | micromatch@^4.0.0, micromatch@^4.0.4: 1176 | version "4.0.5" 1177 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" 1178 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 1179 | dependencies: 1180 | braces "^3.0.2" 1181 | picomatch "^2.3.1" 1182 | 1183 | mime-db@1.52.0: 1184 | version "1.52.0" 1185 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 1186 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 1187 | 1188 | mime-types@^2.1.27: 1189 | version "2.1.35" 1190 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 1191 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 1192 | dependencies: 1193 | mime-db "1.52.0" 1194 | 1195 | minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: 1196 | version "3.1.2" 1197 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 1198 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 1199 | dependencies: 1200 | brace-expansion "^1.1.7" 1201 | 1202 | minimatch@^7.4.1: 1203 | version "7.4.2" 1204 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" 1205 | integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== 1206 | dependencies: 1207 | brace-expansion "^2.0.1" 1208 | 1209 | minipass@^4.0.2, minipass@^4.2.4: 1210 | version "4.2.5" 1211 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" 1212 | integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== 1213 | 1214 | ms@2.1.2: 1215 | version "2.1.2" 1216 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 1217 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1218 | 1219 | nan@^2.17.0: 1220 | version "2.18.0" 1221 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" 1222 | integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== 1223 | 1224 | natural-compare@^1.4.0: 1225 | version "1.4.0" 1226 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 1227 | integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== 1228 | 1229 | neo-async@^2.6.2: 1230 | version "2.6.2" 1231 | resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" 1232 | integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== 1233 | 1234 | node-releases@^2.0.5: 1235 | version "2.0.5" 1236 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" 1237 | integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== 1238 | 1239 | object.entries-ponyfill@^1.0.1: 1240 | version "1.0.1" 1241 | resolved "https://registry.yarnpkg.com/object.entries-ponyfill/-/object.entries-ponyfill-1.0.1.tgz#29abdf77cbfbd26566dd1aa24e9d88f65433d256" 1242 | integrity sha512-j0ixssXc5GirDWhB2cLVPsOs9jx61G/iRndyMdToTsjMYY8BQmG1Ke6mwqXmpDiP8icye1YCR94NswNaa/yyzA== 1243 | 1244 | once@^1.3.0: 1245 | version "1.4.0" 1246 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1247 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 1248 | dependencies: 1249 | wrappy "1" 1250 | 1251 | optionator@^0.9.1: 1252 | version "0.9.1" 1253 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" 1254 | integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== 1255 | dependencies: 1256 | deep-is "^0.1.3" 1257 | fast-levenshtein "^2.0.6" 1258 | levn "^0.4.1" 1259 | prelude-ls "^1.2.1" 1260 | type-check "^0.4.0" 1261 | word-wrap "^1.2.3" 1262 | 1263 | p-limit@^2.2.0: 1264 | version "2.3.0" 1265 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" 1266 | integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== 1267 | dependencies: 1268 | p-try "^2.0.0" 1269 | 1270 | p-locate@^4.1.0: 1271 | version "4.1.0" 1272 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" 1273 | integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== 1274 | dependencies: 1275 | p-limit "^2.2.0" 1276 | 1277 | p-try@^2.0.0: 1278 | version "2.2.0" 1279 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 1280 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 1281 | 1282 | parent-module@^1.0.0: 1283 | version "1.0.1" 1284 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" 1285 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1286 | dependencies: 1287 | callsites "^3.0.0" 1288 | 1289 | path-exists@^4.0.0: 1290 | version "4.0.0" 1291 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 1292 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 1293 | 1294 | path-is-absolute@^1.0.0: 1295 | version "1.0.1" 1296 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1297 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 1298 | 1299 | path-key@^3.1.0: 1300 | version "3.1.1" 1301 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 1302 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 1303 | 1304 | path-parse@^1.0.7: 1305 | version "1.0.7" 1306 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 1307 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 1308 | 1309 | path-scurry@^1.6.1: 1310 | version "1.6.1" 1311 | resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" 1312 | integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== 1313 | dependencies: 1314 | lru-cache "^7.14.1" 1315 | minipass "^4.0.2" 1316 | 1317 | path-type@^4.0.0: 1318 | version "4.0.0" 1319 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" 1320 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 1321 | 1322 | picocolors@^1.0.0: 1323 | version "1.0.0" 1324 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 1325 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 1326 | 1327 | picomatch@^2.3.1: 1328 | version "2.3.1" 1329 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 1330 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1331 | 1332 | pkg-dir@^4.2.0: 1333 | version "4.2.0" 1334 | resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" 1335 | integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== 1336 | dependencies: 1337 | find-up "^4.0.0" 1338 | 1339 | prelude-ls@^1.2.1: 1340 | version "1.2.1" 1341 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" 1342 | integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== 1343 | 1344 | punycode@^2.1.0: 1345 | version "2.1.1" 1346 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 1347 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 1348 | 1349 | queue-microtask@^1.2.2: 1350 | version "1.2.3" 1351 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" 1352 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 1353 | 1354 | randombytes@^2.1.0: 1355 | version "2.1.0" 1356 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" 1357 | integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== 1358 | dependencies: 1359 | safe-buffer "^5.1.0" 1360 | 1361 | rechoir@^0.7.0: 1362 | version "0.7.1" 1363 | resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" 1364 | integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== 1365 | dependencies: 1366 | resolve "^1.9.0" 1367 | 1368 | regenerator-runtime@^0.13.4: 1369 | version "0.13.9" 1370 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" 1371 | integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== 1372 | 1373 | regexpp@^3.2.0: 1374 | version "3.2.0" 1375 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" 1376 | integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== 1377 | 1378 | regextras@^0.7.0: 1379 | version "0.7.1" 1380 | resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.1.tgz#be95719d5f43f9ef0b9fa07ad89b7c606995a3b2" 1381 | integrity sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w== 1382 | 1383 | resolve-cwd@^3.0.0: 1384 | version "3.0.0" 1385 | resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" 1386 | integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== 1387 | dependencies: 1388 | resolve-from "^5.0.0" 1389 | 1390 | resolve-from@^4.0.0: 1391 | version "4.0.0" 1392 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" 1393 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1394 | 1395 | resolve-from@^5.0.0: 1396 | version "5.0.0" 1397 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" 1398 | integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== 1399 | 1400 | resolve@^1.9.0: 1401 | version "1.22.1" 1402 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 1403 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 1404 | dependencies: 1405 | is-core-module "^2.9.0" 1406 | path-parse "^1.0.7" 1407 | supports-preserve-symlinks-flag "^1.0.0" 1408 | 1409 | reusify@^1.0.4: 1410 | version "1.0.4" 1411 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" 1412 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1413 | 1414 | rimraf@^3.0.2: 1415 | version "3.0.2" 1416 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" 1417 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 1418 | dependencies: 1419 | glob "^7.1.3" 1420 | 1421 | run-parallel@^1.1.9: 1422 | version "1.2.0" 1423 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" 1424 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1425 | dependencies: 1426 | queue-microtask "^1.2.2" 1427 | 1428 | safe-buffer@^5.1.0: 1429 | version "5.2.1" 1430 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 1431 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1432 | 1433 | safer-buffer@~2.1.0: 1434 | version "2.1.2" 1435 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1436 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1437 | 1438 | schema-utils@^3.1.0, schema-utils@^3.1.1: 1439 | version "3.1.1" 1440 | resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" 1441 | integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== 1442 | dependencies: 1443 | "@types/json-schema" "^7.0.8" 1444 | ajv "^6.12.5" 1445 | ajv-keywords "^3.5.2" 1446 | 1447 | semver@^6.3.0: 1448 | version "6.3.0" 1449 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 1450 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== 1451 | 1452 | semver@^7.3.4, semver@^7.3.7: 1453 | version "7.3.7" 1454 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" 1455 | integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== 1456 | dependencies: 1457 | lru-cache "^6.0.0" 1458 | 1459 | serialize-javascript@^6.0.0: 1460 | version "6.0.0" 1461 | resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" 1462 | integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== 1463 | dependencies: 1464 | randombytes "^2.1.0" 1465 | 1466 | shallow-clone@^3.0.0: 1467 | version "3.0.1" 1468 | resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" 1469 | integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== 1470 | dependencies: 1471 | kind-of "^6.0.2" 1472 | 1473 | shebang-command@^2.0.0: 1474 | version "2.0.0" 1475 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 1476 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 1477 | dependencies: 1478 | shebang-regex "^3.0.0" 1479 | 1480 | shebang-regex@^3.0.0: 1481 | version "3.0.0" 1482 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 1483 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 1484 | 1485 | "simple-socks@git+https://github.com/jeanp413/simple-socks#main": 1486 | version "2.2.2" 1487 | resolved "git+https://github.com/jeanp413/simple-socks#2ac739301a82d6baff04804ed494436a026acb60" 1488 | dependencies: 1489 | "@babel/runtime-corejs3" "^7.16.8" 1490 | binary "^0.3.0" 1491 | 1492 | slash@^3.0.0: 1493 | version "3.0.0" 1494 | resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" 1495 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 1496 | 1497 | smart-buffer@^4.2.0: 1498 | version "4.2.0" 1499 | resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" 1500 | integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== 1501 | 1502 | socks@^2.5.0: 1503 | version "2.6.2" 1504 | resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" 1505 | integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== 1506 | dependencies: 1507 | ip "^1.1.5" 1508 | smart-buffer "^4.2.0" 1509 | 1510 | source-map-support@~0.5.20: 1511 | version "0.5.21" 1512 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" 1513 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== 1514 | dependencies: 1515 | buffer-from "^1.0.0" 1516 | source-map "^0.6.0" 1517 | 1518 | source-map@^0.6.0: 1519 | version "0.6.1" 1520 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 1521 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 1522 | 1523 | spdx-exceptions@^2.1.0: 1524 | version "2.3.0" 1525 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" 1526 | integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== 1527 | 1528 | spdx-expression-parse@^3.0.0: 1529 | version "3.0.1" 1530 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" 1531 | integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== 1532 | dependencies: 1533 | spdx-exceptions "^2.1.0" 1534 | spdx-license-ids "^3.0.0" 1535 | 1536 | spdx-license-ids@^3.0.0: 1537 | version "3.0.11" 1538 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" 1539 | integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== 1540 | 1541 | "ssh2@git+https://github.com/jeanp413/ssh2#master": 1542 | version "1.14.0" 1543 | resolved "git+https://github.com/jeanp413/ssh2#a169f627213aa663e0aa2fd2f0ef5c8931890c26" 1544 | dependencies: 1545 | asn1 "^0.2.6" 1546 | bcrypt-pbkdf "^1.0.2" 1547 | optionalDependencies: 1548 | cpu-features "~0.0.9" 1549 | nan "^2.17.0" 1550 | 1551 | strip-ansi@^6.0.1: 1552 | version "6.0.1" 1553 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1554 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1555 | dependencies: 1556 | ansi-regex "^5.0.1" 1557 | 1558 | strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: 1559 | version "3.1.1" 1560 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" 1561 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 1562 | 1563 | supports-color@^7.1.0: 1564 | version "7.2.0" 1565 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 1566 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1567 | dependencies: 1568 | has-flag "^4.0.0" 1569 | 1570 | supports-color@^8.0.0: 1571 | version "8.1.1" 1572 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" 1573 | integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== 1574 | dependencies: 1575 | has-flag "^4.0.0" 1576 | 1577 | supports-preserve-symlinks-flag@^1.0.0: 1578 | version "1.0.0" 1579 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 1580 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1581 | 1582 | tapable@^2.1.1, tapable@^2.2.0: 1583 | version "2.2.1" 1584 | resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" 1585 | integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== 1586 | 1587 | terser-webpack-plugin@^5.1.3: 1588 | version "5.3.3" 1589 | resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90" 1590 | integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ== 1591 | dependencies: 1592 | "@jridgewell/trace-mapping" "^0.3.7" 1593 | jest-worker "^27.4.5" 1594 | schema-utils "^3.1.1" 1595 | serialize-javascript "^6.0.0" 1596 | terser "^5.7.2" 1597 | 1598 | terser@^5.7.2: 1599 | version "5.14.1" 1600 | resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca" 1601 | integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ== 1602 | dependencies: 1603 | "@jridgewell/source-map" "^0.3.2" 1604 | acorn "^8.5.0" 1605 | commander "^2.20.0" 1606 | source-map-support "~0.5.20" 1607 | 1608 | text-table@^0.2.0: 1609 | version "0.2.0" 1610 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1611 | integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== 1612 | 1613 | to-regex-range@^5.0.1: 1614 | version "5.0.1" 1615 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1616 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1617 | dependencies: 1618 | is-number "^7.0.0" 1619 | 1620 | "traverse@>=0.3.0 <0.4": 1621 | version "0.3.9" 1622 | resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" 1623 | integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== 1624 | 1625 | ts-loader@^9.2.7: 1626 | version "9.3.1" 1627 | resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.3.1.tgz#fe25cca56e3e71c1087fe48dc67f4df8c59b22d4" 1628 | integrity sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw== 1629 | dependencies: 1630 | chalk "^4.1.0" 1631 | enhanced-resolve "^5.0.0" 1632 | micromatch "^4.0.0" 1633 | semver "^7.3.4" 1634 | 1635 | tslib@^1.8.1: 1636 | version "1.14.1" 1637 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" 1638 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 1639 | 1640 | tsutils@^3.21.0: 1641 | version "3.21.0" 1642 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" 1643 | integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== 1644 | dependencies: 1645 | tslib "^1.8.1" 1646 | 1647 | tweetnacl@^0.14.3: 1648 | version "0.14.5" 1649 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" 1650 | integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== 1651 | 1652 | type-check@^0.4.0, type-check@~0.4.0: 1653 | version "0.4.0" 1654 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" 1655 | integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== 1656 | dependencies: 1657 | prelude-ls "^1.2.1" 1658 | 1659 | type-fest@^0.20.2: 1660 | version "0.20.2" 1661 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" 1662 | integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== 1663 | 1664 | typescript@^4.6.3: 1665 | version "4.7.4" 1666 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" 1667 | integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== 1668 | 1669 | update-browserslist-db@^1.0.0: 1670 | version "1.0.4" 1671 | resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" 1672 | integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== 1673 | dependencies: 1674 | escalade "^3.1.1" 1675 | picocolors "^1.0.0" 1676 | 1677 | uri-js@^4.2.2: 1678 | version "4.4.1" 1679 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" 1680 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 1681 | dependencies: 1682 | punycode "^2.1.0" 1683 | 1684 | v8-compile-cache@^2.0.3: 1685 | version "2.3.0" 1686 | resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" 1687 | integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== 1688 | 1689 | watchpack@^2.3.1: 1690 | version "2.4.0" 1691 | resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" 1692 | integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== 1693 | dependencies: 1694 | glob-to-regexp "^0.4.1" 1695 | graceful-fs "^4.1.2" 1696 | 1697 | webpack-cli@^4.7.2: 1698 | version "4.10.0" 1699 | resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" 1700 | integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== 1701 | dependencies: 1702 | "@discoveryjs/json-ext" "^0.5.0" 1703 | "@webpack-cli/configtest" "^1.2.0" 1704 | "@webpack-cli/info" "^1.5.0" 1705 | "@webpack-cli/serve" "^1.7.0" 1706 | colorette "^2.0.14" 1707 | commander "^7.0.0" 1708 | cross-spawn "^7.0.3" 1709 | fastest-levenshtein "^1.0.12" 1710 | import-local "^3.0.2" 1711 | interpret "^2.2.0" 1712 | rechoir "^0.7.0" 1713 | webpack-merge "^5.7.3" 1714 | 1715 | webpack-merge@^5.7.3: 1716 | version "5.8.0" 1717 | resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" 1718 | integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== 1719 | dependencies: 1720 | clone-deep "^4.0.1" 1721 | wildcard "^2.0.0" 1722 | 1723 | webpack-sources@^3.2.3: 1724 | version "3.2.3" 1725 | resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" 1726 | integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== 1727 | 1728 | webpack@^5, webpack@^5.42.0: 1729 | version "5.73.0" 1730 | resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" 1731 | integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== 1732 | dependencies: 1733 | "@types/eslint-scope" "^3.7.3" 1734 | "@types/estree" "^0.0.51" 1735 | "@webassemblyjs/ast" "1.11.1" 1736 | "@webassemblyjs/wasm-edit" "1.11.1" 1737 | "@webassemblyjs/wasm-parser" "1.11.1" 1738 | acorn "^8.4.1" 1739 | acorn-import-assertions "^1.7.6" 1740 | browserslist "^4.14.5" 1741 | chrome-trace-event "^1.0.2" 1742 | enhanced-resolve "^5.9.3" 1743 | es-module-lexer "^0.9.0" 1744 | eslint-scope "5.1.1" 1745 | events "^3.2.0" 1746 | glob-to-regexp "^0.4.1" 1747 | graceful-fs "^4.2.9" 1748 | json-parse-even-better-errors "^2.3.1" 1749 | loader-runner "^4.2.0" 1750 | mime-types "^2.1.27" 1751 | neo-async "^2.6.2" 1752 | schema-utils "^3.1.0" 1753 | tapable "^2.1.1" 1754 | terser-webpack-plugin "^5.1.3" 1755 | watchpack "^2.3.1" 1756 | webpack-sources "^3.2.3" 1757 | 1758 | which@^2.0.1: 1759 | version "2.0.2" 1760 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 1761 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1762 | dependencies: 1763 | isexe "^2.0.0" 1764 | 1765 | wildcard@^2.0.0: 1766 | version "2.0.0" 1767 | resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" 1768 | integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== 1769 | 1770 | word-wrap@^1.2.3: 1771 | version "1.2.3" 1772 | resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" 1773 | integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== 1774 | 1775 | wrappy@1: 1776 | version "1.0.2" 1777 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1778 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1779 | 1780 | yallist@^4.0.0: 1781 | version "4.0.0" 1782 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1783 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1784 | --------------------------------------------------------------------------------