Compare commits

...

25 Commits

Author SHA1 Message Date
Kyle Carberry
2bd7281fa0 Update @coder/nbin 2019-04-04 09:59:50 -04:00
Asher
e12fcd3a0d Fix error when shared process exits with null 2019-04-03 17:32:20 -05:00
Kyle Carberry
4af84fcaf6 Add flags for customizing user data dir and extensions dir (#420)
* Add flags for customizing extensions directory

* Update @coder/nbin
2019-04-03 17:07:47 -05:00
Asher
c607015a26 Initialize backup service (#419)
- Fixes #399
- Fixes #332
2019-04-03 16:08:15 -05:00
John McCambridge
217515344e Add port in use message (#418)
* Add clear error message if port is in use

* Add bold function for text/numbers

* remove unused dependency:

* remove unused line break

* Change logger message

* Use NodeJS.ErrnoException type

* Add back check for error code
2019-04-03 15:50:52 -05:00
Kyle Carberry
dcf409aecb Improve CI caching (#416)
* Adjust linux distro to ubuntu 14.04

* Cache lib directory for speedy builds

* Fix path linking for default extensions

* Update reset

* Reset to head

* Improve caching

* Still run yarn in CI

* Update yarn before install

* Increase cache timeout

* Install vscode from vstar

* Undo data-dir changes to CLI, add back clean, remove unused CI func

* Remove additional flags added

* Remove unused dependency

* Reset vscode install dir so patching always works
2019-04-03 14:24:00 -05:00
Kyle Carberry
2683b7c734 Update notes title 2019-04-03 11:23:32 -04:00
Asher
3a672d725a Convert fully to protobuf (was partially JSON) (#402)
* Convert fully to protobuf (was partially JSON)

* Handle all floating promises

* Remove stringified proto from trace logging

It wasn't proving to be very useful.
2019-04-02 17:44:28 -05:00
Kyle Carberry
f484781693 Minor update in notes 2019-04-02 11:41:44 -04:00
Anmol Sethi
97f5b07003 Fix icons on safari when using cookie authentication (#398)
Cookie's are not sent with url's in -webkit-mask so we
embed the svg's directly in the css.
2019-04-01 15:20:39 -05:00
MOZGIII
7481395353 Changed "lib" to "/lib" at .gitignore (#395) 2019-04-01 14:15:43 -05:00
Asher
033ef151ca Improve retry
Registering returns an instance that lets you retry and recover without
needing to keep passing the name everywhere.

Also refactored the shared process a little to make better use of the
retry and downgraded stderr messages to warnings because they aren't
critical.
2019-04-01 13:31:34 -05:00
Jeff Delaney
3fec7f432c doc: fixed name of binary to match latest release (#386) 2019-03-31 13:15:33 -05:00
Asher
4887078423 Fix typescript tslint plugin
tslint-language-service is the deprecated version which we don't
actually even have listed in the package.json. typescript-tslint-plugin
is the new version.
2019-03-29 18:44:04 -05:00
Asher
91deaece47 Reduce frequency of port scanning 2019-03-29 16:14:28 -05:00
Asher
03ad2a17b2 Handle disconnects (#363)
* Make proxies decide how to handle disconnects

* Connect to a new terminal instance on disconnect

* Use our retry for the watcher

* Specify method when proxy doesn't exist

* Don't error when closing/killing disconnected proxy

* Specify proxy ID when a method doesn't exist

* Use our retry for the searcher

Also dispose some things for the watcher because it doesn't seem that
was done properly.

The searcher also now starts immediately so there won't be lag when you
perform your first search.

* Use our retry for the extension host

* Emit error in parent proxy class

Reduces duplicate code. Not all items are "supposed" to have an error
event according to the original implementation we are filling, but there
is no reason why we can't emit our own events (and are already doing so
for the "disconnected" event anyway).

* Reconnect spdlog

* Add error message when shared process disconnects

* Pass method resolve to parse

* Don't pass method to getProxy

It doesn't tell you anything that trace logging wouldn't and has
no relation to what the function actually does.

* Fix infinite recursion when disposing protocol client in tests
2019-03-28 17:59:49 -05:00
John McCambridge
a4cca6b759 Add information about release notifications/gif (#355)
* Add information about release notifications/gif

* Remove unecessary space

* Add smaller gif

* Add even smaller gif

* Trim time in new gif

* Cropped a tad more

* Fix weird aligning
2019-03-27 17:05:44 -05:00
NGTmeaty
6105bba0a4 Add higher quality Discord badge and add a link to the license badge. (#364)
* Add higher quality Discord badge and add link 

to license.

* Use @grant's much better version :)
2019-03-27 17:05:23 -05:00
Asher
259095eae2 Watcher and initial load performance improvements (#357)
* Set low CPU priority on watcher

Fixes #247.

* Batch stat and readdir calls

* Fix fs.exists

callbackify seems to always adds an error as the first argument. Opted
to just use the promise for this one.

* Batch lstat

* Add maximum time for flushing batches
2019-03-27 17:04:19 -05:00
Carlos Azaustre
38a0706b18 Add example with letsencrypt certificates
* updated the download link

* added example with letsencrypt certificates
2019-03-27 10:36:03 -05:00
Ryo Ochiai
c7ae12c2ed Add .node-version file (#272) 2019-03-27 10:35:00 -05:00
James Peters
3331f9b28d Update Dockerfile (#327) 2019-03-27 10:34:34 -05:00
Reda Aissaoui
def4104c53 Changed executable name (#353)
code-server-luni should be only code-server
2019-03-27 10:23:23 -05:00
Kyle Carberry
4eb5331ddc Fixes #121 2019-03-27 10:36:32 -04:00
Kyle Carberry
3bb5c0bbe5 Fixes #351 2019-03-27 09:56:05 -04:00
59 changed files with 6655 additions and 3568 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,7 @@
lib /lib
node_modules node_modules
dist dist
out out
.DS_Store .DS_Store
release release
.cache

1
.node-version Normal file
View File

@@ -0,0 +1 @@
8.15.0

View File

@@ -6,11 +6,12 @@ env:
matrix: matrix:
include: include:
- os: linux - os: linux
dist: ubuntu dist: trusty
- os: osx - os: osx
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libxkbfile-dev - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libxkbfile-dev
libsecret-1-dev; fi libsecret-1-dev; fi
- npm install -g yarn@1.12.3
script: script:
- scripts/build.sh - scripts/build.sh
before_deploy: before_deploy:
@@ -35,4 +36,8 @@ deploy:
on: on:
repo: codercom/code-server repo: codercom/code-server
branch: master branch: master
cache: yarn cache:
yarn: true
timeout: 1000
directories:
- .cache

View File

@@ -30,4 +30,5 @@ RUN locale-gen en_US.UTF-8
# We unfortunately cannot use update-locale because docker will not use the env variables # We unfortunately cannot use update-locale because docker will not use the env variables
# configured in /etc/default/locale so we need to set it manually. # configured in /etc/default/locale so we need to set it manually.
ENV LANG=en_US.UTF-8 ENV LANG=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
ENTRYPOINT ["code-server"] ENTRYPOINT ["code-server"]

View File

@@ -2,8 +2,8 @@
[!["Open Issues"](https://img.shields.io/github/issues-raw/codercom/code-server.svg)](https://github.com/codercom/code-server/issues) [!["Open Issues"](https://img.shields.io/github/issues-raw/codercom/code-server.svg)](https://github.com/codercom/code-server/issues)
[!["Latest Release"](https://img.shields.io/github/release/codercom/code-server.svg)](https://github.com/codercom/code-server/releases/latest) [!["Latest Release"](https://img.shields.io/github/release/codercom/code-server.svg)](https://github.com/codercom/code-server/releases/latest)
[![MIT license](https://img.shields.io/badge/license-MIT-green.svg)](#) [![MIT license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/codercom/code-server/blob/master/LICENSE)
[![Discord](https://discordapp.com/api/guilds/463752820026376202/widget.png)](https://discord.gg/zxSwN8Z) [![Discord](https://img.shields.io/discord/463752820026376202.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/zxSwN8Z)
`code-server` is [VS Code](https://github.com/Microsoft/vscode) running on a remote server, accessible through the browser. `code-server` is [VS Code](https://github.com/Microsoft/vscode) running on a remote server, accessible through the browser.
@@ -57,14 +57,15 @@ How to [secure your setup](/doc/security/ssl.md).
- Creating custom VS Code extensions and debugging them doesn't work. - Creating custom VS Code extensions and debugging them doesn't work.
### Future ### Future
- **Stay up to date!** Get notified about new releases of code-server.
![Screenshot](/doc/assets/release.gif)
- Windows support. - Windows support.
- Electron and Chrome OS applications to bridge the gap between local<->remote. - Electron and Chrome OS applications to bridge the gap between local<->remote.
- Run VS Code unit tests against our builds to ensure features work as expected. - Run VS Code unit tests against our builds to ensure features work as expected.
### Notes ### Extensions
- At the moment we can't use the official VSCode Marketplace. We've created a custom extension marketplace focused around open-sourced extensions. However, if you have access to the `.vsix` file, you can manually install the extension. At the moment we can't use the official VSCode Marketplace. We've created a custom extension marketplace focused around open-sourced extensions. However, if you have access to the `.vsix` file, you can manually install the extension.
## Contributing ## Contributing

View File

@@ -4,21 +4,22 @@ import * as fse from "fs-extra";
import * as os from "os"; import * as os from "os";
import * as path from "path"; import * as path from "path";
import * as zlib from "zlib"; import * as zlib from "zlib";
import * as https from "https";
import * as tar from "tar";
const isWin = os.platform() === "win32"; const isWin = os.platform() === "win32";
const libPath = path.join(__dirname, "../lib"); const libPath = path.join(__dirname, "../lib");
const vscodePath = path.join(libPath, "vscode"); const vscodePath = path.join(libPath, "vscode");
const defaultExtensionsPath = path.join(libPath, "extensions");
const pkgsPath = path.join(__dirname, "../packages"); const pkgsPath = path.join(__dirname, "../packages");
const defaultExtensionsPath = path.join(libPath, "VSCode-linux-x64/resources/app/extensions");
const vscodeVersion = process.env.VSCODE_VERSION || "1.32.0"; const vscodeVersion = process.env.VSCODE_VERSION || "1.32.0";
const vsSourceUrl = `https://codesrv-ci.cdr.sh/vstar-${vscodeVersion}.tar.gz`;
const buildServerBinary = register("build:server:binary", async (runner) => { const buildServerBinary = register("build:server:binary", async (runner) => {
await ensureInstalled(); await ensureInstalled();
await copyForDefaultExtensions();
await Promise.all([ await Promise.all([
buildBootstrapFork(), buildBootstrapFork(),
buildWeb(), buildWeb(),
buildDefaultExtensions(),
buildServerBundle(), buildServerBundle(),
buildAppBrowser(), buildAppBrowser(),
]); ]);
@@ -129,97 +130,50 @@ const buildWeb = register("build:web", async (runner) => {
await runner.execute(isWin ? "npm.cmd" : "npm", ["run", "build"]); await runner.execute(isWin ? "npm.cmd" : "npm", ["run", "build"]);
}); });
const extDirPath = path.join("lib", "vscode-default-extensions");
const copyForDefaultExtensions = register("build:copy-vscode", async (runner) => {
if (!fs.existsSync(defaultExtensionsPath)) {
await ensureClean();
await ensureInstalled();
await new Promise((resolve, reject): void => {
fse.remove(extDirPath, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
await new Promise((resolve, reject): void => {
fse.copy(vscodePath, extDirPath, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
}
});
const buildDefaultExtensions = register("build:default-extensions", async (runner) => {
if (!fs.existsSync(defaultExtensionsPath)) {
await copyForDefaultExtensions();
runner.cwd = extDirPath;
const resp = await runner.execute(isWin ? "npx.cmd" : "npx", [isWin ? "gulp.cmd" : "gulp", "vscode-linux-x64", "--max-old-space-size=32384"]);
if (resp.exitCode !== 0) {
throw new Error(`Failed to build default extensions: ${resp.stderr}`);
}
}
});
const ensureInstalled = register("vscode:install", async (runner) => { const ensureInstalled = register("vscode:install", async (runner) => {
await ensureCloned();
runner.cwd = vscodePath;
const install = await runner.execute(isWin ? "yarn.cmd" : "yarn", []);
if (install.exitCode !== 0) {
throw new Error(`Failed to install vscode dependencies: ${install.stderr}`);
}
});
const ensureCloned = register("vscode:clone", async (runner) => {
if (fs.existsSync(vscodePath)) {
await ensureClean();
} else {
fse.mkdirpSync(libPath);
runner.cwd = libPath; runner.cwd = libPath;
const clone = await runner.execute("git", ["clone", "https://github.com/microsoft/vscode", "--branch", vscodeVersion, "--single-branch", "--depth=1"]);
if (clone.exitCode !== 0) { if (fs.existsSync(vscodePath) && fs.existsSync(defaultExtensionsPath)) {
throw new Error(`Failed to clone: ${clone.exitCode}`); const pkgVersion = JSON.parse(fs.readFileSync(path.join(vscodePath, "package.json")).toString("utf8")).version;
if (pkgVersion === vscodeVersion) {
runner.cwd = vscodePath;
const reset = await runner.execute("git", ["reset", "--hard"]);
if (reset.exitCode !== 0) {
throw new Error(`Failed to clean git repository: ${reset.stderr}`);
}
return;
} }
} }
runner.cwd = vscodePath; fse.removeSync(libPath);
const checkout = await runner.execute("git", ["checkout", vscodeVersion]); fse.mkdirpSync(libPath);
if (checkout.exitCode !== 0) {
throw new Error(`Failed to checkout: ${checkout.stderr}`); await new Promise<void>((resolve, reject): void => {
https.get(vsSourceUrl, (res) => {
if (res.statusCode !== 200) {
return reject(res.statusMessage);
} }
res.pipe(tar.x({
C: libPath,
}).on("finish", () => {
resolve();
}).on("error", (err: Error) => {
reject(err);
}));
}).on("error", (err) => {
reject(err);
});
}); });
const ensureClean = register("vscode:clean", async (runner) => {
runner.cwd = vscodePath;
const status = await runner.execute("git", ["status", "--porcelain"]);
if (status.stdout.trim() !== "") {
const clean = await runner.execute("git", ["clean", "-f", "-d", "-X"]);
if (clean.exitCode !== 0) {
throw new Error(`Failed to clean git repository: ${clean.stderr}`);
}
const removeUnstaged = await runner.execute("git", ["checkout", "--", "."]);
if (removeUnstaged.exitCode !== 0) {
throw new Error(`Failed to remove unstaged files: ${removeUnstaged.stderr}`);
}
}
const fetch = await runner.execute("git", ["fetch", "--prune"]);
if (fetch.exitCode !== 0) {
throw new Error(`Failed to fetch latest changes: ${fetch.stderr}`);
}
}); });
const ensurePatched = register("vscode:patch", async (runner) => { const ensurePatched = register("vscode:patch", async (runner) => {
if (!fs.existsSync(vscodePath)) { if (!fs.existsSync(vscodePath)) {
throw new Error("vscode must be cloned to patch"); throw new Error("vscode must be cloned to patch");
} }
await ensureClean(); await ensureInstalled();
runner.cwd = vscodePath; runner.cwd = vscodePath;
const patchPath = path.join(__dirname, "../scripts/vscode.patch"); const patchPath = path.join(__dirname, "../scripts/vscode.patch");

View File

@@ -56,7 +56,7 @@ If you're just starting out, we recommend [installing code-server locally](../..
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../../security/ssl.md) > To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../../security/ssl.md)
- Finally, run - Finally, run
``` ```
sudo ./code-server-linux -p 80 sudo ./code-server -p 80
``` ```
- When you visit the public IP for your AWS instance, you will be greeted with this page. Code-server is using a self-signed SSL certificate for easy setup. To proceed to the IDE, click **"Advanced"**<img src ="../../assets/chrome_warning.png"> - When you visit the public IP for your AWS instance, you will be greeted with this page. Code-server is using a self-signed SSL certificate for easy setup. To proceed to the IDE, click **"Advanced"**<img src ="../../assets/chrome_warning.png">
- Then click **"proceed anyway"**<img src="../../assets/chrome_confirm.png"> - Then click **"proceed anyway"**<img src="../../assets/chrome_confirm.png">

View File

@@ -54,7 +54,7 @@ chmod +x code-server
- Start the code-server - Start the code-server
``` ```
sudo ./code-server-linux -p 80 sudo ./code-server -p 80
``` ```
> For instructions on how to keep the server running after you end your SSH session please checkout [how to use systemd](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) to start linux based services if they are killed > For instructions on how to keep the server running after you end your SSH session please checkout [how to use systemd](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) to start linux based services if they are killed

BIN
doc/assets/release.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -71,6 +71,7 @@ OPTIONS
### Cert and Cert Key ### Cert and Cert Key
To encrypt the traffic between the browser and server use `code-server --cert=` followed by the path to your `.cer` file. Additionally, you can use certificate keys with `code-server --cert-key` followed by the path to your `.key` file. To encrypt the traffic between the browser and server use `code-server --cert=` followed by the path to your `.cer` file. Additionally, you can use certificate keys with `code-server --cert-key` followed by the path to your `.key` file.
> Example (certificate and key): `code-server --cert /etc/letsencrypt/live/example.com/fullchain.cer --cert-key /etc/letsencrypt/live/example.com/fullchain.key` > Example (certificate and key): `code-server --cert /etc/letsencrypt/live/example.com/fullchain.cer --cert-key /etc/letsencrypt/live/example.com/fullchain.key`
> Example (if you are using Letsencrypt or similar): `code-server --cert /etc/letsencrypt/live/example.com/fullchain.pem --cert-key /etc/letsencrypt/live/example.com/privkey.key`
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../security/ssl.md) > To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../security/ssl.md)

View File

@@ -15,7 +15,9 @@
"devDependencies": { "devDependencies": {
"@types/fs-extra": "^5.0.4", "@types/fs-extra": "^5.0.4",
"@types/node": "^10.12.18", "@types/node": "^10.12.18",
"@types/tar": "^4.0.0",
"@types/trash": "^4.3.1", "@types/trash": "^4.3.1",
"cache-loader": "^2.0.1",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"css-loader": "^2.1.0", "css-loader": "^2.1.0",
@@ -34,6 +36,8 @@
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"string-replace-loader": "^2.1.1", "string-replace-loader": "^2.1.1",
"style-loader": "^0.23.1", "style-loader": "^0.23.1",
"tar": "^4.4.8",
"terser-webpack-plugin": "^1.2.3",
"ts-loader": "^5.3.3", "ts-loader": "^5.3.3",
"ts-node": "^7.0.1", "ts-node": "^7.0.1",
"tsconfig-paths": "^3.8.0", "tsconfig-paths": "^3.8.0",
@@ -42,6 +46,7 @@
"typescript": "^3.2.2", "typescript": "^3.2.2",
"typescript-tslint-plugin": "^0.2.1", "typescript-tslint-plugin": "^0.2.1",
"uglifyjs-webpack-plugin": "^2.1.1", "uglifyjs-webpack-plugin": "^2.1.1",
"url-loader": "^1.1.2",
"util": "^0.11.1", "util": "^0.11.1",
"webpack": "^4.28.4", "webpack": "^4.28.4",
"webpack-bundle-analyzer": "^3.0.3", "webpack-bundle-analyzer": "^3.0.3",

View File

@@ -11,7 +11,7 @@ class WebsocketConnection implements ReadWriteConnection {
private activeSocket: WebSocket | undefined; private activeSocket: WebSocket | undefined;
private readonly messageBuffer = <Uint8Array[]>[]; private readonly messageBuffer = <Uint8Array[]>[];
private readonly socketTimeoutDelay = 60 * 1000; private readonly socketTimeoutDelay = 60 * 1000;
private readonly retryName = "Socket"; private readonly retry = retry.register("Socket", () => this.connect());
private isUp: boolean = false; private isUp: boolean = false;
private closed: boolean = false; private closed: boolean = false;
@@ -26,11 +26,14 @@ class WebsocketConnection implements ReadWriteConnection {
public readonly onMessage = this.messageEmitter.event; public readonly onMessage = this.messageEmitter.event;
public constructor() { public constructor() {
retry.register(this.retryName, () => this.connect()); this.retry.block();
retry.block(this.retryName); this.retry.run();
retry.run(this.retryName);
} }
/**
* Send data across the socket. If closed, will error. If connecting, will
* queue.
*/
public send(data: Buffer | Uint8Array): void { public send(data: Buffer | Uint8Array): void {
if (this.closed) { if (this.closed) {
throw new Error("web socket is closed"); throw new Error("web socket is closed");
@@ -42,6 +45,9 @@ class WebsocketConnection implements ReadWriteConnection {
} }
} }
/**
* Close socket connection.
*/
public close(): void { public close(): void {
this.closed = true; this.closed = true;
this.dispose(); this.dispose();
@@ -75,8 +81,8 @@ class WebsocketConnection implements ReadWriteConnection {
field("wasClean", event.wasClean), field("wasClean", event.wasClean),
); );
if (!this.closed) { if (!this.closed) {
retry.block(this.retryName); this.retry.block();
retry.run(this.retryName); this.retry.run();
} }
}); });
@@ -108,15 +114,19 @@ class WebsocketConnection implements ReadWriteConnection {
}, this.socketTimeoutDelay); }, this.socketTimeoutDelay);
await new Promise((resolve, reject): void => { await new Promise((resolve, reject): void => {
const onClose = (): void => { const doReject = (): void => {
clearTimeout(socketWaitTimeout); clearTimeout(socketWaitTimeout);
socket.removeEventListener("close", onClose); socket.removeEventListener("error", doReject);
socket.removeEventListener("close", doReject);
reject(); reject();
}; };
socket.addEventListener("close", onClose); socket.addEventListener("error", doReject);
socket.addEventListener("close", doReject);
socket.addEventListener("open", async () => { socket.addEventListener("open", () => {
clearTimeout(socketWaitTimeout); clearTimeout(socketWaitTimeout);
socket.removeEventListener("error", doReject);
socket.removeEventListener("close", doReject);
resolve(); resolve();
}); });
}); });

View File

@@ -1,14 +1,64 @@
import { logger, field } from "@coder/logger"; import { logger, field } from "@coder/logger";
import { NotificationService, INotificationHandle, INotificationService, Severity } from "./fill/notification"; import { NotificationService, INotificationHandle, INotificationService, Severity } from "./fill/notification";
// tslint:disable no-any can have different return values
interface IRetryItem { interface IRetryItem {
/**
* How many times this item has been retried.
*/
count?: number; count?: number;
delay?: number; // In seconds.
end?: number; // In ms. /**
fn(): any | Promise<any>; // tslint:disable-line no-any can have different return values * In seconds.
*/
delay?: number;
/**
* In milliseconds.
*/
end?: number;
/**
* Function to run when retrying.
*/
fn(): any;
/**
* Timer for running this item.
*/
timeout?: number | NodeJS.Timer; timeout?: number | NodeJS.Timer;
/**
* Whether the item is retrying or waiting to retry.
*/
running?: boolean; running?: boolean;
showInNotification: boolean; }
/**
* An retry-able instance.
*/
export interface RetryInstance {
/**
* Run this retry.
*/
run(error?: Error): void;
/**
* Block on this instance.
*/
block(): void;
}
/**
* A retry-able instance that doesn't use a promise so it must be manually
* ran again on failure and recovered on success.
*/
export interface ManualRetryInstance extends RetryInstance {
/**
* Mark this item as recovered.
*/
recover(): void;
} }
/** /**
@@ -21,7 +71,7 @@ interface IRetryItem {
* to the user explaining what is happening with an option to immediately retry. * to the user explaining what is happening with an option to immediately retry.
*/ */
export class Retry { export class Retry {
private items = new Map<string, IRetryItem>(); private readonly items = new Map<string, IRetryItem>();
// Times are in seconds. // Times are in seconds.
private readonly retryMinDelay = 1; private readonly retryMinDelay = 1;
@@ -50,13 +100,54 @@ export class Retry {
} }
/** /**
* Block retries when we know they will fail (for example when starting Wush * Register a function to retry that starts/connects to a service.
* back up). If a name is passed, that service will still be allowed to retry *
* The service is automatically retried or recovered when the promise resolves
* or rejects. If the service dies after starting, it must be manually
* retried.
*/
public register(name: string, fn: () => Promise<any>): RetryInstance;
/**
* Register a function to retry that starts/connects to a service.
*
* Must manually retry if it fails to start again or dies after restarting and
* manually recover if it succeeds in starting again.
*/
public register(name: string, fn: () => any): ManualRetryInstance;
/**
* Register a function to retry that starts/connects to a service.
*/
public register(name: string, fn: () => any): RetryInstance | ManualRetryInstance {
if (this.items.has(name)) {
throw new Error(`"${name}" is already registered`);
}
this.items.set(name, { fn });
return {
block: (): void => this.block(name),
run: (error?: Error): void => this.run(name, error),
recover: (): void => this.recover(name),
};
}
/**
* Un-register a function to retry.
*/
public unregister(name: string): void {
if (!this.items.has(name)) {
throw new Error(`"${name}" is not registered`);
}
this.items.delete(name);
}
/**
* Block retries when we know they will fail (for example when the socket is
* down ). If a name is passed, that service will still be allowed to retry
* (unless we have already blocked). * (unless we have already blocked).
* *
* Blocking without a name will override a block with a name. * Blocking without a name will override a block with a name.
*/ */
public block(name?: string): void { private block(name?: string): void {
if (!this.blocked || !name) { if (!this.blocked || !name) {
this.blocked = name || true; this.blocked = name || true;
this.items.forEach((item) => { this.items.forEach((item) => {
@@ -68,7 +159,7 @@ export class Retry {
/** /**
* Unblock retries and run any that are pending. * Unblock retries and run any that are pending.
*/ */
public unblock(): void { private unblock(): void {
this.blocked = false; this.blocked = false;
this.items.forEach((item, name) => { this.items.forEach((item, name) => {
if (item.running) { if (item.running) {
@@ -77,35 +168,10 @@ export class Retry {
}); });
} }
/**
* Register a function to retry that starts/connects to a service.
*
* If the function returns a promise, it will automatically be retried,
* recover, & unblock after calling `run` once (otherwise they need to be
* called manually).
*/
// tslint:disable-next-line no-any can have different return values
public register(name: string, fn: () => any | Promise<any>, showInNotification: boolean = true): void {
if (this.items.has(name)) {
throw new Error(`"${name}" is already registered`);
}
this.items.set(name, { fn, showInNotification });
}
/**
* Unregister a function to retry.
*/
public unregister(name: string): void {
if (!this.items.has(name)) {
throw new Error(`"${name}" is not registered`);
}
this.items.delete(name);
}
/** /**
* Retry a service. * Retry a service.
*/ */
public run(name: string, error?: Error): void { private run(name: string, error?: Error): void {
if (!this.items.has(name)) { if (!this.items.has(name)) {
throw new Error(`"${name}" is not registered`); throw new Error(`"${name}" is not registered`);
} }
@@ -149,7 +215,7 @@ export class Retry {
/** /**
* Reset a service after a successfully recovering. * Reset a service after a successfully recovering.
*/ */
public recover(name: string): void { private recover(name: string): void {
if (!this.items.has(name)) { if (!this.items.has(name)) {
throw new Error(`"${name}" is not registered`); throw new Error(`"${name}" is not registered`);
} }
@@ -191,9 +257,9 @@ export class Retry {
if (this.blocked === name) { if (this.blocked === name) {
this.unblock(); this.unblock();
} }
}).catch(() => { }).catch((error) => {
endItem(); endItem();
this.run(name); this.run(name, error);
}); });
} else { } else {
endItem(); endItem();
@@ -214,8 +280,7 @@ export class Retry {
const now = Date.now(); const now = Date.now();
const items = Array.from(this.items.entries()).filter(([_, item]) => { const items = Array.from(this.items.entries()).filter(([_, item]) => {
return item.showInNotification return typeof item.end !== "undefined"
&& typeof item.end !== "undefined"
&& item.end > now && item.end > now
&& item.delay && item.delay >= this.notificationThreshold; && item.delay && item.delay >= this.notificationThreshold;
}).sort((a, b) => { }).sort((a, b) => {

View File

@@ -5,8 +5,8 @@ import { Emitter } from "@coder/events";
import { logger, field } from "@coder/logger"; import { logger, field } from "@coder/logger";
import { ReadWriteConnection, InitData, SharedProcessData } from "../common/connection"; import { ReadWriteConnection, InitData, SharedProcessData } from "../common/connection";
import { Module, ServerProxy } from "../common/proxy"; import { Module, ServerProxy } from "../common/proxy";
import { stringify, parse, moduleToProto, protoToModule, protoToOperatingSystem } from "../common/util"; import { argumentToProto, protoToArgument, moduleToProto, protoToModule, protoToOperatingSystem } from "../common/util";
import { Ping, ServerMessage, ClientMessage, MethodMessage, NamedProxyMessage, NumberedProxyMessage, SuccessMessage, FailMessage, EventMessage, CallbackMessage } from "../proto"; import { Argument, Ping, ServerMessage, ClientMessage, Method, Event, Callback } from "../proto";
import { FsModule, ChildProcessModule, NetModule, NodePtyModule, SpdlogModule, TrashModule } from "./modules"; import { FsModule, ChildProcessModule, NetModule, NodePtyModule, SpdlogModule, TrashModule } from "./modules";
// tslint:disable no-any // tslint:disable no-any
@@ -24,8 +24,8 @@ export class Client {
private messageId = 0; private messageId = 0;
private callbackId = 0; private callbackId = 0;
private readonly proxies = new Map<number | Module, ProxyData>(); private readonly proxies = new Map<number | Module, ProxyData>();
private readonly successEmitter = new Emitter<SuccessMessage>(); private readonly successEmitter = new Emitter<Method.Success>();
private readonly failEmitter = new Emitter<FailMessage>(); private readonly failEmitter = new Emitter<Method.Fail>();
private readonly eventEmitter = new Emitter<{ event: string; args: any[]; }>(); private readonly eventEmitter = new Emitter<{ event: string; args: any[]; }>();
private _initData: InitData | undefined; private _initData: InitData | undefined;
@@ -129,19 +129,13 @@ export class Client {
field("event listeners", this.eventEmitter.counts), field("event listeners", this.eventEmitter.counts),
]); ]);
const message = new FailMessage(); const message = new Method.Fail();
const error = new Error("disconnected"); const error = new Error("disconnected");
message.setResponse(stringify(error)); message.setResponse(argumentToProto(error));
this.failEmitter.emit(message); this.failEmitter.emit(message);
this.eventEmitter.emit({ event: "exit", args: [1] }); this.eventEmitter.emit({ event: "disconnected", args: [error] });
this.eventEmitter.emit({ event: "close", args: [] }); this.eventEmitter.emit({ event: "done", args: [] });
try {
this.eventEmitter.emit({ event: "error", args: [error] });
} catch (error) {
// If nothing is listening, EventEmitter will throw an error.
}
this.eventEmitter.emit({ event: "done", args: [true] });
}; };
connection.onDown(() => handleDisconnect()); connection.onDown(() => handleDisconnect());
@@ -149,6 +143,12 @@ export class Client {
clearTimeout(this.pingTimeout as any); clearTimeout(this.pingTimeout as any);
this.pingTimeout = undefined; this.pingTimeout = undefined;
handleDisconnect(); handleDisconnect();
this.proxies.clear();
this.successEmitter.dispose();
this.failEmitter.dispose();
this.eventEmitter.dispose();
this.initDataEmitter.dispose();
this.sharedProcessActiveEmitter.dispose();
}); });
connection.onUp(() => this.disconnected = false); connection.onUp(() => this.disconnected = false);
@@ -174,19 +174,29 @@ export class Client {
* Make a remote call for a proxy's method using proto. * Make a remote call for a proxy's method using proto.
*/ */
private remoteCall(proxyId: number | Module, method: string, args: any[]): Promise<any> { private remoteCall(proxyId: number | Module, method: string, args: any[]): Promise<any> {
if (this.disconnected) { if (this.disconnected && typeof proxyId === "number") {
return Promise.reject(new Error("disconnected")); // Can assume killing or closing works because a disconnected proxy
// is disposed on the server's side.
switch (method) {
case "close":
case "kill":
return Promise.resolve();
} }
const message = new MethodMessage(); return Promise.reject(
new Error(`Unable to call "${method}" on proxy ${proxyId}: disconnected`),
);
}
const message = new Method();
const id = this.messageId++; const id = this.messageId++;
let proxyMessage: NamedProxyMessage | NumberedProxyMessage; let proxyMessage: Method.Named | Method.Numbered;
if (typeof proxyId === "string") { if (typeof proxyId === "string") {
proxyMessage = new NamedProxyMessage(); proxyMessage = new Method.Named();
proxyMessage.setModule(moduleToProto(proxyId)); proxyMessage.setModule(moduleToProto(proxyId));
message.setNamedProxy(proxyMessage); message.setNamedProxy(proxyMessage);
} else { } else {
proxyMessage = new NumberedProxyMessage(); proxyMessage = new Method.Numbered();
proxyMessage.setProxyId(proxyId); proxyMessage.setProxyId(proxyId);
message.setNumberedProxy(proxyMessage); message.setNumberedProxy(proxyMessage);
} }
@@ -206,16 +216,14 @@ export class Client {
return callbackId; return callbackId;
}; };
const stringifiedArgs = args.map((a) => stringify(a, storeCallback));
logger.trace(() => [ logger.trace(() => [
"sending", "sending",
field("id", id), field("id", id),
field("proxyId", proxyId), field("proxyId", proxyId),
field("method", method), field("method", method),
field("args", stringifiedArgs),
]); ]);
proxyMessage.setArgsList(stringifiedArgs); proxyMessage.setArgsList(args.map((a) => argumentToProto(a, storeCallback)));
const clientMessage = new ClientMessage(); const clientMessage = new ClientMessage();
clientMessage.setMethod(message); clientMessage.setMethod(message);
@@ -237,12 +245,12 @@ export class Client {
const d1 = this.successEmitter.event(id, (message) => { const d1 = this.successEmitter.event(id, (message) => {
dispose(); dispose();
resolve(this.parse(message.getResponse())); resolve(this.protoToArgument(message.getResponse(), promise));
}); });
const d2 = this.failEmitter.event(id, (message) => { const d2 = this.failEmitter.event(id, (message) => {
dispose(); dispose();
reject(parse(message.getResponse())); reject(protoToArgument(message.getResponse()));
}); });
}); });
@@ -253,7 +261,8 @@ export class Client {
* Handle all messages from the server. * Handle all messages from the server.
*/ */
private async handleMessage(message: ServerMessage): Promise<void> { private async handleMessage(message: ServerMessage): Promise<void> {
if (message.hasInit()) { switch (message.getMsgCase()) {
case ServerMessage.MsgCase.INIT:
const init = message.getInit()!; const init = message.getInit()!;
this._initData = { this._initData = {
dataDirectory: init.getDataDirectory(), dataDirectory: init.getDataDirectory(),
@@ -262,33 +271,44 @@ export class Client {
workingDirectory: init.getWorkingDirectory(), workingDirectory: init.getWorkingDirectory(),
os: protoToOperatingSystem(init.getOperatingSystem()), os: protoToOperatingSystem(init.getOperatingSystem()),
shell: init.getShell(), shell: init.getShell(),
extensionsDirectory: init.getExtensionsDirectory(),
builtInExtensionsDirectory: init.getBuiltinExtensionsDir(), builtInExtensionsDirectory: init.getBuiltinExtensionsDir(),
}; };
this.initDataEmitter.emit(this._initData); this.initDataEmitter.emit(this._initData);
} else if (message.hasSuccess()) { break;
case ServerMessage.MsgCase.SUCCESS:
this.emitSuccess(message.getSuccess()!); this.emitSuccess(message.getSuccess()!);
} else if (message.hasFail()) { break;
case ServerMessage.MsgCase.FAIL:
this.emitFail(message.getFail()!); this.emitFail(message.getFail()!);
} else if (message.hasEvent()) { break;
case ServerMessage.MsgCase.EVENT:
await this.emitEvent(message.getEvent()!); await this.emitEvent(message.getEvent()!);
} else if (message.hasCallback()) { break;
case ServerMessage.MsgCase.CALLBACK:
await this.runCallback(message.getCallback()!); await this.runCallback(message.getCallback()!);
} else if (message.hasSharedProcessActive()) { break;
case ServerMessage.MsgCase.SHARED_PROCESS_ACTIVE:
const sharedProcessActiveMessage = message.getSharedProcessActive()!; const sharedProcessActiveMessage = message.getSharedProcessActive()!;
this.sharedProcessActiveEmitter.emit({ this.sharedProcessActiveEmitter.emit({
socketPath: sharedProcessActiveMessage.getSocketPath(), socketPath: sharedProcessActiveMessage.getSocketPath(),
logPath: sharedProcessActiveMessage.getLogPath(), logPath: sharedProcessActiveMessage.getLogPath(),
}); });
} else if (message.hasPong()) { break;
case ServerMessage.MsgCase.PONG:
// Nothing to do since pings are on a timer rather than waiting for the // Nothing to do since pings are on a timer rather than waiting for the
// next pong in case a message from either the client or server is dropped // next pong in case a message from either the client or server is dropped
// which would break the ping cycle. // which would break the ping cycle.
} else { break;
default:
throw new Error("unknown message type"); throw new Error("unknown message type");
} }
} }
private emitSuccess(message: SuccessMessage): void { /**
* Convert message to a success event.
*/
private emitSuccess(message: Method.Success): void {
logger.trace(() => [ logger.trace(() => [
"received resolve", "received resolve",
field("id", message.getId()), field("id", message.getId()),
@@ -297,7 +317,10 @@ export class Client {
this.successEmitter.emit(message.getId(), message); this.successEmitter.emit(message.getId(), message);
} }
private emitFail(message: FailMessage): void { /**
* Convert message to a fail event.
*/
private emitFail(message: Method.Fail): void {
logger.trace(() => [ logger.trace(() => [
"received reject", "received reject",
field("id", message.getId()), field("id", message.getId()),
@@ -313,7 +336,7 @@ export class Client {
* request before it emits. Instead, emit all events from the server so all * request before it emits. Instead, emit all events from the server so all
* events are always caught on the client. * events are always caught on the client.
*/ */
private async emitEvent(message: EventMessage): Promise<void> { private async emitEvent(message: Event): Promise<void> {
const eventMessage = message.getNamedEvent()! || message.getNumberedEvent()!; const eventMessage = message.getNamedEvent()! || message.getNumberedEvent()!;
const proxyId = message.getNamedEvent() const proxyId = message.getNamedEvent()
? protoToModule(message.getNamedEvent()!.getModule()) ? protoToModule(message.getNamedEvent()!.getModule())
@@ -324,10 +347,9 @@ export class Client {
"received event", "received event",
field("proxyId", proxyId), field("proxyId", proxyId),
field("event", event), field("event", event),
field("args", eventMessage.getArgsList()),
]); ]);
const args = eventMessage.getArgsList().map((a) => this.parse(a)); const args = eventMessage.getArgsList().map((a) => this.protoToArgument(a));
this.eventEmitter.emit(proxyId, { event, args }); this.eventEmitter.emit(proxyId, { event, args });
} }
@@ -339,7 +361,7 @@ export class Client {
* also only be used when passed together with the method. If they are sent * also only be used when passed together with the method. If they are sent
* afterward, they may never be called due to timing issues. * afterward, they may never be called due to timing issues.
*/ */
private async runCallback(message: CallbackMessage): Promise<void> { private async runCallback(message: Callback): Promise<void> {
const callbackMessage = message.getNamedCallback()! || message.getNumberedCallback()!; const callbackMessage = message.getNamedCallback()! || message.getNumberedCallback()!;
const proxyId = message.getNamedCallback() const proxyId = message.getNamedCallback()
? protoToModule(message.getNamedCallback()!.getModule()) ? protoToModule(message.getNamedCallback()!.getModule())
@@ -350,16 +372,15 @@ export class Client {
"running callback", "running callback",
field("proxyId", proxyId), field("proxyId", proxyId),
field("callbackId", callbackId), field("callbackId", callbackId),
field("args", callbackMessage.getArgsList()),
]); ]);
const args = callbackMessage.getArgsList().map((a) => this.parse(a)); const args = callbackMessage.getArgsList().map((a) => this.protoToArgument(a));
this.getProxy(proxyId).callbacks.get(callbackId)!(...args); this.getProxy(proxyId).callbacks.get(callbackId)!(...args);
} }
/** /**
* Start the ping loop. Does nothing if already pinging. * Start the ping loop. Does nothing if already pinging.
*/ */
private startPinging = (): void => { private readonly startPinging = (): void => {
if (typeof this.pingTimeout !== "undefined") { if (typeof this.pingTimeout !== "undefined") {
return; return;
} }
@@ -450,12 +471,12 @@ export class Client {
callbacks: new Map(), callbacks: new Map(),
}); });
instance.onDone((disconnected: boolean) => { instance.onDone(() => {
const log = (): void => { const log = (): void => {
logger.trace(() => [ logger.trace(() => [
typeof proxyId === "number" ? "disposed proxy" : "disposed proxy callbacks", typeof proxyId === "number" ? "disposed proxy" : "disposed proxy callbacks",
field("proxyId", proxyId), field("proxyId", proxyId),
field("disconnected", disconnected), field("disconnected", this.disconnected),
field("callbacks", Array.from(this.proxies.values()).reduce((count, proxy) => count + proxy.callbacks.size, 0)), field("callbacks", Array.from(this.proxies.values()).reduce((count, proxy) => count + proxy.callbacks.size, 0)),
field("success listeners", this.successEmitter.counts), field("success listeners", this.successEmitter.counts),
field("fail listeners", this.failEmitter.counts), field("fail listeners", this.failEmitter.counts),
@@ -471,7 +492,7 @@ export class Client {
this.eventEmitter.dispose(proxyId); this.eventEmitter.dispose(proxyId);
log(); log();
}; };
if (!disconnected) { if (!this.disconnected) {
instance.dispose().then(dispose).catch(dispose); instance.dispose().then(dispose).catch(dispose);
} else { } else {
dispose(); dispose();
@@ -496,10 +517,16 @@ export class Client {
await this.getProxy(proxyId).promise; await this.getProxy(proxyId).promise;
} }
private parse(value?: string, promise?: Promise<any>): any { /**
return parse(value, undefined, (id) => this.createProxy(id, promise)); * Same as protoToArgument except provides createProxy.
*/
private protoToArgument(value?: Argument, promise?: Promise<any>): any {
return protoToArgument(value, undefined, (id) => this.createProxy(id, promise));
} }
/**
* Get a proxy. Error if it doesn't exist.
*/
private getProxy(proxyId: number | Module): ProxyData { private getProxy(proxyId: number | Module): ProxyData {
if (!this.proxies.has(proxyId)) { if (!this.proxies.has(proxyId)) {
throw new Error(`proxy ${proxyId} disposed too early`); throw new Error(`proxy ${proxyId} disposed too early`);

View File

@@ -6,6 +6,8 @@ import { ClientProxy } from "../../common/proxy";
import { ChildProcessModuleProxy, ChildProcessProxy, ChildProcessProxies } from "../../node/modules/child_process"; import { ChildProcessModuleProxy, ChildProcessProxy, ChildProcessProxies } from "../../node/modules/child_process";
import { Readable, Writable } from "./stream"; import { Readable, Writable } from "./stream";
// tslint:disable completed-docs
export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.ChildProcess { export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.ChildProcess {
public readonly stdin: stream.Writable; public readonly stdin: stream.Writable;
public readonly stdout: stream.Readable; public readonly stdout: stream.Readable;
@@ -23,10 +25,10 @@ export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.C
this.stderr = new Readable(proxyPromises.then((p) => p.stderr!)); this.stderr = new Readable(proxyPromises.then((p) => p.stderr!));
this.stdio = [this.stdin, this.stdout, this.stderr]; this.stdio = [this.stdin, this.stdout, this.stderr];
this.proxy.getPid().then((pid) => { this.catch(this.proxy.getPid().then((pid) => {
this._pid = pid; this._pid = pid;
this._connected = true; this._connected = true;
}); }));
this.on("disconnect", () => this._connected = false); this.on("disconnect", () => this._connected = false);
this.on("exit", () => { this.on("exit", () => {
this._connected = false; this._connected = false;
@@ -48,19 +50,19 @@ export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.C
public kill(): void { public kill(): void {
this._killed = true; this._killed = true;
this.proxy.kill(); this.catch(this.proxy.kill());
} }
public disconnect(): void { public disconnect(): void {
this.proxy.disconnect(); this.catch(this.proxy.disconnect());
} }
public ref(): void { public ref(): void {
this.proxy.ref(); this.catch(this.proxy.ref());
} }
public unref(): void { public unref(): void {
this.proxy.unref(); this.catch(this.proxy.unref());
} }
public send( public send(
@@ -87,6 +89,14 @@ export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.C
return true; // Always true since we can't get this synchronously. return true; // Always true since we can't get this synchronously.
} }
/**
* Exit and close the process when disconnected.
*/
protected handleDisconnect(): void {
this.emit("exit", 1);
this.emit("close");
}
} }
export class ChildProcessModule { export class ChildProcessModule {

View File

@@ -1,15 +1,50 @@
import * as fs from "fs"; import * as fs from "fs";
import { callbackify } from "util"; import { callbackify } from "util";
import { ClientProxy } from "../../common/proxy"; import { ClientProxy, Batch } from "../../common/proxy";
import { IEncodingOptions, IEncodingOptionsCallback } from "../../common/util"; import { IEncodingOptions, IEncodingOptionsCallback } from "../../common/util";
import { FsModuleProxy, Stats as IStats, WatcherProxy, WriteStreamProxy } from "../../node/modules/fs"; import { FsModuleProxy, Stats as IStats, WatcherProxy, WriteStreamProxy } from "../../node/modules/fs";
import { Writable } from "./stream"; import { Writable } from "./stream";
// tslint:disable no-any // tslint:disable no-any
// tslint:disable completed-docs
class StatBatch extends Batch<IStats, { path: fs.PathLike }> {
public constructor(private readonly proxy: FsModuleProxy) {
super();
}
protected remoteCall(batch: { path: fs.PathLike }[]): Promise<(IStats | Error)[]> {
return this.proxy.statBatch(batch);
}
}
class LstatBatch extends Batch<IStats, { path: fs.PathLike }> {
public constructor(private readonly proxy: FsModuleProxy) {
super();
}
protected remoteCall(batch: { path: fs.PathLike }[]): Promise<(IStats | Error)[]> {
return this.proxy.lstatBatch(batch);
}
}
class ReaddirBatch extends Batch<Buffer[] | fs.Dirent[] | string[], { path: fs.PathLike, options: IEncodingOptions }> {
public constructor(private readonly proxy: FsModuleProxy) {
super();
}
protected remoteCall(queue: { path: fs.PathLike, options: IEncodingOptions }[]): Promise<(Buffer[] | fs.Dirent[] | string[] | Error)[]> {
return this.proxy.readdirBatch(queue);
}
}
class Watcher extends ClientProxy<WatcherProxy> implements fs.FSWatcher { class Watcher extends ClientProxy<WatcherProxy> implements fs.FSWatcher {
public close(): void { public close(): void {
this.proxy.close(); this.catch(this.proxy.close());
}
protected handleDisconnect(): void {
this.emit("close");
} }
} }
@@ -23,12 +58,20 @@ class WriteStream extends Writable<WriteStreamProxy> implements fs.WriteStream {
} }
public close(): void { public close(): void {
this.proxy.close(); this.catch(this.proxy.close());
} }
} }
export class FsModule { export class FsModule {
public constructor(private readonly proxy: FsModuleProxy) {} private readonly statBatch: StatBatch;
private readonly lstatBatch: LstatBatch;
private readonly readdirBatch: ReaddirBatch;
public constructor(private readonly proxy: FsModuleProxy) {
this.statBatch = new StatBatch(this.proxy);
this.lstatBatch = new LstatBatch(this.proxy);
this.readdirBatch = new ReaddirBatch(this.proxy);
}
public access = (path: fs.PathLike, mode: number | undefined | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => { public access = (path: fs.PathLike, mode: number | undefined | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof mode === "function") { if (typeof mode === "function") {
@@ -72,9 +115,7 @@ export class FsModule {
} }
public exists = (path: fs.PathLike, callback: (exists: boolean) => void): void => { public exists = (path: fs.PathLike, callback: (exists: boolean) => void): void => {
callbackify(this.proxy.exists)(path, (exists) => { this.proxy.exists(path).then((exists) => callback(exists)).catch(() => callback(false));
callback!(exists as any);
});
} }
public fchmod = (fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => { public fchmod = (fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
@@ -124,7 +165,7 @@ export class FsModule {
} }
public lstat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => { public lstat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
callbackify(this.proxy.lstat)(path, (error, stats) => { callbackify(this.lstatBatch.add)({ path }, (error, stats) => {
callback(error, stats && new Stats(stats)); callback(error, stats && new Stats(stats));
}); });
} }
@@ -175,7 +216,7 @@ export class FsModule {
callback = options; callback = options;
options = undefined; options = undefined;
} }
callbackify(this.proxy.readdir)(path, options, callback!); callbackify(this.readdirBatch.add)({ path, options }, callback!);
} }
public readlink = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, linkString: string | Buffer) => void): void => { public readlink = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, linkString: string | Buffer) => void): void => {
@@ -203,7 +244,7 @@ export class FsModule {
} }
public stat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => { public stat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
callbackify(this.proxy.stat)(path, (error, stats) => { callbackify(this.statBatch.add)({ path }, (error, stats) => {
callback(error, stats && new Stats(stats)); callback(error, stats && new Stats(stats));
}); });
} }

View File

@@ -4,6 +4,8 @@ import { ClientProxy } from "../../common/proxy";
import { NetModuleProxy, NetServerProxy, NetSocketProxy } from "../../node/modules/net"; import { NetModuleProxy, NetServerProxy, NetSocketProxy } from "../../node/modules/net";
import { Duplex } from "./stream"; import { Duplex } from "./stream";
// tslint:disable completed-docs
export class Socket extends Duplex<NetSocketProxy> implements net.Socket { export class Socket extends Duplex<NetSocketProxy> implements net.Socket {
private _connecting: boolean = false; private _connecting: boolean = false;
private _destroyed: boolean = false; private _destroyed: boolean = false;
@@ -29,9 +31,8 @@ export class Socket extends Duplex<NetSocketProxy> implements net.Socket {
if (callback) { if (callback) {
this.on("connect", callback as () => void); this.on("connect", callback as () => void);
} }
this.proxy.connect(options, host);
return this; return this.catch(this.proxy.connect(options, host));
} }
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
@@ -117,24 +118,30 @@ export class Socket extends Duplex<NetSocketProxy> implements net.Socket {
} }
public unref(): void { public unref(): void {
this.proxy.unref(); this.catch(this.proxy.unref());
} }
public ref(): void { public ref(): void {
this.proxy.ref(); this.catch(this.proxy.ref());
} }
} }
export class Server extends ClientProxy<NetServerProxy> implements net.Server { export class Server extends ClientProxy<NetServerProxy> implements net.Server {
private socketId = 0;
private readonly sockets = new Map<number, net.Socket>(); private readonly sockets = new Map<number, net.Socket>();
private _listening: boolean = false; private _listening: boolean = false;
public constructor(proxyPromise: Promise<NetServerProxy> | NetServerProxy) { public constructor(proxyPromise: Promise<NetServerProxy> | NetServerProxy) {
super(proxyPromise); super(proxyPromise);
this.proxy.onConnection((socketProxy) => { this.catch(this.proxy.onConnection((socketProxy) => {
this.emit("connection", new Socket(socketProxy)); const socket = new Socket(socketProxy);
}); const socketId = this.socketId++;
this.sockets.set(socketId, socket);
socket.on("error", () => this.sockets.delete(socketId));
socket.on("close", () => this.sockets.delete(socketId));
this.emit("connection", socket);
}));
this.on("listening", () => this._listening = true); this.on("listening", () => this._listening = true);
this.on("error", () => this._listening = false); this.on("error", () => this._listening = false);
@@ -154,9 +161,7 @@ export class Server extends ClientProxy<NetServerProxy> implements net.Server {
this.on("listening", callback as () => void); this.on("listening", callback as () => void);
} }
this.proxy.listen(handle, hostname, backlog); return this.catch(this.proxy.listen(handle, hostname, backlog));
return this;
} }
public get connections(): number { public get connections(): number {
@@ -180,26 +185,25 @@ export class Server extends ClientProxy<NetServerProxy> implements net.Server {
if (callback) { if (callback) {
this.on("close", callback); this.on("close", callback);
} }
this.proxy.close();
return this; return this.catch(this.proxy.close());
} }
public ref(): this { public ref(): this {
this.proxy.ref(); return this.catch(this.proxy.ref());
return this;
} }
public unref(): this { public unref(): this {
this.proxy.unref(); return this.catch(this.proxy.unref());
return this;
} }
public getConnections(cb: (error: Error | null, count: number) => void): void { public getConnections(cb: (error: Error | null, count: number) => void): void {
cb(null, this.sockets.size); cb(null, this.sockets.size);
} }
protected handleDisconnect(): void {
this.emit("close");
}
} }
type NodeNet = typeof net; type NodeNet = typeof net;

View File

@@ -2,17 +2,28 @@ import * as pty from "node-pty";
import { ClientProxy } from "../../common/proxy"; import { ClientProxy } from "../../common/proxy";
import { NodePtyModuleProxy, NodePtyProcessProxy } from "../../node/modules/node-pty"; import { NodePtyModuleProxy, NodePtyProcessProxy } from "../../node/modules/node-pty";
// tslint:disable completed-docs
export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements pty.IPty { export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements pty.IPty {
private _pid = -1; private _pid = -1;
private _process = ""; private _process = "";
public constructor(proxyPromise: Promise<NodePtyProcessProxy>) { public constructor(
super(proxyPromise); private readonly moduleProxy: NodePtyModuleProxy,
this.proxy.getPid().then((pid) => this._pid = pid); private readonly file: string,
this.proxy.getProcess().then((process) => this._process = process); private readonly args: string[] | string,
private readonly options: pty.IPtyForkOptions,
) {
super(moduleProxy.spawn(file, args, options));
this.on("process", (process) => this._process = process); this.on("process", (process) => this._process = process);
} }
protected initialize(proxyPromise: Promise<NodePtyProcessProxy>): void {
super.initialize(proxyPromise);
this.catch(this.proxy.getPid().then((p) => this._pid = p));
this.catch(this.proxy.getProcess().then((p) => this._process = p));
}
public get pid(): number { public get pid(): number {
return this._pid; return this._pid;
} }
@@ -22,15 +33,21 @@ export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements
} }
public resize(columns: number, rows: number): void { public resize(columns: number, rows: number): void {
this.proxy.resize(columns, rows); this.catch(this.proxy.resize(columns, rows));
} }
public write(data: string): void { public write(data: string): void {
this.proxy.write(data); this.catch(this.proxy.write(data));
} }
public kill(signal?: string): void { public kill(signal?: string): void {
this.proxy.kill(signal); this.catch(this.proxy.kill(signal));
}
protected handleDisconnect(): void {
this._process += " (disconnected)";
this.emit("data", "\r\n\nLost connection...\r\n\n");
this.initialize(this.moduleProxy.spawn(this.file, this.args, this.options));
} }
} }
@@ -40,6 +57,6 @@ export class NodePtyModule implements NodePty {
public constructor(private readonly proxy: NodePtyModuleProxy) {} public constructor(private readonly proxy: NodePtyModuleProxy) {}
public spawn = (file: string, args: string[] | string, options: pty.IPtyForkOptions): pty.IPty => { public spawn = (file: string, args: string[] | string, options: pty.IPtyForkOptions): pty.IPty => {
return new NodePtyProcess(this.proxy.spawn(file, args, options)); return new NodePtyProcess(this.proxy, file, args, options);
} }
} }

View File

@@ -2,17 +2,33 @@ import * as spdlog from "spdlog";
import { ClientProxy } from "../../common/proxy"; import { ClientProxy } from "../../common/proxy";
import { RotatingLoggerProxy, SpdlogModuleProxy } from "../../node/modules/spdlog"; import { RotatingLoggerProxy, SpdlogModuleProxy } from "../../node/modules/spdlog";
// tslint:disable completed-docs
class RotatingLogger extends ClientProxy<RotatingLoggerProxy> implements spdlog.RotatingLogger { class RotatingLogger extends ClientProxy<RotatingLoggerProxy> implements spdlog.RotatingLogger {
public async trace (message: string): Promise<void> { this.proxy.trace(message); } public constructor(
public async debug (message: string): Promise<void> { this.proxy.debug(message); } private readonly moduleProxy: SpdlogModuleProxy,
public async info (message: string): Promise<void> { this.proxy.info(message); } private readonly name: string,
public async warn (message: string): Promise<void> { this.proxy.warn(message); } private readonly filename: string,
public async error (message: string): Promise<void> { this.proxy.error(message); } private readonly filesize: number,
public async critical (message: string): Promise<void> { this.proxy.critical(message); } private readonly filecount: number,
public async setLevel (level: number): Promise<void> { this.proxy.setLevel(level); } ) {
public async clearFormatters (): Promise<void> { this.proxy.clearFormatters(); } super(moduleProxy.createLogger(name, filename, filesize, filecount));
public async flush (): Promise<void> { this.proxy.flush(); } }
public async drop (): Promise<void> { this.proxy.drop(); }
public trace (message: string): void { this.catch(this.proxy.trace(message)); }
public debug (message: string): void { this.catch(this.proxy.debug(message)); }
public info (message: string): void { this.catch(this.proxy.info(message)); }
public warn (message: string): void { this.catch(this.proxy.warn(message)); }
public error (message: string): void { this.catch(this.proxy.error(message)); }
public critical (message: string): void { this.catch(this.proxy.critical(message)); }
public setLevel (level: number): void { this.catch(this.proxy.setLevel(level)); }
public clearFormatters (): void { this.catch(this.proxy.clearFormatters()); }
public flush (): void { this.catch(this.proxy.flush()); }
public drop (): void { this.catch(this.proxy.drop()); }
protected handleDisconnect(): void {
this.initialize(this.moduleProxy.createLogger(this.name, this.filename, this.filesize, this.filecount));
}
} }
export class SpdlogModule { export class SpdlogModule {
@@ -21,12 +37,12 @@ export class SpdlogModule {
public constructor(private readonly proxy: SpdlogModuleProxy) { public constructor(private readonly proxy: SpdlogModuleProxy) {
this.RotatingLogger = class extends RotatingLogger { this.RotatingLogger = class extends RotatingLogger {
public constructor(name: string, filename: string, filesize: number, filecount: number) { public constructor(name: string, filename: string, filesize: number, filecount: number) {
super(proxy.createLogger(name, filename, filesize, filecount)); super(proxy, name, filename, filesize, filecount);
} }
}; };
} }
public setAsyncMode = (bufferSize: number, flushInterval: number): void => { public setAsyncMode = (bufferSize: number, flushInterval: number): Promise<void> => {
this.proxy.setAsyncMode(bufferSize, flushInterval); return this.proxy.setAsyncMode(bufferSize, flushInterval);
} }
} }

View File

@@ -3,6 +3,8 @@ import { callbackify } from "util";
import { ClientProxy } from "../../common/proxy"; import { ClientProxy } from "../../common/proxy";
import { DuplexProxy, IReadableProxy, WritableProxy } from "../../node/modules/stream"; import { DuplexProxy, IReadableProxy, WritableProxy } from "../../node/modules/stream";
// tslint:disable completed-docs
export class Writable<T extends WritableProxy = WritableProxy> extends ClientProxy<T> implements stream.Writable { export class Writable<T extends WritableProxy = WritableProxy> extends ClientProxy<T> implements stream.Writable {
public get writable(): boolean { public get writable(): boolean {
throw new Error("not implemented"); throw new Error("not implemented");
@@ -41,13 +43,11 @@ export class Writable<T extends WritableProxy = WritableProxy> extends ClientPro
} }
public destroy(): void { public destroy(): void {
this.proxy.destroy(); this.catch(this.proxy.destroy());
} }
public setDefaultEncoding(encoding: string): this { public setDefaultEncoding(encoding: string): this {
this.proxy.setDefaultEncoding(encoding); return this.catch(this.proxy.setDefaultEncoding(encoding));
return this;
} }
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
@@ -81,6 +81,11 @@ export class Writable<T extends WritableProxy = WritableProxy> extends ClientPro
} }
}); });
} }
protected handleDisconnect(): void {
this.emit("close");
this.emit("finish");
}
} }
export class Readable<T extends IReadableProxy = IReadableProxy> extends ClientProxy<T> implements stream.Readable { export class Readable<T extends IReadableProxy = IReadableProxy> extends ClientProxy<T> implements stream.Readable {
@@ -146,13 +151,16 @@ export class Readable<T extends IReadableProxy = IReadableProxy> extends ClientP
} }
public destroy(): void { public destroy(): void {
this.proxy.destroy(); this.catch(this.proxy.destroy());
} }
public setEncoding(encoding: string): this { public setEncoding(encoding: string): this {
this.proxy.setEncoding(encoding); return this.catch(this.proxy.setEncoding(encoding));
}
return this; protected handleDisconnect(): void {
this.emit("close");
this.emit("end");
} }
} }
@@ -226,8 +234,11 @@ export class Duplex<T extends DuplexProxy = DuplexProxy> extends Writable<T> imp
} }
public setEncoding(encoding: string): this { public setEncoding(encoding: string): this {
this.proxy.setEncoding(encoding); return this.catch(this.proxy.setEncoding(encoding));
}
return this; protected handleDisconnect(): void {
super.handleDisconnect();
this.emit("end");
} }
} }

View File

@@ -1,6 +1,8 @@
import * as trash from "trash"; import * as trash from "trash";
import { TrashModuleProxy } from "../../node/modules/trash"; import { TrashModuleProxy } from "../../node/modules/trash";
// tslint:disable completed-docs
export class TrashModule { export class TrashModule {
public constructor(private readonly proxy: TrashModuleProxy) {} public constructor(private readonly proxy: TrashModuleProxy) {}

View File

@@ -23,6 +23,7 @@ export interface InitData {
readonly homeDirectory: string; readonly homeDirectory: string;
readonly tmpDirectory: string; readonly tmpDirectory: string;
readonly shell: string; readonly shell: string;
readonly extensionsDirectory: string;
readonly builtInExtensionsDirectory: string; readonly builtInExtensionsDirectory: string;
} }

View File

@@ -29,21 +29,66 @@ const unpromisify = <T extends ServerProxy>(proxyPromise: Promise<T>): T => {
* need a bunch of `then` calls everywhere. * need a bunch of `then` calls everywhere.
*/ */
export abstract class ClientProxy<T extends ServerProxy> extends EventEmitter { export abstract class ClientProxy<T extends ServerProxy> extends EventEmitter {
protected readonly proxy: T; private _proxy: T | undefined;
/** /**
* You can specify not to bind events in order to avoid emitting twice for * You can specify not to bind events in order to avoid emitting twice for
* duplex streams. * duplex streams.
*/ */
public constructor(proxyPromise: Promise<T> | T, bindEvents: boolean = true) { public constructor(
proxyPromise: Promise<T> | T,
private readonly bindEvents: boolean = true,
) {
super(); super();
this.proxy = isPromise(proxyPromise) ? unpromisify(proxyPromise) : proxyPromise; this.initialize(proxyPromise);
if (bindEvents) { if (this.bindEvents) {
this.proxy.onEvent((event, ...args): void => { this.on("disconnected", (error) => {
this.emit(event, ...args); try {
this.emit("error", error);
} catch (error) {
// If nothing is listening, EventEmitter will throw an error.
}
this.handleDisconnect();
}); });
} }
} }
protected get proxy(): T {
if (!this._proxy) {
throw new Error("not initialized");
}
return this._proxy;
}
/**
* Initialize the proxy by unpromisifying if necessary and binding to its
* events.
*/
protected initialize(proxyPromise: Promise<T> | T): void {
this._proxy = isPromise(proxyPromise) ? unpromisify(proxyPromise) : proxyPromise;
if (this.bindEvents) {
this.catch(this.proxy.onEvent((event, ...args): void => {
this.emit(event, ...args);
}));
}
}
/**
* Perform necessary cleanup on disconnect (or reconnect).
*/
protected abstract handleDisconnect(): void;
/**
* Emit an error event if the promise errors.
*/
protected catch(promise?: Promise<any>): this {
if (promise) {
promise.catch((e) => this.emit("error", e));
}
return this;
}
} }
/** /**
@@ -54,6 +99,9 @@ export abstract class ClientProxy<T extends ServerProxy> extends EventEmitter {
* from those child proxies and fail to dispose them properly. * from those child proxies and fail to dispose them properly.
*/ */
export interface ServerProxy { export interface ServerProxy {
/**
* Dispose the proxy.
*/
dispose(): Promise<void>; dispose(): Promise<void>;
/** /**
@@ -73,6 +121,9 @@ export interface ServerProxy {
onEvent(cb: (event: string, ...args: any[]) => void): Promise<void>; onEvent(cb: (event: string, ...args: any[]) => void): Promise<void>;
} }
/**
* Supported top-level module proxies.
*/
export enum Module { export enum Module {
Fs = "fs", Fs = "fs",
ChildProcess = "child_process", ChildProcess = "child_process",
@@ -81,3 +132,80 @@ export enum Module {
NodePty = "node-pty", NodePty = "node-pty",
Trash = "trash", Trash = "trash",
} }
interface BatchItem<T, A> {
args: A;
resolve: (t: T) => void;
reject: (e: Error) => void;
}
/**
* Batch remote calls.
*/
export abstract class Batch<T, A> {
private idleTimeout: number | NodeJS.Timer | undefined;
private maxTimeout: number | NodeJS.Timer | undefined;
private batch = <BatchItem<T, A>[]>[];
public constructor(
/**
* Flush after reaching this amount of time.
*/
private readonly maxTime: number = 1000,
/**
* Flush after reaching this count.
*/
private readonly maxCount: number = 100,
/**
* Flush after not receiving more requests for this amount of time.
*/
private readonly idleTime: number = 100,
) {}
public add = (args: A): Promise<T> => {
return new Promise((resolve, reject): void => {
this.batch.push({
args,
resolve,
reject,
});
if (this.batch.length >= this.maxCount) {
this.flush();
} else {
clearTimeout(this.idleTimeout as any);
this.idleTimeout = setTimeout(this.flush, this.idleTime);
if (typeof this.maxTimeout === "undefined") {
this.maxTimeout = setTimeout(this.flush, this.maxTime);
}
}
});
}
/**
* Perform remote call for a batch.
*/
protected abstract remoteCall(batch: A[]): Promise<(T | Error)[]>;
/**
* Flush out the current batch.
*/
private readonly flush = (): void => {
clearTimeout(this.idleTimeout as any);
clearTimeout(this.maxTimeout as any);
this.maxTimeout = undefined;
const batch = this.batch;
this.batch = [];
this.remoteCall(batch.map((q) => q.args)).then((results) => {
batch.forEach((item, i) => {
const result = results[i];
if (result && result instanceof Error) {
item.reject(result);
} else {
item.resolve(result);
}
});
}).catch((error) => batch.forEach((item) => item.reject(error)));
}
}

View File

@@ -1,4 +1,4 @@
import { Module as ProtoModule, WorkingInitMessage } from "../proto"; import { Argument, Module as ProtoModule, WorkingInit } from "../proto";
import { OperatingSystem } from "../common/connection"; import { OperatingSystem } from "../common/connection";
import { Module, ServerProxy } from "./proxy"; import { Module, ServerProxy } from "./proxy";
@@ -29,227 +29,144 @@ export type IEncodingOptions = {
export type IEncodingOptionsCallback = IEncodingOptions | ((err: NodeJS.ErrnoException, ...args: any[]) => void); export type IEncodingOptionsCallback = IEncodingOptions | ((err: NodeJS.ErrnoException, ...args: any[]) => void);
interface StringifiedError {
type: "error";
data: {
message: string;
stack?: string;
code?: string;
};
}
interface StringifiedBuffer {
type: "buffer";
data: number[];
}
interface StringifiedObject {
type: "object";
data: { [key: string]: StringifiedValue };
}
interface StringifiedArray {
type: "array";
data: StringifiedValue[];
}
interface StringifiedProxy {
type: "proxy";
data: {
id: number;
};
}
interface StringifiedFunction {
type: "function";
data: {
id: number;
};
}
interface StringifiedUndefined {
type: "undefined";
}
type StringifiedValue = StringifiedFunction | StringifiedProxy
| StringifiedUndefined | StringifiedObject | StringifiedArray
| StringifiedBuffer | StringifiedError | number | string | boolean | null;
const isPrimitive = (value: any): value is number | string | boolean | null => {
return typeof value === "number"
|| typeof value === "string"
|| typeof value === "boolean"
|| value === null;
};
/** /**
* Stringify an argument or a return value. * Convert an argument to proto.
* If sending a function is possible, provide `storeFunction`. * If sending a function is possible, provide `storeFunction`.
* If sending a proxy is possible, provide `storeProxy`. * If sending a proxy is possible, provide `storeProxy`.
*/ */
export const stringify = ( export const argumentToProto = (
value: any, value: any,
storeFunction?: (fn: () => void) => number, storeFunction?: (fn: () => void) => number,
storeProxy?: (proxy: ServerProxy) => number, storeProxy?: (proxy: ServerProxy) => number,
): string => { ): Argument => {
const convert = (currentValue: any): StringifiedValue => { const convert = (currentValue: any): Argument => {
// Errors don't stringify at all. They just become "{}". const message = new Argument();
// For some reason when running in Jest errors aren't instances of Error,
// so also check against the values.
if (currentValue instanceof Error if (currentValue instanceof Error
|| (currentValue && typeof currentValue.message !== "undefined" || (currentValue && typeof currentValue.message !== "undefined"
&& typeof currentValue.stack !== "undefined")) { && typeof currentValue.stack !== "undefined")) {
return { const arg = new Argument.ErrorValue();
type: "error", arg.setMessage(currentValue.message);
data: { arg.setStack(currentValue.stack);
message: currentValue.message, arg.setCode(currentValue.code);
stack: currentValue.stack, message.setError(arg);
code: (currentValue as NodeJS.ErrnoException).code, } else if (currentValue instanceof Uint8Array || currentValue instanceof Buffer) {
}, const arg = new Argument.BufferValue();
}; arg.setData(currentValue);
} message.setBuffer(arg);
} else if (Array.isArray(currentValue)) {
// With stringify, Uint8Array gets turned into objects with each index const arg = new Argument.ArrayValue();
// becoming a key for some reason. Then trying to do something like write arg.setDataList(currentValue.map(convert));
// that data results in [object Object] being written. Stringify them like message.setArray(arg);
// a Buffer instead. Also handle Buffer so it doesn't get caught by the } else if (isProxy(currentValue)) {
// object check and to get the same type.
if (currentValue instanceof Uint8Array || currentValue instanceof Buffer) {
return {
type: "buffer",
data: Array.from(currentValue),
};
}
if (Array.isArray(currentValue)) {
return {
type: "array",
data: currentValue.map((a) => convert(a)),
};
}
if (isProxy(currentValue)) {
if (!storeProxy) { if (!storeProxy) {
throw new Error("no way to serialize proxy"); throw new Error("no way to serialize proxy");
} }
const arg = new Argument.ProxyValue();
return { arg.setId(storeProxy(currentValue));
type: "proxy", message.setProxy(arg);
data: { } else if (currentValue !== null && typeof currentValue === "object") {
id: storeProxy(currentValue), const arg = new Argument.ObjectValue();
}, const map = arg.getDataMap();
};
}
if (currentValue !== null && typeof currentValue === "object") {
const converted: { [key: string]: StringifiedValue } = {};
Object.keys(currentValue).forEach((key) => { Object.keys(currentValue).forEach((key) => {
converted[key] = convert(currentValue[key]); map.set(key, convert(currentValue[key]));
}); });
message.setObject(arg);
return { } else if (currentValue === null) {
type: "object", message.setNull(new Argument.NullValue());
data: converted, } else {
}; switch (typeof currentValue) {
} case "undefined":
message.setUndefined(new Argument.UndefinedValue());
// `undefined` can't be stringified. break;
if (typeof currentValue === "undefined") { case "function":
return {
type: "undefined",
};
}
if (typeof currentValue === "function") {
if (!storeFunction) { if (!storeFunction) {
throw new Error("no way to serialize function"); throw new Error("no way to serialize function");
} }
const arg = new Argument.FunctionValue();
return { arg.setId(storeFunction(currentValue));
type: "function", message.setFunction(arg);
data: { break;
id: storeFunction(currentValue), case "number":
}, message.setNumber(currentValue);
}; break;
case "string":
message.setString(currentValue);
break;
case "boolean":
message.setBoolean(currentValue);
break;
default:
throw new Error(`cannot convert ${typeof currentValue} to proto`);
}
} }
if (!isPrimitive(currentValue)) { return message;
throw new Error(`cannot stringify ${typeof currentValue}`);
}
return currentValue;
}; };
return JSON.stringify(convert(value)); return convert(value);
}; };
/** /**
* Parse an argument. * Convert proto to an argument.
* If running a remote callback is supported, provide `runCallback`. * If running a remote callback is supported, provide `runCallback`.
* If using a remote proxy is supported, provide `createProxy`. * If using a remote proxy is supported, provide `createProxy`.
*/ */
export const parse = ( export const protoToArgument = (
value?: string, message?: Argument,
runCallback?: (id: number, args: any[]) => void, runCallback?: (id: number, args: any[]) => void,
createProxy?: (id: number) => ServerProxy, createProxy?: (id: number) => ServerProxy,
): any => { ): any => {
const convert = (currentValue: StringifiedValue): any => { const convert = (currentMessage: Argument): any => {
if (currentValue && !isPrimitive(currentValue)) { switch (currentMessage.getMsgCase()) {
// Would prefer a switch but the types don't seem to work. case Argument.MsgCase.ERROR:
if (currentValue.type === "buffer") { const errorMessage = currentMessage.getError()!;
return Buffer.from(currentValue.data); const error = new Error(errorMessage.getMessage());
} (error as NodeJS.ErrnoException).code = errorMessage.getCode();
(error as any).originalStack = errorMessage.getStack();
if (currentValue.type === "error") {
const error = new Error(currentValue.data.message);
if (typeof currentValue.data.code !== "undefined") {
(error as NodeJS.ErrnoException).code = currentValue.data.code;
}
(error as any).originalStack = currentValue.data.stack;
return error; return error;
case Argument.MsgCase.BUFFER:
return Buffer.from(currentMessage.getBuffer()!.getData() as Uint8Array);
case Argument.MsgCase.ARRAY:
return currentMessage.getArray()!.getDataList().map((a) => convert(a));
case Argument.MsgCase.PROXY:
if (!createProxy) {
throw new Error("no way to create proxy");
} }
if (currentValue.type === "object") { return createProxy(currentMessage.getProxy()!.getId());
const converted: { [key: string]: any } = {}; case Argument.MsgCase.OBJECT:
Object.keys(currentValue.data).forEach((key) => { const obj: { [Key: string]: any } = {};
converted[key] = convert(currentValue.data[key]); currentMessage.getObject()!.getDataMap().forEach((argument, key) => {
obj[key] = convert(argument);
}); });
return converted; return obj;
} case Argument.MsgCase.UNDEFINED:
if (currentValue.type === "array") {
return currentValue.data.map(convert);
}
if (currentValue.type === "undefined") {
return undefined; return undefined;
} case Argument.MsgCase.NULL:
return null;
if (currentValue.type === "function") { case Argument.MsgCase.FUNCTION:
if (!runCallback) { if (!runCallback) {
throw new Error("no way to run remote callback"); throw new Error("no way to run remote callback");
} }
return (...args: any[]): void => { return (...args: any[]): void => {
return runCallback(currentValue.data.id, args); return runCallback(currentMessage.getFunction()!.getId(), args);
}; };
case Argument.MsgCase.NUMBER:
return currentMessage.getNumber();
case Argument.MsgCase.STRING:
return currentMessage.getString();
case Argument.MsgCase.BOOLEAN:
return currentMessage.getBoolean();
default:
throw new Error("cannot convert unexpected proto to argument");
} }
if (currentValue.type === "proxy") {
if (!createProxy) {
throw new Error("no way to create proxy");
}
return createProxy(currentValue.data.id);
}
}
return currentValue;
}; };
return value && convert(JSON.parse(value)); return message && convert(message);
}; };
export const protoToModule = (protoModule: ProtoModule): Module => { export const protoToModule = (protoModule: ProtoModule): Module => {
@@ -276,20 +193,20 @@ export const moduleToProto = (moduleName: Module): ProtoModule => {
} }
}; };
export const protoToOperatingSystem = (protoOp: WorkingInitMessage.OperatingSystem): OperatingSystem => { export const protoToOperatingSystem = (protoOp: WorkingInit.OperatingSystem): OperatingSystem => {
switch (protoOp) { switch (protoOp) {
case WorkingInitMessage.OperatingSystem.WINDOWS: return OperatingSystem.Windows; case WorkingInit.OperatingSystem.WINDOWS: return OperatingSystem.Windows;
case WorkingInitMessage.OperatingSystem.LINUX: return OperatingSystem.Linux; case WorkingInit.OperatingSystem.LINUX: return OperatingSystem.Linux;
case WorkingInitMessage.OperatingSystem.MAC: return OperatingSystem.Mac; case WorkingInit.OperatingSystem.MAC: return OperatingSystem.Mac;
default: throw new Error(`unsupported operating system ${protoOp}`); default: throw new Error(`unsupported operating system ${protoOp}`);
} }
}; };
export const platformToProto = (platform: NodeJS.Platform): WorkingInitMessage.OperatingSystem => { export const platformToProto = (platform: NodeJS.Platform): WorkingInit.OperatingSystem => {
switch (platform) { switch (platform) {
case "win32": return WorkingInitMessage.OperatingSystem.WINDOWS; case "win32": return WorkingInit.OperatingSystem.WINDOWS;
case "linux": return WorkingInitMessage.OperatingSystem.LINUX; case "linux": return WorkingInit.OperatingSystem.LINUX;
case "darwin": return WorkingInitMessage.OperatingSystem.MAC; case "darwin": return WorkingInit.OperatingSystem.MAC;
default: throw new Error(`unrecognized platform "${platform}"`); default: throw new Error(`unrecognized platform "${platform}"`);
} }
}; };

View File

@@ -3,6 +3,8 @@ import { ServerProxy } from "../../common/proxy";
import { preserveEnv } from "../../common/util"; import { preserveEnv } from "../../common/util";
import { WritableProxy, ReadableProxy } from "./stream"; import { WritableProxy, ReadableProxy } from "./stream";
// tslint:disable completed-docs
export type ForkProvider = (modulePath: string, args?: string[], options?: cp.ForkOptions) => cp.ChildProcess; export type ForkProvider = (modulePath: string, args?: string[], options?: cp.ForkOptions) => cp.ChildProcess;
export class ChildProcessProxy implements ServerProxy { export class ChildProcessProxy implements ServerProxy {
@@ -26,7 +28,7 @@ export class ChildProcessProxy implements ServerProxy {
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
public async send(message: any): Promise<void> { public async send(message: any): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
this.process.send(message, (error) => { this.process.send(message, (error) => {
if (error) { if (error) {
reject(error); reject(error);
@@ -46,8 +48,8 @@ export class ChildProcessProxy implements ServerProxy {
} }
public async dispose(): Promise<void> { public async dispose(): Promise<void> {
this.kill(); this.process.kill();
setTimeout(() => this.kill("SIGKILL"), 5000); // Double tap. setTimeout(() => this.process.kill("SIGKILL"), 5000); // Double tap.
} }
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
@@ -62,9 +64,9 @@ export class ChildProcessProxy implements ServerProxy {
export interface ChildProcessProxies { export interface ChildProcessProxies {
childProcess: ChildProcessProxy; childProcess: ChildProcessProxy;
stdin?: WritableProxy; stdin?: WritableProxy | null;
stdout?: ReadableProxy; stdout?: ReadableProxy | null;
stderr?: ReadableProxy; stderr?: ReadableProxy | null;
} }
export class ChildProcessModuleProxy { export class ChildProcessModuleProxy {

View File

@@ -4,6 +4,8 @@ import { ServerProxy } from "../../common/proxy";
import { IEncodingOptions } from "../../common/util"; import { IEncodingOptions } from "../../common/util";
import { WritableProxy } from "./stream"; import { WritableProxy } from "./stream";
// tslint:disable completed-docs
/** /**
* A serializable version of fs.Stats. * A serializable version of fs.Stats.
*/ */
@@ -41,13 +43,13 @@ export class WriteStreamProxy extends WritableProxy<fs.WriteStream> {
} }
public async dispose(): Promise<void> { public async dispose(): Promise<void> {
super.dispose(); await super.dispose();
this.stream.close(); this.stream.close();
} }
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> { public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
super.onEvent(cb); await super.onEvent(cb);
this.stream.on("open", (fd) => cb("open", fd)); this.stream.on("open", (fd) => cb("open", fd));
} }
} }
@@ -109,7 +111,7 @@ export class FsModuleProxy {
} }
public exists(path: fs.PathLike): Promise<boolean> { public exists(path: fs.PathLike): Promise<boolean> {
return promisify(fs.exists)(path); return promisify(fs.exists)(path); // tslint:disable-line deprecation
} }
public fchmod(fd: number, mode: string | number): Promise<void> { public fchmod(fd: number, mode: string | number): Promise<void> {
@@ -156,6 +158,10 @@ export class FsModuleProxy {
return this.makeStatsSerializable(await promisify(fs.lstat)(path)); return this.makeStatsSerializable(await promisify(fs.lstat)(path));
} }
public async lstatBatch(args: { path: fs.PathLike }[]): Promise<(Stats | Error)[]> {
return Promise.all(args.map((a) => this.lstat(a.path).catch((e) => e)));
}
public mkdir(path: fs.PathLike, mode: number | string | fs.MakeDirectoryOptions | undefined | null): Promise<void> { public mkdir(path: fs.PathLike, mode: number | string | fs.MakeDirectoryOptions | undefined | null): Promise<void> {
return promisify(fs.mkdir)(path, mode); return promisify(fs.mkdir)(path, mode);
} }
@@ -169,7 +175,7 @@ export class FsModuleProxy {
} }
public read(fd: number, length: number, position: number | null): Promise<{ bytesRead: number, buffer: Buffer }> { public read(fd: number, length: number, position: number | null): Promise<{ bytesRead: number, buffer: Buffer }> {
const buffer = new Buffer(length); const buffer = Buffer.alloc(length);
return promisify(fs.read)(fd, buffer, 0, length, position); return promisify(fs.read)(fd, buffer, 0, length, position);
} }
@@ -182,6 +188,10 @@ export class FsModuleProxy {
return promisify(fs.readdir)(path, options); return promisify(fs.readdir)(path, options);
} }
public readdirBatch(args: { path: fs.PathLike, options: IEncodingOptions }[]): Promise<(Buffer[] | fs.Dirent[] | string[] | Error)[]> {
return Promise.all(args.map((a) => this.readdir(a.path, a.options).catch((e) => e)));
}
public readlink(path: fs.PathLike, options: IEncodingOptions): Promise<string | Buffer> { public readlink(path: fs.PathLike, options: IEncodingOptions): Promise<string | Buffer> {
return promisify(fs.readlink)(path, options); return promisify(fs.readlink)(path, options);
} }
@@ -202,6 +212,10 @@ export class FsModuleProxy {
return this.makeStatsSerializable(await promisify(fs.stat)(path)); return this.makeStatsSerializable(await promisify(fs.stat)(path));
} }
public async statBatch(args: { path: fs.PathLike }[]): Promise<(Stats | Error)[]> {
return Promise.all(args.map((a) => this.stat(a.path).catch((e) => e)));
}
public symlink(target: fs.PathLike, path: fs.PathLike, type?: fs.symlink.Type | null): Promise<void> { public symlink(target: fs.PathLike, path: fs.PathLike, type?: fs.symlink.Type | null): Promise<void> {
return promisify(fs.symlink)(target, path, type); return promisify(fs.symlink)(target, path, type);
} }

View File

@@ -2,6 +2,8 @@ import * as net from "net";
import { ServerProxy } from "../../common/proxy"; import { ServerProxy } from "../../common/proxy";
import { DuplexProxy } from "./stream"; import { DuplexProxy } from "./stream";
// tslint:disable completed-docs
export class NetSocketProxy extends DuplexProxy<net.Socket> { export class NetSocketProxy extends DuplexProxy<net.Socket> {
public async connect(options: number | string | net.SocketConnectOpts, host?: string): Promise<void> { public async connect(options: number | string | net.SocketConnectOpts, host?: string): Promise<void> {
this.stream.connect(options as any, host as any); // tslint:disable-line no-any this works fine this.stream.connect(options as any, host as any); // tslint:disable-line no-any this works fine
@@ -28,7 +30,7 @@ export class NetSocketProxy extends DuplexProxy<net.Socket> {
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> { public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
super.onEvent(cb); await super.onEvent(cb);
this.stream.on("connect", () => cb("connect")); this.stream.on("connect", () => cb("connect"));
this.stream.on("lookup", (error, address, family, host) => cb("lookup", error, address, family, host)); this.stream.on("lookup", (error, address, family, host) => cb("lookup", error, address, family, host));
this.stream.on("timeout", () => cb("timeout")); this.stream.on("timeout", () => cb("timeout"));

View File

@@ -4,6 +4,8 @@ import * as pty from "node-pty";
import { ServerProxy } from "../../common/proxy"; import { ServerProxy } from "../../common/proxy";
import { preserveEnv } from "../../common/util"; import { preserveEnv } from "../../common/util";
// tslint:disable completed-docs
/** /**
* Server-side IPty proxy. * Server-side IPty proxy.
*/ */
@@ -22,7 +24,7 @@ export class NodePtyProcessProxy implements ServerProxy {
} }
}, 200); }, 200);
this.onDone(() => clearInterval(timer)); this.process.on("exit", () => clearInterval(timer));
} }
public async getPid(): Promise<number> { public async getPid(): Promise<number> {
@@ -50,8 +52,8 @@ export class NodePtyProcessProxy implements ServerProxy {
} }
public async dispose(): Promise<void> { public async dispose(): Promise<void> {
this.kill(); this.process.kill();
setTimeout(() => this.kill("SIGKILL"), 5000); // Double tap. setTimeout(() => this.process.kill("SIGKILL"), 5000); // Double tap.
this.emitter.removeAllListeners(); this.emitter.removeAllListeners();
} }

View File

@@ -3,6 +3,8 @@ import { EventEmitter } from "events";
import * as spdlog from "spdlog"; import * as spdlog from "spdlog";
import { ServerProxy } from "../../common/proxy"; import { ServerProxy } from "../../common/proxy";
// tslint:disable completed-docs
export class RotatingLoggerProxy implements ServerProxy { export class RotatingLoggerProxy implements ServerProxy {
private readonly emitter = new EventEmitter(); private readonly emitter = new EventEmitter();
@@ -24,7 +26,7 @@ export class RotatingLoggerProxy implements ServerProxy {
} }
public async dispose(): Promise<void> { public async dispose(): Promise<void> {
this.flush(); await this.flush();
this.emitter.emit("dispose"); this.emitter.emit("dispose");
this.emitter.removeAllListeners(); this.emitter.removeAllListeners();
} }

View File

@@ -1,6 +1,8 @@
import * as stream from "stream"; import * as stream from "stream";
import { ServerProxy } from "../../common/proxy"; import { ServerProxy } from "../../common/proxy";
// tslint:disable completed-docs
export class WritableProxy<T extends stream.Writable = stream.Writable> implements ServerProxy { export class WritableProxy<T extends stream.Writable = stream.Writable> implements ServerProxy {
public constructor(protected readonly stream: T) {} public constructor(protected readonly stream: T) {}
@@ -100,7 +102,7 @@ export class DuplexProxy<T extends stream.Duplex = stream.Duplex> extends Writab
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> { public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
super.onEvent(cb); await super.onEvent(cb);
this.stream.on("data", (chunk) => cb("data", chunk)); this.stream.on("data", (chunk) => cb("data", chunk));
this.stream.on("end", () => cb("end")); this.stream.on("end", () => cb("end"));
} }

View File

@@ -1,5 +1,7 @@
import * as trash from "trash"; import * as trash from "trash";
// tslint:disable completed-docs
export class TrashModuleProxy { export class TrashModuleProxy {
public async trash(path: string, options?: trash.Options): Promise<void> { public async trash(path: string, options?: trash.Options): Promise<void> {
return trash(path, options); return trash(path, options);

View File

@@ -3,8 +3,8 @@ import * as os from "os";
import { field, logger} from "@coder/logger"; import { field, logger} from "@coder/logger";
import { ReadWriteConnection } from "../common/connection"; import { ReadWriteConnection } from "../common/connection";
import { Module, ServerProxy } from "../common/proxy"; import { Module, ServerProxy } from "../common/proxy";
import { isPromise, isProxy, moduleToProto, parse, platformToProto, protoToModule, stringify } from "../common/util"; import { isPromise, isProxy, moduleToProto, protoToArgument, platformToProto, protoToModule, argumentToProto } from "../common/util";
import { CallbackMessage, ClientMessage, EventMessage, FailMessage, MethodMessage, NamedCallbackMessage, NamedEventMessage, NumberedCallbackMessage, NumberedEventMessage, Pong, ServerMessage, SuccessMessage, WorkingInitMessage } from "../proto"; import { Argument, Callback, ClientMessage, Event, Method, Pong, ServerMessage, WorkingInit } from "../proto";
import { ChildProcessModuleProxy, ForkProvider, FsModuleProxy, NetModuleProxy, NodePtyModuleProxy, SpdlogModuleProxy, TrashModuleProxy } from "./modules"; import { ChildProcessModuleProxy, ForkProvider, FsModuleProxy, NetModuleProxy, NodePtyModuleProxy, SpdlogModuleProxy, TrashModuleProxy } from "./modules";
// tslint:disable no-any // tslint:disable no-any
@@ -14,6 +14,7 @@ export interface ServerOptions {
readonly dataDirectory: string; readonly dataDirectory: string;
readonly cacheDirectory: string; readonly cacheDirectory: string;
readonly builtInExtensionsDirectory: string; readonly builtInExtensionsDirectory: string;
readonly extensionsDirectory: string;
readonly fork?: ForkProvider; readonly fork?: ForkProvider;
} }
@@ -22,11 +23,14 @@ interface ProxyData {
instance: any; instance: any;
} }
/**
* Handle messages from the client.
*/
export class Server { export class Server {
private proxyId = 0; private proxyId = 0;
private readonly proxies = new Map<number | Module, ProxyData>(); private readonly proxies = new Map<number | Module, ProxyData>();
private disconnected: boolean = false; private disconnected: boolean = false;
private responseTimeout = 10000; private readonly responseTimeout = 10000;
public constructor( public constructor(
private readonly connection: ReadWriteConnection, private readonly connection: ReadWriteConnection,
@@ -57,7 +61,9 @@ export class Server {
this.proxies.forEach((proxy, proxyId) => { this.proxies.forEach((proxy, proxyId) => {
if (isProxy(proxy.instance)) { if (isProxy(proxy.instance)) {
proxy.instance.dispose(); proxy.instance.dispose().catch((error) => {
logger.error(error.message);
});
} }
this.removeProxy(proxyId); this.removeProxy(proxyId);
}); });
@@ -84,14 +90,15 @@ export class Server {
logger.error(error.message, field("error", error)); logger.error(error.message, field("error", error));
}); });
const initMsg = new WorkingInitMessage(); const initMsg = new WorkingInit();
initMsg.setDataDirectory(this.options.dataDirectory); initMsg.setDataDirectory(this.options.dataDirectory);
initMsg.setWorkingDirectory(this.options.workingDirectory); initMsg.setWorkingDirectory(this.options.workingDirectory);
initMsg.setBuiltinExtensionsDir(this.options.builtInExtensionsDirectory); initMsg.setBuiltinExtensionsDir(this.options.builtInExtensionsDirectory);
initMsg.setExtensionsDirectory(this.options.extensionsDirectory);
initMsg.setHomeDirectory(os.homedir()); initMsg.setHomeDirectory(os.homedir());
initMsg.setTmpDirectory(os.tmpdir()); initMsg.setTmpDirectory(os.tmpdir());
initMsg.setOperatingSystem(platformToProto(os.platform())); initMsg.setOperatingSystem(platformToProto(os.platform()));
initMsg.setShell(os.userInfo().shell || global.process.env.SHELL); initMsg.setShell(os.userInfo().shell || global.process.env.SHELL || "");
const srvMsg = new ServerMessage(); const srvMsg = new ServerMessage();
srvMsg.setInit(initMsg); srvMsg.setInit(initMsg);
connection.send(srvMsg.serializeBinary()); connection.send(srvMsg.serializeBinary());
@@ -101,14 +108,17 @@ export class Server {
* Handle all messages from the client. * Handle all messages from the client.
*/ */
private async handleMessage(message: ClientMessage): Promise<void> { private async handleMessage(message: ClientMessage): Promise<void> {
if (message.hasMethod()) { switch (message.getMsgCase()) {
case ClientMessage.MsgCase.METHOD:
await this.runMethod(message.getMethod()!); await this.runMethod(message.getMethod()!);
} else if (message.hasPing()) { break;
case ClientMessage.MsgCase.PING:
logger.trace("ping"); logger.trace("ping");
const srvMsg = new ServerMessage(); const srvMsg = new ServerMessage();
srvMsg.setPong(new Pong()); srvMsg.setPong(new Pong());
this.connection.send(srvMsg.serializeBinary()); this.connection.send(srvMsg.serializeBinary());
} else { break;
default:
throw new Error("unknown message type"); throw new Error("unknown message type");
} }
} }
@@ -116,14 +126,14 @@ export class Server {
/** /**
* Run a method on a proxy. * Run a method on a proxy.
*/ */
private async runMethod(message: MethodMessage): Promise<void> { private async runMethod(message: Method): Promise<void> {
const proxyMessage = message.getNamedProxy()! || message.getNumberedProxy()!; const proxyMessage = message.getNamedProxy()! || message.getNumberedProxy()!;
const id = proxyMessage.getId(); const id = proxyMessage.getId();
const proxyId = message.hasNamedProxy() const proxyId = message.hasNamedProxy()
? protoToModule(message.getNamedProxy()!.getModule()) ? protoToModule(message.getNamedProxy()!.getModule())
: message.getNumberedProxy()!.getProxyId(); : message.getNumberedProxy()!.getProxyId();
const method = proxyMessage.getMethod(); const method = proxyMessage.getMethod();
const args = proxyMessage.getArgsList().map((a) => parse( const args = proxyMessage.getArgsList().map((a) => protoToArgument(
a, a,
(id, args) => this.sendCallback(proxyId, id, args), (id, args) => this.sendCallback(proxyId, id, args),
)); ));
@@ -133,14 +143,13 @@ export class Server {
field("id", id), field("id", id),
field("proxyId", proxyId), field("proxyId", proxyId),
field("method", method), field("method", method),
field("args", proxyMessage.getArgsList()),
]); ]);
let response: any; let response: any;
try { try {
const proxy = this.getProxy(proxyId); const proxy = this.getProxy(proxyId);
if (typeof proxy.instance[method] !== "function") { if (typeof proxy.instance[method] !== "function") {
throw new Error(`"${method}" is not a function`); throw new Error(`"${method}" is not a function on proxy ${proxyId}`);
} }
response = proxy.instance[method](...args); response = proxy.instance[method](...args);
@@ -153,7 +162,7 @@ export class Server {
// Proxies must always return promises. // Proxies must always return promises.
if (!isPromise(response)) { if (!isPromise(response)) {
throw new Error('"${method}" must return a promise'); throw new Error(`"${method}" must return a promise`);
} }
} catch (error) { } catch (error) {
logger.error( logger.error(
@@ -175,27 +184,25 @@ export class Server {
* Send a callback to the client. * Send a callback to the client.
*/ */
private sendCallback(proxyId: number | Module, callbackId: number, args: any[]): void { private sendCallback(proxyId: number | Module, callbackId: number, args: any[]): void {
const stringifiedArgs = args.map((a) => this.stringify(a));
logger.trace(() => [ logger.trace(() => [
"sending callback", "sending callback",
field("proxyId", proxyId), field("proxyId", proxyId),
field("callbackId", callbackId), field("callbackId", callbackId),
field("args", stringifiedArgs),
]); ]);
const message = new CallbackMessage(); const message = new Callback();
let callbackMessage: NamedCallbackMessage | NumberedCallbackMessage; let callbackMessage: Callback.Named | Callback.Numbered;
if (typeof proxyId === "string") { if (typeof proxyId === "string") {
callbackMessage = new NamedCallbackMessage(); callbackMessage = new Callback.Named();
callbackMessage.setModule(moduleToProto(proxyId)); callbackMessage.setModule(moduleToProto(proxyId));
message.setNamedCallback(callbackMessage); message.setNamedCallback(callbackMessage);
} else { } else {
callbackMessage = new NumberedCallbackMessage(); callbackMessage = new Callback.Numbered();
callbackMessage.setProxyId(proxyId); callbackMessage.setProxyId(proxyId);
message.setNumberedCallback(callbackMessage); message.setNumberedCallback(callbackMessage);
} }
callbackMessage.setCallbackId(callbackId); callbackMessage.setCallbackId(callbackId);
callbackMessage.setArgsList(stringifiedArgs); callbackMessage.setArgsList(args.map((a) => this.argumentToProto(a)));
const serverMessage = new ServerMessage(); const serverMessage = new ServerMessage();
serverMessage.setCallback(message); serverMessage.setCallback(message);
@@ -203,15 +210,23 @@ export class Server {
} }
/** /**
* Store a proxy and bind events to send them back to the client. * Store a numbered proxy and bind events to send them back to the client.
*/ */
private storeProxy(instance: ServerProxy): number; private storeProxy(instance: ServerProxy): number;
/**
* Store a unique proxy and bind events to send them back to the client.
*/
private storeProxy(instance: any, moduleProxyId: Module): Module; private storeProxy(instance: any, moduleProxyId: Module): Module;
/**
* Store a proxy and bind events to send them back to the client.
*/
private storeProxy(instance: ServerProxy | any, moduleProxyId?: Module): number | Module { private storeProxy(instance: ServerProxy | any, moduleProxyId?: Module): number | Module {
// In case we disposed while waiting for a function to return. // In case we disposed while waiting for a function to return.
if (this.disconnected) { if (this.disconnected) {
if (isProxy(instance)) { if (isProxy(instance)) {
instance.dispose(); instance.dispose().catch((error) => {
logger.error(error.message);
});
} }
throw new Error("disposed"); throw new Error("disposed");
@@ -226,16 +241,22 @@ export class Server {
this.proxies.set(proxyId, { instance }); this.proxies.set(proxyId, { instance });
if (isProxy(instance)) { if (isProxy(instance)) {
instance.onEvent((event, ...args) => this.sendEvent(proxyId, event, ...args)); instance.onEvent((event, ...args) => this.sendEvent(proxyId, event, ...args)).catch((error) => {
logger.error(error.message);
});
instance.onDone(() => { instance.onDone(() => {
// It might have finished because we disposed it due to a disconnect. // It might have finished because we disposed it due to a disconnect.
if (!this.disconnected) { if (!this.disconnected) {
this.sendEvent(proxyId, "done"); this.sendEvent(proxyId, "done");
this.getProxy(proxyId).disposeTimeout = setTimeout(() => { this.getProxy(proxyId).disposeTimeout = setTimeout(() => {
instance.dispose(); instance.dispose().catch((error) => {
logger.error(error.message);
});
this.removeProxy(proxyId); this.removeProxy(proxyId);
}, this.responseTimeout); }, this.responseTimeout);
} }
}).catch((error) => {
logger.error(error.message);
}); });
} }
@@ -246,27 +267,25 @@ export class Server {
* Send an event to the client. * Send an event to the client.
*/ */
private sendEvent(proxyId: number | Module, event: string, ...args: any[]): void { private sendEvent(proxyId: number | Module, event: string, ...args: any[]): void {
const stringifiedArgs = args.map((a) => this.stringify(a));
logger.trace(() => [ logger.trace(() => [
"sending event", "sending event",
field("proxyId", proxyId), field("proxyId", proxyId),
field("event", event), field("event", event),
field("args", stringifiedArgs),
]); ]);
const message = new EventMessage(); const message = new Event();
let eventMessage: NamedEventMessage | NumberedEventMessage; let eventMessage: Event.Named | Event.Numbered;
if (typeof proxyId === "string") { if (typeof proxyId === "string") {
eventMessage = new NamedEventMessage(); eventMessage = new Event.Named();
eventMessage.setModule(moduleToProto(proxyId)); eventMessage.setModule(moduleToProto(proxyId));
message.setNamedEvent(eventMessage); message.setNamedEvent(eventMessage);
} else { } else {
eventMessage = new NumberedEventMessage(); eventMessage = new Event.Numbered();
eventMessage.setProxyId(proxyId); eventMessage.setProxyId(proxyId);
message.setNumberedEvent(eventMessage); message.setNumberedEvent(eventMessage);
} }
eventMessage.setEvent(event); eventMessage.setEvent(event);
eventMessage.setArgsList(stringifiedArgs); eventMessage.setArgsList(args.map((a) => this.argumentToProto(a)));
const serverMessage = new ServerMessage(); const serverMessage = new ServerMessage();
serverMessage.setEvent(message); serverMessage.setEvent(message);
@@ -277,16 +296,14 @@ export class Server {
* Send a response back to the client. * Send a response back to the client.
*/ */
private sendResponse(id: number, response: any): void { private sendResponse(id: number, response: any): void {
const stringifiedResponse = this.stringify(response);
logger.trace(() => [ logger.trace(() => [
"sending resolve", "sending resolve",
field("id", id), field("id", id),
field("response", stringifiedResponse),
]); ]);
const successMessage = new SuccessMessage(); const successMessage = new Method.Success();
successMessage.setId(id); successMessage.setId(id);
successMessage.setResponse(stringifiedResponse); successMessage.setResponse(this.argumentToProto(response));
const serverMessage = new ServerMessage(); const serverMessage = new ServerMessage();
serverMessage.setSuccess(successMessage); serverMessage.setSuccess(successMessage);
@@ -297,16 +314,14 @@ export class Server {
* Send an exception back to the client. * Send an exception back to the client.
*/ */
private sendException(id: number, error: Error): void { private sendException(id: number, error: Error): void {
const stringifiedError = stringify(error);
logger.trace(() => [ logger.trace(() => [
"sending reject", "sending reject",
field("id", id) , field("id", id) ,
field("response", stringifiedError),
]); ]);
const failedMessage = new FailMessage(); const failedMessage = new Method.Fail();
failedMessage.setId(id); failedMessage.setId(id);
failedMessage.setResponse(stringifiedError); failedMessage.setResponse(argumentToProto(error));
const serverMessage = new ServerMessage(); const serverMessage = new ServerMessage();
serverMessage.setFail(failedMessage); serverMessage.setFail(failedMessage);
@@ -327,10 +342,16 @@ export class Server {
]); ]);
} }
private stringify(value: any): string { /**
return stringify(value, undefined, (p) => this.storeProxy(p)); * Same as argumentToProto but provides storeProxy.
*/
private argumentToProto(value: any): Argument {
return argumentToProto(value, undefined, (p) => this.storeProxy(p));
} }
/**
* Get a proxy. Error if it doesn't exist.
*/
private getProxy(proxyId: number | Module): ProxyData { private getProxy(proxyId: number | Module): ProxyData {
if (!this.proxies.has(proxyId)) { if (!this.proxies.has(proxyId)) {
throw new Error(`proxy ${proxyId} disposed too early`); throw new Error(`proxy ${proxyId} disposed too early`);

View File

@@ -6,7 +6,7 @@ import "vscode.proto";
message ClientMessage { message ClientMessage {
oneof msg { oneof msg {
// node.proto // node.proto
MethodMessage method = 20; Method method = 20;
Ping ping = 21; Ping ping = 21;
} }
} }
@@ -15,20 +15,20 @@ message ClientMessage {
message ServerMessage { message ServerMessage {
oneof msg { oneof msg {
// node.proto // node.proto
FailMessage fail = 13; Method.Fail fail = 13;
SuccessMessage success = 14; Method.Success success = 14;
EventMessage event = 19; Event event = 19;
CallbackMessage callback = 22; Callback callback = 22;
Pong pong = 18; Pong pong = 18;
WorkingInitMessage init = 16; WorkingInit init = 16;
// vscode.proto // vscode.proto
SharedProcessActiveMessage shared_process_active = 17; SharedProcessActive shared_process_active = 17;
} }
} }
message WorkingInitMessage { message WorkingInit {
string home_directory = 1; string home_directory = 1;
string tmp_directory = 2; string tmp_directory = 2;
string data_directory = 3; string data_directory = 3;
@@ -41,4 +41,5 @@ message WorkingInitMessage {
OperatingSystem operating_system = 5; OperatingSystem operating_system = 5;
string shell = 6; string shell = 6;
string builtin_extensions_dir = 7; string builtin_extensions_dir = 7;
string extensions_directory = 8;
} }

View File

@@ -8,8 +8,8 @@ import * as vscode_pb from "./vscode_pb";
export class ClientMessage extends jspb.Message { export class ClientMessage extends jspb.Message {
hasMethod(): boolean; hasMethod(): boolean;
clearMethod(): void; clearMethod(): void;
getMethod(): node_pb.MethodMessage | undefined; getMethod(): node_pb.Method | undefined;
setMethod(value?: node_pb.MethodMessage): void; setMethod(value?: node_pb.Method): void;
hasPing(): boolean; hasPing(): boolean;
clearPing(): void; clearPing(): void;
@@ -29,7 +29,7 @@ export class ClientMessage extends jspb.Message {
export namespace ClientMessage { export namespace ClientMessage {
export type AsObject = { export type AsObject = {
method?: node_pb.MethodMessage.AsObject, method?: node_pb.Method.AsObject,
ping?: node_pb.Ping.AsObject, ping?: node_pb.Ping.AsObject,
} }
@@ -43,23 +43,23 @@ export namespace ClientMessage {
export class ServerMessage extends jspb.Message { export class ServerMessage extends jspb.Message {
hasFail(): boolean; hasFail(): boolean;
clearFail(): void; clearFail(): void;
getFail(): node_pb.FailMessage | undefined; getFail(): node_pb.Method.Fail | undefined;
setFail(value?: node_pb.FailMessage): void; setFail(value?: node_pb.Method.Fail): void;
hasSuccess(): boolean; hasSuccess(): boolean;
clearSuccess(): void; clearSuccess(): void;
getSuccess(): node_pb.SuccessMessage | undefined; getSuccess(): node_pb.Method.Success | undefined;
setSuccess(value?: node_pb.SuccessMessage): void; setSuccess(value?: node_pb.Method.Success): void;
hasEvent(): boolean; hasEvent(): boolean;
clearEvent(): void; clearEvent(): void;
getEvent(): node_pb.EventMessage | undefined; getEvent(): node_pb.Event | undefined;
setEvent(value?: node_pb.EventMessage): void; setEvent(value?: node_pb.Event): void;
hasCallback(): boolean; hasCallback(): boolean;
clearCallback(): void; clearCallback(): void;
getCallback(): node_pb.CallbackMessage | undefined; getCallback(): node_pb.Callback | undefined;
setCallback(value?: node_pb.CallbackMessage): void; setCallback(value?: node_pb.Callback): void;
hasPong(): boolean; hasPong(): boolean;
clearPong(): void; clearPong(): void;
@@ -68,13 +68,13 @@ export class ServerMessage extends jspb.Message {
hasInit(): boolean; hasInit(): boolean;
clearInit(): void; clearInit(): void;
getInit(): WorkingInitMessage | undefined; getInit(): WorkingInit | undefined;
setInit(value?: WorkingInitMessage): void; setInit(value?: WorkingInit): void;
hasSharedProcessActive(): boolean; hasSharedProcessActive(): boolean;
clearSharedProcessActive(): void; clearSharedProcessActive(): void;
getSharedProcessActive(): vscode_pb.SharedProcessActiveMessage | undefined; getSharedProcessActive(): vscode_pb.SharedProcessActive | undefined;
setSharedProcessActive(value?: vscode_pb.SharedProcessActiveMessage): void; setSharedProcessActive(value?: vscode_pb.SharedProcessActive): void;
getMsgCase(): ServerMessage.MsgCase; getMsgCase(): ServerMessage.MsgCase;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
@@ -89,13 +89,13 @@ export class ServerMessage extends jspb.Message {
export namespace ServerMessage { export namespace ServerMessage {
export type AsObject = { export type AsObject = {
fail?: node_pb.FailMessage.AsObject, fail?: node_pb.Method.Fail.AsObject,
success?: node_pb.SuccessMessage.AsObject, success?: node_pb.Method.Success.AsObject,
event?: node_pb.EventMessage.AsObject, event?: node_pb.Event.AsObject,
callback?: node_pb.CallbackMessage.AsObject, callback?: node_pb.Callback.AsObject,
pong?: node_pb.Pong.AsObject, pong?: node_pb.Pong.AsObject,
init?: WorkingInitMessage.AsObject, init?: WorkingInit.AsObject,
sharedProcessActive?: vscode_pb.SharedProcessActiveMessage.AsObject, sharedProcessActive?: vscode_pb.SharedProcessActive.AsObject,
} }
export enum MsgCase { export enum MsgCase {
@@ -110,7 +110,7 @@ export namespace ServerMessage {
} }
} }
export class WorkingInitMessage extends jspb.Message { export class WorkingInit extends jspb.Message {
getHomeDirectory(): string; getHomeDirectory(): string;
setHomeDirectory(value: string): void; setHomeDirectory(value: string): void;
@@ -123,8 +123,8 @@ export class WorkingInitMessage extends jspb.Message {
getWorkingDirectory(): string; getWorkingDirectory(): string;
setWorkingDirectory(value: string): void; setWorkingDirectory(value: string): void;
getOperatingSystem(): WorkingInitMessage.OperatingSystem; getOperatingSystem(): WorkingInit.OperatingSystem;
setOperatingSystem(value: WorkingInitMessage.OperatingSystem): void; setOperatingSystem(value: WorkingInit.OperatingSystem): void;
getShell(): string; getShell(): string;
setShell(value: string): void; setShell(value: string): void;
@@ -132,25 +132,29 @@ export class WorkingInitMessage extends jspb.Message {
getBuiltinExtensionsDir(): string; getBuiltinExtensionsDir(): string;
setBuiltinExtensionsDir(value: string): void; setBuiltinExtensionsDir(value: string): void;
getExtensionsDirectory(): string;
setExtensionsDirectory(value: string): void;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): WorkingInitMessage.AsObject; toObject(includeInstance?: boolean): WorkingInit.AsObject;
static toObject(includeInstance: boolean, msg: WorkingInitMessage): WorkingInitMessage.AsObject; static toObject(includeInstance: boolean, msg: WorkingInit): WorkingInit.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: WorkingInitMessage, writer: jspb.BinaryWriter): void; static serializeBinaryToWriter(message: WorkingInit, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): WorkingInitMessage; static deserializeBinary(bytes: Uint8Array): WorkingInit;
static deserializeBinaryFromReader(message: WorkingInitMessage, reader: jspb.BinaryReader): WorkingInitMessage; static deserializeBinaryFromReader(message: WorkingInit, reader: jspb.BinaryReader): WorkingInit;
} }
export namespace WorkingInitMessage { export namespace WorkingInit {
export type AsObject = { export type AsObject = {
homeDirectory: string, homeDirectory: string,
tmpDirectory: string, tmpDirectory: string,
dataDirectory: string, dataDirectory: string,
workingDirectory: string, workingDirectory: string,
operatingSystem: WorkingInitMessage.OperatingSystem, operatingSystem: WorkingInit.OperatingSystem,
shell: string, shell: string,
builtinExtensionsDir: string, builtinExtensionsDir: string,
extensionsDirectory: string,
} }
export enum OperatingSystem { export enum OperatingSystem {

View File

@@ -12,12 +12,13 @@ var goog = jspb;
var global = Function('return this')(); var global = Function('return this')();
var node_pb = require('./node_pb.js'); var node_pb = require('./node_pb.js');
goog.object.extend(proto, node_pb);
var vscode_pb = require('./vscode_pb.js'); var vscode_pb = require('./vscode_pb.js');
goog.object.extend(proto, vscode_pb);
goog.exportSymbol('proto.ClientMessage', null, global); goog.exportSymbol('proto.ClientMessage', null, global);
goog.exportSymbol('proto.ServerMessage', null, global); goog.exportSymbol('proto.ServerMessage', null, global);
goog.exportSymbol('proto.WorkingInitMessage', null, global); goog.exportSymbol('proto.WorkingInit', null, global);
goog.exportSymbol('proto.WorkingInitMessage.OperatingSystem', null, global); goog.exportSymbol('proto.WorkingInit.OperatingSystem', null, global);
/** /**
* Generated by JsPbCodeGenerator. * Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a * @param {Array=} opt_data Optional initial data array, typically from a
@@ -33,8 +34,55 @@ proto.ClientMessage = function(opt_data) {
}; };
goog.inherits(proto.ClientMessage, jspb.Message); goog.inherits(proto.ClientMessage, jspb.Message);
if (goog.DEBUG && !COMPILED) { if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.ClientMessage.displayName = 'proto.ClientMessage'; proto.ClientMessage.displayName = 'proto.ClientMessage';
} }
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.ServerMessage = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, proto.ServerMessage.oneofGroups_);
};
goog.inherits(proto.ServerMessage, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.ServerMessage.displayName = 'proto.ServerMessage';
}
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.WorkingInit = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.WorkingInit, jspb.Message);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
proto.WorkingInit.displayName = 'proto.WorkingInit';
}
/** /**
* Oneof group definitions for this message. Each group defines the field * Oneof group definitions for this message. Each group defines the field
* numbers belonging to that group. When of these fields' value is set, all * numbers belonging to that group. When of these fields' value is set, all
@@ -89,8 +137,8 @@ proto.ClientMessage.prototype.toObject = function(opt_includeInstance) {
* @suppress {unusedLocalVariables} f is only used for nested messages * @suppress {unusedLocalVariables} f is only used for nested messages
*/ */
proto.ClientMessage.toObject = function(includeInstance, msg) { proto.ClientMessage.toObject = function(includeInstance, msg) {
var f, obj = { var obj = {
method: (f = msg.getMethod()) && node_pb.MethodMessage.toObject(includeInstance, f), method: (f = msg.getMethod()) && node_pb.Method.toObject(includeInstance, f),
ping: (f = msg.getPing()) && node_pb.Ping.toObject(includeInstance, f) ping: (f = msg.getPing()) && node_pb.Ping.toObject(includeInstance, f)
}; };
@@ -129,8 +177,8 @@ proto.ClientMessage.deserializeBinaryFromReader = function(msg, reader) {
var field = reader.getFieldNumber(); var field = reader.getFieldNumber();
switch (field) { switch (field) {
case 20: case 20:
var value = new node_pb.MethodMessage; var value = new node_pb.Method;
reader.readMessage(value,node_pb.MethodMessage.deserializeBinaryFromReader); reader.readMessage(value,node_pb.Method.deserializeBinaryFromReader);
msg.setMethod(value); msg.setMethod(value);
break; break;
case 21: case 21:
@@ -172,7 +220,7 @@ proto.ClientMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage( writer.writeMessage(
20, 20,
f, f,
node_pb.MethodMessage.serializeBinaryToWriter node_pb.Method.serializeBinaryToWriter
); );
} }
f = message.getPing(); f = message.getPing();
@@ -187,21 +235,24 @@ proto.ClientMessage.serializeBinaryToWriter = function(message, writer) {
/** /**
* optional MethodMessage method = 20; * optional Method method = 20;
* @return {?proto.MethodMessage} * @return {?proto.Method}
*/ */
proto.ClientMessage.prototype.getMethod = function() { proto.ClientMessage.prototype.getMethod = function() {
return /** @type{?proto.MethodMessage} */ ( return /** @type{?proto.Method} */ (
jspb.Message.getWrapperField(this, node_pb.MethodMessage, 20)); jspb.Message.getWrapperField(this, node_pb.Method, 20));
}; };
/** @param {?proto.MethodMessage|undefined} value */ /** @param {?proto.Method|undefined} value */
proto.ClientMessage.prototype.setMethod = function(value) { proto.ClientMessage.prototype.setMethod = function(value) {
jspb.Message.setOneofWrapperField(this, 20, proto.ClientMessage.oneofGroups_[0], value); jspb.Message.setOneofWrapperField(this, 20, proto.ClientMessage.oneofGroups_[0], value);
}; };
/**
* Clears the message field making it undefined.
*/
proto.ClientMessage.prototype.clearMethod = function() { proto.ClientMessage.prototype.clearMethod = function() {
this.setMethod(undefined); this.setMethod(undefined);
}; };
@@ -209,7 +260,7 @@ proto.ClientMessage.prototype.clearMethod = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ClientMessage.prototype.hasMethod = function() { proto.ClientMessage.prototype.hasMethod = function() {
return jspb.Message.getField(this, 20) != null; return jspb.Message.getField(this, 20) != null;
@@ -232,6 +283,9 @@ proto.ClientMessage.prototype.setPing = function(value) {
}; };
/**
* Clears the message field making it undefined.
*/
proto.ClientMessage.prototype.clearPing = function() { proto.ClientMessage.prototype.clearPing = function() {
this.setPing(undefined); this.setPing(undefined);
}; };
@@ -239,7 +293,7 @@ proto.ClientMessage.prototype.clearPing = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ClientMessage.prototype.hasPing = function() { proto.ClientMessage.prototype.hasPing = function() {
return jspb.Message.getField(this, 21) != null; return jspb.Message.getField(this, 21) != null;
@@ -247,23 +301,6 @@ proto.ClientMessage.prototype.hasPing = function() {
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.ServerMessage = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, proto.ServerMessage.oneofGroups_);
};
goog.inherits(proto.ServerMessage, jspb.Message);
if (goog.DEBUG && !COMPILED) {
proto.ServerMessage.displayName = 'proto.ServerMessage';
}
/** /**
* Oneof group definitions for this message. Each group defines the field * Oneof group definitions for this message. Each group defines the field
* numbers belonging to that group. When of these fields' value is set, all * numbers belonging to that group. When of these fields' value is set, all
@@ -323,14 +360,14 @@ proto.ServerMessage.prototype.toObject = function(opt_includeInstance) {
* @suppress {unusedLocalVariables} f is only used for nested messages * @suppress {unusedLocalVariables} f is only used for nested messages
*/ */
proto.ServerMessage.toObject = function(includeInstance, msg) { proto.ServerMessage.toObject = function(includeInstance, msg) {
var f, obj = { var obj = {
fail: (f = msg.getFail()) && node_pb.FailMessage.toObject(includeInstance, f), fail: (f = msg.getFail()) && node_pb.Method.Fail.toObject(includeInstance, f),
success: (f = msg.getSuccess()) && node_pb.SuccessMessage.toObject(includeInstance, f), success: (f = msg.getSuccess()) && node_pb.Method.Success.toObject(includeInstance, f),
event: (f = msg.getEvent()) && node_pb.EventMessage.toObject(includeInstance, f), event: (f = msg.getEvent()) && node_pb.Event.toObject(includeInstance, f),
callback: (f = msg.getCallback()) && node_pb.CallbackMessage.toObject(includeInstance, f), callback: (f = msg.getCallback()) && node_pb.Callback.toObject(includeInstance, f),
pong: (f = msg.getPong()) && node_pb.Pong.toObject(includeInstance, f), pong: (f = msg.getPong()) && node_pb.Pong.toObject(includeInstance, f),
init: (f = msg.getInit()) && proto.WorkingInitMessage.toObject(includeInstance, f), init: (f = msg.getInit()) && proto.WorkingInit.toObject(includeInstance, f),
sharedProcessActive: (f = msg.getSharedProcessActive()) && vscode_pb.SharedProcessActiveMessage.toObject(includeInstance, f) sharedProcessActive: (f = msg.getSharedProcessActive()) && vscode_pb.SharedProcessActive.toObject(includeInstance, f)
}; };
if (includeInstance) { if (includeInstance) {
@@ -368,23 +405,23 @@ proto.ServerMessage.deserializeBinaryFromReader = function(msg, reader) {
var field = reader.getFieldNumber(); var field = reader.getFieldNumber();
switch (field) { switch (field) {
case 13: case 13:
var value = new node_pb.FailMessage; var value = new node_pb.Method.Fail;
reader.readMessage(value,node_pb.FailMessage.deserializeBinaryFromReader); reader.readMessage(value,node_pb.Method.Fail.deserializeBinaryFromReader);
msg.setFail(value); msg.setFail(value);
break; break;
case 14: case 14:
var value = new node_pb.SuccessMessage; var value = new node_pb.Method.Success;
reader.readMessage(value,node_pb.SuccessMessage.deserializeBinaryFromReader); reader.readMessage(value,node_pb.Method.Success.deserializeBinaryFromReader);
msg.setSuccess(value); msg.setSuccess(value);
break; break;
case 19: case 19:
var value = new node_pb.EventMessage; var value = new node_pb.Event;
reader.readMessage(value,node_pb.EventMessage.deserializeBinaryFromReader); reader.readMessage(value,node_pb.Event.deserializeBinaryFromReader);
msg.setEvent(value); msg.setEvent(value);
break; break;
case 22: case 22:
var value = new node_pb.CallbackMessage; var value = new node_pb.Callback;
reader.readMessage(value,node_pb.CallbackMessage.deserializeBinaryFromReader); reader.readMessage(value,node_pb.Callback.deserializeBinaryFromReader);
msg.setCallback(value); msg.setCallback(value);
break; break;
case 18: case 18:
@@ -393,13 +430,13 @@ proto.ServerMessage.deserializeBinaryFromReader = function(msg, reader) {
msg.setPong(value); msg.setPong(value);
break; break;
case 16: case 16:
var value = new proto.WorkingInitMessage; var value = new proto.WorkingInit;
reader.readMessage(value,proto.WorkingInitMessage.deserializeBinaryFromReader); reader.readMessage(value,proto.WorkingInit.deserializeBinaryFromReader);
msg.setInit(value); msg.setInit(value);
break; break;
case 17: case 17:
var value = new vscode_pb.SharedProcessActiveMessage; var value = new vscode_pb.SharedProcessActive;
reader.readMessage(value,vscode_pb.SharedProcessActiveMessage.deserializeBinaryFromReader); reader.readMessage(value,vscode_pb.SharedProcessActive.deserializeBinaryFromReader);
msg.setSharedProcessActive(value); msg.setSharedProcessActive(value);
break; break;
default: default:
@@ -436,7 +473,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage( writer.writeMessage(
13, 13,
f, f,
node_pb.FailMessage.serializeBinaryToWriter node_pb.Method.Fail.serializeBinaryToWriter
); );
} }
f = message.getSuccess(); f = message.getSuccess();
@@ -444,7 +481,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage( writer.writeMessage(
14, 14,
f, f,
node_pb.SuccessMessage.serializeBinaryToWriter node_pb.Method.Success.serializeBinaryToWriter
); );
} }
f = message.getEvent(); f = message.getEvent();
@@ -452,7 +489,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage( writer.writeMessage(
19, 19,
f, f,
node_pb.EventMessage.serializeBinaryToWriter node_pb.Event.serializeBinaryToWriter
); );
} }
f = message.getCallback(); f = message.getCallback();
@@ -460,7 +497,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage( writer.writeMessage(
22, 22,
f, f,
node_pb.CallbackMessage.serializeBinaryToWriter node_pb.Callback.serializeBinaryToWriter
); );
} }
f = message.getPong(); f = message.getPong();
@@ -476,7 +513,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage( writer.writeMessage(
16, 16,
f, f,
proto.WorkingInitMessage.serializeBinaryToWriter proto.WorkingInit.serializeBinaryToWriter
); );
} }
f = message.getSharedProcessActive(); f = message.getSharedProcessActive();
@@ -484,28 +521,31 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage( writer.writeMessage(
17, 17,
f, f,
vscode_pb.SharedProcessActiveMessage.serializeBinaryToWriter vscode_pb.SharedProcessActive.serializeBinaryToWriter
); );
} }
}; };
/** /**
* optional FailMessage fail = 13; * optional Method.Fail fail = 13;
* @return {?proto.FailMessage} * @return {?proto.Method.Fail}
*/ */
proto.ServerMessage.prototype.getFail = function() { proto.ServerMessage.prototype.getFail = function() {
return /** @type{?proto.FailMessage} */ ( return /** @type{?proto.Method.Fail} */ (
jspb.Message.getWrapperField(this, node_pb.FailMessage, 13)); jspb.Message.getWrapperField(this, node_pb.Method.Fail, 13));
}; };
/** @param {?proto.FailMessage|undefined} value */ /** @param {?proto.Method.Fail|undefined} value */
proto.ServerMessage.prototype.setFail = function(value) { proto.ServerMessage.prototype.setFail = function(value) {
jspb.Message.setOneofWrapperField(this, 13, proto.ServerMessage.oneofGroups_[0], value); jspb.Message.setOneofWrapperField(this, 13, proto.ServerMessage.oneofGroups_[0], value);
}; };
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearFail = function() { proto.ServerMessage.prototype.clearFail = function() {
this.setFail(undefined); this.setFail(undefined);
}; };
@@ -513,7 +553,7 @@ proto.ServerMessage.prototype.clearFail = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ServerMessage.prototype.hasFail = function() { proto.ServerMessage.prototype.hasFail = function() {
return jspb.Message.getField(this, 13) != null; return jspb.Message.getField(this, 13) != null;
@@ -521,21 +561,24 @@ proto.ServerMessage.prototype.hasFail = function() {
/** /**
* optional SuccessMessage success = 14; * optional Method.Success success = 14;
* @return {?proto.SuccessMessage} * @return {?proto.Method.Success}
*/ */
proto.ServerMessage.prototype.getSuccess = function() { proto.ServerMessage.prototype.getSuccess = function() {
return /** @type{?proto.SuccessMessage} */ ( return /** @type{?proto.Method.Success} */ (
jspb.Message.getWrapperField(this, node_pb.SuccessMessage, 14)); jspb.Message.getWrapperField(this, node_pb.Method.Success, 14));
}; };
/** @param {?proto.SuccessMessage|undefined} value */ /** @param {?proto.Method.Success|undefined} value */
proto.ServerMessage.prototype.setSuccess = function(value) { proto.ServerMessage.prototype.setSuccess = function(value) {
jspb.Message.setOneofWrapperField(this, 14, proto.ServerMessage.oneofGroups_[0], value); jspb.Message.setOneofWrapperField(this, 14, proto.ServerMessage.oneofGroups_[0], value);
}; };
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearSuccess = function() { proto.ServerMessage.prototype.clearSuccess = function() {
this.setSuccess(undefined); this.setSuccess(undefined);
}; };
@@ -543,7 +586,7 @@ proto.ServerMessage.prototype.clearSuccess = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ServerMessage.prototype.hasSuccess = function() { proto.ServerMessage.prototype.hasSuccess = function() {
return jspb.Message.getField(this, 14) != null; return jspb.Message.getField(this, 14) != null;
@@ -551,21 +594,24 @@ proto.ServerMessage.prototype.hasSuccess = function() {
/** /**
* optional EventMessage event = 19; * optional Event event = 19;
* @return {?proto.EventMessage} * @return {?proto.Event}
*/ */
proto.ServerMessage.prototype.getEvent = function() { proto.ServerMessage.prototype.getEvent = function() {
return /** @type{?proto.EventMessage} */ ( return /** @type{?proto.Event} */ (
jspb.Message.getWrapperField(this, node_pb.EventMessage, 19)); jspb.Message.getWrapperField(this, node_pb.Event, 19));
}; };
/** @param {?proto.EventMessage|undefined} value */ /** @param {?proto.Event|undefined} value */
proto.ServerMessage.prototype.setEvent = function(value) { proto.ServerMessage.prototype.setEvent = function(value) {
jspb.Message.setOneofWrapperField(this, 19, proto.ServerMessage.oneofGroups_[0], value); jspb.Message.setOneofWrapperField(this, 19, proto.ServerMessage.oneofGroups_[0], value);
}; };
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearEvent = function() { proto.ServerMessage.prototype.clearEvent = function() {
this.setEvent(undefined); this.setEvent(undefined);
}; };
@@ -573,7 +619,7 @@ proto.ServerMessage.prototype.clearEvent = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ServerMessage.prototype.hasEvent = function() { proto.ServerMessage.prototype.hasEvent = function() {
return jspb.Message.getField(this, 19) != null; return jspb.Message.getField(this, 19) != null;
@@ -581,21 +627,24 @@ proto.ServerMessage.prototype.hasEvent = function() {
/** /**
* optional CallbackMessage callback = 22; * optional Callback callback = 22;
* @return {?proto.CallbackMessage} * @return {?proto.Callback}
*/ */
proto.ServerMessage.prototype.getCallback = function() { proto.ServerMessage.prototype.getCallback = function() {
return /** @type{?proto.CallbackMessage} */ ( return /** @type{?proto.Callback} */ (
jspb.Message.getWrapperField(this, node_pb.CallbackMessage, 22)); jspb.Message.getWrapperField(this, node_pb.Callback, 22));
}; };
/** @param {?proto.CallbackMessage|undefined} value */ /** @param {?proto.Callback|undefined} value */
proto.ServerMessage.prototype.setCallback = function(value) { proto.ServerMessage.prototype.setCallback = function(value) {
jspb.Message.setOneofWrapperField(this, 22, proto.ServerMessage.oneofGroups_[0], value); jspb.Message.setOneofWrapperField(this, 22, proto.ServerMessage.oneofGroups_[0], value);
}; };
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearCallback = function() { proto.ServerMessage.prototype.clearCallback = function() {
this.setCallback(undefined); this.setCallback(undefined);
}; };
@@ -603,7 +652,7 @@ proto.ServerMessage.prototype.clearCallback = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ServerMessage.prototype.hasCallback = function() { proto.ServerMessage.prototype.hasCallback = function() {
return jspb.Message.getField(this, 22) != null; return jspb.Message.getField(this, 22) != null;
@@ -626,6 +675,9 @@ proto.ServerMessage.prototype.setPong = function(value) {
}; };
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearPong = function() { proto.ServerMessage.prototype.clearPong = function() {
this.setPong(undefined); this.setPong(undefined);
}; };
@@ -633,7 +685,7 @@ proto.ServerMessage.prototype.clearPong = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ServerMessage.prototype.hasPong = function() { proto.ServerMessage.prototype.hasPong = function() {
return jspb.Message.getField(this, 18) != null; return jspb.Message.getField(this, 18) != null;
@@ -641,21 +693,24 @@ proto.ServerMessage.prototype.hasPong = function() {
/** /**
* optional WorkingInitMessage init = 16; * optional WorkingInit init = 16;
* @return {?proto.WorkingInitMessage} * @return {?proto.WorkingInit}
*/ */
proto.ServerMessage.prototype.getInit = function() { proto.ServerMessage.prototype.getInit = function() {
return /** @type{?proto.WorkingInitMessage} */ ( return /** @type{?proto.WorkingInit} */ (
jspb.Message.getWrapperField(this, proto.WorkingInitMessage, 16)); jspb.Message.getWrapperField(this, proto.WorkingInit, 16));
}; };
/** @param {?proto.WorkingInitMessage|undefined} value */ /** @param {?proto.WorkingInit|undefined} value */
proto.ServerMessage.prototype.setInit = function(value) { proto.ServerMessage.prototype.setInit = function(value) {
jspb.Message.setOneofWrapperField(this, 16, proto.ServerMessage.oneofGroups_[0], value); jspb.Message.setOneofWrapperField(this, 16, proto.ServerMessage.oneofGroups_[0], value);
}; };
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearInit = function() { proto.ServerMessage.prototype.clearInit = function() {
this.setInit(undefined); this.setInit(undefined);
}; };
@@ -663,7 +718,7 @@ proto.ServerMessage.prototype.clearInit = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ServerMessage.prototype.hasInit = function() { proto.ServerMessage.prototype.hasInit = function() {
return jspb.Message.getField(this, 16) != null; return jspb.Message.getField(this, 16) != null;
@@ -671,21 +726,24 @@ proto.ServerMessage.prototype.hasInit = function() {
/** /**
* optional SharedProcessActiveMessage shared_process_active = 17; * optional SharedProcessActive shared_process_active = 17;
* @return {?proto.SharedProcessActiveMessage} * @return {?proto.SharedProcessActive}
*/ */
proto.ServerMessage.prototype.getSharedProcessActive = function() { proto.ServerMessage.prototype.getSharedProcessActive = function() {
return /** @type{?proto.SharedProcessActiveMessage} */ ( return /** @type{?proto.SharedProcessActive} */ (
jspb.Message.getWrapperField(this, vscode_pb.SharedProcessActiveMessage, 17)); jspb.Message.getWrapperField(this, vscode_pb.SharedProcessActive, 17));
}; };
/** @param {?proto.SharedProcessActiveMessage|undefined} value */ /** @param {?proto.SharedProcessActive|undefined} value */
proto.ServerMessage.prototype.setSharedProcessActive = function(value) { proto.ServerMessage.prototype.setSharedProcessActive = function(value) {
jspb.Message.setOneofWrapperField(this, 17, proto.ServerMessage.oneofGroups_[0], value); jspb.Message.setOneofWrapperField(this, 17, proto.ServerMessage.oneofGroups_[0], value);
}; };
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearSharedProcessActive = function() { proto.ServerMessage.prototype.clearSharedProcessActive = function() {
this.setSharedProcessActive(undefined); this.setSharedProcessActive(undefined);
}; };
@@ -693,7 +751,7 @@ proto.ServerMessage.prototype.clearSharedProcessActive = function() {
/** /**
* Returns whether this field is set. * Returns whether this field is set.
* @return {!boolean} * @return {boolean}
*/ */
proto.ServerMessage.prototype.hasSharedProcessActive = function() { proto.ServerMessage.prototype.hasSharedProcessActive = function() {
return jspb.Message.getField(this, 17) != null; return jspb.Message.getField(this, 17) != null;
@@ -701,23 +759,6 @@ proto.ServerMessage.prototype.hasSharedProcessActive = function() {
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.WorkingInitMessage = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.WorkingInitMessage, jspb.Message);
if (goog.DEBUG && !COMPILED) {
proto.WorkingInitMessage.displayName = 'proto.WorkingInitMessage';
}
if (jspb.Message.GENERATE_TO_OBJECT) { if (jspb.Message.GENERATE_TO_OBJECT) {
@@ -731,8 +772,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) {
* for transitional soy proto support: http://goto/soy-param-migration * for transitional soy proto support: http://goto/soy-param-migration
* @return {!Object} * @return {!Object}
*/ */
proto.WorkingInitMessage.prototype.toObject = function(opt_includeInstance) { proto.WorkingInit.prototype.toObject = function(opt_includeInstance) {
return proto.WorkingInitMessage.toObject(opt_includeInstance, this); return proto.WorkingInit.toObject(opt_includeInstance, this);
}; };
@@ -741,19 +782,20 @@ proto.WorkingInitMessage.prototype.toObject = function(opt_includeInstance) {
* @param {boolean|undefined} includeInstance Whether to include the JSPB * @param {boolean|undefined} includeInstance Whether to include the JSPB
* instance for transitional soy proto support: * instance for transitional soy proto support:
* http://goto/soy-param-migration * http://goto/soy-param-migration
* @param {!proto.WorkingInitMessage} msg The msg instance to transform. * @param {!proto.WorkingInit} msg The msg instance to transform.
* @return {!Object} * @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages * @suppress {unusedLocalVariables} f is only used for nested messages
*/ */
proto.WorkingInitMessage.toObject = function(includeInstance, msg) { proto.WorkingInit.toObject = function(includeInstance, msg) {
var f, obj = { var obj = {
homeDirectory: jspb.Message.getFieldWithDefault(msg, 1, ""), homeDirectory: jspb.Message.getFieldWithDefault(msg, 1, ""),
tmpDirectory: jspb.Message.getFieldWithDefault(msg, 2, ""), tmpDirectory: jspb.Message.getFieldWithDefault(msg, 2, ""),
dataDirectory: jspb.Message.getFieldWithDefault(msg, 3, ""), dataDirectory: jspb.Message.getFieldWithDefault(msg, 3, ""),
workingDirectory: jspb.Message.getFieldWithDefault(msg, 4, ""), workingDirectory: jspb.Message.getFieldWithDefault(msg, 4, ""),
operatingSystem: jspb.Message.getFieldWithDefault(msg, 5, 0), operatingSystem: jspb.Message.getFieldWithDefault(msg, 5, 0),
shell: jspb.Message.getFieldWithDefault(msg, 6, ""), shell: jspb.Message.getFieldWithDefault(msg, 6, ""),
builtinExtensionsDir: jspb.Message.getFieldWithDefault(msg, 7, "") builtinExtensionsDir: jspb.Message.getFieldWithDefault(msg, 7, ""),
extensionsDirectory: jspb.Message.getFieldWithDefault(msg, 8, "")
}; };
if (includeInstance) { if (includeInstance) {
@@ -767,23 +809,23 @@ proto.WorkingInitMessage.toObject = function(includeInstance, msg) {
/** /**
* Deserializes binary data (in protobuf wire format). * Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize. * @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.WorkingInitMessage} * @return {!proto.WorkingInit}
*/ */
proto.WorkingInitMessage.deserializeBinary = function(bytes) { proto.WorkingInit.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes); var reader = new jspb.BinaryReader(bytes);
var msg = new proto.WorkingInitMessage; var msg = new proto.WorkingInit;
return proto.WorkingInitMessage.deserializeBinaryFromReader(msg, reader); return proto.WorkingInit.deserializeBinaryFromReader(msg, reader);
}; };
/** /**
* Deserializes binary data (in protobuf wire format) from the * Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object. * given reader into the given message object.
* @param {!proto.WorkingInitMessage} msg The message object to deserialize into. * @param {!proto.WorkingInit} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use. * @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.WorkingInitMessage} * @return {!proto.WorkingInit}
*/ */
proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) { proto.WorkingInit.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) { while (reader.nextField()) {
if (reader.isEndGroup()) { if (reader.isEndGroup()) {
break; break;
@@ -807,7 +849,7 @@ proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
msg.setWorkingDirectory(value); msg.setWorkingDirectory(value);
break; break;
case 5: case 5:
var value = /** @type {!proto.WorkingInitMessage.OperatingSystem} */ (reader.readEnum()); var value = /** @type {!proto.WorkingInit.OperatingSystem} */ (reader.readEnum());
msg.setOperatingSystem(value); msg.setOperatingSystem(value);
break; break;
case 6: case 6:
@@ -818,6 +860,10 @@ proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
var value = /** @type {string} */ (reader.readString()); var value = /** @type {string} */ (reader.readString());
msg.setBuiltinExtensionsDir(value); msg.setBuiltinExtensionsDir(value);
break; break;
case 8:
var value = /** @type {string} */ (reader.readString());
msg.setExtensionsDirectory(value);
break;
default: default:
reader.skipField(); reader.skipField();
break; break;
@@ -831,9 +877,9 @@ proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
* Serializes the message to binary data (in protobuf wire format). * Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array} * @return {!Uint8Array}
*/ */
proto.WorkingInitMessage.prototype.serializeBinary = function() { proto.WorkingInit.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter(); var writer = new jspb.BinaryWriter();
proto.WorkingInitMessage.serializeBinaryToWriter(this, writer); proto.WorkingInit.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer(); return writer.getResultBuffer();
}; };
@@ -841,11 +887,11 @@ proto.WorkingInitMessage.prototype.serializeBinary = function() {
/** /**
* Serializes the given message to binary data (in protobuf wire * Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter. * format), writing to the given BinaryWriter.
* @param {!proto.WorkingInitMessage} message * @param {!proto.WorkingInit} message
* @param {!jspb.BinaryWriter} writer * @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages * @suppress {unusedLocalVariables} f is only used for nested messages
*/ */
proto.WorkingInitMessage.serializeBinaryToWriter = function(message, writer) { proto.WorkingInit.serializeBinaryToWriter = function(message, writer) {
var f = undefined; var f = undefined;
f = message.getHomeDirectory(); f = message.getHomeDirectory();
if (f.length > 0) { if (f.length > 0) {
@@ -896,13 +942,20 @@ proto.WorkingInitMessage.serializeBinaryToWriter = function(message, writer) {
f f
); );
} }
f = message.getExtensionsDirectory();
if (f.length > 0) {
writer.writeString(
8,
f
);
}
}; };
/** /**
* @enum {number} * @enum {number}
*/ */
proto.WorkingInitMessage.OperatingSystem = { proto.WorkingInit.OperatingSystem = {
WINDOWS: 0, WINDOWS: 0,
LINUX: 1, LINUX: 1,
MAC: 2 MAC: 2
@@ -912,13 +965,13 @@ proto.WorkingInitMessage.OperatingSystem = {
* optional string home_directory = 1; * optional string home_directory = 1;
* @return {string} * @return {string}
*/ */
proto.WorkingInitMessage.prototype.getHomeDirectory = function() { proto.WorkingInit.prototype.getHomeDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.WorkingInitMessage.prototype.setHomeDirectory = function(value) { proto.WorkingInit.prototype.setHomeDirectory = function(value) {
jspb.Message.setProto3StringField(this, 1, value); jspb.Message.setProto3StringField(this, 1, value);
}; };
@@ -927,13 +980,13 @@ proto.WorkingInitMessage.prototype.setHomeDirectory = function(value) {
* optional string tmp_directory = 2; * optional string tmp_directory = 2;
* @return {string} * @return {string}
*/ */
proto.WorkingInitMessage.prototype.getTmpDirectory = function() { proto.WorkingInit.prototype.getTmpDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.WorkingInitMessage.prototype.setTmpDirectory = function(value) { proto.WorkingInit.prototype.setTmpDirectory = function(value) {
jspb.Message.setProto3StringField(this, 2, value); jspb.Message.setProto3StringField(this, 2, value);
}; };
@@ -942,13 +995,13 @@ proto.WorkingInitMessage.prototype.setTmpDirectory = function(value) {
* optional string data_directory = 3; * optional string data_directory = 3;
* @return {string} * @return {string}
*/ */
proto.WorkingInitMessage.prototype.getDataDirectory = function() { proto.WorkingInit.prototype.getDataDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.WorkingInitMessage.prototype.setDataDirectory = function(value) { proto.WorkingInit.prototype.setDataDirectory = function(value) {
jspb.Message.setProto3StringField(this, 3, value); jspb.Message.setProto3StringField(this, 3, value);
}; };
@@ -957,28 +1010,28 @@ proto.WorkingInitMessage.prototype.setDataDirectory = function(value) {
* optional string working_directory = 4; * optional string working_directory = 4;
* @return {string} * @return {string}
*/ */
proto.WorkingInitMessage.prototype.getWorkingDirectory = function() { proto.WorkingInit.prototype.getWorkingDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.WorkingInitMessage.prototype.setWorkingDirectory = function(value) { proto.WorkingInit.prototype.setWorkingDirectory = function(value) {
jspb.Message.setProto3StringField(this, 4, value); jspb.Message.setProto3StringField(this, 4, value);
}; };
/** /**
* optional OperatingSystem operating_system = 5; * optional OperatingSystem operating_system = 5;
* @return {!proto.WorkingInitMessage.OperatingSystem} * @return {!proto.WorkingInit.OperatingSystem}
*/ */
proto.WorkingInitMessage.prototype.getOperatingSystem = function() { proto.WorkingInit.prototype.getOperatingSystem = function() {
return /** @type {!proto.WorkingInitMessage.OperatingSystem} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); return /** @type {!proto.WorkingInit.OperatingSystem} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
}; };
/** @param {!proto.WorkingInitMessage.OperatingSystem} value */ /** @param {!proto.WorkingInit.OperatingSystem} value */
proto.WorkingInitMessage.prototype.setOperatingSystem = function(value) { proto.WorkingInit.prototype.setOperatingSystem = function(value) {
jspb.Message.setProto3EnumField(this, 5, value); jspb.Message.setProto3EnumField(this, 5, value);
}; };
@@ -987,13 +1040,13 @@ proto.WorkingInitMessage.prototype.setOperatingSystem = function(value) {
* optional string shell = 6; * optional string shell = 6;
* @return {string} * @return {string}
*/ */
proto.WorkingInitMessage.prototype.getShell = function() { proto.WorkingInit.prototype.getShell = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.WorkingInitMessage.prototype.setShell = function(value) { proto.WorkingInit.prototype.setShell = function(value) {
jspb.Message.setProto3StringField(this, 6, value); jspb.Message.setProto3StringField(this, 6, value);
}; };
@@ -1002,15 +1055,30 @@ proto.WorkingInitMessage.prototype.setShell = function(value) {
* optional string builtin_extensions_dir = 7; * optional string builtin_extensions_dir = 7;
* @return {string} * @return {string}
*/ */
proto.WorkingInitMessage.prototype.getBuiltinExtensionsDir = function() { proto.WorkingInit.prototype.getBuiltinExtensionsDir = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.WorkingInitMessage.prototype.setBuiltinExtensionsDir = function(value) { proto.WorkingInit.prototype.setBuiltinExtensionsDir = function(value) {
jspb.Message.setProto3StringField(this, 7, value); jspb.Message.setProto3StringField(this, 7, value);
}; };
/**
* optional string extensions_directory = 8;
* @return {string}
*/
proto.WorkingInit.prototype.getExtensionsDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, ""));
};
/** @param {string} value */
proto.WorkingInit.prototype.setExtensionsDirectory = function(value) {
jspb.Message.setProto3StringField(this, 8, value);
};
goog.object.extend(exports, proto); goog.object.extend(exports, proto);

View File

@@ -9,84 +9,128 @@ enum Module {
Trash = 5; Trash = 5;
} }
// A proxy identified by a unique name like "fs". message Argument {
message NamedProxyMessage { message ErrorValue {
uint64 id = 1; string message = 1;
Module module = 2; string stack = 2;
string method = 3; string code = 3;
repeated string args = 4;
} }
// A general proxy identified by an ID like WriteStream. message BufferValue {
message NumberedProxyMessage { bytes data = 1;
}
message ObjectValue {
map<string, Argument> data = 1;
}
message ArrayValue {
repeated Argument data = 1;
}
message ProxyValue {
uint64 id = 1; uint64 id = 1;
uint64 proxy_id = 2; }
string method = 3;
repeated string args = 4; message FunctionValue {
uint64 id = 1;
}
message NullValue {}
message UndefinedValue {}
oneof msg {
ErrorValue error = 1;
BufferValue buffer = 2;
ObjectValue object = 3;
ArrayValue array = 4;
ProxyValue proxy = 5;
FunctionValue function = 6;
NullValue null = 7;
UndefinedValue undefined = 8;
double number = 9;
string string = 10;
bool boolean = 11;
}
} }
// Call a remote method. // Call a remote method.
message MethodMessage { message Method {
oneof msg { // A proxy identified by a unique name like "fs".
NamedProxyMessage named_proxy = 1; message Named {
NumberedProxyMessage numbered_proxy = 2; uint64 id = 1;
} Module module = 2;
string method = 3;
repeated Argument args = 4;
} }
// Call a remote callback. // A general proxy identified by an ID like WriteStream.
message CallbackMessage { message Numbered {
oneof msg { uint64 id = 1;
NamedCallbackMessage named_callback = 1; uint64 proxy_id = 2;
NumberedCallbackMessage numbered_callback = 2; string method = 3;
} repeated Argument args = 4;
}
// A remote callback for uniquely named proxy.
message NamedCallbackMessage {
Module module = 1;
uint64 callback_id = 2;
repeated string args = 3;
}
// A remote callback for a numbered proxy.
message NumberedCallbackMessage {
uint64 proxy_id = 1;
uint64 callback_id = 2;
repeated string args = 3;
}
// Emit an event.
message EventMessage {
oneof msg {
NamedEventMessage named_event = 1;
NumberedEventMessage numbered_event = 2;
}
}
// Emit an event on a uniquely named proxy.
message NamedEventMessage {
Module module = 1;
string event = 2;
repeated string args = 3;
}
// Emit an event on a numbered proxy.
message NumberedEventMessage {
uint64 proxy_id = 1;
string event = 2;
repeated string args = 3;
} }
// Remote method failed. // Remote method failed.
message FailMessage { message Fail {
uint64 id = 1; uint64 id = 1;
string response = 2; Argument response = 2;
} }
// Remote method succeeded. // Remote method succeeded.
message SuccessMessage { message Success {
uint64 id = 1; uint64 id = 1;
string response = 2; Argument response = 2;
}
oneof msg {
Method.Named named_proxy = 1;
Method.Numbered numbered_proxy = 2;
}
}
message Callback {
// A remote callback for uniquely named proxy.
message Named {
Module module = 1;
uint64 callback_id = 2;
repeated Argument args = 3;
}
// A remote callback for a numbered proxy.
message Numbered {
uint64 proxy_id = 1;
uint64 callback_id = 2;
repeated Argument args = 3;
}
oneof msg {
Callback.Named named_callback = 1;
Callback.Numbered numbered_callback = 2;
}
}
message Event {
// Emit an event on a uniquely named proxy.
message Named {
Module module = 1;
string event = 2;
repeated Argument args = 3;
}
// Emit an event on a numbered proxy.
message Numbered {
uint64 proxy_id = 1;
string event = 2;
repeated Argument args = 3;
}
oneof msg {
Event.Named named_event = 1;
Event.Numbered numbered_event = 2;
}
} }
message Ping {} message Ping {}

View File

@@ -3,7 +3,296 @@
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
export class NamedProxyMessage extends jspb.Message { export class Argument extends jspb.Message {
hasError(): boolean;
clearError(): void;
getError(): Argument.ErrorValue | undefined;
setError(value?: Argument.ErrorValue): void;
hasBuffer(): boolean;
clearBuffer(): void;
getBuffer(): Argument.BufferValue | undefined;
setBuffer(value?: Argument.BufferValue): void;
hasObject(): boolean;
clearObject(): void;
getObject(): Argument.ObjectValue | undefined;
setObject(value?: Argument.ObjectValue): void;
hasArray(): boolean;
clearArray(): void;
getArray(): Argument.ArrayValue | undefined;
setArray(value?: Argument.ArrayValue): void;
hasProxy(): boolean;
clearProxy(): void;
getProxy(): Argument.ProxyValue | undefined;
setProxy(value?: Argument.ProxyValue): void;
hasFunction(): boolean;
clearFunction(): void;
getFunction(): Argument.FunctionValue | undefined;
setFunction(value?: Argument.FunctionValue): void;
hasNull(): boolean;
clearNull(): void;
getNull(): Argument.NullValue | undefined;
setNull(value?: Argument.NullValue): void;
hasUndefined(): boolean;
clearUndefined(): void;
getUndefined(): Argument.UndefinedValue | undefined;
setUndefined(value?: Argument.UndefinedValue): void;
hasNumber(): boolean;
clearNumber(): void;
getNumber(): number;
setNumber(value: number): void;
hasString(): boolean;
clearString(): void;
getString(): string;
setString(value: string): void;
hasBoolean(): boolean;
clearBoolean(): void;
getBoolean(): boolean;
setBoolean(value: boolean): void;
getMsgCase(): Argument.MsgCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Argument.AsObject;
static toObject(includeInstance: boolean, msg: Argument): Argument.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Argument, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Argument;
static deserializeBinaryFromReader(message: Argument, reader: jspb.BinaryReader): Argument;
}
export namespace Argument {
export type AsObject = {
error?: Argument.ErrorValue.AsObject,
buffer?: Argument.BufferValue.AsObject,
object?: Argument.ObjectValue.AsObject,
array?: Argument.ArrayValue.AsObject,
proxy?: Argument.ProxyValue.AsObject,
pb_function?: Argument.FunctionValue.AsObject,
pb_null?: Argument.NullValue.AsObject,
undefined?: Argument.UndefinedValue.AsObject,
number: number,
string: string,
pb_boolean: boolean,
}
export class ErrorValue extends jspb.Message {
getMessage(): string;
setMessage(value: string): void;
getStack(): string;
setStack(value: string): void;
getCode(): string;
setCode(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ErrorValue.AsObject;
static toObject(includeInstance: boolean, msg: ErrorValue): ErrorValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ErrorValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ErrorValue;
static deserializeBinaryFromReader(message: ErrorValue, reader: jspb.BinaryReader): ErrorValue;
}
export namespace ErrorValue {
export type AsObject = {
message: string,
stack: string,
code: string,
}
}
export class BufferValue extends jspb.Message {
getData(): Uint8Array | string;
getData_asU8(): Uint8Array;
getData_asB64(): string;
setData(value: Uint8Array | string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): BufferValue.AsObject;
static toObject(includeInstance: boolean, msg: BufferValue): BufferValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: BufferValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): BufferValue;
static deserializeBinaryFromReader(message: BufferValue, reader: jspb.BinaryReader): BufferValue;
}
export namespace BufferValue {
export type AsObject = {
data: Uint8Array | string,
}
}
export class ObjectValue extends jspb.Message {
getDataMap(): jspb.Map<string, Argument>;
clearDataMap(): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ObjectValue.AsObject;
static toObject(includeInstance: boolean, msg: ObjectValue): ObjectValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ObjectValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ObjectValue;
static deserializeBinaryFromReader(message: ObjectValue, reader: jspb.BinaryReader): ObjectValue;
}
export namespace ObjectValue {
export type AsObject = {
dataMap: Array<[string, Argument.AsObject]>,
}
}
export class ArrayValue extends jspb.Message {
clearDataList(): void;
getDataList(): Array<Argument>;
setDataList(value: Array<Argument>): void;
addData(value?: Argument, index?: number): Argument;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ArrayValue.AsObject;
static toObject(includeInstance: boolean, msg: ArrayValue): ArrayValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ArrayValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ArrayValue;
static deserializeBinaryFromReader(message: ArrayValue, reader: jspb.BinaryReader): ArrayValue;
}
export namespace ArrayValue {
export type AsObject = {
dataList: Array<Argument.AsObject>,
}
}
export class ProxyValue extends jspb.Message {
getId(): number;
setId(value: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): ProxyValue.AsObject;
static toObject(includeInstance: boolean, msg: ProxyValue): ProxyValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: ProxyValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): ProxyValue;
static deserializeBinaryFromReader(message: ProxyValue, reader: jspb.BinaryReader): ProxyValue;
}
export namespace ProxyValue {
export type AsObject = {
id: number,
}
}
export class FunctionValue extends jspb.Message {
getId(): number;
setId(value: number): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): FunctionValue.AsObject;
static toObject(includeInstance: boolean, msg: FunctionValue): FunctionValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: FunctionValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): FunctionValue;
static deserializeBinaryFromReader(message: FunctionValue, reader: jspb.BinaryReader): FunctionValue;
}
export namespace FunctionValue {
export type AsObject = {
id: number,
}
}
export class NullValue extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NullValue.AsObject;
static toObject(includeInstance: boolean, msg: NullValue): NullValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NullValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NullValue;
static deserializeBinaryFromReader(message: NullValue, reader: jspb.BinaryReader): NullValue;
}
export namespace NullValue {
export type AsObject = {
}
}
export class UndefinedValue extends jspb.Message {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UndefinedValue.AsObject;
static toObject(includeInstance: boolean, msg: UndefinedValue): UndefinedValue.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: UndefinedValue, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UndefinedValue;
static deserializeBinaryFromReader(message: UndefinedValue, reader: jspb.BinaryReader): UndefinedValue;
}
export namespace UndefinedValue {
export type AsObject = {
}
}
export enum MsgCase {
MSG_NOT_SET = 0,
ERROR = 1,
BUFFER = 2,
OBJECT = 3,
ARRAY = 4,
PROXY = 5,
FUNCTION = 6,
NULL = 7,
UNDEFINED = 8,
NUMBER = 9,
STRING = 10,
BOOLEAN = 11,
}
}
export class Method extends jspb.Message {
hasNamedProxy(): boolean;
clearNamedProxy(): void;
getNamedProxy(): Method.Named | undefined;
setNamedProxy(value?: Method.Named): void;
hasNumberedProxy(): boolean;
clearNumberedProxy(): void;
getNumberedProxy(): Method.Numbered | undefined;
setNumberedProxy(value?: Method.Numbered): void;
getMsgCase(): Method.MsgCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Method.AsObject;
static toObject(includeInstance: boolean, msg: Method): Method.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Method, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Method;
static deserializeBinaryFromReader(message: Method, reader: jspb.BinaryReader): Method;
}
export namespace Method {
export type AsObject = {
namedProxy?: Method.Named.AsObject,
numberedProxy?: Method.Numbered.AsObject,
}
export class Named extends jspb.Message {
getId(): number; getId(): number;
setId(value: number): void; setId(value: number): void;
@@ -14,30 +303,30 @@ export class NamedProxyMessage extends jspb.Message {
setMethod(value: string): void; setMethod(value: string): void;
clearArgsList(): void; clearArgsList(): void;
getArgsList(): Array<string>; getArgsList(): Array<Argument>;
setArgsList(value: Array<string>): void; setArgsList(value: Array<Argument>): void;
addArgs(value: string, index?: number): string; addArgs(value?: Argument, index?: number): Argument;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NamedProxyMessage.AsObject; toObject(includeInstance?: boolean): Named.AsObject;
static toObject(includeInstance: boolean, msg: NamedProxyMessage): NamedProxyMessage.AsObject; static toObject(includeInstance: boolean, msg: Named): Named.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NamedProxyMessage, writer: jspb.BinaryWriter): void; static serializeBinaryToWriter(message: Named, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NamedProxyMessage; static deserializeBinary(bytes: Uint8Array): Named;
static deserializeBinaryFromReader(message: NamedProxyMessage, reader: jspb.BinaryReader): NamedProxyMessage; static deserializeBinaryFromReader(message: Named, reader: jspb.BinaryReader): Named;
} }
export namespace NamedProxyMessage { export namespace Named {
export type AsObject = { export type AsObject = {
id: number, id: number,
module: Module, module: Module,
method: string, method: string,
argsList: Array<string>, argsList: Array<Argument.AsObject>,
} }
} }
export class NumberedProxyMessage extends jspb.Message { export class Numbered extends jspb.Message {
getId(): number; getId(): number;
setId(value: number): void; setId(value: number): void;
@@ -48,55 +337,79 @@ export class NumberedProxyMessage extends jspb.Message {
setMethod(value: string): void; setMethod(value: string): void;
clearArgsList(): void; clearArgsList(): void;
getArgsList(): Array<string>; getArgsList(): Array<Argument>;
setArgsList(value: Array<string>): void; setArgsList(value: Array<Argument>): void;
addArgs(value: string, index?: number): string; addArgs(value?: Argument, index?: number): Argument;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NumberedProxyMessage.AsObject; toObject(includeInstance?: boolean): Numbered.AsObject;
static toObject(includeInstance: boolean, msg: NumberedProxyMessage): NumberedProxyMessage.AsObject; static toObject(includeInstance: boolean, msg: Numbered): Numbered.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NumberedProxyMessage, writer: jspb.BinaryWriter): void; static serializeBinaryToWriter(message: Numbered, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NumberedProxyMessage; static deserializeBinary(bytes: Uint8Array): Numbered;
static deserializeBinaryFromReader(message: NumberedProxyMessage, reader: jspb.BinaryReader): NumberedProxyMessage; static deserializeBinaryFromReader(message: Numbered, reader: jspb.BinaryReader): Numbered;
} }
export namespace NumberedProxyMessage { export namespace Numbered {
export type AsObject = { export type AsObject = {
id: number, id: number,
proxyId: number, proxyId: number,
method: string, method: string,
argsList: Array<string>, argsList: Array<Argument.AsObject>,
} }
} }
export class MethodMessage extends jspb.Message { export class Fail extends jspb.Message {
hasNamedProxy(): boolean; getId(): number;
clearNamedProxy(): void; setId(value: number): void;
getNamedProxy(): NamedProxyMessage | undefined;
setNamedProxy(value?: NamedProxyMessage): void;
hasNumberedProxy(): boolean; hasResponse(): boolean;
clearNumberedProxy(): void; clearResponse(): void;
getNumberedProxy(): NumberedProxyMessage | undefined; getResponse(): Argument | undefined;
setNumberedProxy(value?: NumberedProxyMessage): void; setResponse(value?: Argument): void;
getMsgCase(): MethodMessage.MsgCase;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MethodMessage.AsObject; toObject(includeInstance?: boolean): Fail.AsObject;
static toObject(includeInstance: boolean, msg: MethodMessage): MethodMessage.AsObject; static toObject(includeInstance: boolean, msg: Fail): Fail.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: MethodMessage, writer: jspb.BinaryWriter): void; static serializeBinaryToWriter(message: Fail, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): MethodMessage; static deserializeBinary(bytes: Uint8Array): Fail;
static deserializeBinaryFromReader(message: MethodMessage, reader: jspb.BinaryReader): MethodMessage; static deserializeBinaryFromReader(message: Fail, reader: jspb.BinaryReader): Fail;
} }
export namespace MethodMessage { export namespace Fail {
export type AsObject = { export type AsObject = {
namedProxy?: NamedProxyMessage.AsObject, id: number,
numberedProxy?: NumberedProxyMessage.AsObject, response?: Argument.AsObject,
}
}
export class Success extends jspb.Message {
getId(): number;
setId(value: number): void;
hasResponse(): boolean;
clearResponse(): void;
getResponse(): Argument | undefined;
setResponse(value?: Argument): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Success.AsObject;
static toObject(includeInstance: boolean, msg: Success): Success.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Success, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Success;
static deserializeBinaryFromReader(message: Success, reader: jspb.BinaryReader): Success;
}
export namespace Success {
export type AsObject = {
id: number,
response?: Argument.AsObject,
}
} }
export enum MsgCase { export enum MsgCase {
@@ -106,32 +419,92 @@ export namespace MethodMessage {
} }
} }
export class CallbackMessage extends jspb.Message { export class Callback extends jspb.Message {
hasNamedCallback(): boolean; hasNamedCallback(): boolean;
clearNamedCallback(): void; clearNamedCallback(): void;
getNamedCallback(): NamedCallbackMessage | undefined; getNamedCallback(): Callback.Named | undefined;
setNamedCallback(value?: NamedCallbackMessage): void; setNamedCallback(value?: Callback.Named): void;
hasNumberedCallback(): boolean; hasNumberedCallback(): boolean;
clearNumberedCallback(): void; clearNumberedCallback(): void;
getNumberedCallback(): NumberedCallbackMessage | undefined; getNumberedCallback(): Callback.Numbered | undefined;
setNumberedCallback(value?: NumberedCallbackMessage): void; setNumberedCallback(value?: Callback.Numbered): void;
getMsgCase(): CallbackMessage.MsgCase; getMsgCase(): Callback.MsgCase;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): CallbackMessage.AsObject; toObject(includeInstance?: boolean): Callback.AsObject;
static toObject(includeInstance: boolean, msg: CallbackMessage): CallbackMessage.AsObject; static toObject(includeInstance: boolean, msg: Callback): Callback.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: CallbackMessage, writer: jspb.BinaryWriter): void; static serializeBinaryToWriter(message: Callback, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): CallbackMessage; static deserializeBinary(bytes: Uint8Array): Callback;
static deserializeBinaryFromReader(message: CallbackMessage, reader: jspb.BinaryReader): CallbackMessage; static deserializeBinaryFromReader(message: Callback, reader: jspb.BinaryReader): Callback;
} }
export namespace CallbackMessage { export namespace Callback {
export type AsObject = { export type AsObject = {
namedCallback?: NamedCallbackMessage.AsObject, namedCallback?: Callback.Named.AsObject,
numberedCallback?: NumberedCallbackMessage.AsObject, numberedCallback?: Callback.Numbered.AsObject,
}
export class Named extends jspb.Message {
getModule(): Module;
setModule(value: Module): void;
getCallbackId(): number;
setCallbackId(value: number): void;
clearArgsList(): void;
getArgsList(): Array<Argument>;
setArgsList(value: Array<Argument>): void;
addArgs(value?: Argument, index?: number): Argument;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Named.AsObject;
static toObject(includeInstance: boolean, msg: Named): Named.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Named, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Named;
static deserializeBinaryFromReader(message: Named, reader: jspb.BinaryReader): Named;
}
export namespace Named {
export type AsObject = {
module: Module,
callbackId: number,
argsList: Array<Argument.AsObject>,
}
}
export class Numbered extends jspb.Message {
getProxyId(): number;
setProxyId(value: number): void;
getCallbackId(): number;
setCallbackId(value: number): void;
clearArgsList(): void;
getArgsList(): Array<Argument>;
setArgsList(value: Array<Argument>): void;
addArgs(value?: Argument, index?: number): Argument;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Numbered.AsObject;
static toObject(includeInstance: boolean, msg: Numbered): Numbered.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Numbered, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Numbered;
static deserializeBinaryFromReader(message: Numbered, reader: jspb.BinaryReader): Numbered;
}
export namespace Numbered {
export type AsObject = {
proxyId: number,
callbackId: number,
argsList: Array<Argument.AsObject>,
}
} }
export enum MsgCase { export enum MsgCase {
@@ -141,92 +514,92 @@ export namespace CallbackMessage {
} }
} }
export class NamedCallbackMessage extends jspb.Message { export class Event extends jspb.Message {
getModule(): Module;
setModule(value: Module): void;
getCallbackId(): number;
setCallbackId(value: number): void;
clearArgsList(): void;
getArgsList(): Array<string>;
setArgsList(value: Array<string>): void;
addArgs(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NamedCallbackMessage.AsObject;
static toObject(includeInstance: boolean, msg: NamedCallbackMessage): NamedCallbackMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NamedCallbackMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NamedCallbackMessage;
static deserializeBinaryFromReader(message: NamedCallbackMessage, reader: jspb.BinaryReader): NamedCallbackMessage;
}
export namespace NamedCallbackMessage {
export type AsObject = {
module: Module,
callbackId: number,
argsList: Array<string>,
}
}
export class NumberedCallbackMessage extends jspb.Message {
getProxyId(): number;
setProxyId(value: number): void;
getCallbackId(): number;
setCallbackId(value: number): void;
clearArgsList(): void;
getArgsList(): Array<string>;
setArgsList(value: Array<string>): void;
addArgs(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NumberedCallbackMessage.AsObject;
static toObject(includeInstance: boolean, msg: NumberedCallbackMessage): NumberedCallbackMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NumberedCallbackMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NumberedCallbackMessage;
static deserializeBinaryFromReader(message: NumberedCallbackMessage, reader: jspb.BinaryReader): NumberedCallbackMessage;
}
export namespace NumberedCallbackMessage {
export type AsObject = {
proxyId: number,
callbackId: number,
argsList: Array<string>,
}
}
export class EventMessage extends jspb.Message {
hasNamedEvent(): boolean; hasNamedEvent(): boolean;
clearNamedEvent(): void; clearNamedEvent(): void;
getNamedEvent(): NamedEventMessage | undefined; getNamedEvent(): Event.Named | undefined;
setNamedEvent(value?: NamedEventMessage): void; setNamedEvent(value?: Event.Named): void;
hasNumberedEvent(): boolean; hasNumberedEvent(): boolean;
clearNumberedEvent(): void; clearNumberedEvent(): void;
getNumberedEvent(): NumberedEventMessage | undefined; getNumberedEvent(): Event.Numbered | undefined;
setNumberedEvent(value?: NumberedEventMessage): void; setNumberedEvent(value?: Event.Numbered): void;
getMsgCase(): EventMessage.MsgCase; getMsgCase(): Event.MsgCase;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): EventMessage.AsObject; toObject(includeInstance?: boolean): Event.AsObject;
static toObject(includeInstance: boolean, msg: EventMessage): EventMessage.AsObject; static toObject(includeInstance: boolean, msg: Event): Event.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: EventMessage, writer: jspb.BinaryWriter): void; static serializeBinaryToWriter(message: Event, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): EventMessage; static deserializeBinary(bytes: Uint8Array): Event;
static deserializeBinaryFromReader(message: EventMessage, reader: jspb.BinaryReader): EventMessage; static deserializeBinaryFromReader(message: Event, reader: jspb.BinaryReader): Event;
} }
export namespace EventMessage { export namespace Event {
export type AsObject = { export type AsObject = {
namedEvent?: NamedEventMessage.AsObject, namedEvent?: Event.Named.AsObject,
numberedEvent?: NumberedEventMessage.AsObject, numberedEvent?: Event.Numbered.AsObject,
}
export class Named extends jspb.Message {
getModule(): Module;
setModule(value: Module): void;
getEvent(): string;
setEvent(value: string): void;
clearArgsList(): void;
getArgsList(): Array<Argument>;
setArgsList(value: Array<Argument>): void;
addArgs(value?: Argument, index?: number): Argument;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Named.AsObject;
static toObject(includeInstance: boolean, msg: Named): Named.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Named, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Named;
static deserializeBinaryFromReader(message: Named, reader: jspb.BinaryReader): Named;
}
export namespace Named {
export type AsObject = {
module: Module,
event: string,
argsList: Array<Argument.AsObject>,
}
}
export class Numbered extends jspb.Message {
getProxyId(): number;
setProxyId(value: number): void;
getEvent(): string;
setEvent(value: string): void;
clearArgsList(): void;
getArgsList(): Array<Argument>;
setArgsList(value: Array<Argument>): void;
addArgs(value?: Argument, index?: number): Argument;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Numbered.AsObject;
static toObject(includeInstance: boolean, msg: Numbered): Numbered.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Numbered, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Numbered;
static deserializeBinaryFromReader(message: Numbered, reader: jspb.BinaryReader): Numbered;
}
export namespace Numbered {
export type AsObject = {
proxyId: number,
event: string,
argsList: Array<Argument.AsObject>,
}
} }
export enum MsgCase { export enum MsgCase {
@@ -236,114 +609,6 @@ export namespace EventMessage {
} }
} }
export class NamedEventMessage extends jspb.Message {
getModule(): Module;
setModule(value: Module): void;
getEvent(): string;
setEvent(value: string): void;
clearArgsList(): void;
getArgsList(): Array<string>;
setArgsList(value: Array<string>): void;
addArgs(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NamedEventMessage.AsObject;
static toObject(includeInstance: boolean, msg: NamedEventMessage): NamedEventMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NamedEventMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NamedEventMessage;
static deserializeBinaryFromReader(message: NamedEventMessage, reader: jspb.BinaryReader): NamedEventMessage;
}
export namespace NamedEventMessage {
export type AsObject = {
module: Module,
event: string,
argsList: Array<string>,
}
}
export class NumberedEventMessage extends jspb.Message {
getProxyId(): number;
setProxyId(value: number): void;
getEvent(): string;
setEvent(value: string): void;
clearArgsList(): void;
getArgsList(): Array<string>;
setArgsList(value: Array<string>): void;
addArgs(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NumberedEventMessage.AsObject;
static toObject(includeInstance: boolean, msg: NumberedEventMessage): NumberedEventMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NumberedEventMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NumberedEventMessage;
static deserializeBinaryFromReader(message: NumberedEventMessage, reader: jspb.BinaryReader): NumberedEventMessage;
}
export namespace NumberedEventMessage {
export type AsObject = {
proxyId: number,
event: string,
argsList: Array<string>,
}
}
export class FailMessage extends jspb.Message {
getId(): number;
setId(value: number): void;
getResponse(): string;
setResponse(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): FailMessage.AsObject;
static toObject(includeInstance: boolean, msg: FailMessage): FailMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: FailMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): FailMessage;
static deserializeBinaryFromReader(message: FailMessage, reader: jspb.BinaryReader): FailMessage;
}
export namespace FailMessage {
export type AsObject = {
id: number,
response: string,
}
}
export class SuccessMessage extends jspb.Message {
getId(): number;
setId(value: number): void;
getResponse(): string;
setResponse(value: string): void;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SuccessMessage.AsObject;
static toObject(includeInstance: boolean, msg: SuccessMessage): SuccessMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SuccessMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SuccessMessage;
static deserializeBinaryFromReader(message: SuccessMessage, reader: jspb.BinaryReader): SuccessMessage;
}
export namespace SuccessMessage {
export type AsObject = {
id: number,
response: string,
}
}
export class Ping extends jspb.Message { export class Ping extends jspb.Message {
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Ping.AsObject; toObject(includeInstance?: boolean): Ping.AsObject;

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
syntax = "proto3"; syntax = "proto3";
// Sent when a shared process becomes active // Sent when a shared process becomes active
message SharedProcessActiveMessage { message SharedProcessActive {
string socket_path = 1; string socket_path = 1;
string log_path = 2; string log_path = 2;
} }

View File

@@ -3,7 +3,7 @@
import * as jspb from "google-protobuf"; import * as jspb from "google-protobuf";
export class SharedProcessActiveMessage extends jspb.Message { export class SharedProcessActive extends jspb.Message {
getSocketPath(): string; getSocketPath(): string;
setSocketPath(value: string): void; setSocketPath(value: string): void;
@@ -11,16 +11,16 @@ export class SharedProcessActiveMessage extends jspb.Message {
setLogPath(value: string): void; setLogPath(value: string): void;
serializeBinary(): Uint8Array; serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): SharedProcessActiveMessage.AsObject; toObject(includeInstance?: boolean): SharedProcessActive.AsObject;
static toObject(includeInstance: boolean, msg: SharedProcessActiveMessage): SharedProcessActiveMessage.AsObject; static toObject(includeInstance: boolean, msg: SharedProcessActive): SharedProcessActive.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>}; static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>}; static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: SharedProcessActiveMessage, writer: jspb.BinaryWriter): void; static serializeBinaryToWriter(message: SharedProcessActive, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): SharedProcessActiveMessage; static deserializeBinary(bytes: Uint8Array): SharedProcessActive;
static deserializeBinaryFromReader(message: SharedProcessActiveMessage, reader: jspb.BinaryReader): SharedProcessActiveMessage; static deserializeBinaryFromReader(message: SharedProcessActive, reader: jspb.BinaryReader): SharedProcessActive;
} }
export namespace SharedProcessActiveMessage { export namespace SharedProcessActive {
export type AsObject = { export type AsObject = {
socketPath: string, socketPath: string,
logPath: string, logPath: string,

View File

@@ -11,8 +11,7 @@ var jspb = require('google-protobuf');
var goog = jspb; var goog = jspb;
var global = Function('return this')(); var global = Function('return this')();
goog.exportSymbol('proto.SharedProcessActiveMessage', null, global); goog.exportSymbol('proto.SharedProcessActive', null, global);
/** /**
* Generated by JsPbCodeGenerator. * Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a * @param {Array=} opt_data Optional initial data array, typically from a
@@ -23,15 +22,20 @@ goog.exportSymbol('proto.SharedProcessActiveMessage', null, global);
* @extends {jspb.Message} * @extends {jspb.Message}
* @constructor * @constructor
*/ */
proto.SharedProcessActiveMessage = function(opt_data) { proto.SharedProcessActive = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null); jspb.Message.initialize(this, opt_data, 0, -1, null, null);
}; };
goog.inherits(proto.SharedProcessActiveMessage, jspb.Message); goog.inherits(proto.SharedProcessActive, jspb.Message);
if (goog.DEBUG && !COMPILED) { if (goog.DEBUG && !COMPILED) {
proto.SharedProcessActiveMessage.displayName = 'proto.SharedProcessActiveMessage'; /**
* @public
* @override
*/
proto.SharedProcessActive.displayName = 'proto.SharedProcessActive';
} }
if (jspb.Message.GENERATE_TO_OBJECT) { if (jspb.Message.GENERATE_TO_OBJECT) {
/** /**
* Creates an object representation of this proto suitable for use in Soy templates. * Creates an object representation of this proto suitable for use in Soy templates.
@@ -43,8 +47,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) {
* for transitional soy proto support: http://goto/soy-param-migration * for transitional soy proto support: http://goto/soy-param-migration
* @return {!Object} * @return {!Object}
*/ */
proto.SharedProcessActiveMessage.prototype.toObject = function(opt_includeInstance) { proto.SharedProcessActive.prototype.toObject = function(opt_includeInstance) {
return proto.SharedProcessActiveMessage.toObject(opt_includeInstance, this); return proto.SharedProcessActive.toObject(opt_includeInstance, this);
}; };
@@ -53,12 +57,12 @@ proto.SharedProcessActiveMessage.prototype.toObject = function(opt_includeInstan
* @param {boolean|undefined} includeInstance Whether to include the JSPB * @param {boolean|undefined} includeInstance Whether to include the JSPB
* instance for transitional soy proto support: * instance for transitional soy proto support:
* http://goto/soy-param-migration * http://goto/soy-param-migration
* @param {!proto.SharedProcessActiveMessage} msg The msg instance to transform. * @param {!proto.SharedProcessActive} msg The msg instance to transform.
* @return {!Object} * @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages * @suppress {unusedLocalVariables} f is only used for nested messages
*/ */
proto.SharedProcessActiveMessage.toObject = function(includeInstance, msg) { proto.SharedProcessActive.toObject = function(includeInstance, msg) {
var f, obj = { var obj = {
socketPath: jspb.Message.getFieldWithDefault(msg, 1, ""), socketPath: jspb.Message.getFieldWithDefault(msg, 1, ""),
logPath: jspb.Message.getFieldWithDefault(msg, 2, "") logPath: jspb.Message.getFieldWithDefault(msg, 2, "")
}; };
@@ -74,23 +78,23 @@ proto.SharedProcessActiveMessage.toObject = function(includeInstance, msg) {
/** /**
* Deserializes binary data (in protobuf wire format). * Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize. * @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.SharedProcessActiveMessage} * @return {!proto.SharedProcessActive}
*/ */
proto.SharedProcessActiveMessage.deserializeBinary = function(bytes) { proto.SharedProcessActive.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes); var reader = new jspb.BinaryReader(bytes);
var msg = new proto.SharedProcessActiveMessage; var msg = new proto.SharedProcessActive;
return proto.SharedProcessActiveMessage.deserializeBinaryFromReader(msg, reader); return proto.SharedProcessActive.deserializeBinaryFromReader(msg, reader);
}; };
/** /**
* Deserializes binary data (in protobuf wire format) from the * Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object. * given reader into the given message object.
* @param {!proto.SharedProcessActiveMessage} msg The message object to deserialize into. * @param {!proto.SharedProcessActive} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use. * @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.SharedProcessActiveMessage} * @return {!proto.SharedProcessActive}
*/ */
proto.SharedProcessActiveMessage.deserializeBinaryFromReader = function(msg, reader) { proto.SharedProcessActive.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) { while (reader.nextField()) {
if (reader.isEndGroup()) { if (reader.isEndGroup()) {
break; break;
@@ -118,9 +122,9 @@ proto.SharedProcessActiveMessage.deserializeBinaryFromReader = function(msg, rea
* Serializes the message to binary data (in protobuf wire format). * Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array} * @return {!Uint8Array}
*/ */
proto.SharedProcessActiveMessage.prototype.serializeBinary = function() { proto.SharedProcessActive.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter(); var writer = new jspb.BinaryWriter();
proto.SharedProcessActiveMessage.serializeBinaryToWriter(this, writer); proto.SharedProcessActive.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer(); return writer.getResultBuffer();
}; };
@@ -128,11 +132,11 @@ proto.SharedProcessActiveMessage.prototype.serializeBinary = function() {
/** /**
* Serializes the given message to binary data (in protobuf wire * Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter. * format), writing to the given BinaryWriter.
* @param {!proto.SharedProcessActiveMessage} message * @param {!proto.SharedProcessActive} message
* @param {!jspb.BinaryWriter} writer * @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages * @suppress {unusedLocalVariables} f is only used for nested messages
*/ */
proto.SharedProcessActiveMessage.serializeBinaryToWriter = function(message, writer) { proto.SharedProcessActive.serializeBinaryToWriter = function(message, writer) {
var f = undefined; var f = undefined;
f = message.getSocketPath(); f = message.getSocketPath();
if (f.length > 0) { if (f.length > 0) {
@@ -155,13 +159,13 @@ proto.SharedProcessActiveMessage.serializeBinaryToWriter = function(message, wri
* optional string socket_path = 1; * optional string socket_path = 1;
* @return {string} * @return {string}
*/ */
proto.SharedProcessActiveMessage.prototype.getSocketPath = function() { proto.SharedProcessActive.prototype.getSocketPath = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.SharedProcessActiveMessage.prototype.setSocketPath = function(value) { proto.SharedProcessActive.prototype.setSocketPath = function(value) {
jspb.Message.setProto3StringField(this, 1, value); jspb.Message.setProto3StringField(this, 1, value);
}; };
@@ -170,13 +174,13 @@ proto.SharedProcessActiveMessage.prototype.setSocketPath = function(value) {
* optional string log_path = 2; * optional string log_path = 2;
* @return {string} * @return {string}
*/ */
proto.SharedProcessActiveMessage.prototype.getLogPath = function() { proto.SharedProcessActive.prototype.getLogPath = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
}; };
/** @param {string} value */ /** @param {string} value */
proto.SharedProcessActiveMessage.prototype.setLogPath = function(value) { proto.SharedProcessActive.prototype.setLogPath = function(value) {
jspb.Message.setProto3StringField(this, 2, value); jspb.Message.setProto3StringField(this, 2, value);
}; };

View File

@@ -10,7 +10,7 @@ describe("child_process", () => {
const cp = client.modules[Module.ChildProcess]; const cp = client.modules[Module.ChildProcess];
const getStdout = async (proc: ChildProcess): Promise<string> => { const getStdout = async (proc: ChildProcess): Promise<string> => {
return new Promise((r): Readable => proc.stdout.on("data", r)) return new Promise((r): Readable => proc.stdout!.on("data", r))
.then((s) => s.toString()); .then((s) => s.toString());
}; };
@@ -36,10 +36,10 @@ describe("child_process", () => {
it("should cat", async () => { it("should cat", async () => {
const proc = cp.spawn("cat", []); const proc = cp.spawn("cat", []);
expect(proc.pid).toBe(-1); expect(proc.pid).toBe(-1);
proc.stdin.write("banana"); proc.stdin!.write("banana");
await expect(getStdout(proc)).resolves.toBe("banana"); await expect(getStdout(proc)).resolves.toBe("banana");
proc.stdin.end(); proc.stdin!.end();
proc.kill(); proc.kill();
expect(proc.pid).toBeGreaterThan(-1); expect(proc.pid).toBeGreaterThan(-1);

View File

@@ -12,12 +12,10 @@ describe("Server", () => {
workingDirectory, workingDirectory,
}); });
it("should get init msg", (done) => { it("should get init msg", async () => {
client.initData.then((data) => { const data = await client.initData;
expect(data.dataDirectory).toEqual(dataDirectory); expect(data.dataDirectory).toEqual(dataDirectory);
expect(data.workingDirectory).toEqual(workingDirectory); expect(data.workingDirectory).toEqual(workingDirectory);
expect(data.builtInExtensionsDirectory).toEqual(builtInExtensionsDirectory); expect(data.builtInExtensionsDirectory).toEqual(builtInExtensionsDirectory);
done();
});
}); });
}); });

View File

@@ -0,0 +1,101 @@
import * as fs from "fs";
import * as util from "util";
import { argumentToProto, protoToArgument } from "../src/common/util";
describe("Convert", () => {
it("should convert nothing", () => {
expect(protoToArgument()).toBeUndefined();
});
it("should convert null", () => {
expect(protoToArgument(argumentToProto(null))).toBeNull();
});
it("should convert undefined", () => {
expect(protoToArgument(argumentToProto(undefined))).toBeUndefined();
});
it("should convert string", () => {
expect(protoToArgument(argumentToProto("test"))).toBe("test");
});
it("should convert number", () => {
expect(protoToArgument(argumentToProto(10))).toBe(10);
});
it("should convert boolean", () => {
expect(protoToArgument(argumentToProto(true))).toBe(true);
expect(protoToArgument(argumentToProto(false))).toBe(false);
});
it("should convert error", () => {
const error = new Error("message");
const convertedError = protoToArgument(argumentToProto(error));
expect(convertedError instanceof Error).toBeTruthy();
expect(convertedError.message).toBe("message");
});
it("should convert buffer", async () => {
const buffer = await util.promisify(fs.readFile)(__filename);
expect(buffer instanceof Buffer).toBeTruthy();
const convertedBuffer = protoToArgument(argumentToProto(buffer));
expect(convertedBuffer instanceof Buffer).toBeTruthy();
expect(convertedBuffer.toString()).toBe(buffer.toString());
});
it("should convert proxy", () => {
let i = 0;
const proto = argumentToProto(
{ onEvent: (): void => undefined },
undefined,
() => i++,
);
const proxy = protoToArgument(proto, undefined, (id) => {
return {
id: `created: ${id}`,
dispose: (): Promise<void> => Promise.resolve(),
onDone: (): Promise<void> => Promise.resolve(),
onEvent: (): Promise<void> => Promise.resolve(),
};
});
expect(proxy.id).toBe("created: 0");
});
it("should convert function", () => {
const fn = jest.fn();
// tslint:disable-next-line no-any
const map = new Map<number, (...args: any[]) => void>();
let i = 0;
const proto = argumentToProto(
fn,
(f) => {
map.set(i++, f);
return i - 1;
},
);
const remoteFn = protoToArgument(proto, (id, args) => {
map.get(id)!(...args);
});
remoteFn("a", "b", 1);
expect(fn).toHaveBeenCalledWith("a", "b", 1);
});
it("should convert array", () => {
const array = ["a", "b", 1, [1, "a"], null, undefined];
expect(protoToArgument(argumentToProto(array))).toEqual(array);
});
it("should convert object", () => {
const obj = { a: "test" };
// const obj = { "a": 1, "b": [1, "a"], test: null, test2: undefined };
expect(protoToArgument(argumentToProto(obj))).toEqual(obj);
});
});

View File

@@ -9,7 +9,7 @@
"build:binary": "ts-node scripts/nbin.ts" "build:binary": "ts-node scripts/nbin.ts"
}, },
"dependencies": { "dependencies": {
"@coder/nbin": "^1.0.4", "@coder/nbin": "^1.0.6",
"commander": "^2.19.0", "commander": "^2.19.0",
"express": "^4.16.4", "express": "^4.16.4",
"express-static-gzip": "^1.1.3", "express-static-gzip": "^1.1.3",

View File

@@ -1,5 +1,5 @@
import { field, logger } from "@coder/logger"; import { field, logger } from "@coder/logger";
import { ServerMessage, SharedProcessActiveMessage } from "@coder/protocol/src/proto"; import { ServerMessage, SharedProcessActive } from "@coder/protocol/src/proto";
import { ChildProcess, fork, ForkOptions, spawn } from "child_process"; import { ChildProcess, fork, ForkOptions, spawn } from "child_process";
import { randomFillSync } from "crypto"; import { randomFillSync } from "crypto";
import * as fs from "fs"; import * as fs from "fs";
@@ -19,9 +19,11 @@ import * as commander from "commander";
commander.version(process.env.VERSION || "development") commander.version(process.env.VERSION || "development")
.name("code-server") .name("code-server")
.description("Run VS Code on a remote server.") .description("Run VS Code on a remote server.")
.option("--cert") .option("--cert <value>")
.option("--cert-key") .option("--cert-key <value>")
.option("-d, --data-dir <value>", "Customize where user-data is stored.") .option("-e, --extensions-dir <dir>", "Set the root path for extensions.")
.option("-d --user-data-dir <dir>", " Specifies the directory that user data is kept in, useful when running as root.")
.option("--data-dir <value>", "DEPRECATED: Use '--user-data-dir' instead. Customize where user-data is stored.")
.option("-h, --host <value>", "Customize the hostname.", "0.0.0.0") .option("-h, --host <value>", "Customize the hostname.", "0.0.0.0")
.option("-o, --open", "Open in the browser on startup.", false) .option("-o, --open", "Open in the browser on startup.", false)
.option("-p, --port <number>", "Port to bind on.", 8443) .option("-p, --port <number>", "Port to bind on.", 8443)
@@ -38,6 +40,10 @@ Error.stackTraceLimit = Infinity;
if (isCli) { if (isCli) {
require("nbin").shimNativeFs(buildDir); require("nbin").shimNativeFs(buildDir);
} }
// Makes strings or numbers bold in stdout
const bold = (text: string | number): string | number => {
return `\u001B[1m${text}\u001B[0m`;
};
(async (): Promise<void> => { (async (): Promise<void> => {
const args = commander.args; const args = commander.args;
@@ -47,6 +53,9 @@ if (isCli) {
readonly host: string; readonly host: string;
readonly port: number; readonly port: number;
readonly userDataDir?: string;
readonly extensionsDir?: string;
readonly dataDir?: string; readonly dataDir?: string;
readonly password?: string; readonly password?: string;
readonly open?: boolean; readonly open?: boolean;
@@ -63,7 +72,8 @@ if (isCli) {
const noAuthValue = (commander as any).auth; const noAuthValue = (commander as any).auth;
options.noAuth = !noAuthValue; options.noAuth = !noAuthValue;
const dataDir = path.resolve(options.dataDir || path.join(dataHome, "code-server")); const dataDir = path.resolve(options.userDataDir || options.dataDir || path.join(dataHome, "code-server"));
const extensionsDir = options.extensionsDir ? path.resolve(options.extensionsDir) : path.resolve(dataDir, "extensions");
const workingDir = path.resolve(args[0] || process.cwd()); const workingDir = path.resolve(args[0] || process.cwd());
if (!fs.existsSync(dataDir)) { if (!fs.existsSync(dataDir)) {
@@ -77,6 +87,7 @@ if (isCli) {
await Promise.all([ await Promise.all([
fse.mkdirp(cacheHome), fse.mkdirp(cacheHome),
fse.mkdirp(dataDir), fse.mkdirp(dataDir),
fse.mkdirp(extensionsDir),
fse.mkdirp(workingDir), fse.mkdirp(workingDir),
]); ]);
@@ -140,12 +151,17 @@ if (isCli) {
} }
logger.info(`\u001B[1mcode-server ${process.env.VERSION ? `v${process.env.VERSION}` : "development"}`); logger.info(`\u001B[1mcode-server ${process.env.VERSION ? `v${process.env.VERSION}` : "development"}`);
if (options.dataDir) {
logger.warn('"--data-dir" is deprecated. Use "--user-data-dir" instead.');
}
// TODO: fill in appropriate doc url // TODO: fill in appropriate doc url
logger.info("Additional documentation: http://github.com/codercom/code-server"); logger.info("Additional documentation: http://github.com/codercom/code-server");
logger.info("Initializing", field("data-dir", dataDir), field("working-dir", workingDir), field("log-dir", logDir)); logger.info("Initializing", field("data-dir", dataDir), field("extensions-dir", extensionsDir), field("working-dir", workingDir), field("log-dir", logDir));
const sharedProcess = new SharedProcess(dataDir, builtInExtensionsDir); const sharedProcess = new SharedProcess(dataDir, extensionsDir, builtInExtensionsDir);
const sendSharedProcessReady = (socket: WebSocket): void => { const sendSharedProcessReady = (socket: WebSocket): void => {
const active = new SharedProcessActiveMessage(); const active = new SharedProcessActive();
active.setSocketPath(sharedProcess.socketPath); active.setSocketPath(sharedProcess.socketPath);
active.setLogPath(logDir); active.setLogPath(logDir);
const serverMessage = new ServerMessage(); const serverMessage = new ServerMessage();
@@ -192,6 +208,7 @@ if (isCli) {
} }
}, },
serverOptions: { serverOptions: {
extensionsDirectory: extensionsDir,
builtInExtensionsDirectory: builtInExtensionsDir, builtInExtensionsDirectory: builtInExtensionsDir,
dataDirectory: dataDir, dataDirectory: dataDir,
workingDirectory: workingDir, workingDirectory: workingDir,
@@ -234,11 +251,16 @@ if (isCli) {
logger.info(`WebSocket closed \u001B[0m${req.url}`, field("client", id), field("code", code)); logger.info(`WebSocket closed \u001B[0m${req.url}`, field("client", id), field("code", code));
}); });
}); });
app.wss.on("error", (err: NodeJS.ErrnoException) => {
if (err.code === "EADDRINUSE") {
logger.error(`Port ${bold(options.port)} is in use. Please free up port ${options.port} or specify a different port with the -p flag`);
process.exit(1);
}
});
if (!options.certKey && !options.cert) { if (!options.certKey && !options.cert) {
logger.warn("No certificate specified. \u001B[1mThis could be insecure."); logger.warn("No certificate specified. \u001B[1mThis could be insecure.");
// TODO: fill in appropriate doc url // TODO: fill in appropriate doc url
logger.warn("Documentation on securing your setup: https://coder.com/docs"); logger.warn("Documentation on securing your setup: https://github.com/codercom/code-server/blob/master/doc/security/ssl.md");
} }
if (!options.noAuth) { if (!options.noAuth) {

View File

@@ -17,7 +17,7 @@ export interface PortScanner {
* Will scan local ports and emit events when ports are added or removed. * Will scan local ports and emit events when ports are added or removed.
* Currently only scans TCP ports. * Currently only scans TCP ports.
*/ */
export const createPortScanner = (scanInterval: number = 250): PortScanner => { export const createPortScanner = (scanInterval: number = 5000): PortScanner => {
const ports = new Map<number, number>(); const ports = new Map<number, number>();
const addEmitter = new Emitter<number[]>(); const addEmitter = new Emitter<number[]>();
@@ -76,6 +76,7 @@ export const createPortScanner = (scanInterval: number = 250): PortScanner => {
let disposed: boolean = false; let disposed: boolean = false;
const doInterval = (): void => { const doInterval = (): void => {
logger.trace("scanning ports");
scan((error) => { scan((error) => {
if (error) { if (error) {
logger.error(`Port scanning will not be available: ${error.message}.`); logger.error(`Port scanning will not be available: ${error.message}.`);

View File

@@ -1,7 +1,9 @@
import * as cp from "child_process"; import * as cp from "child_process";
import * as fs from "fs"; import * as fs from "fs";
import * as os from "os";
import * as path from "path"; import * as path from "path";
import * as vm from "vm"; import * as vm from "vm";
import { logger } from "@coder/logger";
import { buildDir, isCli } from "../constants"; import { buildDir, isCli } from "../constants";
let ipcMsgBuffer: Buffer[] | undefined = []; let ipcMsgBuffer: Buffer[] | undefined = [];
@@ -151,6 +153,13 @@ export const forkModule = (modulePath: string, args?: string[], options?: cp.For
} else { } else {
proc = cp.spawn(process.execPath, ["--require", "ts-node/register", "--require", "tsconfig-paths/register", process.argv[1], ...forkArgs], forkOptions); proc = cp.spawn(process.execPath, ["--require", "ts-node/register", "--require", "tsconfig-paths/register", process.argv[1], ...forkArgs], forkOptions);
} }
if (args && args[0] === "--type=watcherService" && os.platform() === "linux") {
cp.exec(`renice -n 19 -p ${proc.pid}`, (error) => {
if (error) {
logger.warn(error.message);
}
});
}
return proc; return proc;
}; };

View File

@@ -1,5 +1,6 @@
import { ChildProcess } from "child_process"; import { ChildProcess } from "child_process";
import * as fs from "fs"; import * as fs from "fs";
import * as fse from "fs-extra";
import * as os from "os"; import * as os from "os";
import * as path from "path"; import * as path from "path";
import { forkModule } from "./bootstrapFork"; import { forkModule } from "./bootstrapFork";
@@ -7,7 +8,7 @@ import { StdioIpcHandler } from "../ipc";
import { ParsedArgs } from "vs/platform/environment/common/environment"; import { ParsedArgs } from "vs/platform/environment/common/environment";
import { Emitter } from "@coder/events/src"; import { Emitter } from "@coder/events/src";
import { retry } from "@coder/ide/src/retry"; import { retry } from "@coder/ide/src/retry";
import { logger, field, Level } from "@coder/logger"; import { logger, Level } from "@coder/logger";
export enum SharedProcessState { export enum SharedProcessState {
Stopped, Stopped,
@@ -23,87 +24,115 @@ export type SharedProcessEvent = {
}; };
export class SharedProcess { export class SharedProcess {
public readonly socketPath: string = os.platform() === "win32" ? path.join("\\\\?\\pipe", os.tmpdir(), `.code-server${Math.random().toString()}`) : path.join(os.tmpdir(), `.code-server${Math.random().toString()}`); public readonly socketPath: string = os.platform() === "win32"
? path.join("\\\\?\\pipe", os.tmpdir(), `.code-server${Math.random().toString()}`)
: path.join(os.tmpdir(), `.code-server${Math.random().toString()}`);
private _state: SharedProcessState = SharedProcessState.Stopped; private _state: SharedProcessState = SharedProcessState.Stopped;
private activeProcess: ChildProcess | undefined; private activeProcess: ChildProcess | undefined;
private ipcHandler: StdioIpcHandler | undefined; private ipcHandler: StdioIpcHandler | undefined;
private readonly onStateEmitter = new Emitter<SharedProcessEvent>(); private readonly onStateEmitter = new Emitter<SharedProcessEvent>();
public readonly onState = this.onStateEmitter.event; public readonly onState = this.onStateEmitter.event;
private readonly retryName = "Shared process";
private readonly logger = logger.named("shared"); private readonly logger = logger.named("shared");
private readonly retry = retry.register("Shared process", () => this.connect());
private disposed: boolean = false;
public constructor( public constructor(
private readonly userDataDir: string, private readonly userDataDir: string,
private readonly extensionsDir: string,
private readonly builtInExtensionsDir: string, private readonly builtInExtensionsDir: string,
) { ) {
retry.register(this.retryName, () => this.restart()); this.retry.run();
retry.run(this.retryName);
} }
public get state(): SharedProcessState { public get state(): SharedProcessState {
return this._state; return this._state;
} }
public restart(): void { /**
* Signal the shared process to terminate.
*/
public dispose(): void {
this.disposed = true;
if (this.ipcHandler) {
this.ipcHandler.send("handshake:goodbye");
}
this.ipcHandler = undefined;
}
/**
* Start and connect to the shared process.
*/
private async connect(): Promise<void> {
this.setState({ state: SharedProcessState.Starting });
const activeProcess = await this.restart();
activeProcess.stderr.on("data", (data) => {
// Warn instead of error to prevent panic. It's unlikely stderr here is
// about anything critical to the functioning of the editor.
logger.warn(data.toString());
});
activeProcess.on("exit", (exitCode) => {
const error = new Error(`Exited with ${exitCode}`);
this.setState({
error: error.message,
state: SharedProcessState.Stopped,
});
if (!this.disposed) {
this.retry.run(error);
}
});
this.setState({ state: SharedProcessState.Ready });
}
/**
* Restart the shared process. Kill existing process if running. Resolve when
* the shared process is ready and reject when it errors or dies before being
* ready.
*/
private async restart(): Promise<ChildProcess> {
if (this.activeProcess && !this.activeProcess.killed) { if (this.activeProcess && !this.activeProcess.killed) {
this.activeProcess.kill(); this.activeProcess.kill();
} }
const extensionsDir = path.join(this.userDataDir, "extensions"); const backupsDir = path.join(this.userDataDir, "Backups");
const mkdir = (dir: string): void => { await Promise.all([
try { fse.mkdirp(backupsDir),
fs.mkdirSync(dir); ]);
} catch (ex) {
if (ex.code !== "EEXIST" && ex.code !== "EISDIR") {
throw ex;
}
}
};
mkdir(this.userDataDir);
mkdir(extensionsDir);
this.setState({ const workspacesFile = path.join(backupsDir, "workspaces.json");
state: SharedProcessState.Starting, if (!fs.existsSync(workspacesFile)) {
}); fs.appendFileSync(workspacesFile, "");
let resolved: boolean = false;
const maybeStop = (error: string): void => {
if (resolved) {
return;
} }
this.setState({
error, const activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain", [], {
state: SharedProcessState.Stopped,
});
if (!this.activeProcess) {
return;
}
this.activeProcess.kill();
};
this.activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain", [], {
env: { env: {
VSCODE_ALLOW_IO: "true", VSCODE_ALLOW_IO: "true",
VSCODE_LOGS: process.env.VSCODE_LOGS, VSCODE_LOGS: process.env.VSCODE_LOGS,
}, },
}, this.userDataDir); }, this.userDataDir);
if (this.logger.level <= Level.Trace) { this.activeProcess = activeProcess;
this.activeProcess.stdout.on("data", (data) => {
this.logger.trace(() => ["stdout", field("data", data.toString())]); await new Promise((resolve, reject): void => {
}); const doReject = (error: Error | number | null): void => {
if (error === null) {
error = new Error("Exited unexpectedly");
} else if (typeof error === "number") {
error = new Error(`Exited with ${error}`);
} }
this.activeProcess.on("error", (error) => { activeProcess.removeAllListeners();
this.logger.error("error", field("error", error));
maybeStop(error.message);
});
this.activeProcess.on("exit", (err) => {
if (this._state !== SharedProcessState.Stopped) {
this.setState({ this.setState({
error: `Exited with ${err}`, error: error.message,
state: SharedProcessState.Stopped, state: SharedProcessState.Stopped,
}); });
} reject(error);
retry.run(this.retryName, new Error(`Exited with ${err}`)); };
});
this.ipcHandler = new StdioIpcHandler(this.activeProcess); activeProcess.on("error", doReject);
activeProcess.on("exit", doReject);
this.ipcHandler = new StdioIpcHandler(activeProcess);
this.ipcHandler.once("handshake:hello", () => { this.ipcHandler.once("handshake:hello", () => {
const data: { const data: {
sharedIPCHandle: string; sharedIPCHandle: string;
@@ -113,7 +142,7 @@ export class SharedProcess {
args: { args: {
"builtin-extensions-dir": this.builtInExtensionsDir, "builtin-extensions-dir": this.builtInExtensionsDir,
"user-data-dir": this.userDataDir, "user-data-dir": this.userDataDir,
"extensions-dir": extensionsDir, "extensions-dir": this.extensionsDir,
}, },
logLevel: this.logger.level, logLevel: this.logger.level,
sharedIPCHandle: this.socketPath, sharedIPCHandle: this.socketPath,
@@ -121,25 +150,18 @@ export class SharedProcess {
this.ipcHandler!.send("handshake:hey there", "", data); this.ipcHandler!.send("handshake:hey there", "", data);
}); });
this.ipcHandler.once("handshake:im ready", () => { this.ipcHandler.once("handshake:im ready", () => {
resolved = true; activeProcess.removeListener("error", doReject);
retry.recover(this.retryName); activeProcess.removeListener("exit", doReject);
this.setState({ resolve();
state: SharedProcessState.Ready,
}); });
}); });
this.activeProcess.stderr.on("data", (data) => {
this.logger.error("stderr", field("data", data.toString())); return activeProcess;
maybeStop(data.toString());
});
}
public dispose(): void {
if (this.ipcHandler) {
this.ipcHandler.send("handshake:goodbye");
}
this.ipcHandler = undefined;
} }
/**
* Set the internal shared process state and emit the state event.
*/
private setState(event: SharedProcessEvent): void { private setState(event: SharedProcessEvent): void {
this._state = event.state; this._state = event.state;
this.onStateEmitter.emit(event); this.onStateEmitter.emit(event);

View File

@@ -7,10 +7,10 @@
resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.0.3.tgz#e0e1ae5496fde5a3c6ef3d748fdfb26a55add8b8" resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.0.3.tgz#e0e1ae5496fde5a3c6ef3d748fdfb26a55add8b8"
integrity sha512-1o5qDZX2VZUNnzgz5KfAdMnaqaX6FNeTs0dUdg73MRHfQW94tFTIryFC1xTTCuzxGDjVHOHkaUAI4uHA2bheOA== integrity sha512-1o5qDZX2VZUNnzgz5KfAdMnaqaX6FNeTs0dUdg73MRHfQW94tFTIryFC1xTTCuzxGDjVHOHkaUAI4uHA2bheOA==
"@coder/nbin@^1.0.4": "@coder/nbin@^1.0.6":
version "1.0.4" version "1.0.6"
resolved "https://registry.yarnpkg.com/@coder/nbin/-/nbin-1.0.4.tgz#13a3d110fe116ed5d5fdbd1384f0335745dfd859" resolved "https://registry.yarnpkg.com/@coder/nbin/-/nbin-1.0.6.tgz#6babfaad93b27eac66c1e6a3acb52135b00c5525"
integrity sha512-mtd5hzPHWBwKpTCYdJdLdiY4CFCEb8HUtv3NgH8SSLFiPDwY7H1UlpqeamIty27NZ+9NLnrBd/DfaE3aVo7rxQ== integrity sha512-61niwcPQhZj1mNMyYEt7h62i2a9sg+XP6DmxUF09cZa4HsXPlspHIfoe9Oe3xa7lg7aP9s2Pg9KaYlaYr70Dcg==
dependencies: dependencies:
"@coder/logger" "^1.0.3" "@coder/logger" "^1.0.3"
fs-extra "^7.0.1" fs-extra "^7.0.1"

View File

@@ -8,7 +8,7 @@ export class EnvironmentService extends environment.EnvironmentService {
} }
public get extensionsPath(): string { public get extensionsPath(): string {
return path.join(paths.getAppDataPath(), "extensions"); return paths.getExtensionsDirectory();
} }
} }

View File

@@ -4,6 +4,7 @@ class Paths {
private _appData: string | undefined; private _appData: string | undefined;
private _defaultUserData: string | undefined; private _defaultUserData: string | undefined;
private _socketPath: string | undefined; private _socketPath: string | undefined;
private _extensionsDirectory: string | undefined;
private _builtInExtensionsDirectory: string | undefined; private _builtInExtensionsDirectory: string | undefined;
private _workingDirectory: string | undefined; private _workingDirectory: string | undefined;
@@ -31,6 +32,14 @@ class Paths {
return this._socketPath; return this._socketPath;
} }
public get extensionsDirectory(): string {
if (!this._extensionsDirectory) {
throw new Error("trying to access extensions directory before it has been set");
}
return this._extensionsDirectory;
}
public get builtInExtensionsDirectory(): string { public get builtInExtensionsDirectory(): string {
if (!this._builtInExtensionsDirectory) { if (!this._builtInExtensionsDirectory) {
throw new Error("trying to access builtin extensions directory before it has been set"); throw new Error("trying to access builtin extensions directory before it has been set");
@@ -52,6 +61,7 @@ class Paths {
this._appData = data.dataDirectory; this._appData = data.dataDirectory;
this._defaultUserData = data.dataDirectory; this._defaultUserData = data.dataDirectory;
this._socketPath = sharedData.socketPath; this._socketPath = sharedData.socketPath;
this._extensionsDirectory = data.extensionsDirectory;
this._builtInExtensionsDirectory = data.builtInExtensionsDirectory; this._builtInExtensionsDirectory = data.builtInExtensionsDirectory;
this._workingDirectory = data.workingDirectory; this._workingDirectory = data.workingDirectory;
} }
@@ -61,5 +71,6 @@ export const _paths = new Paths();
export const getAppDataPath = (): string => _paths.appData; export const getAppDataPath = (): string => _paths.appData;
export const getDefaultUserDataPath = (): string => _paths.defaultUserData; export const getDefaultUserDataPath = (): string => _paths.defaultUserData;
export const getWorkingDirectory = (): string => _paths.workingDirectory; export const getWorkingDirectory = (): string => _paths.workingDirectory;
export const getExtensionsDirectory = (): string => _paths.extensionsDirectory;
export const getBuiltInExtensionsDirectory = (): string => _paths.builtInExtensionsDirectory; export const getBuiltInExtensionsDirectory = (): string => _paths.builtInExtensionsDirectory;
export const getSocketPath = (): string => _paths.socketPath; export const getSocketPath = (): string => _paths.socketPath;

View File

@@ -1,5 +1,6 @@
import * as os from "os"; import * as os from "os";
import { IProgress, INotificationHandle } from "@coder/ide"; import { IProgress, INotificationHandle } from "@coder/ide";
import { logger } from "@coder/logger";
import { client } from "./client"; import { client } from "./client";
import "./fill/platform"; import "./fill/platform";
@@ -28,12 +29,23 @@ import { LogLevel } from "vs/platform/log/common/log";
import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey"; import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection"; import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { URI } from "vs/base/common/uri"; import { URI } from "vs/base/common/uri";
import { BackupMainService } from "vs/platform/backup/electron-main/backupMainService";
import { IInstantiationService } from "vs/platform/instantiation/common/instantiation";
/**
* Initializes VS Code and provides a way to call into general client
* functionality.
*/
export class Workbench { export class Workbench {
public readonly retry = client.retry;
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10); private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
private _serviceCollection: ServiceCollection | undefined; private _serviceCollection: ServiceCollection | undefined;
private _clipboardContextKey: RawContextKey<boolean> | undefined; private _clipboardContextKey: RawContextKey<boolean> | undefined;
/**
* Handle a drop event on the file explorer.
*/
public async handleExternalDrop(target: ExplorerItem | ExplorerModel, originalEvent: DragEvent): Promise<void> { public async handleExternalDrop(target: ExplorerItem | ExplorerModel, originalEvent: DragEvent): Promise<void> {
await client.upload.uploadDropped( await client.upload.uploadDropped(
originalEvent, originalEvent,
@@ -41,11 +53,14 @@ export class Workbench {
); );
} }
/**
* Handle a drop event on the editor.
*/
public handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void { public handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void {
client.upload.uploadDropped(event, URI.file(paths.getWorkingDirectory())).then((paths) => { client.upload.uploadDropped(event, URI.file(paths.getWorkingDirectory())).then(async (paths) => {
const uris = paths.map((p) => URI.file(p)); const uris = paths.map((p) => URI.file(p));
if (uris.length) { if (uris.length) {
(this.serviceCollection.get(IWindowsService) as IWindowsService).addRecentlyOpened(uris); await (this.serviceCollection.get(IWindowsService) as IWindowsService).addRecentlyOpened(uris);
} }
const editors: IResourceEditor[] = uris.map(uri => ({ const editors: IResourceEditor[] = uris.map(uri => ({
@@ -57,10 +72,10 @@ export class Workbench {
})); }));
const targetGroup = resolveTargetGroup(); const targetGroup = resolveTargetGroup();
await (this.serviceCollection.get(IEditorService) as IEditorService).openEditors(editors, targetGroup);
(this.serviceCollection.get(IEditorService) as IEditorService).openEditors(editors, targetGroup).then(() => {
afterDrop(targetGroup); afterDrop(targetGroup);
}); }).catch((error) => {
logger.error(error.message);
}); });
} }
@@ -115,6 +130,15 @@ export class Workbench {
public set serviceCollection(collection: ServiceCollection) { public set serviceCollection(collection: ServiceCollection) {
this._serviceCollection = collection; this._serviceCollection = collection;
// TODO: If possible it might be better to start the app from vs/code/electron-main/app.
// For now, manually initialize services from there as needed.
const inst = this._serviceCollection.get(IInstantiationService) as IInstantiationService;
const backupMainService = inst.createInstance(BackupMainService) as BackupMainService;
backupMainService.initialize().catch((error) => {
logger.error(error.message);
});
client.progressService = { client.progressService = {
start: <T>(title: string, task: (progress: IProgress) => Promise<T>, onCancel: () => void): Promise<T> => { start: <T>(title: string, task: (progress: IProgress) => Promise<T>, onCancel: () => void): Promise<T> => {
let lastProgress = 0; let lastProgress = 0;
@@ -164,6 +188,9 @@ export class Workbench {
}; };
} }
/**
* Start VS Code.
*/
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
this._clipboardContextKey = new RawContextKey("nativeClipboard", client.clipboard.isEnabled); this._clipboardContextKey = new RawContextKey("nativeClipboard", client.clipboard.isEnabled);

View File

@@ -883,7 +883,7 @@ index acb68c8..bee143a 100644
- !isMacintosh || // macOS only - !isMacintosh || // macOS only
+ !browser.isMacintosh || // macOS only + !browser.isMacintosh || // macOS only
diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts
index 7445d7b..0291dee 100644 index 7445d7b..ba6bf4b 100644
--- a/src/vs/workbench/electron-browser/workbench.ts --- a/src/vs/workbench/electron-browser/workbench.ts
+++ b/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts
@@ -19 +19,2 @@ import { Registry } from 'vs/platform/registry/common/platform'; @@ -19 +19,2 @@ import { Registry } from 'vs/platform/registry/common/platform';
@@ -899,13 +899,15 @@ index 7445d7b..0291dee 100644
@@ -458 +462 @@ export class Workbench extends Disposable implements IPartService { @@ -458 +462 @@ export class Workbench extends Disposable implements IPartService {
- addClasses(document.body, platformClass); // used by our fonts - addClasses(document.body, platformClass); // used by our fonts
+ addClasses(document.body, platformClass, isWeb ? 'web' : 'native'); // used by our fonts + addClasses(document.body, platformClass, isWeb ? 'web' : 'native'); // used by our fonts
@@ -633 +637 @@ export class Workbench extends Disposable implements IPartService { @@ -491,0 +496 @@ export class Workbench extends Disposable implements IPartService {
+ client.onClose(() => this.notificationService.error("Disconnected from shared process. Searching, installing, enabling, and disabling extensions will not work until the page is refreshed."));
@@ -633 +638 @@ export class Workbench extends Disposable implements IPartService {
- if (!isMacintosh && this.useCustomTitleBarStyle()) { - if (!isMacintosh && this.useCustomTitleBarStyle()) {
+ if (isWeb || (!isMacintosh && this.useCustomTitleBarStyle())) { + if (isWeb || (!isMacintosh && this.useCustomTitleBarStyle())) {
@@ -1241 +1245 @@ export class Workbench extends Disposable implements IPartService { @@ -1241 +1246 @@ export class Workbench extends Disposable implements IPartService {
- if ((isWindows || isLinux) && this.useCustomTitleBarStyle()) { - if ((isWindows || isLinux) && this.useCustomTitleBarStyle()) {
+ if ((isWeb || isWindows || isLinux) && this.useCustomTitleBarStyle()) { + if ((isWeb || isWindows || isLinux) && this.useCustomTitleBarStyle()) {
@@ -1397 +1401 @@ export class Workbench extends Disposable implements IPartService { @@ -1397 +1402 @@ export class Workbench extends Disposable implements IPartService {
- } else if (isMacintosh) { - } else if (isMacintosh) {
+ } else if (isNative && isMacintosh) { + } else if (isNative && isMacintosh) {
diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
@@ -914,6 +916,16 @@ index 0592910..0ce7e35 100644
+++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
@@ -33,0 +34 @@ function getSystemExtensionsRoot(): string { @@ -33,0 +34 @@ function getSystemExtensionsRoot(): string {
+ return (require('vs/../../../../packages/vscode/src/fill/paths') as typeof import ('vs/../../../../packages/vscode/src/fill/paths')).getBuiltInExtensionsDirectory(); + return (require('vs/../../../../packages/vscode/src/fill/paths') as typeof import ('vs/../../../../packages/vscode/src/fill/paths')).getBuiltInExtensionsDirectory();
diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
index 2c2f9c7..e2ab620 100644
--- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
+++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
@@ -92,0 +93 @@ export class ExtensionService extends Disposable implements IExtensionService {
+ private readonly retry = (require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench')).workbench.retry.register('Extension Host', () => this.startExtensionHost());
@@ -435,0 +437 @@ export class ExtensionService extends Disposable implements IExtensionService {
+ extHostProcessWorker.start().then(() => this.retry.recover());
@@ -458,0 +461 @@ export class ExtensionService extends Disposable implements IExtensionService {
+ return this.retry.run();
diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts
index 484cef9..f728fc8 100644 index 484cef9..f728fc8 100644
--- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts --- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts
@@ -921,6 +933,40 @@ index 484cef9..f728fc8 100644
@@ -137 +137 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer @@ -137 +137 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
- process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore. - process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
+ // process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore. + // process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
index ca03fc9..e8b6326 100644
--- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
+++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
@@ -26,0 +27 @@ export class FileWatcher {
+ private readonly retry = (require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench')).workbench.retry.register('Watcher', () => this.startWatching());
@@ -56,0 +58,2 @@ export class FileWatcher {
+ this.toDispose = dispose(this.toDispose);
+ return this.retry.run();
@@ -113 +116 @@ export class FileWatcher {
- }));
+ })).then(() => this.retry.recover());
diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts
index 7e3a324..a880182 100644
--- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts
+++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts
@@ -26,0 +27 @@ export class FileWatcher {
+ private readonly retry = (require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench')).workbench.retry.register('Watcher', () => this.startWatching());
@@ -59,0 +61,2 @@ export class FileWatcher {
+ this.toDispose = dispose(this.toDispose);
+ return this.retry.run();
@@ -116 +119 @@ export class FileWatcher {
- }));
+ })).then(() => this.retry.recover());
diff --git a/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts b/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts
index 74dad64..7bc591a 100644
--- a/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts
+++ b/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts
@@ -25,0 +26 @@ export class OutOfProcessWin32FolderWatcher {
+ private readonly retry = (require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench')).workbench.retry.register('Watcher', () => this.startWatcher());
@@ -52,0 +54 @@ export class OutOfProcessWin32FolderWatcher {
+ this.handle.stdout.once('data', () => this.retry.recover());
@@ -110,0 +113 @@ export class OutOfProcessWin32FolderWatcher {
+ return this.retry.run();
diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts
index 3c78990..545d91a 100644 index 3c78990..545d91a 100644
--- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts
@@ -931,6 +977,37 @@ index 3c78990..545d91a 100644
@@ -130 +130 @@ export class KeyboardMapperFactory { @@ -130 +130 @@ export class KeyboardMapperFactory {
- if (OS === OperatingSystem.Windows) { - if (OS === OperatingSystem.Windows) {
+ if (isNative && OS === OperatingSystem.Windows) { + if (isNative && OS === OperatingSystem.Windows) {
diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts
index 3eaafa4..3b4cb5f 100644
--- a/src/vs/workbench/services/search/node/searchService.ts
+++ b/src/vs/workbench/services/search/node/searchService.ts
@@ -11 +11 @@ import { Event } from 'vs/base/common/event';
-import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
+import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -433,0 +434 @@ export class DiskSearch implements ISearchResultProvider {
+ private toDispose: IDisposable[] = [];
@@ -470,6 +471,15 @@ export class DiskSearch implements ISearchResultProvider {
- const client = new Client(
- getPathFromAmdModule(require, 'bootstrap-fork'),
- opts);
-
- const channel = getNextTickChannel(client.getChannel('search'));
- this.raw = new SearchChannelClient(channel);
+ const connect = (): Promise<void> => {
+ const client = new Client(
+ getPathFromAmdModule(require, 'bootstrap-fork'),
+ opts);
+ client.onDidProcessExit(() => {
+ this.toDispose = dispose(this.toDispose);
+ retry.run();
+ }, null, this.toDispose);
+ this.toDispose.push(client);
+ const channel = getNextTickChannel(client.getChannel('search'));
+ this.raw = new SearchChannelClient(channel);
+ return this.raw.clearCache('test-connectivity');
+ };
+ const retry = (require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench')).workbench.retry.register('Searcher', connect);
+ retry.run();
diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts
index 6e6fbcc..645bd72 100644 index 6e6fbcc..645bd72 100644
--- a/src/vs/workbench/services/timer/electron-browser/timerService.ts --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts

24
scripts/vstar.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
set -euxo pipefail
# Builds a tarfile containing vscode sourcefiles neccessary for CI.
# Done outside the CI and uploaded to object storage to reduce CI time.
branch=1.32.0
dir=/tmp/vstar
outfile=/tmp/vstar-$branch.tar.gz
rm -rf $dir
mkdir -p $dir
cd $dir
git clone https://github.com/microsoft/vscode --branch $branch --single-branch --depth=1
cd vscode
yarn
npx gulp vscode-linux-x64 --max-old-space-size=32384
rm -rf extensions build out* test
cd ..
mv *-x64/resources/app/extensions ./extensions
rm -rf *-x64
tar -czvf $outfile .

View File

@@ -30,13 +30,16 @@ module.exports = (options = {}) => merge(
loader: "sass-loader", loader: "sass-loader",
}], }],
}, { }, {
test: /\.(svg|png|ttf|woff|eot|woff2)$/, test: /\.(png|ttf|woff|eot|woff2)$/,
use: [{ use: [{
loader: "file-loader", loader: "file-loader",
options: { options: {
name: "[path][name].[ext]", name: "[path][name].[ext]",
}, },
}], }],
}, {
test: /\.svg$/,
loader: 'url-loader'
}], }],
}, },
plugins: [ plugins: [

View File

@@ -3,6 +3,7 @@ const os = require("os");
const environment = process.env.NODE_ENV || "development"; const environment = process.env.NODE_ENV || "development";
const HappyPack = require("happypack"); const HappyPack = require("happypack");
const webpack = require("webpack"); const webpack = require("webpack");
const TerserPlugin = require("terser-webpack-plugin");
const root = path.join(__dirname, ".."); const root = path.join(__dirname, "..");
@@ -107,6 +108,11 @@ module.exports = (options = {}) => ({
id: "ts", id: "ts",
threads: Math.max(os.cpus().length - 1, 1), threads: Math.max(os.cpus().length - 1, 1),
loaders: [{ loaders: [{
path: "cache-loader",
query: {
cacheDirectory: path.join(__dirname, "..", ".cache"),
},
}, {
path: "ts-loader", path: "ts-loader",
query: { query: {
happyPackMode: true, happyPackMode: true,
@@ -121,6 +127,14 @@ module.exports = (options = {}) => ({
"process.env.VERSION": `"${process.env.VERSION || ""}"`, "process.env.VERSION": `"${process.env.VERSION || ""}"`,
}), }),
], ],
optimization: {
minimizer: [
new TerserPlugin({
cache: path.join(__dirname, "..", ".cache", "terser"),
parallel: true,
}),
],
},
stats: { stats: {
all: false, // Fallback for options not defined. all: false, // Fallback for options not defined.
errors: true, errors: true,

View File

@@ -14,7 +14,7 @@
"importHelpers": true, "importHelpers": true,
"plugins": [ "plugins": [
{ {
"name": "tslint-language-service" "name": "typescript-tslint-plugin"
} }
], ],
"paths": { "paths": {

View File

@@ -24,6 +24,13 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
"@types/tar@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.0.tgz#e3239d969eeb693a012200613860d0eb871c94f0"
integrity sha512-YybbEHNngcHlIWVCYsoj7Oo1JU9JqONuAlt1LlTH/lmL8BMhbzdFUgReY87a05rY1j8mfK47Del+TCkaLAXwLw==
dependencies:
"@types/node" "*"
"@types/trash@^4.3.1": "@types/trash@^4.3.1":
version "4.3.1" version "4.3.1"
resolved "https://registry.yarnpkg.com/@types/trash/-/trash-4.3.1.tgz#4880ff17c4eb467f1a26774ea6328428403b5c57" resolved "https://registry.yarnpkg.com/@types/trash/-/trash-4.3.1.tgz#4880ff17c4eb467f1a26774ea6328428403b5c57"
@@ -723,6 +730,17 @@ cache-base@^1.0.1:
union-value "^1.0.0" union-value "^1.0.0"
unset-value "^1.0.0" unset-value "^1.0.0"
cache-loader@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-2.0.1.tgz#5758f41a62d7c23941e3c3c7016e6faeb03acb07"
integrity sha512-V99T3FOynmGx26Zom+JrVBytLBsmUCzVG2/4NnUKgvXN4bEV42R1ERl1IyiH/cvFIDA1Ytq2lPZ9tXDSahcQpQ==
dependencies:
loader-utils "^1.1.0"
mkdirp "^0.5.1"
neo-async "^2.6.0"
normalize-path "^3.0.0"
schema-utils "^1.0.0"
camel-case@3.0.x: camel-case@3.0.x:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
@@ -916,6 +934,11 @@ commander@^2.12.1, commander@^2.18.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
commander@^2.19.0:
version "2.20.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
commondir@^1.0.1: commondir@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -3121,7 +3144,7 @@ mime@1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
mime@^2.3.1: mime@^2.0.3, mime@^2.3.1:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.0.tgz#e051fd881358585f3279df333fe694da0bcffdd6" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.0.tgz#e051fd881358585f3279df333fe694da0bcffdd6"
integrity sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w== integrity sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==
@@ -3305,7 +3328,7 @@ negotiator@0.6.1:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
neo-async@^2.5.0: neo-async@^2.5.0, neo-async@^2.6.0:
version "2.6.0" version "2.6.0"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835"
integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==
@@ -3452,6 +3475,11 @@ normalize-path@^2.1.1:
dependencies: dependencies:
remove-trailing-separator "^1.0.1" remove-trailing-separator "^1.0.1"
normalize-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
npm-bundled@^1.0.1: npm-bundled@^1.0.1:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979"
@@ -4625,6 +4653,14 @@ source-map-support@^0.5.6, source-map-support@~0.5.6:
buffer-from "^1.0.0" buffer-from "^1.0.0"
source-map "^0.6.0" source-map "^0.6.0"
source-map-support@~0.5.10:
version "0.5.11"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.11.tgz#efac2ce0800355d026326a0ca23e162aeac9a4e2"
integrity sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-url@^0.4.0: source-map-url@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
@@ -4924,7 +4960,7 @@ tar@^2.0.0:
fstream "^1.0.2" fstream "^1.0.2"
inherits "2" inherits "2"
tar@^4: tar@^4, tar@^4.4.8:
version "4.4.8" version "4.4.8"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
@@ -4951,6 +4987,29 @@ terser-webpack-plugin@^1.1.0:
webpack-sources "^1.1.0" webpack-sources "^1.1.0"
worker-farm "^1.5.2" worker-farm "^1.5.2"
terser-webpack-plugin@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz#3f98bc902fac3e5d0de730869f50668561262ec8"
integrity sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==
dependencies:
cacache "^11.0.2"
find-cache-dir "^2.0.0"
schema-utils "^1.0.0"
serialize-javascript "^1.4.0"
source-map "^0.6.1"
terser "^3.16.1"
webpack-sources "^1.1.0"
worker-farm "^1.5.2"
terser@^3.16.1:
version "3.17.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2"
integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==
dependencies:
commander "^2.19.0"
source-map "~0.6.1"
source-map-support "~0.5.10"
terser@^3.8.1: terser@^3.8.1:
version "3.14.1" version "3.14.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-3.14.1.tgz#cc4764014af570bc79c79742358bd46926018a32" resolved "https://registry.yarnpkg.com/terser/-/terser-3.14.1.tgz#cc4764014af570bc79c79742358bd46926018a32"
@@ -5236,6 +5295,15 @@ urix@^0.1.0:
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url-loader@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8"
integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==
dependencies:
loader-utils "^1.1.0"
mime "^2.0.3"
schema-utils "^1.0.0"
url-parse@^1.4.3: url-parse@^1.4.3:
version "1.4.4" version "1.4.4"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8"