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
dist
out
.DS_Store
release
.cache

1
.node-version Normal file
View File

@@ -0,0 +1 @@
8.15.0

View File

@@ -6,11 +6,12 @@ env:
matrix:
include:
- os: linux
dist: ubuntu
dist: trusty
- os: osx
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libxkbfile-dev
libsecret-1-dev; fi
- npm install -g yarn@1.12.3
script:
- scripts/build.sh
before_deploy:
@@ -35,4 +36,8 @@ deploy:
on:
repo: codercom/code-server
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
# configured in /etc/default/locale so we need to set it manually.
ENV LANG=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
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)
[!["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)](#)
[![Discord](https://discordapp.com/api/guilds/463752820026376202/widget.png)](https://discord.gg/zxSwN8Z)
[![MIT license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/codercom/code-server/blob/master/LICENSE)
[![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.
@@ -57,14 +57,15 @@ How to [secure your setup](/doc/security/ssl.md).
- Creating custom VS Code extensions and debugging them doesn't work.
### Future
- **Stay up to date!** Get notified about new releases of code-server.
![Screenshot](/doc/assets/release.gif)
- Windows support.
- 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.
### 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

View File

@@ -4,21 +4,22 @@ import * as fse from "fs-extra";
import * as os from "os";
import * as path from "path";
import * as zlib from "zlib";
import * as https from "https";
import * as tar from "tar";
const isWin = os.platform() === "win32";
const libPath = path.join(__dirname, "../lib");
const vscodePath = path.join(libPath, "vscode");
const defaultExtensionsPath = path.join(libPath, "extensions");
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 vsSourceUrl = `https://codesrv-ci.cdr.sh/vstar-${vscodeVersion}.tar.gz`;
const buildServerBinary = register("build:server:binary", async (runner) => {
await ensureInstalled();
await copyForDefaultExtensions();
await Promise.all([
buildBootstrapFork(),
buildWeb(),
buildDefaultExtensions(),
buildServerBundle(),
buildAppBrowser(),
]);
@@ -129,97 +130,50 @@ const buildWeb = register("build:web", async (runner) => {
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) => {
await ensureCloned();
runner.cwd = libPath;
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}`);
}
});
if (fs.existsSync(vscodePath) && fs.existsSync(defaultExtensionsPath)) {
const pkgVersion = JSON.parse(fs.readFileSync(path.join(vscodePath, "package.json")).toString("utf8")).version;
if (pkgVersion === vscodeVersion) {
runner.cwd = vscodePath;
const ensureCloned = register("vscode:clone", async (runner) => {
if (fs.existsSync(vscodePath)) {
await ensureClean();
} else {
fse.mkdirpSync(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) {
throw new Error(`Failed to clone: ${clone.exitCode}`);
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;
const checkout = await runner.execute("git", ["checkout", vscodeVersion]);
if (checkout.exitCode !== 0) {
throw new Error(`Failed to checkout: ${checkout.stderr}`);
}
});
fse.removeSync(libPath);
fse.mkdirpSync(libPath);
const ensureClean = register("vscode:clean", async (runner) => {
runner.cwd = vscodePath;
await new Promise<void>((resolve, reject): void => {
https.get(vsSourceUrl, (res) => {
if (res.statusCode !== 200) {
return reject(res.statusMessage);
}
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}`);
}
res.pipe(tar.x({
C: libPath,
}).on("finish", () => {
resolve();
}).on("error", (err: Error) => {
reject(err);
}));
}).on("error", (err) => {
reject(err);
});
});
});
const ensurePatched = register("vscode:patch", async (runner) => {
if (!fs.existsSync(vscodePath)) {
throw new Error("vscode must be cloned to patch");
}
await ensureClean();
await ensureInstalled();
runner.cwd = vscodePath;
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)
- 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">
- Then click **"proceed anyway"**<img src="../../assets/chrome_confirm.png">

View File

@@ -54,7 +54,7 @@ chmod +x 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

BIN
doc/assets/release.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -6,7 +6,7 @@
> NOTE: If you get stuck or need help, [file an issue](https://github.com/codercom/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
This document pertains to Coder specific implementations of VS Code. For documentation on how to use VS Code itself, please refer to the official [documentation for VS Code](https://code.visualstudio.com/docs)
This document pertains to Coder specific implementations of VS Code. For documentation on how to use VS Code itself, please refer to the official [documentation for VS Code](https://code.visualstudio.com/docs)
It takes just a few minutes to get your own self-hosted server running. If you've got a machine running macOS, Windows, or Linux, you're ready to start the binary which listens on port `8443` by default.
@@ -58,19 +58,20 @@ OPTIONS
Use `code-server -d (path/to/directory)` or `code-server --data-dir=(path/to/directory)`, excluding the parentheses to specify the root folder that VS Code will start in
### Host
By default, code-server will use `0.0.0.0` as its address. This can be changed by using `code-server -h` or `code-server --host=` followed by the address you want to use.
By default, code-server will use `0.0.0.0` as its address. This can be changed by using `code-server -h` or `code-server --host=` followed by the address you want to use.
> Example: `code-server -h 127.0.0.1`
### Open
You can have the server automatically open the VS Code in your browser on startup by using the `code server -o` or `code-server --open` flags
### Port
By default, code-server will use `8443` as its port. This can be changed by using `code-server -p` or `code-server --port=` followed by the port you want to use.
### Port
By default, code-server will use `8443` as its port. This can be changed by using `code-server -p` or `code-server --port=` followed by the port you want to use.
> Example: `code-server -p 9000`
### 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.
> 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)

View File

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

View File

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

View File

@@ -1,14 +1,64 @@
import { logger, field } from "@coder/logger";
import { NotificationService, INotificationHandle, INotificationService, Severity } from "./fill/notification";
// tslint:disable no-any can have different return values
interface IRetryItem {
/**
* How many times this item has been retried.
*/
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;
/**
* Whether the item is retrying or waiting to retry.
*/
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.
*/
export class Retry {
private items = new Map<string, IRetryItem>();
private readonly items = new Map<string, IRetryItem>();
// Times are in seconds.
private readonly retryMinDelay = 1;
@@ -50,13 +100,54 @@ export class Retry {
}
/**
* Block retries when we know they will fail (for example when starting Wush
* back up). If a name is passed, that service will still be allowed to retry
* Register a function to retry that starts/connects to a service.
*
* 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).
*
* 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) {
this.blocked = name || true;
this.items.forEach((item) => {
@@ -68,7 +159,7 @@ export class Retry {
/**
* Unblock retries and run any that are pending.
*/
public unblock(): void {
private unblock(): void {
this.blocked = false;
this.items.forEach((item, name) => {
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.
*/
public run(name: string, error?: Error): void {
private run(name: string, error?: Error): void {
if (!this.items.has(name)) {
throw new Error(`"${name}" is not registered`);
}
@@ -149,7 +215,7 @@ export class Retry {
/**
* Reset a service after a successfully recovering.
*/
public recover(name: string): void {
private recover(name: string): void {
if (!this.items.has(name)) {
throw new Error(`"${name}" is not registered`);
}
@@ -191,9 +257,9 @@ export class Retry {
if (this.blocked === name) {
this.unblock();
}
}).catch(() => {
}).catch((error) => {
endItem();
this.run(name);
this.run(name, error);
});
} else {
endItem();
@@ -214,8 +280,7 @@ export class Retry {
const now = Date.now();
const items = Array.from(this.items.entries()).filter(([_, item]) => {
return item.showInNotification
&& typeof item.end !== "undefined"
return typeof item.end !== "undefined"
&& item.end > now
&& item.delay && item.delay >= this.notificationThreshold;
}).sort((a, b) => {

View File

@@ -5,8 +5,8 @@ import { Emitter } from "@coder/events";
import { logger, field } from "@coder/logger";
import { ReadWriteConnection, InitData, SharedProcessData } from "../common/connection";
import { Module, ServerProxy } from "../common/proxy";
import { stringify, parse, moduleToProto, protoToModule, protoToOperatingSystem } from "../common/util";
import { Ping, ServerMessage, ClientMessage, MethodMessage, NamedProxyMessage, NumberedProxyMessage, SuccessMessage, FailMessage, EventMessage, CallbackMessage } from "../proto";
import { argumentToProto, protoToArgument, moduleToProto, protoToModule, protoToOperatingSystem } from "../common/util";
import { Argument, Ping, ServerMessage, ClientMessage, Method, Event, Callback } from "../proto";
import { FsModule, ChildProcessModule, NetModule, NodePtyModule, SpdlogModule, TrashModule } from "./modules";
// tslint:disable no-any
@@ -24,8 +24,8 @@ export class Client {
private messageId = 0;
private callbackId = 0;
private readonly proxies = new Map<number | Module, ProxyData>();
private readonly successEmitter = new Emitter<SuccessMessage>();
private readonly failEmitter = new Emitter<FailMessage>();
private readonly successEmitter = new Emitter<Method.Success>();
private readonly failEmitter = new Emitter<Method.Fail>();
private readonly eventEmitter = new Emitter<{ event: string; args: any[]; }>();
private _initData: InitData | undefined;
@@ -129,19 +129,13 @@ export class Client {
field("event listeners", this.eventEmitter.counts),
]);
const message = new FailMessage();
const message = new Method.Fail();
const error = new Error("disconnected");
message.setResponse(stringify(error));
message.setResponse(argumentToProto(error));
this.failEmitter.emit(message);
this.eventEmitter.emit({ event: "exit", args: [1] });
this.eventEmitter.emit({ event: "close", 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] });
this.eventEmitter.emit({ event: "disconnected", args: [error] });
this.eventEmitter.emit({ event: "done", args: [] });
};
connection.onDown(() => handleDisconnect());
@@ -149,6 +143,12 @@ export class Client {
clearTimeout(this.pingTimeout as any);
this.pingTimeout = undefined;
handleDisconnect();
this.proxies.clear();
this.successEmitter.dispose();
this.failEmitter.dispose();
this.eventEmitter.dispose();
this.initDataEmitter.dispose();
this.sharedProcessActiveEmitter.dispose();
});
connection.onUp(() => this.disconnected = false);
@@ -174,19 +174,29 @@ export class Client {
* Make a remote call for a proxy's method using proto.
*/
private remoteCall(proxyId: number | Module, method: string, args: any[]): Promise<any> {
if (this.disconnected) {
return Promise.reject(new Error("disconnected"));
if (this.disconnected && typeof proxyId === "number") {
// 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();
}
return Promise.reject(
new Error(`Unable to call "${method}" on proxy ${proxyId}: disconnected`),
);
}
const message = new MethodMessage();
const message = new Method();
const id = this.messageId++;
let proxyMessage: NamedProxyMessage | NumberedProxyMessage;
let proxyMessage: Method.Named | Method.Numbered;
if (typeof proxyId === "string") {
proxyMessage = new NamedProxyMessage();
proxyMessage = new Method.Named();
proxyMessage.setModule(moduleToProto(proxyId));
message.setNamedProxy(proxyMessage);
} else {
proxyMessage = new NumberedProxyMessage();
proxyMessage = new Method.Numbered();
proxyMessage.setProxyId(proxyId);
message.setNumberedProxy(proxyMessage);
}
@@ -206,16 +216,14 @@ export class Client {
return callbackId;
};
const stringifiedArgs = args.map((a) => stringify(a, storeCallback));
logger.trace(() => [
"sending",
field("id", id),
field("proxyId", proxyId),
field("method", method),
field("args", stringifiedArgs),
]);
proxyMessage.setArgsList(stringifiedArgs);
proxyMessage.setArgsList(args.map((a) => argumentToProto(a, storeCallback)));
const clientMessage = new ClientMessage();
clientMessage.setMethod(message);
@@ -223,7 +231,7 @@ export class Client {
// The server will send back a fail or success message when the method
// has completed, so we listen for that based on the message's unique ID.
const promise = new Promise((resolve, reject): void => {
const promise = new Promise((resolve, reject): void => {
const dispose = (): void => {
d1.dispose();
d2.dispose();
@@ -237,12 +245,12 @@ export class Client {
const d1 = this.successEmitter.event(id, (message) => {
dispose();
resolve(this.parse(message.getResponse()));
resolve(this.protoToArgument(message.getResponse(), promise));
});
const d2 = this.failEmitter.event(id, (message) => {
dispose();
reject(parse(message.getResponse()));
reject(protoToArgument(message.getResponse()));
});
});
@@ -253,42 +261,54 @@ export class Client {
* Handle all messages from the server.
*/
private async handleMessage(message: ServerMessage): Promise<void> {
if (message.hasInit()) {
const init = message.getInit()!;
this._initData = {
dataDirectory: init.getDataDirectory(),
homeDirectory: init.getHomeDirectory(),
tmpDirectory: init.getTmpDirectory(),
workingDirectory: init.getWorkingDirectory(),
os: protoToOperatingSystem(init.getOperatingSystem()),
shell: init.getShell(),
builtInExtensionsDirectory: init.getBuiltinExtensionsDir(),
};
this.initDataEmitter.emit(this._initData);
} else if (message.hasSuccess()) {
this.emitSuccess(message.getSuccess()!);
} else if (message.hasFail()) {
this.emitFail(message.getFail()!);
} else if (message.hasEvent()) {
await this.emitEvent(message.getEvent()!);
} else if (message.hasCallback()) {
await this.runCallback(message.getCallback()!);
} else if (message.hasSharedProcessActive()) {
const sharedProcessActiveMessage = message.getSharedProcessActive()!;
this.sharedProcessActiveEmitter.emit({
socketPath: sharedProcessActiveMessage.getSocketPath(),
logPath: sharedProcessActiveMessage.getLogPath(),
});
} else if (message.hasPong()) {
// 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
// which would break the ping cycle.
} else {
throw new Error("unknown message type");
switch (message.getMsgCase()) {
case ServerMessage.MsgCase.INIT:
const init = message.getInit()!;
this._initData = {
dataDirectory: init.getDataDirectory(),
homeDirectory: init.getHomeDirectory(),
tmpDirectory: init.getTmpDirectory(),
workingDirectory: init.getWorkingDirectory(),
os: protoToOperatingSystem(init.getOperatingSystem()),
shell: init.getShell(),
extensionsDirectory: init.getExtensionsDirectory(),
builtInExtensionsDirectory: init.getBuiltinExtensionsDir(),
};
this.initDataEmitter.emit(this._initData);
break;
case ServerMessage.MsgCase.SUCCESS:
this.emitSuccess(message.getSuccess()!);
break;
case ServerMessage.MsgCase.FAIL:
this.emitFail(message.getFail()!);
break;
case ServerMessage.MsgCase.EVENT:
await this.emitEvent(message.getEvent()!);
break;
case ServerMessage.MsgCase.CALLBACK:
await this.runCallback(message.getCallback()!);
break;
case ServerMessage.MsgCase.SHARED_PROCESS_ACTIVE:
const sharedProcessActiveMessage = message.getSharedProcessActive()!;
this.sharedProcessActiveEmitter.emit({
socketPath: sharedProcessActiveMessage.getSocketPath(),
logPath: sharedProcessActiveMessage.getLogPath(),
});
break;
case ServerMessage.MsgCase.PONG:
// 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
// which would break the ping cycle.
break;
default:
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(() => [
"received resolve",
field("id", message.getId()),
@@ -297,7 +317,10 @@ export class Client {
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(() => [
"received reject",
field("id", message.getId()),
@@ -313,7 +336,7 @@ export class Client {
* request before it emits. Instead, emit all events from the server so all
* 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 proxyId = message.getNamedEvent()
? protoToModule(message.getNamedEvent()!.getModule())
@@ -324,10 +347,9 @@ export class Client {
"received event",
field("proxyId", proxyId),
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 });
}
@@ -339,7 +361,7 @@ export class Client {
* also only be used when passed together with the method. If they are sent
* 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 proxyId = message.getNamedCallback()
? protoToModule(message.getNamedCallback()!.getModule())
@@ -350,16 +372,15 @@ export class Client {
"running callback",
field("proxyId", proxyId),
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);
}
/**
* Start the ping loop. Does nothing if already pinging.
*/
private startPinging = (): void => {
private readonly startPinging = (): void => {
if (typeof this.pingTimeout !== "undefined") {
return;
}
@@ -450,12 +471,12 @@ export class Client {
callbacks: new Map(),
});
instance.onDone((disconnected: boolean) => {
instance.onDone(() => {
const log = (): void => {
logger.trace(() => [
typeof proxyId === "number" ? "disposed proxy" : "disposed proxy callbacks",
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("success listeners", this.successEmitter.counts),
field("fail listeners", this.failEmitter.counts),
@@ -471,7 +492,7 @@ export class Client {
this.eventEmitter.dispose(proxyId);
log();
};
if (!disconnected) {
if (!this.disconnected) {
instance.dispose().then(dispose).catch(dispose);
} else {
dispose();
@@ -496,10 +517,16 @@ export class Client {
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 {
if (!this.proxies.has(proxyId)) {
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 { Readable, Writable } from "./stream";
// tslint:disable completed-docs
export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.ChildProcess {
public readonly stdin: stream.Writable;
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.stdio = [this.stdin, this.stdout, this.stderr];
this.proxy.getPid().then((pid) => {
this.catch(this.proxy.getPid().then((pid) => {
this._pid = pid;
this._connected = true;
});
}));
this.on("disconnect", () => this._connected = false);
this.on("exit", () => {
this._connected = false;
@@ -48,19 +50,19 @@ export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.C
public kill(): void {
this._killed = true;
this.proxy.kill();
this.catch(this.proxy.kill());
}
public disconnect(): void {
this.proxy.disconnect();
this.catch(this.proxy.disconnect());
}
public ref(): void {
this.proxy.ref();
this.catch(this.proxy.ref());
}
public unref(): void {
this.proxy.unref();
this.catch(this.proxy.unref());
}
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.
}
/**
* Exit and close the process when disconnected.
*/
protected handleDisconnect(): void {
this.emit("exit", 1);
this.emit("close");
}
}
export class ChildProcessModule {

View File

@@ -1,15 +1,50 @@
import * as fs from "fs";
import { callbackify } from "util";
import { ClientProxy } from "../../common/proxy";
import { ClientProxy, Batch } from "../../common/proxy";
import { IEncodingOptions, IEncodingOptionsCallback } from "../../common/util";
import { FsModuleProxy, Stats as IStats, WatcherProxy, WriteStreamProxy } from "../../node/modules/fs";
import { Writable } from "./stream";
// 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 {
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 {
this.proxy.close();
this.catch(this.proxy.close());
}
}
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 => {
if (typeof mode === "function") {
@@ -72,9 +115,7 @@ export class FsModule {
}
public exists = (path: fs.PathLike, callback: (exists: boolean) => void): void => {
callbackify(this.proxy.exists)(path, (exists) => {
callback!(exists as any);
});
this.proxy.exists(path).then((exists) => callback(exists)).catch(() => callback(false));
}
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 => {
callbackify(this.proxy.lstat)(path, (error, stats) => {
callbackify(this.lstatBatch.add)({ path }, (error, stats) => {
callback(error, stats && new Stats(stats));
});
}
@@ -175,7 +216,7 @@ export class FsModule {
callback = options;
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 => {
@@ -203,7 +244,7 @@ export class FsModule {
}
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));
});
}

View File

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

View File

@@ -2,17 +2,28 @@ import * as pty from "node-pty";
import { ClientProxy } from "../../common/proxy";
import { NodePtyModuleProxy, NodePtyProcessProxy } from "../../node/modules/node-pty";
// tslint:disable completed-docs
export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements pty.IPty {
private _pid = -1;
private _process = "";
public constructor(proxyPromise: Promise<NodePtyProcessProxy>) {
super(proxyPromise);
this.proxy.getPid().then((pid) => this._pid = pid);
this.proxy.getProcess().then((process) => this._process = process);
public constructor(
private readonly moduleProxy: NodePtyModuleProxy,
private readonly file: string,
private readonly args: string[] | string,
private readonly options: pty.IPtyForkOptions,
) {
super(moduleProxy.spawn(file, args, options));
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 {
return this._pid;
}
@@ -22,15 +33,21 @@ export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements
}
public resize(columns: number, rows: number): void {
this.proxy.resize(columns, rows);
this.catch(this.proxy.resize(columns, rows));
}
public write(data: string): void {
this.proxy.write(data);
this.catch(this.proxy.write(data));
}
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 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 { RotatingLoggerProxy, SpdlogModuleProxy } from "../../node/modules/spdlog";
// tslint:disable completed-docs
class RotatingLogger extends ClientProxy<RotatingLoggerProxy> implements spdlog.RotatingLogger {
public async trace (message: string): Promise<void> { this.proxy.trace(message); }
public async debug (message: string): Promise<void> { this.proxy.debug(message); }
public async info (message: string): Promise<void> { this.proxy.info(message); }
public async warn (message: string): Promise<void> { this.proxy.warn(message); }
public async error (message: string): Promise<void> { this.proxy.error(message); }
public async critical (message: string): Promise<void> { this.proxy.critical(message); }
public async setLevel (level: number): Promise<void> { this.proxy.setLevel(level); }
public async clearFormatters (): Promise<void> { this.proxy.clearFormatters(); }
public async flush (): Promise<void> { this.proxy.flush(); }
public async drop (): Promise<void> { this.proxy.drop(); }
public constructor(
private readonly moduleProxy: SpdlogModuleProxy,
private readonly name: string,
private readonly filename: string,
private readonly filesize: number,
private readonly filecount: number,
) {
super(moduleProxy.createLogger(name, filename, filesize, filecount));
}
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 {
@@ -21,12 +37,12 @@ export class SpdlogModule {
public constructor(private readonly proxy: SpdlogModuleProxy) {
this.RotatingLogger = class extends RotatingLogger {
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 => {
this.proxy.setAsyncMode(bufferSize, flushInterval);
public setAsyncMode = (bufferSize: number, flushInterval: number): Promise<void> => {
return this.proxy.setAsyncMode(bufferSize, flushInterval);
}
}

View File

@@ -3,6 +3,8 @@ import { callbackify } from "util";
import { ClientProxy } from "../../common/proxy";
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 {
public get writable(): boolean {
throw new Error("not implemented");
@@ -41,13 +43,11 @@ export class Writable<T extends WritableProxy = WritableProxy> extends ClientPro
}
public destroy(): void {
this.proxy.destroy();
this.catch(this.proxy.destroy());
}
public setDefaultEncoding(encoding: string): this {
this.proxy.setDefaultEncoding(encoding);
return this;
return this.catch(this.proxy.setDefaultEncoding(encoding));
}
// 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 {
@@ -146,13 +151,16 @@ export class Readable<T extends IReadableProxy = IReadableProxy> extends ClientP
}
public destroy(): void {
this.proxy.destroy();
this.catch(this.proxy.destroy());
}
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 {
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 { TrashModuleProxy } from "../../node/modules/trash";
// tslint:disable completed-docs
export class TrashModule {
public constructor(private readonly proxy: TrashModuleProxy) {}

View File

@@ -23,6 +23,7 @@ export interface InitData {
readonly homeDirectory: string;
readonly tmpDirectory: string;
readonly shell: string;
readonly extensionsDirectory: 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.
*/
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
* duplex streams.
*/
public constructor(proxyPromise: Promise<T> | T, bindEvents: boolean = true) {
public constructor(
proxyPromise: Promise<T> | T,
private readonly bindEvents: boolean = true,
) {
super();
this.proxy = isPromise(proxyPromise) ? unpromisify(proxyPromise) : proxyPromise;
if (bindEvents) {
this.proxy.onEvent((event, ...args): void => {
this.emit(event, ...args);
this.initialize(proxyPromise);
if (this.bindEvents) {
this.on("disconnected", (error) => {
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.
*/
export interface ServerProxy {
/**
* Dispose the proxy.
*/
dispose(): Promise<void>;
/**
@@ -73,6 +121,9 @@ export interface ServerProxy {
onEvent(cb: (event: string, ...args: any[]) => void): Promise<void>;
}
/**
* Supported top-level module proxies.
*/
export enum Module {
Fs = "fs",
ChildProcess = "child_process",
@@ -81,3 +132,80 @@ export enum Module {
NodePty = "node-pty",
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 { Module, ServerProxy } from "./proxy";
@@ -29,227 +29,144 @@ export type IEncodingOptions = {
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 proxy is possible, provide `storeProxy`.
*/
export const stringify = (
export const argumentToProto = (
value: any,
storeFunction?: (fn: () => void) => number,
storeProxy?: (proxy: ServerProxy) => number,
): string => {
const convert = (currentValue: any): StringifiedValue => {
// Errors don't stringify at all. They just become "{}".
// For some reason when running in Jest errors aren't instances of Error,
// so also check against the values.
): Argument => {
const convert = (currentValue: any): Argument => {
const message = new Argument();
if (currentValue instanceof Error
|| (currentValue && typeof currentValue.message !== "undefined"
&& typeof currentValue.stack !== "undefined")) {
return {
type: "error",
data: {
message: currentValue.message,
stack: currentValue.stack,
code: (currentValue as NodeJS.ErrnoException).code,
},
};
}
// With stringify, Uint8Array gets turned into objects with each index
// becoming a key for some reason. Then trying to do something like write
// that data results in [object Object] being written. Stringify them like
// a Buffer instead. Also handle Buffer so it doesn't get caught by the
// 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)) {
const arg = new Argument.ErrorValue();
arg.setMessage(currentValue.message);
arg.setStack(currentValue.stack);
arg.setCode(currentValue.code);
message.setError(arg);
} else if (currentValue instanceof Uint8Array || currentValue instanceof Buffer) {
const arg = new Argument.BufferValue();
arg.setData(currentValue);
message.setBuffer(arg);
} else if (Array.isArray(currentValue)) {
const arg = new Argument.ArrayValue();
arg.setDataList(currentValue.map(convert));
message.setArray(arg);
} else if (isProxy(currentValue)) {
if (!storeProxy) {
throw new Error("no way to serialize proxy");
}
return {
type: "proxy",
data: {
id: storeProxy(currentValue),
},
};
}
if (currentValue !== null && typeof currentValue === "object") {
const converted: { [key: string]: StringifiedValue } = {};
const arg = new Argument.ProxyValue();
arg.setId(storeProxy(currentValue));
message.setProxy(arg);
} else if (currentValue !== null && typeof currentValue === "object") {
const arg = new Argument.ObjectValue();
const map = arg.getDataMap();
Object.keys(currentValue).forEach((key) => {
converted[key] = convert(currentValue[key]);
map.set(key, convert(currentValue[key]));
});
return {
type: "object",
data: converted,
};
}
// `undefined` can't be stringified.
if (typeof currentValue === "undefined") {
return {
type: "undefined",
};
}
if (typeof currentValue === "function") {
if (!storeFunction) {
throw new Error("no way to serialize function");
message.setObject(arg);
} else if (currentValue === null) {
message.setNull(new Argument.NullValue());
} else {
switch (typeof currentValue) {
case "undefined":
message.setUndefined(new Argument.UndefinedValue());
break;
case "function":
if (!storeFunction) {
throw new Error("no way to serialize function");
}
const arg = new Argument.FunctionValue();
arg.setId(storeFunction(currentValue));
message.setFunction(arg);
break;
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`);
}
return {
type: "function",
data: {
id: storeFunction(currentValue),
},
};
}
if (!isPrimitive(currentValue)) {
throw new Error(`cannot stringify ${typeof currentValue}`);
}
return currentValue;
return message;
};
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 using a remote proxy is supported, provide `createProxy`.
*/
export const parse = (
value?: string,
export const protoToArgument = (
message?: Argument,
runCallback?: (id: number, args: any[]) => void,
createProxy?: (id: number) => ServerProxy,
): any => {
const convert = (currentValue: StringifiedValue): any => {
if (currentValue && !isPrimitive(currentValue)) {
// Would prefer a switch but the types don't seem to work.
if (currentValue.type === "buffer") {
return Buffer.from(currentValue.data);
}
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;
const convert = (currentMessage: Argument): any => {
switch (currentMessage.getMsgCase()) {
case Argument.MsgCase.ERROR:
const errorMessage = currentMessage.getError()!;
const error = new Error(errorMessage.getMessage());
(error as NodeJS.ErrnoException).code = errorMessage.getCode();
(error as any).originalStack = errorMessage.getStack();
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") {
const converted: { [key: string]: any } = {};
Object.keys(currentValue.data).forEach((key) => {
converted[key] = convert(currentValue.data[key]);
return createProxy(currentMessage.getProxy()!.getId());
case Argument.MsgCase.OBJECT:
const obj: { [Key: string]: any } = {};
currentMessage.getObject()!.getDataMap().forEach((argument, key) => {
obj[key] = convert(argument);
});
return converted;
}
if (currentValue.type === "array") {
return currentValue.data.map(convert);
}
if (currentValue.type === "undefined") {
return obj;
case Argument.MsgCase.UNDEFINED:
return undefined;
}
if (currentValue.type === "function") {
case Argument.MsgCase.NULL:
return null;
case Argument.MsgCase.FUNCTION:
if (!runCallback) {
throw new Error("no way to run remote callback");
}
return (...args: any[]): void => {
return runCallback(currentValue.data.id, args);
return runCallback(currentMessage.getFunction()!.getId(), args);
};
}
if (currentValue.type === "proxy") {
if (!createProxy) {
throw new Error("no way to create proxy");
}
return createProxy(currentValue.data.id);
}
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");
}
return currentValue;
};
return value && convert(JSON.parse(value));
return message && convert(message);
};
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) {
case WorkingInitMessage.OperatingSystem.WINDOWS: return OperatingSystem.Windows;
case WorkingInitMessage.OperatingSystem.LINUX: return OperatingSystem.Linux;
case WorkingInitMessage.OperatingSystem.MAC: return OperatingSystem.Mac;
case WorkingInit.OperatingSystem.WINDOWS: return OperatingSystem.Windows;
case WorkingInit.OperatingSystem.LINUX: return OperatingSystem.Linux;
case WorkingInit.OperatingSystem.MAC: return OperatingSystem.Mac;
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) {
case "win32": return WorkingInitMessage.OperatingSystem.WINDOWS;
case "linux": return WorkingInitMessage.OperatingSystem.LINUX;
case "darwin": return WorkingInitMessage.OperatingSystem.MAC;
case "win32": return WorkingInit.OperatingSystem.WINDOWS;
case "linux": return WorkingInit.OperatingSystem.LINUX;
case "darwin": return WorkingInit.OperatingSystem.MAC;
default: throw new Error(`unrecognized platform "${platform}"`);
}
};

View File

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

View File

@@ -4,6 +4,8 @@ import { ServerProxy } from "../../common/proxy";
import { IEncodingOptions } from "../../common/util";
import { WritableProxy } from "./stream";
// tslint:disable completed-docs
/**
* A serializable version of fs.Stats.
*/
@@ -41,13 +43,13 @@ export class WriteStreamProxy extends WritableProxy<fs.WriteStream> {
}
public async dispose(): Promise<void> {
super.dispose();
await super.dispose();
this.stream.close();
}
// tslint:disable-next-line no-any
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));
}
}
@@ -109,7 +111,7 @@ export class FsModuleProxy {
}
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> {
@@ -156,6 +158,10 @@ export class FsModuleProxy {
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> {
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 }> {
const buffer = new Buffer(length);
const buffer = Buffer.alloc(length);
return promisify(fs.read)(fd, buffer, 0, length, position);
}
@@ -182,6 +188,10 @@ export class FsModuleProxy {
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> {
return promisify(fs.readlink)(path, options);
}
@@ -202,6 +212,10 @@ export class FsModuleProxy {
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> {
return promisify(fs.symlink)(target, path, type);
}

View File

@@ -2,6 +2,8 @@ import * as net from "net";
import { ServerProxy } from "../../common/proxy";
import { DuplexProxy } from "./stream";
// tslint:disable completed-docs
export class NetSocketProxy extends DuplexProxy<net.Socket> {
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
@@ -28,7 +30,7 @@ export class NetSocketProxy extends DuplexProxy<net.Socket> {
// tslint:disable-next-line no-any
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("lookup", (error, address, family, host) => cb("lookup", error, address, family, host));
this.stream.on("timeout", () => cb("timeout"));

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
import * as stream from "stream";
import { ServerProxy } from "../../common/proxy";
// tslint:disable completed-docs
export class WritableProxy<T extends stream.Writable = stream.Writable> implements ServerProxy {
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
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("end", () => cb("end"));
}

View File

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

View File

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

View File

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

View File

@@ -12,12 +12,13 @@ var goog = jspb;
var global = Function('return this')();
var node_pb = require('./node_pb.js');
goog.object.extend(proto, node_pb);
var vscode_pb = require('./vscode_pb.js');
goog.object.extend(proto, vscode_pb);
goog.exportSymbol('proto.ClientMessage', null, global);
goog.exportSymbol('proto.ServerMessage', null, global);
goog.exportSymbol('proto.WorkingInitMessage', null, global);
goog.exportSymbol('proto.WorkingInitMessage.OperatingSystem', null, global);
goog.exportSymbol('proto.WorkingInit', null, global);
goog.exportSymbol('proto.WorkingInit.OperatingSystem', null, global);
/**
* Generated by JsPbCodeGenerator.
* @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);
if (goog.DEBUG && !COMPILED) {
/**
* @public
* @override
*/
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
* 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
*/
proto.ClientMessage.toObject = function(includeInstance, msg) {
var f, obj = {
method: (f = msg.getMethod()) && node_pb.MethodMessage.toObject(includeInstance, f),
var obj = {
method: (f = msg.getMethod()) && node_pb.Method.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();
switch (field) {
case 20:
var value = new node_pb.MethodMessage;
reader.readMessage(value,node_pb.MethodMessage.deserializeBinaryFromReader);
var value = new node_pb.Method;
reader.readMessage(value,node_pb.Method.deserializeBinaryFromReader);
msg.setMethod(value);
break;
case 21:
@@ -172,7 +220,7 @@ proto.ClientMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage(
20,
f,
node_pb.MethodMessage.serializeBinaryToWriter
node_pb.Method.serializeBinaryToWriter
);
}
f = message.getPing();
@@ -187,21 +235,24 @@ proto.ClientMessage.serializeBinaryToWriter = function(message, writer) {
/**
* optional MethodMessage method = 20;
* @return {?proto.MethodMessage}
* optional Method method = 20;
* @return {?proto.Method}
*/
proto.ClientMessage.prototype.getMethod = function() {
return /** @type{?proto.MethodMessage} */ (
jspb.Message.getWrapperField(this, node_pb.MethodMessage, 20));
return /** @type{?proto.Method} */ (
jspb.Message.getWrapperField(this, node_pb.Method, 20));
};
/** @param {?proto.MethodMessage|undefined} value */
/** @param {?proto.Method|undefined} value */
proto.ClientMessage.prototype.setMethod = function(value) {
jspb.Message.setOneofWrapperField(this, 20, proto.ClientMessage.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
*/
proto.ClientMessage.prototype.clearMethod = function() {
this.setMethod(undefined);
};
@@ -209,7 +260,7 @@ proto.ClientMessage.prototype.clearMethod = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ClientMessage.prototype.hasMethod = function() {
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() {
this.setPing(undefined);
};
@@ -239,7 +293,7 @@ proto.ClientMessage.prototype.clearPing = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ClientMessage.prototype.hasPing = function() {
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
* 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
*/
proto.ServerMessage.toObject = function(includeInstance, msg) {
var f, obj = {
fail: (f = msg.getFail()) && node_pb.FailMessage.toObject(includeInstance, f),
success: (f = msg.getSuccess()) && node_pb.SuccessMessage.toObject(includeInstance, f),
event: (f = msg.getEvent()) && node_pb.EventMessage.toObject(includeInstance, f),
callback: (f = msg.getCallback()) && node_pb.CallbackMessage.toObject(includeInstance, f),
var obj = {
fail: (f = msg.getFail()) && node_pb.Method.Fail.toObject(includeInstance, f),
success: (f = msg.getSuccess()) && node_pb.Method.Success.toObject(includeInstance, f),
event: (f = msg.getEvent()) && node_pb.Event.toObject(includeInstance, f),
callback: (f = msg.getCallback()) && node_pb.Callback.toObject(includeInstance, f),
pong: (f = msg.getPong()) && node_pb.Pong.toObject(includeInstance, f),
init: (f = msg.getInit()) && proto.WorkingInitMessage.toObject(includeInstance, f),
sharedProcessActive: (f = msg.getSharedProcessActive()) && vscode_pb.SharedProcessActiveMessage.toObject(includeInstance, f)
init: (f = msg.getInit()) && proto.WorkingInit.toObject(includeInstance, f),
sharedProcessActive: (f = msg.getSharedProcessActive()) && vscode_pb.SharedProcessActive.toObject(includeInstance, f)
};
if (includeInstance) {
@@ -368,23 +405,23 @@ proto.ServerMessage.deserializeBinaryFromReader = function(msg, reader) {
var field = reader.getFieldNumber();
switch (field) {
case 13:
var value = new node_pb.FailMessage;
reader.readMessage(value,node_pb.FailMessage.deserializeBinaryFromReader);
var value = new node_pb.Method.Fail;
reader.readMessage(value,node_pb.Method.Fail.deserializeBinaryFromReader);
msg.setFail(value);
break;
case 14:
var value = new node_pb.SuccessMessage;
reader.readMessage(value,node_pb.SuccessMessage.deserializeBinaryFromReader);
var value = new node_pb.Method.Success;
reader.readMessage(value,node_pb.Method.Success.deserializeBinaryFromReader);
msg.setSuccess(value);
break;
case 19:
var value = new node_pb.EventMessage;
reader.readMessage(value,node_pb.EventMessage.deserializeBinaryFromReader);
var value = new node_pb.Event;
reader.readMessage(value,node_pb.Event.deserializeBinaryFromReader);
msg.setEvent(value);
break;
case 22:
var value = new node_pb.CallbackMessage;
reader.readMessage(value,node_pb.CallbackMessage.deserializeBinaryFromReader);
var value = new node_pb.Callback;
reader.readMessage(value,node_pb.Callback.deserializeBinaryFromReader);
msg.setCallback(value);
break;
case 18:
@@ -393,13 +430,13 @@ proto.ServerMessage.deserializeBinaryFromReader = function(msg, reader) {
msg.setPong(value);
break;
case 16:
var value = new proto.WorkingInitMessage;
reader.readMessage(value,proto.WorkingInitMessage.deserializeBinaryFromReader);
var value = new proto.WorkingInit;
reader.readMessage(value,proto.WorkingInit.deserializeBinaryFromReader);
msg.setInit(value);
break;
case 17:
var value = new vscode_pb.SharedProcessActiveMessage;
reader.readMessage(value,vscode_pb.SharedProcessActiveMessage.deserializeBinaryFromReader);
var value = new vscode_pb.SharedProcessActive;
reader.readMessage(value,vscode_pb.SharedProcessActive.deserializeBinaryFromReader);
msg.setSharedProcessActive(value);
break;
default:
@@ -436,7 +473,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage(
13,
f,
node_pb.FailMessage.serializeBinaryToWriter
node_pb.Method.Fail.serializeBinaryToWriter
);
}
f = message.getSuccess();
@@ -444,7 +481,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage(
14,
f,
node_pb.SuccessMessage.serializeBinaryToWriter
node_pb.Method.Success.serializeBinaryToWriter
);
}
f = message.getEvent();
@@ -452,7 +489,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage(
19,
f,
node_pb.EventMessage.serializeBinaryToWriter
node_pb.Event.serializeBinaryToWriter
);
}
f = message.getCallback();
@@ -460,7 +497,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage(
22,
f,
node_pb.CallbackMessage.serializeBinaryToWriter
node_pb.Callback.serializeBinaryToWriter
);
}
f = message.getPong();
@@ -476,7 +513,7 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage(
16,
f,
proto.WorkingInitMessage.serializeBinaryToWriter
proto.WorkingInit.serializeBinaryToWriter
);
}
f = message.getSharedProcessActive();
@@ -484,28 +521,31 @@ proto.ServerMessage.serializeBinaryToWriter = function(message, writer) {
writer.writeMessage(
17,
f,
vscode_pb.SharedProcessActiveMessage.serializeBinaryToWriter
vscode_pb.SharedProcessActive.serializeBinaryToWriter
);
}
};
/**
* optional FailMessage fail = 13;
* @return {?proto.FailMessage}
* optional Method.Fail fail = 13;
* @return {?proto.Method.Fail}
*/
proto.ServerMessage.prototype.getFail = function() {
return /** @type{?proto.FailMessage} */ (
jspb.Message.getWrapperField(this, node_pb.FailMessage, 13));
return /** @type{?proto.Method.Fail} */ (
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) {
jspb.Message.setOneofWrapperField(this, 13, proto.ServerMessage.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearFail = function() {
this.setFail(undefined);
};
@@ -513,7 +553,7 @@ proto.ServerMessage.prototype.clearFail = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ServerMessage.prototype.hasFail = function() {
return jspb.Message.getField(this, 13) != null;
@@ -521,21 +561,24 @@ proto.ServerMessage.prototype.hasFail = function() {
/**
* optional SuccessMessage success = 14;
* @return {?proto.SuccessMessage}
* optional Method.Success success = 14;
* @return {?proto.Method.Success}
*/
proto.ServerMessage.prototype.getSuccess = function() {
return /** @type{?proto.SuccessMessage} */ (
jspb.Message.getWrapperField(this, node_pb.SuccessMessage, 14));
return /** @type{?proto.Method.Success} */ (
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) {
jspb.Message.setOneofWrapperField(this, 14, proto.ServerMessage.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearSuccess = function() {
this.setSuccess(undefined);
};
@@ -543,7 +586,7 @@ proto.ServerMessage.prototype.clearSuccess = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ServerMessage.prototype.hasSuccess = function() {
return jspb.Message.getField(this, 14) != null;
@@ -551,21 +594,24 @@ proto.ServerMessage.prototype.hasSuccess = function() {
/**
* optional EventMessage event = 19;
* @return {?proto.EventMessage}
* optional Event event = 19;
* @return {?proto.Event}
*/
proto.ServerMessage.prototype.getEvent = function() {
return /** @type{?proto.EventMessage} */ (
jspb.Message.getWrapperField(this, node_pb.EventMessage, 19));
return /** @type{?proto.Event} */ (
jspb.Message.getWrapperField(this, node_pb.Event, 19));
};
/** @param {?proto.EventMessage|undefined} value */
/** @param {?proto.Event|undefined} value */
proto.ServerMessage.prototype.setEvent = function(value) {
jspb.Message.setOneofWrapperField(this, 19, proto.ServerMessage.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearEvent = function() {
this.setEvent(undefined);
};
@@ -573,7 +619,7 @@ proto.ServerMessage.prototype.clearEvent = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ServerMessage.prototype.hasEvent = function() {
return jspb.Message.getField(this, 19) != null;
@@ -581,21 +627,24 @@ proto.ServerMessage.prototype.hasEvent = function() {
/**
* optional CallbackMessage callback = 22;
* @return {?proto.CallbackMessage}
* optional Callback callback = 22;
* @return {?proto.Callback}
*/
proto.ServerMessage.prototype.getCallback = function() {
return /** @type{?proto.CallbackMessage} */ (
jspb.Message.getWrapperField(this, node_pb.CallbackMessage, 22));
return /** @type{?proto.Callback} */ (
jspb.Message.getWrapperField(this, node_pb.Callback, 22));
};
/** @param {?proto.CallbackMessage|undefined} value */
/** @param {?proto.Callback|undefined} value */
proto.ServerMessage.prototype.setCallback = function(value) {
jspb.Message.setOneofWrapperField(this, 22, proto.ServerMessage.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearCallback = function() {
this.setCallback(undefined);
};
@@ -603,7 +652,7 @@ proto.ServerMessage.prototype.clearCallback = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ServerMessage.prototype.hasCallback = function() {
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() {
this.setPong(undefined);
};
@@ -633,7 +685,7 @@ proto.ServerMessage.prototype.clearPong = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ServerMessage.prototype.hasPong = function() {
return jspb.Message.getField(this, 18) != null;
@@ -641,21 +693,24 @@ proto.ServerMessage.prototype.hasPong = function() {
/**
* optional WorkingInitMessage init = 16;
* @return {?proto.WorkingInitMessage}
* optional WorkingInit init = 16;
* @return {?proto.WorkingInit}
*/
proto.ServerMessage.prototype.getInit = function() {
return /** @type{?proto.WorkingInitMessage} */ (
jspb.Message.getWrapperField(this, proto.WorkingInitMessage, 16));
return /** @type{?proto.WorkingInit} */ (
jspb.Message.getWrapperField(this, proto.WorkingInit, 16));
};
/** @param {?proto.WorkingInitMessage|undefined} value */
/** @param {?proto.WorkingInit|undefined} value */
proto.ServerMessage.prototype.setInit = function(value) {
jspb.Message.setOneofWrapperField(this, 16, proto.ServerMessage.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearInit = function() {
this.setInit(undefined);
};
@@ -663,7 +718,7 @@ proto.ServerMessage.prototype.clearInit = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ServerMessage.prototype.hasInit = function() {
return jspb.Message.getField(this, 16) != null;
@@ -671,21 +726,24 @@ proto.ServerMessage.prototype.hasInit = function() {
/**
* optional SharedProcessActiveMessage shared_process_active = 17;
* @return {?proto.SharedProcessActiveMessage}
* optional SharedProcessActive shared_process_active = 17;
* @return {?proto.SharedProcessActive}
*/
proto.ServerMessage.prototype.getSharedProcessActive = function() {
return /** @type{?proto.SharedProcessActiveMessage} */ (
jspb.Message.getWrapperField(this, vscode_pb.SharedProcessActiveMessage, 17));
return /** @type{?proto.SharedProcessActive} */ (
jspb.Message.getWrapperField(this, vscode_pb.SharedProcessActive, 17));
};
/** @param {?proto.SharedProcessActiveMessage|undefined} value */
/** @param {?proto.SharedProcessActive|undefined} value */
proto.ServerMessage.prototype.setSharedProcessActive = function(value) {
jspb.Message.setOneofWrapperField(this, 17, proto.ServerMessage.oneofGroups_[0], value);
};
/**
* Clears the message field making it undefined.
*/
proto.ServerMessage.prototype.clearSharedProcessActive = function() {
this.setSharedProcessActive(undefined);
};
@@ -693,7 +751,7 @@ proto.ServerMessage.prototype.clearSharedProcessActive = function() {
/**
* Returns whether this field is set.
* @return {!boolean}
* @return {boolean}
*/
proto.ServerMessage.prototype.hasSharedProcessActive = function() {
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) {
@@ -731,8 +772,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) {
* for transitional soy proto support: http://goto/soy-param-migration
* @return {!Object}
*/
proto.WorkingInitMessage.prototype.toObject = function(opt_includeInstance) {
return proto.WorkingInitMessage.toObject(opt_includeInstance, this);
proto.WorkingInit.prototype.toObject = function(opt_includeInstance) {
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
* instance for transitional soy proto support:
* 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}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.WorkingInitMessage.toObject = function(includeInstance, msg) {
var f, obj = {
proto.WorkingInit.toObject = function(includeInstance, msg) {
var obj = {
homeDirectory: jspb.Message.getFieldWithDefault(msg, 1, ""),
tmpDirectory: jspb.Message.getFieldWithDefault(msg, 2, ""),
dataDirectory: jspb.Message.getFieldWithDefault(msg, 3, ""),
workingDirectory: jspb.Message.getFieldWithDefault(msg, 4, ""),
operatingSystem: jspb.Message.getFieldWithDefault(msg, 5, 0),
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) {
@@ -767,23 +809,23 @@ proto.WorkingInitMessage.toObject = function(includeInstance, msg) {
/**
* Deserializes binary data (in protobuf wire format).
* @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 msg = new proto.WorkingInitMessage;
return proto.WorkingInitMessage.deserializeBinaryFromReader(msg, reader);
var msg = new proto.WorkingInit;
return proto.WorkingInit.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* 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.
* @return {!proto.WorkingInitMessage}
* @return {!proto.WorkingInit}
*/
proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
proto.WorkingInit.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
@@ -807,7 +849,7 @@ proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
msg.setWorkingDirectory(value);
break;
case 5:
var value = /** @type {!proto.WorkingInitMessage.OperatingSystem} */ (reader.readEnum());
var value = /** @type {!proto.WorkingInit.OperatingSystem} */ (reader.readEnum());
msg.setOperatingSystem(value);
break;
case 6:
@@ -818,6 +860,10 @@ proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
var value = /** @type {string} */ (reader.readString());
msg.setBuiltinExtensionsDir(value);
break;
case 8:
var value = /** @type {string} */ (reader.readString());
msg.setExtensionsDirectory(value);
break;
default:
reader.skipField();
break;
@@ -831,9 +877,9 @@ proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.WorkingInitMessage.prototype.serializeBinary = function() {
proto.WorkingInit.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.WorkingInitMessage.serializeBinaryToWriter(this, writer);
proto.WorkingInit.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
@@ -841,11 +887,11 @@ proto.WorkingInitMessage.prototype.serializeBinary = function() {
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.WorkingInitMessage} message
* @param {!proto.WorkingInit} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.WorkingInitMessage.serializeBinaryToWriter = function(message, writer) {
proto.WorkingInit.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getHomeDirectory();
if (f.length > 0) {
@@ -896,13 +942,20 @@ proto.WorkingInitMessage.serializeBinaryToWriter = function(message, writer) {
f
);
}
f = message.getExtensionsDirectory();
if (f.length > 0) {
writer.writeString(
8,
f
);
}
};
/**
* @enum {number}
*/
proto.WorkingInitMessage.OperatingSystem = {
proto.WorkingInit.OperatingSystem = {
WINDOWS: 0,
LINUX: 1,
MAC: 2
@@ -912,13 +965,13 @@ proto.WorkingInitMessage.OperatingSystem = {
* optional string home_directory = 1;
* @return {string}
*/
proto.WorkingInitMessage.prototype.getHomeDirectory = function() {
proto.WorkingInit.prototype.getHomeDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/** @param {string} value */
proto.WorkingInitMessage.prototype.setHomeDirectory = function(value) {
proto.WorkingInit.prototype.setHomeDirectory = function(value) {
jspb.Message.setProto3StringField(this, 1, value);
};
@@ -927,13 +980,13 @@ proto.WorkingInitMessage.prototype.setHomeDirectory = function(value) {
* optional string tmp_directory = 2;
* @return {string}
*/
proto.WorkingInitMessage.prototype.getTmpDirectory = function() {
proto.WorkingInit.prototype.getTmpDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
};
/** @param {string} value */
proto.WorkingInitMessage.prototype.setTmpDirectory = function(value) {
proto.WorkingInit.prototype.setTmpDirectory = function(value) {
jspb.Message.setProto3StringField(this, 2, value);
};
@@ -942,13 +995,13 @@ proto.WorkingInitMessage.prototype.setTmpDirectory = function(value) {
* optional string data_directory = 3;
* @return {string}
*/
proto.WorkingInitMessage.prototype.getDataDirectory = function() {
proto.WorkingInit.prototype.getDataDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
};
/** @param {string} value */
proto.WorkingInitMessage.prototype.setDataDirectory = function(value) {
proto.WorkingInit.prototype.setDataDirectory = function(value) {
jspb.Message.setProto3StringField(this, 3, value);
};
@@ -957,28 +1010,28 @@ proto.WorkingInitMessage.prototype.setDataDirectory = function(value) {
* optional string working_directory = 4;
* @return {string}
*/
proto.WorkingInitMessage.prototype.getWorkingDirectory = function() {
proto.WorkingInit.prototype.getWorkingDirectory = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
};
/** @param {string} value */
proto.WorkingInitMessage.prototype.setWorkingDirectory = function(value) {
proto.WorkingInit.prototype.setWorkingDirectory = function(value) {
jspb.Message.setProto3StringField(this, 4, value);
};
/**
* optional OperatingSystem operating_system = 5;
* @return {!proto.WorkingInitMessage.OperatingSystem}
* @return {!proto.WorkingInit.OperatingSystem}
*/
proto.WorkingInitMessage.prototype.getOperatingSystem = function() {
return /** @type {!proto.WorkingInitMessage.OperatingSystem} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
proto.WorkingInit.prototype.getOperatingSystem = function() {
return /** @type {!proto.WorkingInit.OperatingSystem} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
};
/** @param {!proto.WorkingInitMessage.OperatingSystem} value */
proto.WorkingInitMessage.prototype.setOperatingSystem = function(value) {
/** @param {!proto.WorkingInit.OperatingSystem} value */
proto.WorkingInit.prototype.setOperatingSystem = function(value) {
jspb.Message.setProto3EnumField(this, 5, value);
};
@@ -987,13 +1040,13 @@ proto.WorkingInitMessage.prototype.setOperatingSystem = function(value) {
* optional string shell = 6;
* @return {string}
*/
proto.WorkingInitMessage.prototype.getShell = function() {
proto.WorkingInit.prototype.getShell = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
};
/** @param {string} value */
proto.WorkingInitMessage.prototype.setShell = function(value) {
proto.WorkingInit.prototype.setShell = function(value) {
jspb.Message.setProto3StringField(this, 6, value);
};
@@ -1002,15 +1055,30 @@ proto.WorkingInitMessage.prototype.setShell = function(value) {
* optional string builtin_extensions_dir = 7;
* @return {string}
*/
proto.WorkingInitMessage.prototype.getBuiltinExtensionsDir = function() {
proto.WorkingInit.prototype.getBuiltinExtensionsDir = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, ""));
};
/** @param {string} value */
proto.WorkingInitMessage.prototype.setBuiltinExtensionsDir = function(value) {
proto.WorkingInit.prototype.setBuiltinExtensionsDir = function(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);

View File

@@ -9,84 +9,128 @@ enum Module {
Trash = 5;
}
// A proxy identified by a unique name like "fs".
message NamedProxyMessage {
uint64 id = 1;
Module module = 2;
string method = 3;
repeated string args = 4;
}
message Argument {
message ErrorValue {
string message = 1;
string stack = 2;
string code = 3;
}
// A general proxy identified by an ID like WriteStream.
message NumberedProxyMessage {
uint64 id = 1;
uint64 proxy_id = 2;
string method = 3;
repeated string args = 4;
message BufferValue {
bytes data = 1;
}
message ObjectValue {
map<string, Argument> data = 1;
}
message ArrayValue {
repeated Argument data = 1;
}
message ProxyValue {
uint64 id = 1;
}
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.
message MethodMessage {
message Method {
// A proxy identified by a unique name like "fs".
message Named {
uint64 id = 1;
Module module = 2;
string method = 3;
repeated Argument args = 4;
}
// A general proxy identified by an ID like WriteStream.
message Numbered {
uint64 id = 1;
uint64 proxy_id = 2;
string method = 3;
repeated Argument args = 4;
}
// Remote method failed.
message Fail {
uint64 id = 1;
Argument response = 2;
}
// Remote method succeeded.
message Success {
uint64 id = 1;
Argument response = 2;
}
oneof msg {
NamedProxyMessage named_proxy = 1;
NumberedProxyMessage numbered_proxy = 2;
Method.Named named_proxy = 1;
Method.Numbered numbered_proxy = 2;
}
}
// Call a remote callback.
message CallbackMessage {
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 {
NamedCallbackMessage named_callback = 1;
NumberedCallbackMessage numbered_callback = 2;
Callback.Named named_callback = 1;
Callback.Numbered numbered_callback = 2;
}
}
// 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;
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 uniquely named proxy.
message NamedEventMessage {
Module module = 1;
string event = 2;
repeated string args = 3;
}
// Emit an event on a numbered proxy.
message Numbered {
uint64 proxy_id = 1;
string event = 2;
repeated Argument 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.
message FailMessage {
uint64 id = 1;
string response = 2;
}
// Remote method succeeded.
message SuccessMessage {
uint64 id = 1;
string response = 2;
oneof msg {
Event.Named named_event = 1;
Event.Numbered numbered_event = 2;
}
}
message Ping {}

View File

@@ -3,100 +3,413 @@
import * as jspb from "google-protobuf";
export class NamedProxyMessage extends jspb.Message {
getId(): number;
setId(value: number): void;
export class Argument extends jspb.Message {
hasError(): boolean;
clearError(): void;
getError(): Argument.ErrorValue | undefined;
setError(value?: Argument.ErrorValue): void;
getModule(): Module;
setModule(value: Module): void;
hasBuffer(): boolean;
clearBuffer(): void;
getBuffer(): Argument.BufferValue | undefined;
setBuffer(value?: Argument.BufferValue): void;
getMethod(): string;
setMethod(value: string): void;
hasObject(): boolean;
clearObject(): void;
getObject(): Argument.ObjectValue | undefined;
setObject(value?: Argument.ObjectValue): void;
clearArgsList(): void;
getArgsList(): Array<string>;
setArgsList(value: Array<string>): void;
addArgs(value: string, index?: number): string;
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): NamedProxyMessage.AsObject;
static toObject(includeInstance: boolean, msg: NamedProxyMessage): NamedProxyMessage.AsObject;
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: NamedProxyMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NamedProxyMessage;
static deserializeBinaryFromReader(message: NamedProxyMessage, reader: jspb.BinaryReader): NamedProxyMessage;
static serializeBinaryToWriter(message: Argument, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Argument;
static deserializeBinaryFromReader(message: Argument, reader: jspb.BinaryReader): Argument;
}
export namespace NamedProxyMessage {
export namespace Argument {
export type AsObject = {
id: number,
module: Module,
method: string,
argsList: Array<string>,
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 NumberedProxyMessage extends jspb.Message {
getId(): number;
setId(value: number): void;
getProxyId(): number;
setProxyId(value: number): void;
getMethod(): string;
setMethod(value: string): void;
clearArgsList(): void;
getArgsList(): Array<string>;
setArgsList(value: Array<string>): void;
addArgs(value: string, index?: number): string;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): NumberedProxyMessage.AsObject;
static toObject(includeInstance: boolean, msg: NumberedProxyMessage): NumberedProxyMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: NumberedProxyMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): NumberedProxyMessage;
static deserializeBinaryFromReader(message: NumberedProxyMessage, reader: jspb.BinaryReader): NumberedProxyMessage;
}
export namespace NumberedProxyMessage {
export type AsObject = {
id: number,
proxyId: number,
method: string,
argsList: Array<string>,
}
}
export class MethodMessage extends jspb.Message {
export class Method extends jspb.Message {
hasNamedProxy(): boolean;
clearNamedProxy(): void;
getNamedProxy(): NamedProxyMessage | undefined;
setNamedProxy(value?: NamedProxyMessage): void;
getNamedProxy(): Method.Named | undefined;
setNamedProxy(value?: Method.Named): void;
hasNumberedProxy(): boolean;
clearNumberedProxy(): void;
getNumberedProxy(): NumberedProxyMessage | undefined;
setNumberedProxy(value?: NumberedProxyMessage): void;
getNumberedProxy(): Method.Numbered | undefined;
setNumberedProxy(value?: Method.Numbered): void;
getMsgCase(): MethodMessage.MsgCase;
getMsgCase(): Method.MsgCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): MethodMessage.AsObject;
static toObject(includeInstance: boolean, msg: MethodMessage): MethodMessage.AsObject;
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: MethodMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): MethodMessage;
static deserializeBinaryFromReader(message: MethodMessage, reader: jspb.BinaryReader): MethodMessage;
static serializeBinaryToWriter(message: Method, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Method;
static deserializeBinaryFromReader(message: Method, reader: jspb.BinaryReader): Method;
}
export namespace MethodMessage {
export namespace Method {
export type AsObject = {
namedProxy?: NamedProxyMessage.AsObject,
numberedProxy?: NumberedProxyMessage.AsObject,
namedProxy?: Method.Named.AsObject,
numberedProxy?: Method.Numbered.AsObject,
}
export class Named extends jspb.Message {
getId(): number;
setId(value: number): void;
getModule(): Module;
setModule(value: Module): void;
getMethod(): string;
setMethod(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 = {
id: number,
module: Module,
method: string,
argsList: Array<Argument.AsObject>,
}
}
export class Numbered extends jspb.Message {
getId(): number;
setId(value: number): void;
getProxyId(): number;
setProxyId(value: number): void;
getMethod(): string;
setMethod(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 = {
id: number,
proxyId: number,
method: string,
argsList: Array<Argument.AsObject>,
}
}
export class Fail 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): Fail.AsObject;
static toObject(includeInstance: boolean, msg: Fail): Fail.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: Fail, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Fail;
static deserializeBinaryFromReader(message: Fail, reader: jspb.BinaryReader): Fail;
}
export namespace Fail {
export type AsObject = {
id: number,
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 {
@@ -106,32 +419,92 @@ export namespace MethodMessage {
}
}
export class CallbackMessage extends jspb.Message {
export class Callback extends jspb.Message {
hasNamedCallback(): boolean;
clearNamedCallback(): void;
getNamedCallback(): NamedCallbackMessage | undefined;
setNamedCallback(value?: NamedCallbackMessage): void;
getNamedCallback(): Callback.Named | undefined;
setNamedCallback(value?: Callback.Named): void;
hasNumberedCallback(): boolean;
clearNumberedCallback(): void;
getNumberedCallback(): NumberedCallbackMessage | undefined;
setNumberedCallback(value?: NumberedCallbackMessage): void;
getNumberedCallback(): Callback.Numbered | undefined;
setNumberedCallback(value?: Callback.Numbered): void;
getMsgCase(): CallbackMessage.MsgCase;
getMsgCase(): Callback.MsgCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): CallbackMessage.AsObject;
static toObject(includeInstance: boolean, msg: CallbackMessage): CallbackMessage.AsObject;
toObject(includeInstance?: boolean): Callback.AsObject;
static toObject(includeInstance: boolean, msg: Callback): Callback.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: CallbackMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): CallbackMessage;
static deserializeBinaryFromReader(message: CallbackMessage, reader: jspb.BinaryReader): CallbackMessage;
static serializeBinaryToWriter(message: Callback, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Callback;
static deserializeBinaryFromReader(message: Callback, reader: jspb.BinaryReader): Callback;
}
export namespace CallbackMessage {
export namespace Callback {
export type AsObject = {
namedCallback?: NamedCallbackMessage.AsObject,
numberedCallback?: NumberedCallbackMessage.AsObject,
namedCallback?: Callback.Named.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 {
@@ -141,92 +514,92 @@ export namespace CallbackMessage {
}
}
export class NamedCallbackMessage 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 {
export class Event extends jspb.Message {
hasNamedEvent(): boolean;
clearNamedEvent(): void;
getNamedEvent(): NamedEventMessage | undefined;
setNamedEvent(value?: NamedEventMessage): void;
getNamedEvent(): Event.Named | undefined;
setNamedEvent(value?: Event.Named): void;
hasNumberedEvent(): boolean;
clearNumberedEvent(): void;
getNumberedEvent(): NumberedEventMessage | undefined;
setNumberedEvent(value?: NumberedEventMessage): void;
getNumberedEvent(): Event.Numbered | undefined;
setNumberedEvent(value?: Event.Numbered): void;
getMsgCase(): EventMessage.MsgCase;
getMsgCase(): Event.MsgCase;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): EventMessage.AsObject;
static toObject(includeInstance: boolean, msg: EventMessage): EventMessage.AsObject;
toObject(includeInstance?: boolean): Event.AsObject;
static toObject(includeInstance: boolean, msg: Event): Event.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: EventMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): EventMessage;
static deserializeBinaryFromReader(message: EventMessage, reader: jspb.BinaryReader): EventMessage;
static serializeBinaryToWriter(message: Event, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): Event;
static deserializeBinaryFromReader(message: Event, reader: jspb.BinaryReader): Event;
}
export namespace EventMessage {
export namespace Event {
export type AsObject = {
namedEvent?: NamedEventMessage.AsObject,
numberedEvent?: NumberedEventMessage.AsObject,
namedEvent?: Event.Named.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 {
@@ -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 {
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): Ping.AsObject;

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -11,8 +11,7 @@ var jspb = require('google-protobuf');
var goog = jspb;
var global = Function('return this')();
goog.exportSymbol('proto.SharedProcessActiveMessage', null, global);
goog.exportSymbol('proto.SharedProcessActive', null, global);
/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
@@ -23,15 +22,20 @@ goog.exportSymbol('proto.SharedProcessActiveMessage', null, global);
* @extends {jspb.Message}
* @constructor
*/
proto.SharedProcessActiveMessage = function(opt_data) {
proto.SharedProcessActive = function(opt_data) {
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) {
proto.SharedProcessActiveMessage.displayName = 'proto.SharedProcessActiveMessage';
/**
* @public
* @override
*/
proto.SharedProcessActive.displayName = 'proto.SharedProcessActive';
}
if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* 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
* @return {!Object}
*/
proto.SharedProcessActiveMessage.prototype.toObject = function(opt_includeInstance) {
return proto.SharedProcessActiveMessage.toObject(opt_includeInstance, this);
proto.SharedProcessActive.prototype.toObject = function(opt_includeInstance) {
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
* instance for transitional soy proto support:
* 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}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.SharedProcessActiveMessage.toObject = function(includeInstance, msg) {
var f, obj = {
proto.SharedProcessActive.toObject = function(includeInstance, msg) {
var obj = {
socketPath: jspb.Message.getFieldWithDefault(msg, 1, ""),
logPath: jspb.Message.getFieldWithDefault(msg, 2, "")
};
@@ -74,23 +78,23 @@ proto.SharedProcessActiveMessage.toObject = function(includeInstance, msg) {
/**
* Deserializes binary data (in protobuf wire format).
* @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 msg = new proto.SharedProcessActiveMessage;
return proto.SharedProcessActiveMessage.deserializeBinaryFromReader(msg, reader);
var msg = new proto.SharedProcessActive;
return proto.SharedProcessActive.deserializeBinaryFromReader(msg, reader);
};
/**
* Deserializes binary data (in protobuf wire format) from the
* 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.
* @return {!proto.SharedProcessActiveMessage}
* @return {!proto.SharedProcessActive}
*/
proto.SharedProcessActiveMessage.deserializeBinaryFromReader = function(msg, reader) {
proto.SharedProcessActive.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
@@ -118,9 +122,9 @@ proto.SharedProcessActiveMessage.deserializeBinaryFromReader = function(msg, rea
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.SharedProcessActiveMessage.prototype.serializeBinary = function() {
proto.SharedProcessActive.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.SharedProcessActiveMessage.serializeBinaryToWriter(this, writer);
proto.SharedProcessActive.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};
@@ -128,11 +132,11 @@ proto.SharedProcessActiveMessage.prototype.serializeBinary = function() {
/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.SharedProcessActiveMessage} message
* @param {!proto.SharedProcessActive} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.SharedProcessActiveMessage.serializeBinaryToWriter = function(message, writer) {
proto.SharedProcessActive.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getSocketPath();
if (f.length > 0) {
@@ -155,13 +159,13 @@ proto.SharedProcessActiveMessage.serializeBinaryToWriter = function(message, wri
* optional string socket_path = 1;
* @return {string}
*/
proto.SharedProcessActiveMessage.prototype.getSocketPath = function() {
proto.SharedProcessActive.prototype.getSocketPath = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
};
/** @param {string} value */
proto.SharedProcessActiveMessage.prototype.setSocketPath = function(value) {
proto.SharedProcessActive.prototype.setSocketPath = function(value) {
jspb.Message.setProto3StringField(this, 1, value);
};
@@ -170,13 +174,13 @@ proto.SharedProcessActiveMessage.prototype.setSocketPath = function(value) {
* optional string log_path = 2;
* @return {string}
*/
proto.SharedProcessActiveMessage.prototype.getLogPath = function() {
proto.SharedProcessActive.prototype.getLogPath = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
};
/** @param {string} value */
proto.SharedProcessActiveMessage.prototype.setLogPath = function(value) {
proto.SharedProcessActive.prototype.setLogPath = function(value) {
jspb.Message.setProto3StringField(this, 2, value);
};

View File

@@ -10,7 +10,7 @@ describe("child_process", () => {
const cp = client.modules[Module.ChildProcess];
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());
};
@@ -36,10 +36,10 @@ describe("child_process", () => {
it("should cat", async () => {
const proc = cp.spawn("cat", []);
expect(proc.pid).toBe(-1);
proc.stdin.write("banana");
proc.stdin!.write("banana");
await expect(getStdout(proc)).resolves.toBe("banana");
proc.stdin.end();
proc.stdin!.end();
proc.kill();
expect(proc.pid).toBeGreaterThan(-1);

View File

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

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"
},
"dependencies": {
"@coder/nbin": "^1.0.4",
"@coder/nbin": "^1.0.6",
"commander": "^2.19.0",
"express": "^4.16.4",
"express-static-gzip": "^1.1.3",

View File

@@ -1,5 +1,5 @@
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 { randomFillSync } from "crypto";
import * as fs from "fs";
@@ -19,9 +19,11 @@ import * as commander from "commander";
commander.version(process.env.VERSION || "development")
.name("code-server")
.description("Run VS Code on a remote server.")
.option("--cert")
.option("--cert-key")
.option("-d, --data-dir <value>", "Customize where user-data is stored.")
.option("--cert <value>")
.option("--cert-key <value>")
.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("-o, --open", "Open in the browser on startup.", false)
.option("-p, --port <number>", "Port to bind on.", 8443)
@@ -38,6 +40,10 @@ Error.stackTraceLimit = Infinity;
if (isCli) {
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> => {
const args = commander.args;
@@ -47,6 +53,9 @@ if (isCli) {
readonly host: string;
readonly port: number;
readonly userDataDir?: string;
readonly extensionsDir?: string;
readonly dataDir?: string;
readonly password?: string;
readonly open?: boolean;
@@ -63,7 +72,8 @@ if (isCli) {
const noAuthValue = (commander as any).auth;
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());
if (!fs.existsSync(dataDir)) {
@@ -77,6 +87,7 @@ if (isCli) {
await Promise.all([
fse.mkdirp(cacheHome),
fse.mkdirp(dataDir),
fse.mkdirp(extensionsDir),
fse.mkdirp(workingDir),
]);
@@ -140,12 +151,17 @@ if (isCli) {
}
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
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));
const sharedProcess = new SharedProcess(dataDir, builtInExtensionsDir);
logger.info("Initializing", field("data-dir", dataDir), field("extensions-dir", extensionsDir), field("working-dir", workingDir), field("log-dir", logDir));
const sharedProcess = new SharedProcess(dataDir, extensionsDir, builtInExtensionsDir);
const sendSharedProcessReady = (socket: WebSocket): void => {
const active = new SharedProcessActiveMessage();
const active = new SharedProcessActive();
active.setSocketPath(sharedProcess.socketPath);
active.setLogPath(logDir);
const serverMessage = new ServerMessage();
@@ -192,6 +208,7 @@ if (isCli) {
}
},
serverOptions: {
extensionsDirectory: extensionsDir,
builtInExtensionsDirectory: builtInExtensionsDir,
dataDirectory: dataDir,
workingDirectory: workingDir,
@@ -234,11 +251,16 @@ if (isCli) {
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) {
logger.warn("No certificate specified. \u001B[1mThis could be insecure.");
// 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) {

View File

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

View File

@@ -1,7 +1,9 @@
import * as cp from "child_process";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as vm from "vm";
import { logger } from "@coder/logger";
import { buildDir, isCli } from "../constants";
let ipcMsgBuffer: Buffer[] | undefined = [];
@@ -151,6 +153,13 @@ export const forkModule = (modulePath: string, args?: string[], options?: cp.For
} else {
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;
};

View File

@@ -1,5 +1,6 @@
import { ChildProcess } from "child_process";
import * as fs from "fs";
import * as fse from "fs-extra";
import * as os from "os";
import * as path from "path";
import { forkModule } from "./bootstrapFork";
@@ -7,7 +8,7 @@ import { StdioIpcHandler } from "../ipc";
import { ParsedArgs } from "vs/platform/environment/common/environment";
import { Emitter } from "@coder/events/src";
import { retry } from "@coder/ide/src/retry";
import { logger, field, Level } from "@coder/logger";
import { logger, Level } from "@coder/logger";
export enum SharedProcessState {
Stopped,
@@ -23,123 +24,144 @@ export type SharedProcessEvent = {
};
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 activeProcess: ChildProcess | undefined;
private ipcHandler: StdioIpcHandler | undefined;
private readonly onStateEmitter = new Emitter<SharedProcessEvent>();
public readonly onState = this.onStateEmitter.event;
private readonly retryName = "Shared process";
private readonly logger = logger.named("shared");
private readonly retry = retry.register("Shared process", () => this.connect());
private disposed: boolean = false;
public constructor(
private readonly userDataDir: string,
private readonly extensionsDir: string,
private readonly builtInExtensionsDir: string,
) {
retry.register(this.retryName, () => this.restart());
retry.run(this.retryName);
this.retry.run();
}
public get state(): SharedProcessState {
return this._state;
}
public restart(): void {
if (this.activeProcess && !this.activeProcess.killed) {
this.activeProcess.kill();
}
const extensionsDir = path.join(this.userDataDir, "extensions");
const mkdir = (dir: string): void => {
try {
fs.mkdirSync(dir);
} catch (ex) {
if (ex.code !== "EEXIST" && ex.code !== "EISDIR") {
throw ex;
}
}
};
mkdir(this.userDataDir);
mkdir(extensionsDir);
this.setState({
state: SharedProcessState.Starting,
});
let resolved: boolean = false;
const maybeStop = (error: string): void => {
if (resolved) {
return;
}
this.setState({
error,
state: SharedProcessState.Stopped,
});
if (!this.activeProcess) {
return;
}
this.activeProcess.kill();
};
this.activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain", [], {
env: {
VSCODE_ALLOW_IO: "true",
VSCODE_LOGS: process.env.VSCODE_LOGS,
},
}, this.userDataDir);
if (this.logger.level <= Level.Trace) {
this.activeProcess.stdout.on("data", (data) => {
this.logger.trace(() => ["stdout", field("data", data.toString())]);
});
}
this.activeProcess.on("error", (error) => {
this.logger.error("error", field("error", error));
maybeStop(error.message);
});
this.activeProcess.on("exit", (err) => {
if (this._state !== SharedProcessState.Stopped) {
this.setState({
error: `Exited with ${err}`,
state: SharedProcessState.Stopped,
});
}
retry.run(this.retryName, new Error(`Exited with ${err}`));
});
this.ipcHandler = new StdioIpcHandler(this.activeProcess);
this.ipcHandler.once("handshake:hello", () => {
const data: {
sharedIPCHandle: string;
args: Partial<ParsedArgs>;
logLevel: Level;
} = {
args: {
"builtin-extensions-dir": this.builtInExtensionsDir,
"user-data-dir": this.userDataDir,
"extensions-dir": extensionsDir,
},
logLevel: this.logger.level,
sharedIPCHandle: this.socketPath,
};
this.ipcHandler!.send("handshake:hey there", "", data);
});
this.ipcHandler.once("handshake:im ready", () => {
resolved = true;
retry.recover(this.retryName);
this.setState({
state: SharedProcessState.Ready,
});
});
this.activeProcess.stderr.on("data", (data) => {
this.logger.error("stderr", field("data", data.toString()));
maybeStop(data.toString());
});
}
/**
* 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) {
this.activeProcess.kill();
}
const backupsDir = path.join(this.userDataDir, "Backups");
await Promise.all([
fse.mkdirp(backupsDir),
]);
const workspacesFile = path.join(backupsDir, "workspaces.json");
if (!fs.existsSync(workspacesFile)) {
fs.appendFileSync(workspacesFile, "");
}
const activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain", [], {
env: {
VSCODE_ALLOW_IO: "true",
VSCODE_LOGS: process.env.VSCODE_LOGS,
},
}, this.userDataDir);
this.activeProcess = activeProcess;
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}`);
}
activeProcess.removeAllListeners();
this.setState({
error: error.message,
state: SharedProcessState.Stopped,
});
reject(error);
};
activeProcess.on("error", doReject);
activeProcess.on("exit", doReject);
this.ipcHandler = new StdioIpcHandler(activeProcess);
this.ipcHandler.once("handshake:hello", () => {
const data: {
sharedIPCHandle: string;
args: Partial<ParsedArgs>;
logLevel: Level;
} = {
args: {
"builtin-extensions-dir": this.builtInExtensionsDir,
"user-data-dir": this.userDataDir,
"extensions-dir": this.extensionsDir,
},
logLevel: this.logger.level,
sharedIPCHandle: this.socketPath,
};
this.ipcHandler!.send("handshake:hey there", "", data);
});
this.ipcHandler.once("handshake:im ready", () => {
activeProcess.removeListener("error", doReject);
activeProcess.removeListener("exit", doReject);
resolve();
});
});
return activeProcess;
}
/**
* Set the internal shared process state and emit the state event.
*/
private setState(event: SharedProcessEvent): void {
this._state = event.state;
this.onStateEmitter.emit(event);

View File

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

View File

@@ -8,7 +8,7 @@ export class EnvironmentService extends environment.EnvironmentService {
}
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 _defaultUserData: string | undefined;
private _socketPath: string | undefined;
private _extensionsDirectory: string | undefined;
private _builtInExtensionsDirectory: string | undefined;
private _workingDirectory: string | undefined;
@@ -31,6 +32,14 @@ class Paths {
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 {
if (!this._builtInExtensionsDirectory) {
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._defaultUserData = data.dataDirectory;
this._socketPath = sharedData.socketPath;
this._extensionsDirectory = data.extensionsDirectory;
this._builtInExtensionsDirectory = data.builtInExtensionsDirectory;
this._workingDirectory = data.workingDirectory;
}
@@ -61,5 +71,6 @@ export const _paths = new Paths();
export const getAppDataPath = (): string => _paths.appData;
export const getDefaultUserDataPath = (): string => _paths.defaultUserData;
export const getWorkingDirectory = (): string => _paths.workingDirectory;
export const getExtensionsDirectory = (): string => _paths.extensionsDirectory;
export const getBuiltInExtensionsDirectory = (): string => _paths.builtInExtensionsDirectory;
export const getSocketPath = (): string => _paths.socketPath;

View File

@@ -1,5 +1,6 @@
import * as os from "os";
import { IProgress, INotificationHandle } from "@coder/ide";
import { logger } from "@coder/logger";
import { client } from "./client";
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 { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
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 {
public readonly retry = client.retry;
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
private _serviceCollection: ServiceCollection | undefined;
private _clipboardContextKey: RawContextKey<boolean> | undefined;
/**
* Handle a drop event on the file explorer.
*/
public async handleExternalDrop(target: ExplorerItem | ExplorerModel, originalEvent: DragEvent): Promise<void> {
await client.upload.uploadDropped(
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 {
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));
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 => ({
@@ -57,10 +72,10 @@ export class Workbench {
}));
const targetGroup = resolveTargetGroup();
(this.serviceCollection.get(IEditorService) as IEditorService).openEditors(editors, targetGroup).then(() => {
afterDrop(targetGroup);
});
await (this.serviceCollection.get(IEditorService) as IEditorService).openEditors(editors, targetGroup);
afterDrop(targetGroup);
}).catch((error) => {
logger.error(error.message);
});
}
@@ -115,6 +130,15 @@ export class Workbench {
public set serviceCollection(collection: ServiceCollection) {
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 = {
start: <T>(title: string, task: (progress: IProgress) => Promise<T>, onCancel: () => void): Promise<T> => {
let lastProgress = 0;
@@ -164,6 +188,9 @@ export class Workbench {
};
}
/**
* Start VS Code.
*/
public async initialize(): Promise<void> {
this._clipboardContextKey = new RawContextKey("nativeClipboard", client.clipboard.isEnabled);

View File

@@ -883,7 +883,7 @@ index acb68c8..bee143a 100644
- !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
index 7445d7b..0291dee 100644
index 7445d7b..ba6bf4b 100644
--- a/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';
@@ -899,13 +899,15 @@ index 7445d7b..0291dee 100644
@@ -458 +462 @@ export class Workbench extends Disposable implements IPartService {
- addClasses(document.body, platformClass); // 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 (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 ((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 (isNative && isMacintosh) {
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
@@ -33,0 +34 @@ function getSystemExtensionsRoot(): string {
+ 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
index 484cef9..f728fc8 100644
--- 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
- 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
index 3c78990..545d91a 100644
--- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts
@@ -931,6 +977,37 @@ index 3c78990..545d91a 100644
@@ -130 +130 @@ export class KeyboardMapperFactory {
- if (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
index 6e6fbcc..645bd72 100644
--- 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",
}],
}, {
test: /\.(svg|png|ttf|woff|eot|woff2)$/,
test: /\.(png|ttf|woff|eot|woff2)$/,
use: [{
loader: "file-loader",
options: {
name: "[path][name].[ext]",
},
}],
}, {
test: /\.svg$/,
loader: 'url-loader'
}],
},
plugins: [

View File

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

View File

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

View File

@@ -24,6 +24,13 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
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":
version "4.3.1"
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"
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:
version "3.0.0"
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"
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:
version "1.0.1"
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"
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
mime@^2.3.1:
mime@^2.0.3, mime@^2.3.1:
version "2.4.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.0.tgz#e051fd881358585f3279df333fe694da0bcffdd6"
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"
integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=
neo-async@^2.5.0:
neo-async@^2.5.0, neo-async@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835"
integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==
@@ -3452,6 +3475,11 @@ normalize-path@^2.1.1:
dependencies:
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:
version "1.0.5"
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"
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:
version "0.4.0"
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"
inherits "2"
tar@^4:
tar@^4, tar@^4.4.8:
version "4.4.8"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
@@ -4951,6 +4987,29 @@ terser-webpack-plugin@^1.1.0:
webpack-sources "^1.1.0"
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:
version "3.14.1"
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"
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:
version "1.4.4"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8"