From 572311e0acd097c46a53f3b315e81da923ae541b Mon Sep 17 00:00:00 2001 From: Teffen Ellis Date: Mon, 26 Jul 2021 19:53:03 -0400 Subject: [PATCH] Flesh out lib/vscode migration. WIP Fix issues surrounding IPC interfaces. Update names. Fix spacing. Add missing property. Flesh out web socket cleanup. Flesh out web socket migration. WIP. Fix issues surrounding web socket frames. Touch up. Combine web command with server. Allow JS interop. Fix issues surrounding built in extensions. WIP Fix issues surrounding net socket lifecycle, and frequent restarts. Clean up socket lifecycle. Remove unused socket wrappers. Fix issues where logger is defined before ready. Clean up extension lifecycle. --- .eslintrc.json | 2 + .gitignore | 3 +- .vscode/settings.json | 6 + build/hygiene.js | 14 +- build/lib/extensions.ts | 2 +- build/lib/util.js | 2 + build/package.json | 4 - build/yarn.lock | 720 +----------------- coder.js | 71 ++ .../github-authentication/src/githubServer.ts | 6 +- extensions/postinstall.js | 3 + package.json | 7 + resources/web/code-web.d.ts | 4 + resources/web/code-web.js | 127 ++- src/vs/base/common/ipc.d.ts | 15 + src/vs/base/common/network.ts | 7 +- src/vs/base/common/platform.ts | 12 + src/vs/base/common/processes.ts | 1 + src/vs/base/common/product.ts | 5 + src/vs/base/common/types.ts | 7 + src/vs/base/common/uriServer.ts | 50 ++ src/vs/base/common/util.ts | 64 ++ src/vs/base/node/languagePacks.js | 5 +- src/vs/base/node/proxy_agent.ts | 85 +++ src/vs/base/parts/ipc/common/ipc.net.ts | 26 +- src/vs/base/parts/ipc/node/ipc.cp.ts | 108 ++- src/vs/code/browser/workbench/workbench.ts | 94 +-- src/vs/code/electron-main/main.ts | 134 +--- src/vs/code/node/cli.ts | 7 + src/vs/platform/environment/argumentParser.ts | 135 ++++ src/vs/platform/environment/common/argv.ts | 6 + .../environment/common/environment.ts | 3 + .../environment/common/environmentService.ts | 14 + src/vs/platform/environment/node/argv.ts | 8 +- .../node/extensionsScanner.ts | 10 +- src/vs/platform/log/node/spdlogLog.ts | 4 +- src/vs/platform/product/common/product.ts | 7 + .../remote/browser/browserSocketFactory.ts | 5 +- .../remote/common/remoteAgentConnection.ts | 44 +- src/vs/platform/storage/common/storage.ts | 25 +- src/vs/server/channel.ts | 564 ++++++++++++++ .../server/connection/abstractConnection.ts | 120 +++ .../connection/extensionHostConnection.ts | 202 +++++ .../server/connection/managementConnection.ts | 34 + src/vs/server/entry.ts | 96 +++ src/vs/server/insights.ts | 129 ++++ src/vs/server/marketplace.ts | 179 +++++ src/vs/server/nls.ts | 93 +++ src/vs/server/protocol.ts | 193 +++++ src/vs/server/server.ts | 407 ++++++++++ .../api/browser/mainThreadStorage.ts | 5 +- src/vs/workbench/api/node/extHostCLIServer.ts | 7 + src/vs/workbench/browser/client.ts | 172 +++++ .../browser/parts/dialogs/dialogHandler.ts | 5 +- src/vs/workbench/browser/web.main.ts | 7 + src/vs/workbench/common/resources.ts | 6 +- .../extensions/browser/extensionsViewlet.ts | 44 +- .../contrib/files/browser/explorerViewlet.ts | 10 +- .../page/browser/vs_code_welcome_page.ts | 18 +- .../welcome/page/browser/welcomePage.ts | 2 +- .../environment/browser/environmentService.ts | 38 +- .../browser/extensionEnablementService.ts | 2 +- .../common/extensionManagementService.ts | 6 + .../common/extensionHostProtocol.ts | 2 + .../extensions/common/remoteExtensionHost.ts | 9 +- .../node/extensionHostProcessSetup.ts | 28 +- .../host/browser/browserHostService.ts | 8 + .../browser/keyboardLayoutService.ts | 3 + .../browser/localizationsService.ts | 29 + src/vs/workbench/workbench.web.api.ts | 28 +- yarn.lock | 200 ++++- 71 files changed, 3506 insertions(+), 992 deletions(-) create mode 100644 coder.js create mode 100644 resources/web/code-web.d.ts create mode 100644 src/vs/base/common/ipc.d.ts create mode 100644 src/vs/base/common/uriServer.ts create mode 100644 src/vs/base/common/util.ts create mode 100644 src/vs/base/node/proxy_agent.ts create mode 100644 src/vs/platform/environment/argumentParser.ts create mode 100644 src/vs/server/channel.ts create mode 100644 src/vs/server/connection/abstractConnection.ts create mode 100644 src/vs/server/connection/extensionHostConnection.ts create mode 100644 src/vs/server/connection/managementConnection.ts create mode 100644 src/vs/server/entry.ts create mode 100644 src/vs/server/insights.ts create mode 100644 src/vs/server/marketplace.ts create mode 100644 src/vs/server/nls.ts create mode 100644 src/vs/server/protocol.ts create mode 100644 src/vs/server/server.ts create mode 100644 src/vs/workbench/browser/client.ts create mode 100644 src/vs/workbench/services/localizations/browser/localizationsService.ts diff --git a/.eslintrc.json b/.eslintrc.json index 51e623e8f1bdc..ac07b6d0e48cd 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -825,10 +825,12 @@ "target": "**/vs/server/**", "restrictions": [ "vs/nls", + "**/vs/code/**/{common,server,browser,node,electron-sandbox,electron-browser}/**", "**/vs/base/**/{common,node}/**", "**/vs/base/parts/**/{common,node}/**", "**/vs/platform/**/{common,node}/**", "**/vs/workbench/**/{common,node}/**", + "**/vs/workbench/workbench.web.api", "**/vs/server/**", "*" // node modules ] diff --git a/.gitignore b/.gitignore index 11a7486bf533c..1eb2d8963240c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,8 @@ node_modules/ extensions/**/dist/ /out*/ /extensions/**/out/ -src/vs/server +# @coder: The server directory is omitted upstream. +# src/vs/server resources/server build/node_modules coverage/ diff --git a/.vscode/settings.json b/.vscode/settings.json index a97841683c0ef..d0ffadd1d5d69 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -84,6 +84,12 @@ "editor.defaultFormatter": "vscode.typescript-language-features", "editor.formatOnSave": true, }, + "typescript.format.insertSpaceAfterConstructor": false, + "javascript.format.insertSpaceAfterConstructor": false, + "javascript.format.insertSpaceBeforeFunctionParenthesis": false, + "typescript.format.insertSpaceBeforeFunctionParenthesis": false, + "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, + "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false, "typescript.tsc.autoDetect": "off", "notebook.experimental.useMarkdownRenderer": true, "testing.autoRun.mode": "rerun", diff --git a/build/hygiene.js b/build/hygiene.js index 9d6756dbc86d7..024d12c0c2616 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -19,6 +19,18 @@ const copyrightHeaderLines = [ ' *--------------------------------------------------------------------------------------------*/', ]; +/** + * @remark While this helps delineate Coder's additions to the upstream project, + * this notice should be examined within the context of the application. + * Code from both maintainers often overlaps. + */ +const coderCopyrightHeaderLines = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Coder Technologies. All rights reserved.', + ' * Licensed under the MIT License. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', +]; + function hygiene(some, linting = true) { const gulpeslint = require('gulp-eslint'); const tsfmt = require('typescript-formatter'); @@ -62,7 +74,7 @@ function hygiene(some, linting = true) { const lines = file.__lines; for (let i = 0; i < copyrightHeaderLines.length; i++) { - if (lines[i] !== copyrightHeaderLines[i]) { + if (lines[i] !== copyrightHeaderLines[i] && lines[i] !== coderCopyrightHeaderLines[i]) { console.error(file.relative + ': Missing or bad copyright statement'); errorCount++; break; diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index bd7b508bb03c5..dcfaeb01656ea 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -67,7 +67,7 @@ function fromLocal(extensionPath: string, forWeb: boolean): Stream { if (isWebPacked) { input = updateExtensionPackageJSON(input, (data: any) => { delete data.scripts; - delete data.dependencies; + // https://github.com/cdr/code-server/pull/2041#issuecomment-685910322 delete data.devDependencies; if (data.main) { data.main = data.main.replace('/out/', /dist/); diff --git a/build/lib/util.js b/build/lib/util.js index 8d0294e4cebc0..b58fb754d2bb8 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -269,6 +269,8 @@ function streamToPromise(stream) { } exports.streamToPromise = streamToPromise; function getElectronVersion() { + // NOTE@coder: Fix version due to .yarnrc removal. + return process.versions.node; const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8'); const target = /^target "(.*)"$/m.exec(yarnrc)[1]; return target; diff --git a/build/package.json b/build/package.json index c1910d8b2012a..2a38c628d8b68 100644 --- a/build/package.json +++ b/build/package.json @@ -3,8 +3,6 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@azure/cosmos": "^3.9.3", - "@azure/storage-blob": "^12.4.0", "@types/ansi-colors": "^3.2.0", "@types/azure": "0.9.19", "@types/byline": "^4.2.32", @@ -37,7 +35,6 @@ "@typescript-eslint/experimental-utils": "~2.13.0", "@typescript-eslint/parser": "^3.3.0", "applicationinsights": "1.0.8", - "azure-storage": "^2.1.0", "byline": "^5.0.0", "colors": "^1.4.0", "commander": "^7.0.0", @@ -50,7 +47,6 @@ "mime": "^1.4.1", "mkdirp": "^1.0.4", "p-limit": "^3.1.0", - "plist": "^3.0.1", "source-map": "0.6.1", "typescript": "^4.4.0-dev.20210607", "vsce": "1.48.0", diff --git a/build/yarn.lock b/build/yarn.lock index ef2db1ff90cde..5f866f43e1ddc 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2,112 +2,6 @@ # yarn lockfile v1 -"@azure/abort-controller@^1.0.0": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.2.tgz#822405c966b2aec16fb62c1b19d37eaccf231995" - integrity sha512-XUyTo+bcyxHEf+jlN2MXA7YU9nxVehaubngHV1MIZZaqYmZqykkoeAz/JMMEeR7t3TcyDwbFa3Zw8BZywmIx4g== - dependencies: - tslib "^2.0.0" - -"@azure/core-asynciterator-polyfill@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz#dcccebb88406e5c76e0e1d52e8cc4c43a68b3ee7" - integrity sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg== - -"@azure/core-auth@^1.1.3": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.1.4.tgz#af9a334acf3cb9c49e6013e6caf6dc9d43476030" - integrity sha512-+j1embyH1jqf04AIfJPdLafd5SC1y6z1Jz4i+USR1XkTp6KM8P5u4/AjmWMVoEQdM/M29PJcRDZcCEWjK9S1bw== - dependencies: - "@azure/abort-controller" "^1.0.0" - tslib "^2.0.0" - -"@azure/core-http@^1.2.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@azure/core-http/-/core-http-1.2.2.tgz#a6f7717184fd2657d3acabd1d64dfdc0bd531ce3" - integrity sha512-9eu2OcbR7e44gqBy4U1Uv8NTWgLIMwKXMEGgO2MahsJy5rdTiAhs5fJHQffPq8uX2MFh21iBODwO9R/Xlov88A== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-auth" "^1.1.3" - "@azure/core-tracing" "1.0.0-preview.9" - "@azure/logger" "^1.0.0" - "@opentelemetry/api" "^0.10.2" - "@types/node-fetch" "^2.5.0" - "@types/tunnel" "^0.0.1" - form-data "^3.0.0" - node-fetch "^2.6.0" - process "^0.11.10" - tough-cookie "^4.0.0" - tslib "^2.0.0" - tunnel "^0.0.6" - uuid "^8.3.0" - xml2js "^0.4.19" - -"@azure/core-lro@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-1.0.3.tgz#1ddfb4ecdb81ce87b5f5d972ffe2acbbc46e524e" - integrity sha512-Py2crJ84qx1rXkzIwfKw5Ni4WJuzVU7KAF6i1yP3ce8fbynUeu8eEWS4JGtSQgU7xv02G55iPDROifmSDbxeHA== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-http" "^1.2.0" - events "^3.0.0" - tslib "^2.0.0" - -"@azure/core-paging@^1.1.1": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.1.3.tgz#3587c9898a0530cacb64bab216d7318468aa5efc" - integrity sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A== - dependencies: - "@azure/core-asynciterator-polyfill" "^1.0.0" - -"@azure/core-tracing@1.0.0-preview.9": - version "1.0.0-preview.9" - resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.9.tgz#84f3b85572013f9d9b85e1e5d89787aa180787eb" - integrity sha512-zczolCLJ5QG42AEPQ+Qg9SRYNUyB+yZ5dzof4YEc+dyWczO9G2sBqbAjLB7IqrsdHN2apkiB2oXeDKCsq48jug== - dependencies: - "@opencensus/web-types" "0.0.7" - "@opentelemetry/api" "^0.10.2" - tslib "^2.0.0" - -"@azure/cosmos@^3.9.3": - version "3.9.3" - resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.9.3.tgz#7e95ff92e5c3e9da7e8316bc50c9cc928be6c1d6" - integrity sha512-1mh8a6LAIykz24tJvQpafXiABUfq+HSAZBFJVZXea0Rd0qG8Ia9z8AK9FtPbC1nPvDC2RID2mRIjJvYbxRM/BA== - dependencies: - "@types/debug" "^4.1.4" - debug "^4.1.1" - fast-json-stable-stringify "^2.0.0" - jsbi "^3.1.3" - node-abort-controller "^1.0.4" - node-fetch "^2.6.0" - priorityqueuejs "^1.0.0" - semaphore "^1.0.5" - tslib "^2.0.0" - universal-user-agent "^6.0.0" - uuid "^8.3.0" - -"@azure/logger@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.1.tgz#19b333203d1b2931353d8879e814b64a7274837a" - integrity sha512-QYQeaJ+A5x6aMNu8BG5qdsVBnYBop9UMwgUvGihSjf1PdZZXB+c/oMdM2ajKwzobLBh9e9QuMQkN9iL+IxLBLA== - dependencies: - tslib "^2.0.0" - -"@azure/storage-blob@^12.4.0": - version "12.4.0" - resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.4.0.tgz#7127ddd9f413105e2c3688691bc4c6245d0806b3" - integrity sha512-OnhVSoKD1HzBB79/rFzPbC4w9TdzFXeoOwkX+aIu3rb8qvN0VaqvUqZXSrBCyG2LcLyVkY4MPCJQBrmEUm9kvw== - dependencies: - "@azure/abort-controller" "^1.0.0" - "@azure/core-http" "^1.2.0" - "@azure/core-lro" "^1.0.2" - "@azure/core-paging" "^1.1.1" - "@azure/core-tracing" "1.0.0-preview.9" - "@azure/logger" "^1.0.0" - "@opentelemetry/api" "^0.10.2" - events "^3.0.0" - tslib "^2.0.0" - "@malept/cross-spawn-promise@^1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" @@ -115,23 +9,6 @@ dependencies: cross-spawn "^7.0.1" -"@opencensus/web-types@0.0.7": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@opencensus/web-types/-/web-types-0.0.7.tgz#4426de1fe5aa8f624db395d2152b902874f0570a" - integrity sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g== - -"@opentelemetry/api@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.10.2.tgz#9647b881f3e1654089ff7ea59d587b2d35060654" - integrity sha512-GtpMGd6vkzDMYcpu2t9LlhEgMy/SzBwRnz48EejlRArYqZzqSzAsKmegUK7zHgl+EOIaK9mKHhnRaQu3qw20cA== - dependencies: - "@opentelemetry/context-base" "^0.10.2" - -"@opentelemetry/context-base@^0.10.2": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.10.2.tgz#55bea904b2b91aa8a8675df9eaba5961bddb1def" - integrity sha512-hZNKjKOYsckoOEgBziGMnBcX0M7EtstnCmwz5jZUOUYwlZ+/xxX6z3jPu1XVO2Jivk0eLfuP9GP+vFD49CMetw== - "@sindresorhus/is@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" @@ -191,7 +68,7 @@ resolved "https://registry.yarnpkg.com/@types/debounce/-/debounce-1.0.0.tgz#417560200331e1bb84d72da85391102c2fcd61b7" integrity sha1-QXVgIAMx4buE1y2oU5EQLC/NYbc= -"@types/debug@^4.1.4", "@types/debug@^4.1.5": +"@types/debug@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== @@ -358,14 +235,6 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.0.tgz#3eb56d13a1de1d347ecb1957c6860c911704bc44" integrity sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ== -"@types/node-fetch@^2.5.0": - version "2.5.8" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb" - integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - "@types/node@*": version "8.0.51" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" @@ -447,13 +316,6 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.2.tgz#e0d481d8bb282ad8a8c9e100ceb72c995fb5e709" integrity sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA== -"@types/tunnel@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c" - integrity sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A== - dependencies: - "@types/node" "*" - "@types/underscore@^1.8.9": version "1.8.9" resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.8.9.tgz#fef41f800cd23db1b4f262ddefe49cd952d82323" @@ -564,16 +426,6 @@ dependencies: eslint-visitor-keys "^1.1.0" -ajv@^6.12.3: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -602,71 +454,20 @@ asar@^3.0.3: optionalDependencies: "@types/glob" "^7.1.1" -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - at-least-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -azure-storage@^2.1.0: - version "2.10.3" - resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-2.10.3.tgz#c5966bf929d87587d78f6847040ea9a4b1d4a50a" - integrity sha512-IGLs5Xj6kO8Ii90KerQrrwuJKexLgSwYC4oLWmc11mzKe7Jt2E5IVg+ZQ8K53YWZACtVTMBNO3iGuA+4ipjJxQ== - dependencies: - browserify-mime "~1.2.9" - extend "^3.0.2" - json-edm-parser "0.1.2" - md5.js "1.3.4" - readable-stream "~2.0.0" - request "^2.86.0" - underscore "~1.8.3" - uuid "^3.0.0" - validator "~9.4.1" - xml2js "0.2.8" - xmlbuilder "^9.0.7" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base64-js@^1.2.3: - version "1.3.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" +base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== bluebird@^3.5.0: version "3.7.2" @@ -686,11 +487,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -browserify-mime@~1.2.9: - version "1.2.9" - resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f" - integrity sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8= - buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -742,11 +538,6 @@ cacheable-request@^7.0.1: normalize-url "^4.1.0" responselike "^2.0.0" -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - cheerio@^1.0.0-rc.1: version "1.0.0-rc.2" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" @@ -781,13 +572,6 @@ colors@^1.4.0: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" @@ -796,9 +580,9 @@ commander@2.9.0: graceful-readlink ">= 1.0.0" commander@^2.8.1: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@^5.0.0: version "5.1.0" @@ -820,11 +604,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - cross-spawn@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -849,13 +628,6 @@ css-what@2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -882,11 +654,6 @@ defer-to-connect@^2.0.0: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - denodeify@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" @@ -960,18 +727,10 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - electron-osx-sign@^0.4.16: - version "0.4.16" - resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.16.tgz#0be8e579b2d9fa4c12d2a21f063898294b3434aa" - integrity sha512-ziMWfc3NmQlwnWLW6EaZq8nH2BWVng/atX5GWsGwhexJYpdW6hsg//MkAfRTRx1kR3Veiqkeiog1ibkbA4x0rg== + version "0.4.17" + resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.17.tgz#2727ca0c79e1e4e5ccd3861fb3da9c3c913b006c" + integrity sha512-wUJPmZJQCs1zgdlQgeIpRcvrf7M5/COQaOV68Va1J/SgmWx5KL2otgg+fAae7luw6qz9R8Gvu/Qpe9tAOu/3xQ== dependencies: bluebird "^3.5.0" compare-version "^0.1.2" @@ -993,9 +752,9 @@ entities@^1.1.1, entities@~1.1.1: integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== esbuild@^0.12.6: - version "0.12.6" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.6.tgz#85bc755c7cf3005d4f34b4f10f98049ce0ee67ce" - integrity sha512-RDvVLvAjsq/kIZJoneMiUOH7EE7t2QaW7T3Q7EdQij14+bZbDq5sndb0tTanmHIFSqZVMBMMyqzVHkS3dJobeA== + version "0.12.16" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.16.tgz#c397144ce13b445a6ead9c1f747da11f79ec5e67" + integrity sha512-XqI9cXP2bmQ6MREIqrYBb13KfYFSERsV1+e5jSVWps8dNlLZK+hln7d0mznzDIpfISsg/AgQW0DW3kSInXWhrg== eslint-scope@^5.0.0: version "5.0.0" @@ -1029,36 +788,6 @@ estraverse@^4.1.0, estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -events@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" - integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== - -extend@^3.0.2, extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -1066,29 +795,6 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -1111,13 +817,6 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - glob@^7.0.6: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" @@ -1169,28 +868,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - htmlparser2@^3.9.1: version "3.10.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" @@ -1208,15 +885,6 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - http2-wrapper@^1.0.0-beta.5.2: version "1.0.0-beta.5.2" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3" @@ -1238,7 +906,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.4, inherits@~2.0.1: +inherits@2, inherits@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1260,16 +928,6 @@ is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - isbinaryfile@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80" @@ -1282,48 +940,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbi@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.4.tgz#9654dd02207a66a4911b4e4bb74265bc2cbc9dd0" - integrity sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg== - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-edm-parser@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/json-edm-parser/-/json-edm-parser-0.1.2.tgz#1e60b0fef1bc0af67bc0d146dfdde5486cd615b4" - integrity sha1-HmCw/vG8CvZ7wNFG393lSGzWFbQ= - dependencies: - jsonparse "~1.2.0" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - jsonc-parser@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.0.tgz#7c7fc988ee1486d35734faaaa866fadb00fa91ee" @@ -1338,21 +959,6 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonparse@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd" - integrity sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - keyv@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" @@ -1400,31 +1006,11 @@ markdown-it@^8.3.1: mdurl "^1.0.1" uc.micro "^1.0.5" -md5.js@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" - integrity sha1-6b296UogpawYsENA/Fdk1bCdkB0= - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= -mime-db@1.45.0: - version "1.45.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" - integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== - -mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.28" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" - integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== - dependencies: - mime-db "1.45.0" - mime@^1.3.4: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -1453,9 +1039,9 @@ minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f" - integrity sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw== + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== mkdirp@^1.0.4: version "1.0.4" @@ -1477,16 +1063,6 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -node-abort-controller@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-1.1.0.tgz#8a734a631b022af29963be7245c1483cbb9e070d" - integrity sha512-dEYmUqjtbivotqjraOe8UvhT/poFfog1BQRNsZm/MSEDDESk2cQ1tvD8kGyuN07TM/zoW+n42odL8zTeJupYdQ== - -node-fetch@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - normalize-url@^4.1.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" @@ -1499,11 +1075,6 @@ nth-check@~1.0.1: dependencies: boolbase "~1.0.0" -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1570,39 +1141,14 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - plist@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c" - integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ== + version "3.0.2" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.2.tgz#74bbf011124b90421c22d15779cee60060ba95bc" + integrity sha512-MSrkwZBdQ6YapHy87/8hDU8MnIcyxBKjeF+McXnr5A9MtffPewTs7G3hlpodT5TacyfIyFTaJEhh3GGcmasTgQ== dependencies: - base64-js "^1.2.3" + base64-js "^1.5.1" xmlbuilder "^9.0.7" - xmldom "0.1.x" - -priorityqueuejs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz#2ee4f23c2560913e08c07ce5ccdd6de3df2c5af8" - integrity sha1-LuTyPCVgkT4IwHzlzN1t498sWvg= - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -psl@^1.1.28, psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + xmldom "^0.5.0" pump@^3.0.0: version "3.0.0" @@ -1612,21 +1158,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - q@^1.0.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -1648,53 +1184,6 @@ readable-stream@^3.0.6: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -request@^2.86.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - resolve-alpn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c" @@ -1707,36 +1196,11 @@ responselike@^2.0.0: dependencies: lowercase-keys "^2.0.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-buffer@~5.1.0: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sax@0.5.x: - version "0.5.8" - resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" - integrity sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE= - -sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semaphore@^1.0.5: - version "1.1.0" - resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" - integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== - semver@^5.1.0, semver@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" @@ -1776,21 +1240,6 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - string_decoder@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -1798,11 +1247,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.1.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - tmp@0.0.29: version "0.0.29" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" @@ -1810,33 +1254,11 @@ tmp@0.0.29: dependencies: os-tmpdir "~1.0.1" -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - tslib@^1.8.1: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== - tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -1844,28 +1266,11 @@ tsutils@^3.17.1: dependencies: tslib "^1.8.1" -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - tunnel@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.4.tgz#2d3785a158c174c9a16dc2c046ec5fc5f1742213" integrity sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM= -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - typed-rest-client@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-0.9.0.tgz#f768cc0dc3f4e950f06e04825c36b3e7834aa1f2" @@ -1889,72 +1294,31 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== -underscore@1.8.3, underscore@~1.8.3: +underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= underscore@^1.8.3: - version "1.9.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" - integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== - -universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== - -universalify@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + version "1.13.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" + integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== universalify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - url-join@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg= -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -uuid@^3.0.0, uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.0: - version "8.3.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" - integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== - -validator@~9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/validator/-/validator-9.4.1.tgz#abf466d398b561cd243050112c6ff1de6cc12663" - integrity sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vsce@1.48.0: version "1.48.0" resolved "https://registry.yarnpkg.com/vsce/-/vsce-1.48.0.tgz#31c1a4c6909c3b8bdc48b3d32cc8c8e94c7113a2" @@ -2014,21 +1378,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xml2js@0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.8.tgz#9b81690931631ff09d1957549faf54f4f980b3c2" - integrity sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I= - dependencies: - sax "0.5.x" - -xml2js@^0.4.19: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - xmlbuilder@>=11.0.1: version "15.1.1" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" @@ -2039,15 +1388,10 @@ xmlbuilder@^9.0.7: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - -xmldom@0.1.x: - version "0.1.31" - resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" - integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== +xmldom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e" + integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== yallist@^4.0.0: version "4.0.0" diff --git a/coder.js b/coder.js new file mode 100644 index 0000000000000..e487491df8258 --- /dev/null +++ b/coder.js @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// This must be ran from VS Code's root. +const gulp = require('gulp'); +const path = require('path'); +const _ = require('underscore'); +const buildfile = require('./src/buildfile'); +const common = require('./build/lib/optimize'); +const util = require('./build/lib/util'); + +const vscodeEntryPoints = _.flatten([ + buildfile.entrypoint('vs/workbench/workbench.web.api'), + buildfile.entrypoint('vs/server/entry'), + buildfile.base, + buildfile.workbenchWeb, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.keyboardMaps, + // See ./src/vs/workbench/buildfile.desktop.js + buildfile.entrypoint('vs/platform/files/node/watcher/unix/watcherApp'), + buildfile.entrypoint('vs/platform/files/node/watcher/nsfw/watcherApp'), + buildfile.entrypoint(`vs/platform/terminal/node/ptyHostMain`), + buildfile.entrypoint('vs/workbench/services/extensions/node/extensionHostProcess'), +]); + +// See ./build/gulpfile.vscode.js +const vscodeResources = [ + 'out-build/vs/server/fork.js', + '!out-build/vs/server/doc/**', + 'out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js', + 'out-build/bootstrap.js', + 'out-build/bootstrap-fork.js', + 'out-build/bootstrap-amd.js', + 'out-build/bootstrap-node.js', + 'out-build/vs/**/*.{svg,png,html,ttf,jpg}', + '!out-build/vs/code/browser/workbench/*.html', + '!out-build/vs/code/electron-browser/**', + 'out-build/vs/base/common/performance.js', + 'out-build/vs/base/node/languagePacks.js', + 'out-build/vs/base/browser/ui/codicons/codicon/**', + 'out-build/vs/base/node/userDataPath.js', + 'out-build/vs/workbench/browser/media/*-theme.css', + 'out-build/vs/workbench/contrib/debug/**/*.json', + 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', + 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', + 'out-build/vs/**/markdown.css', + 'out-build/vs/workbench/contrib/tasks/**/*.json', + 'out-build/vs/platform/files/**/*.md', + '!**/test/**' +]; + +gulp.task('optimize', gulp.series( + util.rimraf('out-vscode'), + common.optimizeTask({ + src: 'out-build', + entryPoints: vscodeEntryPoints, + resources: vscodeResources, + loaderConfig: common.loaderConfig(), + out: 'out-vscode', + inlineAmdImages: true, + bundleInfo: undefined + }), +)); + +gulp.task('minify', gulp.series( + util.rimraf('out-vscode-min'), + common.minifyTask('out-vscode') +)); diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index 74aa5cc3f8d80..b0c57ee9810b4 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -15,7 +15,11 @@ import { AuthProviderType } from './github'; const localize = nls.loadMessageBundle(); export const NETWORK_ERROR = 'network error'; -const AUTH_RELAY_SERVER = 'vscode-auth.github.com'; +/** + * @coder Domain to our own auth relay. + * @remark `AUTH_RELAY_STAGING_SERVER` comes from Microsoft's + */ +const AUTH_RELAY_SERVER = 'auth.code-server.dev'; // const AUTH_RELAY_STAGING_SERVER = 'client-auth-staging-14a768b.herokuapp.com'; class UriEventHandler extends vscode.EventEmitter implements vscode.UriHandler { diff --git a/extensions/postinstall.js b/extensions/postinstall.js index da4fa3e9d0443..0d43b3a200685 100644 --- a/extensions/postinstall.js +++ b/extensions/postinstall.js @@ -24,6 +24,9 @@ function processRoot() { rimraf.sync(filePath); } } + + /** @coder: Delete .bin so it doesn't contain broken symlinks that trip up nfpm. */ + rimraf.sync(path.join(__dirname, 'node_modules', '.bin')); } function processLib() { diff --git a/package.json b/package.json index 563b835f33f6a..1c77389c21fde 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "web": "node resources/web/code-web.js", "compile-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-web", "watch-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-web", + "server": "node --max_old_space_size=4095 out/vs/server/entry.js", "eslint": "node build/eslint", "playwright-install": "node build/azure-pipelines/common/installPlaywright.js", "compile-build": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-build", @@ -57,6 +58,8 @@ "extensions-ci": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js extensions-ci" }, "dependencies": { + "@types/proxy-from-env": "^1.0.1", + "@types/tar-stream": "^2.2.1", "applicationinsights": "1.0.8", "chokidar": "3.5.1", "graceful-fs": "4.2.6", @@ -71,8 +74,11 @@ "native-watchdog": "1.3.0", "node-pty": "0.11.0-beta7", "nsfw": "2.1.2", + "proxy-agent": "^4.0.1", + "proxy-from-env": "^1.1.0", "spdlog": "^0.13.0", "sudo-prompt": "9.2.1", + "tar-stream": "^2.2.0", "tas-client-umd": "0.1.4", "v8-inspect-profiler": "^0.0.21", "vscode-oniguruma": "1.5.1", @@ -113,6 +119,7 @@ "@types/windows-mutex": "^0.4.0", "@types/windows-process-tree": "^0.2.0", "@types/winreg": "^1.2.30", + "@types/ws": "^7.4.7", "@types/yauzl": "^2.9.1", "@types/yazl": "^2.4.2", "@typescript-eslint/eslint-plugin": "3.2.0", diff --git a/resources/web/code-web.d.ts b/resources/web/code-web.d.ts new file mode 100644 index 0000000000000..3ea14c7bc26d7 --- /dev/null +++ b/resources/web/code-web.d.ts @@ -0,0 +1,4 @@ +import * as http from 'http'; +import { IServerWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; + +export function requestHandler(req: http.IncomingMessage, res: http.ServerResponse, webConfigJSON: IServerWorkbenchConstructionOptions): void; diff --git a/resources/web/code-web.js b/resources/web/code-web.js index 13ff160ceb6a7..46137601612a7 100644 --- a/resources/web/code-web.js +++ b/resources/web/code-web.js @@ -4,6 +4,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, '__esModule', { value: true }); // @ts-check @@ -31,6 +32,8 @@ const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench' // This is useful to simulate real world CORS const ALLOWED_CORS_ORIGINS = [ + 'http://localhost:8082', + 'http://127.0.0.1:8082', 'http://localhost:8081', 'http://127.0.0.1:8081', 'http://localhost:8080', @@ -41,15 +44,17 @@ const WEB_PLAYGROUND_VERSION = '0.0.12'; const args = minimist(process.argv, { boolean: [ - 'no-launch', + 'launch', 'help', 'verbose', 'wrap-iframe', 'enable-sync', ], string: [ + 'server', 'scheme', 'host', + 'remote-authority', 'port', 'local_port', 'extension', @@ -61,12 +66,13 @@ const args = minimist(process.argv, { if (args.help) { console.log( 'yarn web [options]\n' + - ' --no-launch Do not open VSCode web in the browser\n' + + ' --launch Open VSCode web in the browser\n' + ' --wrap-iframe Wrap the Web Worker Extension Host in an iframe\n' + ' --enable-sync Enable sync by default\n' + ' --scheme Protocol (https or http)\n' + ' --host Remote host\n' + ' --port Remote/Local port\n' + + ' --remote-authority Remote auth\n' + ' --local_port Local port override\n' + ' --secondary-port Secondary port\n' + ' --extension Path of an extension to include\n' + @@ -221,8 +227,10 @@ const mapCallbackUriToRequestId = new Map(); /** * @param req {http.IncomingMessage} * @param res {http.ServerResponse} + * @param webConfigJSON {import('../../src/vs/workbench/workbench.web.api').IServerWorkbenchConstructionOptions} + * @param webConfigJSON undefined */ -const requestHandler = (req, res) => { +const requestHandler = (req, res, webConfigJSON) => { const parsedUrl = url.parse(req.url, true); const pathname = parsedUrl.pathname; @@ -253,6 +261,9 @@ const requestHandler = (req, res) => { return handleExtension(req, res, parsedUrl); } if (pathname === '/') { + if (args.server) { + return handleRootFromServer(req, res, webConfigJSON); + } // main web return handleRoot(req, res); } else if (pathname === '/callback') { @@ -261,8 +272,11 @@ const requestHandler = (req, res) => { } else if (pathname === '/fetch-callback') { // callback fetch support return handleFetchCallback(req, res, parsedUrl); + } else if (pathname === '/vscode-remote-resource') { + // callback fetch support + return handleRemoteResource(req, res, parsedUrl); } else if (pathname === '/builtin') { - // builtin extnesions JSON + // builtin extensions JSON return handleBuiltInExtensions(req, res, parsedUrl); } @@ -274,26 +288,28 @@ const requestHandler = (req, res) => { } }; -const server = http.createServer(requestHandler); -server.listen(LOCAL_PORT, () => { - if (LOCAL_PORT !== PORT) { - console.log(`Operating location at http://0.0.0.0:${LOCAL_PORT}`); - } - console.log(`Web UI available at ${SCHEME}://${AUTHORITY}`); -}); -server.on('error', err => { - console.error(`Error occurred in server:`); - console.error(err); -}); +if (!args.server) { + const server = http.createServer(requestHandler); + server.listen(LOCAL_PORT, () => { + if (LOCAL_PORT !== PORT) { + console.log(`Operating location at http://0.0.0.0:${LOCAL_PORT}`); + } + console.log(`Web UI available at ${SCHEME}://${AUTHORITY}`); + }); + server.on('error', err => { + console.error(`Error occurred in server:`); + console.error(err); + }); -const secondaryServer = http.createServer(requestHandler); -secondaryServer.listen(SECONDARY_PORT, () => { - console.log(`Secondary server available at ${SCHEME}://${HOST}:${SECONDARY_PORT}`); -}); -secondaryServer.on('error', err => { - console.error(`Error occurred in server:`); - console.error(err); -}); + const secondaryServer = http.createServer(requestHandler); + secondaryServer.listen(SECONDARY_PORT, () => { + console.log(`Secondary server available at ${SCHEME}://${HOST}:${SECONDARY_PORT}`); + }); + secondaryServer.on('error', err => { + console.error(`Error occurred in server:`); + console.error(err); + }); +} /** * @param {import('http').IncomingMessage} req @@ -317,6 +333,20 @@ async function handleBuiltInExtensions(req, res, parsedUrl) { return res.end(JSON.stringify(extensions)); } +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param {import('url').UrlWithParsedQuery} parsedUrl + */ +async function handleRemoteResource(req, res, parsedUrl) { + const { path } = parsedUrl.query; + + if (path) { + res.setHeader('Content-Type', getMediaMime(path)); + res.end(await readFile(path)); + } +} + /** * @param {import('http').IncomingMessage} req * @param {import('http').ServerResponse} res @@ -420,6 +450,7 @@ async function handleRoot(req, res) { ? req.headers['host'].replace(':' + PORT, ':' + SECONDARY_PORT) : `${HOST}:${SECONDARY_PORT}` ); + const webConfigJSON = { folderUri: folderUri, additionalBuiltinExtensions, @@ -454,6 +485,52 @@ async function handleRoot(req, res) { return res.end(data); } +/** + * @param {import('http').IncomingMessage} req + * @param {import('http').ServerResponse} res + * @param webConfigJSON {import('../../src/vs/workbench/workbench.web.api').IServerWorkbenchConstructionOptions} + */ +async function handleRootFromServer(req, res, webConfigJSON) { + // const { extensions: builtInExtensions } = await builtInExtensionsPromise; + // const { extensions: additionalBuiltinExtensions, locations: staticLocations } = await commandlineProvidedExtensionsPromise; + + // const dedupedBuiltInExtensions = []; + // for (const builtInExtension of builtInExtensions) { + // const extensionId = `${builtInExtension.packageJSON.publisher}.${builtInExtension.packageJSON.name}`; + // if (staticLocations[extensionId]) { + // fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: Ignoring built-in ${extensionId} because it was overridden via --extension argument`); + // continue; + // } + + // dedupedBuiltInExtensions.push(builtInExtension); + // } + + if (args.verbose) { + fancyLog(`${ansiColors.magenta('BuiltIn extensions')}: ${dedupedBuiltInExtensions.map(e => path.basename(e.extensionPath)).join(', ')}`); + fancyLog(`${ansiColors.magenta('Additional extensions')}: ${additionalBuiltinExtensions.map(e => path.basename(e.extensionLocation.path)).join(', ') || 'None'}`); + } + + if (req.headers['x-forwarded-host']) { + // support for running in codespace => no iframe wrapping + delete webConfigJSON.webWorkerExtensionHostIframeSrc; + } + const authSessionInfo = undefined; + + const data = (await readFile(WEB_MAIN)).toString() + .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => escapeAttribute(JSON.stringify(webConfigJSON))) // use a replace function to avoid that regexp replace patterns ($&, $0, ...) are applied + // .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeAttribute(JSON.stringify(dedupedBuiltInExtensions))) + .replace('{{WORKBENCH_AUTH_SESSION}}', () => authSessionInfo ? escapeAttribute(JSON.stringify(authSessionInfo)) : '') + .replace('{{WEBVIEW_ENDPOINT}}', ''); + + const headers = { + 'Content-Type': 'text/html', + 'Content-Security-Policy': 'require-trusted-types-for \'script\';' + }; + + res.writeHead(200, headers); + return res.end(data); +} + /** * Handle HTTP requests for /callback * @param {import('http').IncomingMessage} req @@ -613,7 +690,7 @@ const mapExtToMediaMimes = { function getMediaMime(forPath) { const ext = path.extname(forPath); - return mapExtToMediaMimes[ext.toLowerCase()]; + return mapExtToMediaMimes[ext.toLowerCase()] || 'text/plain'; } /** @@ -655,3 +732,5 @@ async function serveFile(req, res, filePath, responseHeaders = Object.create(nul if (args.launch !== false) { opn(`${SCHEME}://${HOST}:${PORT}`); } + +exports.requestHandler = requestHandler; diff --git a/src/vs/base/common/ipc.d.ts b/src/vs/base/common/ipc.d.ts new file mode 100644 index 0000000000000..2c3dc0cbf401b --- /dev/null +++ b/src/vs/base/common/ipc.d.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * External interfaces for integration into code-server over IPC. + */ +export interface CodeServerConfiguration { + authed: boolean + base: string + csStaticBase: string + disableUpdateCheck: boolean + logLevel: number +} diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 69351f7d9180b..5feb80d16a901 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -128,16 +128,17 @@ class RemoteAuthoritiesImpl { if (host && host.indexOf(':') !== -1) { host = `[${host}]`; } - const port = this._ports[authority]; + // const port = this._ports[authority]; const connectionToken = this._connectionTokens[authority]; let query = `path=${encodeURIComponent(uri.path)}`; if (typeof connectionToken === 'string') { query += `&tkn=${encodeURIComponent(connectionToken)}`; } + // NOTE@coder: Changed this to work against the current path. return URI.from({ scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource, - authority: `${host}:${port}`, - path: `/vscode-remote-resource`, + authority: window.location.host, + path: `${window.location.pathname.replace(/\/+$/, '')}/vscode-remote-resource`, query }); } diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 2c281b62d9b99..dce7d7187ab12 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -107,6 +107,18 @@ if (typeof navigator === 'object' && !isElectronRenderer) { _isWeb = true; _locale = navigator.language; _language = _locale; + + // NOTE@coder: Make languages work. + const el = typeof document !== 'undefined' && document.getElementById('vscode-remote-nls-configuration'); + const rawNlsConfig = el && el.getAttribute('data-settings'); + if (rawNlsConfig) { + try { + const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig); + _locale = nlsConfig.locale; + _translationsConfigFile = nlsConfig._translationsConfigFile; + _language = nlsConfig.availableLanguages['*'] || LANGUAGE_DEFAULT; + } catch (error) { /* Oh well. */ } + } } // Native environment diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts index 6c52c3f9ce2bf..6b84dbedbf57a 100644 --- a/src/vs/base/common/processes.ts +++ b/src/vs/base/common/processes.ts @@ -111,6 +111,7 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve /^VSCODE_.+$/, /^SNAP(|_.*)$/, /^GDK_PIXBUF_.+$/, + /^CODE_SERVER_.+$/, ]; const envKeys = Object.keys(env); envKeys diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index d47fc57bf84e5..b1c0ffef17211 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -31,6 +31,11 @@ export type ExtensionVirtualWorkspaceSupport = { }; export interface IProductConfiguration { + /** @coder BEGIN */ + /** @deprecated Should be replaced with code-oss version. */ + readonly codeServerVersion?: string; + /** @coder END */ + readonly version: string; readonly date?: string; readonly quality?: string; diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 3e431196b998f..7d17b82d6ec34 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -272,6 +272,13 @@ export type Dto = T extends { toJSON(): infer U } ? { [k in keyof T]: Dto; } : T; +/** + * Marks optional types as required. + */ +export type Complete = { + [P in keyof Required]: Pick extends Required> ? T[P] : (NonNullable); +}; + export function NotImplementedProxy(name: string): { new(): T } { return class { constructor() { diff --git a/src/vs/base/common/uriServer.ts b/src/vs/base/common/uriServer.ts new file mode 100644 index 0000000000000..a78b44fb574be --- /dev/null +++ b/src/vs/base/common/uriServer.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Schemas } from 'vs/base/common/network'; +import { IRawURITransformer, UriParts, URITransformer } from 'vs/base/common/uriIpc'; + +/** + * Transforms URIs such that incoming and outgoing requests are directed to their + * respective local and remote routes. + */ +class RawURITransformer implements IRawURITransformer { + constructor(private readonly authority: string) { } + + transformIncoming(uri: UriParts): UriParts { + switch (uri.scheme) { + case Schemas.vscodeRemote: + return { scheme: Schemas.file, path: uri.path }; + default: + return uri; + } + } + + transformOutgoing(uri: UriParts): UriParts { + switch (uri.scheme) { + case Schemas.file: + return { scheme: Schemas.vscodeRemote, authority: this.authority, path: uri.path }; + default: + return uri; + } + } + + transformOutgoingScheme(scheme: string): string { + switch (scheme) { + case 'file': + return Schemas.vscodeRemote; + default: + return scheme; + } + } +} + +/** + * Convenience function, given that a server's raw URI transformer is often wrapped + * by VSCode's `URITransformer`. + */ +export function createServerURITransformer(authority: string) { + return new URITransformer(new RawURITransformer(authority)); +} diff --git a/src/vs/base/common/util.ts b/src/vs/base/common/util.ts new file mode 100644 index 0000000000000..f83b86482d2a3 --- /dev/null +++ b/src/vs/base/common/util.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Base options included on every page. + */ +export interface Options { + base: string + csStaticBase: string + logLevel: number +} + +/** + * Remove extra slashes in a URL. + */ +export const normalize = (url: string, keepTrailing = false): string => { + return url.replace(/\/\/+/g, '/').replace(/\/+$/, keepTrailing ? '/' : ''); +}; + +/** + * Resolve a relative base against the window location. This is used for + * anything that doesn't work with a relative path. + */ +export const resolveBase = (base?: string): string => { + // After resolving the base will either start with / or be an empty string. + if (!base || base.startsWith('/')) { + return base ?? ''; + } + const parts = location.pathname.split('/'); + parts[parts.length - 1] = base; + const url = new URL(location.origin + '/' + parts.join('/')); + return normalize(url.pathname); +}; + +/** + * Get options embedded in the HTML or query params. + */ +export const getOptions = (): T => { + let options: T; + try { + options = JSON.parse(document.getElementById('coder-options')!.getAttribute('data-settings')!); + } catch (error) { + options = {} as T; + } + + // You can also pass options in stringified form to the options query + // variable. Options provided here will override the ones in the options + // element. + const params = new URLSearchParams(location.search); + const queryOpts = params.get('options'); + if (queryOpts) { + options = { + ...options, + ...JSON.parse(queryOpts), + }; + } + + options.base = resolveBase(options.base); + options.csStaticBase = resolveBase(options.csStaticBase); + + return options; +}; diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js index 5c14fade87895..0899cab4ed150 100644 --- a/src/vs/base/node/languagePacks.js +++ b/src/vs/base/node/languagePacks.js @@ -73,7 +73,10 @@ function getLanguagePackConfigurations(userDataPath) { const configFile = path.join(userDataPath, 'languagepacks.json'); try { - return nodeRequire(configFile); + // NOTE@coder: Swapped require with readFile since require is cached and + // we don't restart the server-side portion of code-server when the + // language changes. + return JSON.parse(fs.readFileSync(configFile, 'utf8')); } catch (err) { // Do nothing. If we can't read the file we have no // language pack config. diff --git a/src/vs/base/node/proxy_agent.ts b/src/vs/base/node/proxy_agent.ts new file mode 100644 index 0000000000000..495634bdc7652 --- /dev/null +++ b/src/vs/base/node/proxy_agent.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as http from "http" +import * as proxyAgent from "proxy-agent" +import * as proxyFromEnv from "proxy-from-env" + +/** + * This file has nothing to do with the code-server proxy. + * It is to support $HTTP_PROXY, $HTTPS_PROXY and $NO_PROXY. + * + * - https://github.com/cdr/code-server/issues/124 + * - https://www.npmjs.com/package/proxy-agent + * - https://www.npmjs.com/package/proxy-from-env + * + * This file exists in two locations: + * - src/node/proxy_agent.ts + * - lib/vscode/src/vs/base/node/proxy_agent.ts + * The second is a symlink to the first. + */ + +/** + * monkeyPatch patches the node http,https modules to route all requests through the + * agent we get from the proxy-agent package. + * + * This approach only works if there is no code specifying an explicit agent when making + * a request. + * + * None of our code ever passes in a explicit agent to the http,https modules. + * VS Code's does sometimes but only when a user sets the http.proxy configuration. + * See https://code.visualstudio.com/docs/setup/network#_legacy-proxy-server-support + * + * Even if they do, it's probably the same proxy so we should be fine! And those knobs + * are deprecated anyway. + */ +export function monkeyPatch(inVSCode: boolean): void { + if (shouldEnableProxy()) { + const http = require("http") + const https = require("https") + + // If we do not pass in a proxy URL, proxy-agent will get the URL from the environment. + // See https://www.npmjs.com/package/proxy-from-env. + // Also see shouldEnableProxy. + const pa = newProxyAgent(inVSCode) + http.globalAgent = pa + https.globalAgent = pa + } +} + +function newProxyAgent(inVSCode: boolean): http.Agent { + // The reasoning for this split is that VS Code's build process does not have + // esModuleInterop enabled but the code-server one does. As a result depending on where + // we execute, we either have a default attribute or we don't. + // + // I can't enable esModuleInterop in VS Code's build process as it breaks and spits out + // a huge number of errors. And we can't use require as otherwise the modules won't be + // included in the final product. + if (inVSCode) { + return new (proxyAgent as any)() + } else { + return new (proxyAgent as any).default() + } +} + +// If they have $NO_PROXY set to example.com then this check won't work! +// But that's drastically unlikely. +function shouldEnableProxy(): boolean { + let shouldEnable = false + + const httpProxy = proxyFromEnv.getProxyForUrl(`http://example.com`) + if (httpProxy) { + shouldEnable = true + console.debug(`using $HTTP_PROXY ${httpProxy}`) + } + + const httpsProxy = proxyFromEnv.getProxyForUrl(`https://example.com`) + if (httpsProxy) { + shouldEnable = true + console.debug(`using $HTTPS_PROXY ${httpsProxy}`) + } + + return shouldEnable +} diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 5df1869678459..3f03b8b51c62c 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -175,6 +175,16 @@ const enum ProtocolMessageType { ReplayRequest = 6 } +const ProtocolMessageTypeToLabel: { [K in ProtocolMessageType]: string } = { + 0: 'None', + 1: 'Regular', + 2: 'Control', + 3: 'Ack', + 4: 'KeepAlive', + 5: 'Disconnect', + 6: 'ReplayRequest' +}; + export const enum ProtocolConstants { HeaderLength = 13, /** @@ -219,6 +229,10 @@ class ProtocolMessage { public get size(): number { return this.data.byteLength; } + + public get label(): string { + return ProtocolMessageTypeToLabel[this.type as ProtocolMessageType] || 'Unknown'; + } } class ProtocolReader extends Disposable { @@ -262,8 +276,6 @@ class ProtocolReader extends Disposable { const buff = this._incomingData.read(this._state.readLen); if (this._state.readHead) { - // buff is the header - // save new state => next time will read the body this._state.readHead = false; this._state.readLen = buff.readUInt32BE(9); @@ -703,7 +715,10 @@ export class PersistentProtocol implements IMessagePassingProtocol { this._socketReader = new ProtocolReader(this._socket); this._socketDisposables.push(this._socketReader); this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg))); - this._socketDisposables.push(this._socket.onClose((e) => this._onSocketClose.fire(e))); + this._socketDisposables.push(this._socket.onClose((e) => { + console.log('SOCKET DISPOSABLE CLOSE', (e as WebSocketCloseEvent).reason); + return this._onSocketClose.fire(e); + })); if (initialChunk) { this._socketReader.acceptChunk(initialChunk); } @@ -792,6 +807,11 @@ export class PersistentProtocol implements IMessagePassingProtocol { return this._socket; } + // NOTE@coder: add setSocket + public setSocket(socket: ISocket) { + this._socket = socket; + } + public getMillisSinceLastIncomingData(): number { return Date.now() - this._socketReader.lastReadTime; } diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 84ef5cb0a0482..2d6d6164cab66 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -3,18 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ChildProcess, fork, ForkOptions } from 'child_process'; +import { ChildProcess, fork, ForkOptions, SendHandle } from 'child_process'; import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { Delayer, createCancelablePromise } from 'vs/base/common/async'; import { deepClone } from 'vs/base/common/objects'; import { Emitter, Event } from 'vs/base/common/event'; import { createQueuedSender } from 'vs/base/node/processes'; import { IChannel, ChannelServer as IPCServer, ChannelClient as IPCClient, IChannelClient } from 'vs/base/parts/ipc/common/ipc'; -import { isRemoteConsoleLog, log } from 'vs/base/common/console'; +import { IRemoteConsoleLog, isRemoteConsoleLog, log } from 'vs/base/common/console'; import { CancellationToken } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { VSBuffer } from 'vs/base/common/buffer'; import { isMacintosh } from 'vs/base/common/platform'; +// eslint-disable-next-line code-import-patterns +import { IExtHostMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; /** * This implementation doesn't perform well since it uses base64 encoding for buffers. @@ -290,3 +292,105 @@ export class Client implements IChannelClient, IDisposable { this.activeRequests.clear(); } } + +export type ExtensionHostMessage = IRemoteConsoleLog | IExtHostReadyMessage; +/** + * Creates an extension host child process with a standard disposable interface. + */ +export class ExtensionHost implements IDisposable { + private child: ChildProcess | null = null; + private readonly _onDidProcessExit = new Emitter<{ code: number, signal: string }>(); + public readonly onDidProcessExit = this._onDidProcessExit.event; + + private readonly _onReadyMessage = new Emitter(); + public readonly onReady = this._onReadyMessage.event; + + private disposeClient() { + if (this.child) { + this.child.kill(); + this.child = null; + } + } + + dispose() { + this._onDidProcessExit.dispose(); + + this.disposeClient(); + } + + sendIPCMessage(message: IExtHostMessage, sendHandle?: SendHandle): boolean { + if (this.child && this.child.connected) { + return this.child.send(message, sendHandle); + } + + return false; + } + + constructor(private modulePath: string, private options: IIPCOptions) { + const args = options && options.args ? this.options.args : []; + const forkOpts: ForkOptions = Object.create(null); + + forkOpts.silent = true; + forkOpts.env = { ...deepClone(process.env), 'VSCODE_PARENT_PID': String(process.pid) }; + + if (this.options && this.options.env) { + forkOpts.env = { ...forkOpts.env, ...this.options.env }; + } + + if (this.options && this.options.freshExecArgv) { + forkOpts.execArgv = []; + } + + if (this.options && typeof this.options.debug === 'number') { + forkOpts.execArgv = ['--nolazy', '--inspect=' + this.options.debug]; + } + + if (this.options && typeof this.options.debugBrk === 'number') { + forkOpts.execArgv = ['--nolazy', '--inspect-brk=' + this.options.debugBrk]; + } + + if (forkOpts.execArgv === undefined) { + // if not set, the forked process inherits the execArgv of the parent process + // --inspect and --inspect-brk can not be inherited as the port would conflict + forkOpts.execArgv = process.execArgv.filter(a => !/^--inspect(-brk)?=/.test(a)); // remove + } + + if (isMacintosh && forkOpts.env) { + // Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes + // See https://github.com/microsoft/vscode/issues/105848 + delete forkOpts.env['DYLD_LIBRARY_PATH']; + } + + this.child = fork(this.modulePath, args, forkOpts); + + const onRawMessage = Event.fromNodeEventEmitter(this.child, 'message', msg => msg); + + onRawMessage(msg => { + // Handle remote console logs specially + if (isRemoteConsoleLog(msg)) { + log(msg, `IPC Library: ${this.options.serverName}`); + return; + } + + if (msg.type === 'VSCODE_EXTHOST_IPC_READY') { + this._onReadyMessage.fire(msg); + } + }); + + const onExit = () => this.disposeClient(); + process.once('exit', onExit); + + this.child.on('error', err => console.warn('IPC "' + this.options.serverName + '" errored with ' + err)); + + this.child.on('exit', (code: any, signal: any) => { + process.removeListener('exit' as 'loaded', onExit); // https://github.com/electron/electron/issues/21475 + + + if (code !== 0 && signal !== 'SIGTERM') { + console.warn('IPC "' + this.options.serverName + '" crashed with exit code ' + code + ' and signal ' + signal); + } + + this._onDidProcessExit.fire({ code, signal }); + }); + } +} diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 9c222a0048ec3..e79458b885e4f 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IHomeIndicator, IProductQualityChangeHandler, ISettingsSyncOptions } from 'vs/workbench/workbench.web.api'; +import { create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IProductQualityChangeHandler, ISettingsSyncOptions, IServerWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; @@ -17,7 +17,6 @@ import { isStandalone } from 'vs/base/browser/browser'; import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import product from 'vs/platform/product/common/product'; -import { parseLogLevel } from 'vs/platform/log/common/log'; function doCreateUri(path: string, queryValues: Map): URI { let query: string | undefined = undefined; @@ -37,6 +36,15 @@ function doCreateUri(path: string, queryValues: Map): URI { return URI.parse(window.location.href).with({ path, query }); } +/** + * NOTE@coder: Add this function. + * Encode a path for opening via the folder or workspace query parameter. This + * preserves slashes so it can be edited by hand more easily. + */ +export const encodePath = (path: string): string => { + return path.split('/').map((p) => encodeURIComponent(p)).join('/'); +}; + interface ICredential { service: string; account: string; @@ -316,12 +324,18 @@ class WorkspaceProvider implements IWorkspaceProvider { // Folder else if (isFolderToOpen(workspace)) { - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${encodeURIComponent(workspace.folderUri.toString())}`; + const target = workspace.folderUri.scheme === Schemas.vscodeRemote + ? encodePath(workspace.folderUri.path) + : encodeURIComponent(workspace.folderUri.toString()); + targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${target}`; } // Workspace else if (isWorkspaceToOpen(workspace)) { - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${encodeURIComponent(workspace.workspaceUri.toString())}`; + const target = workspace.workspaceUri.scheme === Schemas.vscodeRemote + ? encodePath(workspace.workspaceUri.path) + : encodeURIComponent(workspace.workspaceUri.toString()); + targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${target}`; } // Append payload if any @@ -403,7 +417,6 @@ class WindowIndicator implements IWindowIndicator { } (function () { - // Find config by checking for DOM const configElement = document.getElementById('vscode-workbench-web-configuration'); const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; @@ -411,51 +424,28 @@ class WindowIndicator implements IWindowIndicator { throw new Error('Missing web configuration element'); } - const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute); + const config: IServerWorkbenchConstructionOptions = JSON.parse(configElementAttribute); + // const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = { + // webviewEndpoint: `${window.location.origin}${window.location.pathname.replace(/\/+$/, '')}/webview`, + // ...JSON.parse(configElementAttribute), + // }; + + // Strip the protocol from the authority if it exists. + const normalizeAuthority = (authority: string): string => authority.replace(/^https?:\/\//, ''); + if (config.remoteAuthority) { + (config as any).remoteAuthority = normalizeAuthority(config.remoteAuthority); + } + if (config.workspaceUri && config.workspaceUri.authority) { + config.workspaceUri.authority = normalizeAuthority(config.workspaceUri.authority); + } + if (config.folderUri && config.folderUri.authority) { + config.folderUri.authority = normalizeAuthority(config.folderUri.authority); + } // Find workspace to open and payload let foundWorkspace = false; let workspace: IWorkspace; - let payload = Object.create(null); - let logLevel: string | undefined = undefined; - - const query = new URL(document.location.href).searchParams; - query.forEach((value, key) => { - switch (key) { - - // Folder - case WorkspaceProvider.QUERY_PARAM_FOLDER: - workspace = { folderUri: URI.parse(value) }; - foundWorkspace = true; - break; - - // Workspace - case WorkspaceProvider.QUERY_PARAM_WORKSPACE: - workspace = { workspaceUri: URI.parse(value) }; - foundWorkspace = true; - break; - - // Empty - case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: - workspace = undefined; - foundWorkspace = true; - break; - - // Payload - case WorkspaceProvider.QUERY_PARAM_PAYLOAD: - try { - payload = JSON.parse(value); - } catch (error) { - console.error(error); // possible invalid JSON - } - break; - - // Log level - case 'logLevel': - logLevel = value; - break; - } - }); + let payload = config.workspaceProvider?.payload || Object.create(null); // If no workspace is provided through the URL, check for config attribute from server if (!foundWorkspace) { @@ -471,13 +461,6 @@ class WindowIndicator implements IWindowIndicator { // Workspace Provider const workspaceProvider = new WorkspaceProvider(workspace, payload); - // Home Indicator - const homeIndicator: IHomeIndicator = { - href: 'https://github.com/microsoft/vscode', - icon: 'code', - title: localize('home', "Home") - }; - // Window indicator (unless connected to a remote) let windowIndicator: WindowIndicator | undefined = undefined; if (!workspaceProvider.hasRemote()) { @@ -520,12 +503,7 @@ class WindowIndicator implements IWindowIndicator { // Finally create workbench create(document.body, { ...config, - developmentOptions: { - logLevel: logLevel ? parseLogLevel(logLevel) : undefined, - ...config.developmentOptions - }, settingsSyncOptions, - homeIndicator, windowIndicator, productQualityChangeHandler, workspaceProvider, diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index c3477d25bc751..791a55d86a9b4 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -8,11 +8,9 @@ import { app, dialog } from 'electron'; import { unlinkSync } from 'fs'; import { Promises as FSPromises } from 'vs/base/node/pfs'; import { localize } from 'vs/nls'; -import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; +import { isWindows, IProcessEnvironment } from 'vs/base/common/platform'; import { mark } from 'vs/base/common/performance'; import product from 'vs/platform/product/common/product'; -import { parseMainProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; -import { createWaitMarkerFile } from 'vs/platform/environment/node/wait'; import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { Server as NodeIPCServer, serve as nodeIPCServe, connect as nodeIPCConnect, XDG_RUNTIME_DIR } from 'vs/base/parts/ipc/node/ipc.net'; @@ -25,7 +23,6 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService, ConsoleMainLogger, MultiplexLogService, getLogLevel, ILoggerService } from 'vs/platform/log/common/log'; import { StateMainService } from 'vs/platform/state/electron-main/stateMainService'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; -import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; @@ -47,18 +44,17 @@ import { IFileService } from 'vs/platform/files/common/files'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/platform/remote/node/tunnelService'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IPathWithLineAndColumn, isValidBasename, parseLineAndColumnAware, sanitizeFilePath } from 'vs/base/common/extpath'; -import { rtrim, trim } from 'vs/base/common/strings'; -import { basename, join, resolve } from 'vs/base/common/path'; -import { coalesce, distinct } from 'vs/base/common/arrays'; +import { join } from 'vs/base/common/path'; +import { coalesce } from 'vs/base/common/arrays'; import { EnvironmentMainService, IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { LoggerService } from 'vs/platform/log/node/loggerService'; -import { cwd } from 'vs/base/common/process'; import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol'; import { ProtocolMainService } from 'vs/platform/protocol/electron-main/protocolMainService'; import { Promises } from 'vs/base/common/async'; +// eslint-disable-next-line code-import-patterns +import { ArgumentParser } from 'vs/platform/environment/argumentParser'; /** * The main VS Code entry point. @@ -67,9 +63,10 @@ import { Promises } from 'vs/base/common/async'; * running and a second instance is started from the command line. It will always * try to communicate with an existing instance to prevent that 2 VS Code instances * are running at the same time. + * + * @coder Moved argument parsing code to `ArgumentParser` for sharing with `CodeServer`. */ -class CodeMain { - +export class CodeMain extends ArgumentParser { main(): void { try { this.startup(); @@ -419,122 +416,7 @@ class CodeMain { lifecycleMainService.kill(exitCode); } - //#region Command line arguments utilities - - private resolveArgs(): NativeParsedArgs { - - // Parse arguments - const args = this.validatePaths(parseMainProcessArgv(process.argv)); - - // If we are started with --wait create a random temporary file - // and pass it over to the starting instance. We can use this file - // to wait for it to be deleted to monitor that the edited file - // is closed and then exit the waiting process. - // - // Note: we are not doing this if the wait marker has been already - // added as argument. This can happen if Code was started from CLI. - if (args.wait && !args.waitMarkerFilePath) { - const waitMarkerFilePath = createWaitMarkerFile(args.verbose); - if (waitMarkerFilePath) { - addArg(process.argv, '--waitMarkerFilePath', waitMarkerFilePath); - args.waitMarkerFilePath = waitMarkerFilePath; - } - } - - return args; - } - - private validatePaths(args: NativeParsedArgs): NativeParsedArgs { - - // Track URLs if they're going to be used - if (args['open-url']) { - args._urls = args._; - args._ = []; - } - - // Normalize paths and watch out for goto line mode - if (!args['remote']) { - const paths = this.doValidatePaths(args._, args.goto); - args._ = paths; - } - - return args; - } - - private doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { - const currentWorkingDir = cwd(); - const result = args.map(arg => { - let pathCandidate = String(arg); - - let parsedPath: IPathWithLineAndColumn | undefined = undefined; - if (gotoLineMode) { - parsedPath = parseLineAndColumnAware(pathCandidate); - pathCandidate = parsedPath.path; - } - - if (pathCandidate) { - pathCandidate = this.preparePath(currentWorkingDir, pathCandidate); - } - - const sanitizedFilePath = sanitizeFilePath(pathCandidate, currentWorkingDir); - - const filePathBasename = basename(sanitizedFilePath); - if (filePathBasename /* can be empty if code is opened on root */ && !isValidBasename(filePathBasename)) { - return null; // do not allow invalid file names - } - - if (gotoLineMode && parsedPath) { - parsedPath.path = sanitizedFilePath; - - return this.toPath(parsedPath); - } - - return sanitizedFilePath; - }); - - const caseInsensitive = isWindows || isMacintosh; - const distinctPaths = distinct(result, path => path && caseInsensitive ? path.toLowerCase() : (path || '')); - - return coalesce(distinctPaths); - } - - private preparePath(cwd: string, path: string): string { - - // Trim trailing quotes - if (isWindows) { - path = rtrim(path, '"'); // https://github.com/microsoft/vscode/issues/1498 - } - - // Trim whitespaces - path = trim(trim(path, ' '), '\t'); - - if (isWindows) { - - // Resolve the path against cwd if it is relative - path = resolve(cwd, path); - - // Trim trailing '.' chars on Windows to prevent invalid file names - path = rtrim(path, '.'); - } - - return path; - } - - private toPath(pathWithLineAndCol: IPathWithLineAndColumn): string { - const segments = [pathWithLineAndCol.path]; - - if (typeof pathWithLineAndCol.line === 'number') { - segments.push(String(pathWithLineAndCol.line)); - } - - if (typeof pathWithLineAndCol.column === 'number') { - segments.push(String(pathWithLineAndCol.column)); - } - - return segments.join(':'); - } - //#endregion } // Main Startup diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 9c7ad6268b6fb..642603996e2b2 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -54,6 +54,13 @@ export async function main(argv: string[]): Promise { console.log(buildVersionMessage(product.version, product.commit)); } + else if (args.server) { + const server = await new Promise((resolve, reject) => require(['vs/server/entry'], resolve, reject)); + await server.main(args); + + return; + } + // Extensions Management else if (shouldSpawnCliProcess(args)) { const cli = await new Promise((resolve, reject) => require(['vs/code/node/cliProcessMain'], resolve, reject)); diff --git a/src/vs/platform/environment/argumentParser.ts b/src/vs/platform/environment/argumentParser.ts new file mode 100644 index 0000000000000..0be59fa6e828f --- /dev/null +++ b/src/vs/platform/environment/argumentParser.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { addArg, parseMainProcessArgv } from 'vs/platform/environment/node/argvHelper'; +import { isWindows, isMacintosh } from 'vs/base/common/platform'; +import { createWaitMarkerFile } from 'vs/platform/environment/node/wait'; +import { IPathWithLineAndColumn, isValidBasename, parseLineAndColumnAware, sanitizeFilePath } from 'vs/base/common/extpath'; +import { rtrim, trim } from 'vs/base/common/strings'; +import { basename, resolve } from 'vs/base/common/path'; +import { cwd } from 'vs/base/common/process'; +import { coalesce, distinct } from 'vs/base/common/arrays'; + +/** + * Command line arg parser. + * + * @coder This is originally from `CodeMain` but has been separated for use in `CodeServer` + */ +export class ArgumentParser { + protected resolveArgs(): NativeParsedArgs { + + // Parse arguments + const args = this.validatePaths(parseMainProcessArgv(process.argv)); + + // If we are started with --wait create a random temporary file + // and pass it over to the starting instance. We can use this file + // to wait for it to be deleted to monitor that the edited file + // is closed and then exit the waiting process. + // + // Note: we are not doing this if the wait marker has been already + // added as argument. This can happen if Code was started from CLI. + if (args.wait && !args.waitMarkerFilePath) { + const waitMarkerFilePath = createWaitMarkerFile(args.verbose); + if (waitMarkerFilePath) { + addArg(process.argv, '--waitMarkerFilePath', waitMarkerFilePath); + args.waitMarkerFilePath = waitMarkerFilePath; + } + } + + return args; + } + + private validatePaths(args: NativeParsedArgs): NativeParsedArgs { + + // Track URLs if they're going to be used + if (args['open-url']) { + args._urls = args._; + args._ = []; + } + + // Normalize paths and watch out for goto line mode + if (!args['remote']) { + const paths = this.doValidatePaths(args._, args.goto); + args._ = paths; + } + + return args; + } + + private doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { + const currentWorkingDir = cwd(); + const result = args.map(arg => { + let pathCandidate = String(arg); + + let parsedPath: IPathWithLineAndColumn | undefined = undefined; + if (gotoLineMode) { + parsedPath = parseLineAndColumnAware(pathCandidate); + pathCandidate = parsedPath.path; + } + + if (pathCandidate) { + pathCandidate = this.preparePath(currentWorkingDir, pathCandidate); + } + + const sanitizedFilePath = sanitizeFilePath(pathCandidate, currentWorkingDir); + + const filePathBasename = basename(sanitizedFilePath); + if (filePathBasename /* can be empty if code is opened on root */ && !isValidBasename(filePathBasename)) { + return null; // do not allow invalid file names + } + + if (gotoLineMode && parsedPath) { + parsedPath.path = sanitizedFilePath; + + return this.toPath(parsedPath); + } + + return sanitizedFilePath; + }); + + const caseInsensitive = isWindows || isMacintosh; + const distinctPaths = distinct(result, path => path && caseInsensitive ? path.toLowerCase() : (path || '')); + + return coalesce(distinctPaths); + } + + private preparePath(cwd: string, path: string): string { + + // Trim trailing quotes + if (isWindows) { + path = rtrim(path, '"'); // https://github.com/microsoft/vscode/issues/1498 + } + + // Trim whitespaces + path = trim(trim(path, ' '), '\t'); + + if (isWindows) { + + // Resolve the path against cwd if it is relative + path = resolve(cwd, path); + + // Trim trailing '.' chars on Windows to prevent invalid file names + path = rtrim(path, '.'); + } + + return path; + } + + private toPath(pathWithLineAndCol: IPathWithLineAndColumn): string { + const segments = [pathWithLineAndCol.path]; + + if (typeof pathWithLineAndCol.line === 'number') { + segments.push(String(pathWithLineAndCol.line)); + } + + if (typeof pathWithLineAndCol.column === 'number') { + segments.push(String(pathWithLineAndCol.column)); + } + + return segments.join(':'); + } +} diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 70a7eb4b86e41..a09cd6a72e3ae 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -40,6 +40,12 @@ export interface NativeParsedArgs { 'extensions-dir'?: string; 'extensions-download-dir'?: string; 'builtin-extensions-dir'?: string; + /** @coder: BEGIN */ + 'extra-extensions-dir'?: string[]; + 'extra-builtin-extensions-dir'?: string[]; + 'ignore-last-opened'?: string, + 'server'?: string, + /** @coder: END */ extensionDevelopmentPath?: string[]; // undefined or array of 1 or more local paths or URIs extensionTestsPath?: string; // either a local path or a URI extensionDevelopmentKind?: string[]; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 77efaac3fbaff..f5d5fd956db88 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -124,6 +124,9 @@ export interface INativeEnvironmentService extends IEnvironmentService { extensionsPath: string; extensionsDownloadPath: string; builtinExtensionsPath: string; + // NOTE@coder: add extraExtensionPaths/extraBuiltinExtensionPaths + extraExtensionPaths: string[]; + extraBuiltinExtensionPaths: string[]; // --- smoke test support driverHandle?: string; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index 70e06765c6970..03650810378f0 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -15,6 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionKind } from 'vs/platform/extensions/common/extensions'; import { env } from 'vs/base/common/process'; + export interface INativeEnvironmentPaths { /** @@ -157,6 +158,19 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron return joinPath(this.userHome, this.productService.dataFolderName, 'extensions').fsPath; } + /** + * NOTE@coder: add extraExtensionPaths and extraBuiltinExtensionPaths + */ + @memoize + get extraExtensionPaths(): string[] { + return (this._args['extra-extensions-dir'] || []).map((p) => resolve(p)); + } + + @memoize + get extraBuiltinExtensionPaths(): string[] { + return (this._args['extra-builtin-extensions-dir'] || []).map((p) => resolve(p)); + } + @memoize get extensionDevelopmentLocationURI(): URI[] | undefined { const extensionDevelopmentPaths = this.args.extensionDevelopmentPath; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 2715617e2bc92..169e56652effa 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -51,7 +51,13 @@ export const OPTIONS: OptionDescriptions> = { 'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, 'extensions-download-dir': { type: 'string' }, + /** @coder: BEGIN */ 'builtin-extensions-dir': { type: 'string' }, + 'extra-extensions-dir': { type: 'string[]', cat: 'o', description: localize('extra-extensions-dir', 'Path to an extra user extension directory.') }, + 'extra-builtin-extensions-dir': { type: 'string[]', cat: 'o', description: localize('extra-builtin-extensions-dir', 'Path to an extra builtin extension directory.') }, + 'ignore-last-opened': { type: 'string', cat: 'o', description: localize('ignore-last-opened', "Ignore last opened.") }, + 'server': { type: 'string', cat: 'o', description: localize('server', "Run a remote server e.g. http://localhost:8080") }, + /** @coder: END */ 'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, 'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extensions.") }, 'category': { type: 'string', cat: 'e', description: localize('category', "Filters installed extensions by provided category, when using --list-extensions."), args: 'category' }, @@ -78,7 +84,7 @@ export const OPTIONS: OptionDescriptions> = { 'max-memory': { type: 'string', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes)."), args: 'memory' }, 'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, - 'remote': { type: 'string' }, + 'remote': { type: 'string', cat: 'o', description: localize('remote', "Remote authority e.g. http://localhost:8080") }, 'folder-uri': { type: 'string[]', cat: 'o', args: 'uri' }, 'file-uri': { type: 'string[]', cat: 'o', args: 'uri' }, diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts index 052919c84798a..120386e3e7302 100644 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ b/src/vs/platform/extensionManagement/node/extensionsScanner.ts @@ -97,7 +97,7 @@ export class ExtensionsScanner extends Disposable { } async scanAllUserExtensions(): Promise { - return this.scanExtensionsInDir(this.extensionsPath, ExtensionType.User); + return this.scanExtensionsInDirs([this.extensionsPath, ...this.environmentService.extraExtensionPaths], ExtensionType.User); } async extractUserExtension(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, token: CancellationToken): Promise { @@ -273,6 +273,7 @@ export class ExtensionsScanner extends Disposable { return [...systemExtensions, ...devSystemExtensions]; } + private async scanExtensionsInDir(dir: string, type: ExtensionType): Promise { const limiter = new Limiter(10); const stat = await this.fileService.resolve(URI.file(dir)); @@ -313,7 +314,7 @@ export class ExtensionsScanner extends Disposable { } private async scanDefaultSystemExtensions(): Promise { - const result = await this.scanExtensionsInDir(this.systemExtensionsPath, ExtensionType.System); + const result = await this.scanExtensionsInDirs([this.systemExtensionsPath, ...this.environmentService.extraBuiltinExtensionPaths], ExtensionType.System); this.logService.trace('Scanned system extensions:', result.length); return result; } @@ -417,4 +418,9 @@ export class ExtensionsScanner extends Disposable { } }); } + + private async scanExtensionsInDirs(dirs: string[], type: ExtensionType): Promise { + const results = await Promise.all(dirs.map((path) => this.scanExtensionsInDir(path, type))); + return results.reduce((flat, current) => flat.concat(current), []); + } } diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index a57db48bc31d4..9a6caa8e2f2e4 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -14,7 +14,9 @@ async function createSpdLogLogger(name: string, logfilePath: string, filesize: n _spdlog.setFlushOn(LogLevel.Trace); return _spdlog.createAsyncRotatingLogger(name, logfilePath, filesize, filecount); } catch (e) { - console.error(e); + // console.error(e); + // TODO@coder enable + console.error('generic log failure'); } return null; } diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 1eafa991bd947..c41a9f0337ae3 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -73,6 +73,13 @@ else { ], }); } + + // NOTE@coder: Add the ability to inject settings from the server. + const el = document.getElementById('vscode-remote-product-configuration'); + const rawProductConfiguration = el && el.getAttribute('data-settings'); + if (rawProductConfiguration) { + Object.assign(product, JSON.parse(rawProductConfiguration)); + } } export default product; diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts index abf7428a8ff60..d83dc184c1e95 100644 --- a/src/vs/platform/remote/browser/browserSocketFactory.ts +++ b/src/vs/platform/remote/browser/browserSocketFactory.ts @@ -241,6 +241,8 @@ export class BrowserSocketFactory implements ISocketFactory { connect(host: string, port: number, query: string, callback: IConnectCallback): void { const socket = this._webSocketFactory.create(`ws://${/:/.test(host) ? `[${host}]` : host}:${port}/?${query}&skipWebSocketFrames=false`); + // // NOTE@coder: Modified to work against the current path. + // const socket = this._webSocketFactory.create(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}${window.location.pathname}?${query}&skipWebSocketFrames=false`); const errorListener = socket.onError((err) => callback(err, undefined)); socket.onOpen(() => { errorListener.dispose(); @@ -248,6 +250,3 @@ export class BrowserSocketFactory implements ISocketFactory { }); } } - - - diff --git a/src/vs/platform/remote/common/remoteAgentConnection.ts b/src/vs/platform/remote/common/remoteAgentConnection.ts index e6e9a607fbfe2..e261f9f546ad1 100644 --- a/src/vs/platform/remote/common/remoteAgentConnection.ts +++ b/src/vs/platform/remote/common/remoteAgentConnection.ts @@ -25,7 +25,10 @@ export const enum ConnectionType { Tunnel = 3, } -function connectionTypeToString(connectionType: ConnectionType): string { +/** + * @coder exported for server side use. + */ +export function connectionTypeToString(connectionType: ConnectionType): string { switch (connectionType) { case ConnectionType.Management: return 'Management'; @@ -63,7 +66,17 @@ export interface OKMessage { type: 'ok'; } -export type HandshakeMessage = AuthRequest | SignRequest | ConnectionTypeRequest | ErrorMessage | OKMessage; +/** + * Expected response from `CodeServer` connection. + * @coder Moved from inline type for use outside this module. + */ +export interface DebugMessage { + type: 'debug'; + debugPort?: NonNullable; +} + + +export type HandshakeMessage = AuthRequest | SignRequest | ConnectionTypeRequest | ErrorMessage | OKMessage | DebugMessage; interface ISimpleConnectionOptions { @@ -225,9 +238,10 @@ function raceWithTimeoutCancellation(promise: Promise, timeoutCancellation async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptions, connectionType: ConnectionType, args: any | undefined, timeoutCancellationToken: CancellationToken): Promise<{ protocol: PersistentProtocol; ownsProtocol: boolean; }> { const logPrefix = connectLogPrefix(options, connectionType); - options.logService.trace(`${logPrefix} 1/6. invoking socketFactory.connect().`); + options.logService.info(`${logPrefix} 1/6. invoking socketFactory.connect().`); let socket: ISocket; + try { socket = await createSocket(options.logService, options.socketFactory, options.host, options.port, `reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`, timeoutCancellationToken); } catch (error) { @@ -236,7 +250,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio throw error; } - options.logService.trace(`${logPrefix} 2/6. socketFactory.connect() was successful.`); + options.logService.info(`${logPrefix} 2/6. socketFactory.connect() was successful.`); let protocol: PersistentProtocol; let ownsProtocol: boolean; @@ -249,7 +263,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio ownsProtocol = true; } - options.logService.trace(`${logPrefix} 3/6. sending AuthRequest control message.`); + options.logService.info(`${logPrefix} 3/6. sending AuthRequest control message.`); const authRequest: AuthRequest = { type: 'auth', auth: options.connectionToken || '00000000000000000000' @@ -265,7 +279,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio throw error; } - options.logService.trace(`${logPrefix} 4/6. received SignRequest control message.`); + options.logService.info(`${logPrefix} 4/6. received SignRequest control message.`); const signed = await raceWithTimeoutCancellation(options.signService.sign(msg.data), timeoutCancellationToken); const connTypeRequest: ConnectionTypeRequest = { @@ -278,7 +292,7 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio connTypeRequest.args = args; } - options.logService.trace(`${logPrefix} 5/6. sending ConnectionTypeRequest control message.`); + options.logService.info(`${logPrefix} 5/6. sending ConnectionTypeRequest control message.`); protocol.sendControl(VSBuffer.fromString(JSON.stringify(connTypeRequest))); return { protocol, ownsProtocol }; @@ -322,7 +336,7 @@ async function connectToRemoteExtensionHostAgentAndReadOneMessage(options: IS if (options.reconnectionProtocol) { options.reconnectionProtocol.endAcceptReconnection(); } - options.logService.trace(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`); + options.logService.info(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`); result.resolve({ protocol, firstMessage: msg }); } })); @@ -348,7 +362,9 @@ interface IExtensionHostConnectionResult { } async function doConnectRemoteAgentExtensionHost(options: ISimpleConnectionOptions, startArguments: IRemoteExtensionHostStartParams, timeoutCancellationToken: CancellationToken): Promise { - const { protocol, firstMessage } = await connectToRemoteExtensionHostAgentAndReadOneMessage<{ debugPort?: number; }>(options, ConnectionType.ExtensionHost, startArguments, timeoutCancellationToken); + console.log('>>> READING ONE MESSAGE', options, startArguments); + const { protocol, firstMessage } = await connectToRemoteExtensionHostAgentAndReadOneMessage(options, ConnectionType.ExtensionHost, startArguments, timeoutCancellationToken); + console.log('>>> READ ONE MESSAGE', JSON.stringify(firstMessage)); const debugPort = firstMessage && firstMessage.debugPort; return { protocol, debugPort }; } @@ -361,7 +377,7 @@ async function doConnectRemoteAgentTunnel(options: ISimpleConnectionOptions, sta const startTime = Date.now(); const logPrefix = connectLogPrefix(options, ConnectionType.Tunnel); const { protocol } = await connectToRemoteExtensionHostAgent(options, ConnectionType.Tunnel, startParams, timeoutCancellationToken); - options.logService.trace(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`); + options.logService.info(`${logPrefix} 6/6. handshake finished, connection is up and running after ${logElapsed(startTime)}!`); return protocol; } @@ -551,7 +567,7 @@ abstract class PersistentConnection extends Disposable { })); this._register(protocol.onSocketTimeout(() => { const logPrefix = commonLogPrefix(this._connectionType, this.reconnectionToken, true); - this._options.logService.trace(`${logPrefix} received socket timeout event.`); + this._options.logService.info(`${logPrefix} received socket timeout event.`); this._beginReconnecting(); })); @@ -630,19 +646,19 @@ abstract class PersistentConnection extends Disposable { } if (RemoteAuthorityResolverError.isTemporarilyNotAvailable(err)) { this._options.logService.info(`${logPrefix} A temporarily not available error occurred while trying to reconnect, will try again...`); - this._options.logService.trace(err); + this._options.logService.info(err); // try again! continue; } if ((err.code === 'ETIMEDOUT' || err.code === 'ENETUNREACH' || err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') && err.syscall === 'connect') { this._options.logService.info(`${logPrefix} A network error occurred while trying to reconnect, will try again...`); - this._options.logService.trace(err); + this._options.logService.info(err); // try again! continue; } if (isPromiseCanceledError(err)) { this._options.logService.info(`${logPrefix} A promise cancelation error occurred while trying to reconnect, will try again...`); - this._options.logService.trace(err); + this._options.logService.info(err); // try again! continue; } diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 939b3a436439c..697af9adadfa4 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -105,8 +105,10 @@ export interface IStorageService { * * @param target allows to define the target of the storage operation * to either the current machine or user. + * + * NOTE@coder: Add a promise so extensions can await storage writes. */ - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void; + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): Promise | void; /** * Delete an element stored under the provided key from storage. @@ -333,46 +335,49 @@ export abstract class AbstractStorageService extends Disposable implements IStor return this.getStorage(scope)?.getNumber(key, fallbackValue); } - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void { + // NOTE@coder: Make a promise so extensions can await storage writes. + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): Promise | void { // We remove the key for undefined/null values if (isUndefinedOrNull(value)) { this.remove(key, scope); - return; + return Promise.resolve(); } // Update our datastructures but send events only after - this.withPausedEmitters(() => { + return this.withPausedEmitters(() => { // Update key-target map this.updateKeyTarget(key, scope, target); // Store actual value - this.getStorage(scope)?.set(key, value); + return this.getStorage(scope)?.set(key, value); }); } - remove(key: string, scope: StorageScope): void { + // NOTE@coder: Make a promise so extensions can await the storage write. + remove(key: string, scope: StorageScope): Promise | void { // Update our datastructures but send events only after - this.withPausedEmitters(() => { + return this.withPausedEmitters(() => { // Update key-target map this.updateKeyTarget(key, scope, undefined); // Remove actual key - this.getStorage(scope)?.delete(key); + return this.getStorage(scope)?.delete(key); }); } - private withPausedEmitters(fn: Function): void { + // NOTE@coder: Return the function's return so extensions can await the storage write. + private withPausedEmitters(fn: () => T): T { // Pause emitters this._onDidChangeValue.pause(); this._onDidChangeTarget.pause(); try { - fn(); + return fn(); } finally { // Resume emitters diff --git a/src/vs/server/channel.ts b/src/vs/server/channel.ts new file mode 100644 index 0000000000000..f9bb92ef8daff --- /dev/null +++ b/src/vs/server/channel.ts @@ -0,0 +1,564 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as os from 'os'; +import * as path from 'path'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import * as resources from 'vs/base/common/resources'; +import { ReadableStreamEventPayload } from 'vs/base/common/stream'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { transformOutgoingURIs } from 'vs/base/common/uriIpc'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileReadStreamOptions, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; +import { ConsoleLogger, ILogService } from 'vs/platform/log/common/log'; +import product from 'vs/platform/product/common/product'; +import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/platform/terminal/common/terminal'; +import { getTranslations } from 'vs/server/nls'; +import { IFileChangeDto } from 'vs/workbench/api/common/extHost.protocol'; +import { IEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; +import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; +import { deserializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; +import * as terminal from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; +import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; +import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver'; +import { ExtensionScanner, ExtensionScannerInput } from 'vs/workbench/services/extensions/node/extensionPoints'; +import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; +import { createServerURITransformer } from 'vs/base/common/uriServer'; + +const logger = new ConsoleLogger(); + +/** + * Extend the file provider to allow unwatching. + */ +class Watcher extends DiskFileSystemProvider { + public readonly watches = new Map(); + + public override dispose(): void { + this.watches.forEach((w) => w.dispose()); + this.watches.clear(); + super.dispose(); + } + + public _watch(req: number, resource: URI, opts: IWatchOptions): void { + this.watches.set(req, this.watch(resource, opts)); + } + + public unwatch(req: number): void { + this.watches.get(req)!.dispose(); + this.watches.delete(req); + } +} + +export class FileProviderChannel implements IServerChannel, IDisposable { + private readonly provider: DiskFileSystemProvider; + private readonly watchers = new Map(); + + public constructor( + private readonly environmentService: INativeEnvironmentService, + private readonly logService: ILogService, + ) { + this.provider = new DiskFileSystemProvider(this.logService); + } + + public listen(context: RemoteAgentConnectionContext, event: string, args?: any): Event { + switch (event) { + case 'filechange': return this.filechange(context, args[0]); + case 'readFileStream': return this.readFileStream(args[0], args[1]); + } + + throw new Error(`Invalid listen '${event}'`); + } + + private filechange(context: RemoteAgentConnectionContext, session: string): Event { + const emitter = new Emitter({ + onFirstListenerAdd: () => { + const provider = new Watcher(this.logService); + this.watchers.set(session, provider); + const transformer = createServerURITransformer(context.remoteAuthority); + provider.onDidChangeFile((events) => { + emitter.fire(events.map((event) => ({ + ...event, + resource: transformer.transformOutgoing(event.resource), + }))); + }); + provider.onDidErrorOccur((event) => this.logService.error(event)); + }, + onLastListenerRemove: () => { + this.watchers.get(session)!.dispose(); + this.watchers.delete(session); + }, + }); + + return emitter.event; + } + + private readFileStream(resource: UriComponents, opts: FileReadStreamOptions): Event> { + const cts = new CancellationTokenSource(); + const fileStream = this.provider.readFileStream(this.transform(resource), opts, cts.token); + const emitter = new Emitter>({ + onFirstListenerAdd: () => { + fileStream.on('data', (data) => emitter.fire(VSBuffer.wrap(data))); + fileStream.on('error', (error) => emitter.fire(error)); + fileStream.on('end', () => emitter.fire('end')); + }, + onLastListenerRemove: () => cts.cancel(), + }); + + return emitter.event; + } + + public call(_: unknown, command: string, args?: any): Promise { + switch (command) { + case 'stat': return this.stat(args[0]); + case 'open': return this.open(args[0], args[1]); + case 'close': return this.close(args[0]); + case 'read': return this.read(args[0], args[1], args[2]); + case 'readFile': return this.readFile(args[0]); + case 'write': return this.write(args[0], args[1], args[2], args[3], args[4]); + case 'writeFile': return this.writeFile(args[0], args[1], args[2]); + case 'delete': return this.delete(args[0], args[1]); + case 'mkdir': return this.mkdir(args[0]); + case 'readdir': return this.readdir(args[0]); + case 'rename': return this.rename(args[0], args[1], args[2]); + case 'copy': return this.copy(args[0], args[1], args[2]); + case 'watch': return this.watch(args[0], args[1], args[2], args[3]); + case 'unwatch': return this.unwatch(args[0], args[1]); + } + + throw new Error(`Invalid call '${command}'`); + } + + public dispose(): void { + this.watchers.forEach((w) => w.dispose()); + this.watchers.clear(); + } + + private async stat(resource: UriComponents): Promise { + return this.provider.stat(this.transform(resource)); + } + + private async open(resource: UriComponents, opts: FileOpenOptions): Promise { + return this.provider.open(this.transform(resource), opts); + } + + private async close(fd: number): Promise { + return this.provider.close(fd); + } + + private async read(fd: number, pos: number, length: number): Promise<[VSBuffer, number]> { + const buffer = VSBuffer.alloc(length); + const bytesRead = await this.provider.read(fd, pos, buffer.buffer, 0, length); + return [buffer, bytesRead]; + } + + private async readFile(resource: UriComponents): Promise { + return VSBuffer.wrap(await this.provider.readFile(this.transform(resource))); + } + + private write(fd: number, pos: number, buffer: VSBuffer, offset: number, length: number): Promise { + return this.provider.write(fd, pos, buffer.buffer, offset, length); + } + + private writeFile(resource: UriComponents, buffer: VSBuffer, opts: FileWriteOptions): Promise { + return this.provider.writeFile(this.transform(resource), buffer.buffer, opts); + } + + private async delete(resource: UriComponents, opts: FileDeleteOptions): Promise { + return this.provider.delete(this.transform(resource), opts); + } + + private async mkdir(resource: UriComponents): Promise { + return this.provider.mkdir(this.transform(resource)); + } + + private async readdir(resource: UriComponents): Promise<[string, FileType][]> { + return this.provider.readdir(this.transform(resource)); + } + + private async rename(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { + return this.provider.rename(this.transform(resource), URI.from(target), opts); + } + + private copy(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise { + return this.provider.copy(this.transform(resource), URI.from(target), opts); + } + + private async watch(session: string, req: number, resource: UriComponents, opts: IWatchOptions): Promise { + this.watchers.get(session)!._watch(req, this.transform(resource), opts); + } + + private async unwatch(session: string, req: number): Promise { + this.watchers.get(session)!.unwatch(req); + } + + private transform(resource: UriComponents): URI { + // Used for walkthrough content. + if (/^\/static[^/]*\//.test(resource.path)) { + return URI.file(this.environmentService.appRoot + resource.path.replace(/^\/static[^/]*\//, '/')); + // Used by the webview service worker to load resources. + } else if (resource.path === '/vscode-resource' && resource.query) { + try { + const query = JSON.parse(resource.query); + if (query.requestResourcePath) { + return URI.file(query.requestResourcePath); + } + } catch (error) { /* Carry on. */ } + } + return URI.from(resource); + } +} + +// See ../../workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +export class ExtensionEnvironmentChannel implements IServerChannel { + public constructor( + private readonly environment: INativeEnvironmentService, + private readonly log: ILogService, + private readonly telemetry: ITelemetryService, + private readonly connectionToken: string, + ) { } + + public listen(_: unknown, event: string): Event { + throw new Error(`Invalid listen '${event}'`); + } + + public async call(context: any, command: string, args: any): Promise { + switch (command) { + case 'getEnvironmentData': + return transformOutgoingURIs( + await this.getEnvironmentData(), + createServerURITransformer(context.remoteAuthority), + ); + case 'scanExtensions': + return transformOutgoingURIs( + await this.scanExtensions(args.language), + createServerURITransformer(context.remoteAuthority), + ); + case 'getDiagnosticInfo': return this.getDiagnosticInfo(); + case 'disableTelemetry': return this.disableTelemetry(); + case 'logTelemetry': return this.logTelemetry(args.eventName, args.data); + case 'flushTelemetry': return this.flushTelemetry(); + } + throw new Error(`Invalid call '${command}'`); + } + + private async getEnvironmentData(): Promise { + return { + pid: process.pid, + connectionToken: this.connectionToken, + appRoot: URI.file(this.environment.appRoot), + settingsPath: this.environment.settingsResource, + logsPath: URI.file(this.environment.logsPath), + extensionsPath: URI.file(this.environment.extensionsPath!), + extensionHostLogsPath: URI.file(path.join(this.environment.logsPath, 'extension-host')), + globalStorageHome: this.environment.globalStorageHome, + workspaceStorageHome: this.environment.workspaceStorageHome, + userHome: this.environment.userHome, + useHostProxy: false, + os: platform.OS, + marks: [] + }; + } + + private async scanExtensions(language: string): Promise { + const translations = await getTranslations(language, this.environment.userDataPath); + + const scanMultiple = (isBuiltin: boolean, isUnderDevelopment: boolean, paths: string[]): Promise => { + return Promise.all(paths.map((path) => { + return ExtensionScanner.scanExtensions(new ExtensionScannerInput( + product.version, + product.date, + product.commit, + language, + !!process.env.VSCODE_DEV, + path, + isBuiltin, + isUnderDevelopment, + translations, + ), this.log); + })); + }; + + const scanBuiltin = async (): Promise => { + return scanMultiple(true, false, [this.environment.builtinExtensionsPath, ...this.environment.extraBuiltinExtensionPaths]); + }; + + const scanInstalled = async (): Promise => { + return scanMultiple(false, true, [this.environment.extensionsPath!, ...this.environment.extraExtensionPaths]); + }; + + return Promise.all([scanBuiltin(), scanInstalled()]).then((allExtensions) => { + const uniqueExtensions = new Map(); + allExtensions.forEach((multipleExtensions) => { + multipleExtensions.forEach((extensions) => { + extensions.forEach((extension) => { + const id = ExtensionIdentifier.toKey(extension.identifier); + if (uniqueExtensions.has(id)) { + const oldPath = uniqueExtensions.get(id)!.extensionLocation.fsPath; + const newPath = extension.extensionLocation.fsPath; + this.log.warn(`${oldPath} has been overridden ${newPath}`); + } + uniqueExtensions.set(id, extension); + }); + }); + }); + return Array.from(uniqueExtensions.values()); + }); + } + + private getDiagnosticInfo(): Promise { + throw new Error('not implemented'); + } + + private async disableTelemetry(): Promise { + this.telemetry.setEnabled(false); + } + + private async logTelemetry(eventName: string, data: ITelemetryData): Promise { + this.telemetry.publicLog(eventName, data); + } + + private async flushTelemetry(): Promise { + // We always send immediately at the moment. + } +} + +// Reference: - ../../workbench/api/common/extHostDebugService.ts +class VariableResolverService extends AbstractVariableResolverService { + constructor( + remoteAuthority: string, + args: terminal.ICreateTerminalProcessArguments, + env: platform.IProcessEnvironment, + ) { + super({ + getFolderUri: (name: string): URI | undefined => { + const folder = args.workspaceFolders.find((f) => f.name === name); + return folder && URI.revive(folder.uri); + }, + getWorkspaceFolderCount: (): number => { + return args.workspaceFolders.length; + }, + // In ../../workbench/contrib/terminal/common/remoteTerminalChannel.ts it + // looks like there are `config:` entries which must be for this? Not sure + // how/if the URI comes into play though. + getConfigurationValue: (_: URI, section: string): string | undefined => { + return args.resolvedVariables[`config:${section}`]; + }, + getAppRoot: (): string | undefined => { + return (args.resolverEnv && args.resolverEnv['VSCODE_CWD']) || env['VSCODE_CWD'] || process.cwd(); + }, + getExecPath: (): string | undefined => { + // Assuming that resolverEnv is just for use in the resolver and not for + // the terminal itself. + return (args.resolverEnv && args.resolverEnv['VSCODE_EXEC_PATH']) || env['VSCODE_EXEC_PATH']; + }, + // This is just a guess; this is the only file-related thing we're sent + // and none of these resolver methods seem to get called so I don't know + // how to test. + getFilePath: (): string | undefined => { + const resource = transformIncoming(remoteAuthority, args.activeFileResource); + if (!resource) { + return undefined; + } + // See ../../editor/standalone/browser/simpleServices.ts; + // `BaseConfigurationResolverService` calls `getUriLabel` from there. + if (resource.scheme === 'file') { + return resource.fsPath; + } + return resource.path; + }, + // It looks like these are set here although they aren't on the types: + // ../../workbench/contrib/terminal/common/remoteTerminalChannel.ts + getSelectedText: (): string | undefined => { + return args.resolvedVariables.selectedText; + }, + getLineNumber: (): string | undefined => { + return args.resolvedVariables.selectedText; + }, + }, undefined, Promise.resolve(env)); + } +} + +export class TerminalProviderChannel implements IServerChannel, IDisposable { + public constructor( + private readonly logService: ILogService, + private readonly ptyService: PtyHostService, + ) { } + + public listen(_: RemoteAgentConnectionContext, event: string, args: any): Event { + logger.trace('TerminalProviderChannel:listen', event, args); + + switch (event) { + case '$onPtyHostExitEvent': return this.ptyService.onPtyHostExit || Event.None; + case '$onPtyHostStartEvent': return this.ptyService.onPtyHostStart || Event.None; + case '$onPtyHostUnresponsiveEvent': return this.ptyService.onPtyHostUnresponsive || Event.None; + case '$onPtyHostResponsiveEvent': return this.ptyService.onPtyHostResponsive || Event.None; + case '$onPtyHostRequestResolveVariablesEvent': return this.ptyService.onPtyHostRequestResolveVariables || Event.None; + case '$onProcessDataEvent': return this.ptyService.onProcessData; + case '$onProcessExitEvent': return this.ptyService.onProcessExit; + case '$onProcessReadyEvent': return this.ptyService.onProcessReady; + case '$onProcessReplayEvent': return this.ptyService.onProcessReplay; + case '$onProcessTitleChangedEvent': return this.ptyService.onProcessTitleChanged; + case '$onProcessShellTypeChangedEvent': return this.ptyService.onProcessShellTypeChanged; + case '$onProcessOverrideDimensionsEvent': return this.ptyService.onProcessOverrideDimensions; + case '$onProcessResolvedShellLaunchConfigEvent': return this.ptyService.onProcessResolvedShellLaunchConfig; + case '$onProcessOrphanQuestion': return this.ptyService.onProcessOrphanQuestion; + // NOTE@asher: I think this must have something to do with running + // commands on the terminal that will do things in VS Code but we + // already have that functionality via a socket so I'm not sure what + // this is for. + // NOTE: VSCODE_IPC_HOOK_CLI is now missing, perhaps this is meant to + // replace that in some way. + case '$onExecuteCommand': return Event.None; + } + + throw new Error(`Invalid listen '${event}'`); + } + + public call(context: RemoteAgentConnectionContext, command: string, args: any): Promise { + logger.trace('TerminalProviderChannel:call', command, args); + + switch (command) { + case '$restartPtyHost': return this.ptyService.restartPtyHost(); + case '$createProcess': return this.createProcess(context.remoteAuthority, args); + case '$attachToProcess': return this.ptyService.attachToProcess(args[0]); + case '$start': return this.ptyService.start(args[0]); + case '$input': return this.ptyService.input(args[0], args[1]); + case '$acknowledgeDataEvent': return this.ptyService.acknowledgeDataEvent(args[0], args[1]); + case '$shutdown': return this.ptyService.shutdown(args[0], args[1]); + case '$resize': return this.ptyService.resize(args[0], args[1], args[2]); + case '$getInitialCwd': return this.ptyService.getInitialCwd(args[0]); + case '$getCwd': return this.ptyService.getCwd(args[0]); + case '$sendCommandResult': return this.sendCommandResult(args[0], args[1], args[2], args[3]); + case '$orphanQuestionReply': return this.ptyService.orphanQuestionReply(args[0]); + case '$listProcesses': return this.ptyService.listProcesses(); + case '$setTerminalLayoutInfo': return this.ptyService.setTerminalLayoutInfo(args); + case '$getTerminalLayoutInfo': return this.ptyService.getTerminalLayoutInfo(args); + case '$getEnvironment': return this.ptyService.getEnvironment(); + case '$getDefaultSystemShell': return this.ptyService.getDefaultSystemShell(args[0]); + case '$reduceConnectionGraceTime': return this.ptyService.reduceConnectionGraceTime(); + case '$updateTitle': return this.ptyService.updateTitle(args[0], args[1], args[2]); + case '$getProfiles': return this.ptyService.getProfiles(args[0], args[1], args[2]); + case '$acceptPtyHostResolvedVariables': return this.ptyService.acceptPtyHostResolvedVariables(args[0], args[1]); + } + + throw new Error(`Invalid call '${command}'`); + } + + public async dispose(): Promise { + // Nothing at the moment. + } + + // References: - ../../workbench/api/node/extHostTerminalService.ts + // - ../../workbench/contrib/terminal/browser/terminalProcessManager.ts + private async createProcess(remoteAuthority: string, args: terminal.ICreateTerminalProcessArguments): Promise { + const shellLaunchConfig: IShellLaunchConfig = { + name: args.shellLaunchConfig.name, + executable: args.shellLaunchConfig.executable, + args: args.shellLaunchConfig.args, + // TODO: Should we transform if it's a string as well? The incoming + // transform only takes `UriComponents` so I suspect it's not necessary. + cwd: typeof args.shellLaunchConfig.cwd !== 'string' + ? transformIncoming(remoteAuthority, args.shellLaunchConfig.cwd) + : args.shellLaunchConfig.cwd, + env: args.shellLaunchConfig.env, + }; + + const activeWorkspaceUri = transformIncoming(remoteAuthority, args.activeWorkspaceFolder?.uri); + const activeWorkspace = activeWorkspaceUri && args.activeWorkspaceFolder ? { + ...args.activeWorkspaceFolder, + uri: activeWorkspaceUri, + toResource: (relativePath: string) => resources.joinPath(activeWorkspaceUri, relativePath), + } : undefined; + + const resolverService = new VariableResolverService(remoteAuthority, args, process.env); + const resolver = terminalEnvironment.createVariableResolver(activeWorkspace, process.env, resolverService); + + shellLaunchConfig.cwd = terminalEnvironment.getCwd( + shellLaunchConfig, + os.homedir(), + resolver, + activeWorkspaceUri, + args.configuration['terminal.integrated.cwd'], + this.logService, + ); + + // Use instead of `terminal.integrated.env.${platform}` to make types work. + const getEnvFromConfig = (): ITerminalEnvironment => { + if (platform.isWindows) { + return args.configuration['terminal.integrated.env.windows']; + } else if (platform.isMacintosh) { + return args.configuration['terminal.integrated.env.osx']; + } + return args.configuration['terminal.integrated.env.linux']; + }; + + // ptyHostService calls getEnvironment in the ptyHost process it creates, + // which uses that process's environment. The process spawned doesn't have + // VSCODE_IPC_HOOK_CLI in its env, so we add it here. + const getEnvironment = async (): Promise => { + const env = await this.ptyService.getEnvironment(); + env.VSCODE_IPC_HOOK_CLI = process.env['VSCODE_IPC_HOOK_CLI']!; + return env; + }; + + const env = terminalEnvironment.createTerminalEnvironment( + shellLaunchConfig, + getEnvFromConfig(), + resolver, + product.version, + args.configuration['terminal.integrated.detectLocale'], + await getEnvironment() + ); + + // Apply extension environment variable collections to the environment. + if (!shellLaunchConfig.strictEnv) { + // They come in an array and in serialized format. + const envVariableCollections = new Map(); + for (const [k, v] of args.envVariableCollections) { + envVariableCollections.set(k, { map: deserializeEnvironmentVariableCollection(v) }); + } + const mergedCollection = new MergedEnvironmentVariableCollection(envVariableCollections); + mergedCollection.applyToProcessEnvironment(env); + } + + const persistentTerminalId = await this.ptyService.createProcess( + shellLaunchConfig, + shellLaunchConfig.cwd, + args.cols, + args.rows, + env, + process.env as platform.IProcessEnvironment, // Environment used for findExecutable + false, // windowsEnableConpty + args.shouldPersistTerminal, + args.workspaceId, + args.workspaceName, + ); + + return { + persistentTerminalId, + resolvedShellLaunchConfig: shellLaunchConfig, + }; + } + + private async sendCommandResult(_id: number, _reqId: number, _isError: boolean, _payload: any): Promise { + // NOTE: Not required unless we implement the matching event, see above. + throw new Error('not implemented'); + } +} + +function transformIncoming(remoteAuthority: string, uri: UriComponents | undefined): URI | undefined { + const transformer = createServerURITransformer(remoteAuthority); + return uri ? URI.revive(transformer.transformIncoming(uri)) : uri; +} diff --git a/src/vs/server/connection/abstractConnection.ts b/src/vs/server/connection/abstractConnection.ts new file mode 100644 index 0000000000000..41a5e05e1d410 --- /dev/null +++ b/src/vs/server/connection/abstractConnection.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter } from 'vs/base/common/event'; +import { ConsoleLogger } from 'vs/platform/log/common/log'; +import { ServerProtocol } from 'vs/server/protocol'; + +export abstract class AbstractConnection { + private readonly _onClose = new Emitter(); + /** + * Fire when the connection is closed (not just disconnected). This should + * only happen when the connection is offline and old or has an error. + */ + public readonly onClose = this._onClose.event; + private disposed = false; + private _offline: number | undefined; + + protected get logPrefix() { + return `[${this.name}] ${this.protocol.logPrefix}`; + } + + public constructor( + protected readonly protocol: ServerProtocol, + protected readonly logService: ConsoleLogger, + public readonly name: string, + ) { + + this.logService.debug('Connecting...'); + this.onClose(() => this.logService.debug('Closed')); + } + + public get offline(): number | undefined { + return this._offline; + } + + public reconnect(protocol: ServerProtocol): void { + this.logService.debug(`${this.protocol.reconnectionToken} Reconnecting...`); + this._offline = undefined; + this.doReconnect(protocol); + } + + public dispose(reason?: string): void { + this.logService.debug(`${this.protocol.reconnectionToken} Disposing...`, reason); + if (!this.disposed) { + this.disposed = true; + this.doDispose(); + this._onClose.fire(); + } + } + + public safeDisposeProtocolAndSocket(): void { + try { + this.protocol.acceptDisconnect(); + const socket = this.protocol.getSocket(); + this.protocol.dispose(); + socket.dispose(); + } catch (err) { + onUnexpectedError(err); + } + } + + protected setOffline(): void { + this.logService.debug('Disconnected'); + if (!this._offline) { + this._offline = Date.now(); + } + } + + /** + * Set up the connection on a new socket. + */ + protected abstract doReconnect(protcol: ServerProtocol): void; + + /** + * Dispose/destroy everything permanently. + */ + protected abstract doDispose(): void; +} + +/** + * Connection options sent by the client via a remote agent connection. + */ +export interface ConnectionOptions { + /** The token is how we identify and connect to existing sessions. */ + readonly reconnectionToken: string, + /** Specifies that the client is trying to reconnect. */ + readonly reconnection: boolean, + /** If true assume this is not a web socket (always false for code-server). */ + readonly skipWebSocketFrames: boolean, +} + +/** + * Convenience function to convert a client's query params into a useful object. + */ +export function parseQueryConnectionOptions(query: URLSearchParams): ConnectionOptions { + const reconnectionToken = query.get('reconnectionToken'); + const reconnection = query.get('reconnection'); + const skipWebSocketFrames = query.get('skipWebSocketFrames'); + + if (typeof reconnectionToken !== 'string') { + throw new Error('`reconnectionToken` not present in query'); + } + + if (typeof reconnection !== 'string') { + throw new Error('`reconnection` not present in query'); + } + + if (typeof skipWebSocketFrames !== 'string') { + throw new Error('`skipWebSocketFrames` not present in query'); + } + + return { + reconnectionToken, + reconnection: reconnection === 'true', + skipWebSocketFrames: skipWebSocketFrames === 'true', + }; +} diff --git a/src/vs/server/connection/extensionHostConnection.ts b/src/vs/server/connection/extensionHostConnection.ts new file mode 100644 index 0000000000000..b16ebca756064 --- /dev/null +++ b/src/vs/server/connection/extensionHostConnection.ts @@ -0,0 +1,202 @@ +import { FileAccess } from 'vs/base/common/network'; +import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { DebugMessage, IRemoteExtensionHostStartParams } from 'vs/platform/remote/common/remoteAgentConnection'; +import { AbstractConnection } from 'vs/server/connection/abstractConnection'; +import { getNlsConfiguration } from 'vs/server/nls'; +import { ServerProtocol } from 'vs/server/protocol'; +import { NLSConfiguration } from 'vs/base/node/languagePacks'; +import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; +import { findFreePort } from 'vs/base/node/ports'; +import { ExtensionHost, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { SendHandle } from 'child_process'; +import { ConsoleLogger } from 'vs/platform/log/common/log'; + +export interface ForkEnvironmentVariables { + VSCODE_AMD_ENTRYPOINT: string; + /** One or the other. */ + VSCODE_EXTHOST_WILL_SEND_SOCKET: true; + VSCODE_HANDLES_UNCAUGHT_ERRORS: boolean; + VSCODE_LOG_LEVEL?: string; + VSCODE_LOG_NATIVE: boolean; + VSCODE_LOG_STACK: boolean; + VSCODE_NLS_CONFIG: NLSConfiguration; + VSCODE_PIPE_LOGGING: boolean; + VSCODE_VERBOSE_LOGGING: boolean; + VSCODE_CODE_CACHE_PATH?: string; +} + +/** + * This complements the client-side `PersistantConnection` in `RemoteExtensionHost`. + */ +export class ExtensionHostConnection extends AbstractConnection { + private clientProcess?: ExtensionHost; + + /** @TODO Document usage. */ + public readonly _isExtensionDevHost: boolean; + public readonly _isExtensionDevDebug: boolean; + public readonly _isExtensionDevTestFromCli: boolean; + + public constructor( + protocol: ServerProtocol, + logService: ConsoleLogger, + private readonly startParams: IRemoteExtensionHostStartParams, + private readonly _environmentService: INativeEnvironmentService, + ) { + super(protocol, logService, 'ExtensionHost'); + + const devOpts = parseExtensionDevOptions(this._environmentService); + this._isExtensionDevHost = devOpts.isExtensionDevHost; + this._isExtensionDevDebug = devOpts.isExtensionDevDebug; + this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; + } + + private get debugMessage(): DebugMessage { + return { + type: 'debug', + debugPort: typeof this.startParams.port === 'number' ? this.startParams.port : undefined + }; + } + + /** + * Find a free port if extension host debugging is enabled. + */ + private async _tryFindDebugPort(): Promise { + if (typeof this._environmentService.debugExtensionHost.port !== 'number') { + return 0; + } + + const expected = this.startParams.port || this._environmentService.debugExtensionHost.port; + const port = await findFreePort(expected, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */); + + if (!this._isExtensionDevTestFromCli) { + if (!port) { + console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color:'); + } else { + if (port !== expected) { + console.warn(`%c[Extension Host] %cProvided debugging port ${expected} is not free, using ${port} instead.`, 'color: blue', 'color:'); + } + } + } + + return port || 0; + } + + protected doDispose(): void { + this.protocol.dispose(); + + this.clientProcess?.dispose(); + } + + protected doReconnect(reconnectionProtocol: ServerProtocol): void { + this.logService.info(this.logPrefix, '(Reconnect 1/4)', 'Sending new protocol debug message...'); + reconnectionProtocol.sendMessage(this.debugMessage); + + this.logService.info(this.logPrefix, '(Reconnect 2/4)', 'Swapping socket references...'); + + this.protocol.beginAcceptReconnection(reconnectionProtocol.getSocket(), reconnectionProtocol.readEntireBuffer()); + this.protocol.endAcceptReconnection(); + + this.logService.info(this.logPrefix, '(Reconnect 3/4)', 'Pausing socket until we have a chance to forward its data.'); + const { initialDataChunk, sendHandle } = this.protocol.suspend(); + + const messageSent = this.sendInitMessage(initialDataChunk, this.protocol.inflateBytes, sendHandle); + + if (!messageSent) { + new Error('Child process did not receive init message. Is their a backlog?'); + } + + this.logService.info(this.logPrefix, '(Reconnect 4/4)', 'Child process received init message!'); + } + + /** + * Sends IPC socket to client process. + * @remark This is the complement of `extensionHostProcessSetup.ts#_createExtHostProtocol` + */ + private sendInitMessage(initialDataChunk: VSBuffer, inflateBytes: VSBuffer, sendHandle: SendHandle): boolean { + if (!this.clientProcess) { + throw new Error(`${this.logPrefix} Client process is not set`); + } + + this.logService.info(this.logPrefix, 'Sending init message to client process...'); + + return this.clientProcess.sendIPCMessage({ + type: 'VSCODE_EXTHOST_IPC_SOCKET', + initialDataChunk: Buffer.from(initialDataChunk.buffer).toString('base64'), + skipWebSocketFrames: this.protocol.skipWebSocketFrames, + permessageDeflate: this.protocol.getSocket().permessageDeflate, + inflateBytes: inflateBytes ? Buffer.from(inflateBytes.buffer).toString('base64') : '', + }, sendHandle); + } + + private async generateClientOptions(): Promise { + this.logService.debug('Getting NLS configuration...'); + const config = await getNlsConfiguration(this.startParams.language, this._environmentService.userDataPath); + const portNumber = await this._tryFindDebugPort(); + + return { + serverName: 'Server Extension Host', + freshExecArgv: true, + debugBrk: this.startParams.break ? portNumber : undefined, + debug: this.startParams.break ? undefined : portNumber, + args: ['--type=extensionHost', '--skipWorkspaceStorageLock'], + env: { + VSCODE_AMD_ENTRYPOINT: 'vs/workbench/services/extensions/node/extensionHostProcess', + VSCODE_PIPE_LOGGING: true, + VSCODE_VERBOSE_LOGGING: true, + /** Extension child process will wait until socket is sent. */ + VSCODE_EXTHOST_WILL_SEND_SOCKET: true, + VSCODE_HANDLES_UNCAUGHT_ERRORS: true, + VSCODE_LOG_STACK: false, + VSCODE_LOG_LEVEL: this._environmentService.verbose ? 'trace' : (this._environmentService.logLevel || process.env.LOG_LEVEL), + VSCODE_NLS_CONFIG: config, + VSCODE_LOG_NATIVE: this._isExtensionDevHost, + // Unset `VSCODE_CODE_CACHE_PATH` when developing extensions because it might + // be that dependencies, that otherwise would be cached, get modified. + VSCODE_CODE_CACHE_PATH: this._isExtensionDevHost ? undefined : process.env['VSCODE_CODE_CACHE_PATH'] + } + }; + } + + /** + * Creates an extension host child process. + * @remark this is very similar to `LocalProcessExtensionHost` + */ + public spawn(): Promise { + return new Promise(async (resolve, reject) => { + this.logService.info(this.logPrefix, '(Spawn 1/7)', 'Sending client initial debug message.'); + this.protocol.sendMessage(this.debugMessage); + + this.logService.info(this.logPrefix, '(Spawn 2/7)', 'Pausing socket until we have a chance to forward its data.'); + + const { initialDataChunk, sendHandle } = this.protocol.suspend(); + + this.logService.info(this.logPrefix, '(Spawn 3/7)', 'Generating IPC client options...'); + const clientOptions = await this.generateClientOptions(); + + this.logService.info(this.logPrefix, '(Spawn 4/7)', 'Starting extension host child process...'); + this.clientProcess = new ExtensionHost(FileAccess.asFileUri('bootstrap-fork', require).fsPath, clientOptions); + + this.clientProcess.onDidProcessExit(({ code, signal }) => { + this.dispose(); + + if (code !== 0 && signal !== 'SIGTERM') { + this.logService.error(`${this.logPrefix} Extension host exited with code: ${code} and signal: ${signal}.`); + } + }); + + this.clientProcess.onReady(() => { + this.logService.info(this.logPrefix, '(Spawn 5/7)', 'Extension host is ready!'); + this.logService.info(this.logPrefix, '(Spawn 6/7)', 'Sending init message to child process...'); + const messageSent = this.sendInitMessage(initialDataChunk, this.protocol.inflateBytes, sendHandle); + + if (messageSent) { + this.logService.info(this.logPrefix, '(Spawn 7/7)', 'Child process received init message!'); + return resolve(); + } + + reject(new Error('Child process did not receive init message. Is their a backlog?')); + }); + }); + } +} diff --git a/src/vs/server/connection/managementConnection.ts b/src/vs/server/connection/managementConnection.ts new file mode 100644 index 0000000000000..26e44e79e48d0 --- /dev/null +++ b/src/vs/server/connection/managementConnection.ts @@ -0,0 +1,34 @@ +// /*--------------------------------------------------------------------------------------------- +// * Copyright (c) Coder Technologies. All rights reserved. +// * Licensed under the MIT License. See License.txt in the project root for license information. +// *--------------------------------------------------------------------------------------------*/ + +import { ConsoleLogger } from 'vs/platform/log/common/log'; +import { AbstractConnection } from 'vs/server/connection/abstractConnection'; +import { ServerProtocol } from 'vs/server/protocol'; + +/** + * Used for all the IPC channels. + */ +export class ManagementConnection extends AbstractConnection { + public constructor(protocol: ServerProtocol, logService: ConsoleLogger) { + super(protocol, logService, 'management'); + + protocol.onDidDispose(() => this.dispose('Explicitly closed')); + protocol.onSocketClose(() => this.setOffline()); // Might reconnect. + + protocol.sendMessage({ type: 'ok' }); + } + + protected doDispose(): void { + this.protocol.dispose(); + } + + protected doReconnect(reconnectionProtocol: ServerProtocol): void { + reconnectionProtocol.sendMessage({ type: 'ok' }); + + this.protocol.beginAcceptReconnection(reconnectionProtocol.getSocket(), reconnectionProtocol.readEntireBuffer()); + this.protocol.endAcceptReconnection(); + reconnectionProtocol.dispose(); + } +} diff --git a/src/vs/server/entry.ts b/src/vs/server/entry.ts new file mode 100644 index 0000000000000..7b673df1b4b7d --- /dev/null +++ b/src/vs/server/entry.ts @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import * as proxyAgent from 'vs/base/node/proxy_agent'; +import { enableCustomMarketplace } from 'vs/server/marketplace'; +import { CodeServerMain, VscodeServerArgs as ServerArgs } from 'vs/server/server'; +import { createServer, IncomingHttpHeaders, IncomingMessage } from 'http'; +import * as net from 'net'; + +// eslint-disable-next-line code-import-patterns +import { requestHandler as defaultRequestHandler } from '../../../resources/web/code-web'; +import { createHash } from 'crypto'; +import { ConnectionOptions, parseQueryConnectionOptions } from 'vs/server/connection/abstractConnection'; + +const logger = console; + +setUnexpectedErrorHandler((error) => { + logger.warn('Uncaught error', error instanceof Error ? error.message : error); +}); + +enableCustomMarketplace(); +proxyAgent.monkeyPatch(true); + +type UpgradeHandler = (request: IncomingMessage, socket: net.Socket, upgradeHead: Buffer) => void; + +export async function main(args: ServerArgs) { + const serverUrl = new URL(`http://${args.server}`); + + const codeServer = new CodeServerMain(); + const workbenchConstructionOptions = await codeServer.createWorkbenchConstructionOptions(serverUrl); + + const httpServer = createServer((req, res) => defaultRequestHandler(req, res, workbenchConstructionOptions)); + + const upgrade: UpgradeHandler = (req, socket) => { + if (req.headers['upgrade'] !== 'websocket' || !req.url) { + logger.error(`failed to upgrade for header "${req.headers['upgrade']}" and url: "${req.url}".`); + socket.end('HTTP/1.1 400 Bad Request'); + return; + } + + const upgradeUrl = new URL(req.url, serverUrl.toString()); + logger.log('Upgrade from', upgradeUrl.toString()); + + let connectionOptions: ConnectionOptions; + + try { + connectionOptions = parseQueryConnectionOptions(upgradeUrl.searchParams); + } catch (error: unknown) { + logger.error(error); + socket.end('HTTP/1.1 400 Bad Request'); + return; + } + + socket.on('error', e => { + logger.error(`[${connectionOptions.reconnectionToken}] Socket failed for "${req.url}".`, e); + }); + + + const { responseHeaders, permessageDeflate } = createReponseHeaders(req.headers); + socket.write(responseHeaders); + + codeServer.handleWebSocket(socket, connectionOptions, permessageDeflate); + }; + + httpServer.on('upgrade', upgrade); + + return new Promise((resolve, reject) => { + httpServer.listen(parseInt(serverUrl.port, 10), serverUrl.hostname, () => { + logger.info('Code Server active listening at:', serverUrl.toString()); + }); + }); +} + +function createReponseHeaders(incomingHeaders: IncomingHttpHeaders) { + const acceptKey = incomingHeaders['sec-websocket-key']; + // WebSocket standard hash suffix. + const hash = createHash('sha1').update(acceptKey + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64'); + + const responseHeaders = ['HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade', `Sec-WebSocket-Accept: ${hash}`]; + + let permessageDeflate = false; + + if (String(incomingHeaders['sec-websocket-extensions']).indexOf('permessage-deflate') !== -1) { + permessageDeflate = true; + responseHeaders.push('Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15'); + } + + return { + responseHeaders: responseHeaders.join('\r\n') + '\r\n\r\n', + permessageDeflate + }; +} + diff --git a/src/vs/server/insights.ts b/src/vs/server/insights.ts new file mode 100644 index 0000000000000..db9a65c7dfbbc --- /dev/null +++ b/src/vs/server/insights.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as appInsights from 'applicationinsights'; +import * as https from 'https'; +import * as http from 'http'; +import * as os from 'os'; + +class Channel { + public get _sender() { + throw new Error('unimplemented'); + } + public get _buffer() { + throw new Error('unimplemented'); + } + + public setUseDiskRetryCaching(): void { + throw new Error('unimplemented'); + } + public send(): void { + throw new Error('unimplemented'); + } + public triggerSend(): void { + throw new Error('unimplemented'); + } +} + +export class TelemetryClient { + public context: any = undefined; + public commonProperties: any = undefined; + public config: any = {}; + + public channel: any = new Channel(); + + public addTelemetryProcessor(): void { + throw new Error('unimplemented'); + } + + public clearTelemetryProcessors(): void { + throw new Error('unimplemented'); + } + + public runTelemetryProcessors(): void { + throw new Error('unimplemented'); + } + + public trackTrace(): void { + throw new Error('unimplemented'); + } + + public trackMetric(): void { + throw new Error('unimplemented'); + } + + public trackException(): void { + throw new Error('unimplemented'); + } + + public trackRequest(): void { + throw new Error('unimplemented'); + } + + public trackDependency(): void { + throw new Error('unimplemented'); + } + + public track(): void { + throw new Error('unimplemented'); + } + + public trackNodeHttpRequestSync(): void { + throw new Error('unimplemented'); + } + + public trackNodeHttpRequest(): void { + throw new Error('unimplemented'); + } + + public trackNodeHttpDependency(): void { + throw new Error('unimplemented'); + } + + public trackEvent(options: appInsights.Contracts.EventTelemetry): void { + if (!options.properties) { + options.properties = {}; + } + if (!options.measurements) { + options.measurements = {}; + } + + try { + const cpus = os.cpus(); + options.measurements.cores = cpus.length; + options.properties['common.cpuModel'] = cpus[0].model; + } catch (error) { } + + try { + options.measurements.memoryFree = os.freemem(); + options.measurements.memoryTotal = os.totalmem(); + } catch (error) { } + + try { + options.properties['common.shell'] = os.userInfo().shell; + options.properties['common.release'] = os.release(); + options.properties['common.arch'] = os.arch(); + } catch (error) { } + + try { + const url = process.env.TELEMETRY_URL || 'https://v1.telemetry.coder.com/track'; + const request = (/^http:/.test(url) ? http : https).request(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }); + request.on('error', () => { /* We don't care. */ }); + request.write(JSON.stringify(options)); + request.end(); + } catch (error) { } + } + + public flush(options: { callback: (v: string) => void }): void { + if (options.callback) { + options.callback(''); + } + } +} diff --git a/src/vs/server/marketplace.ts b/src/vs/server/marketplace.ts new file mode 100644 index 0000000000000..da2ad9983efb3 --- /dev/null +++ b/src/vs/server/marketplace.ts @@ -0,0 +1,179 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as tarStream from 'tar-stream'; +import * as util from 'util'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import * as vszip from 'vs/base/node/zip'; +import * as nls from 'vs/nls'; +import product from 'vs/platform/product/common/product'; +import { IProductConfiguration } from 'vs/workbench/workbench.web.api'; + +// We will be overriding these, so keep a reference to the original. +const vszipExtract = vszip.extract; +const vszipBuffer = vszip.buffer; + +export const tar = async (tarPath: string, files: vszip.IFile[]): Promise => { + const pack = tarStream.pack(); + const chunks: Buffer[] = []; + const ended = new Promise((resolve) => { + pack.on('end', () => resolve(Buffer.concat(chunks))); + }); + pack.on('data', (chunk: Buffer) => chunks.push(chunk)); + for (let i = 0; i < files.length; i++) { + const file = files[i]; + pack.entry({ name: file.path }, file.contents); + } + pack.finalize(); + await util.promisify(fs.writeFile)(tarPath, await ended); + return tarPath; +}; + +export const extract = async (archivePath: string, extractPath: string, options: vszip.IExtractOptions = {}, token: CancellationToken): Promise => { + try { + await extractTar(archivePath, extractPath, options, token); + } catch (error) { + if (error.toString().includes('Invalid tar header')) { + await vszipExtract(archivePath, extractPath, options, token); + } + } +}; + +export const buffer = (targetPath: string, filePath: string): Promise => { + return new Promise(async (resolve, reject) => { + try { + let done: boolean = false; + await extractAssets(targetPath, new RegExp(filePath), (assetPath: string, data: Buffer) => { + if (path.normalize(assetPath) === path.normalize(filePath)) { + done = true; + resolve(data); + } + }); + if (!done) { + throw new Error('couldn\'t find asset ' + filePath); + } + } catch (error) { + if (error.toString().includes('Invalid tar header')) { + vszipBuffer(targetPath, filePath).then(resolve).catch(reject); + } else { + reject(error); + } + } + }); +}; + +const extractAssets = async (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise => { + return new Promise((resolve, reject): void => { + const extractor = tarStream.extract(); + const fail = (error: Error) => { + extractor.destroy(); + reject(error); + }; + extractor.once('error', fail); + extractor.on('entry', async (header, stream, next) => { + const name = header.name; + if (match.test(name)) { + extractData(stream).then((data) => { + callback(name, data); + next(); + }).catch(fail); + } else { + stream.on('end', () => next()); + stream.resume(); // Just drain it. + } + }); + extractor.on('finish', resolve); + fs.createReadStream(tarPath).pipe(extractor); + }); +}; + +const extractData = (stream: NodeJS.ReadableStream): Promise => { + return new Promise((resolve, reject): void => { + const fileData: Buffer[] = []; + stream.on('error', reject); + stream.on('end', () => resolve(Buffer.concat(fileData))); + stream.on('data', (data) => fileData.push(data)); + }); +}; + +const extractTar = async (tarPath: string, targetPath: string, options: vszip.IExtractOptions = {}, token: CancellationToken): Promise => { + return new Promise((resolve, reject): void => { + const sourcePathRegex = new RegExp(options.sourcePath ? `^${options.sourcePath}` : ''); + const extractor = tarStream.extract(); + const fail = (error: Error) => { + extractor.destroy(); + reject(error); + }; + extractor.once('error', fail); + extractor.on('entry', async (header, stream, next) => { + const nextEntry = (): void => { + stream.on('end', () => next()); + stream.resume(); + }; + + const rawName = path.normalize(header.name); + if (token.isCancellationRequested || !sourcePathRegex.test(rawName)) { + return nextEntry(); + } + + const fileName = rawName.replace(sourcePathRegex, ''); + const targetFileName = path.join(targetPath, fileName); + if (/\/$/.test(fileName)) { + /* + NOTE:@coder: they removed mkdirp in favor of fs.promises + See commit: https://github.com/microsoft/vscode/commit/a0d76bb9834b63a02fba8017a6306511fe1ab4fe#diff-2bf233effbb62ea789bb7c4739d222a43ccd97ed9f1219f75bb07e9dee91c1a7 + 3/11/21 @jsjoeio + */ + return fs.promises.mkdir(targetFileName, { recursive: true }).then(nextEntry); + } + + const dirName = path.dirname(fileName); + const targetDirName = path.join(targetPath, dirName); + if (targetDirName.indexOf(targetPath) !== 0) { + return fail(new Error(nls.localize('invalid file', 'Error extracting {0}. Invalid file.', fileName))); + } + + /* + NOTE:@coder: they removed mkdirp in favor of fs.promises + See commit: https://github.com/microsoft/vscode/commit/a0d76bb9834b63a02fba8017a6306511fe1ab4fe#diff-2bf233effbb62ea789bb7c4739d222a43ccd97ed9f1219f75bb07e9dee91c1a7 + 3/11/21 @jsjoeio + */ + await fs.promises.mkdir(targetDirName, { recursive: true }); + + const fstream = fs.createWriteStream(targetFileName, { mode: header.mode }); + fstream.once('close', () => next()); + fstream.once('error', fail); + stream.pipe(fstream); + }); + extractor.once('finish', resolve); + fs.createReadStream(tarPath).pipe(extractor); + }); +}; + +/** + * Override original functionality so we can use a custom marketplace with + * either tars or zips. + */ +export const enableCustomMarketplace = (): void => { + const extensionsGallery: IProductConfiguration['extensionsGallery'] = { + serviceUrl: process.env.SERVICE_URL || 'https://extensions.coder.com/api', + resourceUrlTemplate: '', + itemUrl: process.env.ITEM_URL || '', + controlUrl: '', + recommendationsUrl: '', + ...(product.extensionsGallery || {}), + }; + + // Workaround for readonly property. + Object.assign(product, { extensionsGallery }); + + const target = vszip as typeof vszip; + + target.zip = tar; + target.extract = extract; + target.buffer = buffer; +}; diff --git a/src/vs/server/nls.ts b/src/vs/server/nls.ts new file mode 100644 index 0000000000000..4b16cc37c31a4 --- /dev/null +++ b/src/vs/server/nls.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as util from 'util'; +import { FileAccess } from 'vs/base/common/network'; +import * as lp from 'vs/base/node/languagePacks'; +import product from 'vs/platform/product/common/product'; +import { Translations } from 'vs/workbench/services/extensions/common/extensionPoints'; + +const configurations = new Map>(); +const metadataPath = path.join(FileAccess.asFileUri('', require).fsPath, 'nls.metadata.json'); + +export const isInternalConfiguration = (config: lp.NLSConfiguration): config is lp.InternalNLSConfiguration => { + return config && !!(config)._languagePackId; +}; + +const DefaultConfiguration = { + locale: 'en', + availableLanguages: {}, +}; + +export const getNlsConfiguration = async (locale: string, userDataPath: string): Promise => { + const id = `${locale}: ${userDataPath}`; + if (!configurations.has(id)) { + configurations.set(id, new Promise(async (resolve) => { + const config = product.commit && await util.promisify(fs.exists)(metadataPath) + ? await lp.getNLSConfiguration(product.commit, userDataPath, metadataPath, locale) + : DefaultConfiguration; + if (isInternalConfiguration(config)) { + config._languagePackSupport = true; + } + // If the configuration has no results keep trying since code-server + // doesn't restart when a language is installed so this result would + // persist (the plugin might not be installed yet or something). + if (config.locale !== 'en' && config.locale !== 'en-us' && Object.keys(config.availableLanguages).length === 0) { + configurations.delete(id); + } + resolve(config); + })); + } + return configurations.get(id)!; +}; + +export const getTranslations = async (locale: string, userDataPath: string): Promise => { + const config = await getNlsConfiguration(locale, userDataPath); + if (isInternalConfiguration(config)) { + try { + return JSON.parse(await util.promisify(fs.readFile)(config._translationsConfigFile, 'utf8')); + } catch (error) { /* Nothing yet. */ } + } + return {}; +}; + +export const getLocaleFromConfig = async (userDataPath: string): Promise => { + const files = ['locale.json', 'argv.json']; + for (let i = 0; i < files.length; ++i) { + try { + const localeConfigUri = path.join(userDataPath, 'User', files[i]); + const content = stripComments(await util.promisify(fs.readFile)(localeConfigUri, 'utf8')); + return JSON.parse(content).locale; + } catch (error) { /* Ignore. */ } + } + return 'en'; +}; + +// Taken from src/main.js in the main VS Code source. +const stripComments = (content: string): string => { + const regexp = /('(?:[^\\']*(?:\\.)?)*')|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; + + return content.replace(regexp, (match, _m1, _m2, m3, m4) => { + // Only one of m1, m2, m3, m4 matches + if (m3) { + // A block comment. Replace with nothing + return ''; + } else if (m4) { + // A line comment. If it ends in \r?\n then keep it. + const length_1 = m4.length; + if (length_1 > 2 && m4[length_1 - 1] === '\n') { + return m4[length_1 - 2] === '\r' ? '\r\n' : '\n'; + } + else { + return ''; + } + } else { + // We match a string + return match; + } + }); +}; diff --git a/src/vs/server/protocol.ts b/src/vs/server/protocol.ts new file mode 100644 index 0000000000000..1ac3cb3ea0287 --- /dev/null +++ b/src/vs/server/protocol.ts @@ -0,0 +1,193 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SendHandle } from 'child_process'; +import * as net from 'net'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { ISocket, PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; +import { WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { ConsoleLogger } from 'vs/platform/log/common/log'; +import { ConnectionTypeRequest, HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection'; +import { ConnectionOptions } from 'vs/server/connection/abstractConnection'; + +/** + * Matches `remoteAgentConnection.ts#connectToRemoteExtensionHostAgent` + */ +const HANDSHAKE_TIMEOUT_DURATION = 10000; + + +/** + * Trims a long token for a cleaner logging. + */ +export function trimConnectionToken(token: string): string { + return `${token.slice(0, 3)}...${token.slice(-3)}`; +} + +/** + * This server-side protocol is the complement of the client-side `PersistentConnection`. + */ +export class ServerProtocol extends PersistentProtocol { + public readonly logPrefix: string; + + constructor( + socket: ISocket, + private readonly logger: ConsoleLogger, + private readonly options: ConnectionOptions, + // `initialChunk` is likely not used. + initialChunk?: VSBuffer | null, + ) { + super(socket, initialChunk); + + this.logPrefix = `[${trimConnectionToken(this.reconnectionToken)}]`; + } + + public override getSocket() { + return super.getSocket() as WebSocketNodeSocket; + } + + /** + * Used when passing the underlying socket to a child process. + * + * @remark this may be undefined if the socket or its parent container is disposed. + */ + private getSendHandle(): net.Socket | undefined { + return this.getSocket().socket.socket; + } + + public get reconnectionToken() { + return this.options.reconnectionToken; + } + + public get reconnection() { + return this.options.reconnection; + } + + public get skipWebSocketFrames() { + return this.options.skipWebSocketFrames; + } + + /** + * Perform a handshake to get a connection request. + */ + public handshake(): Promise { + this.logger.info(this.logPrefix, '(Handshake 1/4)', 'Waiting for client authentication...'); + + return new Promise((resolve, reject) => { + const cleanup = () => { + onControlMessageHandler.dispose(); + onClose.dispose(); + clearTimeout(handshakeTimeout); + }; + + const onClose = this.onSocketClose(() => { + cleanup(); + this.logger.error('Handshake failed'); + reject(new Error('Protocol socket closed unexpectedly')); + }); + + const handshakeTimeout = setTimeout(() => { + cleanup(); + this.logger.error('Handshake timed out'); + reject(new Error('Protocol handshake timed out')); + }, HANDSHAKE_TIMEOUT_DURATION); + + const onControlMessageHandler = this.onControlMessage((rawMessage) => { + try { + const raw = rawMessage.toString(); + const message: HandshakeMessage = JSON.parse(raw); + + switch (message.type) { + + case 'auth': + this.logger.info(this.logPrefix, '(Handshake 2/4)', 'Client auth received!'); + this.sendMessage({ type: 'sign', data: message.auth }); + this.logger.info(this.logPrefix, '(Handshake 3/4)', 'Sent client signed auth...'); + break; + case 'connectionType': + cleanup(); + this.logger.info(this.logPrefix, '(Handshake 4/4)', 'Client has requested a connection!'); + resolve(message); + default: + throw new Error('Unrecognized message type'); + } + } catch (error) { + cleanup(); + reject(error); + } + }); + }); + } + + /** + * TODO: implement. + */ + public tunnel(): void { + throw new Error('Tunnel is not implemented yet'); + } + + /** + * Send a handshake message as a VSBuffer. + * @remark In the case of ExtensionHost it should only send a debug port. + */ + public sendMessage(message: HandshakeMessage): void { + this.logger.debug(this.logPrefix, `Sending control message to client (${message.type})`); + this.sendControl(VSBuffer.fromString(JSON.stringify(message))); + } + + /** + * Disconnect and dispose protocol. + */ + public override dispose(errorReason?: string): void { + try { + if (errorReason) { + this.sendMessage({ type: 'error', reason: errorReason }); + } + + // If still connected try notifying the client. + this.sendDisconnect(); + } catch (error) { + // I think the write might fail if already disconnected. + this.logger.warn(error.message || error); + } + + // This disposes timers and socket event handlers. + super.dispose(); + } + + /** + * Suspends protocol in preparation for socket passing to a child process + * @remark This will partially dispose the protocol! + */ + public suspend(): { initialDataChunk: VSBuffer, sendHandle: SendHandle } { + const sendHandle = this.getSendHandle(); + + if (!sendHandle) { + throw new Error('Send handle is not present in protocol. Was it disposed?'); + } + const initialDataChunk = this.readEntireBuffer(); + + // This disposes timers and socket event handlers. + super.dispose(); + sendHandle.pause(); + this.getSocket().drain(); + + return { + initialDataChunk, + sendHandle + }; + } + + /** + * Get inflateBytes from the current socket. + * Seed zlib with these bytes (web socket only). If parts of inflating was + * done in a different zlib instance we need to pass all those bytes into zlib + * otherwise the inflate might hit an inflated portion referencing a distance + * too far back. + */ + public get inflateBytes() { + const socket = this.getSocket(); + return socket.recordedInflateBytes; + } +} diff --git a/src/vs/server/server.ts b/src/vs/server/server.ts new file mode 100644 index 0000000000000..1e027b01ffb9e --- /dev/null +++ b/src/vs/server/server.ts @@ -0,0 +1,407 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { promises as fs } from 'fs'; +import * as net from 'net'; +import { hostname, release } from 'os'; +import * as path from 'path'; +import { Emitter } from 'vs/base/common/event'; +import { Schemas } from 'vs/base/common/network'; +import { Complete } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { createServerURITransformer } from 'vs/base/common/uriServer'; +import { getMachineId } from 'vs/base/node/id'; +import { ClientConnectionEvent, IPCServer, IServerChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; +import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; +import { main } from 'vs/code/node/cliProcessMain'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; +import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; +// eslint-disable-next-line code-import-patterns +import { ArgumentParser } from 'vs/platform/environment/argumentParser'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; +import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; +import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { FileService } from 'vs/platform/files/common/fileService'; +import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; +import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; +import { ConsoleMainLogger, getLogLevel, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log'; +import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; +import { LoggerService } from 'vs/platform/log/node/loggerService'; +import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import productConfiguration from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { ConnectionType, connectionTypeToString, IRemoteExtensionHostStartParams } from 'vs/platform/remote/common/remoteAgentConnection'; +import { RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { RequestChannel } from 'vs/platform/request/common/requestIpc'; +import { RequestService } from 'vs/platform/request/node/requestService'; +import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; +import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; +import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; +import ErrorTelemetry from 'vs/platform/telemetry/node/errorTelemetry'; +import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; +import { toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ExtensionEnvironmentChannel, FileProviderChannel, TerminalProviderChannel } from 'vs/server/channel'; +import { ConnectionOptions } from 'vs/server/connection/abstractConnection'; +import { TelemetryClient } from 'vs/server/insights'; +import { getLocaleFromConfig, getNlsConfiguration } from 'vs/server/nls'; +import { ServerProtocol } from 'vs/server/protocol'; +import { REMOTE_TERMINAL_CHANNEL_NAME } from 'vs/workbench/contrib/terminal/common/remoteTerminalChannel'; +import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel'; +import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IServerWorkbenchConstructionOptions, IWorkspace } from 'vs/workbench/workbench.web.api'; +import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { BufferLogService } from 'vs/platform/log/common/bufferLog'; +import { ExtensionHostConnection } from 'vs/server/connection/extensionHostConnection'; +import { ManagementConnection } from 'vs/server/connection/managementConnection'; + +const commit = productConfiguration.commit || 'development'; + +export type VscodeServerArgs = NativeParsedArgs & Complete>; +type Connection = ExtensionHostConnection | ManagementConnection; + +/** + * Handles client connections to a editor instance via IPC. + */ +export class CodeServerMain extends ArgumentParser { + public readonly _onDidClientConnect = new Emitter(); + public readonly onDidClientConnect = this._onDidClientConnect.event; + + private readonly ipc = new IPCServer(this.onDidClientConnect); + + private readonly maxExtraOfflineConnections = 0; + + private readonly services = new ServiceCollection(); + private servicesPromise?: Promise; + private authority: string = ''; + + private readonly connections = new Map>(); + + /** + * Initializes connection map for this type of connection. + */ + private getCachedConnectionMap(desiredConnectionType: T) { + let connectionMap = this.connections.get(desiredConnectionType); + + if (!connectionMap) { + connectionMap = new Map(); + this.connections.set(desiredConnectionType, connectionMap); + } + + return connectionMap; + } + public async cli(args: NativeParsedArgs): Promise { + return main(args); + } + + private createWorkbenchURIs(paths: string[]) { + return paths.map(path => toWorkspaceFolder(URI.from({ + scheme: Schemas.vscodeRemote, + authority: this.authority, + path, + }))); + } + + public async createWorkbenchConstructionOptions(serverUrl: URL): Promise { + const parsedArgs = this.resolveArgs(); + + if (!parsedArgs.server) { + throw new Error('Server argument not provided'); + } + + this.authority = parsedArgs.server; + + const transformer = createServerURITransformer(this.authority); + + if (!this.servicesPromise) { + this.servicesPromise = this.initializeServices(parsedArgs); + } + await this.servicesPromise; + + const environment = this.services.get(IEnvironmentService) as INativeEnvironmentService; + + /** + * A workspace to open in the workbench can either be: + * - a workspace file with 0-N folders (via `workspaceUri`) + * - a single folder (via `folderUri`) + * - empty (via `undefined`) + */ + const workbenchURIs = this.createWorkbenchURIs(parsedArgs._.slice(1)); + // const hasSingleEntry = workbenchURIs.length > 0; + // const isSingleEntry = workbenchURIs.length === 1; + + const workspace: IWorkspace = { + // workspaceUri: isSingleEntry ? undefined : fs.stat(path), + workspaceUri: undefined, + folderUri: workbenchURIs[0].uri, + }; + + const webEndpointUrl = new URL(serverUrl.toString()); + webEndpointUrl.pathname = '/static'; + + return { + ...workspace, + remoteAuthority: parsedArgs.remote || serverUrl.toJSON(), + logLevel: getLogLevel(environment), + workspaceProvider: { + workspace, + trusted: undefined, + payload: [ + ['userDataPath', environment.userDataPath], + ['enableProposedApi', JSON.stringify(parsedArgs['enable-proposed-api'] || [])] + ], + }, + remoteUserDataUri: transformer.transformOutgoing(URI.file(environment.userDataPath)), + productConfiguration: { + ...productConfiguration, + webEndpointUrl: webEndpointUrl.toJSON() + }, + nlsConfiguration: await getNlsConfiguration(environment.args.locale || await getLocaleFromConfig(environment.userDataPath), environment.userDataPath), + commit, + }; + } + + public async handleWebSocket(socket: net.Socket, connectionOptions: ConnectionOptions, permessageDeflate = false): Promise { + const logger = this.services.get(ILogService) as MultiplexLogService; + + const protocol = new ServerProtocol( + new WebSocketNodeSocket(new NodeSocket(socket), permessageDeflate, null, permessageDeflate), + logger, + connectionOptions + ); + + try { + await this.connect(protocol); + } catch (error) { + protocol.dispose(error.message); + } + return true; + } + + private async connect(protocol: ServerProtocol): Promise { + const logger = this.services.get(ILogService) as MultiplexLogService; + + const message = await protocol.handshake(); + + const clientVersion = message.commit; + const serverVersion = productConfiguration.commit; + if (serverVersion && clientVersion !== serverVersion) { + logger.warn(`Client version (${message.commit} does not match server version ${serverVersion})`); + } + + // `desiredConnectionType` is marked as optional, + // but it's a scenario we haven't yet seen. + if (!message.desiredConnectionType) { + throw new Error(`Expected desired connection type in protocol handshake: ${JSON.stringify(message)}`); + } + + const connections = this.getCachedConnectionMap(message.desiredConnectionType); + let connection = connections.get(protocol.reconnectionToken); + const logPrefix = connectLogPrefix(message.desiredConnectionType, protocol); + + if (protocol.reconnection && connection) { + logger.info(logPrefix, 'Client attempting to reconnect'); + return connection.reconnect(protocol); + } + + // This probably means the process restarted so the session was lost + // while the browser remained open. + if (protocol.reconnection) { + throw new Error(`Unable to reconnect; session no longer exists (${protocol.reconnectionToken})`); + } + + // This will probably never happen outside a chance collision. + if (connection) { + throw new Error('Unable to connect; token is already in use'); + } + + // Now that the initial exchange has completed we can create the actual + // connection on top of the protocol then send it to whatever uses it. + logger.info(logPrefix, 'Client requesting connection'); + + switch (message.desiredConnectionType) { + case ConnectionType.Management: + connection = new ManagementConnection(protocol, logger); + + // The management connection is used by firing onDidClientConnect + // which makes the IPC server become aware of the connection. + this._onDidClientConnect.fire({ + protocol, + onDidClientDisconnect: connection.onClose, + }); + break; + case ConnectionType.ExtensionHost: + // The extension host connection is used by spawning an extension host + // and then passing the socket into it. + + const startParams: IRemoteExtensionHostStartParams = { + language: 'en', + ...message.args, + }; + + connection = new ExtensionHostConnection( + protocol, + logger, + startParams, + // Partial fix for imprecise typing. + this.services.get(INativeEnvironmentService) as INativeEnvironmentService); + + await connection.spawn(); + break; + case ConnectionType.Tunnel: + return protocol.tunnel(); + default: + throw new Error(`Unknown desired connection type ${message.desiredConnectionType}`); + } + + connections.set(protocol.reconnectionToken, connection); + connection.onClose(() => connections.delete(protocol.reconnectionToken)); + + this.disposeOldOfflineConnections(connections); + logger.debug(`${connections.size} active ${connection.name} connection(s)`); + + } + + private disposeOldOfflineConnections(connections: Map): void { + const offline = Array.from(connections.values()) + .filter((connection) => typeof connection.offline !== 'undefined'); + for (let i = 0, max = offline.length - this.maxExtraOfflineConnections; i < max; ++i) { + offline[i].dispose('old'); + } + } + + // References: + // ../../electron-browser/sharedProcess/sharedProcessMain.ts#L148 + // ../../../code/electron-main/app.ts + private async initializeServices(args: NativeParsedArgs): Promise { + const productService: IProductService = { _serviceBrand: undefined, ...productConfiguration }; + const environmentService = new NativeEnvironmentService(args, productService); + + await Promise.all([ + environmentService.extensionsPath, + environmentService.logsPath, + environmentService.globalStorageHome.fsPath, + environmentService.workspaceStorageHome.fsPath, + ...environmentService.extraExtensionPaths, + ...environmentService.extraBuiltinExtensionPaths, + ].map((p) => fs.mkdir(p, { recursive: true }).catch((error) => { + console.warn(error.message || error); + }))); + + + // Loggers + // src/vs/code/electron-main/main.ts#142 + const bufferLogService = new BufferLogService(); + const logService = new MultiplexLogService([ + new ConsoleMainLogger(getLogLevel(environmentService)), + bufferLogService, + ]); + // registerErrorHandler(logService); + + const fileService = new FileService(logService); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService)); + + const loggerService = new LoggerService(logService, fileService); + + const piiPaths = [ + path.join(environmentService.userDataPath, 'clp'), // Language packs. + environmentService.appRoot, + environmentService.extensionsPath, + environmentService.builtinExtensionsPath, + ...environmentService.extraExtensionPaths, + ...environmentService.extraBuiltinExtensionPaths, + ]; + + this.ipc.registerChannel('logger', new LogLevelChannel(logService)); + this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel()); + + this.services.set(ILogService, logService); + this.services.set(IEnvironmentService, environmentService); + this.services.set(INativeEnvironmentService, environmentService); + this.services.set(ILoggerService, loggerService); + + const configurationService = new ConfigurationService(environmentService.settingsResource, fileService); + await configurationService.initialize(); + this.services.set(IConfigurationService, configurationService); + + this.services.set(IRequestService, new SyncDescriptor(RequestService)); + this.services.set(IFileService, fileService); + this.services.set(IProductService, productService); + + await configurationService.initialize(); + this.services.set(IConfigurationService, configurationService); + + const machineId = await getMachineId(); + + await new Promise((resolve) => { + const instantiationService = new InstantiationService(this.services); + + instantiationService.invokeFunction((accessor) => { + // Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906) + bufferLogService.logger = new SpdLogLogger('main', path.join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, bufferLogService.getLevel()); + + instantiationService.createInstance(LogsDataCleaner); + + let telemetryService: ITelemetryService; + + if (!environmentService.isExtensionDevelopment && !environmentService.disableTelemetry && !!productService.enableTelemetry) { + telemetryService = new TelemetryService({ + appender: combinedAppender( + new AppInsightsAppender('code-server', null, () => new TelemetryClient() as any), + new TelemetryLogAppender(accessor.get(ILoggerService), environmentService) + ), + sendErrorTelemetry: true, + commonProperties: resolveCommonProperties( + fileService, release(), hostname(), process.arch, commit, productConfiguration.version, machineId, + undefined, environmentService.installSourcePath, 'code-server', + ), + piiPaths, + }, configurationService); + } else { + telemetryService = NullTelemetryService; + } + + this.services.set(ITelemetryService, telemetryService); + + this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + this.services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + + this.ipc.registerChannel('extensions', new ExtensionManagementChannel( + accessor.get(IExtensionManagementService), + (context) => createServerURITransformer(context.remoteAuthority), + )); + this.ipc.registerChannel('remoteextensionsenvironment', new ExtensionEnvironmentChannel( + environmentService, logService, telemetryService, '', + )); + this.ipc.registerChannel('request', new RequestChannel(accessor.get(IRequestService))); + this.ipc.registerChannel('localizations', >ProxyChannel.fromService(accessor.get(ILocalizationsService))); + this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService)); + + const ptyHostService = new PtyHostService({ GraceTime: 60000, ShortGraceTime: 6000 }, configurationService, logService, telemetryService); + this.ipc.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new TerminalProviderChannel(logService, ptyHostService)); + + resolve(new ErrorTelemetry(telemetryService)); + }); + }); + } +} + +function connectLogPrefix(connectionType: ConnectionType, protocol: ServerProtocol) { + return `[${connectionTypeToString(connectionType)}] ${protocol.logPrefix}`; +} diff --git a/src/vs/workbench/api/browser/mainThreadStorage.ts b/src/vs/workbench/api/browser/mainThreadStorage.ts index 5cb658e91d1c6..8fd15e86f9f8f 100644 --- a/src/vs/workbench/api/browser/mainThreadStorage.ts +++ b/src/vs/workbench/api/browser/mainThreadStorage.ts @@ -62,12 +62,13 @@ export class MainThreadStorage implements MainThreadStorageShape { return JSON.parse(jsonValue); } - $setValue(shared: boolean, key: string, value: object): Promise { + async $setValue(shared: boolean, key: string, value: object): Promise { let jsonValue: string; try { jsonValue = JSON.stringify(value); // Extension state is synced separately through extensions - this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE, StorageTarget.MACHINE); + // NOTE@coder: Wait for the actual storage write. + await this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE, StorageTarget.MACHINE); } catch (err) { return Promise.reject(err); } diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index 6177f03c80fd1..ee509b68f1899 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -11,6 +11,8 @@ import { IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/ import { URI } from 'vs/base/common/uri'; import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; import { ILogService } from 'vs/platform/log/common/log'; +import { tmpdir } from 'os'; +import { join } from 'vs/base/common/path'; export interface OpenCommandPipeArgs { type: 'open'; @@ -67,6 +69,11 @@ export class CLIServerBase { } private async setup(): Promise { + // NOTE@coder: Write this out so we can get the most recent path. + fs.promises.writeFile(join(tmpdir(), 'vscode-ipc'), this._ipcHandlePath).catch((error) => { + this.logService.error(error); + }); + try { this._server.listen(this.ipcHandlePath); this._server.on('error', err => this.logService.error(err)); diff --git a/src/vs/workbench/browser/client.ts b/src/vs/workbench/browser/client.ts new file mode 100644 index 0000000000000..b74bbc4115197 --- /dev/null +++ b/src/vs/workbench/browser/client.ts @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from 'vs/base/common/path'; +import { CodeServerConfiguration } from 'vs/base/common/ipc'; +import { localize } from 'vs/nls'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { getOptions } from 'vs/base/common/util'; +import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; // eslint-disable-line code-import-patterns +import 'vs/workbench/services/localizations/browser/localizationsService'; + +/** + * All client-side customization to VS Code should live in this file when + * possible. + */ + +const options = getOptions(); + +/** + * This is called by vs/workbench/browser/web.main.ts after the workbench has + * been initialized so we can initialize our own client-side code. + */ +export const initialize = async (services: ServiceCollection): Promise => { + const event = new CustomEvent('ide-ready'); + window.dispatchEvent(event); + + if (parent) { + // Tell the parent loading has completed. + parent.postMessage({ event: 'loaded' }, '*'); + + // Proxy or stop proxing events as requested by the parent. + const listeners = new Map void>(); + window.addEventListener('message', (parentEvent) => { + const eventName = parentEvent.data.bind || parentEvent.data.unbind; + if (eventName) { + const oldListener = listeners.get(eventName); + if (oldListener) { + document.removeEventListener(eventName, oldListener); + } + } + + if (parentEvent.data.bind && parentEvent.data.prop) { + const listener = (event: Event) => { + parent?.postMessage({ + event: parentEvent.data.event, + [parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event] + }, window.location.origin); + }; + listeners.set(parentEvent.data.bind, listener); + document.addEventListener(parentEvent.data.bind, listener); + } + }); + } + + if (!window.isSecureContext) { + (services.get(INotificationService) as INotificationService).notify({ + severity: Severity.Warning, + message: 'code-server is being accessed over an insecure domain. Web views, the clipboard, and other functionality will not work as expected.', + actions: { + primary: [{ + id: 'understand', + label: 'I understand', + tooltip: '', + class: undefined, + enabled: true, + checked: true, + dispose: () => undefined, + run: () => { + return Promise.resolve(); + } + }], + } + }); + } + + const logService = (services.get(ILogService) as ILogService); + const storageService = (services.get(IStorageService) as IStorageService); + const updateCheckEndpoint = path.join(options.base, '/update/check'); + const getUpdate = async (): Promise => { + logService.debug('Checking for update...'); + + const response = await fetch(updateCheckEndpoint, { + headers: { 'Accept': 'application/json' }, + }); + if (!response.ok) { + throw new Error(response.statusText); + } + const json = await response.json(); + if (json.error) { + throw new Error(json.error); + } + if (json.isLatest) { + return; + } + + const lastNoti = storageService.getNumber('csLastUpdateNotification', StorageScope.GLOBAL); + if (lastNoti) { + // Only remind them again after 1 week. + const timeout = 1000 * 60 * 60 * 24 * 7; + const threshold = lastNoti + timeout; + if (Date.now() < threshold) { + return; + } + } + + storageService.store('csLastUpdateNotification', Date.now(), StorageScope.GLOBAL, StorageTarget.MACHINE); + (services.get(INotificationService) as INotificationService).notify({ + severity: Severity.Info, + message: `[code-server v${json.latest}](https://github.com/cdr/code-server/releases/tag/v${json.latest}) has been released!`, + }); + }; + + const updateLoop = (): void => { + getUpdate().catch((error) => { + logService.debug(`failed to check for update: ${error}`); + }).finally(() => { + // Check again every 6 hours. + setTimeout(updateLoop, 1000 * 60 * 60 * 6); + }); + }; + + // TODO@coder enable + // if (!options.disableUpdateCheck) { + // updateLoop(); + // } + + // This will be used to set the background color while VS Code loads. + const theme = storageService.get('colorThemeData', StorageScope.GLOBAL); + if (theme) { + localStorage.setItem('colorThemeData', theme); + } + + // Use to show or hide logout commands and menu options. + const contextKeyService = (services.get(IContextKeyService) as IContextKeyService); + contextKeyService.createKey('code-server.authed', options.authed); + + // Add a logout command. + const logoutEndpoint = path.join(options.base, '/logout') + `?base=${options.base}`; + const LOGOUT_COMMAND_ID = 'code-server.logout'; + CommandsRegistry.registerCommand( + LOGOUT_COMMAND_ID, + () => { + window.location.href = logoutEndpoint; + }, + ); + + // Add logout to command palette. + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: LOGOUT_COMMAND_ID, + title: localize('logout', "Log out") + }, + when: ContextKeyExpr.has('code-server.authed') + }); + + // Add logout to the (web-only) home menu. + MenuRegistry.appendMenuItem(MenuId.MenubarHomeMenu, { + command: { + id: LOGOUT_COMMAND_ID, + title: localize('logout', "Log out") + }, + when: ContextKeyExpr.has('code-server.authed') + }); +}; diff --git a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts index 530ce5e5c287a..8e71a71ae1494 100644 --- a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts @@ -144,11 +144,12 @@ export class BrowserDialogHandler implements IDialogHandler { async about(): Promise { const detailString = (useAgo: boolean): string => { return localize('aboutDetail', - "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", + "code-server: v{4}\n VS Code: v{0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", this.productService.version || 'Unknown', this.productService.commit || 'Unknown', this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown', - navigator.userAgent + navigator.userAgent, + this.productService.codeServerVersion || 'Unknown', ); }; diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index d985a9e1b06a5..7daac7d8c41c6 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -64,6 +64,7 @@ import { WorkspaceTrustManagementService } from 'vs/workbench/services/workspace import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { initialize } from './client'; class BrowserMain extends Disposable { @@ -96,6 +97,9 @@ class BrowserMain extends Disposable { // Startup const instantiationService = workbench.startup(); + // NOTE@coder: initialize our additions + await initialize(services.serviceCollection); + // Window this._register(instantiationService.createInstance(BrowserWindow)); @@ -364,6 +368,9 @@ class BrowserMain extends Disposable { } } + /** + * @coder There seems to be only partial support for workspaces on the web. + */ private resolveWorkspaceInitializationPayload(): IWorkspaceInitializationPayload { let workspace: IWorkspace | undefined = undefined; if (this.configuration.workspaceProvider) { diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index f99661f7ea790..79eb43d845f5c 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -16,6 +16,7 @@ import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { Schemas } from 'vs/base/common/network'; export class ResourceContextKey extends Disposable implements IContextKey { @@ -75,7 +76,10 @@ export class ResourceContextKey extends Disposable implements IContextKey { if (!ResourceContextKey._uriEquals(this._resourceKey.get(), value)) { this._contextKeyService.bufferChangeEvents(() => { this._resourceKey.set(value); - this._schemeKey.set(value ? value.scheme : null); + // NOTE@coder: this is to get Git context actions to show up + // See issue #1140 / commit 7e4a73ce2d19eee08ceea25113debefeb8ac27e2 + // TODO@oxy: Codespaces has this working alright without this patch - investigate why we need this and remove it. + this._schemeKey.set(value ? (value.scheme === Schemas.vscodeRemote ? Schemas.file : value.scheme) : null); this._filenameKey.set(value ? basename(value) : null); this._dirnameKey.set(value ? dirname(value).fsPath : null); this._pathKey.set(value ? value.fsPath : null); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 088b128bd827e..21337a635d911 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -27,7 +27,7 @@ import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import Severity from 'vs/base/common/severity'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewDescriptorService, IAddedViewDescriptorRef } from 'vs/workbench/common/views'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; @@ -59,7 +59,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { isWeb } from 'vs/base/common/platform'; import { installLocalInRemoteIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; - +import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); const SearchIntalledExtensionsContext = new RawContextKey('searchInstalledExtensions', false); const SearchOutdatedExtensionsContext = new RawContextKey('searchOutdatedExtensions', false); @@ -524,6 +524,46 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE overlay.style.backgroundColor = overlayBackgroundColor; hide(overlay); + // NOTE@coder this UI element helps users understand the extension marketplace divergence + const extensionHelperLocalStorageKey = 'coder.extension-help-message'; + + if (localStorage.getItem(extensionHelperLocalStorageKey) === null) { + const helperHeader = append(this.root, $('.header')); + helperHeader.id = 'codeServerMarketplaceHelper'; + helperHeader.style.height = 'auto'; + helperHeader.style.fontWeight = '600'; + helperHeader.style.padding = 'padding: 5px 16px'; + helperHeader.style.position = 'relative'; + + const helperText = append(helperHeader, $('div')); + + + // We call this function because it gives us access to the current theme + // Then we can apply the link color to the links in the helper header + registerThemingParticipant((theme) => { + const linkColor = theme.getColor(textLinkForeground); + + append(helperText, $('div', { style: 'margin-bottom: 8px;' }, + $('p', { style: 'margin-bottom: 0; display: flex; align-items: center' }, + $('span', { class: 'codicon codicon-warning', style: 'margin-right: 2px; color: #C4A103' }), + 'WARNING'), + $('p', { style: 'margin-top: 0; margin-bottom: 4px' }, + 'These extensions are not official. Find additional open-source extensions ', + $('a', { style: `color: ${linkColor}`, href: 'https://open-vsx.org/', target: '_blank' }, 'here'), + '. See ', + $('a', { style: `color: ${linkColor}`, href: 'https://github.com/cdr/code-server/blob/master/docs/FAQ.md#differences-compared-to-vs-code', target: '_blank' }, 'docs'), + '.' + ))); + }); + + const dismiss = append(helperHeader, $('span', { style: `display: 'block'; textAlign: 'right'; cursor: 'pointer';`, tabindex: '0' }, 'Dismiss')); + + dismiss.onclick = () => { + helperHeader.remove(); + localStorage.setItem(extensionHelperLocalStorageKey, 'viewed'); + }; + } + const header = append(this.root, $('.header')); const placeholder = localize('searchExtensions', "Search Extensions in Marketplace"); diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index ad72a5cc2c43c..5b7af077020a9 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -298,9 +298,17 @@ viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { order: 1 }); +/** + * @coder + * We use `OpenFolderAction.ID` instead of `commandId` + * because for some reason, the command `openFileFolder` + * does not work as expected and causes the "Open Folder" + * command to not work + * See: https://github.com/cdr/code-server/issues/3457 + */ viewsRegistry.registerViewWelcomeContent(EmptyView.ID, { content: localize({ key: 'noFolderHelp', comment: ['Please do not translate the word "commmand", it is part of our internal syntax which must not change'] }, - "You have not yet opened a folder.\n[Open Folder](command:{0})", commandId), + "You have not yet opened a folder.\n[Open Folder](command:{0})", OpenFolderAction.ID), when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext)), group: ViewContentGroups.Open, order: 1 diff --git a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts index a405271ac1c1c..c32048e7df18c 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts @@ -5,13 +5,14 @@ import { escape } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; +import product from 'vs/platform/product/common/product'; export default () => `

${escape(localize('welcomePage.vscode', "Visual Studio Code"))}

-

${escape(localize({ key: 'welcomePage.editingEvolved', comment: ['Shown as subtitle on the Welcome page.'] }, "Editing evolved"))}

+

VS Code v${product.version}

+
+

code-server ${escape(localize('welcomePage.help', "Help"))}

+ +

${escape(localize('welcomePage.help', "Help"))}

    diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 2df033a4409b3..5b16a93ec88b8 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -357,7 +357,7 @@ class WelcomePage extends Disposable { const prodName = container.querySelector('.welcomePage .title .caption') as HTMLElement; if (prodName) { - prodName.textContent = this.productService.nameLong; + prodName.textContent = `code-server v${this.productService.codeServerVersion}`; } recentlyOpened.then(({ workspaces }) => { diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index b4f4bec0a93b6..724033aaa9099 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -120,8 +120,37 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get logFile(): URI { return joinPath(this.options.logsPath, 'window.log'); } + /** + * Path helper + * + * @coder modified to prefer local user data. + */ @memoize - get userRoamingDataHome(): URI { return URI.file('/User').with({ scheme: Schemas.userData }); } + get userRoamingDataHome(): URI { + if (this.userDataPath) { + return joinPath(URI.file(this.userDataPath).with({ scheme: Schemas.vscodeRemote }), 'User'); + } + + return URI.file('/User').with({ scheme: Schemas.userData }); + } + + /** + * Local and persistant user data directory. + * @coder The browser environment service payload should include a `userDataPath` + * matching the implementation used in `NativeEnvironmentService` + * This solves two problems: + * 1. Extensions running in the browser (like Vim) might use these paths + * directly instead of using the file service and most likely can't write + * to `/User` on disk. + * 2. Settings will be stored in the file system instead of in browser + * storage. Using browser storage makes sharing or seeding settings + * between browsers difficult. We may want to revisit this once/if we get + * settings sync. + */ + @memoize + get userDataPath(): string | undefined { + return this.payload?.get('userDataPath'); + } @memoize get settingsResource(): URI { return joinPath(this.userRoamingDataHome, 'settings.json'); } @@ -318,7 +347,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment extensionHostDebugEnvironment.params.port = parseInt(value); break; case 'enableProposedApi': - extensionHostDebugEnvironment.extensionEnabledProposedApi = []; + try { + extensionHostDebugEnvironment.extensionEnabledProposedApi = JSON.parse(value); + } catch (error) { + console.error(error); + extensionHostDebugEnvironment.extensionEnabledProposedApi = []; + } break; } } diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index 627c10c1d70ea..1cc9d5444f570 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -321,7 +321,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench } } } - return true; + return false; // NOTE@coder: Don't disable anything by extensionKind. } return false; } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 97c82ff1aecca..a67de12609160 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -26,6 +26,7 @@ import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementServ import { Promises } from 'vs/base/common/async'; import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; +import { isWeb } from 'vs/base/common/platform'; export class ExtensionManagementService extends Disposable implements IWorkbenchExtensionManagementService { @@ -316,6 +317,11 @@ export class ExtensionManagementService extends Disposable implements IWorkbench } } + // NOTE@coder: Fall back to installing on the remote server on web. + if (isWeb && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer; + } + // Local server can accept any extension. So return local server if not compatible server found. return this.extensionManagementServerService.localExtensionManagementServer; } diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index 7d3d4c30dc55c..be6d7f5c996dc 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -27,6 +27,8 @@ export interface IExtHostReduceGraceTimeMessage { type: 'VSCODE_EXTHOST_IPC_REDUCE_GRACE_TIME'; } +export type IExtHostMessage = IExtHostReadyMessage | IExtHostSocketMessage | IExtHostReduceGraceTimeMessage; + export const enum MessageType { Initialized, Ready, diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index d238526f526c7..6737308ee0859 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -88,6 +88,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { } public start(): Promise { + this._logService.info('>>> STARTING REMOT EXT HOST'); const options: IConnectionOptions = { commit: this._productService.commit, socketFactory: this._socketFactory, @@ -101,6 +102,8 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { logService: this._logService, ipcLogger: null }; + this._logService.info('>>> RESOLVING AUTH'); + return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolverResult) => { const startParams: IRemoteExtensionHostStartParams = { @@ -124,11 +127,13 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { if (!debugOk) { startParams.break = false; } - + this._logService.info('>>> CONNECTING REMOTE AGENT EXT HOST'); return connectRemoteAgentExtensionHost(options, startParams).then(result => { let { protocol, debugPort } = result; const isExtensionDevelopmentDebug = typeof debugPort === 'number'; if (debugOk && this._environmentService.isExtensionDevelopment && this._environmentService.debugExtensionHost.debugId && debugPort) { + this._logService.info('>>> ATTACHING DEBUG'); + this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, debugPort, this._initDataProvider.remoteAuthority); } @@ -145,6 +150,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { // 1) wait for the incoming `ready` event and send the initialization data. // 2) wait for the incoming `initialized` event. return new Promise((resolve, reject) => { + this._logService.info('>>> WAITING FOR MESSAGE'); let handle = setTimeout(() => { reject('timeout'); @@ -153,6 +159,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { let logFile: URI; const disposable = protocol.onMessage(msg => { + this._logService.info('>>> PROTOCOL MESSAGE RECEIVED', msg.toString()); if (isMessageOfType(msg, MessageType.Ready)) { // 1) Extension Host is ready to receive messages, initialize it diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index e6d56c8ff7b75..24793921376fc 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -17,12 +17,14 @@ import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage, ExtensionHostExitCode } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; +import { IRawURITransformer, IURITransformer, URITransformer } from 'vs/base/common/uriIpc'; import { Promises } from 'vs/base/node/pfs'; +import { createServerURITransformer } from 'vs/base/common/uriServer'; import { realpath } from 'vs/base/node/extpath'; import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService'; import { RunOnceScheduler } from 'vs/base/common/async'; import { boolean } from 'vs/editor/common/config/editorOptions'; +import * as proxyAgent from 'vs/base/node/proxy_agent'; import 'vs/workbench/api/common/extHost.common.services'; import 'vs/workbench/api/node/extHost.node.services'; @@ -122,6 +124,7 @@ function _createExtHostProtocol(): Promise { if (msg && msg.type === 'VSCODE_EXTHOST_IPC_SOCKET') { const initialDataChunk = VSBuffer.wrap(Buffer.from(msg.initialDataChunk, 'base64')); let socket: NodeSocket | WebSocketNodeSocket; + if (msg.skipWebSocketFrames) { socket = new NodeSocket(handle); } else { @@ -313,12 +316,16 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise>> WANT TO SEND READY'); // Tell the outside that we are ready to receive messages protocol.send(createMessageOfType(MessageType.Ready)); }); } export async function startExtensionHostProcess(): Promise { + // NOTE@coder: add proxy agent patch + proxyAgent.monkeyPatch(true); + performance.mark(`code/extHost/willConnectToRenderer`); const protocol = await createExtHostProtocol(); performance.mark(`code/extHost/didConnectToRenderer`); @@ -340,13 +347,18 @@ export async function startExtensionHostProcess(): Promise { // Attempt to load uri transformer let uriTransformer: IURITransformer | null = null; - if (initData.remote.authority && args.uriTransformerPath) { - try { - const rawURITransformerFactory = require.__$__nodeRequire(args.uriTransformerPath); - const rawURITransformer = rawURITransformerFactory(initData.remote.authority); - uriTransformer = new URITransformer(rawURITransformer); - } catch (e) { - console.error(e); + + if (initData.remote.authority) { + if (args.uriTransformerPath) { + try { + const rawURITransformerFactory = require.__$__nodeRequire(args.uriTransformerPath); + const rawURITransformer = rawURITransformerFactory(initData.remote.authority); + uriTransformer = new URITransformer(rawURITransformer); + } catch (e) { + console.error(e); + } + } else { + uriTransformer = createServerURITransformer(initData.remote.authority); } } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 21ab06e1c6af7..1f013d6856b96 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -71,6 +71,14 @@ export interface IWorkspaceProvider { open(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): Promise; } +/** + * @coder Similar to the workspace provider, without `open` helper. + * This allows for JSON serialization when passing options to a client. + */ +export interface IServerWorkspaceProvider extends Omit { + payload: [['userDataPath', string], ['enableProposedApi', string]]; +} + enum HostShutdownReason { /** diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts index 6d71a818bbeaf..0fcefe306e617 100644 --- a/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts @@ -406,6 +406,9 @@ export class BrowserKeyboardMapperFactoryBase { // } // return null; + }).catch(() => { + // NOTE@coder: It looks like the intention was to catch this error but + // a try/catch won't do it when using promises without `await`. }); } catch { // getLayoutMap can throw if invoked from a nested browsing context diff --git a/src/vs/workbench/services/localizations/browser/localizationsService.ts b/src/vs/workbench/services/localizations/browser/localizationsService.ts new file mode 100644 index 0000000000000..9a26f6b1a3d9a --- /dev/null +++ b/src/vs/workbench/services/localizations/browser/localizationsService.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * @file This appears to fix localization as of v1.58.2 + * However, upstream diverges from this behavior: + * https://github.com/microsoft/vscode/commit/3ef4aa861a38a1aac95e3f560e073fe98929ddda + */ + +import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; + +// @ts-ignore: interface is implemented via proxy +export class LocalizationsService implements ILocalizationsService { + + declare readonly _serviceBrand: undefined; + + constructor( + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + ) { + return ProxyChannel.toService(remoteAgentService.getConnection()!.getChannel('localizations')); + } +} + +registerSingleton(ILocalizationsService, LocalizationsService, true); diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index ae1618a515430..aef98e174f1bd 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -13,12 +13,14 @@ import { LogLevel } from 'vs/platform/log/common/log'; import { IUpdateProvider, IUpdate } from 'vs/workbench/services/update/browser/updateService'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; +import { IWorkspaceProvider, IWorkspace, IServerWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IProductConfiguration } from 'vs/base/common/product'; import { mark } from 'vs/base/common/performance'; import { ICredentialsProvider } from 'vs/workbench/services/credentials/common/credentials'; import { TunnelProviderFeatures } from 'vs/platform/remote/common/tunnel'; +// eslint-disable-next-line code-import-patterns +import { NLSConfiguration } from 'vs/base/node/languagePacks'; interface IResourceUriProvider { (uri: URI): URI; @@ -441,6 +443,28 @@ interface IWorkbenchConstructionOptions { } +/** + * @coder This is commonly used when initializing a code server. + */ +interface IOptionalPathURIs { + folderUri?: UriComponents + workspaceUri?: UriComponents +} + +/** + * @coder Standard workbench constructor options with additional server paths. + * JSON serializable. + */ +interface IServerWorkbenchConstructionOptions extends Omit, IOptionalPathURIs { + readonly workspaceProvider: IServerWorkspaceProvider + /** @TODO still used? */ + readonly logLevel?: number + + readonly remoteUserDataUri: UriComponents + readonly nlsConfiguration: NLSConfiguration + readonly commit: string +} + interface IDevelopmentOptions { /** @@ -609,6 +633,8 @@ export { // Factory create, IWorkbenchConstructionOptions, + IServerWorkbenchConstructionOptions, + IOptionalPathURIs, IWorkbench, // Basic Types diff --git a/yarn.lock b/yarn.lock index 62c3a19de68dd..7918274e4be6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -538,6 +538,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" integrity sha512-XYmBiy+ohOR4Lh5jE379fV2IU+6Jn4g5qASinhitfyO71b/sCo6MKsMLF5tc7Zf2CE8hViVQyYSobJNke8OvUw== +"@types/proxy-from-env@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/proxy-from-env/-/proxy-from-env-1.0.1.tgz#b5f3e99230ca4518af196c18267055fc51f892b7" + integrity sha512-luG++TFHyS61eKcfkR1CVV6a1GMNXDjtqEQIIfaSHax75xp0HU3SlezjOi1yqubJwrG8e9DeW59n6wTblIDwFg== + dependencies: + "@types/node" "*" + "@types/q@^1.5.1": version "1.5.4" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" @@ -572,6 +579,13 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ== +"@types/tar-stream@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-2.2.1.tgz#7cb4516fe6d1a926a37b7733905c50885718e7ad" + integrity sha512-zhcfACZ4HavArMutfAB1/ApfSx44kNF2zyytU4mbO1dGCT/y9kL2IZwRDRyYYtBUxW6LRparZpLoX8i67b6IZw== + dependencies: + "@types/node" "*" + "@types/trusted-types@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.6.tgz#569b8a08121d3203398290d602d84d73c8dcf5da" @@ -643,6 +657,13 @@ resolved "https://registry.yarnpkg.com/@types/winreg/-/winreg-1.2.30.tgz#91d6710e536d345b9c9b017c574cf6a8da64c518" integrity sha1-kdZxDlNtNFucmwF8V0z2qNpkxRg= +"@types/ws@^7.4.7": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + "@types/yauzl@^2.9.1": version "2.9.1" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" @@ -930,7 +951,7 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" -agent-base@^6.0.2: +agent-base@^6.0.0, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -1319,6 +1340,13 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types@^0.13.2: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1716,7 +1744,7 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= -bytes@^3.0.0: +bytes@3.1.0, bytes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== @@ -2826,6 +2854,15 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +degenerator@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-2.2.0.tgz#49e98c11fa0293c5b26edfbb52f15729afcdb254" + integrity sha512-aiQcQowF01RxFI4ZLFMpzyotbQonhNpBao6dkI8JPk5a+hmSjR5ErHp2CQySmQe8os3VBqLCIh87nDBgZXvsmg== + dependencies: + ast-types "^0.13.2" + escodegen "^1.8.1" + esprima "^4.0.0" + delayed-stream@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.6.tgz#a2646cb7ec3d5d7774614670a7a65de0c173edbc" @@ -2841,6 +2878,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -3264,6 +3306,18 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escodegen@^1.8.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-plugin-jsdoc@^19.1.0: version "19.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-19.1.0.tgz#fcc17f0378fdd6ee1c847a79b7211745cb05d014" @@ -3416,7 +3470,7 @@ espree@^6.1.2: acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -3435,7 +3489,7 @@ esrecurse@^4.1.0, esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -4144,7 +4198,7 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-uri@^3.0.2: +get-uri@3, get-uri@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== @@ -4859,6 +4913,17 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-errors@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -4867,7 +4932,7 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-proxy-agent@^4.0.1: +http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -4890,6 +4955,14 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +https-proxy-agent@5, https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + https-proxy-agent@^2.2.3: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" @@ -4906,14 +4979,6 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - husky@^0.13.1: version "0.13.4" resolved "https://registry.yarnpkg.com/husky/-/husky-0.13.4.tgz#48785c5028de3452a51c48c12c4f94b2124a1407" @@ -4929,18 +4994,18 @@ iconv-lite-umd@0.6.8: resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0" integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A== -iconv-lite@^0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== - -iconv-lite@^0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== + icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -5034,7 +5099,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6552,6 +6617,11 @@ neo-async@^2.5.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +netmask@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + next-tick@1, next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" @@ -6916,7 +6986,7 @@ optimist@0.3.5: dependencies: wordwrap "~0.0.2" -optionator@^0.8.2, optionator@^0.8.3: +optionator@^0.8.1, optionator@^0.8.2, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -7061,6 +7131,30 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-4.1.0.tgz#66883eeabadc915fc5e95457324cb0f0ac78defb" + integrity sha512-ejNgYm2HTXSIYX9eFlkvqFp8hyJ374uDf0Zq5YUAifiSh1D6fo+iBivQZirGvVv8dCYUsLhmLBRhlAYvBKI5+Q== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + get-uri "3" + http-proxy-agent "^4.0.1" + https-proxy-agent "5" + pac-resolver "^4.1.0" + raw-body "^2.2.0" + socks-proxy-agent "5" + +pac-resolver@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-4.2.0.tgz#b82bcb9992d48166920bc83c7542abb454bd9bdd" + integrity sha512-rPACZdUyuxT5Io/gFKUeeZFfE5T7ve7cAkE5TUZRRfuKP0u5Hocwe48X7ZEm6mYB+bTB0Qf+xlVlA/RM/i6RCQ== + dependencies: + degenerator "^2.2.0" + ip "^1.1.5" + netmask "^2.0.1" + pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -7800,7 +7894,21 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -proxy-from-env@^1.1.0: +proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-4.0.1.tgz#326c3250776c7044cd19655ccbfadf2e065a045c" + integrity sha512-ODnQnW2jc/FUVwHHuaZEfN5otg/fMbvMxz9nMSUQfJ9JU7q2SZvSULSsjLloVgJOiv9yhc8GlNMKc4GkFmcVEA== + dependencies: + agent-base "^6.0.0" + debug "4" + http-proxy-agent "^4.0.0" + https-proxy-agent "^5.0.0" + lru-cache "^5.1.1" + pac-proxy-agent "^4.1.0" + proxy-from-env "^1.0.0" + socks-proxy-agent "^5.0.0" + +proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -7945,6 +8053,16 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" +raw-body@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -8551,6 +8669,11 @@ setimmediate@^1.0.4: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -8678,6 +8801,15 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socks-proxy-agent@5: + version "5.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" + integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== + dependencies: + agent-base "^6.0.2" + debug "4" + socks "^2.3.3" + socks-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" @@ -8891,6 +9023,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +"statuses@>= 1.5.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -9230,7 +9367,7 @@ tar-fs@^2.0.0: pump "^3.0.0" tar-stream "^2.1.4" -tar-stream@^2.1.4: +tar-stream@^2.1.4, tar-stream@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== @@ -9478,6 +9615,11 @@ to-through@^2.0.0: dependencies: through2 "^2.0.3" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -9543,6 +9685,11 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -9725,6 +9872,11 @@ universalify@^0.1.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + unquote@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"