Compare commits

...

2 Commits

Author SHA1 Message Date
Teffen Ellis 2b49507053 Refactor vscode endpoints to use fork directly. 2021-09-29 23:14:56 -04:00
Teffen Ellis 2504f6fce4 Add linkup command to improve link functionality 2021-09-29 18:45:19 -04:00
49 changed files with 435 additions and 1818 deletions

View File

@ -3,4 +3,9 @@ root = true
[*] [*]
indent_style = space indent_style = space
trim_trailing_whitespace = true trim_trailing_whitespace = true
# The indent size used in the `package.json` file cannot be changed
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
[{*.yml,*.yaml,package.json}]
indent_style = space
indent_size = 2 indent_size = 2

1
.gitignore vendored
View File

@ -12,6 +12,7 @@ vendor/modules
node-* node-*
/plugins /plugins
/lib/coder-cloud-agent /lib/coder-cloud-agent
/lib/linkup
.home .home
coverage coverage
**/.DS_Store **/.DS_Store

View File

@ -2,3 +2,16 @@ printWidth: 120
semi: false semi: false
trailingComma: all trailingComma: all
arrowParens: always arrowParens: always
singleQuote: false
useTabs: false
overrides:
# Attempt to keep VScode's existing code style intact.
- files: "vendor/modules/code-oss-dev/**/*.ts"
options:
# No limit defined upstream.
printWidth: 10000
semi: true
singleQuote: true
useTabs: true
arrowParens: avoid

View File

@ -15,20 +15,29 @@ main() {
chmod +x out/node/entry.js chmod +x out/node/entry.js
fi fi
if ! [ -f ./lib/coder-cloud-agent ]; then
echo "Downloading the cloud agent..."
# for arch; we do not use OS from lib.sh and get our own. # for arch; we do not use OS from lib.sh and get our own.
# lib.sh normalizes macos to darwin - but cloud-agent's binaries do not # lib.sh normalizes macos to darwin - but cloud-agent's binaries do not
source ./ci/lib.sh source ./ci/lib.sh
OS="$(uname | tr '[:upper:]' '[:lower:]')" OS="$(uname | tr '[:upper:]' '[:lower:]')"
if ! [ -f ./lib/coder-cloud-agent ]; then
echo "Downloading the cloud agent..."
set +e set +e
curl -fsSL "https://github.com/cdr/cloud-agent/releases/latest/download/cloud-agent-$OS-$ARCH" -o ./lib/coder-cloud-agent curl -fsSL "https://github.com/cdr/cloud-agent/releases/latest/download/cloud-agent-$OS-$ARCH" -o ./lib/coder-cloud-agent
chmod +x ./lib/coder-cloud-agent chmod +x ./lib/coder-cloud-agent
set -e set -e
fi fi
if ! [ -f ./lib/linkup ]; then
echo "Downloading Link agent..."
set +e
curl -fsSL "https://storage.googleapis.com/coder-link-releases/latest/linkup-$OS-$ARCH" -o ./lib/linkup
chmod +x ./lib/linkup
set -e
fi
yarn browserify out/browser/register.js -o out/browser/register.browserified.js yarn browserify out/browser/register.js -o out/browser/register.browserified.js
yarn browserify out/browser/pages/login.js -o out/browser/pages/login.browserified.js yarn browserify out/browser/pages/login.js -o out/browser/pages/login.browserified.js
yarn browserify out/browser/pages/vscode.js -o out/browser/pages/vscode.browserified.js yarn browserify out/browser/pages/vscode.js -o out/browser/pages/vscode.browserified.js

View File

@ -61,6 +61,7 @@ EOF
rsync node_modules/ "$RELEASE_PATH/node_modules" rsync node_modules/ "$RELEASE_PATH/node_modules"
mkdir -p "$RELEASE_PATH/lib" mkdir -p "$RELEASE_PATH/lib"
rsync ./lib/coder-cloud-agent "$RELEASE_PATH/lib" rsync ./lib/coder-cloud-agent "$RELEASE_PATH/lib"
rsync ./lib/linkup "$RELEASE_PATH/lib"
fi fi
} }

View File

@ -11,8 +11,10 @@ main() {
cd vendor/modules/code-oss-dev cd vendor/modules/code-oss-dev
yarn gulp compile-build compile-extensions-build compile-extension-media yarn gulp compile-build compile-extensions-build compile-extension-media compile-web
yarn gulp optimize --gulpfile ./coder.js yarn gulp optimize --gulpfile ./coder.js
if [[ $MINIFY ]]; then if [[ $MINIFY ]]; then
yarn gulp minify --gulpfile ./coder.js yarn gulp minify --gulpfile ./coder.js
fi fi

View File

@ -63,6 +63,12 @@ main() {
echo "Failed to download cloud agent; --link will not work" echo "Failed to download cloud agent; --link will not work"
fi fi
if curl -fsSL "https://storage.googleapis.com/coder-link-releases/latest/linkup-$OS-$ARCH" -o ./lib/linkup; then
chmod +x ./lib/linkup
else
echo "Failed to download Link agent; the Link extension will not work"
fi
if ! vscode_yarn; then if ! vscode_yarn; then
echo "You may not have the required dependencies to build the native modules." echo "You may not have the required dependencies to build the native modules."
echo "Please see https://github.com/cdr/code-server/blob/master/docs/npm.md" echo "Please see https://github.com/cdr/code-server/blob/master/docs/npm.md"

View File

@ -8,7 +8,7 @@ async function main(): Promise<void> {
try { try {
const watcher = new Watcher() const watcher = new Watcher()
await watcher.watch() await watcher.watch()
} catch (error) { } catch (error: any) {
console.error(error.message) console.error(error.message)
process.exit(1) process.exit(1)
} }
@ -38,6 +38,9 @@ class Watcher {
} }
const vscode = cp.spawn("yarn", ["watch"], { cwd: this.vscodeSourcePath }) 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 tsc = cp.spawn("tsc", ["--watch", "--pretty", "--preserveWatchOutput"], { cwd: this.rootPath })
const plugin = process.env.PLUGIN_DIR const plugin = process.env.PLUGIN_DIR
? cp.spawn("yarn", ["build", "--watch"], { cwd: process.env.PLUGIN_DIR }) ? cp.spawn("yarn", ["build", "--watch"], { cwd: process.env.PLUGIN_DIR })
@ -48,6 +51,10 @@ class Watcher {
vscode.removeAllListeners() vscode.removeAllListeners()
vscode.kill() vscode.kill()
Watcher.log("killing vs code web extension watcher")
vscodeWebExtensions.removeAllListeners()
vscodeWebExtensions.kill()
Watcher.log("killing tsc") Watcher.log("killing tsc")
tsc.removeAllListeners() tsc.removeAllListeners()
tsc.kill() tsc.kill()
@ -75,10 +82,17 @@ class Watcher {
Watcher.log("vs code watcher terminated unexpectedly") Watcher.log("vs code watcher terminated unexpectedly")
cleanup(code) cleanup(code)
}) })
vscodeWebExtensions.on("exit", (code) => {
Watcher.log("vs code extension watcher terminated unexpectedly")
cleanup(code)
})
tsc.on("exit", (code) => { tsc.on("exit", (code) => {
Watcher.log("tsc terminated unexpectedly") Watcher.log("tsc terminated unexpectedly")
cleanup(code) cleanup(code)
}) })
if (plugin) { if (plugin) {
plugin.on("exit", (code) => { plugin.on("exit", (code) => {
Watcher.log("plugin terminated unexpectedly") 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)) vscode.stderr.on("data", (d) => process.stderr.write(d))
tsc.stderr.on("data", (d) => process.stderr.write(d)) tsc.stderr.on("data", (d) => process.stderr.write(d))
if (plugin) { if (plugin) {
plugin.stderr.on("data", (d) => process.stderr.write(d)) plugin.stderr.on("data", (d) => process.stderr.write(d))
} }

View File

@ -35,7 +35,6 @@
"main": "out/node/entry.js", "main": "out/node/entry.js",
"devDependencies": { "devDependencies": {
"@schemastore/package": "^0.0.6", "@schemastore/package": "^0.0.6",
"@types/body-parser": "^1.19.0",
"@types/browserify": "^12.0.36", "@types/browserify": "^12.0.36",
"@types/compression": "^1.7.0", "@types/compression": "^1.7.0",
"@types/cookie-parser": "^1.4.2", "@types/cookie-parser": "^1.4.2",
@ -48,8 +47,7 @@
"@types/safe-compare": "^1.1.0", "@types/safe-compare": "^1.1.0",
"@types/semver": "^7.1.0", "@types/semver": "^7.1.0",
"@types/split2": "^3.2.0", "@types/split2": "^3.2.0",
"@types/tar-fs": "^2.0.0", "@types/trusted-types": "^2.0.2",
"@types/tar-stream": "^2.1.0",
"@types/ws": "^8.0.0", "@types/ws": "^8.0.0",
"@typescript-eslint/eslint-plugin": "^4.7.0", "@typescript-eslint/eslint-plugin": "^4.7.0",
"@typescript-eslint/parser": "^4.7.0", "@typescript-eslint/parser": "^4.7.0",
@ -68,7 +66,7 @@
"stylelint": "^13.0.0", "stylelint": "^13.0.0",
"stylelint-config-recommended": "^5.0.0", "stylelint-config-recommended": "^5.0.0",
"ts-node": "^10.0.0", "ts-node": "^10.0.0",
"typescript": "^4.1.3" "typescript": "^4.4.0-dev.20210528"
}, },
"resolutions": { "resolutions": {
"ansi-regex": "^5.0.1", "ansi-regex": "^5.0.1",
@ -85,7 +83,6 @@
"dependencies": { "dependencies": {
"@coder/logger": "1.1.16", "@coder/logger": "1.1.16",
"argon2": "^0.28.0", "argon2": "^0.28.0",
"body-parser": "^1.19.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"env-paths": "^2.2.0", "env-paths": "^2.2.0",
@ -103,7 +100,6 @@
"safe-compare": "^1.1.4", "safe-compare": "^1.1.4",
"semver": "^7.1.3", "semver": "^7.1.3",
"split2": "^3.2.2", "split2": "^3.2.2",
"tar-fs": "^2.0.0",
"ws": "^8.0.0", "ws": "^8.0.0",
"xdg-basedir": "^4.0.0", "xdg-basedir": "^4.0.0",
"yarn": "^1.22.4" "yarn": "^1.22.4"

View File

@ -1,18 +1,18 @@
{ {
"name": "code-server", "name": "code-server",
"short_name": "code-server", "short_name": "code-server",
"start_url": "{{BASE}}", "start_url": ".",
"display": "fullscreen", "display": "fullscreen",
"background-color": "#fff", "background-color": "#fff",
"description": "Run editors on a remote server.", "description": "Run editors on a remote server.",
"icons": [ "icons": [
{ {
"src": "{{CS_STATIC_BASE}}/src/browser/media/pwa-icon-192.png", "src": "./src/browser/media/pwa-icon-192.png",
"type": "image/png", "type": "image/png",
"sizes": "192x192" "sizes": "192x192"
}, },
{ {
"src": "{{CS_STATIC_BASE}}/src/browser/media/pwa-icon-512.png", "src": "./src/browser/media/pwa-icon-512.png",
"type": "image/png", "type": "image/png",
"sizes": "512x512" "sizes": "512x512"
} }

View File

@ -11,13 +11,13 @@
content="style-src 'self'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;" content="style-src 'self'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/> />
<title>{{ERROR_TITLE}} - code-server</title> <title>{{ERROR_TITLE}} - code-server</title>
<link rel="icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon-dark-support.svg" /> <link rel="icon" href="./_static/src/browser/media/favicon-dark-support.svg" />
<link rel="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" /> <link rel="alternate icon" href="./_static/src/browser/media/favicon.ico" />
<link rel="manifest" href="{{CS_STATIC_BASE}}/src/browser/media/manifest.json" crossorigin="use-credentials" /> <link rel="manifest" href="./manifest.json" crossorigin="use-credentials" />
<link rel="apple-touch-icon" sizes="192x192" href="{{CS_STATIC_BASE}}/src/browser/media/pwa-icon-192.png" /> <link rel="apple-touch-icon" sizes="192x192" href="./_static/src/browser/media/pwa-icon-192.png" />
<link rel="apple-touch-icon" sizes="512x512" href="{{CS_STATIC_BASE}}/src/browser/media/pwa-icon-512.png" /> <link rel="apple-touch-icon" sizes="512x512" href="./_static/src/browser/media/pwa-icon-512.png" />
<link href="{{CS_STATIC_BASE}}/src/browser/pages/global.css" rel="stylesheet" /> <link href="./_static/src/browser/pages/global.css" rel="stylesheet" />
<link href="{{CS_STATIC_BASE}}/src/browser/pages/error.css" rel="stylesheet" /> <link href="./_static/src/browser/pages/error.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" /> <meta id="coder-options" data-settings="{{OPTIONS}}" />
</head> </head>
<body> <body>
@ -30,6 +30,6 @@
</div> </div>
</div> </div>
</div> </div>
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/out/browser/register.browserified.js"></script> <script data-cfasync="false" src="./_static/out/browser/register.browserified.js"></script>
</body> </body>
</html> </html>

View File

@ -11,13 +11,13 @@
content="style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;" content="style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:; font-src 'self' data:;"
/> />
<title>code-server login</title> <title>code-server login</title>
<link rel="icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon-dark-support.svg" /> <link rel="icon" href="./_static/src/browser/media/favicon-dark-support.svg" />
<link rel="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" /> <link rel="alternate icon" href="./_static/src/browser/media/favicon.ico" />
<link rel="manifest" href="{{CS_STATIC_BASE}}/src/browser/media/manifest.json" crossorigin="use-credentials" /> <link rel="manifest" href="./manifest.json" crossorigin="use-credentials" />
<link rel="apple-touch-icon" sizes="192x192" href="{{CS_STATIC_BASE}}/src/browser/media/pwa-icon-192.png" /> <link rel="apple-touch-icon" sizes="192x192" href="./_static/src/browser/media/pwa-icon-192.png" />
<link rel="apple-touch-icon" sizes="512x512" href="{{CS_STATIC_BASE}}/src/browser/media/pwa-icon-512.png" /> <link rel="apple-touch-icon" sizes="512x512" href="./_static/src/browser/media/pwa-icon-512.png" />
<link href="{{CS_STATIC_BASE}}/src/browser/pages/global.css" rel="stylesheet" /> <link href="./_static/src/browser/pages/global.css" rel="stylesheet" />
<link href="{{CS_STATIC_BASE}}/src/browser/pages/login.css" rel="stylesheet" /> <link href="./_static/src/browser/pages/login.css" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}" /> <meta id="coder-options" data-settings="{{OPTIONS}}" />
</head> </head>
<body> <body>
@ -30,7 +30,6 @@
<div class="content"> <div class="content">
<form class="login-form" method="post"> <form class="login-form" method="post">
<input class="user" type="text" autocomplete="username" /> <input class="user" type="text" autocomplete="username" />
<input id="base" type="hidden" name="base" value="/" />
<div class="field"> <div class="field">
<input <input
required required
@ -49,5 +48,5 @@
</div> </div>
</div> </div>
</body> </body>
<script data-cfasync="false" src="{{CS_STATIC_BASE}}/out/browser/pages/login.browserified.js"></script> <script data-cfasync="false" src="./_static/out/browser/pages/login.browserified.js"></script>
</html> </html>

View File

@ -1,8 +1 @@
import { getOptions } from "../../common/util"
import "../register" import "../register"
const options = getOptions()
const el = document.getElementById("base") as HTMLInputElement
if (el) {
el.value = options.base
}

View File

@ -17,11 +17,6 @@
<!-- Workbench Configuration --> <!-- Workbench Configuration -->
<meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}" /> <meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}" />
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}" />
<meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}" />
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}" />
<!-- Workbench Icon/Manifest/CSS --> <!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon-dark-support.svg" /> <link rel="icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon-dark-support.svg" />
<link rel="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" /> <link rel="alternate icon" href="{{CS_STATIC_BASE}}/src/browser/media/favicon.ico" />

View File

@ -1,253 +0,0 @@
import { getOptions, Options } from "../../common/util"
import "../register"
// TODO@jsjoeio: Add proper types.
type FixMeLater = any
// NOTE@jsjoeio
// This lives here ../../../lib/vscode/src/vs/base/common/platform.ts#L106
export const nlsConfigElementId = "vscode-remote-nls-configuration"
type NlsConfiguration = {
locale: string
availableLanguages: { [key: string]: string } | {}
_languagePackId?: string
_translationsConfigFile?: string
_cacheRoot?: string
_resolvedLanguagePackCoreLocation?: string
_corruptedFile?: string
_languagePackSupport?: boolean
loadBundle?: FixMeLater
}
/**
* Helper function to create the path to the bundle
* for getNlsConfiguration.
*/
export function createBundlePath(_resolvedLanguagePackCoreLocation: string | undefined, bundle: string) {
// NOTE@jsjoeio - this comment was here before me
// Refers to operating systems that use a different path separator.
// Probably just Windows but we're not sure if "/" breaks on Windows
// so we'll leave it alone for now.
// FIXME: Only works if path separators are /.
return (_resolvedLanguagePackCoreLocation || "") + "/" + bundle.replace(/\//g, "!") + ".nls.json"
}
/**
* A helper function to get the NLS Configuration settings.
*
* This is used by VSCode for localizations (i.e. changing
* the display language).
*
* Make sure to wrap this in a try/catch block when you call it.
**/
export function getNlsConfiguration(_document: Document, base: string) {
const errorMsgPrefix = "[vscode]"
const nlsConfigElement = _document?.getElementById(nlsConfigElementId)
const dataSettings = nlsConfigElement?.getAttribute("data-settings")
if (!nlsConfigElement) {
throw new Error(
`${errorMsgPrefix} Could not parse NLS configuration. Could not find nlsConfigElement with id: ${nlsConfigElementId}`,
)
}
if (!dataSettings) {
throw new Error(
`${errorMsgPrefix} Could not parse NLS configuration. Found nlsConfigElement but missing data-settings attribute.`,
)
}
const nlsConfig = JSON.parse(dataSettings) as NlsConfiguration
if (nlsConfig._resolvedLanguagePackCoreLocation) {
// NOTE@jsjoeio
// Not sure why we use Object.create(null) instead of {}
// They are not the same
// See: https://stackoverflow.com/a/15518712/3015595
// We copied this from ../../../lib/vscode/src/bootstrap.js#L143
const bundles: {
[key: string]: string
} = Object.create(null)
type LoadBundleCallback = (_: undefined, result?: string) => void
nlsConfig.loadBundle = async (bundle: string, _language: string, cb: LoadBundleCallback): Promise<void> => {
const result = bundles[bundle]
if (result) {
return cb(undefined, result)
}
try {
const path = createBundlePath(nlsConfig._resolvedLanguagePackCoreLocation, bundle)
const response = await fetch(`${base}/vscode/resource/?path=${encodeURIComponent(path)}`)
const json = await response.json()
bundles[bundle] = json
return cb(undefined, json)
} catch (error) {
return cb(error)
}
}
}
return nlsConfig
}
type GetLoaderParams = {
nlsConfig: NlsConfiguration
options: Options
_window: Window
}
/**
* Link to types in the loader source repo
* https://github.com/microsoft/vscode-loader/blob/main/src/loader.d.ts#L280
*/
type Loader = {
baseUrl: string
recordStats: boolean
// TODO@jsjoeio: There don't appear to be any types for trustedTypes yet.
trustedTypesPolicy: FixMeLater
paths: {
[key: string]: string
}
"vs/nls": NlsConfiguration
}
/**
* A helper function which creates a script url if the value
* is valid.
*
* Extracted into a function to make it easier to test
*/
export function _createScriptURL(value: string, origin: string): string {
if (value.startsWith(origin)) {
return value
}
throw new Error(`Invalid script url: ${value}`)
}
/**
* A helper function to get the require loader
*
* This used by VSCode/code-server
* to load files.
*
* We extracted the logic into a function so that
* it's easier to test.
**/
export function getConfigurationForLoader({ nlsConfig, options, _window }: GetLoaderParams) {
const loader: Loader = {
// Without the full URL VS Code will try to load file://.
baseUrl: `${window.location.origin}${options.csStaticBase}/vendor/modules/code-oss-dev/out`,
recordStats: true,
trustedTypesPolicy: (_window as FixMeLater).trustedTypes?.createPolicy("amdLoader", {
createScriptURL(value: string): string {
return _createScriptURL(value, window.location.origin)
},
}),
paths: {
"vscode-textmate": `../node_modules/vscode-textmate/release/main`,
"vscode-oniguruma": `../node_modules/vscode-oniguruma/release/main`,
xterm: `../node_modules/xterm/lib/xterm.js`,
"xterm-addon-search": `../node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
"xterm-addon-unicode11": `../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
"xterm-addon-webgl": `../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
"tas-client-umd": `../node_modules/tas-client-umd/lib/tas-client-umd.js`,
"iconv-lite-umd": `../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js`,
jschardet: `../node_modules/jschardet/dist/jschardet.min.js`,
},
"vs/nls": nlsConfig,
}
return loader
}
/**
* Sets the body background color to match the theme.
*/
export function setBodyBackgroundToThemeBackgroundColor(_document: Document, _localStorage: Storage) {
const errorMsgPrefix = "[vscode]"
const colorThemeData = _localStorage.getItem("colorThemeData")
if (!colorThemeData) {
throw new Error(
`${errorMsgPrefix} Could not set body background to theme background color. Could not find colorThemeData in localStorage.`,
)
}
let _colorThemeData
try {
// We wrap this JSON.parse logic in a try/catch
// because it can throw if the JSON is invalid.
// and instead of throwing a random error
// we can throw our own error, which will be more helpful
// to the end user.
_colorThemeData = JSON.parse(colorThemeData)
} catch {
throw new Error(
`${errorMsgPrefix} Could not set body background to theme background color. Could not parse colorThemeData from localStorage.`,
)
}
const hasColorMapProperty = Object.prototype.hasOwnProperty.call(_colorThemeData, "colorMap")
if (!hasColorMapProperty) {
throw new Error(
`${errorMsgPrefix} Could not set body background to theme background color. colorThemeData is missing colorMap.`,
)
}
const editorBgColor = _colorThemeData.colorMap["editor.background"]
if (!editorBgColor) {
throw new Error(
`${errorMsgPrefix} Could not set body background to theme background color. colorThemeData.colorMap["editor.background"] is undefined.`,
)
}
_document.body.style.background = editorBgColor
return null
}
/**
* A helper function to encapsulate all the
* logic used in this file.
*
* We purposely include all of this in a single function
* so that it's easier to test.
*/
export function main(_document: Document | undefined, _window: Window | undefined, _localStorage: Storage | undefined) {
if (!_document) {
throw new Error(`document is undefined.`)
}
if (!_window) {
throw new Error(`window is undefined.`)
}
if (!_localStorage) {
throw new Error(`localStorage is undefined.`)
}
const options = getOptions()
const nlsConfig = getNlsConfiguration(_document, options.base)
const loader = getConfigurationForLoader({
nlsConfig,
options,
_window,
})
;(self.require as unknown as Loader) = loader
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}
try {
main(document, window, localStorage)
} catch (error) {
console.error("[vscode] failed to initialize VS Code")
console.error(error)
}

View File

@ -1,9 +1,8 @@
import { logger } from "@coder/logger" import { logger } from "@coder/logger"
import { getOptions, normalize, logError } from "../common/util" import { getClientConfiguration, normalize, logError } from "../common/util"
export async function registerServiceWorker(): Promise<void> { export async function registerServiceWorker(): Promise<void> {
const options = getOptions() const options = getClientConfiguration()
logger.level = options.logLevel
const path = normalize(`${options.csStaticBase}/out/browser/serviceWorker.js`) const path = normalize(`${options.csStaticBase}/out/browser/serviceWorker.js`)
try { try {
@ -11,8 +10,8 @@ export async function registerServiceWorker(): Promise<void> {
scope: options.base + "/", scope: options.base + "/",
}) })
logger.info(`[Service Worker] registered`) logger.info(`[Service Worker] registered`)
} catch (error) { } catch (error: unknown) {
logError(logger, `[Service Worker] registration`, error) logError(logger, `[Service Worker] registration`, error as Error)
} }
} }

View File

@ -46,7 +46,7 @@ export class Emitter<T> {
this.listeners.map(async (cb) => { this.listeners.map(async (cb) => {
try { try {
await cb(value, promise) await cb(value, promise)
} catch (error) { } catch (error: any) {
logger.error(error.message) logger.error(error.message)
} }
}), }),

View File

@ -1,19 +1,3 @@
/*
* This file exists in two locations:
* - src/common/util.ts
* - lib/vscode/src/vs/server/common/util.ts
* The second is a symlink to the first.
*/
/**
* Base options included on every page.
*/
export interface Options {
base: string
csStaticBase: string
logLevel: number
}
/** /**
* Split a string up to the delimiter. If the delimiter doesn't exist the first * Split a string up to the delimiter. If the delimiter doesn't exist the first
* item will have all the text and the second item will be an empty string. * item will have all the text and the second item will be an empty string.
@ -67,14 +51,14 @@ export const resolveBase = (base?: string): string => {
} }
/** /**
* Get options embedded in the HTML or query params. * Get client-side configuration embedded in the HTML or query params.
*/ */
export const getOptions = <T extends Options>(): T => { export const getClientConfiguration = <T extends CodeServerLib.ClientConfiguration>(): T => {
let options: T let config: T
try { try {
options = JSON.parse(document.getElementById("coder-options")!.getAttribute("data-settings")!) config = JSON.parse(document.getElementById("coder-options")!.getAttribute("data-settings")!)
} catch (error) { } catch (error) {
options = {} as T config = {} as T
} }
// You can also pass options in stringified form to the options query // You can also pass options in stringified form to the options query
@ -83,16 +67,16 @@ export const getOptions = <T extends Options>(): T => {
const params = new URLSearchParams(location.search) const params = new URLSearchParams(location.search)
const queryOpts = params.get("options") const queryOpts = params.get("options")
if (queryOpts) { if (queryOpts) {
options = { config = {
...options, ...config,
...JSON.parse(queryOpts), ...JSON.parse(queryOpts),
} }
} }
options.base = resolveBase(options.base) config.base = resolveBase(config.base)
options.csStaticBase = resolveBase(options.csStaticBase) config.csStaticBase = resolveBase(config.csStaticBase)
return options return config
} }
/** /**
@ -109,17 +93,6 @@ export const arrayify = <T>(value?: T | T[]): T[] => {
return [value] return [value]
} }
/**
* Get the first string. If there's no string return undefined.
*/
export const getFirstString = (value: string | string[] | object | undefined): string | undefined => {
if (Array.isArray(value)) {
return value[0]
}
return typeof value === "string" ? value : undefined
}
// TODO: Might make sense to add Error handling to the logger itself. // TODO: Might make sense to add Error handling to the logger itself.
export function logError(logger: { error: (msg: string) => void }, prefix: string, err: Error | string): void { export function logError(logger: { error: (msg: string) => void }, prefix: string, err: Error | string): void {
if (err instanceof Error) { if (err instanceof Error) {

View File

@ -9,6 +9,35 @@ import { DefaultedArgs } from "./cli"
import { isNodeJSErrnoException } from "./util" import { isNodeJSErrnoException } from "./util"
import { handleUpgrade } from "./wsRouter" import { handleUpgrade } from "./wsRouter"
type ListenOptions = Pick<DefaultedArgs, "socket" | "port" | "host">
const listen = (server: http.Server, { host, port, socket }: ListenOptions) => {
return new Promise<void>(async (resolve, reject) => {
server.on("error", reject)
const onListen = () => {
// Promise resolved earlier so this is an unrelated error.
server.off("error", reject)
server.on("error", (err) => util.logError(logger, "http server error", err))
resolve()
}
if (socket) {
try {
await fs.unlink(socket)
} catch (error: any) {
handleArgsSocketCatchError(error)
}
server.listen(socket, onListen)
} else {
// [] is the correct format when using :: but Node errors with them.
server.listen(port, host.replace(/^\[|\]$/g, ""), onListen)
}
})
}
/** /**
* Create an Express app and an HTTP/S server to serve it. * Create an Express app and an HTTP/S server to serve it.
*/ */
@ -27,28 +56,7 @@ export const createApp = async (args: DefaultedArgs): Promise<[Express, Express,
) )
: http.createServer(app) : http.createServer(app)
let resolved = false await listen(server, args)
await new Promise<void>(async (resolve2, reject) => {
const resolve = () => {
resolved = true
resolve2()
}
server.on("error", (err) => {
handleServerError(resolved, err, reject)
})
if (args.socket) {
try {
await fs.unlink(args.socket)
} catch (error: any) {
handleArgsSocketCatchError(error)
}
server.listen(args.socket, resolve)
} else {
// [] is the correct format when using :: but Node errors with them.
server.listen(args.port, args.host.replace(/^\[|\]$/g, ""), resolve)
}
})
const wsApp = express() const wsApp = express()
handleUpgrade(wsApp, server) handleUpgrade(wsApp, server)

View File

@ -3,12 +3,11 @@ import { promises as fs } from "fs"
import yaml from "js-yaml" import yaml from "js-yaml"
import * as os from "os" import * as os from "os"
import * as path from "path" import * as path from "path"
import { Args as VsArgs } from "../../typings/ipc"
import { canConnect, generateCertificate, generatePassword, humanPath, paths } from "./util" import { canConnect, generateCertificate, generatePassword, humanPath, paths } from "./util"
export enum Feature { export enum Feature {
/** Web socket compression. */ // No current experimental features!
PermessageDeflate = "permessage-deflate", Placeholder = "placeholder",
} }
export enum AuthType { export enum AuthType {
@ -30,7 +29,21 @@ export enum LogLevel {
export class OptionalString extends Optional<string> {} export class OptionalString extends Optional<string> {}
export interface Args extends VsArgs { export interface Args
extends Pick<
CodeServerLib.NativeParsedArgs,
| "user-data-dir"
| "enable-proposed-api"
| "extensions-dir"
| "builtin-extensions-dir"
| "extra-extensions-dir"
| "extra-builtin-extensions-dir"
| "ignore-last-opened"
| "locale"
| "log"
| "verbose"
| "_"
> {
config?: string config?: string
auth?: AuthType auth?: AuthType
password?: string password?: string
@ -56,8 +69,6 @@ export interface Args extends VsArgs {
"show-versions"?: boolean "show-versions"?: boolean
"uninstall-extension"?: string[] "uninstall-extension"?: string[]
"proxy-domain"?: string[] "proxy-domain"?: string[]
locale?: string
_: string[]
"reuse-window"?: boolean "reuse-window"?: boolean
"new-window"?: boolean "new-window"?: boolean
@ -546,7 +557,7 @@ export async function readConfigFile(configPath?: string): Promise<ConfigArgs> {
flag: "wx", // wx means to fail if the path exists. flag: "wx", // wx means to fail if the path exists.
}) })
logger.info(`Wrote default config file to ${humanPath(configPath)}`) logger.info(`Wrote default config file to ${humanPath(configPath)}`)
} catch (error) { } catch (error: any) {
// EEXIST is fine; we don't want to overwrite existing configurations. // EEXIST is fine; we don't want to overwrite existing configurations.
if (error.code !== "EEXIST") { if (error.code !== "EEXIST") {
throw error throw error
@ -670,7 +681,7 @@ export const shouldOpenInExistingInstance = async (args: Args): Promise<string |
const readSocketPath = async (): Promise<string | undefined> => { const readSocketPath = async (): Promise<string | undefined> => {
try { try {
return await fs.readFile(path.join(os.tmpdir(), "vscode-ipc"), "utf8") return await fs.readFile(path.join(os.tmpdir(), "vscode-ipc"), "utf8")
} catch (error) { } catch (error: any) {
if (error.code !== "ENOENT") { if (error.code !== "ENOENT") {
throw error throw error
} }

View File

@ -3,11 +3,13 @@ import { JSONSchemaForNPMPackageJsonFiles } from "@schemastore/package"
import * as os from "os" import * as os from "os"
import * as path from "path" import * as path from "path"
export const WORKBENCH_WEB_CONFIG_ID = "vscode-workbench-web-configuration"
export function getPackageJson(relativePath: string): JSONSchemaForNPMPackageJsonFiles { export function getPackageJson(relativePath: string): JSONSchemaForNPMPackageJsonFiles {
let pkg = {} let pkg = {}
try { try {
pkg = require(relativePath) pkg = require(relativePath)
} catch (error) { } catch (error: any) {
logger.warn(error.message) logger.warn(error.message)
} }
@ -19,5 +21,6 @@ const pkg = getPackageJson("../../package.json")
export const version = pkg.version || "development" export const version = pkg.version || "development"
export const commit = pkg.commit || "development" export const commit = pkg.commit || "development"
export const rootPath = path.resolve(__dirname, "../..") export const rootPath = path.resolve(__dirname, "../..")
export const vsRootPath = path.join(rootPath, "vendor/modules/code-oss-dev")
export const tmpdir = path.join(os.tmpdir(), "code-server") export const tmpdir = path.join(os.tmpdir(), "code-server")
export const isDevMode = commit === "development" export const isDevMode = commit === "development"

View File

@ -9,11 +9,11 @@ import {
} from "./cli" } from "./cli"
import { commit, version } from "./constants" import { commit, version } from "./constants"
import { openInExistingInstance, runCodeServer, runVsCodeCli } from "./main" import { openInExistingInstance, runCodeServer, runVsCodeCli } from "./main"
import * as proxyAgent from "./proxy_agent" // import * as proxyAgent from "./proxy_agent"
import { isChild, wrapper } from "./wrapper" import { isChild, wrapper } from "./wrapper"
async function entry(): Promise<void> { async function entry(): Promise<void> {
proxyAgent.monkeyPatch(false) // proxyAgent.monkeyPatch(false)
// There's no need to check flags like --help or to spawn in an existing // There's no need to check flags like --help or to spawn in an existing
// instance for the child process because these would have already happened in // instance for the child process because these would have already happened in
@ -46,11 +46,13 @@ async function entry(): Promise<void> {
if (args.version) { if (args.version) {
if (args.json) { if (args.json) {
console.log({ console.log(
JSON.stringify({
codeServer: version, codeServer: version,
commit, commit,
vscode: require("../../vendor/modules/code-oss-dev/package.json").version, vscode: require("../../vendor/modules/code-oss-dev/package.json").version,
}) }),
)
} else { } else {
console.log(version, commit) console.log(version, commit)
} }

View File

@ -3,11 +3,11 @@ import * as express from "express"
import * as expressCore from "express-serve-static-core" import * as expressCore from "express-serve-static-core"
import qs from "qs" import qs from "qs"
import { HttpCode, HttpError } from "../common/http" import { HttpCode, HttpError } from "../common/http"
import { normalize, Options } from "../common/util" import { normalize } from "../common/util"
import { AuthType, DefaultedArgs } from "./cli" import { AuthType, DefaultedArgs } from "./cli"
import { commit, rootPath } from "./constants" import { rootPath, version as codeServerVersion } from "./constants"
import { Heart } from "./heart" import { Heart } from "./heart"
import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString, escapeHtml } from "./util" import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString, escapeHtml, escapeJSON } from "./util"
declare global { declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
@ -19,6 +19,16 @@ declare global {
} }
} }
export const createClientConfiguration = (req: express.Request): CodeServerLib.ClientConfiguration => {
const base = relativeRoot(req)
return {
base,
csStaticBase: "/_static/" + rootPath,
codeServerVersion,
}
}
/** /**
* Replace common variable strings in HTML templates. * Replace common variable strings in HTML templates.
*/ */
@ -27,18 +37,16 @@ export const replaceTemplates = <T extends object>(
content: string, content: string,
extraOpts?: Omit<T, "base" | "csStaticBase" | "logLevel">, extraOpts?: Omit<T, "base" | "csStaticBase" | "logLevel">,
): string => { ): string => {
const base = relativeRoot(req) const serverOptions: CodeServerLib.ClientConfiguration = {
const options: Options = { ...createClientConfiguration(req),
base,
csStaticBase: base + "/static/" + commit + rootPath,
logLevel: logger.level,
...extraOpts, ...extraOpts,
} }
return content return content
.replace(/{{TO}}/g, (typeof req.query.to === "string" && escapeHtml(req.query.to)) || "/") .replace(/{{TO}}/g, (typeof req.query.to === "string" && escapeHtml(req.query.to)) || "/")
.replace(/{{BASE}}/g, options.base) .replace(/{{BASE}}/g, serverOptions.base)
.replace(/{{CS_STATIC_BASE}}/g, options.csStaticBase) .replace(/{{CS_STATIC_BASE}}/g, serverOptions.csStaticBase)
.replace(/"{{OPTIONS}}"/, `'${JSON.stringify(options)}'`) .replace("{{OPTIONS}}", () => escapeJSON(serverOptions))
} }
/** /**

22
src/node/link.ts Normal file
View File

@ -0,0 +1,22 @@
import { logger } from "@coder/logger"
import { spawn } from "child_process"
import path from "path"
export function startLink(port: number): Promise<void> {
logger.debug(`running link targetting ${port}`)
const agent = spawn(path.resolve(__dirname, "../../lib/linkup"), ["--devurl", `code:${port}:code-server`], {
shell: false,
})
return new Promise((res, rej) => {
agent.on("error", rej)
agent.on("close", (code) => {
if (code !== 0) {
return rej({
message: `Link exited with ${code}`,
})
}
res()
})
})
}

View File

@ -1,49 +1,42 @@
import { field, logger } from "@coder/logger" import { field, logger } from "@coder/logger"
import * as cp from "child_process"
import http from "http" import http from "http"
import * as path from "path" import path from "path"
import { CliMessage, OpenCommandPipeArgs } from "../../typings/ipc"
import { plural } from "../common/util" import { plural } from "../common/util"
import { createApp, ensureAddress } from "./app" import { createApp, ensureAddress } from "./app"
import { AuthType, DefaultedArgs, Feature } from "./cli" import { AuthType, DefaultedArgs, Feature } from "./cli"
import { coderCloudBind } from "./coder_cloud" import { coderCloudBind } from "./coder_cloud"
import { commit, version } from "./constants" import { commit, version } from "./constants"
import { startLink } from "./link"
import { register } from "./routes" import { register } from "./routes"
import { humanPath, isFile, open } from "./util" import { humanPath, isFile, loadAMDModule, open } from "./util"
export const runVsCodeCli = (args: DefaultedArgs): void => { /**
logger.debug("forking vs code cli...") * This is useful when an CLI arg should be passed to VS Code directly,
const vscode = cp.fork(path.resolve(__dirname, "../../vendor/modules/code-oss-dev/out/vs/server/fork"), [], { * such as when managing extensions.
env: { * @deprecated This should be removed when code-server merges with lib/vscode.
...process.env, */
CODE_SERVER_PARENT_PID: process.pid.toString(), export const runVsCodeCli = async (args: DefaultedArgs): Promise<void> => {
}, logger.debug("Running VS Code CLI")
})
vscode.once("message", (message: any) => { const cliProcessMain = await loadAMDModule<CodeServerLib.IMainCli["main"]>("vs/code/node/cliProcessMain", "main")
logger.debug("got message from VS Code", field("message", message))
if (message.type !== "ready") { try {
logger.error("Unexpected response waiting for ready response", field("type", message.type)) await cliProcessMain(args)
process.exit(1) } catch (error) {
}
const send: CliMessage = { type: "cli", args }
vscode.send(send)
})
vscode.once("error", (error) => {
logger.error("Got error from VS Code", field("error", error)) logger.error("Got error from VS Code", field("error", error))
process.exit(1) }
})
vscode.on("exit", (code) => process.exit(code || 0)) process.exit(0)
} }
export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => { export const openInExistingInstance = async (args: DefaultedArgs, socketPath: string): Promise<void> => {
const pipeArgs: OpenCommandPipeArgs & { fileURIs: string[] } = { const pipeArgs: CodeServerLib.OpenCommandPipeArgs & { fileURIs: string[] } = {
type: "open", type: "open",
folderURIs: [], folderURIs: [],
fileURIs: [], fileURIs: [],
forceReuseWindow: args["reuse-window"], forceReuseWindow: args["reuse-window"],
forceNewWindow: args["new-window"], forceNewWindow: args["new-window"],
} }
for (let i = 0; i < args._.length; i++) { for (let i = 0; i < args._.length; i++) {
const fp = path.resolve(args._[i]) const fp = path.resolve(args._[i])
if (await isFile(fp)) { if (await isFile(fp)) {
@ -52,17 +45,14 @@ export const openInExistingInstance = async (args: DefaultedArgs, socketPath: st
pipeArgs.folderURIs.push(fp) pipeArgs.folderURIs.push(fp)
} }
} }
if (pipeArgs.forceNewWindow && pipeArgs.fileURIs.length > 0) { if (pipeArgs.forceNewWindow && pipeArgs.fileURIs.length > 0) {
logger.error("--new-window can only be used with folder paths") logger.error("--new-window can only be used with folder paths")
process.exit(1) process.exit(1)
} }
if (pipeArgs.folderURIs.length === 0 && pipeArgs.fileURIs.length === 0) { if (pipeArgs.folderURIs.length === 0 && pipeArgs.fileURIs.length === 0) {
logger.error("Please specify at least one file or folder") logger.error("Please specify at least one file or folder")
process.exit(1) process.exit(1)
} }
const vscode = http.request( const vscode = http.request(
{ {
path: "/", path: "/",
@ -129,6 +119,15 @@ export const runCodeServer = async (args: DefaultedArgs): Promise<http.Server> =
logger.info(" - Connected to cloud agent") logger.info(" - Connected to cloud agent")
} }
try {
const port = parseInt(serverAddress.split(":").pop() as string, 10)
startLink(port).catch((ex) => {
logger.debug("Link daemon exited!", field("error", ex))
})
} catch (error) {
logger.debug("Failed to start link daemon!", error as any)
}
if (args.enable && args.enable.length > 0) { if (args.enable && args.enable.length > 0) {
logger.info("Enabling the following experimental features:") logger.info("Enabling the following experimental features:")
args.enable.forEach((feature) => { args.enable.forEach((feature) => {

View File

@ -172,9 +172,9 @@ export class PluginAPI {
} }
await this.loadPlugin(path.join(dir, ent.name)) await this.loadPlugin(path.join(dir, ent.name))
} }
} catch (err) { } catch (error: any) {
if (err.code !== "ENOENT") { if (error.code !== "ENOENT") {
this.logger.warn(`failed to load plugins from ${q(dir)}: ${err.message}`) this.logger.warn(`failed to load plugins from ${q(dir)}: ${error.message}`)
} }
} }
} }
@ -195,9 +195,9 @@ export class PluginAPI {
} }
const p = this._loadPlugin(dir, packageJSON) const p = this._loadPlugin(dir, packageJSON)
this.plugins.set(p.name, p) this.plugins.set(p.name, p)
} catch (err) { } catch (error: any) {
if (err.code !== "ENOENT") { if (error.code !== "ENOENT") {
this.logger.warn(`failed to load plugin: ${err.stack}`) this.logger.warn(`failed to load plugin: ${error.stack}`)
} }
} }
} }
@ -278,7 +278,7 @@ export class PluginAPI {
} }
try { try {
await p.deinit() await p.deinit()
} catch (error) { } catch (error: any) {
this.logger.error("plugin failed to deinit", field("name", p.name), field("error", error.message)) this.logger.error("plugin failed to deinit", field("name", p.name), field("error", error.message))
} }
}), }),

View File

@ -1,5 +1,4 @@
import { logger } from "@coder/logger" import { logger } from "@coder/logger"
import bodyParser from "body-parser"
import cookieParser from "cookie-parser" import cookieParser from "cookie-parser"
import * as express from "express" import * as express from "express"
import { promises as fs } from "fs" import { promises as fs } from "fs"
@ -10,7 +9,7 @@ import * as pluginapi from "../../../typings/pluginapi"
import { HttpCode, HttpError } from "../../common/http" import { HttpCode, HttpError } from "../../common/http"
import { plural } from "../../common/util" import { plural } from "../../common/util"
import { AuthType, DefaultedArgs } from "../cli" import { AuthType, DefaultedArgs } from "../cli"
import { rootPath } from "../constants" import { commit, isDevMode, rootPath } from "../constants"
import { Heart } from "../heart" import { Heart } from "../heart"
import { ensureAuthenticated, redirect, replaceTemplates } from "../http" import { ensureAuthenticated, redirect, replaceTemplates } from "../http"
import { PluginAPI } from "../plugin" import { PluginAPI } from "../plugin"
@ -22,10 +21,8 @@ import * as health from "./health"
import * as login from "./login" import * as login from "./login"
import * as logout from "./logout" import * as logout from "./logout"
import * as pathProxy from "./pathProxy" import * as pathProxy from "./pathProxy"
// static is a reserved keyword.
import * as _static from "./static"
import * as update from "./update" import * as update from "./update"
import * as vscode from "./vscode" import { createVSServerRouter, VSServerResult } from "./vscode"
/** /**
* Register all routes and middleware. * Register all routes and middleware.
@ -124,13 +121,15 @@ export const register = async (
wrapper.onDispose(() => pluginApi.dispose()) wrapper.onDispose(() => pluginApi.dispose())
} }
app.use(bodyParser.json()) app.use(express.json())
app.use(bodyParser.urlencoded({ extended: true })) app.use(express.urlencoded({ extended: true }))
app.use("/", vscode.router) app.use(
wsApp.use("/", vscode.wsRouter.router) "/_static",
app.use("/vscode", vscode.router) express.static(rootPath, {
wsApp.use("/vscode", vscode.wsRouter.router) cacheControl: commit !== "development",
}),
)
app.use("/healthz", health.router) app.use("/healthz", health.router)
wsApp.use("/healthz", health.wsRouter.router) wsApp.use("/healthz", health.wsRouter.router)
@ -143,9 +142,28 @@ export const register = async (
app.all("/logout", (req, res) => redirect(req, res, "/", {})) app.all("/logout", (req, res) => redirect(req, res, "/", {}))
} }
app.use("/static", _static.router)
app.use("/update", update.router) app.use("/update", update.router)
let vscode: VSServerResult
try {
vscode = await createVSServerRouter(args)
} catch (error: any) {
if (isDevMode) {
logger.warn(error)
logger.warn("VS Server router may still be compiling.")
}
throw error
}
app.use("/", vscode.router)
wsApp.use("/", vscode.wsRouter.router)
app.use("/vscode", vscode.router)
wsApp.use("/vscode", vscode.wsRouter.router)
server.on("close", () => {
vscode.vscodeServer.close()
})
app.use(() => { app.use(() => {
throw new HttpError("Not Found", HttpCode.NotFound) throw new HttpError("Not Found", HttpCode.NotFound)
}) })

View File

@ -111,7 +111,7 @@ router.post("/", async (req, res) => {
) )
throw new Error("Incorrect password") throw new Error("Incorrect password")
} catch (error) { } catch (error: any) {
const renderedHtml = await getRoot(req, error) const renderedHtml = await getRoot(req, error)
res.send(renderedHtml) res.send(renderedHtml)
} }

View File

@ -1,71 +0,0 @@
import { field, logger } from "@coder/logger"
import { Router } from "express"
import { promises as fs } from "fs"
import * as path from "path"
import { Readable } from "stream"
import * as tarFs from "tar-fs"
import * as zlib from "zlib"
import { HttpCode, HttpError } from "../../common/http"
import { getFirstString } from "../../common/util"
import { rootPath } from "../constants"
import { authenticated, ensureAuthenticated, replaceTemplates } from "../http"
import { getMediaMime, pathToFsPath } from "../util"
export const router = Router()
// The commit is for caching.
router.get("/(:commit)(/*)?", async (req, res) => {
// Used by VS Code to load extensions into the web worker.
const tar = getFirstString(req.query.tar)
if (tar) {
await ensureAuthenticated(req)
let stream: Readable = tarFs.pack(pathToFsPath(tar))
if (req.headers["accept-encoding"] && req.headers["accept-encoding"].includes("gzip")) {
logger.debug("gzipping tar", field("path", tar))
const compress = zlib.createGzip()
stream.pipe(compress)
stream.on("error", (error) => compress.destroy(error))
stream.on("close", () => compress.end())
stream = compress
res.header("content-encoding", "gzip")
}
res.set("Content-Type", "application/x-tar")
stream.on("close", () => res.end())
return stream.pipe(res)
}
// If not a tar use the remainder of the path to load the resource.
if (!req.params[0]) {
throw new HttpError("Not Found", HttpCode.NotFound)
}
const resourcePath = path.resolve(req.params[0])
// Make sure it's in code-server if you aren't authenticated. This lets
// unauthenticated users load the login assets.
const isAuthenticated = await authenticated(req)
if (!resourcePath.startsWith(rootPath) && !isAuthenticated) {
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
}
// Don't cache during development. - can also be used if you want to make a
// static request without caching.
if (req.params.commit !== "development" && req.params.commit !== "-") {
res.header("Cache-Control", "public, max-age=31536000")
}
// Without this the default is to use the directory the script loaded from.
if (req.headers["service-worker"]) {
res.header("service-worker-allowed", "/")
}
res.set("Content-Type", getMediaMime(resourcePath))
if (resourcePath.endsWith("manifest.json")) {
const content = await fs.readFile(resourcePath, "utf8")
return res.send(replaceTemplates(req, content))
}
const content = await fs.readFile(resourcePath)
return res.send(content)
})

View File

@ -1,232 +1,45 @@
import * as crypto from "crypto" import * as express from "express"
import { Request, Router } from "express" import { Server } from "http"
import { promises as fs } from "fs" import { AuthType, DefaultedArgs } from "../cli"
import * as path from "path" import { version as codeServerVersion } from "../constants"
import qs from "qs" import { ensureAuthenticated } from "../http"
import * as ipc from "../../../typings/ipc" import { loadAMDModule } from "../util"
import { Emitter } from "../../common/emitter" import { Router as WsRouter, WebsocketRouter } from "../wsRouter"
import { HttpCode, HttpError } from "../../common/http"
import { getFirstString } from "../../common/util"
import { Feature } from "../cli"
import { isDevMode, rootPath, version } from "../constants"
import { authenticated, ensureAuthenticated, redirect, replaceTemplates } from "../http"
import { getMediaMime, pathToFsPath } from "../util"
import { VscodeProvider } from "../vscode"
import { Router as WsRouter } from "../wsRouter"
export const router = Router() export interface VSServerResult {
router: express.Router
const vscode = new VscodeProvider() wsRouter: WebsocketRouter
vscodeServer: Server
router.get("/", async (req, res) => {
const isAuthenticated = await authenticated(req)
if (!isAuthenticated) {
return redirect(req, res, "login", {
// req.baseUrl can be blank if already at the root.
to: req.baseUrl && req.baseUrl !== "/" ? req.baseUrl : undefined,
})
} }
const [content, options] = await Promise.all([ export const createVSServerRouter = async (args: DefaultedArgs): Promise<VSServerResult> => {
await fs.readFile(path.join(rootPath, "src/browser/pages/vscode.html"), "utf8"), const vscodeServerMain = await loadAMDModule<CodeServerLib.CreateVSServer>("vs/server/entry", "createVSServer")
(async () => {
try {
return await vscode.initialize({ args: req.args, remoteAuthority: req.headers.host || "" }, req.query)
} catch (error) {
const devMessage = isDevMode ? "It might not have finished compiling." : ""
throw new Error(`VS Code failed to load. ${devMessage} ${error.message}`)
}
})(),
])
options.productConfiguration.codeServerVersion = version const serverUrl = new URL(`${args.cert ? "https" : "http"}://${args.host}:${args.port}`)
const vscodeServer = await vscodeServerMain({
res.send( codeServerVersion,
replaceTemplates<ipc.Options>( serverUrl,
req, args,
// Uncomment prod blocks if not in development. TODO: Would this be authed: args.auth !== AuthType.None,
// better as a build step? Or maintain two HTML files again? disableUpdateCheck: !!args["disable-update-check"],
!isDevMode ? content.replace(/<!-- PROD_ONLY/g, "").replace(/END_PROD_ONLY -->/g, "") : content,
{
authed: req.args.auth !== "none",
disableUpdateCheck: !!req.args["disable-update-check"],
},
)
.replace(`"{{REMOTE_USER_DATA_URI}}"`, `'${JSON.stringify(options.remoteUserDataUri)}'`)
.replace(`"{{PRODUCT_CONFIGURATION}}"`, `'${JSON.stringify(options.productConfiguration)}'`)
.replace(`"{{WORKBENCH_WEB_CONFIGURATION}}"`, `'${JSON.stringify(options.workbenchWebConfiguration)}'`)
.replace(`"{{NLS_CONFIGURATION}}"`, `'${JSON.stringify(options.nlsConfiguration)}'`),
)
}) })
/** const router = express.Router()
* TODO: Might currently be unused. const wsRouter = WsRouter()
*/
router.get("/resource(/*)?", ensureAuthenticated, async (req, res) => { router.all("*", ensureAuthenticated, (req, res) => {
const path = getFirstString(req.query.path) vscodeServer.emit("request", req, res)
if (path) {
res.set("Content-Type", getMediaMime(path))
res.send(await fs.readFile(pathToFsPath(path)))
}
}) })
/** wsRouter.ws("/", ensureAuthenticated, (req) => {
* Used by VS Code to load files. vscodeServer.emit("upgrade", req, req.socket, req.head)
*/
router.get("/vscode-remote-resource(/*)?", ensureAuthenticated, async (req, res) => { req.socket.resume()
const path = getFirstString(req.query.path)
if (path) {
res.set("Content-Type", getMediaMime(path))
res.send(await fs.readFile(pathToFsPath(path)))
}
}) })
/** return {
* VS Code webviews use these paths to load files and to load webview assets router,
* like HTML and JavaScript. wsRouter,
*/ vscodeServer,
router.get("/webview/*", ensureAuthenticated, async (req, res) => {
res.set("Content-Type", getMediaMime(req.path))
if (/^vscode-resource/.test(req.params[0])) {
return res.send(await fs.readFile(req.params[0].replace(/^vscode-resource(\/file)?/, "")))
}
return res.send(
await fs.readFile(path.join(vscode.vsRootPath, "out/vs/workbench/contrib/webview/browser/pre", req.params[0])),
)
})
interface Callback {
uri: {
scheme: string
authority?: string
path?: string
query?: string
fragment?: string
}
timeout: NodeJS.Timeout
}
const callbacks = new Map<string, Callback>()
const callbackEmitter = new Emitter<{ id: string; callback: Callback }>()
/**
* Get vscode-requestId from the query and throw if it's missing or invalid.
*/
const getRequestId = (req: Request): string => {
if (!req.query["vscode-requestId"]) {
throw new HttpError("vscode-requestId is missing", HttpCode.BadRequest)
}
if (typeof req.query["vscode-requestId"] !== "string") {
throw new HttpError("vscode-requestId is not a string", HttpCode.BadRequest)
}
return req.query["vscode-requestId"]
}
// Matches VS Code's fetch timeout.
const fetchTimeout = 5 * 60 * 1000
// The callback endpoints are used during authentication. A URI is stored on
// /callback and then fetched later on /fetch-callback.
// See ../../../lib/vscode/resources/web/code-web.js
router.get("/callback", ensureAuthenticated, async (req, res) => {
const uriKeys = [
"vscode-requestId",
"vscode-scheme",
"vscode-authority",
"vscode-path",
"vscode-query",
"vscode-fragment",
]
const id = getRequestId(req)
// Move any query variables that aren't URI keys into the URI's query
// (importantly, this will include the code for oauth).
const query: qs.ParsedQs = {}
for (const key in req.query) {
if (!uriKeys.includes(key)) {
query[key] = req.query[key]
} }
} }
const callback = {
uri: {
scheme: getFirstString(req.query["vscode-scheme"]) || "code-oss",
authority: getFirstString(req.query["vscode-authority"]),
path: getFirstString(req.query["vscode-path"]),
query: (getFirstString(req.query.query) || "") + "&" + qs.stringify(query),
fragment: getFirstString(req.query["vscode-fragment"]),
},
// Make sure the map doesn't leak if nothing fetches this URI.
timeout: setTimeout(() => callbacks.delete(id), fetchTimeout),
}
callbacks.set(id, callback)
callbackEmitter.emit({ id, callback })
res.sendFile(path.join(rootPath, "vendor/modules/code-oss-dev/resources/web/callback.html"))
})
router.get("/fetch-callback", ensureAuthenticated, async (req, res) => {
const id = getRequestId(req)
const send = (callback: Callback) => {
clearTimeout(callback.timeout)
callbacks.delete(id)
res.json(callback.uri)
}
const callback = callbacks.get(id)
if (callback) {
return send(callback)
}
// VS Code will try again if the route returns no content but it seems more
// efficient to just wait on this request for as long as possible?
const handler = callbackEmitter.event(({ id: emitId, callback }) => {
if (id === emitId) {
handler.dispose()
send(callback)
}
})
// If the client closes the connection.
req.on("close", () => handler.dispose())
})
export const wsRouter = WsRouter()
wsRouter.ws("/", ensureAuthenticated, async (req) => {
const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
const reply = crypto
.createHash("sha1")
.update(req.headers["sec-websocket-key"] + magic)
.digest("base64")
const responseHeaders = [
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
`Sec-WebSocket-Accept: ${reply}`,
]
// See if the browser reports it supports web socket compression.
// TODO: Parse this header properly.
const extensions = req.headers["sec-websocket-extensions"]
const isCompressionSupported = extensions ? extensions.includes("permessage-deflate") : false
// TODO: For now we only use compression if the user enables it.
const isCompressionEnabled = !!req.args.enable?.includes(Feature.PermessageDeflate)
const useCompression = isCompressionEnabled && isCompressionSupported
if (useCompression) {
// This response header tells the browser the server supports compression.
responseHeaders.push("Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15")
}
req.ws.write(responseHeaders.join("\r\n") + "\r\n\r\n")
await vscode.sendWebsocket(req.ws, req.query, useCompression)
})

View File

@ -20,7 +20,7 @@ export class SettingsProvider<T> {
try { try {
const raw = (await fs.readFile(this.settingsPath, "utf8")).trim() const raw = (await fs.readFile(this.settingsPath, "utf8")).trim()
return raw ? JSON.parse(raw) : {} return raw ? JSON.parse(raw) : {}
} catch (error) { } catch (error: any) {
if (error.code !== "ENOENT") { if (error.code !== "ENOENT") {
logger.warn(error.message) logger.warn(error.message)
} }
@ -37,7 +37,7 @@ export class SettingsProvider<T> {
const oldSettings = await this.read() const oldSettings = await this.read()
const nextSettings = { ...oldSettings, ...settings } const nextSettings = { ...oldSettings, ...settings }
await fs.writeFile(this.settingsPath, JSON.stringify(nextSettings, null, 2)) await fs.writeFile(this.settingsPath, JSON.stringify(nextSettings, null, 2))
} catch (error) { } catch (error: any) {
logger.warn(error.message) logger.warn(error.message)
} }
} }

View File

@ -60,7 +60,7 @@ export class UpdateProvider {
} }
logger.debug("got latest version", field("latest", update.version)) logger.debug("got latest version", field("latest", update.version))
return update return update
} catch (error) { } catch (error: any) {
logger.error("Failed to get latest version", field("error", error.message)) logger.error("Failed to get latest version", field("error", error.message))
return { return {
checked: now, checked: now,

View File

@ -1,66 +0,0 @@
// In a bit of a hack, this file is stored in two places
// - src/node/uri_transformer.ts
// - lib/vscode/src/vs/server/uriTransformer.ts
// The reason for this is that we need a CommonJS-compiled
// version of this file to supply as a command line argument
// to extensionHostProcessSetup.ts; but we also need to include
// it ourselves cleanly in `lib/vscode/src/vs/server`.
// @oxy: Could not figure out how to compile as a CommonJS module
// in the same tree as VSCode, which is why I came up with the solution
// of storing it in two places.
// NOTE: copied over from lib/vscode/src/vs/common/uriIpc.ts
// remember to update this for proper type checks!
interface UriParts {
scheme: string
authority?: string
path?: string
}
interface IRawURITransformer {
transformIncoming(uri: UriParts): UriParts
transformOutgoing(uri: UriParts): UriParts
transformOutgoingScheme(scheme: string): string
}
// Using `export =` is deliberate.
// See lib/vscode/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts;
// they include the file directly with a node require and expect a function as `module.exports`.
// `export =` in TypeScript is equivalent to `module.exports =` in vanilla JS.
export = function rawURITransformerFactory(authority: string) {
return new RawURITransformer(authority)
}
class RawURITransformer implements IRawURITransformer {
constructor(private readonly authority: string) {}
transformIncoming(uri: UriParts): UriParts {
switch (uri.scheme) {
case "vscode-remote":
return { scheme: "file", path: uri.path }
default:
return uri
}
}
transformOutgoing(uri: UriParts): UriParts {
switch (uri.scheme) {
case "file":
return { scheme: "vscode-remote", authority: this.authority, path: uri.path }
default:
return uri
}
}
transformOutgoingScheme(scheme: string): string {
switch (scheme) {
case "file":
return "vscode-remote"
default:
return scheme
}
}
}

View File

@ -10,7 +10,7 @@ import * as path from "path"
import safeCompare from "safe-compare" import safeCompare from "safe-compare"
import * as util from "util" import * as util from "util"
import xdgBasedir from "xdg-basedir" import xdgBasedir from "xdg-basedir"
import { getFirstString } from "../common/util" import { vsRootPath } from "./constants"
export interface Paths { export interface Paths {
data: string data: string
@ -157,7 +157,7 @@ export const generatePassword = async (length = 24): Promise<string> => {
export const hash = async (password: string): Promise<string> => { export const hash = async (password: string): Promise<string> => {
try { try {
return await argon2.hash(password) return await argon2.hash(password)
} catch (error) { } catch (error: any) {
logger.error(error) logger.error(error)
return "" return ""
} }
@ -172,7 +172,7 @@ export const isHashMatch = async (password: string, hash: string) => {
} }
try { try {
return await argon2.verify(hash, password) return await argon2.verify(hash, password)
} catch (error) { } catch (error: any) {
throw new Error(error) throw new Error(error)
} }
} }
@ -439,55 +439,6 @@ export const isObject = <T extends object>(obj: T): obj is T => {
return !Array.isArray(obj) && typeof obj === "object" && obj !== null return !Array.isArray(obj) && typeof obj === "object" && obj !== null
} }
/**
* Taken from vs/base/common/charCode.ts. Copied for now instead of importing so
* we don't have to set up a `vs` alias to be able to import with types (since
* the alternative is to directly import from `out`).
*/
enum CharCode {
Slash = 47,
A = 65,
Z = 90,
a = 97,
z = 122,
Colon = 58,
}
/**
* Compute `fsPath` for the given uri.
* Taken from vs/base/common/uri.ts. It's not imported to avoid also importing
* everything that file imports.
*/
export function pathToFsPath(path: string, keepDriveLetterCasing = false): string {
const isWindows = process.platform === "win32"
const uri = { authority: undefined, path: getFirstString(path) || "", scheme: "file" }
let value: string
if (uri.authority && uri.path.length > 1 && uri.scheme === "file") {
// unc path: file://shares/c$/far/boo
value = `//${uri.authority}${uri.path}`
} else if (
uri.path.charCodeAt(0) === CharCode.Slash &&
((uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z) ||
(uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z)) &&
uri.path.charCodeAt(2) === CharCode.Colon
) {
if (!keepDriveLetterCasing) {
// windows drive letter: file:///c:/far/boo
value = uri.path[1].toLowerCase() + uri.path.substr(2)
} else {
value = uri.path.substr(1)
}
} else {
// other path
value = uri.path
}
if (isWindows) {
value = value.replace(/\//g, "\\")
}
return value
}
/** /**
* Return a promise that resolves with whether the socket path is active. * Return a promise that resolves with whether the socket path is active.
*/ */
@ -533,3 +484,30 @@ export function escapeHtml(unsafe: string): string {
export function isNodeJSErrnoException(error: unknown): error is NodeJS.ErrnoException { export function isNodeJSErrnoException(error: unknown): error is NodeJS.ErrnoException {
return error instanceof Error && (error as NodeJS.ErrnoException).code !== undefined return error instanceof Error && (error as NodeJS.ErrnoException).code !== undefined
} }
// TODO: Replace with proper templating system.
export const escapeJSON = (value: cp.Serializable) => JSON.stringify(value).replace(/"/g, "&quot;")
type AMDModule<T> = { [exportName: string]: T }
const AMDModules = new Map<string, AMDModule<unknown>>()
/**
* Loads AMD module, typically from a compiled VSCode bundle.
*
* @deprecated This should be gradually phased out as code-server migrates to lib/vscode
* @param amdPath Path to module relative to lib/vscode
* @param exportName Given name of export in the file
*/
export const loadAMDModule = async <T>(amdPath: string, exportName: string): Promise<T> => {
let module = AMDModules.get(amdPath)
if (!module) {
module = await new Promise<AMDModule<T>>((resolve, reject) => {
require(path.join(vsRootPath, "out/bootstrap-amd")).load(amdPath, resolve, reject)
})
AMDModules.set(amdPath, module)
}
return module[exportName] as T
}

View File

@ -1,168 +0,0 @@
import { logger } from "@coder/logger"
import * as cp from "child_process"
import * as net from "net"
import * as path from "path"
import * as ipc from "../../typings/ipc"
import { arrayify, generateUuid } from "../common/util"
import { rootPath } from "./constants"
import { settings } from "./settings"
import { SocketProxyProvider } from "./socket"
import { isFile } from "./util"
import { onMessage, wrapper } from "./wrapper"
export class VscodeProvider {
public readonly serverRootPath: string
public readonly vsRootPath: string
private _vscode?: Promise<cp.ChildProcess>
private readonly socketProvider = new SocketProxyProvider()
public constructor() {
this.vsRootPath = path.resolve(rootPath, "vendor/modules/code-oss-dev")
this.serverRootPath = path.join(this.vsRootPath, "out/vs/server")
wrapper.onDispose(() => this.dispose())
}
public async dispose(): Promise<void> {
this.socketProvider.stop()
if (this._vscode) {
const vscode = await this._vscode
vscode.removeAllListeners()
vscode.kill()
this._vscode = undefined
}
}
public async initialize(
options: Omit<ipc.VscodeOptions, "startPath">,
query: ipc.Query,
): Promise<ipc.WorkbenchOptions> {
const { lastVisited } = await settings.read()
let startPath = await this.getFirstPath([
{ url: query.workspace, workspace: true },
{ url: query.folder, workspace: false },
options.args._ && options.args._.length > 0
? { url: path.resolve(options.args._[options.args._.length - 1]) }
: undefined,
!options.args["ignore-last-opened"] ? lastVisited : undefined,
])
if (query.ew) {
startPath = undefined
}
settings.write({
lastVisited: startPath,
query,
})
const id = generateUuid()
const vscode = await this.fork()
logger.debug("setting up vs code...")
this.send(
{
type: "init",
id,
options: {
...options,
startPath,
},
},
vscode,
)
const message = await onMessage<ipc.VscodeMessage, ipc.OptionsMessage>(
vscode,
(message): message is ipc.OptionsMessage => {
// There can be parallel initializations so wait for the right ID.
return message.type === "options" && message.id === id
},
)
return message.options
}
private fork(): Promise<cp.ChildProcess> {
if (this._vscode) {
return this._vscode
}
logger.debug("forking vs code...")
const vscode = cp.fork(path.join(this.serverRootPath, "fork"))
const dispose = () => {
vscode.removeAllListeners()
vscode.kill()
this._vscode = undefined
}
vscode.on("error", (error: Error) => {
logger.error(error.message)
if (error.stack) {
logger.debug(error.stack)
}
dispose()
})
vscode.on("exit", (code) => {
logger.error(`VS Code exited unexpectedly with code ${code}`)
dispose()
})
this._vscode = onMessage<ipc.VscodeMessage, ipc.ReadyMessage>(vscode, (message): message is ipc.ReadyMessage => {
return message.type === "ready"
}).then(() => vscode)
return this._vscode
}
/**
* VS Code expects a raw socket. It will handle all the web socket frames.
*/
public async sendWebsocket(socket: net.Socket, query: ipc.Query, permessageDeflate: boolean): Promise<void> {
const vscode = await this._vscode
// TLS sockets cannot be transferred to child processes so we need an
// in-between. Non-TLS sockets will be returned as-is.
const socketProxy = await this.socketProvider.createProxy(socket)
this.send({ type: "socket", query, permessageDeflate }, vscode, socketProxy)
}
private send(message: ipc.CodeServerMessage, vscode?: cp.ChildProcess, socket?: net.Socket): void {
if (!vscode || vscode.killed) {
throw new Error("vscode is not running")
}
vscode.send(message, socket)
}
/**
* Choose the first non-empty path from the provided array.
*
* Each array item consists of `url` and an optional `workspace` boolean that
* indicates whether that url is for a workspace.
*
* `url` can be a fully qualified URL or just the path portion.
*
* `url` can also be a query object to make it easier to pass in query
* variables directly but anything that isn't a string or string array is not
* valid and will be ignored.
*/
private async getFirstPath(
startPaths: Array<{ url?: string | string[] | ipc.Query | ipc.Query[]; workspace?: boolean } | undefined>,
): Promise<ipc.StartPath | undefined> {
for (let i = 0; i < startPaths.length; ++i) {
const startPath = startPaths[i]
const url = arrayify(startPath && startPath.url).find((p) => !!p)
if (startPath && url && typeof url === "string") {
return {
url,
// The only time `workspace` is undefined is for the command-line
// argument, in which case it's a path (not a URL) so we can stat it
// without having to parse it.
workspace: typeof startPath.workspace !== "undefined" ? startPath.workspace : await isFile(url),
}
}
}
return undefined
}
}

View File

@ -267,7 +267,7 @@ export class ParentProcess extends Process {
try { try {
this.started = this._start() this.started = this._start()
await this.started await this.started
} catch (error) { } catch (error: any) {
this.logger.error(error.message) this.logger.error(error.message)
this.exit(typeof error.code === "number" ? error.code : 1) this.exit(typeof error.code === "number" ? error.code : 1)
} }

View File

@ -4,7 +4,7 @@
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.12.1", "@playwright/test": "^1.12.1",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/jsdom": "^16.2.6", "@types/jsdom": "^16.2.13",
"@types/node-fetch": "^2.5.8", "@types/node-fetch": "^2.5.8",
"@types/supertest": "^2.0.10", "@types/supertest": "^2.0.10",
"@types/wtfnode": "^0.7.0", "@types/wtfnode": "^0.7.0",

View File

@ -24,10 +24,8 @@ describe("login", () => {
const spy = jest.spyOn(document, "getElementById") const spy = jest.spyOn(document, "getElementById")
// Create a fake element and set the attribute // Create a fake element and set the attribute
const mockElement = document.createElement("input") const mockElement = document.createElement("input")
mockElement.setAttribute("id", "base")
const expected = { const expected = {
base: "./hello-world", base: "./hello-world",
csStaticBase: "./static/development/Users/jp/Dev/code-server",
logLevel: 2, logLevel: 2,
disableTelemetry: false, disableTelemetry: false,
disableUpdateCheck: false, disableUpdateCheck: false,
@ -37,9 +35,6 @@ describe("login", () => {
spy.mockImplementation(() => mockElement) spy.mockImplementation(() => mockElement)
// Load file // Load file
require("../../../../src/browser/pages/login") require("../../../../src/browser/pages/login")
const el: HTMLInputElement | null = document.querySelector("input#base")
expect(el?.value).toBe("/hello-world")
}) })
}) })
describe("there is not an element with id 'base'", () => { describe("there is not an element with id 'base'", () => {
@ -76,15 +71,5 @@ describe("login", () => {
afterAll(() => { afterAll(() => {
jest.restoreAllMocks() jest.restoreAllMocks()
}) })
it("should do nothing", () => {
spy.mockImplementation(() => null)
// Load file
require("../../../../src/browser/pages/login")
// It's called once by getOptions in the top of the file
// and then another to get the base element
expect(spy).toHaveBeenCalledTimes(2)
})
}) })
}) })

View File

@ -1,400 +0,0 @@
/**
* @jest-environment jsdom
*/
import fetchMock from "jest-fetch-mock"
import { JSDOM } from "jsdom"
import {
getNlsConfiguration,
nlsConfigElementId,
getConfigurationForLoader,
setBodyBackgroundToThemeBackgroundColor,
_createScriptURL,
main,
createBundlePath,
} from "../../../../src/browser/pages/vscode"
describe("vscode", () => {
describe("getNlsConfiguration", () => {
let _document: Document
beforeEach(() => {
// We use underscores to not confuse with global values
const { window: _window } = new JSDOM()
_document = _window.document
fetchMock.enableMocks()
})
afterEach(() => {
fetchMock.resetMocks()
})
it("should throw an error if no nlsConfigElement", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not parse NLS configuration. Could not find nlsConfigElement with id: ${nlsConfigElementId}`
expect(() => {
getNlsConfiguration(_document, "")
}).toThrowError(errorMessage)
})
it("should throw an error if no nlsConfig", () => {
const mockElement = _document.createElement("div")
mockElement.setAttribute("id", nlsConfigElementId)
_document.body.appendChild(mockElement)
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not parse NLS configuration. Found nlsConfigElement but missing data-settings attribute.`
expect(() => {
getNlsConfiguration(_document, "")
}).toThrowError(errorMessage)
_document.body.removeChild(mockElement)
})
it("should return the correct configuration", () => {
const mockElement = _document.createElement("div")
const dataSettings = {
first: "Jane",
last: "Doe",
}
mockElement.setAttribute("id", nlsConfigElementId)
mockElement.setAttribute("data-settings", JSON.stringify(dataSettings))
_document.body.appendChild(mockElement)
const actual = getNlsConfiguration(_document, "")
expect(actual).toStrictEqual(dataSettings)
_document.body.removeChild(mockElement)
})
it("should return and have a loadBundle property if _resolvedLangaugePackCoreLocation", async () => {
const mockElement = _document.createElement("div")
const dataSettings = {
locale: "en",
availableLanguages: ["en", "de"],
_resolvedLanguagePackCoreLocation: "./",
}
mockElement.setAttribute("id", nlsConfigElementId)
mockElement.setAttribute("data-settings", JSON.stringify(dataSettings))
_document.body.appendChild(mockElement)
const nlsConfig = getNlsConfiguration(_document, "")
expect(nlsConfig._resolvedLanguagePackCoreLocation).not.toBe(undefined)
expect(nlsConfig.loadBundle).not.toBe(undefined)
const mockCallbackFn = jest.fn((_, bundle) => {
return bundle
})
fetchMock.mockOnce(JSON.stringify({ key: "hello world" }))
// Ensure that load bundle works as expected
// by mocking the fetch response and checking that the callback
// had the expected value
await nlsConfig.loadBundle("hello", "en", mockCallbackFn)
expect(mockCallbackFn).toHaveBeenCalledTimes(1)
expect(mockCallbackFn).toHaveBeenCalledWith(undefined, { key: "hello world" })
// Call it again to ensure it loads from the cache
// it should return the same value
await nlsConfig.loadBundle("hello", "en", mockCallbackFn)
expect(mockCallbackFn).toHaveBeenCalledTimes(2)
expect(mockCallbackFn).toHaveBeenCalledWith(undefined, { key: "hello world" })
fetchMock.mockReject(new Error("fake error message"))
const mockCallbackFn2 = jest.fn((error) => error)
// Call it for a different bundle and mock a failed fetch call
// to ensure we get the expected error
const error = await nlsConfig.loadBundle("goodbye", "es", mockCallbackFn2)
expect(error.message).toEqual("fake error message")
// Clean up
_document.body.removeChild(mockElement)
})
})
describe("createBundlePath", () => {
it("should return the correct path", () => {
const _resolvedLangaugePackCoreLocation = "./languages"
const bundle = "/bundle.js"
const expected = "./languages/!bundle.js.nls.json"
const actual = createBundlePath(_resolvedLangaugePackCoreLocation, bundle)
expect(actual).toBe(expected)
})
it("should return the correct path (even if _resolvedLangaugePackCoreLocation is undefined)", () => {
const _resolvedLangaugePackCoreLocation = undefined
const bundle = "/bundle.js"
const expected = "/!bundle.js.nls.json"
const actual = createBundlePath(_resolvedLangaugePackCoreLocation, bundle)
expect(actual).toBe(expected)
})
})
describe("setBodyBackgroundToThemeBackgroundColor", () => {
let _document: Document
let _localStorage: Storage
beforeEach(() => {
// We need to set the url in the JSDOM constructor
// to prevent this error "SecurityError: localStorage is not available for opaque origins"
// See: https://github.com/jsdom/jsdom/issues/2304#issuecomment-622314949
const { window: _window } = new JSDOM("", { url: "http://localhost" })
_document = _window.document
_localStorage = _window.localStorage
})
it("should return null", () => {
const test = {
colorMap: {
[`editor.background`]: "#ff3270",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
expect(setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)).toBeNull()
_localStorage.removeItem("colorThemeData")
})
it("should throw an error if it can't find colorThemeData in localStorage", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. Could not find colorThemeData in localStorage.`
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
})
it("should throw an error if there is an error parsing colorThemeData from localStorage", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. Could not parse colorThemeData from localStorage.`
_localStorage.setItem(
"colorThemeData",
'{"id":"vs-dark max-SS-Cyberpunk-themes-cyberpunk-umbra-color-theme-json","label":"Activate UMBRA protocol","settingsId":"Activate "errorForeground":"#ff3270","foreground":"#ffffff","sideBarTitle.foreground":"#bbbbbb"},"watch\\":::false}',
)
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
localStorage.removeItem("colorThemeData")
})
it("should throw an error if there is no colorMap property", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. colorThemeData is missing colorMap.`
const test = {
id: "hey-joe",
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
_localStorage.removeItem("colorThemeData")
})
it("should throw an error if there is no editor.background color", () => {
const errorMsgPrefix = "[vscode]"
const errorMessage = `${errorMsgPrefix} Could not set body background to theme background color. colorThemeData.colorMap["editor.background"] is undefined.`
const test = {
id: "hey-joe",
colorMap: {
editor: "#fff",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
expect(() => {
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
}).toThrowError(errorMessage)
_localStorage.removeItem("colorThemeData")
})
it("should set the body background to the editor background color", () => {
const test = {
colorMap: {
[`editor.background`]: "#ff3270",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
setBodyBackgroundToThemeBackgroundColor(_document, _localStorage)
// When the body.style.backgroundColor is set using hex
// it is converted to rgb
// which is why we use that in the assertion
expect(_document.body.style.backgroundColor).toBe("rgb(255, 50, 112)")
_localStorage.removeItem("colorThemeData")
})
})
describe("getConfigurationForLoader", () => {
let _window: Window
beforeEach(() => {
const { window: __window } = new JSDOM()
// @ts-expect-error the Window from JSDOM is not exactly the same as Window
// so we expect an error here
_window = __window
})
it("should return a loader object (with undefined trustedTypesPolicy)", () => {
const options = {
base: ".",
csStaticBase: "/",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const loader = getConfigurationForLoader({
options,
_window,
nlsConfig: nlsConfig,
})
expect(loader).toStrictEqual({
baseUrl: "http://localhost//vendor/modules/code-oss-dev/out",
paths: {
"iconv-lite-umd": "../node_modules/iconv-lite-umd/lib/iconv-lite-umd.js",
jschardet: "../node_modules/jschardet/dist/jschardet.min.js",
"tas-client-umd": "../node_modules/tas-client-umd/lib/tas-client-umd.js",
"vscode-oniguruma": "../node_modules/vscode-oniguruma/release/main",
"vscode-textmate": "../node_modules/vscode-textmate/release/main",
xterm: "../node_modules/xterm/lib/xterm.js",
"xterm-addon-search": "../node_modules/xterm-addon-search/lib/xterm-addon-search.js",
"xterm-addon-unicode11": "../node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js",
"xterm-addon-webgl": "../node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js",
},
recordStats: true,
trustedTypesPolicy: undefined,
"vs/nls": {
availableLanguages: {},
first: "Jane",
last: "Doe",
locale: "en",
},
})
})
it("should return a loader object with trustedTypesPolicy", () => {
interface PolicyOptions {
createScriptUrl: (url: string) => string
}
function mockCreatePolicy(policyName: string, options: PolicyOptions) {
return {
name: policyName,
...options,
}
}
const mockFn = jest.fn(mockCreatePolicy)
// @ts-expect-error we are adding a custom property to window
_window.trustedTypes = {
createPolicy: mockFn,
}
const options = {
base: "/",
csStaticBase: "/",
logLevel: 1,
}
const nlsConfig = {
first: "Jane",
last: "Doe",
locale: "en",
availableLanguages: {},
}
const loader = getConfigurationForLoader({
options,
_window,
nlsConfig: nlsConfig,
})
expect(loader.trustedTypesPolicy).not.toBe(undefined)
expect(loader.trustedTypesPolicy.name).toBe("amdLoader")
// Check that we can actually create a script URL
// using the createScriptURL on the loader object
const scriptUrl = loader.trustedTypesPolicy.createScriptURL("http://localhost/foo.js")
expect(scriptUrl).toBe("http://localhost/foo.js")
})
})
describe("_createScriptURL", () => {
it("should return the correct url", () => {
const url = _createScriptURL("localhost/foo/bar.js", "localhost")
expect(url).toBe("localhost/foo/bar.js")
})
it("should throw if the value doesn't start with the origin", () => {
expect(() => {
_createScriptURL("localhost/foo/bar.js", "coder.com")
}).toThrow("Invalid script url: localhost/foo/bar.js")
})
})
describe("main", () => {
let _window: Window
let _document: Document
let _localStorage: Storage
beforeEach(() => {
// We need to set the url in the JSDOM constructor
// to prevent this error "SecurityError: localStorage is not available for opaque origins"
// See: https://github.com/jsdom/jsdom/issues/2304#issuecomment-62231494
const { window: __window } = new JSDOM("", { url: "http://localhost" })
// @ts-expect-error the Window from JSDOM is not exactly the same as Window
// so we expect an error here
_window = __window
_document = __window.document
_localStorage = __window.localStorage
const mockElement = _document.createElement("div")
const dataSettings = {
first: "Jane",
last: "Doe",
}
mockElement.setAttribute("id", nlsConfigElementId)
mockElement.setAttribute("data-settings", JSON.stringify(dataSettings))
_document.body.appendChild(mockElement)
const test = {
colorMap: {
[`editor.background`]: "#ff3270",
},
}
_localStorage.setItem("colorThemeData", JSON.stringify(test))
})
afterEach(() => {
_localStorage.removeItem("colorThemeData")
})
it("should throw if document is missing", () => {
expect(() => {
main(undefined, _window, _localStorage)
}).toThrow("document is undefined.")
})
it("should throw if window is missing", () => {
expect(() => {
main(_document, undefined, _localStorage)
}).toThrow("window is undefined.")
})
it("should throw if localStorage is missing", () => {
expect(() => {
main(_document, _window, undefined)
}).toThrow("localStorage is undefined.")
})
it("should add loader to self.require", () => {
main(_document, _window, _localStorage)
expect(Object.prototype.hasOwnProperty.call(self, "require")).toBe(true)
})
it("should not throw in browser context", () => {
// Assuming we call it in a normal browser context
// where everything is defined
expect(() => {
main(_document, _window, _localStorage)
}).not.toThrow()
})
})
})

View File

@ -131,7 +131,7 @@ describe("util", () => {
}) })
it("should return options with base and cssStaticBase even if it doesn't exist", () => { it("should return options with base and cssStaticBase even if it doesn't exist", () => {
expect(util.getOptions()).toStrictEqual({ expect(util.getClientConfiguration()).toStrictEqual({
base: "", base: "",
csStaticBase: "", csStaticBase: "",
}) })
@ -151,7 +151,7 @@ describe("util", () => {
// it returns the element // it returns the element
spy.mockImplementation(() => mockElement) spy.mockImplementation(() => mockElement)
expect(util.getOptions()).toStrictEqual({ expect(util.getClientConfiguration()).toStrictEqual({
base: "", base: "",
csStaticBase: "/static/development/Users/jp/Dev/code-server", csStaticBase: "/static/development/Users/jp/Dev/code-server",
disableUpdateCheck: false, disableUpdateCheck: false,
@ -167,7 +167,7 @@ describe("util", () => {
// spreads the original options // spreads the original options
// then parses the queryOpts // then parses the queryOpts
location.search = '?options={"logLevel":2}' location.search = '?options={"logLevel":2}'
expect(util.getOptions()).toStrictEqual({ expect(util.getClientConfiguration()).toStrictEqual({
base: "", base: "",
csStaticBase: "", csStaticBase: "",
logLevel: 2, logLevel: 2,
@ -194,20 +194,6 @@ describe("util", () => {
}) })
}) })
describe("getFirstString", () => {
it("should return the string if passed a string", () => {
expect(util.getFirstString("Hello world!")).toBe("Hello world!")
})
it("should get the first string from an array", () => {
expect(util.getFirstString(["Hello", "World"])).toBe("Hello")
})
it("should return undefined if the value isn't an array or a string", () => {
expect(util.getFirstString({ name: "Coder" })).toBe(undefined)
})
})
describe("logError", () => { describe("logError", () => {
afterEach(() => { afterEach(() => {
jest.clearAllMocks() jest.clearAllMocks()

View File

@ -122,26 +122,6 @@ describe("createApp", () => {
expect(unlinkSpy).toHaveBeenCalledTimes(1) expect(unlinkSpy).toHaveBeenCalledTimes(1)
server.close() server.close()
}) })
it("should catch errors thrown when unlinking a socket", async () => {
const tmpDir2 = await tmpdir("unlink-socket-error")
const tmpFile = path.join(tmpDir2, "unlink-socket-file")
// await promises.writeFile(tmpFile, "")
const socketPath = tmpFile
const defaultArgs = await setDefaults({
_: [],
socket: socketPath,
})
const app = await createApp(defaultArgs)
const server = app[2]
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(`ENOENT: no such file or directory, unlink '${socketPath}'`)
server.close()
// Ensure directory was removed
rmdirSync(tmpDir2, { recursive: true })
})
it("should create an https server if args.cert exists", async () => { it("should create an https server if args.cert exists", async () => {
const testCertificate = await generateCertificate("localhost") const testCertificate = await generateCertificate("localhost")

View File

@ -1,10 +1,20 @@
import { promises as fs } from "fs" import { promises as fs } from "fs"
import * as path from "path" import * as path from "path"
import { rootPath } from "../../../../src/node/constants"
import { tmpdir } from "../../../utils/helpers" import { tmpdir } from "../../../utils/helpers"
import * as httpserver from "../../../utils/httpserver" import * as httpserver from "../../../utils/httpserver"
import * as integration from "../../../utils/integration" import * as integration from "../../../utils/integration"
describe("/static", () => { const NOT_FOUND = {
code: 404,
message: "Not Found",
}
const NOT_FOUND_RESPONSE = {
error: NOT_FOUND.message,
}
describe("/_static", () => {
let _codeServer: httpserver.HttpServer | undefined let _codeServer: httpserver.HttpServer | undefined
function codeServer(): httpserver.HttpServer { function codeServer(): httpserver.HttpServer {
if (!_codeServer) { if (!_codeServer) {
@ -17,14 +27,8 @@ describe("/static", () => {
let testFileContent: string | undefined let testFileContent: string | undefined
let nonExistentTestFile: string | undefined let nonExistentTestFile: string | undefined
// The static endpoint expects a commit and then the full path of the file.
// The commit is just for cache busting so we can use anything we want. `-`
// and `development` are specially recognized in that they will cause the
// static endpoint to avoid sending cache headers.
const commit = "-"
beforeAll(async () => { beforeAll(async () => {
const testDir = await tmpdir("static") const testDir = await tmpdir("_static")
testFile = path.join(testDir, "test") testFile = path.join(testDir, "test")
testFileContent = "static file contents" testFileContent = "static file contents"
nonExistentTestFile = path.join(testDir, "i-am-not-here") nonExistentTestFile = path.join(testDir, "i-am-not-here")
@ -40,19 +44,19 @@ describe("/static", () => {
function commonTests() { function commonTests() {
it("should return a 404 when a commit and file are not provided", async () => { it("should return a 404 when a commit and file are not provided", async () => {
const resp = await codeServer().fetch("/static") const resp = await codeServer().fetch("/_static")
expect(resp.status).toBe(404) expect(resp.status).toBe(NOT_FOUND.code)
const content = await resp.json() const content = await resp.json()
expect(content).toStrictEqual({ error: "Not Found" }) expect(content).toStrictEqual(NOT_FOUND_RESPONSE)
}) })
it("should return a 404 when a file is not provided", async () => { it("should return a 404 when a file is not provided", async () => {
const resp = await codeServer().fetch(`/static/${commit}`) const resp = await codeServer().fetch(`/_static/`)
expect(resp.status).toBe(404) expect(resp.status).toBe(NOT_FOUND.code)
const content = await resp.json() const content = await resp.json()
expect(content).toStrictEqual({ error: "Not Found" }) expect(content).toStrictEqual(NOT_FOUND_RESPONSE)
}) })
} }
@ -64,73 +68,25 @@ describe("/static", () => {
commonTests() commonTests()
it("should return a 404 for a nonexistent file", async () => { it("should return a 404 for a nonexistent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}/${nonExistentTestFile}`) const filePath = path.join("/_static/", nonExistentTestFile!)
expect(resp.status).toBe(404)
const resp = await codeServer().fetch(filePath)
expect(resp.status).toBe(NOT_FOUND.code)
const content = await resp.json() const content = await resp.json()
expect(content.error).toMatch("ENOENT") expect(content.error).toMatch(NOT_FOUND.message)
}) })
it("should return a 200 and file contents for an existent file", async () => { it("should return a 200 and file contents for an existent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}${testFile}`) const resp = await codeServer().fetch("/_static/src/browser/robots.txt")
expect(resp.status).toBe(200) expect(resp.status).toBe(200)
const localFilePath = path.join(rootPath, "src/browser/robots.txt")
const localFileContent = await fs.readFile(localFilePath, "utf8")
// console.log(localFileContent)
const content = await resp.text() const content = await resp.text()
expect(content).toStrictEqual(testFileContent) expect(content).toStrictEqual(localFileContent)
})
})
describe("enabled authentication", () => {
// Store whatever might be in here so we can restore it afterward.
// TODO: We should probably pass this as an argument somehow instead of
// manipulating the environment.
const previousEnvPassword = process.env.PASSWORD
beforeEach(async () => {
process.env.PASSWORD = "test"
_codeServer = await integration.setup(["--auth=password"], "")
})
afterEach(() => {
process.env.PASSWORD = previousEnvPassword
})
commonTests()
describe("inside code-server root", () => {
it("should return a 404 for a nonexistent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}/${__filename}-does-not-exist`)
expect(resp.status).toBe(404)
const content = await resp.json()
expect(content.error).toMatch("ENOENT")
})
it("should return a 200 and file contents for an existent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}${__filename}`)
expect(resp.status).toBe(200)
const content = await resp.text()
expect(content).toStrictEqual(await fs.readFile(__filename, "utf8"))
})
})
describe("outside code-server root", () => {
it("should return a 401 for a nonexistent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}/${nonExistentTestFile}`)
expect(resp.status).toBe(401)
const content = await resp.json()
expect(content).toStrictEqual({ error: "Unauthorized" })
})
it("should return a 401 for an existent file", async () => {
const resp = await codeServer().fetch(`/static/${commit}${testFile}`)
expect(resp.status).toBe(401)
const content = await resp.json()
expect(content).toStrictEqual({ error: "Unauthorized" })
})
}) })
}) })
}) })

View File

@ -457,31 +457,6 @@ describe("escapeHtml", () => {
}) })
}) })
describe("pathToFsPath", () => {
it("should convert a path to a file system path", () => {
expect(util.pathToFsPath("/foo/bar/baz")).toBe("/foo/bar/baz")
})
it("should lowercase drive letter casing by default", () => {
expect(util.pathToFsPath("/C:/far/boo")).toBe("c:/far/boo")
})
it("should keep drive letter casing when set to true", () => {
expect(util.pathToFsPath("/C:/far/bo", true)).toBe("C:/far/bo")
})
it("should replace / with \\ on Windows", () => {
const ORIGINAL_PLATFORM = process.platform
Object.defineProperty(process, "platform", {
value: "win32",
})
expect(util.pathToFsPath("/C:/far/boo")).toBe("c:\\far\\boo")
Object.defineProperty(process, "platform", {
value: ORIGINAL_PLATFORM,
})
})
})
describe("isFile", () => { describe("isFile", () => {
const testDir = path.join(tmpdir, "tests", "isFile") const testDir = path.join(tmpdir, "tests", "isFile")
let pathToFile = "" let pathToFile = ""

View File

@ -1101,10 +1101,10 @@
jest-diff "^26.0.0" jest-diff "^26.0.0"
pretty-format "^26.0.0" pretty-format "^26.0.0"
"@types/jsdom@^16.2.6": "@types/jsdom@^16.2.13":
version "16.2.6" version "16.2.13"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.6.tgz#9ddf0521e49be5365797e690c3ba63148e562c29" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.13.tgz#126c8b7441b159d6234610a48de77b6066f1823f"
integrity sha512-yQA+HxknGtW9AkRTNyiSH3OKW5V+WzO8OPTdne99XwJkYC+KYxfNIcoJjeiSqP3V00PUUpFP6Myoo9wdIu78DQ== integrity sha512-8JQCjdeAidptSsOcRWk2iTm9wCcwn9l+kRG6k5bzUacrnm1ezV4forq0kWjUih/tumAeoG+OspOvQEbbRucBTw==
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/parse5" "*" "@types/parse5" "*"

View File

@ -15,9 +15,14 @@
"sourceMap": true, "sourceMap": true,
"tsBuildInfoFile": "./.cache/tsbuildinfo", "tsBuildInfoFile": "./.cache/tsbuildinfo",
"incremental": true, "incremental": true,
"typeRoots": ["./node_modules/@types", "./typings", "./test/node_modules/@types"], "typeRoots": [
"downlevelIteration": true "./node_modules/@types",
"./typings",
"./test/node_modules/@types",
"./vendor/modules/code-oss-dev/src/vs/server/@types"
],
"downlevelIteration": true,
}, },
"include": ["./src/**/*.ts"], "include": ["./src/**/*"],
"exclude": ["/test", "/lib", "/ci", "/doc"] "exclude": ["/test", "/lib", "/ci", "/doc"]
} }

137
typings/ipc.d.ts vendored
View File

@ -1,137 +0,0 @@
/**
* External interfaces for integration into code-server over IPC.
* This file exists in two locations:
* - typings/ipc.d.ts
* - lib/vscode/src/typings/ipc.d.ts
* The second is a symlink to the first.
*/
export interface Options {
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: WorkbenchOptions
}
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
}
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 NLSConfiguration {
locale: string
availableLanguages: {
[key: string]: string
}
pseudo?: boolean
_languagePackSupport?: boolean
}
export interface WorkbenchOptions {
readonly workbenchWebConfiguration: {
readonly remoteAuthority?: string
readonly folderUri?: UriComponents
readonly workspaceUri?: UriComponents
readonly logLevel?: number
readonly workspaceProvider?: {
payload: [["userDataPath", string], ["enableProposedApi", string]]
}
}
readonly remoteUserDataUri: UriComponents
readonly productConfiguration: {
codeServerVersion?: string
readonly extensionsGallery?: {
readonly serviceUrl: string
readonly itemUrl: string
readonly controlUrl: string
readonly recommendationsUrl: string
}
}
readonly nlsConfiguration: NLSConfiguration
readonly commit: string
}
export interface WorkbenchOptionsMessage {
id: string
}

2
vendor/package.json vendored
View File

@ -7,6 +7,6 @@
"postinstall": "./postinstall.sh" "postinstall": "./postinstall.sh"
}, },
"devDependencies": { "devDependencies": {
"code-oss-dev": "cdr/vscode#9cb5fb3759f46b10bc66e676fa7f44c51e84824b" "code-oss-dev": "cdr/vscode#8bbce0f30d70179ab87052a732ca4b86dd816497"
} }
} }

48
vendor/yarn.lock vendored
View File

@ -2,11 +2,6 @@
# yarn lockfile v1 # yarn lockfile v1
"@coder/logger@^1.1.16":
version "1.1.16"
resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.1.16.tgz#ee5b1b188f680733f35c11b065bbd139d618c1e1"
integrity sha512-X6VB1++IkosYY6amRAiMvuvCf12NA4+ooX+gOuu5bJIkdjmh4Lz7QpJcWRdgxesvo1msriDDr9E/sDbIWf6vsQ==
"@electron/get@^1.0.1": "@electron/get@^1.0.1":
version "1.13.0" version "1.13.0"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.0.tgz#95c6bcaff4f9a505ea46792424f451efea89228c" resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.0.tgz#95c6bcaff4f9a505ea46792424f451efea89228c"
@ -313,17 +308,17 @@ clone-response@^1.0.2:
dependencies: dependencies:
mimic-response "^1.0.0" mimic-response "^1.0.0"
code-oss-dev@cdr/vscode#9cb5fb3759f46b10bc66e676fa7f44c51e84824b: code-oss-dev@cdr/vscode#8bbce0f30d70179ab87052a732ca4b86dd816497:
version "1.60.0" version "1.60.2"
resolved "https://codeload.github.com/cdr/vscode/tar.gz/9cb5fb3759f46b10bc66e676fa7f44c51e84824b" resolved "https://codeload.github.com/cdr/vscode/tar.gz/8bbce0f30d70179ab87052a732ca4b86dd816497"
dependencies: dependencies:
"@coder/logger" "^1.1.16"
"@microsoft/applicationinsights-web" "^2.6.4" "@microsoft/applicationinsights-web" "^2.6.4"
"@vscode/sqlite3" "4.0.12" "@vscode/sqlite3" "4.0.12"
"@vscode/vscode-languagedetection" "1.0.20" "@vscode/vscode-languagedetection" "1.0.20"
applicationinsights "1.0.8" applicationinsights "1.0.8"
chokidar "3.5.1" chokidar "3.5.1"
graceful-fs "4.2.6" graceful-fs "4.2.6"
handlebars "^4.7.7"
http-proxy-agent "^2.1.0" http-proxy-agent "^2.1.0"
https-proxy-agent "^2.2.3" https-proxy-agent "^2.2.3"
iconv-lite-umd "0.6.8" iconv-lite-umd "0.6.8"
@ -333,6 +328,7 @@ code-oss-dev@cdr/vscode#9cb5fb3759f46b10bc66e676fa7f44c51e84824b:
native-watchdog "1.3.0" native-watchdog "1.3.0"
node-pty "0.11.0-beta7" node-pty "0.11.0-beta7"
nsfw "2.1.2" nsfw "2.1.2"
path-to-regexp "^6.2.0"
proxy-agent "^4.0.1" proxy-agent "^4.0.1"
proxy-from-env "^1.1.0" proxy-from-env "^1.1.0"
spdlog "^0.13.0" spdlog "^0.13.0"
@ -775,6 +771,18 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
neo-async "^2.6.0"
source-map "^0.6.1"
wordwrap "^1.0.0"
optionalDependencies:
uglify-js "^3.1.4"
has-unicode@^2.0.0: has-unicode@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@ -1068,6 +1076,11 @@ native-watchdog@1.3.0:
resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.3.0.tgz#88cee94c9dc766b85c8506eda14c8bd8c9618e27" resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.3.0.tgz#88cee94c9dc766b85c8506eda14c8bd8c9618e27"
integrity sha512-WOjGRNGkYZ5MXsntcvCYrKtSYMaewlbCFplbcUVo9bE80LPVt8TAVFHYWB8+a6fWCGYheq21+Wtt6CJrUaCJhw== integrity sha512-WOjGRNGkYZ5MXsntcvCYrKtSYMaewlbCFplbcUVo9bE80LPVt8TAVFHYWB8+a6fWCGYheq21+Wtt6CJrUaCJhw==
neo-async@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
netmask@^2.0.1: netmask@^2.0.1:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7"
@ -1195,6 +1208,11 @@ pac-resolver@^4.1.0:
ip "^1.1.5" ip "^1.1.5"
netmask "^2.0.1" netmask "^2.0.1"
path-to-regexp@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38"
integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==
pend@~1.2.0: pend@~1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
@ -1454,7 +1472,7 @@ socks@^2.3.3:
ip "^1.1.5" ip "^1.1.5"
smart-buffer "^4.1.0" smart-buffer "^4.1.0"
source-map@~0.6.1: source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1" version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@ -1622,6 +1640,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
uglify-js@^3.1.4:
version "3.14.2"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99"
integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==
universalify@^0.1.0: universalify@^0.1.0:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@ -1733,6 +1756,11 @@ word-wrap@~1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
wordwrap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
wrappy@1: wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"

107
yarn.lock
View File

@ -339,7 +339,7 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1"
integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==
"@types/body-parser@*", "@types/body-parser@^1.19.0": "@types/body-parser@*":
version "1.19.1" version "1.19.1"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c"
integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==
@ -505,20 +505,10 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/tar-fs@^2.0.0": "@types/trusted-types@^2.0.2":
version "2.0.1" version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-2.0.1.tgz#6391dcad1b03dea2d79fac07371585ab54472bb1" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
integrity sha512-qlsQyIY9sN7p221xHuXKNoMfUenOcvEBN4zI8dGsYbYCqHtTarXOEXSIgUnK+GcR0fZDse6pAIc5pIrCh9NefQ== integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
dependencies:
"@types/node" "*"
"@types/tar-stream" "*"
"@types/tar-stream@*", "@types/tar-stream@^2.1.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-2.2.1.tgz#7cb4516fe6d1a926a37b7733905c50885718e7ad"
integrity sha512-zhcfACZ4HavArMutfAB1/ApfSx44kNF2zyytU4mbO1dGCT/y9kL2IZwRDRyYYtBUxW6LRparZpLoX8i67b6IZw==
dependencies:
"@types/node" "*"
"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2":
version "2.0.3" version "2.0.3"
@ -859,20 +849,11 @@ balanced-match@^2.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
base64-js@^1.0.2, base64-js@^1.3.1: base64-js@^1.0.2:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
bl@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
dependencies:
buffer "^5.5.0"
inherits "^2.0.4"
readable-stream "^3.4.0"
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9:
version "4.12.0" version "4.12.0"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
@ -883,7 +864,7 @@ bn.js@^5.0.0, bn.js@^5.1.1:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
body-parser@1.19.0, body-parser@^1.19.0: body-parser@1.19.0:
version "1.19.0" version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
@ -1097,14 +1078,6 @@ buffer-xor@^1.0.3:
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
buffer@^5.5.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.1.13"
buffer@~5.2.1: buffer@~5.2.1:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
@ -1207,11 +1180,6 @@ charenc@0.0.2:
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
chownr@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
chownr@^2.0.0: chownr@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
@ -1773,13 +1741,6 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
dependencies:
once "^1.4.0"
enquirer@^2.3.5: enquirer@^2.3.5:
version "2.3.6" version "2.3.6"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
@ -2290,11 +2251,6 @@ from@^0.1.7:
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-extra@^8.1.0: fs-extra@^8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
@ -2644,7 +2600,7 @@ iconv-lite@0.4.24:
dependencies: dependencies:
safer-buffer ">= 2.1.2 < 3" safer-buffer ">= 2.1.2 < 3"
ieee754@^1.1.13, ieee754@^1.1.4: ieee754@^1.1.4:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
@ -3549,7 +3505,7 @@ on-headers@~1.0.2:
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
once@^1.3.0, once@^1.3.1, once@^1.4.0: once@^1.3.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
@ -3999,14 +3955,6 @@ public-encrypt@^4.0.0:
randombytes "^2.0.1" randombytes "^2.0.1"
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
punycode@1.3.2: punycode@1.3.2:
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
@ -4160,7 +4108,7 @@ readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable
string_decoder "~1.1.1" string_decoder "~1.1.1"
util-deprecate "~1.0.1" util-deprecate "~1.0.1"
readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.5.0, readable-stream@^3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -4851,27 +4799,6 @@ table@^6.0.9, table@^6.6.0:
string-width "^4.2.0" string-width "^4.2.0"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
tar-fs@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
dependencies:
chownr "^1.1.1"
mkdirp-classic "^0.5.2"
pump "^3.0.0"
tar-stream "^2.1.4"
tar-stream@^2.1.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
dependencies:
bl "^4.0.3"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
tar@^6.1.0, tar@^6.1.9: tar@^6.1.0, tar@^6.1.9:
version "6.1.11" version "6.1.11"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
@ -5064,10 +4991,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^4.1.3: typescript@^4.4.0-dev.20210528:
version "4.3.5" version "4.4.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
umd@^3.0.0: umd@^3.0.0:
version "3.0.3" version "3.0.3"
@ -5394,9 +5321,9 @@ write-file-atomic@^3.0.3:
typedarray-to-buffer "^3.1.5" typedarray-to-buffer "^3.1.5"
ws@^8.0.0: ws@^8.0.0:
version "8.0.0" version "8.2.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.0.0.tgz#550605d13dfc1437c9ec1396975709c6d7ffc57d" resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.0.tgz#0b738cd484bfc9303421914b11bb4011e07615bb"
integrity sha512-6AcSIXpBlS0QvCVKk+3cWnWElLsA6SzC0lkQ43ciEglgXJXiCWK3/CGFEJ+Ybgp006CMibamAsqOlxE9s4AvYA== integrity sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g==
x-is-string@^0.1.0: x-is-string@^0.1.0:
version "0.1.0" version "0.1.0"