diff --git a/ci/build/build-vscode.sh b/ci/build/build-vscode.sh index faddf46e6c41..85c75ea864d7 100755 --- a/ci/build/build-vscode.sh +++ b/ci/build/build-vscode.sh @@ -10,7 +10,7 @@ main() { cd "$(dirname "${0}")/../.." cd lib/vscode - yarn gulp compile-build compile-extensions-build + yarn gulp compile-build compile-web compile-extensions-build yarn gulp optimize --gulpfile ./coder.js if [[ $MINIFY ]]; then yarn gulp minify --gulpfile ./coder.js diff --git a/ci/dev/watch.ts b/ci/dev/watch.ts index 1503256c088e..63c56e6d0f27 100644 --- a/ci/dev/watch.ts +++ b/ci/dev/watch.ts @@ -8,7 +8,7 @@ async function main(): Promise { try { const watcher = new Watcher() await watcher.watch() - } catch (error) { + } catch (error: any) { console.error(error.message) process.exit(1) } @@ -38,6 +38,9 @@ class Watcher { } const vscode = cp.spawn("yarn", ["watch"], { cwd: this.vscodeSourcePath }) + + const vscodeWebExtensions = cp.spawn("yarn", ["watch-web"], { cwd: this.vscodeSourcePath }) + const tsc = cp.spawn("tsc", ["--watch", "--pretty", "--preserveWatchOutput"], { cwd: this.rootPath }) const plugin = process.env.PLUGIN_DIR ? cp.spawn("yarn", ["build", "--watch"], { cwd: process.env.PLUGIN_DIR }) @@ -48,6 +51,10 @@ class Watcher { vscode.removeAllListeners() vscode.kill() + Watcher.log("killing vs code web extension watcher") + vscodeWebExtensions.removeAllListeners() + vscodeWebExtensions.kill() + Watcher.log("killing tsc") tsc.removeAllListeners() tsc.kill() @@ -75,10 +82,17 @@ class Watcher { Watcher.log("vs code watcher terminated unexpectedly") cleanup(code) }) + + vscodeWebExtensions.on("exit", (code) => { + Watcher.log("vs code extension watcher terminated unexpectedly") + cleanup(code) + }) + tsc.on("exit", (code) => { Watcher.log("tsc terminated unexpectedly") cleanup(code) }) + if (plugin) { plugin.on("exit", (code) => { Watcher.log("plugin terminated unexpectedly") @@ -86,8 +100,10 @@ class Watcher { }) } + vscodeWebExtensions.stderr.on("data", (d) => process.stderr.write(d)) vscode.stderr.on("data", (d) => process.stderr.write(d)) tsc.stderr.on("data", (d) => process.stderr.write(d)) + if (plugin) { plugin.stderr.on("data", (d) => process.stderr.write(d)) } diff --git a/lib/vscode/package.json b/lib/vscode/package.json index 6bab710c9941..647c9de3cbc6 100644 --- a/lib/vscode/package.json +++ b/lib/vscode/package.json @@ -60,6 +60,7 @@ "dependencies": { "applicationinsights": "1.0.8", "chokidar": "3.5.1", + "express": "^5.0.0-alpha.8", "graceful-fs": "4.2.6", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", @@ -91,15 +92,13 @@ "yazl": "^2.4.3" }, "devDependencies": { - "7zip": "0.0.6", - "@types/proxy-from-env": "^1.0.1", - "@types/tar-stream": "^2.2.1", "@types/applicationinsights": "0.20.0", "@types/chokidar": "2.1.3", "@types/cookie": "^0.3.3", "@types/copy-webpack-plugin": "^6.0.3", "@types/cssnano": "^4.0.0", "@types/debug": "4.1.5", + "@types/express": "^4.17.8", "@types/graceful-fs": "4.1.2", "@types/gulp-postcss": "^8.0.0", "@types/http-proxy-agent": "^2.0.1", @@ -107,7 +106,9 @@ "@types/minimist": "^1.2.1", "@types/mocha": "^8.2.0", "@types/node": "14.x", + "@types/proxy-from-env": "^1.0.1", "@types/sinon": "^1.16.36", + "@types/tar-stream": "^2.2.1", "@types/trusted-types": "^1.0.6", "@types/vscode-windows-registry": "^1.0.0", "@types/webpack": "^4.41.25", @@ -120,6 +121,7 @@ "@types/yazl": "^2.4.2", "@typescript-eslint/eslint-plugin": "3.2.0", "@typescript-eslint/parser": "^3.3.0", + "7zip": "0.0.6", "ansi-colors": "^3.2.3", "asar": "^3.0.3", "chromium-pickle-js": "^0.2.0", @@ -129,14 +131,13 @@ "cssnano": "^5.0.2", "debounce": "^1.0.0", "deemon": "^1.4.0", - "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", + "eslint": "6.8.0", "event-stream": "3.3.4", "fancy-log": "^1.3.3", "fast-plist": "0.1.2", "file-loader": "^4.2.0", "glob": "^5.0.13", - "gulp": "^4.0.0", "gulp-bom": "^3.0.0", "gulp-buffer": "0.0.2", "gulp-concat": "^2.6.1", @@ -155,6 +156,7 @@ "gulp-sourcemaps": "^3.0.0", "gulp-tsb": "4.0.6", "gulp-vinyl-zip": "^2.1.2", + "gulp": "^4.0.0", "husky": "^0.13.1", "innosetup": "6.0.5", "is": "^3.1.0", @@ -170,9 +172,9 @@ "minimatch": "^3.0.4", "minimist": "^1.2.5", "mkdirp": "^1.0.4", - "mocha": "^8.2.1", "mocha-junit-reporter": "^2.0.0", "mocha-multi-reporters": "^1.5.1", + "mocha": "^8.2.1", "npm-run-all": "^4.1.5", "opn": "^6.0.0", "optimist": "0.3.5", @@ -184,22 +186,22 @@ "rcedit": "^1.1.0", "request": "^2.85.0", "sinon": "^1.17.2", - "source-map": "0.6.1", "source-map-support": "^0.3.2", + "source-map": "0.6.1", "style-loader": "^1.0.0", "ts-loader": "^6.2.1", "tsec": "0.1.4", - "typescript": "^4.4.0-dev.20210528", "typescript-formatter": "7.1.0", + "typescript": "^4.4.0-dev.20210528", "underscore": "^1.12.1", - "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", + "vinyl": "^2.0.0", "vscode-debugprotocol": "1.47.0", "vscode-nls-dev": "^3.3.1", "vscode-telemetry-extractor": "^1.7.0", - "webpack": "^4.43.0", "webpack-cli": "^3.3.12", "webpack-stream": "^5.2.1", + "webpack": "^4.43.0", "xml2js": "^0.4.17", "yaserver": "^0.2.0" }, diff --git a/lib/vscode/resources/web/code-web.d.ts b/lib/vscode/resources/web/code-web.d.ts deleted file mode 100644 index 4e555df8ac87..000000000000 --- a/lib/vscode/resources/web/code-web.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Coder Technologies. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * @file this is a temporary type definition. - * Upstream seems to be modifying the web module often and will likely switch to TypeScript. - */ - -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/lib/vscode/resources/web/code-web.js b/lib/vscode/resources/web/code-web.js index ba1b7f747854..645e36cd17d9 100644 --- a/lib/vscode/resources/web/code-web.js +++ b/lib/vscode/resources/web/code-web.js @@ -4,7 +4,6 @@ * 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 @@ -42,17 +41,15 @@ const WEB_PLAYGROUND_VERSION = '0.0.10'; const args = minimist(process.argv, { boolean: [ - 'launch', + 'no-launch', 'help', 'verbose', 'wrap-iframe', 'enable-sync', ], string: [ - 'server', 'scheme', 'host', - 'remote-authority', 'port', 'local_port', 'extension', @@ -63,13 +60,12 @@ const args = minimist(process.argv, { if (args.help) { console.log( 'yarn web [options]\n' + - ' --launch Open VSCode web in the browser\n' + + ' --no-launch Do not 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' + @@ -225,10 +221,8 @@ 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, webConfigJSON) => { +const requestHandler = (req, res) => { const parsedUrl = url.parse(req.url, true); const pathname = parsedUrl.pathname; @@ -259,9 +253,6 @@ const requestHandler = (req, res, webConfigJSON) => { 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') { @@ -270,11 +261,8 @@ const requestHandler = (req, res, webConfigJSON) => { } 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 extensions JSON + // builtin extnesions JSON return handleBuiltInExtensions(req, res, parsedUrl); } @@ -286,28 +274,26 @@ const requestHandler = (req, res, webConfigJSON) => { } }; -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 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 @@ -331,20 +317,6 @@ 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 @@ -448,7 +420,6 @@ async function handleRoot(req, res) { ? req.headers['host'].replace(':' + PORT, ':' + SECONDARY_PORT) : `${HOST}:${SECONDARY_PORT}` ); - const webConfigJSON = { folderUri: folderUri, staticExtensions, @@ -486,37 +457,6 @@ 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) { - 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_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 @@ -676,7 +616,7 @@ const mapExtToMediaMimes = { function getMediaMime(forPath) { const ext = path.extname(forPath); - return mapExtToMediaMimes[ext.toLowerCase()] || 'text/plain'; + return mapExtToMediaMimes[ext.toLowerCase()]; } /** @@ -717,6 +657,4 @@ async function serveFile(req, res, filePath, responseHeaders = Object.create(nul if (args.launch !== false) { opn(`${SCHEME}://${HOST}:${PORT}`); -} - -exports.requestHandler = requestHandler; +} \ No newline at end of file diff --git a/lib/vscode/src/vs/base/common/ipc.d.ts b/lib/vscode/src/vs/base/common/ipc.d.ts deleted file mode 100644 index 84526e70beb7..000000000000 --- a/lib/vscode/src/vs/base/common/ipc.d.ts +++ /dev/null @@ -1,104 +0,0 @@ -// TODO@teffen partial fix for cross-project interface issues. -// This shouldn't be necessary after code-server stops handling requests directly. - -// import { IServerWorkbenchConstructionOptions } from '../../workbench/workbench.web.api'; -export type IServerWorkbenchConstructionOptions = any; - -export interface CodeServerConfiguration { - authed: boolean; - base: string; - csStaticBase: string; - disableUpdateCheck: boolean; - logLevel: number; -} - -export interface InitMessage { - type: 'init'; - id: string; - options: VscodeOptions; -} - -export type Query = { [key: string]: string | string[] | undefined | Query | Query[] }; - -export interface SocketMessage { - type: 'socket'; - query: Query; - permessageDeflate: boolean; -} - -export interface CliMessage { - type: 'cli'; - args: Args; -} - -export interface OpenCommandPipeArgs { - type: 'open'; - fileURIs?: string[]; - folderURIs: string[]; - forceNewWindow?: boolean; - diffMode?: boolean; - addMode?: boolean; - gotoLineMode?: boolean; - forceReuseWindow?: boolean; - waitMarkerFilePath?: string; -} - -export type CodeServerMessage = InitMessage | SocketMessage | CliMessage; - -export interface ReadyMessage { - type: 'ready'; -} - -export interface OptionsMessage { - id: string; - type: 'options'; - options: IServerWorkbenchConstructionOptions; -} - -export type VscodeMessage = ReadyMessage | OptionsMessage; - -export interface StartPath { - url: string; - workspace: boolean; -} - -export interface Args { - 'user-data-dir'?: string; - - 'enable-proposed-api'?: string[]; - 'extensions-dir'?: string; - 'builtin-extensions-dir'?: string; - 'extra-extensions-dir'?: string[]; - 'extra-builtin-extensions-dir'?: string[]; - 'ignore-last-opened'?: boolean; - - locale?: string; - - log?: string; - verbose?: boolean; - - _: string[]; -} - -export interface VscodeOptions { - readonly args: Args; - readonly remoteAuthority: string; - readonly startPath?: StartPath; - readonly csStaticBase: string; -} - -export interface VscodeOptionsMessage extends VscodeOptions { - readonly id: string; -} - -export interface UriComponents { - readonly scheme: string; - readonly authority: string; - readonly path: string; - readonly query: string; - readonly fragment: string; -} - -export interface WorkbenchOptionsMessage { - id: string; -} diff --git a/lib/vscode/src/vs/base/common/platform.ts b/lib/vscode/src/vs/base/common/platform.ts index 6de08a982116..385f7f7ba732 100644 --- a/lib/vscode/src/vs/base/common/platform.ts +++ b/lib/vscode/src/vs/base/common/platform.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // eslint-disable-next-line code-import-patterns -import { IServerWorkbenchConstructionOptions } from '../../workbench/workbench.web.api'; +import { IWorkbenchWebConfiguration } from '../../workbench/workbench.web.api'; const LANGUAGE_DEFAULT = 'en'; @@ -110,7 +110,7 @@ if (typeof navigator === 'object' && !isElectronRenderer) { const rawWorkbenchConfig = el && el.getAttribute('data-settings'); if (rawWorkbenchConfig) { try { - const workbenchConfig: IServerWorkbenchConstructionOptions = JSON.parse(rawWorkbenchConfig); + const workbenchConfig: IWorkbenchWebConfiguration = JSON.parse(rawWorkbenchConfig); const nlsConfig = workbenchConfig.nlsConfiguration; _locale = nlsConfig.locale; diff --git a/lib/vscode/src/vs/base/common/product.ts b/lib/vscode/src/vs/base/common/product.ts index 7fd5a671d486..419099dae330 100644 --- a/lib/vscode/src/vs/base/common/product.ts +++ b/lib/vscode/src/vs/base/common/product.ts @@ -31,10 +31,14 @@ export type ExtensionVirtualWorkspaceSupport = { }; export interface IProductConfiguration { - /** @coder BEGIN */ + // @coder BEGIN /** @deprecated Should be replaced with code-oss version. */ readonly codeServerVersion?: string; - /** @coder END */ + readonly authed?: boolean; + readonly logoutEndpointUrl: string; + readonly icons: Array<{ src: string; type: string; sizes: string }>; + + // @coder END */ readonly version: string; readonly date?: string; diff --git a/lib/vscode/src/vs/base/common/types.ts b/lib/vscode/src/vs/base/common/types.ts index 7d17b82d6ec3..6c1f3a02227f 100644 --- a/lib/vscode/src/vs/base/common/types.ts +++ b/lib/vscode/src/vs/base/common/types.ts @@ -273,13 +273,12 @@ export type Dto = T extends { toJSON(): infer U } : T; /** - * Marks optional types as required. + * Marks readonly properties as wriatable. */ -export type Complete = { - [P in keyof Required]: Pick extends Required> ? T[P] : (NonNullable); -}; +export type Writeable = { -readonly [P in keyof T]: T[P] }; export function NotImplementedProxy(name: string): { new(): T } { + return class { constructor() { return new Proxy({}, { diff --git a/lib/vscode/src/vs/base/common/util.ts b/lib/vscode/src/vs/base/common/util.ts deleted file mode 120000 index 625cfa1cbf97..000000000000 --- a/lib/vscode/src/vs/base/common/util.ts +++ /dev/null @@ -1 +0,0 @@ -../../../../../../src/common/util.ts \ No newline at end of file diff --git a/lib/vscode/src/vs/base/parts/ipc/common/ipc.net.ts b/lib/vscode/src/vs/base/parts/ipc/common/ipc.net.ts index 44bdbf30f2ae..761384957060 100644 --- a/lib/vscode/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/lib/vscode/src/vs/base/parts/ipc/common/ipc.net.ts @@ -756,11 +756,6 @@ 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/lib/vscode/src/vs/base/parts/ipc/node/ipc.net.ts b/lib/vscode/src/vs/base/parts/ipc/node/ipc.net.ts index aa2512c4272d..f9daca4d8ddf 100644 --- a/lib/vscode/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/lib/vscode/src/vs/base/parts/ipc/node/ipc.net.ts @@ -18,7 +18,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Platform, platform } from 'vs/base/common/platform'; export class NodeSocket implements ISocket { - public readonly socket: Socket; private readonly _errorListener: (err: any) => void; @@ -50,21 +49,21 @@ export class NodeSocket implements ISocket { const listener = (buff: Buffer) => _listener(VSBuffer.wrap(buff)); this.socket.on('data', listener); return { - dispose: () => this.socket.off('data', listener) + dispose: () => this.socket.off('data', listener), }; } public onClose(listener: () => void): IDisposable { this.socket.on('close', listener); return { - dispose: () => this.socket.off('close', listener) + dispose: () => this.socket.off('close', listener), }; } public onEnd(listener: () => void): IDisposable { this.socket.on('end', listener); return { - dispose: () => this.socket.off('end', listener) + dispose: () => this.socket.off('end', listener), }; } @@ -136,21 +135,20 @@ export class NodeSocket implements ISocket { } const enum Constants { - MinHeaderByteSize = 2 + MinHeaderByteSize = 2, } const enum ReadState { PeekHeader = 1, ReadHeader = 2, ReadBody = 3, - Fin = 4 + Fin = 4, } /** * See https://tools.ietf.org/html/rfc6455#section-5.2 */ export class WebSocketNodeSocket extends Disposable implements ISocket { - public readonly socket: NodeSocket; public readonly permessageDeflate: boolean; private _totalIncomingWireBytes: number; @@ -174,7 +172,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { state: ReadState.PeekHeader, readLen: Constants.MinHeaderByteSize, fin: 0, - mask: 0 + mask: 0, }; public get totalIncomingWireBytes(): number { @@ -226,9 +224,9 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { // To simplify our logic, we don't negociate the window size // and simply dedicate (2^15) / 32kb per web socket this._zlibInflate = zlib.createInflateRaw({ - windowBits: 15 + windowBits: 15, }); - this._zlibInflate.on('error', (err) => { + this._zlibInflate.on('error', err => { // zlib errors are fatal, since we have no idea how to recover console.error(err); onUnexpectedError(err); @@ -245,9 +243,9 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { } this._zlibDeflate = zlib.createDeflateRaw({ - windowBits: 15 + windowBits: 15, }); - this._zlibDeflate.on('error', (err) => { + this._zlibDeflate.on('error', err => { // zlib errors are fatal, since we have no idea how to recover console.error(err); onUnexpectedError(err); @@ -269,9 +267,11 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { public override dispose(): void { if (this._zlibDeflateFlushWaitingCount > 0) { // Wait for any outstanding writes to finish before disposing - this._register(this._onDidZlibFlush.event(() => { - this.dispose(); - })); + this._register( + this._onDidZlibFlush.event(() => { + this.dispose(); + }), + ); } else { this.socket.dispose(); super.dispose(); @@ -298,7 +298,7 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { this._zlibDeflateFlushWaitingCount++; // See https://zlib.net/manual.html#Constants - this._zlibDeflate.flush(/*Z_SYNC_FLUSH*/2, () => { + this._zlibDeflate.flush(/*Z_SYNC_FLUSH*/ 2, () => { this._zlibDeflateFlushWaitingCount--; let data = Buffer.concat(this._pendingDeflateData); this._pendingDeflateData.length = 0; @@ -375,7 +375,6 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { this._incomingData.acceptChunk(data); while (this._incomingData.byteLength >= this._state.readLen) { - if (this._state.state === ReadState.PeekHeader) { // peek to see if we can read the entire header const peekHeader = this._incomingData.peek(this._state.readLen); @@ -383,53 +382,34 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { const finBit = (firstByte & 0b10000000) >>> 7; const secondByte = peekHeader.readUInt8(1); const hasMask = (secondByte & 0b10000000) >>> 7; - const len = (secondByte & 0b01111111); + const len = secondByte & 0b01111111; this._state.state = ReadState.ReadHeader; this._state.readLen = Constants.MinHeaderByteSize + (hasMask ? 4 : 0) + (len === 126 ? 2 : 0) + (len === 127 ? 8 : 0); this._state.fin = finBit; this._state.mask = 0; - } else if (this._state.state === ReadState.ReadHeader) { // read entire header const header = this._incomingData.read(this._state.readLen); const secondByte = header.readUInt8(1); const hasMask = (secondByte & 0b10000000) >>> 7; - let len = (secondByte & 0b01111111); + let len = secondByte & 0b01111111; let offset = 1; if (len === 126) { - len = ( - header.readUInt8(++offset) * 2 ** 8 - + header.readUInt8(++offset) - ); + len = header.readUInt8(++offset) * 2 ** 8 + header.readUInt8(++offset); } else if (len === 127) { - len = ( - header.readUInt8(++offset) * 0 - + header.readUInt8(++offset) * 0 - + header.readUInt8(++offset) * 0 - + header.readUInt8(++offset) * 0 - + header.readUInt8(++offset) * 2 ** 24 - + header.readUInt8(++offset) * 2 ** 16 - + header.readUInt8(++offset) * 2 ** 8 - + header.readUInt8(++offset) - ); + len = header.readUInt8(++offset) * 0 + header.readUInt8(++offset) * 0 + header.readUInt8(++offset) * 0 + header.readUInt8(++offset) * 0 + header.readUInt8(++offset) * 2 ** 24 + header.readUInt8(++offset) * 2 ** 16 + header.readUInt8(++offset) * 2 ** 8 + header.readUInt8(++offset); } let mask = 0; if (hasMask) { - mask = ( - header.readUInt8(++offset) * 2 ** 24 - + header.readUInt8(++offset) * 2 ** 16 - + header.readUInt8(++offset) * 2 ** 8 - + header.readUInt8(++offset) - ); + mask = header.readUInt8(++offset) * 2 ** 24 + header.readUInt8(++offset) * 2 ** 16 + header.readUInt8(++offset) * 2 ** 8 + header.readUInt8(++offset); } this._state.state = ReadState.ReadBody; this._state.readLen = len; this._state.mask = mask; - } else if (this._state.state === ReadState.ReadBody) { // read body @@ -505,7 +485,7 @@ export const XDG_RUNTIME_DIR = process.env['XDG_RUNTIME_DIR' const safeIpcPathLengths: { [platform: number]: number } = { [Platform.Linux]: 107, - [Platform.Mac]: 103 + [Platform.Mac]: 103, }; export function createRandomIPCHandle(): string { @@ -564,13 +544,12 @@ function validateIPCHandleLength(handle: string): void { } export class Server extends IPCServer { - private static toClientConnectionEvent(server: NetServer): Event { const onConnection = Event.fromNodeEventEmitter(server, 'connection'); return Event.map(onConnection, socket => ({ protocol: new Protocol(new NodeSocket(socket)), - onDidClientDisconnect: Event.once(Event.fromNodeEventEmitter(socket, 'close')) + onDidClientDisconnect: Event.once(Event.fromNodeEventEmitter(socket, 'close')), })); } @@ -604,7 +583,7 @@ export function serve(hook: any): Promise { }); } -export function connect(options: { host: string, port: number }, clientId: string): Promise; +export function connect(options: { host: string; port: number }, clientId: string): Promise; export function connect(port: number, clientId: string): Promise; export function connect(namedPipe: string, clientId: string): Promise; export function connect(hook: any, clientId: string): Promise { diff --git a/lib/vscode/src/vs/code/browser/workbench/workbench.ts b/lib/vscode/src/vs/code/browser/workbench/workbench.ts index a6524607df91..1ba25b9af069 100644 --- a/lib/vscode/src/vs/code/browser/workbench/workbench.ts +++ b/lib/vscode/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 { create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IProductQualityChangeHandler, ISettingsSyncOptions, IServerWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; +import { create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IWindowIndicator, IProductQualityChangeHandler, ISettingsSyncOptions, IWorkbenchWebConfiguration } 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'; @@ -426,7 +426,7 @@ class WindowIndicator implements IWindowIndicator { throw new Error('Missing web configuration element'); } - const config: IServerWorkbenchConstructionOptions = JSON.parse(configElementAttribute); + const config: IWorkbenchWebConfiguration = JSON.parse(configElementAttribute); // Strip the protocol from the authority if it exists. const normalizeAuthority = (authority: string): string => authority.replace(/^https?:\/\//, ''); diff --git a/lib/vscode/src/vs/platform/environment/argumentParser.ts b/lib/vscode/src/vs/platform/environment/argumentParser.ts index 0be59fa6e828..37cd17969eff 100644 --- a/lib/vscode/src/vs/platform/environment/argumentParser.ts +++ b/lib/vscode/src/vs/platform/environment/argumentParser.ts @@ -3,7 +3,6 @@ * 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'; @@ -20,8 +19,7 @@ import { coalesce, distinct } from 'vs/base/common/arrays'; * @coder This is originally from `CodeMain` but has been separated for use in `CodeServer` */ export class ArgumentParser { - protected resolveArgs(): NativeParsedArgs { - + public resolveArgs(): NativeParsedArgs { // Parse arguments const args = this.validatePaths(parseMainProcessArgv(process.argv)); @@ -44,7 +42,6 @@ export class ArgumentParser { } private validatePaths(args: NativeParsedArgs): NativeParsedArgs { - // Track URLs if they're going to be used if (args['open-url']) { args._urls = args._; @@ -92,13 +89,12 @@ export class ArgumentParser { }); const caseInsensitive = isWindows || isMacintosh; - const distinctPaths = distinct(result, path => path && caseInsensitive ? path.toLowerCase() : (path || '')); + 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 @@ -108,7 +104,6 @@ export class ArgumentParser { path = trim(trim(path, ' '), '\t'); if (isWindows) { - // Resolve the path against cwd if it is relative path = resolve(cwd, path); diff --git a/lib/vscode/src/vs/platform/environment/common/argv.ts b/lib/vscode/src/vs/platform/environment/common/argv.d.ts similarity index 93% rename from lib/vscode/src/vs/platform/environment/common/argv.ts rename to lib/vscode/src/vs/platform/environment/common/argv.d.ts index 967b267cee26..7f00f77b2bf5 100644 --- a/lib/vscode/src/vs/platform/environment/common/argv.ts +++ b/lib/vscode/src/vs/platform/environment/common/argv.d.ts @@ -42,8 +42,8 @@ export interface NativeParsedArgs { /** @coder: BEGIN */ 'extra-extensions-dir'?: string[]; 'extra-builtin-extensions-dir'?: string[]; - 'ignore-last-opened'?: string, - 'server'?: string, + 'ignore-last-opened'?: boolean; + 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 @@ -58,7 +58,7 @@ export interface NativeParsedArgs { 'disable-extension'?: string[]; // undefined or array of 1 or more 'list-extensions'?: boolean; 'show-versions'?: boolean; - 'category'?: string; + category?: string; 'install-extension'?: string[]; // undefined or array of 1 or more 'install-builtin-extension'?: string[]; // undefined or array of 1 or more 'uninstall-extension'?: string[]; // undefined or array of 1 or more @@ -79,31 +79,31 @@ export interface NativeParsedArgs { 'max-memory'?: string; 'file-write'?: boolean; 'file-chmod'?: boolean; - 'driver'?: string; + driver?: string; 'driver-verbose'?: boolean; - 'remote'?: string; - 'force'?: boolean; + remote?: string; + force?: boolean; 'do-not-sync'?: boolean; 'force-user-env'?: boolean; 'force-disable-user-env'?: boolean; - 'sync'?: 'on' | 'off'; - '__sandbox'?: boolean; - 'logsPath'?: string; + sync?: 'on' | 'off'; + __sandbox?: boolean; + logsPath?: string; // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches 'no-proxy-server'?: boolean; 'proxy-server'?: string; 'proxy-bypass-list'?: string; 'proxy-pac-url'?: string; - 'inspect'?: string; + inspect?: string; 'inspect-brk'?: string; 'js-flags'?: string; 'disable-gpu'?: boolean; - 'nolazy'?: boolean; + nolazy?: boolean; 'force-device-scale-factor'?: string; 'force-renderer-accessibility'?: boolean; 'ignore-certificate-errors'?: boolean; 'allow-insecure-localhost'?: boolean; 'log-net-log'?: string; - 'vmodule'?: string; + vmodule?: string; } diff --git a/lib/vscode/src/vs/platform/environment/common/environment.ts b/lib/vscode/src/vs/platform/environment/common/environment.ts index f5d5fd956db8..6b863059deff 100644 --- a/lib/vscode/src/vs/platform/environment/common/environment.ts +++ b/lib/vscode/src/vs/platform/environment/common/environment.ts @@ -124,8 +124,9 @@ export interface INativeEnvironmentService extends IEnvironmentService { extensionsPath: string; extensionsDownloadPath: string; builtinExtensionsPath: string; - // NOTE@coder: add extraExtensionPaths/extraBuiltinExtensionPaths + /** @coder Extra extensions path. */ extraExtensionPaths: string[]; + /** @coder Extra built-in extensions path. */ extraBuiltinExtensionPaths: string[]; // --- smoke test support diff --git a/lib/vscode/src/vs/platform/environment/common/environmentService.ts b/lib/vscode/src/vs/platform/environment/common/environmentService.ts index 6728234071f6..f392c2301b0e 100644 --- a/lib/vscode/src/vs/platform/environment/common/environmentService.ts +++ b/lib/vscode/src/vs/platform/environment/common/environmentService.ts @@ -158,16 +158,35 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron } /** - * NOTE@coder: add extraExtensionPaths and extraBuiltinExtensionPaths + * @coder Extra extensions path from initial arguments. */ @memoize get extraExtensionPaths(): string[] { - return (this._args['extra-extensions-dir'] || []).map((p) => resolve(p)); + return (this.args['extra-extensions-dir'] || []).map((p) => resolve(p)); } + /** + * @coder Extra built-in extensions paths from initial arguments. + */ @memoize get extraBuiltinExtensionPaths(): string[] { - return (this._args['extra-builtin-extensions-dir'] || []).map((p) => resolve(p)); + return (this.args['extra-builtin-extensions-dir'] || []).map((p) => resolve(p)); + } + + /** + * @coder Extension path from initial arguments. + */ + @memoize + get extensionEnabledProposedApi(): string[] | undefined { + if (Array.isArray(this.args['enable-proposed-api'])) { + return this.args['enable-proposed-api']; + } + + if ('enable-proposed-api' in this.args) { + return []; + } + + return undefined; } @memoize diff --git a/lib/vscode/src/vs/platform/environment/node/argv.ts b/lib/vscode/src/vs/platform/environment/node/argv.ts index 1e2b70798435..fa5b39562b65 100644 --- a/lib/vscode/src/vs/platform/environment/node/argv.ts +++ b/lib/vscode/src/vs/platform/environment/node/argv.ts @@ -55,7 +55,7 @@ export const OPTIONS: OptionDescriptions> = { '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.") }, + 'ignore-last-opened': { type: 'boolean', 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.") }, diff --git a/lib/vscode/src/vs/server/@types/code-server-lib/index.d.ts b/lib/vscode/src/vs/server/@types/code-server-lib/index.d.ts new file mode 100644 index 000000000000..ec47916725c9 --- /dev/null +++ b/lib/vscode/src/vs/server/@types/code-server-lib/index.d.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Server } from 'http'; +import type { NativeParsedArgs } from '../../../platform/environment/common/argv'; +import type { NLSConfiguration, InternalNLSConfiguration } from '../../../base/node/languagePacks'; + +declare global { + namespace CodeServerLib { + export interface StartPath { + url: string; + workspace: boolean; + } + + export interface ServerConfiguration { + args: NativeParsedArgs; + authed: boolean; + disableUpdateCheck: boolean; + startPath?: StartPath; + codeServerVersion: string; + serverUrl: URL; + } + + /** + * @deprecated This primarily exists to bridge the gap between code-server and lib/vscode + */ + + export type CreateVSServer = (serverConfiguration: ServerConfiguration) => Promise; + + /** + * Base options included on every page. + */ + export interface ClientConfiguration extends Pick { + base: string; + csStaticBase: string; + } + + /** + * @deprecated This should be removed when code-server merges with lib/vscode + */ + export interface IMainCli { + main: (argv: NativeParsedArgs) => Promise; + } + + /** + * @deprecated This should be removed when code-server merges with lib/vscode + */ + export interface CliMessage { + type: 'cli'; + args: NativeParsedArgs; + } + + /** + * @deprecated This should be removed when code-server merges with lib/vscode + */ + export interface OpenCommandPipeArgs { + type: 'open'; + fileURIs?: string[]; + folderURIs: string[]; + forceNewWindow?: boolean; + diffMode?: boolean; + addMode?: boolean; + gotoLineMode?: boolean; + forceReuseWindow?: boolean; + waitMarkerFilePath?: string; + } + + export type NLSConfigurationWeb = NLSConfiguration | InternalNLSConfiguration; + export { NativeParsedArgs, NLSConfiguration, InternalNLSConfiguration }; + } +} + +export {}; diff --git a/lib/vscode/src/vs/server/channel.ts b/lib/vscode/src/vs/server/channel.ts index 978e0461d7e0..0f5ab2820011 100644 --- a/lib/vscode/src/vs/server/channel.ts +++ b/lib/vscode/src/vs/server/channel.ts @@ -36,6 +36,7 @@ import { AbstractVariableResolverService } from 'vs/workbench/services/configura 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'; +import { IEnvironmentServerService } from 'vs/server/environmentService'; /** * Extend the file provider to allow unwatching. @@ -237,7 +238,7 @@ export class FileProviderChannel implements IServerChannel { throw new Error(`Invalid listen '${event}'`); diff --git a/lib/vscode/src/vs/server/cliProcessMain.ts b/lib/vscode/src/vs/server/cliProcessMain.ts deleted file mode 120000 index 2bf3aa39df0e..000000000000 --- a/lib/vscode/src/vs/server/cliProcessMain.ts +++ /dev/null @@ -1 +0,0 @@ -../code/node/cliProcessMain.ts \ No newline at end of file diff --git a/lib/vscode/src/vs/server/entry.ts b/lib/vscode/src/vs/server/entry.ts index 26515e6515c3..4ce986d6357b 100644 --- a/lib/vscode/src/vs/server/entry.ts +++ b/lib/vscode/src/vs/server/entry.ts @@ -1,92 +1,35 @@ -import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import * as proxyAgent from 'vs/base/node/proxy_agent'; -import { CodeServerMessage, VscodeMessage } from 'vs/base/common/ipc'; -import { enableCustomMarketplace } from 'vs/server/marketplace'; -import { CodeServerMain } from './server'; -import { ConnectionOptions } from './connection/abstractConnection'; +import { ArgumentParser } from '../platform/environment/argumentParser'; +import { ServerProcessMain } from './main'; -setUnexpectedErrorHandler(error => { - console.warn('Uncaught error', error instanceof Error ? error.message : error); -}); -enableCustomMarketplace(); -proxyAgent.monkeyPatch(true); +export const main = async () => { + const argumentParser = new ArgumentParser(); + const args = argumentParser.resolveArgs(); -/** - * Ensure we control when the process exits. - */ -const exit = process.exit; -process.exit = function (code?: number) { - console.warn(`process.exit() was prevented: ${code || 'unknown code'}.`); -} as (code?: number) => never; + if (!args['server']) { + throw new Error('Server argument was not given'); + } -// Kill VS Code if the parent process dies. -if (typeof process.env.CODE_SERVER_PARENT_PID !== 'undefined') { - const parentPid = parseInt(process.env.CODE_SERVER_PARENT_PID, 10); - setInterval(() => { - try { - process.kill(parentPid, 0); // Throws an exception if the process doesn't exist anymore. - } catch (e) { - exit(); - } - }, 5000); -} else { - console.error('no parent process'); - exit(1); -} + args['user-data-dir'] = '/Users/teffen/.local/share/code-server'; -const vscode = new CodeServerMain(); + const serverUrl = new URL(args['server']); -const send = (message: VscodeMessage): void => { - if (!process.send) { - throw new Error('not spawned with IPC'); - } - process.send(message); -}; + const codeServer = new ServerProcessMain({ + args, + authed: false, + disableUpdateCheck: true, + codeServerVersion: 'Unknown', + serverUrl, + }); -// Wait for the init message then start up VS Code. Subsequent messages will -// return new workbench options without starting a new instance. -process.on('message', async (message: CodeServerMessage, socket) => { - // console.debug('got message from code-server', message.type); - // console.trace('code-server message content', message); - switch (message.type) { - case 'init': - try { - // TODO: get protocol. - const serverUrl = new URL(`http://${message.options.remoteAuthority}`); + const netServer = await codeServer.startup(); - const options = await vscode.createWorkbenchConstructionOptions(serverUrl, message.options); + return new Promise(resolve => { + netServer.on('close', resolve); + }); +}; - send({ type: 'options', id: message.id, options }); - } catch (error) { - console.error(error.message); - console.error(error.stack); - exit(1); - } - break; - case 'cli': - try { - await vscode.cli(message.args); - exit(0); - } catch (error) { - console.error(error.message); - console.error(error.stack); - exit(1); - } - break; - case 'socket': - const connectionOptions: ConnectionOptions = { - reconnectionToken: message.query.reconnectionToken as string, - reconnection: (message.query.reconnection as string) === 'true', - skipWebSocketFrames: (message.query.skipWebSocketFrames as string) === 'true', - }; - vscode.handleWebSocket(socket, connectionOptions, message.permessageDeflate); - break; - } -}); -if (!process.send) { - console.error('not spawned with IPC'); - exit(1); -} else { - // This lets the parent know the child is ready to receive messages. - send({ type: 'ready' }); -} +export const createVSServer: CodeServerLib.CreateVSServer = async serverConfig => { + const codeServer = new ServerProcessMain(serverConfig); + + return codeServer.startup({ listenWhenReady: false }); +}; diff --git a/lib/vscode/src/vs/server/environmentService.ts b/lib/vscode/src/vs/server/environmentService.ts new file mode 100644 index 000000000000..14d3869da443 --- /dev/null +++ b/lib/vscode/src/vs/server/environmentService.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'path'; +import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { createServerURITransformer } from 'vs/base/common/uriServer'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { getLogLevel } from 'vs/platform/log/common/log'; +import { toWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { getLocaleFromConfig, getNlsConfiguration } from 'vs/server/nls'; +import { IProductConfiguration, IWorkbenchWebConfiguration, IWorkspaceSerialized } from 'vs/workbench/workbench.web.api'; +import { memoize } from '../base/common/decorators'; +import { Writeable } from '../base/common/types'; +import { NativeParsedArgs } from '../platform/environment/common/argv'; +import { INativeEnvironmentService } from '../platform/environment/common/environment'; +import { refineServiceDecorator } from '../platform/instantiation/common/instantiation'; +import { IProductService } from '../platform/product/common/productService'; +import { ParsedRequest } from './net/abstractNetRequestHandler'; + +export interface IEnvironmentServerService extends INativeEnvironmentService { + extensionEnabledProposedApi: string[] | undefined; + createWorkbenchWebConfiguration: (req: ParsedRequest) => Promise; + protocol: string; +} + +export const IEnvironmentServerService = refineServiceDecorator(INativeEnvironmentService); + +export interface IEnvironmentServerServiceConfiguration { + readonly serverUrl: URL; + readonly disableUpdateCheck: boolean; +} + +/** + * The `EnvironmentServerService` is fairly similar to the Electron specific + * `EnvironmentMainService`. However, it's capable of creating a web specific workbench. + */ +export class EnvironmentServerService extends NativeEnvironmentService implements IEnvironmentServerService { + constructor(args: NativeParsedArgs, productService: IProductService, private configuration: IEnvironmentServerServiceConfiguration) { + super(args, productService); + } + + /** + * Creates the workbench options which are then injected into the front-end. + * @remark When passing data to the front-end, this should be your main point of entry, + * even extending `IWorkbenchWebConfiguration` if truly necessary. + */ + public async createWorkbenchWebConfiguration(req: ParsedRequest): Promise { + const { productService, workspaceArgPaths } = this; + const remoteAuthority = req.headers.host; + + if (!remoteAuthority) { + throw new Error('Expected host in request headers to determine remote authority'); + } + + // Workspace + const workspaceSearchParamPaths = req.parsedUrl.searchParams.getAll('folder'); + const workspace = this.parseWorkspace(workspaceSearchParamPaths.length ? workspaceSearchParamPaths : workspaceArgPaths, remoteAuthority); + const transformer = createServerURITransformer(remoteAuthority); + + // Only append dynamic properties here. + // Static properties should be inherited when constructing `EnvironmentServerService` + + const logoutEndpointUrl = this.createRequestUrl(req, '/logout'); + logoutEndpointUrl.searchParams.set('base', req.pathPrefix); + // const webViewExternalUrl = this.createRequestUrl(req, 'webview'); + + const productConfiguration: Writeable = { + ...productService, + logoutEndpointUrl: logoutEndpointUrl.toString(), + webEndpointUrl: this.createRequestUrl(req, '/static').toString(), + icons: [ + { + src: this.createRequestUrl(req, '/static/src/browser/media/pwa-icon-192.png').toString(), + type: 'image/png', + sizes: '192x192', + }, + { + src: this.createRequestUrl(req, '/static/src/browser/media/pwa-icon-512.png').toString(), + type: 'image/png', + sizes: '512x512', + }, + ], + // webviewContentExternalBaseUrlTemplate: webViewExternalUrl.toString(), + }; + + if (!this.configuration.disableUpdateCheck) { + productConfiguration.updateUrl = path.join(req.pathPrefix, '/update/check'); + } + + return { + ...workspace, + remoteAuthority, + logLevel: getLogLevel(this), + workspaceProvider: { + workspace, + trusted: undefined, + payload: [ + ['userDataPath', this.userDataPath], + ['enableProposedApi', JSON.stringify(this.extensionEnabledProposedApi || [])], + ], + }, + remoteUserDataUri: transformer.transformOutgoing(URI.file(this.userDataPath)), + productConfiguration, + nlsConfiguration: await this.parseNlsConfiguration(), + }; + } + + private _cachedNlsConfiguration: CodeServerLib.NLSConfigurationWeb | null = null; + private async parseNlsConfiguration() { + if (!this._cachedNlsConfiguration) { + this._cachedNlsConfiguration = await getNlsConfiguration(this.args.locale || (await getLocaleFromConfig(this.userDataPath)), this.userDataPath); + } + + return this._cachedNlsConfiguration; + } + + private createRequestUrl({ pathPrefix, parsedUrl }: ParsedRequest, pathname: string): URL { + return new URL(path.join('/', pathPrefix, pathname), `${parsedUrl.protocol}//${parsedUrl.host}`); + } + + /** + * 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`) + */ + private parseWorkspace(workbenchPaths: string[], remoteAuthority: string): IWorkspaceSerialized | undefined { + /** @todo `startPath` should eventually be merged with the parsed path arg. */ + // const workbenchPaths: string[] = startPath ? [startPath.url] : this.args._.slice(1); + + if (!workbenchPaths.length) { + return; + } + + const workbenchURIs = workbenchPaths.map(path => + toWorkspaceFolder( + URI.from({ + scheme: Schemas.vscodeRemote, + authority: remoteAuthority, + path, + }), + ), + ); + + // TODO: multiple workbench entries needs further testing. + // const hasSingleEntry = workbenchURIs.length > 0; + // const isSingleEntry = workbenchURIs.length === 1; + + return { + // workspaceUri: isSingleEntry ? undefined : fs.stat(path), + workspaceUri: undefined, + folderUri: workbenchURIs[0].uri.toJSON(), + }; + } + + @memoize + public get protocol(): string { + return this.configuration.serverUrl.protocol; + } + + @memoize + public get commit(): string { + return this.productService.commit || 'development'; + } + + @memoize + public override get isBuilt(): boolean { + return this.commit !== 'development'; + } + + @memoize + public get disableUpdateCheck(): boolean { + return this.configuration.disableUpdateCheck; + } + + @memoize + public get environmentPaths(): string[] { + return [this.extensionsPath, this.logsPath, this.globalStorageHome.fsPath, this.workspaceStorageHome.fsPath, ...this.extraExtensionPaths, ...this.extraBuiltinExtensionPaths]; + } + + /** + * Workspace paths provided as CLI arguments. + */ + @memoize + private get workspaceArgPaths(): string[] { + return this.args._.slice(1); + } + + @memoize + public get piiPaths(): string[] { + return [ + path.join(this.userDataPath, 'clp'), // Language packs. + this.appRoot, + this.extensionsPath, + this.builtinExtensionsPath, + ...this.extraExtensionPaths, + ...this.extraBuiltinExtensionPaths, + ]; + } +} diff --git a/lib/vscode/src/vs/server/fork.js b/lib/vscode/src/vs/server/fork.js deleted file mode 100644 index 56331ff1fc32..000000000000 --- a/lib/vscode/src/vs/server/fork.js +++ /dev/null @@ -1,3 +0,0 @@ -// This must be a JS file otherwise when it gets compiled it turns into AMD -// syntax which will not work without the right loader. -require('../../bootstrap-amd').load('vs/server/entry'); diff --git a/lib/vscode/src/vs/server/ipc.ts b/lib/vscode/src/vs/server/ipc.ts deleted file mode 100644 index 5e560eb46e6a..000000000000 --- a/lib/vscode/src/vs/server/ipc.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as cp from 'child_process'; -import { Emitter } from 'vs/base/common/event'; - -enum ControlMessage { - okToChild = 'ok>', - okFromChild = 'ok<', -} - -interface RelaunchMessage { - type: 'relaunch'; - version: string; -} - -export type Message = RelaunchMessage; - -class IpcMain { - protected readonly _onMessage = new Emitter(); - public readonly onMessage = this._onMessage.event; - - public handshake(child?: cp.ChildProcess): Promise { - return new Promise((resolve, reject) => { - const target = child || process; - if (!target.send) { - throw new Error('Not spawned with IPC enabled'); - } - target.on('message', (message) => { - if (message === child ? ControlMessage.okFromChild : ControlMessage.okToChild) { - target.removeAllListeners(); - target.on('message', (msg) => this._onMessage.fire(msg)); - if (child) { - target.send!(ControlMessage.okToChild); - } - resolve(); - } - }); - if (child) { - child.once('error', reject); - child.once('exit', (code) => { - const error = new Error(`Unexpected exit with code ${code}`); - (error as any).code = code; - reject(error); - }); - } else { - target.send(ControlMessage.okFromChild); - } - }); - } - - public relaunch(version: string): void { - this.send({ type: 'relaunch', version }); - } - - private send(message: Message): void { - if (!process.send) { - throw new Error('Not a child process with IPC enabled'); - } - process.send(message); - } -} - -export const ipcMain = new IpcMain(); diff --git a/lib/vscode/src/vs/server/main.ts b/lib/vscode/src/vs/server/main.ts new file mode 100644 index 000000000000..0624da04e113 --- /dev/null +++ b/lib/vscode/src/vs/server/main.ts @@ -0,0 +1,265 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { gracefulify } from 'graceful-fs'; +import { createServer as createNetServer, Server as NetServer } from 'http'; +import { hostname, release } from 'os'; +import * as path from 'path'; +import { combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { createServerURITransformer } from 'vs/base/common/uriServer'; +import * as proxyAgent from 'vs/base/node/proxy_agent'; +import { IPCServer, IServerChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; +import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; +import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; +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 { BufferLogService } from 'vs/platform/log/common/bufferLog'; +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 product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; +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 { ITelemetryServiceConfig, 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 { ExtensionEnvironmentChannel, FileProviderChannel, TerminalProviderChannel } from 'vs/server/channel'; +import { TelemetryClient } from 'vs/server/insights'; +import { enableCustomMarketplace } from 'vs/server/marketplace'; +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 { toErrorMessage } from '../base/common/errorMessage'; +import { setUnexpectedErrorHandler } from '../base/common/errors'; +import { getMachineId } from '../base/node/id'; +import { IInstantiationService } from '../platform/instantiation/common/instantiation'; +import { RemoteAgentConnectionContext } from '../platform/remote/common/remoteAgentEnvironment'; +import { WebSocketHandler } from './net/webSocketHandler'; +import { EnvironmentServerService, IEnvironmentServerService } from './environmentService'; +import { WebRequestHandler } from './net/webRequestHandler'; + +interface IServerProcessMainStartupOptions { + listenWhenReady?: boolean; +} + +interface IServerProcessMain { + startup(startupOptions: IServerProcessMainStartupOptions): Promise; +} + +/** + * Handles client connections to a editor instance via IPC. + */ +export class ServerProcessMain extends Disposable implements IServerProcessMain { + constructor(private readonly configuration: CodeServerLib.ServerConfiguration) { + super(); + + enableCustomMarketplace(); + proxyAgent.monkeyPatch(true); + + // Enable gracefulFs + gracefulify(fs); + + this.registerListeners(); + } + + private registerListeners(): void { + process.once('exit', () => this.dispose()); + } + + public async startup({ listenWhenReady = true }: IServerProcessMainStartupOptions = {}): Promise { + // Services + const [instantiationService, environmentServerService, logService, bufferLogService] = await this.createServices(); + + // Net Server + const netServer = createNetServer(); + + const webSocketHandler = new WebSocketHandler(netServer, environmentServerService, logService); + webSocketHandler.listen(); + const webRequestHandler = new WebRequestHandler(netServer, environmentServerService, logService); + webRequestHandler.listen(); + + // IPC Server + const ipcServer = new IPCServer(webSocketHandler.onDidClientConnect); + + // Log info + logService.trace('Server configuration', JSON.stringify(this.configuration)); + + // Channels + await this.initChannels(instantiationService, ipcServer); + + // Error handler + this.registerErrorHandler(logService); + + // Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906) + bufferLogService.logger = new SpdLogLogger('main', path.join(environmentServerService.logsPath, `${RemoteExtensionLogFileName}.log`), true, bufferLogService.getLevel()); + + // Instantiate Contributions + this._register(combinedDisposable(instantiationService.createInstance(LogsDataCleaner), instantiationService.createInstance(ErrorTelemetry), webRequestHandler, webSocketHandler, ipcServer)); + + // Listen for incoming connections + if (listenWhenReady) { + const { serverUrl } = this.configuration; + + await listen(netServer, parseInt(serverUrl.port, 10), serverUrl.hostname); + logService.info('Code Server active and listening at:', serverUrl.toString()); + } + + return netServer; + } + + // References: + // ../../electron-browser/sharedProcess/sharedProcessMain.ts#L148 + // ../../../code/electron-main/app.ts + public async createServices(): Promise<[IInstantiationService, IEnvironmentServerService, ILogService, BufferLogService]> { + const services = new ServiceCollection(); + + // Product + const productService: IProductService = { + _serviceBrand: undefined, + ...product, + }; + + services.set(IProductService, productService); + + // Environment + const environmentServerService = new EnvironmentServerService(this.configuration.args, productService, { + disableUpdateCheck: this.configuration.disableUpdateCheck, + serverUrl: this.configuration.serverUrl, + }); + + services.set(IEnvironmentServerService, environmentServerService); + + await Promise.all( + environmentServerService.environmentPaths.map(p => + fs.promises.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(environmentServerService)), bufferLogService]); + process.once('exit', () => logService.dispose()); + services.set(ILogService, logService); + + // Files + const fileService = new FileService(logService); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService)); + + const loggerService = new LoggerService(logService, fileService); + + services.set(ILogService, logService); + services.set(ILoggerService, loggerService); + + const configurationService = new ConfigurationService(environmentServerService.settingsResource, fileService); + await configurationService.initialize(); + services.set(IConfigurationService, configurationService); + + services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IFileService, fileService); + + await configurationService.initialize(); + services.set(IConfigurationService, configurationService); + + const machineId = await this.resolveMachineId(); + + const instantiationService = new InstantiationService(services); + + // Telemetry + if (!environmentServerService.isExtensionDevelopment && !environmentServerService.disableTelemetry && productService.enableTelemetry) { + const appender = combinedAppender(new AppInsightsAppender('code-server', null, () => new TelemetryClient() as any), new TelemetryLogAppender(loggerService, environmentServerService)); + + const commonProperties = resolveCommonProperties(fileService, release(), hostname(), process.arch, environmentServerService.commit, product.version, machineId, undefined, environmentServerService.installSourcePath, 'code-server'); + + const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths: environmentServerService.piiPaths, sendErrorTelemetry: true }; + + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); + } else { + services.set(ITelemetryService, NullTelemetryService); + } + + services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); + services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + + return [instantiationService, environmentServerService, logService, bufferLogService]; + } + + private initChannels(instantiationService: IInstantiationService, ipcServer: IPCServer): Promise { + return new Promise(resolve => { + instantiationService.invokeFunction(accessor => { + ipcServer.registerChannel('logger', new LogLevelChannel(accessor.get(ILogService))); + ipcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel()); + + ipcServer.registerChannel('extensions', new ExtensionManagementChannel(accessor.get(IExtensionManagementService), context => createServerURITransformer(context.remoteAuthority))); + ipcServer.registerChannel('remoteextensionsenvironment', new ExtensionEnvironmentChannel(accessor.get(IEnvironmentServerService), accessor.get(ILogService), accessor.get(ITelemetryService), '')); + ipcServer.registerChannel('request', new RequestChannel(accessor.get(IRequestService))); + ipcServer.registerChannel('localizations', >ProxyChannel.fromService(accessor.get(ILocalizationsService))); + ipcServer.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(accessor.get(IEnvironmentServerService), accessor.get(ILogService))); + + const ptyHostService = new PtyHostService({ GraceTime: 60000, ShortGraceTime: 6000 }, accessor.get(IConfigurationService), accessor.get(ILogService), accessor.get(ITelemetryService)); + ipcServer.registerChannel(REMOTE_TERMINAL_CHANNEL_NAME, new TerminalProviderChannel(accessor.get(ILogService), ptyHostService)); + + resolve(); + }); + }); + } + + private registerErrorHandler(logService: ILogService): void { + // Install handler for unexpected errors + setUnexpectedErrorHandler(error => { + const message = toErrorMessage(error, true); + if (!message) { + return; + } + + logService.error(`[uncaught exception in sharedProcess]: ${message}`); + }); + } + + // TODO cache machine ID with StateService for faster startup. + private async resolveMachineId(): Promise { + return getMachineId(); + } +} + +export const listen = (server: NetServer, port: number, host: string) => { + return new Promise(async (resolve, reject) => { + server.on('error', reject); + + const onListen = () => { + // Promise resolved earlier so this is an unrelated error. + server.off('error', reject); + + resolve(); + }; + // [] is the correct format when using :: but Node errors with them. + server.listen(port, host.replace(/^\[|\]$/g, ''), onListen); + }); +}; diff --git a/lib/vscode/src/vs/server/marketplace.ts b/lib/vscode/src/vs/server/marketplace.ts index 57701cb06971..b093ae1f25ef 100644 --- a/lib/vscode/src/vs/server/marketplace.ts +++ b/lib/vscode/src/vs/server/marketplace.ts @@ -36,7 +36,7 @@ export const tar = async (tarPath: string, files: vszip.IFile[]): Promise => { try { await extractTar(archivePath, extractPath, options, token); - } catch (error) { + } catch (error: any) { if (error.toString().includes('Invalid tar header')) { await vszipExtract(archivePath, extractPath, options, token); } @@ -56,7 +56,7 @@ export const buffer = (targetPath: string, filePath: string): Promise => if (!done) { throw new Error(`couldn't find asset ` + filePath); } - } catch (error) { + } catch (error: any) { if (error.toString().includes('Invalid tar header')) { vszipBuffer(targetPath, filePath).then(resolve).catch(reject); } else { diff --git a/lib/vscode/src/vs/server/net/abstractNetRequestHandler.ts b/lib/vscode/src/vs/server/net/abstractNetRequestHandler.ts new file mode 100644 index 000000000000..0ef0d7f3606c --- /dev/null +++ b/lib/vscode/src/vs/server/net/abstractNetRequestHandler.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as net from 'net'; +import * as http from 'http'; +import * as path from 'path'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentServerService } from 'vs/server/environmentService'; +import { Serializable } from 'child_process'; + +export interface ParsedRequest extends http.IncomingMessage { + parsedUrl: URL; + pathPrefix: string; +} + +export type NetEventListener = (req: ParsedRequest, ...args: any[]) => void; +export abstract class AbstractNetRequestHandler extends Disposable { + protected abstract eventName: string; + protected abstract eventListener: E; + + constructor(protected readonly netServer: net.Server, protected readonly environmentService: IEnvironmentServerService, protected readonly logService: ILogService) { + super(); + } + + private _handleEvent = (req: http.IncomingMessage, ...args: any[]) => { + const parsedUrl = new URL(req.url || '/', `${this.environmentService.protocol}//${req.headers.host}`); + + Object.assign(req, { + parsedUrl, + pathPrefix: relativeRoot(parsedUrl), + }); + + this.eventListener(req as ParsedRequest, ...args); + }; + + public listen() { + this.netServer.on(this.eventName, this._handleEvent); + } + + override dispose(): void { + super.dispose(); + + if (this.netServer) { + this.netServer.off(this.eventName, this._handleEvent); + } + } +} + +/** + * Generates a prefix used to normalize a request's base path. + * @remark This is especially useful when serving the editor from directory. + * e.g. `"localhost:8080/some/user/path/" + * + * @example: + * / => . + * /foo => . + * /foo/ => ./.. + * /foo/bar => ./.. + * /foo/bar/ => ./../.. + */ +export const relativeRoot = (url: URL): string => { + const depth = (url.pathname.match(/\//g) || []).length; + return path.normalize('./' + (depth > 1 ? '../'.repeat(depth - 1) : '')); +}; + +export const escapeJSON = (value: Serializable) => JSON.stringify(value).replace(/"/g, '"'); diff --git a/lib/vscode/src/vs/server/connection/abstractConnection.ts b/lib/vscode/src/vs/server/net/connection/abstractConnection.ts similarity index 96% rename from lib/vscode/src/vs/server/connection/abstractConnection.ts rename to lib/vscode/src/vs/server/net/connection/abstractConnection.ts index 0905e9352927..3fb5c8b3ffd8 100644 --- a/lib/vscode/src/vs/server/connection/abstractConnection.ts +++ b/lib/vscode/src/vs/server/net/connection/abstractConnection.ts @@ -5,7 +5,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; -import { ConsoleLogger } from 'vs/platform/log/common/log'; +import { ILogService } from 'vs/platform/log/common/log'; import { ServerProtocol } from 'vs/server/protocol'; export abstract class AbstractConnection { @@ -22,7 +22,7 @@ export abstract class AbstractConnection { return `[${this.name}] ${this.protocol.logPrefix}`; } - public constructor(protected readonly protocol: ServerProtocol, protected readonly logService: ConsoleLogger, public readonly name: string) { + public constructor(protected readonly protocol: ServerProtocol, protected readonly logService: ILogService, public readonly name: string) { this.logService.debug('Connecting...'); this.onClose(() => this.logService.debug('Closed')); } diff --git a/lib/vscode/src/vs/server/connection/extensionHostConnection.ts b/lib/vscode/src/vs/server/net/connection/extensionHostConnection.ts similarity index 94% rename from lib/vscode/src/vs/server/connection/extensionHostConnection.ts rename to lib/vscode/src/vs/server/net/connection/extensionHostConnection.ts index 34edacf1a11a..7ea8e4013797 100644 --- a/lib/vscode/src/vs/server/connection/extensionHostConnection.ts +++ b/lib/vscode/src/vs/server/net/connection/extensionHostConnection.ts @@ -1,7 +1,6 @@ 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 { AbstractConnection } from 'vs/server/net/connection/abstractConnection'; import { getNlsConfiguration } from 'vs/server/nls'; import { ServerProtocol } from 'vs/server/protocol'; import { NLSConfiguration } from 'vs/base/node/languagePacks'; @@ -10,7 +9,8 @@ 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'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentServerService } from 'vs/server/environmentService'; export interface ForkEnvironmentVariables { VSCODE_AMD_ENTRYPOINT: string; @@ -37,7 +37,7 @@ export class ExtensionHostConnection extends AbstractConnection { public readonly _isExtensionDevDebug: boolean; public readonly _isExtensionDevTestFromCli: boolean; - public constructor(protocol: ServerProtocol, logService: ConsoleLogger, private readonly startParams: IRemoteExtensionHostStartParams, private readonly _environmentService: INativeEnvironmentService) { + public constructor(protocol: ServerProtocol, logService: ILogService, private readonly startParams: IRemoteExtensionHostStartParams, private readonly _environmentService: IEnvironmentServerService) { super(protocol, logService, 'ExtensionHost'); const devOpts = parseExtensionDevOptions(this._environmentService); diff --git a/lib/vscode/src/vs/server/connection/managementConnection.ts b/lib/vscode/src/vs/server/net/connection/managementConnection.ts similarity index 83% rename from lib/vscode/src/vs/server/connection/managementConnection.ts rename to lib/vscode/src/vs/server/net/connection/managementConnection.ts index 4b36aee14880..db424d1bb30e 100644 --- a/lib/vscode/src/vs/server/connection/managementConnection.ts +++ b/lib/vscode/src/vs/server/net/connection/managementConnection.ts @@ -3,15 +3,15 @@ * 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 { ILogService } from 'vs/platform/log/common/log'; +import { AbstractConnection } from 'vs/server/net/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) { + public constructor(protocol: ServerProtocol, logService: ILogService) { super(protocol, logService, 'management'); protocol.onDidDispose(() => this.dispose('Explicitly closed')); diff --git a/lib/vscode/src/vs/server/net/webRequestHandler.ts b/lib/vscode/src/vs/server/net/webRequestHandler.ts new file mode 100644 index 000000000000..b4a3f2800fa0 --- /dev/null +++ b/lib/vscode/src/vs/server/net/webRequestHandler.ts @@ -0,0 +1,329 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IncomingMessage, ServerResponse } from 'http'; +import { readFileSync, createReadStream, promises as fs } from 'fs'; +import { extname, join, normalize } from 'path'; +import { UriComponents } from 'vs/base/common/uri'; +import { AbstractNetRequestHandler, escapeJSON, ParsedRequest } from './abstractNetRequestHandler'; +import { ProtocolConstants } from '../../base/parts/ipc/common/ipc.net'; + +const APP_ROOT = join(__dirname, '..', '..', '..', '..'); + +const paths = { + WEBVIEW: join(APP_ROOT, 'out/vs/workbench/contrib/webview/browser/pre'), + FAVICON: join(APP_ROOT, 'resources', 'win32', 'code.ico'), +}; + +/** Matching the given keys in `PollingURLCallbackProvider.QUERY_KEYS` */ +type PollingURLQueryKeys = 'vscode-requestId' | 'vscode-scheme' | 'vscode-authority' | 'vscode-path' | 'vscode-query' | 'vscode-fragment'; +const wellKnownKeys: PollingURLQueryKeys[] = [ + // TODO: Can this type be inferred without importing a browser specific file? + 'vscode-requestId', + 'vscode-scheme', + 'vscode-authority', + 'vscode-path', + 'vscode-query', + 'vscode-fragment', +]; + +export interface WebManifest { + name: string; + short_name: string; + start_url: string; + display: string; + 'background-color': string; + description: string; + icons: Array<{ src: string; type: string; sizes: string }>; +} + +/** + * A callback response matching the expected value in `PollingURLCallbackProvider` + */ +interface Callback { + uri: Partial; + /** This should be no longer than `PollingURLCallbackProvider.FETCH_TIMEOUT` */ + timeout: NodeJS.Timeout; +} + +export type WebRequestListener = (req: ParsedRequest, res: ServerResponse) => void | Promise; + +export class WebRequestHandler extends AbstractNetRequestHandler { + /** Stored callback URI's sent over from client-side `PollingURLCallbackProvider`. */ + private callbackUriToRequestId = new Map(); + + private templates = { + workbenchDev: readFileSync(join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html')).toString(), + workbenchProd: readFileSync(join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench.html')).toString(), + callback: readFileSync(join(APP_ROOT, 'resources', 'web', 'callback.html')).toString(), + }; + + protected eventName = 'request'; + /** + * Event listener which handles all incoming requests. + */ + protected eventListener: WebRequestListener = async (req, res) => { + const { pathname } = req.parsedUrl; + + res.setHeader('Access-Control-Allow-Origin', '*'); + + try { + if (/(\/static)?\/favicon\.ico/.test(pathname)) { + return serveFile(req, res, paths.FAVICON); + } + if (/(\/static)?\/manifest\.json/.test(pathname)) { + return this.$manifest(req, res); + } + + if (/^\/static\//.test(pathname)) { + return this.$static(req, res); + } + + // if (/^\/webview\//.test(pathname)) { + // return this.$webview(req, res); + // } + + switch (pathname) { + case '/': + return this.$root(req, res); + case '/callback': + return this.$callback(req, res); + case '/fetch-callback': + return this.$fetchCallback(req, res); + case '/vscode-remote-resource': + return this.$remoteResource(req, res); + default: + return serveError(res, 404, 'Not found.'); + } + } catch (error: any) { + this.logService.error(error); + + return serveError(res, 500, 'Internal Server Error.'); + } + }; + + /** + * PWA manifest file. This informs the browser that the app may be installed. + */ + private $manifest: WebRequestListener = async (req, res) => { + const { productConfiguration } = await this.environmentService.createWorkbenchWebConfiguration(req); + + const webManifest: WebManifest = { + name: productConfiguration.nameLong!, + short_name: productConfiguration.nameShort!, + start_url: req.pathPrefix, + display: 'fullscreen', + 'background-color': '#fff', + description: 'Run editors on a remote server.', + // icons: productConfiguration.icons || [], + icons: [], + }; + + res.writeHead(200, { 'Content-Type': 'application/manifest+json' }); + + return res.end(JSON.stringify(webManifest)); + }; + + /** + * Static files endpoint. + */ + private $static: WebRequestListener = async (req, res) => { + const { parsedUrl } = req; + + // Strip `/static/` from the path + const relativeFilePath = normalize(decodeURIComponent(parsedUrl.pathname.substr('/static/'.length))); + + return serveFile(req, res, join(APP_ROOT, relativeFilePath)); + }; + + /** + * Root application endpoint. + * @remark This is generally where the server and client interact for the first time. + */ + private $root: WebRequestListener = async (req, res) => { + const webConfigJSON = await this.environmentService.createWorkbenchWebConfiguration(req); + // TODO: investigate auth session for authentication. + const authSessionInfo = null; + + const content = this.templates[this.environmentService.isBuilt ? 'workbenchProd' : 'workbenchDev'] + // Inject server-side workbench configuration for client-side workbench. + .replace('{{WORKBENCH_WEB_CONFIGURATION}}', () => escapeJSON(webConfigJSON)) + .replace('{{PATH_PREFIX}}', () => req.pathPrefix) + .replace('{{WORKBENCH_BUILTIN_EXTENSIONS}}', () => escapeJSON([])) + .replace('{{WORKBENCH_AUTH_SESSION}}', () => (authSessionInfo ? escapeJSON(authSessionInfo) : '')); + + const headers = { + 'Content-Type': 'text/html', + 'Content-Security-Policy': "require-trusted-types-for 'script';", + }; + + res.writeHead(200, headers); + return res.end(content); + }; + + /** + * Callback endpoint. + * @remark The callback cycle is further documented in `PollingURLCallbackProvider`. + */ + private $callback: WebRequestListener = async (req, res) => { + const { parsedUrl } = req; + const [requestId, vscodeScheme = 'code-oss', vscodeAuthority, vscodePath, vscodeQuery, vscodeFragment] = wellKnownKeys.map(key => { + const value = parsedUrl.searchParams.get(key); + + return value && value !== null ? decodeURIComponent(value) : undefined; + }); + + if (!requestId) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + return res.end('Bad request.'); + } + + // merge over additional query values that we got. + let query = new URLSearchParams(vscodeQuery || ''); + + for (const key in query.keys()) { + // Omit duplicate keys within query. + if (wellKnownKeys.includes(key as PollingURLQueryKeys)) { + query.delete(key); + } + } + + const callback: Callback = { + uri: { + scheme: vscodeScheme || 'code-oss', + authority: vscodeAuthority, + path: vscodePath, + query: query.toString(), + fragment: vscodeFragment, + }, + // Make sure the map doesn't leak if nothing fetches this URI. + timeout: setTimeout(() => this.callbackUriToRequestId.delete(requestId), ProtocolConstants.ReconnectionShortGraceTime), + }; + + // Add to map of known callbacks. + this.callbackUriToRequestId.set(requestId, callback); + + res.writeHead(200, { 'Content-Type': 'text/html' }); + return res.end(this.templates.callback); + }; + + /** + * Fetch callback endpoint. + * @remark This is the follow up to a client's initial `/callback` lifecycle. + */ + private $fetchCallback: WebRequestListener = (req, res) => { + const requestId = req.parsedUrl.searchParams.get('vscode-requestId'); + if (!requestId) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + return res.end(`Bad request.`); + } + + const knownCallback = this.callbackUriToRequestId.get(requestId); + + if (knownCallback) { + this.callbackUriToRequestId.delete(requestId); + clearTimeout(knownCallback.timeout); + } + + res.writeHead(200, { 'Content-Type': 'text/json' }); + return res.end(JSON.stringify(knownCallback?.uri)); + }; + + /** + * Remote resource endpoint. + * @remark Used to load resources on the client-side. + */ + private $remoteResource: WebRequestListener = async (req, res) => { + const path = req.parsedUrl.searchParams.get('path'); + + if (path) { + res.setHeader('Content-Type', getMediaMime(path)); + res.end(await fs.readFile(path)); + } + }; + + // /** + // * Webview endpoint + // */ + // private $webview: WebRequestListener = async (req, res) => { + // const foo = req.parsedUrl.pathname.foo; + + // if (/^vscode-resource/.test(foo)) { + // return serveFile(req, res, foo.replace(/^vscode-resource(\/file)?/, '')); + // } + + // return serveFile(req, res, join(paths.WEBVIEW, foo)); + // }; + + public override dispose() { + super.dispose(); + this.callbackUriToRequestId.clear(); + } +} + +async function serveError(res: ServerResponse, errorCode: number, errorMessage: string, responseHeaders = Object.create(null)) { + responseHeaders['Content-Type'] = 'text/plain'; + res.writeHead(errorCode, responseHeaders); + res.end(errorMessage); +} + +async function serveFile(req: IncomingMessage, res: ServerResponse, filePath: string, responseHeaders = Object.create(null)) { + try { + // Sanity checks + filePath = normalize(filePath); // ensure no "." and ".." + + const stat = await fs.stat(filePath); + + // Check if file modified since + // Weak validator (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) + const etag = `W/"${[stat.ino, stat.size, stat.mtime.getTime()].join('-')}"`; + if (req.headers['if-none-match'] === etag) { + res.writeHead(304); + return res.end(); + } + + // Headers + responseHeaders['Content-Type'] = textMimeType[extname(filePath)] || getMediaMime(filePath); + responseHeaders['Etag'] = etag; + + res.writeHead(200, responseHeaders); + + // Data + createReadStream(filePath).pipe(res); + } catch (error: any) { + console.error(error.toString()); + responseHeaders['Content-Type'] = 'text/plain'; + res.writeHead(404, responseHeaders); + return res.end('Not found'); + } +} + +const textMimeType: { [fileExt: string]: string | undefined } = { + '.html': 'text/html', + '.js': 'text/javascript', + '.json': 'application/json', + '.css': 'text/css', + '.svg': 'image/svg+xml', +}; + +const mapExtToMediaMimes: { [fileExt: string]: string | undefined } = { + '.bmp': 'image/bmp', + '.gif': 'image/gif', + '.ico': 'image/x-icon', + '.jpe': 'image/jpg', + '.jpeg': 'image/jpg', + '.jpg': 'image/jpg', + '.png': 'image/png', + '.tga': 'image/x-tga', + '.tif': 'image/tiff', + '.tiff': 'image/tiff', + '.woff': 'application/font-woff', +}; + +function getMediaMime(forPath: string) { + const ext = extname(forPath); + + return mapExtToMediaMimes[ext.toLowerCase()] || 'text/plain'; +} diff --git a/lib/vscode/src/vs/server/net/webSocketHandler.ts b/lib/vscode/src/vs/server/net/webSocketHandler.ts new file mode 100644 index 000000000000..225fc29a1dfd --- /dev/null +++ b/lib/vscode/src/vs/server/net/webSocketHandler.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Coder Technologies. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createHash } from 'crypto'; +import { IncomingHttpHeaders } from 'http'; +import * as net from 'net'; +import { Emitter } from 'vs/base/common/event'; +import { ClientConnectionEvent } from 'vs/base/parts/ipc/common/ipc'; +import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import product from 'vs/platform/product/common/product'; +import { ConnectionType, connectionTypeToString, IRemoteExtensionHostStartParams } from 'vs/platform/remote/common/remoteAgentConnection'; +import { ConnectionOptions, parseQueryConnectionOptions } from 'vs/server/net/connection/abstractConnection'; +import { ExtensionHostConnection } from 'vs/server/net/connection/extensionHostConnection'; +import { ManagementConnection } from 'vs/server/net/connection/managementConnection'; +import { ServerProtocol } from 'vs/server/protocol'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { AbstractNetRequestHandler, ParsedRequest } from './abstractNetRequestHandler'; + +type Connection = ExtensionHostConnection | ManagementConnection; + +export type UpgradeListener = (req: ParsedRequest, socket: net.Socket, head: Buffer) => void; + +/** + * Handles client connections to a editor instance via IPC. + */ +export class WebSocketHandler extends AbstractNetRequestHandler { + protected eventName = 'upgrade'; + private readonly _onDidClientConnect = new Emitter(); + public readonly onDidClientConnect = this._onDidClientConnect.event; + + private readonly maxExtraOfflineConnections = 0; + 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; + } + + protected eventListener: UpgradeListener = (req, socket, head) => { + if (req.headers['upgrade'] !== 'websocket' || !req.url) { + const errorMessage = `failed to upgrade for header "${req.headers['upgrade']}" and url: "${req.url}".`; + this.logService.error(errorMessage); + + socket.end('HTTP/1.1 400 Bad Request'); + throw new Error(errorMessage); + } + + this.logService.trace('Upgrade from', req.url, JSON.stringify(this.netServer?.address())); + + let connectionOptions: ConnectionOptions; + + try { + connectionOptions = parseQueryConnectionOptions(req.parsedUrl.searchParams); + } catch (error: unknown) { + this.logService.error(error as Error); + socket.end('HTTP/1.1 400 Bad Request'); + throw error; + } + + socket.on('error', e => { + this.logService.error(`[${connectionOptions.reconnectionToken}] Socket failed for "${req.url}".`, e); + }); + + const { responseHeaders, permessageDeflate } = createReponseHeaders(req.headers); + socket.write(responseHeaders); + + const protocol = new ServerProtocol(new WebSocketNodeSocket(new NodeSocket(socket), permessageDeflate, null, permessageDeflate), this.logService, connectionOptions, VSBuffer.wrap(head)); + + try { + this.connect(protocol); + } catch (error: any) { + protocol.dispose(error.message); + } + }; + + private async connect(protocol: ServerProtocol): Promise { + const message = await protocol.handshake(); + + const clientVersion = message.commit; + const serverVersion = product.commit; + if (serverVersion && clientVersion !== serverVersion) { + this.logService.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) { + this.logService.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. + this.logService.info(logPrefix, 'Client requesting connection'); + + switch (message.desiredConnectionType) { + case ConnectionType.Management: + connection = new ManagementConnection(protocol, this.logService); + + // 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, this.logService, startParams, this.environmentService); + + 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); + this.logService.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'); + } + } +} + +/** Magic number defined by Websocket spec. */ +const WEBSOCKET_MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; + +function createReponseHeaders(incomingHeaders: IncomingHttpHeaders) { + const acceptKey = incomingHeaders['sec-websocket-key']; + // WebSocket standard hash suffix. + const hash = createHash('sha1') + .update(acceptKey + WEBSOCKET_MAGIC) + .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, + }; +} + +function connectLogPrefix(connectionType: ConnectionType, protocol: ServerProtocol) { + return `[${connectionTypeToString(connectionType)}] ${protocol.logPrefix}`; +} diff --git a/lib/vscode/src/vs/server/protocol.ts b/lib/vscode/src/vs/server/protocol.ts index 749c77307229..1944f34598f0 100644 --- a/lib/vscode/src/vs/server/protocol.ts +++ b/lib/vscode/src/vs/server/protocol.ts @@ -8,9 +8,9 @@ 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 { ILogService } from 'vs/platform/log/common/log'; import { ConnectionTypeRequest, HandshakeMessage } from 'vs/platform/remote/common/remoteAgentConnection'; -import { ConnectionOptions } from 'vs/server/connection/abstractConnection'; +import { ConnectionOptions } from 'vs/server/net/connection/abstractConnection'; /** * Matches `remoteAgentConnection.ts#connectToRemoteExtensionHostAgent` @@ -32,7 +32,7 @@ export class ServerProtocol extends PersistentProtocol { constructor( socket: ISocket, - private readonly logger: ConsoleLogger, + private readonly logger: ILogService, private readonly options: ConnectionOptions, // `initialChunk` is likely not used. initialChunk?: VSBuffer | null, @@ -51,7 +51,7 @@ export class ServerProtocol extends PersistentProtocol { * * @remark this may be undefined if the socket or its parent container is disposed. */ - private getSendHandle(): net.Socket | undefined { + public getSendHandle(): net.Socket | undefined { return this.getSocket().socket.socket; } @@ -145,7 +145,7 @@ export class ServerProtocol extends PersistentProtocol { // If still connected try notifying the client. this.sendDisconnect(); - } catch (error) { + } catch (error: any) { // I think the write might fail if already disconnected. this.logger.warn(error.message || error); } diff --git a/lib/vscode/src/vs/server/server.ts b/lib/vscode/src/vs/server/server.ts deleted file mode 100644 index 13d4ba62ccfc..000000000000 --- a/lib/vscode/src/vs/server/server.ts +++ /dev/null @@ -1,402 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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'; -import { CliMessage, VscodeOptions } from '../base/common/ipc'; - -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: CliMessage['args']): Promise { - return main(args as any); - } - - private createWorkbenchURIs(paths: string[]) { - return paths.map(path => - toWorkspaceFolder( - URI.from({ - scheme: Schemas.vscodeRemote, - authority: this.authority, - path, - }), - ), - ); - } - - public async createWorkbenchConstructionOptions(serverUrl: URL, { startPath, args, csStaticBase }: VscodeOptions): Promise { - // TODO: temporarily disabled until parsed arguments are uniformly - // used on the server and within code-oss-dev - // const parsedArgs = this.resolveArgs(); - - // if (!parsedArgs.server) { - // throw new Error('Server argument not provided'); - // } - - // this.authority = parsedArgs.server; - this.authority = serverUrl.host; - - const transformer = createServerURITransformer(this.authority); - - if (!this.servicesPromise) { - this.servicesPromise = this.initializeServices(args as any); - } - await this.servicesPromise; - - const environment = this.services.get(IEnvironmentService) as INativeEnvironmentService; - - const webEndpointUrl = new URL(serverUrl.toString()); - webEndpointUrl.pathname = path.join(csStaticBase, 'lib/vscode'); - - /** - * 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`) - */ - - let workspace: IWorkspace | undefined = undefined; - - if (startPath) { - const workbenchURIs = this.createWorkbenchURIs([startPath.url]); - - // TODO: multiple workbench entries needs further testing. - // const hasSingleEntry = workbenchURIs.length > 0; - // const isSingleEntry = workbenchURIs.length === 1; - - workspace = { - // workspaceUri: isSingleEntry ? undefined : fs.stat(path), - workspaceUri: undefined, - folderUri: workbenchURIs[0].uri, - }; - } - - return { - ...workspace, - remoteAuthority: this.authority, - logLevel: getLogLevel(environment), - workspaceProvider: { - workspace, - trusted: undefined, - payload: [ - ['userDataPath', environment.userDataPath], - ['enableProposedApi', JSON.stringify(args['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/lib/vscode/src/vs/workbench/browser/client.ts b/lib/vscode/src/vs/workbench/browser/client.ts index 3498d62afb00..f220af25d0b8 100644 --- a/lib/vscode/src/vs/workbench/browser/client.ts +++ b/lib/vscode/src/vs/workbench/browser/client.ts @@ -3,8 +3,6 @@ * 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'; @@ -13,22 +11,20 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle 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'; +import { IWorkbenchWebConfiguration } from '../workbench.web.api'; /** - * All client-side customization to VS Code should live in this file when + * @file 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 => { +export const initialize = async (services: ServiceCollection, { productConfiguration }: IWorkbenchWebConfiguration): Promise => { const event = new CustomEvent('ide-ready'); window.dispatchEvent(event); @@ -85,11 +81,10 @@ export const initialize = async (services: ServiceCollection): Promise => }, }); } - 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 => { + + const getUpdate = async (updateCheckEndpoint: string): Promise => { logService.debug('Checking for update...'); const response = await fetch(updateCheckEndpoint, { @@ -124,7 +119,12 @@ export const initialize = async (services: ServiceCollection): Promise => }; const updateLoop = (): void => { - getUpdate() + const updateUrl = productConfiguration.updateUrl; + if (!updateUrl) { + return; + } + + getUpdate(updateUrl) .catch(error => { logService.debug(`failed to check for update: ${error}`); }) @@ -134,9 +134,7 @@ export const initialize = async (services: ServiceCollection): Promise => }); }; - if (!options.disableUpdateCheck) { - updateLoop(); - } + updateLoop(); // This will be used to set the background color while VS Code loads. const theme = storageService.get('colorThemeData', StorageScope.GLOBAL); @@ -146,13 +144,12 @@ export const initialize = async (services: ServiceCollection): Promise => // Use to show or hide logout commands and menu options. const contextKeyService = services.get(IContextKeyService) as IContextKeyService; - contextKeyService.createKey('code-server.authed', options.authed); + contextKeyService.createKey('code-server.authed', !!productConfiguration.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; + window.location.href = productConfiguration.logoutEndpointUrl; }); // Add logout to command palette. diff --git a/lib/vscode/src/vs/workbench/browser/web.main.ts b/lib/vscode/src/vs/workbench/browser/web.main.ts index 8c54945faac1..310e57cda679 100644 --- a/lib/vscode/src/vs/workbench/browser/web.main.ts +++ b/lib/vscode/src/vs/workbench/browser/web.main.ts @@ -32,7 +32,7 @@ import { WorkspaceService } from 'vs/workbench/services/configuration/browser/co import { ConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/browser/signService'; -import type { IWorkbenchConstructionOptions, IWorkspace, IWorkbench } from 'vs/workbench/workbench.web.api'; +import type { IWorkbenchConstructionOptions, IWorkspace, IWorkbench, IWorkbenchWebConfiguration } from 'vs/workbench/workbench.web.api'; import { BrowserStorageService } from 'vs/platform/storage/browser/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; @@ -97,8 +97,8 @@ class BrowserMain extends Disposable { // Startup const instantiationService = workbench.startup(); - // NOTE@coder: initialize our additions - await initialize(services.serviceCollection); + /** @coder Initialize our own additions */ + await initialize(services.serviceCollection, this.configuration as unknown as IWorkbenchWebConfiguration); // Window this._register(instantiationService.createInstance(BrowserWindow)); diff --git a/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 951836de1048..edb07445bda6 100644 --- a/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/lib/vscode/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -13,7 +13,7 @@ import { Event } from 'vs/base/common/event'; import { Action } from 'vs/base/common/actions'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { append, $, Dimension, hide, show } from 'vs/base/browser/dom'; +import { append, $, Dimension, hide, show, reset } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -520,7 +520,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE helperHeader.style.position = 'relative'; const helperText = append(helperHeader, $('div')); - helperText.innerHTML = ''; + reset(helperText); // 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 diff --git a/lib/vscode/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/lib/vscode/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index 948a3aa89351..ba0c0f2e5bf7 100644 --- a/lib/vscode/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/lib/vscode/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -74,8 +74,9 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment @memoize get logExtensionHostCommunication(): boolean { return !!this.args.logExtensionHostCommunication; } + /** @coder Added `override` as this is brought into `AbstractNativeEnvironmentService` */ @memoize - get extensionEnabledProposedApi(): string[] | undefined { + override get extensionEnabledProposedApi(): string[] | undefined { if (Array.isArray(this.args['enable-proposed-api'])) { return this.args['enable-proposed-api']; } diff --git a/lib/vscode/src/vs/workbench/services/host/browser/browserHostService.ts b/lib/vscode/src/vs/workbench/services/host/browser/browserHostService.ts index e4acd70af3bc..cb88d871b9ba 100644 --- a/lib/vscode/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/lib/vscode/src/vs/workbench/services/host/browser/browserHostService.ts @@ -16,7 +16,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; import { IModifierKeyStatus, ModifierKeyEmitter, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { domEvent } from 'vs/base/browser/event'; import { memoize } from 'vs/base/common/decorators'; @@ -39,6 +39,15 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; */ export type IWorkspace = { workspaceUri: URI } | { folderUri: URI } | undefined; + +/** + * @coder This is commonly used when initializing a code server. + */ +export interface IWorkspaceSerialized { + folderUri?: UriComponents; + workspaceUri?: UriComponents; +} + export interface IWorkspaceProvider { /** @@ -71,12 +80,28 @@ export interface IWorkspaceProvider { open(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): Promise; } +/** + * @coder A list of known payload keys. + * @remark This should probably be sent upstream to match `BrowserWorkbenchEnvironmentService` + * This allows for JSON serialization when passing options to a client. + */ +export type PayloadKeys = 'extensionDevelopmentPath' + | 'extensionDevelopmentKind' + | 'extensionTestsPath' + | 'debugRenderer' + | 'debugId' + | 'inspect-brk-extensions' + | 'inspect-extensions' + | 'enableProposedApi' + | 'userDataPath'; + /** * @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]]; +export interface IServerWorkspaceProvider extends Omit { + payload: Array<[PayloadKeys, string]>; + workspace?: IWorkspaceSerialized } enum HostShutdownReason { diff --git a/lib/vscode/src/vs/workbench/workbench.web.api.ts b/lib/vscode/src/vs/workbench/workbench.web.api.ts index f360b5cf782d..425387a501bf 100644 --- a/lib/vscode/src/vs/workbench/workbench.web.api.ts +++ b/lib/vscode/src/vs/workbench/workbench.web.api.ts @@ -13,7 +13,7 @@ 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, IServerWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; +import { IWorkspaceProvider, IWorkspace, IServerWorkspaceProvider as IWebWorkspaceProvider, IWorkspaceSerialized } 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'; @@ -409,26 +409,16 @@ interface IWorkbenchConstructionOptions { //#endregion } -/** - * @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? */ +interface IWorkbenchWebConfiguration extends Omit, IWorkspaceSerialized { + readonly workspaceProvider: IWebWorkspaceProvider; readonly logLevel?: number; - readonly remoteUserDataUri: UriComponents; readonly nlsConfiguration: NLSConfiguration | InternalNLSConfiguration; - readonly commit: string; + readonly productConfiguration: Partial & Pick; } interface IDevelopmentOptions { @@ -591,8 +581,8 @@ export { // Factory create, IWorkbenchConstructionOptions, - IServerWorkbenchConstructionOptions, - IOptionalPathURIs, + IWorkbenchWebConfiguration, + IWorkspaceSerialized, IWorkbench, // Basic Types URI, diff --git a/lib/vscode/yarn.lock b/lib/vscode/yarn.lock index 89c5857e7ad4..cc47fb3611ca 100644 --- a/lib/vscode/yarn.lock +++ b/lib/vscode/yarn.lock @@ -228,6 +228,14 @@ dependencies: applicationinsights "*" +"@types/body-parser@*": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" + integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/chokidar@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@types/chokidar/-/chokidar-2.1.3.tgz#123ab795dba6d89be04bf076e6aecaf8620db674" @@ -247,6 +255,13 @@ dependencies: commander "*" +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + "@types/cookie@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" @@ -281,6 +296,25 @@ resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== +"@types/express-serve-static-core@^4.17.18": + version "4.17.24" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" + integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@^4.17.8": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/glob@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -326,6 +360,11 @@ resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.0.tgz#ca24e6ee6d0df10c003aafe26e93113b8faf0d8e" integrity sha512-cq/NkUUy6rpWD8n7PweNQQBpw2o0cf5v6fbkUVEpOB9VzzIvyPvSEId1/goIj+MciW2v1Lw5mRimKO01XgE9EA== +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -363,11 +402,29 @@ dependencies: "@types/node" "*" +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "@types/semver@^5.4.0", "@types/semver@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== +"@types/serve-static@*": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/sinon@^1.16.36": version "1.16.36" resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-1.16.36.tgz#74bb6ed7928597c1b3fb1b009005e94dc6eae357" @@ -707,6 +764,14 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + acorn-jsx@^5.0.0, acorn-jsx@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" @@ -1009,6 +1074,11 @@ array-each@^1.0.0, array-each@^1.0.1: resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= +array-flatten@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296" + integrity sha1-Qmu52oQJDBg42BLIFQryCoMx4pY= + array-initial@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" @@ -1299,6 +1369,22 @@ bn.js@^5.0.0, bn.js@^5.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -2039,6 +2125,18 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + continuation-local-storage@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" @@ -2054,6 +2152,16 @@ convert-source-map@^1.0.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -2358,6 +2466,13 @@ debug-fabulous@^1.0.0: memoizee "0.4.X" object-assign "4.X" +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -2386,13 +2501,6 @@ debug@4.2.0: dependencies: ms "2.1.2" -debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -2508,6 +2616,11 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" @@ -2691,6 +2804,11 @@ editorconfig@^0.15.2: semver "^5.6.0" sigmund "^1.0.1" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + electron-to-chromium@^1.3.723: version "1.3.737" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.737.tgz#196f2e9656f4f3c31930750e1899c091b72d36b5" @@ -2741,6 +2859,11 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -2870,6 +2993,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + escape-string-regexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -3078,6 +3206,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + event-emitter@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" @@ -3145,6 +3278,43 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" +express@^5.0.0-alpha.8: + version "5.0.0-alpha.8" + resolved "https://registry.yarnpkg.com/express/-/express-5.0.0-alpha.8.tgz#b9dd3a568eab791e3391db47f9e6ab91e61b13fe" + integrity sha512-PL8wTLgaNOiq7GpXt187/yWHkrNSfbr4H0yy+V0fpqJt5wpUzBi9DprAkwGKBFOqWHylJ8EyPy34V5u9YArfng== + dependencies: + accepts "~1.3.7" + array-flatten "2.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "3.1.0" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-is-absolute "1.0.1" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + router "2.0.0-alpha.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + ext@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" @@ -3364,6 +3534,19 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -3521,6 +3704,11 @@ formatio@1.1.1: dependencies: samsam "~1.1" +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -3528,6 +3716,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -4178,7 +4371,18 @@ html-escaper@^2.0.0: inherits "^2.0.1" readable-stream "^3.1.1" -http-errors@1.7.3: +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.7.3, http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -4447,6 +4651,11 @@ ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-absolute-url@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" @@ -5310,6 +5519,11 @@ mdn-data@2.0.14: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + mem@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" @@ -5354,6 +5568,11 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + merge-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-1.0.1.tgz#2a64b24457becd4e4dc608283247e94ce589aa32" @@ -5366,6 +5585,11 @@ merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -5406,6 +5630,11 @@ mime-db@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-db@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== + 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" @@ -5413,7 +5642,14 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.45.0" -mime@^1.4.1: +mime-types@~2.1.24: + version "2.1.32" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== + dependencies: + mime-db "1.49.0" + +mime@1.6.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -5594,6 +5830,11 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -5676,6 +5917,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + neo-async@^2.5.0, neo-async@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -5942,6 +6188,13 @@ object.reduce@^1.0.0: for-own "^1.0.0" make-iterator "^1.0.0" +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -6204,6 +6457,11 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" integrity sha1-m387DeMr543CQBsXVzzK8Pb1nZQ= +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -6231,7 +6489,7 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0: +path-is-absolute@1.0.1, path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= @@ -6263,6 +6521,11 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -6741,6 +7004,14 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-4.0.1.tgz#326c3250776c7044cd19655ccbfadf2e065a045c" @@ -6835,6 +7106,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -6879,6 +7155,21 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + raw-body@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" @@ -7241,6 +7532,19 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +router@2.0.0-alpha.1: + version "2.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/router/-/router-2.0.0-alpha.1.tgz#9188213b972215e03ef830e0ac77837870085f6d" + integrity sha512-fz/T/qLkJM6RTtbqGqA1+uZ88ejqJoPyKeJAeXPYjebA7HzV/UyflH4gXWqW/Y6SERnp4kDwNARjqy6se3PcOw== + dependencies: + array-flatten "2.1.1" + debug "3.1.0" + methods "~1.1.2" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + setprototypeof "1.1.0" + utils-merge "1.0.1" + run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -7265,16 +7569,16 @@ rxjs@^6.4.0, rxjs@^6.6.0: dependencies: tslib "^1.9.0" +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, 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, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -7355,6 +7659,25 @@ semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + serialize-javascript@5.0.1, serialize-javascript@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" @@ -7369,6 +7692,16 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -7389,6 +7722,11 @@ setimmediate@^1.0.4: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" @@ -7701,7 +8039,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.5.0 < 2": +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -8287,6 +8625,14 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + type@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" @@ -8403,7 +8749,7 @@ 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: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= @@ -8465,6 +8811,11 @@ util@^0.11.0: dependencies: inherits "2.0.3" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -8502,6 +8853,11 @@ value-or-function@^3.0.0: resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + vendors@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" diff --git a/package.json b/package.json index 674180589141..80d1cc25b801 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "main": "out/node/entry.js", "devDependencies": { "@schemastore/package": "^0.0.6", - "@types/body-parser": "^1.19.0", "@types/browserify": "^12.0.36", "@types/compression": "^1.7.0", "@types/cookie-parser": "^1.4.2", @@ -50,8 +49,6 @@ "@types/safe-compare": "^1.1.0", "@types/semver": "^7.1.0", "@types/split2": "^3.2.0", - "@types/tar-fs": "^2.0.0", - "@types/tar-stream": "^2.1.0", "@types/trusted-types": "^2.0.2", "@types/ws": "^7.2.6", "@typescript-eslint/eslint-plugin": "^4.7.0", @@ -71,7 +68,7 @@ "stylelint": "^13.0.0", "stylelint-config-recommended": "^5.0.0", "ts-node": "^10.0.0", - "typescript": "^4.1.3" + "typescript": "^4.4.0-dev.20210528" }, "resolutions": { "normalize-package-data": "^3.0.0", @@ -87,7 +84,6 @@ "dependencies": { "@coder/logger": "1.1.16", "argon2": "^0.28.0", - "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "^1.4.5", "env-paths": "^2.2.0", @@ -105,7 +101,6 @@ "safe-compare": "^1.1.4", "semver": "^7.1.3", "split2": "^3.2.2", - "tar-fs": "^2.0.0", "ws": "^8.0.0", "xdg-basedir": "^4.0.0", "yarn": "^1.22.4" diff --git a/src/browser/pages/login.html b/src/browser/pages/login.html index 896927e3812c..5e6c578b05e6 100644 --- a/src/browser/pages/login.html +++ b/src/browser/pages/login.html @@ -30,7 +30,7 @@

Welcome to code-server