Getting the client to run (#12)
* Clean up workbench and integrate initialization data * Uncomment Electron fill * Run server & client together * Clean up Electron fill & patch * Bind fs methods This makes them usable with the promise form: `promisify(access)(...)`. * Add space between tag and title to browser logger * Add typescript dep to server and default __dirname for path * Serve web files from server * Adjust some dev options * Rework workbench a bit to use a class and catch unexpected errors * No mkdirs for now, fix util fill, use bash with exec * More fills, make general client abstract * More fills * Fix cp.exec * Fix require calls in fs fill being aliased * Create data and storage dir * Implement fs.watch Using exec for now. * Implement storage database fill * Fix os export and homedir * Add comment to use navigator.sendBeacon * Fix fs callbacks (some args are optional) * Make sure data directory exists when passing it back * Update patch * Target es5 * More fills * Add APIs required for bootstrap-fork to function (#15) * Add bootstrap-fork execution * Add createConnection * Bundle bootstrap-fork into cli * Remove .node directory created from spdlog * Fix npm start * Remove unnecessary comment * Add webpack-hot-middleware if CLI env is not set * Add restarting to shared process * Fix starting with yarn
This commit is contained in:
parent
05899b5edf
commit
72bf4547d4
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
lib/vscode
|
||||
lib/vscode*
|
||||
lib/VSCode*
|
||||
node_modules
|
||||
dist
|
||||
|
@ -9,9 +9,9 @@
|
||||
"vscode:clone": "mkdir -p ./lib && test -d ./lib/vscode || git clone https://github.com/Microsoft/vscode/ ./lib/vscode",
|
||||
"vscode:install": "cd ./lib/vscode && git checkout tags/1.30.1 && yarn",
|
||||
"vscode": "npm-run-all vscode:*",
|
||||
"packages:install": "cd ./packages && yarn && ts-node ../scripts/install-packages.ts",
|
||||
"packages:install": "cd ./packages && yarn",
|
||||
"postinstall": "npm-run-all --parallel vscode packages:install build:rules",
|
||||
"start": "webpack-dev-server --hot --config ./webpack.config.app.js",
|
||||
"start": "cd ./packages/server && yarn start",
|
||||
"test": "cd ./packages && yarn test"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -26,9 +26,9 @@
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"os-browserify": "^0.3.0",
|
||||
"preload-webpack-plugin": "^3.0.0-beta.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
"string-replace-loader": "^2.1.1",
|
||||
"style-loader": "^0.23.1",
|
||||
"ts-loader": "^5.3.3",
|
||||
"ts-node": "^7.0.1",
|
||||
@ -39,7 +39,9 @@
|
||||
"webpack": "^4.28.4",
|
||||
"webpack-bundle-analyzer": "^3.0.3",
|
||||
"webpack-cli": "^3.2.1",
|
||||
"webpack-dev-middleware": "^3.5.0",
|
||||
"webpack-dev-server": "^3.1.14",
|
||||
"webpack-hot-middleware": "^2.24.3",
|
||||
"write-file-webpack-plugin": "^4.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -1,11 +1,26 @@
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { field, logger, time, Time } from "@coder/logger";
|
||||
import { escapePath } from "@coder/protocol";
|
||||
import { retry } from "./retry";
|
||||
import { InitData } from "@coder/protocol";
|
||||
import { retry, Retry } from "./retry";
|
||||
import { client } from "./fill/client";
|
||||
import { Clipboard, clipboard } from "./fill/clipboard";
|
||||
|
||||
export interface IURI {
|
||||
|
||||
readonly path: string;
|
||||
readonly fsPath: string;
|
||||
readonly scheme: string;
|
||||
|
||||
}
|
||||
|
||||
export interface IURIFactory {
|
||||
|
||||
/**
|
||||
* Convert the object to an instance of a real URI.
|
||||
*/
|
||||
create<T extends IURI>(uri: IURI): T;
|
||||
file(path: string): IURI;
|
||||
parse(raw: string): IURI;
|
||||
|
||||
export interface IClientOptions {
|
||||
mkDirs?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -14,33 +29,81 @@ export interface IClientOptions {
|
||||
* Everything the client provides is asynchronous so you can wait on what
|
||||
* you need from it without blocking anything else.
|
||||
*
|
||||
* It also provides task management to help asynchronously load and time
|
||||
* external code.
|
||||
* It also provides task management to help asynchronously load and time code.
|
||||
*/
|
||||
export class Client {
|
||||
export abstract class Client {
|
||||
|
||||
public readonly mkDirs: Promise<void>;
|
||||
public readonly retry: Retry = retry;
|
||||
public readonly clipboard: Clipboard = clipboard;
|
||||
public readonly uriFactory: IURIFactory;
|
||||
private start: Time | undefined;
|
||||
private readonly progressElement: HTMLElement | undefined;
|
||||
private tasks: string[];
|
||||
private finishedTaskCount: number;
|
||||
private tasks: string[] = [];
|
||||
private finishedTaskCount = 0;
|
||||
private readonly loadTime: Time;
|
||||
|
||||
public constructor() {
|
||||
logger.info("Loading IDE");
|
||||
|
||||
this.loadTime = time(2500);
|
||||
|
||||
const overlay = document.getElementById("overlay");
|
||||
const logo = document.getElementById("logo");
|
||||
const msgElement = overlay
|
||||
? overlay.querySelector(".message") as HTMLElement
|
||||
: undefined;
|
||||
|
||||
if (overlay && logo) {
|
||||
overlay.addEventListener("mousemove", (event) => {
|
||||
const xPos = ((event.clientX - logo.offsetLeft) / 24).toFixed(2);
|
||||
const yPos = ((logo.offsetTop - event.clientY) / 24).toFixed(2);
|
||||
|
||||
logo.style.transform = `perspective(200px) rotateX(${yPos}deg) rotateY(${xPos}deg)`;
|
||||
});
|
||||
}
|
||||
|
||||
public constructor(options: IClientOptions) {
|
||||
this.tasks = [];
|
||||
this.finishedTaskCount = 0;
|
||||
this.progressElement = typeof document !== "undefined"
|
||||
? document.querySelector("#fill") as HTMLElement
|
||||
: undefined;
|
||||
|
||||
this.mkDirs = this.wrapTask("Creating directories", 100, async () => {
|
||||
if (options.mkDirs && options.mkDirs.length > 0) {
|
||||
await promisify(exec)(`mkdir -p ${options.mkDirs.map(escapePath).join(" ")}`);
|
||||
}
|
||||
require("path").posix = require("path");
|
||||
|
||||
window.addEventListener("contextmenu", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Prevent Firefox from trying to reconnect when the page unloads.
|
||||
window.addEventListener("unload", () => {
|
||||
retry.block();
|
||||
this.retry.block();
|
||||
logger.info("Unloaded");
|
||||
});
|
||||
|
||||
this.uriFactory = this.createUriFactory();
|
||||
|
||||
this.initialize().then(() => {
|
||||
if (overlay) {
|
||||
overlay.style.opacity = "0";
|
||||
overlay.addEventListener("transitionend", () => {
|
||||
overlay.remove();
|
||||
});
|
||||
}
|
||||
logger.info("Load completed", field("duration", this.loadTime));
|
||||
}).catch((error) => {
|
||||
logger.error(error.message);
|
||||
if (overlay) {
|
||||
overlay.classList.add("error");
|
||||
}
|
||||
if (msgElement) {
|
||||
const button = document.createElement("div");
|
||||
button.className = "reload-button";
|
||||
button.innerText = "Reload";
|
||||
button.addEventListener("click", () => {
|
||||
location.reload();
|
||||
});
|
||||
msgElement.innerText = `Failed to load: ${error.message}.`;
|
||||
msgElement.parentElement!.appendChild(button);
|
||||
}
|
||||
logger.warn("Load completed with errors", field("duration", this.loadTime));
|
||||
});
|
||||
}
|
||||
|
||||
@ -48,14 +111,14 @@ export class Client {
|
||||
* Wrap a task in some logging, timing, and progress updates. Can optionally
|
||||
* wait on other tasks which won't count towards this task's time.
|
||||
*/
|
||||
public async wrapTask<T>(description: string, duration: number, task: () => Promise<T>): Promise<T>;
|
||||
public async wrapTask<T, V>(description: string, duration: number, task: (v: V) => Promise<T>, t: Promise<V>): Promise<T>;
|
||||
public async wrapTask<T, V1, V2>(description: string, duration: number, task: (v1: V1, v2: V2) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>): Promise<T>;
|
||||
public async wrapTask<T, V1, V2, V3>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>): Promise<T>;
|
||||
public async wrapTask<T, V1, V2, V3, V4>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3, v4: V4) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>, t4: Promise<V4>): Promise<T>;
|
||||
public async wrapTask<T, V1, V2, V3, V4, V5>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>, t4: Promise<V4>, t5: Promise<V5>): Promise<T>;
|
||||
public async wrapTask<T, V1, V2, V3, V4, V5, V6>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>, t4: Promise<V4>, t5: Promise<V5>, t6: Promise<V6>): Promise<T>;
|
||||
public async wrapTask<T>(
|
||||
public async task<T>(description: string, duration: number, task: () => Promise<T>): Promise<T>;
|
||||
public async task<T, V>(description: string, duration: number, task: (v: V) => Promise<T>, t: Promise<V>): Promise<T>;
|
||||
public async task<T, V1, V2>(description: string, duration: number, task: (v1: V1, v2: V2) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>): Promise<T>;
|
||||
public async task<T, V1, V2, V3>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>): Promise<T>;
|
||||
public async task<T, V1, V2, V3, V4>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3, v4: V4) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>, t4: Promise<V4>): Promise<T>;
|
||||
public async task<T, V1, V2, V3, V4, V5>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>, t4: Promise<V4>, t5: Promise<V5>): Promise<T>;
|
||||
public async task<T, V1, V2, V3, V4, V5, V6>(description: string, duration: number, task: (v1: V1, v2: V2, v3: V3, v4: V4, v5: V5, v6: V6) => Promise<T>, t1: Promise<V1>, t2: Promise<V2>, t3: Promise<V3>, t4: Promise<V4>, t5: Promise<V5>, t6: Promise<V6>): Promise<T>;
|
||||
public async task<T>(
|
||||
description: string, duration: number = 100, task: (...args: any[]) => Promise<T>, ...after: Array<Promise<any>> // tslint:disable-line no-any
|
||||
): Promise<T> {
|
||||
this.tasks.push(description);
|
||||
@ -97,4 +160,21 @@ export class Client {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise that resolves with initialization data.
|
||||
*/
|
||||
public get initData(): Promise<InitData> {
|
||||
return client.initData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the IDE.
|
||||
*/
|
||||
protected abstract initialize(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Create URI factory.
|
||||
*/
|
||||
protected abstract createUriFactory(): IURIFactory;
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Emitter } from "@coder/events";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { field, logger } from "@coder/logger";
|
||||
import { Client, ReadWriteConnection } from "@coder/protocol";
|
||||
import { retry } from "../retry";
|
||||
|
||||
@ -10,27 +10,20 @@ import { retry } from "../retry";
|
||||
class Connection implements ReadWriteConnection {
|
||||
|
||||
private activeSocket: WebSocket | undefined;
|
||||
private readonly messageEmitter: Emitter<Uint8Array>;
|
||||
private readonly closeEmitter: Emitter<void>;
|
||||
private readonly upEmitter: Emitter<void>;
|
||||
private readonly downEmitter: Emitter<void>;
|
||||
private readonly messageBuffer: Uint8Array[];
|
||||
private readonly messageEmitter: Emitter<Uint8Array> = new Emitter();
|
||||
private readonly closeEmitter: Emitter<void> = new Emitter();
|
||||
private readonly upEmitter: Emitter<void> = new Emitter();
|
||||
private readonly downEmitter: Emitter<void> = new Emitter();
|
||||
private readonly messageBuffer: Uint8Array[] = [];
|
||||
private socketTimeoutDelay = 60 * 1000;
|
||||
private retryName = "Web socket";
|
||||
private isUp: boolean | undefined;
|
||||
private closed: boolean | undefined;
|
||||
private isUp: boolean = false;
|
||||
private closed: boolean = false;
|
||||
|
||||
public constructor() {
|
||||
this.messageEmitter = new Emitter();
|
||||
this.closeEmitter = new Emitter();
|
||||
this.upEmitter = new Emitter();
|
||||
this.downEmitter = new Emitter();
|
||||
this.messageBuffer = [];
|
||||
retry.register(this.retryName, () => this.connect());
|
||||
this.connect().catch(() => {
|
||||
retry.block(this.retryName);
|
||||
retry.run(this.retryName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,7 +65,7 @@ class Connection implements ReadWriteConnection {
|
||||
this.closeEmitter.emit();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Connect to the server.
|
||||
*/
|
||||
private async connect(): Promise<void> {
|
||||
@ -116,7 +109,7 @@ class Connection implements ReadWriteConnection {
|
||||
private async openSocket(): Promise<WebSocket> {
|
||||
this.dispose();
|
||||
const socket = new WebSocket(
|
||||
`${location.protocol === "https" ? "wss" : "ws"}://${location.host}/websocket`,
|
||||
`${location.protocol === "https" ? "wss" : "ws"}://${location.host}`,
|
||||
);
|
||||
socket.binaryType = "arraybuffer";
|
||||
this.activeSocket = socket;
|
||||
@ -153,7 +146,5 @@ class Connection implements ReadWriteConnection {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A client for proxying Node APIs based on web sockets.
|
||||
*/
|
||||
// Global instance so all fills can use the same client.
|
||||
export const client = new Client(new Connection());
|
||||
|
132
packages/ide/src/fill/clipboard.ts
Normal file
132
packages/ide/src/fill/clipboard.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import { IDisposable } from "@coder/disposable";
|
||||
import { Emitter } from "@coder/events";
|
||||
|
||||
/**
|
||||
* Native clipboard.
|
||||
*/
|
||||
export class Clipboard {
|
||||
|
||||
private readonly enableEmitter: Emitter<boolean> = new Emitter();
|
||||
private _isEnabled: boolean = false;
|
||||
|
||||
/**
|
||||
* Ask for permission to use the clipboard.
|
||||
*/
|
||||
public initialize(): void {
|
||||
// tslint:disable no-any
|
||||
const navigatorClip = (navigator as any).clipboard;
|
||||
const navigatorPerms = (navigator as any).permissions;
|
||||
// tslint:enable no-any
|
||||
if (navigatorClip && navigatorPerms) {
|
||||
navigatorPerms.query({
|
||||
name: "clipboard-read",
|
||||
}).then((permissionStatus: {
|
||||
onchange: () => void,
|
||||
state: "denied" | "granted" | "prompt",
|
||||
}) => {
|
||||
const updateStatus = (): void => {
|
||||
this._isEnabled = permissionStatus.state !== "denied";
|
||||
this.enableEmitter.emit(this.isEnabled);
|
||||
};
|
||||
updateStatus();
|
||||
permissionStatus.onchange = (): void => {
|
||||
updateStatus();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the native clipboard is supported.
|
||||
*/
|
||||
public get isSupported(): boolean {
|
||||
// tslint:disable no-any
|
||||
return typeof navigator !== "undefined"
|
||||
&& typeof (navigator as any).clipboard !== "undefined"
|
||||
&& typeof (navigator as any).clipboard.readText !== "undefined";
|
||||
// tslint:enable no-any
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when the native clipboard is
|
||||
* enabled/disabled.
|
||||
*/
|
||||
public onPermissionChange(cb: (enabled: boolean) => void): IDisposable {
|
||||
return this.enableEmitter.event(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read text from the clipboard.
|
||||
*/
|
||||
public readText(): Promise<string> {
|
||||
return this.instance ? this.instance.readText() : Promise.resolve("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write text to the clipboard.
|
||||
*/
|
||||
public writeText(value: string): Promise<void> {
|
||||
return this.instance
|
||||
? this.instance.writeText(value)
|
||||
: this.writeTextFallback(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the clipboard is currently enabled.
|
||||
*/
|
||||
public get isEnabled(): boolean {
|
||||
return !!this._isEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return clipboard instance if there is one.
|
||||
*/
|
||||
private get instance(): ({
|
||||
readText(): Promise<string>;
|
||||
writeText(value: string): Promise<void>;
|
||||
}) | undefined {
|
||||
// tslint:disable-next-line no-any
|
||||
return this.isSupported ? (navigator as any).clipboard : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback for writing text to the clipboard.
|
||||
* Taken from https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f
|
||||
*/
|
||||
private writeTextFallback(value: string): Promise<void> {
|
||||
// Note the current focus and selection.
|
||||
const active = document.activeElement as HTMLElement;
|
||||
const selection = document.getSelection();
|
||||
const selected = selection && selection.rangeCount > 0
|
||||
? selection.getRangeAt(0)
|
||||
: false;
|
||||
|
||||
// Insert a hidden textarea to put the text to copy in.
|
||||
const el = document.createElement("textarea");
|
||||
el.value = value;
|
||||
el.setAttribute("readonly", "");
|
||||
el.style.position = "absolute";
|
||||
el.style.left = "-9999px";
|
||||
document.body.appendChild(el);
|
||||
|
||||
// Select the textarea and execute a copy (this will only work as part of a
|
||||
// user interaction).
|
||||
el.select();
|
||||
document.execCommand("copy");
|
||||
|
||||
// Remove the textarea and put focus and selection back to where it was
|
||||
// previously.
|
||||
document.body.removeChild(el);
|
||||
active.focus();
|
||||
if (selected && selection) {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(selected);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Global clipboard instance since it's used in the Electron fill.
|
||||
export const clipboard = new Clipboard();
|
@ -131,7 +131,7 @@ export class Dialog {
|
||||
/**
|
||||
* Display or remove an error.
|
||||
*/
|
||||
public set error(error: string) {
|
||||
public set error(error: string | undefined) {
|
||||
while (this.errors.lastChild) {
|
||||
this.errors.removeChild(this.errors.lastChild);
|
||||
}
|
||||
|
@ -1,352 +1,388 @@
|
||||
// import * as electron from "electron";
|
||||
// import { EventEmitter } from "events";
|
||||
// import * as fs from "fs";
|
||||
// import { getFetchUrl } from "../src/coder/api";
|
||||
// import { escapePath } from "../src/coder/common";
|
||||
// import { wush } from "../src/coder/server";
|
||||
// import { IKey, Dialog } from "./dialog";
|
||||
/// <reference path="../../../../lib/vscode/src/typings/electron.d.ts" />
|
||||
import { exec } from "child_process";
|
||||
import { EventEmitter } from "events";
|
||||
import * as fs from "fs";
|
||||
import { promisify } from "util";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { escapePath } from "@coder/protocol";
|
||||
import { IKey, Dialog as DialogBox } from "./dialog";
|
||||
import { clipboard } from "./clipboard";
|
||||
|
||||
// (global as any).getOpenUrls = () => {
|
||||
// return [];
|
||||
// };
|
||||
// tslint:disable-next-line no-any
|
||||
(global as any).getOpenUrls = (): string[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
// const oldCreateElement = document.createElement;
|
||||
if (typeof document === "undefined") {
|
||||
(<any>global).document = {} as any;
|
||||
}
|
||||
|
||||
// document.createElement = (tagName: string) => {
|
||||
// const createElement = (tagName: string) => {
|
||||
// return oldCreateElement.call(document, tagName);
|
||||
// };
|
||||
const oldCreateElement: <K extends keyof HTMLElementTagNameMap>(
|
||||
tagName: K, options?: ElementCreationOptions,
|
||||
) => HTMLElementTagNameMap[K] = document.createElement;
|
||||
|
||||
// if (tagName === "webview") {
|
||||
// const view = createElement("iframe") as HTMLIFrameElement;
|
||||
// view.style.border = "0px";
|
||||
// const frameID = Math.random().toString();
|
||||
// view.addEventListener("error", (event) => {
|
||||
// console.log("Got iframe error", event.error, event.message);
|
||||
// });
|
||||
// window.addEventListener("message", (event) => {
|
||||
// if (!event.data || !event.data.id) {
|
||||
// return;
|
||||
// }
|
||||
// if (event.data.id !== frameID) {
|
||||
// return;
|
||||
// }
|
||||
// const e = new CustomEvent("ipc-message");
|
||||
// (e as any).channel = event.data.channel;
|
||||
// (e as any).args = event.data.data;
|
||||
// view.dispatchEvent(e);
|
||||
// });
|
||||
// view.sandbox.add("allow-same-origin", "allow-scripts", "allow-popups", "allow-forms");
|
||||
// Object.defineProperty(view, "preload", {
|
||||
// set: (url: string) => {
|
||||
// view.onload = () => {
|
||||
// view.contentDocument.body.id = frameID;
|
||||
// view.contentDocument.body.parentElement.style.overflow = "hidden";
|
||||
// const script = document.createElement("script");
|
||||
// script.src = url;
|
||||
// view.contentDocument.head.appendChild(script);
|
||||
// };
|
||||
// },
|
||||
// });
|
||||
// (view as any).getWebContents = () => undefined;
|
||||
// (view as any).send = (channel: string, ...args) => {
|
||||
// if (args[0] && typeof args[0] === "object" && args[0].contents) {
|
||||
// args[0].contents = (args[0].contents as string).replace(/"(file:\/\/[^"]*)"/g, (m) => `"${getFetchUrl(m)}"`);
|
||||
// args[0].contents = (args[0].contents as string).replace(/"vscode-resource:([^"]*)"/g, (m) => `"${getFetchUrl(m)}"`);
|
||||
// }
|
||||
// view.contentWindow.postMessage({
|
||||
// channel,
|
||||
// data: args,
|
||||
// id: frameID,
|
||||
// }, "*");
|
||||
// };
|
||||
// return view;
|
||||
// }
|
||||
const newCreateElement = <K extends keyof HTMLElementTagNameMap>(tagName: K): HTMLElementTagNameMap[K] => {
|
||||
const createElement = <K extends keyof HTMLElementTagNameMap>(tagName: K): HTMLElementTagNameMap[K] => {
|
||||
return oldCreateElement.call(document, tagName);
|
||||
};
|
||||
|
||||
// return createElement(tagName);
|
||||
// };
|
||||
if (tagName === "webview") {
|
||||
const view = createElement("iframe") as HTMLIFrameElement;
|
||||
view.style.border = "0px";
|
||||
const frameID = Math.random().toString();
|
||||
view.addEventListener("error", (event) => {
|
||||
logger.error("iframe error", field("event", event));
|
||||
});
|
||||
window.addEventListener("message", (event) => {
|
||||
if (!event.data || !event.data.id) {
|
||||
return;
|
||||
}
|
||||
if (event.data.id !== frameID) {
|
||||
return;
|
||||
}
|
||||
const e = new CustomEvent("ipc-message");
|
||||
(e as any).channel = event.data.channel; // tslint:disable-line no-any
|
||||
(e as any).args = event.data.data; // tslint:disable-line no-any
|
||||
view.dispatchEvent(e);
|
||||
});
|
||||
view.sandbox.add("allow-same-origin", "allow-scripts", "allow-popups", "allow-forms");
|
||||
Object.defineProperty(view, "preload", {
|
||||
set: (url: string): void => {
|
||||
view.onload = (): void => {
|
||||
if (view.contentDocument) {
|
||||
view.contentDocument.body.id = frameID;
|
||||
view.contentDocument.body.parentElement!.style.overflow = "hidden";
|
||||
const script = document.createElement("script");
|
||||
script.src = url;
|
||||
view.contentDocument.head.appendChild(script);
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
(view as any).getWebContents = (): void => undefined; // tslint:disable-line no-any
|
||||
(view as any).send = (channel: string, ...args: any[]): void => { // tslint:disable-line no-any
|
||||
if (args[0] && typeof args[0] === "object" && args[0].contents) {
|
||||
// TODO
|
||||
// args[0].contents = (args[0].contents as string).replace(/"(file:\/\/[^"]*)"/g, (m) => `"${getFetchUrl(m)}"`);
|
||||
// args[0].contents = (args[0].contents as string).replace(/"vscode-resource:([^"]*)"/g, (m) => `"${getFetchUrl(m)}"`);
|
||||
}
|
||||
if (view.contentWindow) {
|
||||
view.contentWindow.postMessage({
|
||||
channel,
|
||||
data: args,
|
||||
id: frameID,
|
||||
}, "*");
|
||||
}
|
||||
};
|
||||
|
||||
// const rendererToMainEmitter = new EventEmitter();
|
||||
// const mainToRendererEmitter = new EventEmitter();
|
||||
return view;
|
||||
}
|
||||
|
||||
// module.exports = {
|
||||
// clipboard: {
|
||||
// has: () => {
|
||||
// return false;
|
||||
// },
|
||||
// writeText: (value: string) => {
|
||||
// // Taken from https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f
|
||||
// const active = document.activeElement as HTMLElement;
|
||||
// const el = document.createElement('textarea'); // Create a <textarea> element
|
||||
// el.value = value; // Set its value to the string that you want copied
|
||||
// el.setAttribute('readonly', ''); // Make it readonly to be tamper-proof
|
||||
// el.style.position = 'absolute';
|
||||
// el.style.left = '-9999px'; // Move outside the screen to make it invisible
|
||||
// document.body.appendChild(el); // Append the <textarea> element to the HTML document
|
||||
// const selected =
|
||||
// document.getSelection().rangeCount > 0 // Check if there is any content selected previously
|
||||
// ? document.getSelection().getRangeAt(0) // Store selection if found
|
||||
// : false; // Mark as false to know no selection existed before
|
||||
// el.select(); // Select the <textarea> content
|
||||
// document.execCommand('copy'); // Copy - only works as a result of a user action (e.g. click events)
|
||||
// document.body.removeChild(el); // Remove the <textarea> element
|
||||
// if (selected) { // If a selection existed before copying
|
||||
// document.getSelection().removeAllRanges(); // Unselect everything on the HTML document
|
||||
// document.getSelection().addRange(selected); // Restore the original selection
|
||||
// }
|
||||
// active.focus();
|
||||
// },
|
||||
// },
|
||||
// dialog: {
|
||||
// showSaveDialog: (_: void, options: Electron.SaveDialogOptions, callback: (filename: string) => void): void => {
|
||||
// const defaultPath = options.defaultPath || "/untitled";
|
||||
// const fileIndex = defaultPath.lastIndexOf("/");
|
||||
// const extensionIndex = defaultPath.lastIndexOf(".");
|
||||
// const saveDialogOptions = {
|
||||
// buttons: ["Cancel", "Save"],
|
||||
// detail: "Enter a path for this file",
|
||||
// input: {
|
||||
// value: defaultPath,
|
||||
// selection: {
|
||||
// start: fileIndex === -1 ? 0 : fileIndex + 1,
|
||||
// end: extensionIndex === -1 ? defaultPath.length : extensionIndex,
|
||||
// },
|
||||
// },
|
||||
// message: "Save file",
|
||||
// };
|
||||
return createElement(tagName);
|
||||
};
|
||||
|
||||
// const dialog = new Dialog(saveDialogOptions);
|
||||
// dialog.onAction((action) => {
|
||||
// if (action.key !== IKey.Enter && action.buttonIndex !== 1) {
|
||||
// dialog.hide();
|
||||
// return callback(undefined);
|
||||
// }
|
||||
document.createElement = newCreateElement;
|
||||
|
||||
// const filePath = dialog.inputValue.replace(/\/+$/, "");
|
||||
// const split = filePath.split("/");
|
||||
// const fileName = split.pop();
|
||||
// const parentName = split.pop() || "/";
|
||||
// if (fileName === "") {
|
||||
// dialog.error = "You must enter a file name.";
|
||||
// return;
|
||||
// }
|
||||
class Clipboard {
|
||||
|
||||
// fs.stat(filePath, (error, stats) => {
|
||||
// if (error && error.code === "ENOENT") {
|
||||
// dialog.hide();
|
||||
// callback(filePath);
|
||||
// } else if (error) {
|
||||
// dialog.error = error.message;
|
||||
// } else if (stats.isDirectory()) {
|
||||
// dialog.error = `A directory named "${fileName}" already exists.`;
|
||||
// } else {
|
||||
// dialog.error = undefined;
|
||||
public has(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// const confirmDialog = new Dialog({
|
||||
// message: `A file named "${fileName}" already exists. Do you want to replace it?`,
|
||||
// detail: `The file already exists in "${parentName}". Replacing it will overwrite its contents.`,
|
||||
// buttons: ["Cancel", "Replace"],
|
||||
// });
|
||||
public writeText(value: string): Promise<void> {
|
||||
return clipboard.writeText(value);
|
||||
}
|
||||
|
||||
// confirmDialog.onAction((action) => {
|
||||
// if (action.buttonIndex === 1) {
|
||||
// confirmDialog.hide();
|
||||
// return callback(filePath);
|
||||
// }
|
||||
}
|
||||
|
||||
// confirmDialog.hide();
|
||||
// dialog.show();
|
||||
// });
|
||||
class Shell {
|
||||
|
||||
// dialog.hide();
|
||||
// confirmDialog.show();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// dialog.show();
|
||||
// },
|
||||
// showOpenDialog: () => {
|
||||
// console.log("Trying to show the open dialog");
|
||||
// },
|
||||
// showMessageBox: (_: void, options: Electron.MessageBoxOptions, callback: (button: number, checked: boolean) => void): void => {
|
||||
// const dialog = new Dialog(options);
|
||||
// dialog.onAction((action) => {
|
||||
// dialog.hide();
|
||||
// callback(action.buttonIndex, false);
|
||||
// });
|
||||
// dialog.show();
|
||||
// },
|
||||
// },
|
||||
// remote: {
|
||||
// dialog: {
|
||||
// showOpenDialog: () => {
|
||||
// console.log("Trying to remotely open");
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// webFrame: {
|
||||
// getZoomFactor: () => {
|
||||
// return 1;
|
||||
// },
|
||||
// getZoomLevel: () => {
|
||||
// return 1;
|
||||
// },
|
||||
// setZoomLevel: () => {
|
||||
// return;
|
||||
// },
|
||||
// },
|
||||
// screen: {
|
||||
// getAllDisplays: () => {
|
||||
// return [{
|
||||
// bounds: {
|
||||
// x: 1000,
|
||||
// y: 1000,
|
||||
// },
|
||||
// }];
|
||||
// },
|
||||
// },
|
||||
// app: {
|
||||
// isAccessibilitySupportEnabled: () => {
|
||||
// return false;
|
||||
// },
|
||||
// setAsDefaultProtocolClient: () => {
|
||||
public async moveItemToTrash(path: string): Promise<void> {
|
||||
await promisify(exec)(
|
||||
`trash-put --trash-dir ${escapePath("~/.Trash")} ${escapePath(path)}`,
|
||||
);
|
||||
}
|
||||
|
||||
// },
|
||||
// send: (str) => {
|
||||
// console.log("APP Trying to send", str);
|
||||
// //
|
||||
// },
|
||||
// on: () => {
|
||||
// //
|
||||
// },
|
||||
// once: () => {
|
||||
// //
|
||||
// },
|
||||
// },
|
||||
// // ipcRenderer communicates with ipcMain
|
||||
// ipcRenderer: {
|
||||
// send: (str, ...args) => {
|
||||
// rendererToMainEmitter.emit(str, {
|
||||
// sender: module.exports.ipcMain,
|
||||
// }, ...args);
|
||||
// },
|
||||
// on: (str, listener) => {
|
||||
// mainToRendererEmitter.on(str, listener);
|
||||
// },
|
||||
// once: (str, listener) => {
|
||||
// mainToRendererEmitter.once(str, listener);
|
||||
// },
|
||||
// removeListener: (str, listener) => {
|
||||
// mainToRendererEmitter.removeListener(str, listener);
|
||||
// },
|
||||
// },
|
||||
// ipcMain: {
|
||||
// send: (str, ...args) => {
|
||||
// mainToRendererEmitter.emit(str, {
|
||||
// sender: module.exports.ipcRenderer,
|
||||
// }, ...args);
|
||||
// },
|
||||
// on: (str, listener) => {
|
||||
// rendererToMainEmitter.on(str, listener);
|
||||
// },
|
||||
// once: (str, listener) => {
|
||||
// rendererToMainEmitter.once(str, listener);
|
||||
// },
|
||||
// },
|
||||
// shell: {
|
||||
// moveItemToTrash: async (path) => {
|
||||
// const response = await wush.execute({
|
||||
// command: `trash-put --trash-dir ${escapePath("~/.Trash")} ${escapePath(path)}`,
|
||||
// }).done();
|
||||
// return response.wasSuccessful();
|
||||
// },
|
||||
// },
|
||||
// BrowserWindow: class {
|
||||
}
|
||||
|
||||
// public webContents = {
|
||||
// on: () => {
|
||||
class App extends EventEmitter {
|
||||
|
||||
// },
|
||||
// session: {
|
||||
// webRequest: {
|
||||
// onBeforeRequest: () => {
|
||||
public isAccessibilitySupportEnabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// },
|
||||
public setAsDefaultProtocolClient(): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
// onBeforeSendHeaders: () => {
|
||||
}
|
||||
|
||||
// },
|
||||
class Dialog {
|
||||
|
||||
// onHeadersReceived: () => {
|
||||
public showSaveDialog(_: void, options: Electron.SaveDialogOptions, callback: (filename: string | undefined) => void): void {
|
||||
const defaultPath = options.defaultPath || "/untitled";
|
||||
const fileIndex = defaultPath.lastIndexOf("/");
|
||||
const extensionIndex = defaultPath.lastIndexOf(".");
|
||||
const saveDialogOptions = {
|
||||
buttons: ["Cancel", "Save"],
|
||||
detail: "Enter a path for this file",
|
||||
input: {
|
||||
value: defaultPath,
|
||||
selection: {
|
||||
start: fileIndex === -1 ? 0 : fileIndex + 1,
|
||||
end: extensionIndex === -1 ? defaultPath.length : extensionIndex,
|
||||
},
|
||||
},
|
||||
message: "Save file",
|
||||
};
|
||||
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
// removeAllListeners: () => {
|
||||
const dialog = new DialogBox(saveDialogOptions);
|
||||
dialog.onAction((action) => {
|
||||
if (action.key !== IKey.Enter && action.buttonIndex !== 1) {
|
||||
dialog.hide();
|
||||
|
||||
// },
|
||||
// }
|
||||
return callback(undefined);
|
||||
}
|
||||
|
||||
// public static getFocusedWindow() {
|
||||
// return undefined;
|
||||
// }
|
||||
const inputValue = dialog.inputValue || "";
|
||||
const filePath = inputValue.replace(/\/+$/, "");
|
||||
const split = filePath.split("/");
|
||||
const fileName = split.pop();
|
||||
const parentName = split.pop() || "/";
|
||||
if (fileName === "") {
|
||||
dialog.error = "You must enter a file name.";
|
||||
|
||||
// public isMaximized() {
|
||||
// return false;
|
||||
// }
|
||||
return;
|
||||
}
|
||||
|
||||
// public isFullScreen() {
|
||||
// return false;
|
||||
// }
|
||||
fs.stat(filePath, (error, stats) => {
|
||||
if (error && error.code === "ENOENT") {
|
||||
dialog.hide();
|
||||
callback(filePath);
|
||||
} else if (error) {
|
||||
dialog.error = error.message;
|
||||
} else if (stats.isDirectory()) {
|
||||
dialog.error = `A directory named "${fileName}" already exists.`;
|
||||
} else {
|
||||
dialog.error = undefined;
|
||||
|
||||
// public setMenuBarVisibility(visibility) {
|
||||
// console.log("We are setting the menu bar to ", visibility);
|
||||
// }
|
||||
const confirmDialog = new DialogBox({
|
||||
message: `A file named "${fileName}" already exists. Do you want to replace it?`,
|
||||
detail: `The file already exists in "${parentName}". Replacing it will overwrite its contents.`,
|
||||
buttons: ["Cancel", "Replace"],
|
||||
});
|
||||
|
||||
// public setAutoHideMenuBar() {
|
||||
confirmDialog.onAction((action) => {
|
||||
if (action.buttonIndex === 1) {
|
||||
confirmDialog.hide();
|
||||
|
||||
// }
|
||||
return callback(filePath);
|
||||
}
|
||||
|
||||
// public on() {
|
||||
confirmDialog.hide();
|
||||
dialog.show();
|
||||
});
|
||||
|
||||
// }
|
||||
dialog.hide();
|
||||
confirmDialog.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
// public setTitle(value: string): void {
|
||||
// document.title = value;
|
||||
// }
|
||||
// },
|
||||
// toggleFullScreen: () => {
|
||||
// const doc = document as any;
|
||||
// const isInFullScreen = doc.fullscreenElement
|
||||
// || doc.webkitFullscreenElement
|
||||
// || doc.mozFullScreenElement
|
||||
// || doc.msFullscreenElement;
|
||||
public showOpenDialog(): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
// const body = doc.body;
|
||||
// if (!isInFullScreen) {
|
||||
// if (body.requestFullscreen) {
|
||||
// body.requestFullscreen();
|
||||
// } else if (body.mozRequestFullScreen) {
|
||||
// body.mozRequestFullScreen();
|
||||
// } else if (body.webkitRequestFullScreen) {
|
||||
// body.webkitRequestFullScreen();
|
||||
// } else if (body.msRequestFullscreen) {
|
||||
// body.msRequestFullscreen();
|
||||
// }
|
||||
// } else {
|
||||
// if (doc.exitFullscreen) {
|
||||
// doc.exitFullscreen();
|
||||
// } else if (doc.webkitExitFullscreen) {
|
||||
// doc.webkitExitFullscreen();
|
||||
// } else if (doc.mozCancelFullScreen) {
|
||||
// doc.mozCancelFullScreen();
|
||||
// } else if (doc.msExitFullscreen) {
|
||||
// doc.msExitFullscreen();
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// focusWindow: () => {
|
||||
// console.log("focusing window");
|
||||
// window.focus();
|
||||
// },
|
||||
// };
|
||||
public showMessageBox(_: void, options: Electron.MessageBoxOptions, callback: (button: number | undefined, checked: boolean) => void): void {
|
||||
const dialog = new DialogBox(options);
|
||||
dialog.onAction((action) => {
|
||||
dialog.hide();
|
||||
callback(action.buttonIndex, false);
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WebFrame {
|
||||
|
||||
public getZoomFactor(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public getZoomLevel(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public setZoomLevel(): void {
|
||||
// Nothing.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Screen {
|
||||
|
||||
public getAllDisplays(): [] {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WebRequest extends EventEmitter {
|
||||
|
||||
public onBeforeRequest(): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public onBeforeSendHeaders(): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public onHeadersReceived(): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Session extends EventEmitter {
|
||||
|
||||
public webRequest = new WebRequest();
|
||||
|
||||
public resolveProxy(url: string, callback: (proxy: string) => void): void {
|
||||
// TODO: not sure what this actually does.
|
||||
callback(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WebContents extends EventEmitter {
|
||||
|
||||
public session = new Session();
|
||||
|
||||
}
|
||||
|
||||
class BrowserWindow extends EventEmitter {
|
||||
|
||||
public webContents = new WebContents();
|
||||
private representedFilename: string = "";
|
||||
|
||||
public static getFocusedWindow(): undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
window.focus();
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
window.focus();
|
||||
}
|
||||
|
||||
public reload(): void {
|
||||
location.reload();
|
||||
}
|
||||
|
||||
public isMaximized(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public setFullScreen(fullscreen: boolean): void {
|
||||
if (fullscreen) {
|
||||
document.documentElement.requestFullscreen();
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
public isFullScreen(): boolean {
|
||||
return document.fullscreenEnabled;
|
||||
}
|
||||
|
||||
public isFocused(): boolean {
|
||||
return document.hasFocus();
|
||||
}
|
||||
|
||||
public setMenuBarVisibility(): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public setAutoHideMenuBar(): void {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public setRepresentedFilename(filename: string): void {
|
||||
this.representedFilename = filename;
|
||||
}
|
||||
|
||||
public getRepresentedFilename(): string {
|
||||
return this.representedFilename;
|
||||
}
|
||||
|
||||
public setTitle(value: string): void {
|
||||
document.title = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We won't be able to do a 1 to 1 fill because things like moveItemToTrash for
|
||||
* example returns a boolean while we need a promise.
|
||||
*/
|
||||
class ElectronFill {
|
||||
|
||||
public readonly shell = new Shell();
|
||||
public readonly clipboard = new Clipboard();
|
||||
public readonly app = new App();
|
||||
public readonly dialog = new Dialog();
|
||||
public readonly webFrame = new WebFrame();
|
||||
public readonly screen = new Screen();
|
||||
|
||||
private readonly rendererToMainEmitter = new EventEmitter();
|
||||
private readonly mainToRendererEmitter = new EventEmitter();
|
||||
|
||||
public get BrowserWindow(): typeof BrowserWindow {
|
||||
return BrowserWindow;
|
||||
}
|
||||
|
||||
// tslint:disable no-any
|
||||
public get ipcRenderer(): object {
|
||||
return {
|
||||
send: (str: string, ...args: any[]): void => {
|
||||
this.rendererToMainEmitter.emit(str, {
|
||||
sender: module.exports.ipcMain,
|
||||
}, ...args);
|
||||
},
|
||||
on: (str: string, listener: (...args: any[]) => void): void => {
|
||||
this.mainToRendererEmitter.on(str, listener);
|
||||
},
|
||||
once: (str: string, listener: (...args: any[]) => void): void => {
|
||||
this.mainToRendererEmitter.once(str, listener);
|
||||
},
|
||||
removeListener: (str: string, listener: (...args: any[]) => void): void => {
|
||||
this.mainToRendererEmitter.removeListener(str, listener);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public get ipcMain(): object {
|
||||
return {
|
||||
send: (str: string, ...args: any[]): void => {
|
||||
this.mainToRendererEmitter.emit(str, {
|
||||
sender: module.exports.ipcRenderer,
|
||||
}, ...args);
|
||||
},
|
||||
on: (str: string, listener: (...args: any[]) => void): void => {
|
||||
this.rendererToMainEmitter.on(str, listener);
|
||||
},
|
||||
once: (str: string, listener: (...args: any[]) => void): void => {
|
||||
this.rendererToMainEmitter.once(str, listener);
|
||||
},
|
||||
};
|
||||
}
|
||||
// tslint:enable no-any
|
||||
|
||||
}
|
||||
|
||||
module.exports = new ElectronFill();
|
||||
|
@ -1 +1 @@
|
||||
module.exports = {};
|
||||
export = {};
|
||||
|
38
packages/ide/src/fill/os.ts
Normal file
38
packages/ide/src/fill/os.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { InitData } from "@coder/protocol";
|
||||
import { client } from "./client";
|
||||
|
||||
class OS {
|
||||
|
||||
private _homedir: string | undefined;
|
||||
private _tmpdir: string | undefined;
|
||||
|
||||
public constructor() {
|
||||
client.initData.then((data) => {
|
||||
this.initialize(data);
|
||||
});
|
||||
}
|
||||
|
||||
public homedir(): string {
|
||||
if (typeof this._homedir === "undefined") {
|
||||
throw new Error("not initialized");
|
||||
}
|
||||
|
||||
return this._homedir;
|
||||
}
|
||||
|
||||
public tmpdir(): string {
|
||||
if (typeof this._tmpdir === "undefined") {
|
||||
throw new Error("not initialized");
|
||||
}
|
||||
|
||||
return this._tmpdir;
|
||||
}
|
||||
|
||||
public initialize(data: InitData): void {
|
||||
this._homedir = data.homeDirectory;
|
||||
this._tmpdir = data.tmpDirectory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export = new OS();
|
@ -1,5 +1,4 @@
|
||||
import { implementation as promisify } from "util.promisify";
|
||||
export * from "../../../../node_modules/util";
|
||||
import { implementation } from "util.promisify";
|
||||
|
||||
export {
|
||||
promisify,
|
||||
}
|
||||
export const promisify = implementation;
|
||||
|
@ -1,4 +1,2 @@
|
||||
export * from "./client";
|
||||
export * from "./retry";
|
||||
export * from "./upload";
|
||||
export * from "./uri";
|
||||
|
@ -209,7 +209,7 @@ export class Retry {
|
||||
|
||||
const item = this.items.get(name)!;
|
||||
if (typeof item.timeout === "undefined" && !item.running && typeof item.count !== "undefined") {
|
||||
logger.info(`Recovered connection to ${name.toLowerCase()}`);
|
||||
logger.info(`Connected to ${name.toLowerCase()}`);
|
||||
item.delay = undefined;
|
||||
item.count = undefined;
|
||||
}
|
||||
@ -228,7 +228,7 @@ export class Retry {
|
||||
const retryCountText = item.count <= this.maxImmediateRetries
|
||||
? `[${item.count}/${this.maxImmediateRetries}]`
|
||||
: `[${item.count}]`;
|
||||
logger.info(`Retrying ${name.toLowerCase()} ${retryCountText}...`);
|
||||
logger.info(`Trying ${name.toLowerCase()} ${retryCountText}...`);
|
||||
|
||||
const endItem = (): void => {
|
||||
this.stopItem(item);
|
||||
@ -341,4 +341,6 @@ export class Retry {
|
||||
|
||||
}
|
||||
|
||||
// Global instance so we can block other retries when retrying the main
|
||||
// connection.
|
||||
export const retry = new Retry();
|
||||
|
@ -1,45 +0,0 @@
|
||||
export interface IURI {
|
||||
|
||||
readonly path: string;
|
||||
readonly fsPath: string;
|
||||
readonly scheme: string;
|
||||
|
||||
}
|
||||
|
||||
export interface IURIFactory {
|
||||
|
||||
/**
|
||||
* Convert the object to an instance of a real URI.
|
||||
*/
|
||||
create<T extends IURI>(uri: IURI): T;
|
||||
file(path: string): IURI;
|
||||
parse(raw: string): IURI;
|
||||
|
||||
}
|
||||
|
||||
let activeUriFactory: IURIFactory;
|
||||
|
||||
/**
|
||||
* Get the active URI factory
|
||||
*/
|
||||
export const getFactory = (): IURIFactory => {
|
||||
if (!activeUriFactory) {
|
||||
throw new Error("default uri factory not set");
|
||||
}
|
||||
|
||||
return activeUriFactory;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the active URI factory.
|
||||
*/
|
||||
export const setUriFactory = (factory: IURIFactory): void => {
|
||||
activeUriFactory = factory;
|
||||
};
|
||||
|
||||
export interface IUriSwitcher {
|
||||
|
||||
strip(uri: IURI): IURI;
|
||||
prepend(uri: IURI): IURI;
|
||||
|
||||
}
|
@ -158,6 +158,8 @@ export class BrowserFormatter extends Formatter {
|
||||
+ " padding-bottom: 1px; font-size: 12px; font-weight: bold; color: white;"
|
||||
+ (name.length === 4 ? "padding-left: 3px; padding-right: 4px;" : ""),
|
||||
);
|
||||
// A space to separate the tag from the title.
|
||||
this.push(" ");
|
||||
}
|
||||
|
||||
public push(arg: any, color: string = "inherit", weight: string = "normal"): void { // tslint:disable-line no-any
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"scripts": {
|
||||
"postinstall": "../node_modules/.bin/ts-node ../scripts/install-packages.ts",
|
||||
"test": "jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ReadWriteConnection, InitData, OperatingSystem } from "../common/connection";
|
||||
import { NewEvalMessage, ServerMessage, EvalDoneMessage, EvalFailedMessage, TypedValue, ClientMessage, NewSessionMessage, TTYDimensions, SessionOutputMessage, CloseSessionInputMessage, InitMessage } from "../proto";
|
||||
import { Emitter, Event } from "@coder/events";
|
||||
import { NewEvalMessage, ServerMessage, EvalDoneMessage, EvalFailedMessage, TypedValue, ClientMessage, NewSessionMessage, TTYDimensions, SessionOutputMessage, CloseSessionInputMessage, WorkingInitMessage, NewConnectionMessage } from "../proto";
|
||||
import { Emitter } from "@coder/events";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { ChildProcess, SpawnOptions, ServerProcess } from "./command";
|
||||
import { ChildProcess, SpawnOptions, ServerProcess, ServerSocket, Socket } from "./command";
|
||||
|
||||
/**
|
||||
* Client accepts an arbitrary connection intended to communicate with the Server.
|
||||
@ -13,10 +13,14 @@ export class Client {
|
||||
private evalFailedEmitter: Emitter<EvalFailedMessage> = new Emitter();
|
||||
|
||||
private sessionId: number = 0;
|
||||
private sessions: Map<number, ServerProcess> = new Map();
|
||||
private readonly sessions: Map<number, ServerProcess> = new Map();
|
||||
|
||||
private connectionId: number = 0;
|
||||
private readonly connections: Map<number, ServerSocket> = new Map();
|
||||
|
||||
private _initData: InitData | undefined;
|
||||
private initDataEmitter: Emitter<InitData> = new Emitter();
|
||||
private initDataPromise: Promise<InitData>;
|
||||
|
||||
/**
|
||||
* @param connection Established connection to the server
|
||||
@ -33,14 +37,14 @@ export class Client {
|
||||
logger.error("Failed to handle server message", field("length", data.byteLength), field("exception", ex));
|
||||
}
|
||||
});
|
||||
|
||||
this.initDataPromise = new Promise((resolve): void => {
|
||||
this.initDataEmitter.event(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
public get onInitData(): Event<InitData> {
|
||||
return this.initDataEmitter.event;
|
||||
}
|
||||
|
||||
public get initData(): InitData | undefined {
|
||||
return this._initData;
|
||||
public get initData(): Promise<InitData> {
|
||||
return this.initDataPromise;
|
||||
}
|
||||
|
||||
public evaluate<R>(func: () => R | Promise<R>): Promise<R>;
|
||||
@ -151,6 +155,27 @@ export class Client {
|
||||
return this.doSpawn(modulePath, args, options, true);
|
||||
}
|
||||
|
||||
public createConnection(path: string, callback?: () => void): Socket;
|
||||
public createConnection(port: number, callback?: () => void): Socket;
|
||||
public createConnection(target: string | number, callback?: () => void): Socket {
|
||||
const id = this.connectionId++;
|
||||
const newCon = new NewConnectionMessage();
|
||||
newCon.setId(id);
|
||||
if (typeof target === "string") {
|
||||
newCon.setPath(target);
|
||||
} else {
|
||||
newCon.setPort(target);
|
||||
}
|
||||
const clientMsg = new ClientMessage();
|
||||
clientMsg.setNewConnection(newCon);
|
||||
this.connection.send(clientMsg.serializeBinary());
|
||||
|
||||
const socket = new ServerSocket(this.connection, id, callback);
|
||||
this.connections.set(id, socket);
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
private doSpawn(command: string, args: string[] = [], options?: SpawnOptions, isFork: boolean = false): ChildProcess {
|
||||
const id = this.sessionId++;
|
||||
const newSess = new NewSessionMessage();
|
||||
@ -200,13 +225,13 @@ export class Client {
|
||||
const init = message.getInit()!;
|
||||
let opSys: OperatingSystem;
|
||||
switch (init.getOperatingSystem()) {
|
||||
case InitMessage.OperatingSystem.WINDOWS:
|
||||
case WorkingInitMessage.OperatingSystem.WINDOWS:
|
||||
opSys = OperatingSystem.Windows;
|
||||
break;
|
||||
case InitMessage.OperatingSystem.LINUX:
|
||||
case WorkingInitMessage.OperatingSystem.LINUX:
|
||||
opSys = OperatingSystem.Linux;
|
||||
break;
|
||||
case InitMessage.OperatingSystem.MAC:
|
||||
case WorkingInitMessage.OperatingSystem.MAC:
|
||||
opSys = OperatingSystem.Mac;
|
||||
break;
|
||||
default:
|
||||
@ -253,6 +278,33 @@ export class Client {
|
||||
return;
|
||||
}
|
||||
s.pid = message.getIdentifySession()!.getPid();
|
||||
} else if (message.hasConnectionEstablished()) {
|
||||
const c = this.connections.get(message.getConnectionEstablished()!.getId());
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c.emit("connect");
|
||||
} else if (message.hasConnectionOutput()) {
|
||||
const c = this.connections.get(message.getConnectionOutput()!.getId());
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c.emit("data", Buffer.from(message.getConnectionOutput()!.getData_asU8()));
|
||||
} else if (message.hasConnectionClose()) {
|
||||
const c = this.connections.get(message.getConnectionClose()!.getId());
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c.emit("close");
|
||||
c.emit("end");
|
||||
this.connections.delete(message.getConnectionClose()!.getId());
|
||||
} else if (message.hasConnectionFailure()) {
|
||||
const c = this.connections.get(message.getConnectionFailure()!.getId());
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c.emit("end");
|
||||
this.connections.delete(message.getConnectionFailure()!.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as events from "events";
|
||||
import * as stream from "stream";
|
||||
import { SendableConnection } from "../common/connection";
|
||||
import { ShutdownSessionMessage, ClientMessage, SessionOutputMessage, WriteToSessionMessage, ResizeSessionTTYMessage, TTYDimensions as ProtoTTYDimensions } from "../proto";
|
||||
import { ShutdownSessionMessage, ClientMessage, WriteToSessionMessage, ResizeSessionTTYMessage, TTYDimensions as ProtoTTYDimensions, ConnectionOutputMessage, ConnectionCloseMessage } from "../proto";
|
||||
|
||||
export interface TTYDimensions {
|
||||
readonly columns: number;
|
||||
@ -33,8 +33,8 @@ export interface ChildProcess {
|
||||
|
||||
export class ServerProcess extends events.EventEmitter implements ChildProcess {
|
||||
public readonly stdin = new stream.Writable();
|
||||
public readonly stdout = new stream.Readable({ read: () => true });
|
||||
public readonly stderr = new stream.Readable({ read: () => true });
|
||||
public readonly stdout = new stream.Readable({ read: (): boolean => true });
|
||||
public readonly stderr = new stream.Readable({ read: (): boolean => true });
|
||||
public pid: number | undefined;
|
||||
|
||||
private _killed: boolean = false;
|
||||
@ -77,7 +77,7 @@ export class ServerProcess extends events.EventEmitter implements ChildProcess {
|
||||
this.connection.send(client.serializeBinary());
|
||||
}
|
||||
|
||||
public resize(dimensions: TTYDimensions) {
|
||||
public resize(dimensions: TTYDimensions): void {
|
||||
const resize = new ResizeSessionTTYMessage();
|
||||
resize.setId(this.id);
|
||||
const tty = new ProtoTTYDimensions();
|
||||
@ -89,3 +89,129 @@ export class ServerProcess extends events.EventEmitter implements ChildProcess {
|
||||
this.connection.send(client.serializeBinary());
|
||||
}
|
||||
}
|
||||
|
||||
export interface Socket {
|
||||
readonly destroyed: boolean;
|
||||
readonly connecting: boolean;
|
||||
write(buffer: Buffer): void;
|
||||
end(): void;
|
||||
|
||||
addListener(event: "data", listener: (data: Buffer) => void): this;
|
||||
addListener(event: "close", listener: (hasError: boolean) => void): this;
|
||||
addListener(event: "connect", listener: () => void): this;
|
||||
addListener(event: "end", listener: () => void): this;
|
||||
|
||||
on(event: "data", listener: (data: Buffer) => void): this;
|
||||
on(event: "close", listener: (hasError: boolean) => void): this;
|
||||
on(event: "connect", listener: () => void): this;
|
||||
on(event: "end", listener: () => void): this;
|
||||
|
||||
once(event: "data", listener: (data: Buffer) => void): this;
|
||||
once(event: "close", listener: (hasError: boolean) => void): this;
|
||||
once(event: "connect", listener: () => void): this;
|
||||
once(event: "end", listener: () => void): this;
|
||||
|
||||
removeListener(event: "data", listener: (data: Buffer) => void): this;
|
||||
removeListener(event: "close", listener: (hasError: boolean) => void): this;
|
||||
removeListener(event: "connect", listener: () => void): this;
|
||||
removeListener(event: "end", listener: () => void): this;
|
||||
|
||||
emit(event: "data", data: Buffer): boolean;
|
||||
emit(event: "close"): boolean;
|
||||
emit(event: "connect"): boolean;
|
||||
emit(event: "end"): boolean;
|
||||
}
|
||||
|
||||
|
||||
export class ServerSocket extends events.EventEmitter implements Socket {
|
||||
|
||||
public writable: boolean = true;
|
||||
public readable: boolean = true;
|
||||
|
||||
private _destroyed: boolean = false;
|
||||
private _connecting: boolean = true;
|
||||
|
||||
public constructor(
|
||||
private readonly connection: SendableConnection,
|
||||
private readonly id: number,
|
||||
connectCallback?: () => void,
|
||||
) {
|
||||
super();
|
||||
|
||||
if (connectCallback) {
|
||||
this.once("connect", () => {
|
||||
this._connecting = false;
|
||||
connectCallback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get destroyed(): boolean {
|
||||
return this._destroyed;
|
||||
}
|
||||
|
||||
public get connecting(): boolean {
|
||||
return this._connecting;
|
||||
}
|
||||
|
||||
public write(buffer: Buffer): void {
|
||||
const sendData = new ConnectionOutputMessage();
|
||||
sendData.setId(this.id);
|
||||
sendData.setData(buffer);
|
||||
const client = new ClientMessage();
|
||||
client.setConnectionOutput(sendData);
|
||||
this.connection.send(client.serializeBinary());
|
||||
}
|
||||
|
||||
public end(): void {
|
||||
const closeMsg = new ConnectionCloseMessage();
|
||||
closeMsg.setId(this.id);
|
||||
const client = new ClientMessage();
|
||||
client.setConnectionClose(closeMsg);
|
||||
this.connection.send(client.serializeBinary());
|
||||
}
|
||||
|
||||
public addListener(event: "data", listener: (data: Buffer) => void): this;
|
||||
public addListener(event: "close", listener: (hasError: boolean) => void): this;
|
||||
public addListener(event: "connect", listener: () => void): this;
|
||||
public addListener(event: "end", listener: () => void): this;
|
||||
public addListener(event: string, listener: any): this {
|
||||
return super.addListener(event, listener);
|
||||
}
|
||||
|
||||
public removeListener(event: "data", listener: (data: Buffer) => void): this;
|
||||
public removeListener(event: "close", listener: (hasError: boolean) => void): this;
|
||||
public removeListener(event: "connect", listener: () => void): this;
|
||||
public removeListener(event: "end", listener: () => void): this;
|
||||
public removeListener(event: string, listener: any): this {
|
||||
return super.removeListener(event, listener);
|
||||
}
|
||||
|
||||
public on(event: "data", listener: (data: Buffer) => void): this;
|
||||
public on(event: "close", listener: (hasError: boolean) => void): this;
|
||||
public on(event: "connect", listener: () => void): this;
|
||||
public on(event: "end", listener: () => void): this;
|
||||
public on(event: string, listener: any): this {
|
||||
return super.on(event, listener);
|
||||
}
|
||||
|
||||
public once(event: "data", listener: (data: Buffer) => void): this;
|
||||
public once(event: "close", listener: (hasError: boolean) => void): this;
|
||||
public once(event: "connect", listener: () => void): this;
|
||||
public once(event: "end", listener: () => void): this;
|
||||
public once(event: string, listener: any): this {
|
||||
return super.once(event, listener);
|
||||
}
|
||||
|
||||
public emit(event: "data", data: Buffer): boolean;
|
||||
public emit(event: "close"): boolean;
|
||||
public emit(event: "connect"): boolean;
|
||||
public emit(event: "end"): boolean;
|
||||
public emit(event: string, ...args: any[]): boolean {
|
||||
return super.emit(event, ...args);
|
||||
}
|
||||
|
||||
public setDefaultEncoding(encoding: string): this {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import * as cp from "child_process";
|
||||
import { Client } from "../client";
|
||||
import { useBuffer } from "./util";
|
||||
import { useBuffer } from "../../common/util";
|
||||
|
||||
export class CP {
|
||||
|
||||
@ -13,19 +13,21 @@ export class CP {
|
||||
options?: { encoding?: BufferEncoding | string | "buffer" | null } & cp.ExecOptions | null | ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void),
|
||||
callback?: ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void),
|
||||
): cp.ChildProcess => {
|
||||
const process = this.client.spawn(command);
|
||||
// TODO: Probably should add an `exec` instead of using `spawn`, especially
|
||||
// since bash might not be available.
|
||||
const childProcess = this.client.spawn("bash", ["-c", command.replace(/"/g, "\\\"")]);
|
||||
|
||||
let stdout = "";
|
||||
process.stdout.on("data", (data) => {
|
||||
childProcess.stdout.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
let stderr = "";
|
||||
process.stderr.on("data", (data) => {
|
||||
childProcess.stderr.on("data", (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
process.on("exit", (exitCode) => {
|
||||
childProcess.on("exit", (exitCode) => {
|
||||
const error = exitCode !== 0 ? new Error(stderr) : null;
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
@ -39,15 +41,15 @@ export class CP {
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
return process;
|
||||
return childProcess;
|
||||
}
|
||||
|
||||
public fork(modulePath: string): cp.ChildProcess {
|
||||
public fork = (modulePath: string, args?: ReadonlyArray<string> | cp.ForkOptions, options?: cp.ForkOptions): cp.ChildProcess => {
|
||||
//@ts-ignore
|
||||
return this.client.fork(modulePath);
|
||||
return this.client.fork(modulePath, args, options);
|
||||
}
|
||||
|
||||
public spawn(command: string, args?: ReadonlyArray<string> | cp.SpawnOptions, _options?: cp.SpawnOptions): cp.ChildProcess {
|
||||
public spawn = (command: string, args?: ReadonlyArray<string> | cp.SpawnOptions, options?: cp.SpawnOptions): cp.ChildProcess => {
|
||||
// TODO: fix this ignore. Should check for args or options here
|
||||
//@ts-ignore
|
||||
return this.client.spawn(command, args, options);
|
||||
|
@ -1,9 +1,20 @@
|
||||
import { exec, ChildProcess } from "child_process";
|
||||
import { EventEmitter } from "events";
|
||||
import * as fs from "fs";
|
||||
import { IEncodingOptions, IEncodingOptionsCallback, escapePath, useBuffer } from "../../common/util";
|
||||
import { Client } from "../client";
|
||||
|
||||
// Use this to get around Webpack inserting our fills.
|
||||
// TODO: is there a better way?
|
||||
declare var _require: typeof require;
|
||||
|
||||
/**
|
||||
* Implements the native fs module
|
||||
* Doesn't use `implements typeof import("fs")` to remove need for __promisify__ impls
|
||||
*
|
||||
* TODO: For now we can't use async in the evaluate calls because they get
|
||||
* transpiled to TypeScript's helpers. tslib is included but we also need to set
|
||||
* _this somehow which the __awaiter helper uses.
|
||||
*/
|
||||
export class FS {
|
||||
|
||||
@ -11,34 +22,46 @@ export class FS {
|
||||
private readonly client: Client,
|
||||
) { }
|
||||
|
||||
public access(path: fs.PathLike, mode: number | undefined, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public access = (path: fs.PathLike, mode: number | undefined | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof mode === "function") {
|
||||
callback = mode;
|
||||
mode = undefined;
|
||||
}
|
||||
this.client.evaluate((path, mode) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.access)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback(undefined!);
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public appendFile(file: fs.PathLike | number, data: any, options: { encoding?: string | null, mode?: string | number, flag?: string } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
// tslint:disable-next-line no-any
|
||||
public appendFile = (file: fs.PathLike | number, data: any, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
this.client.evaluate((path, data, options) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.appendFile)(path, data, options);
|
||||
}, file, data, options).then(() => {
|
||||
callback(undefined!);
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public chmod(path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public chmod = (path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((path, mode) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.chmod)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback(undefined!);
|
||||
@ -47,10 +70,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public chown(path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public chown = (path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((path, uid, gid) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.chown)(path, uid, gid);
|
||||
}, path, uid, gid).then(() => {
|
||||
callback(undefined!);
|
||||
@ -59,10 +83,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public close(fd: number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public close = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((fd) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.close)(fd);
|
||||
}, fd).then(() => {
|
||||
callback(undefined!);
|
||||
@ -71,26 +96,31 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public copyFile(src: fs.PathLike, dest: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
this.client.evaluate((src, dest) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
return util.promisify(fs.copyFile)(src, dest);
|
||||
}, src, dest).then(() => {
|
||||
callback(undefined!);
|
||||
public copyFile = (src: fs.PathLike, dest: fs.PathLike, flags: number | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof flags === "function") {
|
||||
callback = flags;
|
||||
}
|
||||
this.client.evaluate((src, dest, flags) => {
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.copyFile)(src, dest, flags);
|
||||
}, src, dest, typeof flags !== "function" ? flags : undefined).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public createWriteStream(): void {
|
||||
public createWriteStream = (): void => {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public exists(path: fs.PathLike, callback: (exists: boolean) => void): void {
|
||||
public exists = (path: fs.PathLike, callback: (exists: boolean) => void): void => {
|
||||
this.client.evaluate((path) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.exists)(path);
|
||||
}, path).then((r) => {
|
||||
callback(r);
|
||||
@ -99,10 +129,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public fchmod(fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public fchmod = (fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((fd, mode) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fchmod)(fd, mode);
|
||||
}, fd, mode).then(() => {
|
||||
callback(undefined!);
|
||||
@ -111,10 +142,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public fchown(fd: number, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public fchown = (fd: number, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((fd, uid, gid) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fchown)(fd, uid, gid);
|
||||
}, fd, uid, gid).then(() => {
|
||||
callback(undefined!);
|
||||
@ -123,10 +155,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public fdatasync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public fdatasync = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((fd) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fdatasync)(fd);
|
||||
}, fd).then(() => {
|
||||
callback(undefined!);
|
||||
@ -135,11 +168,12 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public fstat(fd: number, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void {
|
||||
this.client.evaluate(async (fd) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const stats = await util.promisify(fs.fstat)(fd);
|
||||
public fstat = (fd: number, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
|
||||
this.client.evaluate((fd) => {
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fstat)(fd).then((stats) => {
|
||||
return {
|
||||
...stats,
|
||||
_isBlockDevice: stats.isBlockDevice(),
|
||||
@ -150,6 +184,7 @@ export class FS {
|
||||
_isSocket: stats.isSocket(),
|
||||
_isSymbolicLink: stats.isSymbolicLink(),
|
||||
};
|
||||
});
|
||||
}, fd).then((stats) => {
|
||||
callback(undefined!, Stats.fromObject(stats));
|
||||
}).catch((ex) => {
|
||||
@ -157,10 +192,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public fsync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public fsync = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((fd) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fsync)(fd);
|
||||
}, fd).then(() => {
|
||||
callback(undefined!);
|
||||
@ -169,22 +205,28 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public ftruncate(fd: number, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public ftruncate = (fd: number, len: number | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof len === "function") {
|
||||
callback = len;
|
||||
len = undefined;
|
||||
}
|
||||
this.client.evaluate((fd, len) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.ftruncate)(fd, len);
|
||||
}, fd, len).then(() => {
|
||||
callback(undefined!);
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public futimes(fd: number, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public futimes = (fd: number, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((fd, atime, mtime) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.futimes)(fd, atime, mtime);
|
||||
}, fd, atime, mtime).then(() => {
|
||||
callback(undefined!);
|
||||
@ -193,10 +235,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public lchmod(path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public lchmod = (path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((path, mode) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.lchmod)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback(undefined!);
|
||||
@ -205,10 +248,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public lchown(path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public lchown = (path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((path, uid, gid) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.lchown)(path, uid, gid);
|
||||
}, path, uid, gid).then(() => {
|
||||
callback(undefined!);
|
||||
@ -217,10 +261,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public link(existingPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public link = (existingPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((existingPath, newPath) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.link)(existingPath, newPath);
|
||||
}, existingPath, newPath).then(() => {
|
||||
callback(undefined!);
|
||||
@ -229,11 +274,12 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public lstat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void {
|
||||
this.client.evaluate(async (path) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const stats = await util.promisify(fs.lstat)(path);
|
||||
public lstat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
|
||||
this.client.evaluate((path) => {
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.lstat)(path).then((stats) => {
|
||||
return {
|
||||
...stats,
|
||||
_isBlockDevice: stats.isBlockDevice(),
|
||||
@ -244,6 +290,7 @@ export class FS {
|
||||
_isSocket: stats.isSocket(),
|
||||
_isSymbolicLink: stats.isSymbolicLink(),
|
||||
};
|
||||
});
|
||||
}, path).then((stats) => {
|
||||
callback(undefined!, Stats.fromObject(stats));
|
||||
}).catch((ex) => {
|
||||
@ -251,54 +298,70 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public mkdir(path: fs.PathLike, mode: number | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public mkdir = (path: fs.PathLike, mode: number | string | fs.MakeDirectoryOptions | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof mode === "function") {
|
||||
callback = mode;
|
||||
mode = undefined;
|
||||
}
|
||||
this.client.evaluate((path, mode) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.mkdir)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback(undefined!);
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public mkdtemp(prefix: string, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, folder: string) => void): void {
|
||||
public mkdtemp = (prefix: string, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, folder: string | Buffer) => void): void => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
this.client.evaluate((prefix, options) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.mkdtemp)(prefix, options);
|
||||
}, prefix, options).then((folder) => {
|
||||
callback(undefined!, folder);
|
||||
callback!(undefined!, folder);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public open(path: fs.PathLike, flags: string | number, mode: string | number | undefined | null, callback: (err: NodeJS.ErrnoException, fd: number) => void): void {
|
||||
public open = (path: fs.PathLike, flags: string | number, mode: string | number | undefined | null | ((err: NodeJS.ErrnoException, fd: number) => void), callback?: (err: NodeJS.ErrnoException, fd: number) => void): void => {
|
||||
if (typeof mode === "function") {
|
||||
callback = mode;
|
||||
mode = undefined;
|
||||
}
|
||||
this.client.evaluate((path, flags, mode) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.open)(path, flags, mode);
|
||||
}, path, flags, mode).then((fd) => {
|
||||
callback(undefined!, fd);
|
||||
callback!(undefined!, fd);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public read<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number, length: number, position: number | null, callback: (err: NodeJS.ErrnoException, bytesRead: number, buffer: TBuffer) => void): void {
|
||||
this.client.evaluate(async (fd, bufferLength, length, position) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
public read = <TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number, length: number, position: number | null, callback: (err: NodeJS.ErrnoException, bytesRead: number, buffer: TBuffer) => void): void => {
|
||||
this.client.evaluate((fd, length, position) => {
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
const buffer = new Buffer(length);
|
||||
const resp = await util.promisify(fs.read)(fd, buffer, 0, length, position);
|
||||
|
||||
return util.promisify(fs.read)(fd, buffer, 0, length, position).then((resp) => {
|
||||
return {
|
||||
bytesRead: resp.bytesRead,
|
||||
content: buffer.toString("utf8"),
|
||||
};
|
||||
}, fd, buffer.byteLength, length, position).then((resp) => {
|
||||
}):
|
||||
}, fd, length, position).then((resp) => {
|
||||
const newBuf = Buffer.from(resp.content, "utf8");
|
||||
buffer.set(newBuf, offset);
|
||||
callback(undefined!, resp.bytesRead, newBuf as TBuffer);
|
||||
@ -307,60 +370,80 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public readFile(path: fs.PathLike | number, options: string | { encoding?: string | null | undefined; flag?: string | undefined; } | null | undefined, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void {
|
||||
this.client.evaluate(async (path, options) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const value = await util.promisify(fs.readFile)(path, options);
|
||||
public readFile = (path: fs.PathLike | number, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
this.client.evaluate((path, options) => {
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return value.toString();
|
||||
return util.promisify(fs.readFile)(path, options).then((value) => value.toString());
|
||||
}, path, options).then((buffer) => {
|
||||
callback(undefined!, buffer);
|
||||
callback!(undefined!, buffer);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public readdir(path: fs.PathLike, options: { encoding: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, files: string[]) => void): void {
|
||||
public readdir = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, files: Buffer[] | fs.Dirent[] | string[]) => void): void => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
// TODO: options can also take `withFileTypes` but the types aren't working.
|
||||
this.client.evaluate((path, options) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.readdir)(path, options);
|
||||
}, path, options).then((files) => {
|
||||
callback(undefined!, files);
|
||||
callback!(undefined!, files);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public readlink(path: fs.PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, linkString: string) => void): void {
|
||||
public readlink = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, linkString: string | Buffer) => void): void => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
this.client.evaluate((path, options) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.readlink)(path, options);
|
||||
}, path, options).then((linkString) => {
|
||||
callback(undefined!, linkString);
|
||||
callback!(undefined!, linkString);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public realpath(path: fs.PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => void): void {
|
||||
public realpath = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, resolvedPath: string | Buffer) => void): void => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
this.client.evaluate((path, options) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.realpath)(path, options);
|
||||
}, path, options).then((resolvedPath) => {
|
||||
callback(undefined!, resolvedPath);
|
||||
callback!(undefined!, resolvedPath);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public rename(oldPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public rename = (oldPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((oldPath, newPath) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.rename)(oldPath, newPath);
|
||||
}, oldPath, newPath).then(() => {
|
||||
callback(undefined!);
|
||||
@ -369,10 +452,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public rmdir(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public rmdir = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((path) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.rmdir)(path);
|
||||
}, path).then(() => {
|
||||
callback(undefined!);
|
||||
@ -381,11 +465,12 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public stat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void {
|
||||
this.client.evaluate(async (path) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const stats = await util.promisify(fs.stat)(path);
|
||||
public stat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
|
||||
this.client.evaluate((path) => {
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.stat)(path).then((stats) => {
|
||||
return {
|
||||
...stats,
|
||||
_isBlockDevice: stats.isBlockDevice(),
|
||||
@ -396,6 +481,7 @@ export class FS {
|
||||
_isSocket: stats.isSocket(),
|
||||
_isSymbolicLink: stats.isSymbolicLink(),
|
||||
};
|
||||
});
|
||||
}, path).then((stats) => {
|
||||
callback(undefined!, Stats.fromObject(stats));
|
||||
}).catch((ex) => {
|
||||
@ -403,34 +489,45 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public symlink(target: fs.PathLike, path: fs.PathLike, type: fs.symlink.Type | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public symlink = (target: fs.PathLike, path: fs.PathLike, type: fs.symlink.Type | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof type === "function") {
|
||||
callback = type;
|
||||
type = undefined;
|
||||
}
|
||||
this.client.evaluate((target, path, type) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.symlink)(target, path, type);
|
||||
}, target, path, type).then(() => {
|
||||
callback(undefined!);
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public truncate(path: fs.PathLike, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public truncate = (path: fs.PathLike, len: number | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof len === "function") {
|
||||
callback = len;
|
||||
len = undefined;
|
||||
}
|
||||
this.client.evaluate((path, len) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.truncate)(path, len);
|
||||
}, path, len).then(() => {
|
||||
callback(undefined!);
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public unlink(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public unlink = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((path) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.unlink)(path);
|
||||
}, path).then(() => {
|
||||
callback(undefined!);
|
||||
@ -439,10 +536,11 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public utimes(path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
public utimes = (path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((path, atime, mtime) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.utimes)(path, atime, mtime);
|
||||
}, path, atime, mtime).then(() => {
|
||||
callback(undefined!);
|
||||
@ -451,38 +549,112 @@ export class FS {
|
||||
});
|
||||
}
|
||||
|
||||
public write<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined, length: number | undefined, position: number | undefined, callback: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void {
|
||||
this.client.evaluate(async (fd, buffer, offset, length, position) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const resp = await util.promisify(fs.write)(fd, Buffer.from(buffer, "utf8"), offset, length, position);
|
||||
public write = <TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined, length: number | undefined, position: number | undefined | ((err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void), callback?: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void => {
|
||||
if (typeof position === "function") {
|
||||
callback = position;
|
||||
position = undefined;
|
||||
}
|
||||
this.client.evaluate((fd, buffer, offset, length, position) => {
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.write)(fd, Buffer.from(buffer, "utf8"), offset, length, position).then((resp) => {
|
||||
return {
|
||||
bytesWritten: resp.bytesWritten,
|
||||
content: resp.buffer.toString("utf8"),
|
||||
}
|
||||
};
|
||||
});
|
||||
}, fd, buffer.toString(), offset, length, position).then((r) => {
|
||||
callback(undefined!, r.bytesWritten, Buffer.from(r.content, "utf8") as TBuffer);
|
||||
callback!(undefined!, r.bytesWritten, Buffer.from(r.content, "utf8") as TBuffer);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!, undefined!);
|
||||
callback!(ex, undefined!, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public writeFile(path: fs.PathLike | number, data: any, options: { encoding?: string | null; mode?: number | string; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
|
||||
// tslint:disable-next-line no-any
|
||||
public writeFile = (path: fs.PathLike | number, data: any, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException) => void): void => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
this.client.evaluate((path, data, options) => {
|
||||
const fs = require("fs") as typeof import("fs");
|
||||
const util = require("util") as typeof import("util");
|
||||
const fs = _require("fs") as typeof import("fs");
|
||||
const util = _require("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.writeFile)(path, data, options);
|
||||
}, path, data, options).then(() => {
|
||||
callback(undefined!);
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public watch = (filename: fs.PathLike, options: IEncodingOptions, listener?: ((event: string, filename: string) => void) | ((event: string, filename: Buffer) => void)): fs.FSWatcher => {
|
||||
// TODO: can we modify `evaluate` for long-running processes like watch?
|
||||
// Especially since inotifywait might not be available.
|
||||
const buffer = new NewlineInputBuffer((msg): void => {
|
||||
msg = msg.trim();
|
||||
const index = msg.lastIndexOf(":");
|
||||
const events = msg.substring(index + 1).split(",");
|
||||
const baseFilename = msg.substring(0, index).split("/").pop();
|
||||
events.forEach((event) => {
|
||||
switch (event) {
|
||||
// Rename is emitted when a file appears or disappears in the directory.
|
||||
case "CREATE":
|
||||
case "DELETE":
|
||||
case "MOVED_FROM":
|
||||
case "MOVED_TO":
|
||||
watcher.emit("rename", baseFilename);
|
||||
break;
|
||||
case "CLOSE_WRITE":
|
||||
watcher.emit("change", baseFilename);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: `exec` is undefined for some reason.
|
||||
const process = exec(`inotifywait ${escapePath(filename.toString())} -m --format "%w%f:%e"`);
|
||||
process.on("exit", (exitCode) => {
|
||||
watcher.emit("error", new Error(`process terminated unexpectedly with code ${exitCode}`));
|
||||
});
|
||||
process.stdout.on("data", (data) => {
|
||||
buffer.push(data);
|
||||
});
|
||||
|
||||
const watcher = new Watcher(process);
|
||||
if (listener) {
|
||||
const l = listener;
|
||||
watcher.on("change", (filename) => {
|
||||
// @ts-ignore not sure how to make this work.
|
||||
l("change", useBuffer(options) ? Buffer.from(filename) : filename);
|
||||
});
|
||||
watcher.on("rename", (filename) => {
|
||||
// @ts-ignore not sure how to make this work.
|
||||
l("rename", useBuffer(options) ? Buffer.from(filename) : filename);
|
||||
});
|
||||
}
|
||||
|
||||
return watcher;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Watcher extends EventEmitter implements fs.FSWatcher {
|
||||
|
||||
public constructor(private readonly process: ChildProcess) {
|
||||
super();
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.process.kill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Stats implements fs.Stats {
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public static fromObject(object: any): Stats {
|
||||
return new Stats(object);
|
||||
}
|
||||
@ -575,3 +747,39 @@ class Stats implements fs.Stats {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for safely taking input and turning it into separate messages.
|
||||
* Assumes that messages are split by newlines.
|
||||
*/
|
||||
export class NewlineInputBuffer {
|
||||
|
||||
private callback: (msg: string) => void;
|
||||
private buffer: string | undefined;
|
||||
|
||||
public constructor(callback: (msg: string) => void) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data to be buffered.
|
||||
*/
|
||||
public push(data: string | Uint8Array): void {
|
||||
let input = typeof data === "string" ? data : data.toString();
|
||||
if (this.buffer) {
|
||||
input = this.buffer + input;
|
||||
this.buffer = undefined;
|
||||
}
|
||||
const lines = input.split("\n");
|
||||
const length = lines.length - 1;
|
||||
const lastLine = lines[length];
|
||||
if (lastLine.length > 0) {
|
||||
this.buffer = lastLine;
|
||||
}
|
||||
lines.pop(); // This is either the line we buffered or an empty string.
|
||||
for (let i = 0; i < length; ++i) {
|
||||
this.callback(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as net from "net";
|
||||
import { Client } from '../client';
|
||||
|
||||
type NodeNet = typeof net;
|
||||
|
||||
@ -7,6 +8,10 @@ type NodeNet = typeof net;
|
||||
*/
|
||||
export class Net implements NodeNet {
|
||||
|
||||
public constructor(
|
||||
private readonly client: Client,
|
||||
) {}
|
||||
|
||||
public get Socket(): typeof net.Socket {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
@ -19,8 +24,9 @@ export class Net implements NodeNet {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public createConnection(): net.Socket {
|
||||
throw new Error("not implemented");
|
||||
public createConnection(...args: any[]): net.Socket {
|
||||
//@ts-ignore
|
||||
return this.client.createConnection(...args) as net.Socket;
|
||||
}
|
||||
|
||||
public isIP(_input: string): number {
|
||||
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Return true if the options specify to use a Buffer instead of string.
|
||||
*/
|
||||
export const useBuffer = (options: { encoding?: string | null } | string | undefined | null | Function): boolean => {
|
||||
return options === "buffer"
|
||||
|| (!!options && typeof options !== "string" && typeof options !== "function"
|
||||
&& (options.encoding === "buffer" || options.encoding === null));
|
||||
};
|
@ -12,3 +12,23 @@ export const isBrowserEnvironment = (): boolean => {
|
||||
export const escapePath = (path: string): string => {
|
||||
return `'${path.replace(/'/g, "'\\''")}'`;
|
||||
};
|
||||
|
||||
export type IEncodingOptions = {
|
||||
encoding?: string | null;
|
||||
flag?: string;
|
||||
mode?: string;
|
||||
persistent?: boolean;
|
||||
recursive?: boolean;
|
||||
} | string | undefined | null;
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
export type IEncodingOptionsCallback = IEncodingOptions | ((err: NodeJS.ErrnoException, ...args: any[]) => void);
|
||||
|
||||
/**
|
||||
* Return true if the options specify to use a Buffer instead of string.
|
||||
*/
|
||||
export const useBuffer = (options: IEncodingOptionsCallback): boolean => {
|
||||
return options === "buffer"
|
||||
|| (!!options && typeof options !== "string" && typeof options !== "function"
|
||||
&& (options.encoding === "buffer" || options.encoding === null));
|
||||
};
|
||||
|
@ -1,8 +1,9 @@
|
||||
import * as cp from "child_process";
|
||||
import * as net from "net";
|
||||
import * as nodePty from "node-pty";
|
||||
import * as stream from "stream";
|
||||
import { TextEncoder } from "text-encoding";
|
||||
import { NewSessionMessage, ServerMessage, SessionDoneMessage, SessionOutputMessage, ShutdownSessionMessage, IdentifySessionMessage, ClientMessage } from "../proto";
|
||||
import { NewSessionMessage, ServerMessage, SessionDoneMessage, SessionOutputMessage, ShutdownSessionMessage, IdentifySessionMessage, ClientMessage, NewConnectionMessage, ConnectionEstablishedMessage, NewConnectionFailureMessage, ConnectionCloseMessage, ConnectionOutputMessage } from "../proto";
|
||||
import { SendableConnection } from "../common/connection";
|
||||
|
||||
export interface Process {
|
||||
@ -59,7 +60,7 @@ export const handleNewSession = (connection: SendableConnection, newSession: New
|
||||
};
|
||||
}
|
||||
|
||||
const sendOutput = (fd: SessionOutputMessage.FD, msg: string | Uint8Array): void => {
|
||||
const sendOutput = (_fd: SessionOutputMessage.FD, msg: string | Uint8Array): void => {
|
||||
const serverMsg = new ServerMessage();
|
||||
const d = new SessionOutputMessage();
|
||||
d.setId(newSession.getId());
|
||||
@ -90,7 +91,7 @@ export const handleNewSession = (connection: SendableConnection, newSession: New
|
||||
sm.setIdentifySession(id);
|
||||
connection.send(sm.serializeBinary());
|
||||
|
||||
process.on("exit", (code, signal) => {
|
||||
process.on("exit", (code) => {
|
||||
const serverMsg = new ServerMessage();
|
||||
const exit = new SessionDoneMessage();
|
||||
exit.setId(newSession.getId());
|
||||
@ -103,3 +104,61 @@ export const handleNewSession = (connection: SendableConnection, newSession: New
|
||||
|
||||
return process;
|
||||
};
|
||||
|
||||
export const handleNewConnection = (connection: SendableConnection, newConnection: NewConnectionMessage, onExit: () => void): net.Socket => {
|
||||
const id = newConnection.getId();
|
||||
let socket: net.Socket;
|
||||
let didConnect = false;
|
||||
const connectCallback = () => {
|
||||
didConnect = true;
|
||||
const estab = new ConnectionEstablishedMessage();
|
||||
estab.setId(id);
|
||||
const servMsg = new ServerMessage();
|
||||
servMsg.setConnectionEstablished(estab);
|
||||
connection.send(servMsg.serializeBinary());
|
||||
};
|
||||
|
||||
if (newConnection.getPath()) {
|
||||
socket = net.createConnection(newConnection.getPath(), connectCallback);
|
||||
} else if (newConnection.getPort()) {
|
||||
socket = net.createConnection(newConnection.getPort(), undefined, connectCallback);
|
||||
} else {
|
||||
throw new Error("No path or port provided for new connection");
|
||||
}
|
||||
|
||||
socket.addListener("error", (err) => {
|
||||
if (!didConnect) {
|
||||
const errMsg = new NewConnectionFailureMessage();
|
||||
errMsg.setId(id);
|
||||
errMsg.setMessage(err.message);
|
||||
const servMsg = new ServerMessage();
|
||||
servMsg.setConnectionFailure(errMsg);
|
||||
connection.send(servMsg.serializeBinary());
|
||||
|
||||
onExit();
|
||||
}
|
||||
});
|
||||
|
||||
socket.addListener("close", () => {
|
||||
if (didConnect) {
|
||||
const closed = new ConnectionCloseMessage();
|
||||
closed.setId(id);
|
||||
const servMsg = new ServerMessage();
|
||||
servMsg.setConnectionClose(closed);
|
||||
connection.send(servMsg.serializeBinary());
|
||||
|
||||
onExit();
|
||||
}
|
||||
});
|
||||
|
||||
socket.addListener("data", (data) => {
|
||||
const dataMsg = new ConnectionOutputMessage();
|
||||
dataMsg.setId(id);
|
||||
dataMsg.setData(data);
|
||||
const servMsg = new ServerMessage();
|
||||
servMsg.setConnectionOutput(dataMsg);
|
||||
connection.send(servMsg.serializeBinary());
|
||||
});
|
||||
|
||||
return socket;
|
||||
}
|
@ -29,8 +29,7 @@ export const evaluate = async (connection: SendableConnection, message: NewEvalM
|
||||
t = TypedValue.Type.NUMBER;
|
||||
break;
|
||||
default:
|
||||
sendErr(EvalFailedMessage.Reason.EXCEPTION, `unsupported response type ${tof}`);
|
||||
return;
|
||||
return sendErr(EvalFailedMessage.Reason.EXCEPTION, `unsupported response type ${tof}`);
|
||||
}
|
||||
tv.setValue(tof === "string" ? resp : JSON.stringify(resp));
|
||||
tv.setType(t);
|
||||
@ -52,7 +51,13 @@ export const evaluate = async (connection: SendableConnection, message: NewEvalM
|
||||
connection.send(serverMsg.serializeBinary());
|
||||
};
|
||||
try {
|
||||
const value = vm.runInNewContext(`(${message.getFunction()})(${argStr.join(",")})`, { Buffer, require: typeof __non_webpack_require__ !== "undefined" ? __non_webpack_require__ : require, setTimeout }, {
|
||||
const value = vm.runInNewContext(`(${message.getFunction()})(${argStr.join(",")})`, {
|
||||
Buffer,
|
||||
require: typeof __non_webpack_require__ !== "undefined" ? __non_webpack_require__ : require,
|
||||
_require: typeof __non_webpack_require__ !== "undefined" ? __non_webpack_require__ : require,
|
||||
tslib_1: require("tslib"), // TODO: is there a better way to do this?
|
||||
setTimeout,
|
||||
}, {
|
||||
timeout: message.getTimeout() || 30000,
|
||||
});
|
||||
sendResp(await value);
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { logger, field } from "@coder/logger";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { mkdir } from "fs";
|
||||
import { promisify } from "util";
|
||||
import { TextDecoder } from "text-encoding";
|
||||
import { ClientMessage, InitMessage, ServerMessage } from "../proto";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { ClientMessage, WorkingInitMessage, ServerMessage } from "../proto";
|
||||
import { evaluate } from "./evaluate";
|
||||
import { ReadWriteConnection } from "../common/connection";
|
||||
import { Process, handleNewSession } from "./command";
|
||||
import { Process, handleNewSession, handleNewConnection } from "./command";
|
||||
import * as net from "net";
|
||||
|
||||
export interface ServerOptions {
|
||||
readonly workingDirectory: string;
|
||||
@ -13,14 +17,13 @@ export interface ServerOptions {
|
||||
|
||||
export class Server {
|
||||
|
||||
private readonly sessions: Map<number, Process>;
|
||||
private readonly sessions: Map<number, Process> = new Map();
|
||||
private readonly connections: Map<number, net.Socket> = new Map();
|
||||
|
||||
public constructor(
|
||||
private readonly connection: ReadWriteConnection,
|
||||
options?: ServerOptions,
|
||||
) {
|
||||
this.sessions = new Map();
|
||||
|
||||
connection.onMessage((data) => {
|
||||
try {
|
||||
this.handleMessage(ClientMessage.deserializeBinary(data));
|
||||
@ -35,22 +38,43 @@ export class Server {
|
||||
return;
|
||||
}
|
||||
|
||||
const initMsg = new InitMessage();
|
||||
// Ensure the data directory exists.
|
||||
const mkdirP = async (path: string): Promise<void> => {
|
||||
const split = path.replace(/^\/*|\/*$/g, "").split("/");
|
||||
let dir = "";
|
||||
while (split.length > 0) {
|
||||
dir += "/" + split.shift();
|
||||
try {
|
||||
await promisify(mkdir)(dir);
|
||||
} catch (error) {
|
||||
if (error.code !== "EEXIST") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Promise.all([ mkdirP(path.join(options.dataDirectory, "User", "workspaceStorage")) ]).then(() => {
|
||||
logger.info("Created data directory");
|
||||
}).catch((error) => {
|
||||
logger.error(error.message, field("error", error));
|
||||
});
|
||||
|
||||
const initMsg = new WorkingInitMessage();
|
||||
initMsg.setDataDirectory(options.dataDirectory);
|
||||
initMsg.setWorkingDirectory(options.workingDirectory);
|
||||
initMsg.setHomeDirectory(os.homedir());
|
||||
initMsg.setTmpDirectory(os.tmpdir());
|
||||
const platform = os.platform();
|
||||
let operatingSystem: InitMessage.OperatingSystem;
|
||||
let operatingSystem: WorkingInitMessage.OperatingSystem;
|
||||
switch (platform) {
|
||||
case "win32":
|
||||
operatingSystem = InitMessage.OperatingSystem.WINDOWS;
|
||||
operatingSystem = WorkingInitMessage.OperatingSystem.WINDOWS;
|
||||
break;
|
||||
case "linux":
|
||||
operatingSystem = InitMessage.OperatingSystem.LINUX;
|
||||
operatingSystem = WorkingInitMessage.OperatingSystem.LINUX;
|
||||
break;
|
||||
case "darwin":
|
||||
operatingSystem = InitMessage.OperatingSystem.MAC;
|
||||
operatingSystem = WorkingInitMessage.OperatingSystem.MAC;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`unrecognized platform "${platform}"`);
|
||||
@ -68,7 +92,6 @@ export class Server {
|
||||
const session = handleNewSession(this.connection, message.getNewSession()!, () => {
|
||||
this.sessions.delete(message.getNewSession()!.getId());
|
||||
});
|
||||
|
||||
this.sessions.set(message.getNewSession()!.getId(), session);
|
||||
} else if (message.hasCloseSessionInput()) {
|
||||
const s = this.getSession(message.getCloseSessionInput()!.getId());
|
||||
@ -95,7 +118,28 @@ export class Server {
|
||||
return;
|
||||
}
|
||||
s.write(new TextDecoder().decode(message.getWriteToSession()!.getData_asU8()));
|
||||
} else if (message.hasNewConnection()) {
|
||||
const socket = handleNewConnection(this.connection, message.getNewConnection()!, () => {
|
||||
this.connections.delete(message.getNewConnection()!.getId());
|
||||
});
|
||||
this.connections.set(message.getNewConnection()!.getId(), socket);
|
||||
} else if (message.hasConnectionOutput()) {
|
||||
const c = this.getConnection(message.getConnectionOutput()!.getId());
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c.write(Buffer.from(message.getConnectionOutput()!.getData_asU8()));
|
||||
} else if (message.hasConnectionClose()) {
|
||||
const c = this.getConnection(message.getConnectionClose()!.getId());
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
c.end();
|
||||
}
|
||||
}
|
||||
|
||||
private getConnection(id: number): net.Socket | undefined {
|
||||
return this.connections.get(id);
|
||||
}
|
||||
|
||||
private getSession(id: number): Process | undefined {
|
||||
|
@ -1,6 +1,7 @@
|
||||
syntax = "proto3";
|
||||
import "command.proto";
|
||||
import "node.proto";
|
||||
import "vscode.proto";
|
||||
|
||||
message ClientMessage {
|
||||
oneof msg {
|
||||
@ -10,9 +11,14 @@ message ClientMessage {
|
||||
WriteToSessionMessage write_to_session = 3;
|
||||
CloseSessionInputMessage close_session_input = 4;
|
||||
ResizeSessionTTYMessage resize_session_tty = 5;
|
||||
NewConnectionMessage new_connection = 6;
|
||||
ConnectionOutputMessage connection_output = 7;
|
||||
ConnectionCloseMessage connection_close = 8;
|
||||
|
||||
// node.proto
|
||||
NewEvalMessage new_eval = 6;
|
||||
NewEvalMessage new_eval = 9;
|
||||
|
||||
SharedProcessInitMessage shared_process_init = 10;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,16 +29,20 @@ message ServerMessage {
|
||||
SessionDoneMessage session_done = 2;
|
||||
SessionOutputMessage session_output = 3;
|
||||
IdentifySessionMessage identify_session = 4;
|
||||
NewConnectionFailureMessage connection_failure = 5;
|
||||
ConnectionOutputMessage connection_output = 6;
|
||||
ConnectionCloseMessage connection_close = 7;
|
||||
ConnectionEstablishedMessage connection_established = 8;
|
||||
|
||||
// node.proto
|
||||
EvalFailedMessage eval_failed = 5;
|
||||
EvalDoneMessage eval_done = 6;
|
||||
EvalFailedMessage eval_failed = 9;
|
||||
EvalDoneMessage eval_done = 10;
|
||||
|
||||
InitMessage init = 7;
|
||||
WorkingInitMessage init = 11;
|
||||
}
|
||||
}
|
||||
|
||||
message InitMessage {
|
||||
message WorkingInitMessage {
|
||||
string home_directory = 1;
|
||||
string tmp_directory = 2;
|
||||
string data_directory = 3;
|
||||
|
91
packages/protocol/src/proto/client_pb.d.ts
vendored
91
packages/protocol/src/proto/client_pb.d.ts
vendored
@ -4,6 +4,7 @@
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as command_pb from "./command_pb";
|
||||
import * as node_pb from "./node_pb";
|
||||
import * as vscode_pb from "./vscode_pb";
|
||||
|
||||
export class ClientMessage extends jspb.Message {
|
||||
hasNewSession(): boolean;
|
||||
@ -31,11 +32,31 @@ export class ClientMessage extends jspb.Message {
|
||||
getResizeSessionTty(): command_pb.ResizeSessionTTYMessage | undefined;
|
||||
setResizeSessionTty(value?: command_pb.ResizeSessionTTYMessage): void;
|
||||
|
||||
hasNewConnection(): boolean;
|
||||
clearNewConnection(): void;
|
||||
getNewConnection(): command_pb.NewConnectionMessage | undefined;
|
||||
setNewConnection(value?: command_pb.NewConnectionMessage): void;
|
||||
|
||||
hasConnectionOutput(): boolean;
|
||||
clearConnectionOutput(): void;
|
||||
getConnectionOutput(): command_pb.ConnectionOutputMessage | undefined;
|
||||
setConnectionOutput(value?: command_pb.ConnectionOutputMessage): void;
|
||||
|
||||
hasConnectionClose(): boolean;
|
||||
clearConnectionClose(): void;
|
||||
getConnectionClose(): command_pb.ConnectionCloseMessage | undefined;
|
||||
setConnectionClose(value?: command_pb.ConnectionCloseMessage): void;
|
||||
|
||||
hasNewEval(): boolean;
|
||||
clearNewEval(): void;
|
||||
getNewEval(): node_pb.NewEvalMessage | undefined;
|
||||
setNewEval(value?: node_pb.NewEvalMessage): void;
|
||||
|
||||
hasSharedProcessInit(): boolean;
|
||||
clearSharedProcessInit(): void;
|
||||
getSharedProcessInit(): vscode_pb.SharedProcessInitMessage | undefined;
|
||||
setSharedProcessInit(value?: vscode_pb.SharedProcessInitMessage): void;
|
||||
|
||||
getMsgCase(): ClientMessage.MsgCase;
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): ClientMessage.AsObject;
|
||||
@ -54,7 +75,11 @@ export namespace ClientMessage {
|
||||
writeToSession?: command_pb.WriteToSessionMessage.AsObject,
|
||||
closeSessionInput?: command_pb.CloseSessionInputMessage.AsObject,
|
||||
resizeSessionTty?: command_pb.ResizeSessionTTYMessage.AsObject,
|
||||
newConnection?: command_pb.NewConnectionMessage.AsObject,
|
||||
connectionOutput?: command_pb.ConnectionOutputMessage.AsObject,
|
||||
connectionClose?: command_pb.ConnectionCloseMessage.AsObject,
|
||||
newEval?: node_pb.NewEvalMessage.AsObject,
|
||||
sharedProcessInit?: vscode_pb.SharedProcessInitMessage.AsObject,
|
||||
}
|
||||
|
||||
export enum MsgCase {
|
||||
@ -64,7 +89,11 @@ export namespace ClientMessage {
|
||||
WRITE_TO_SESSION = 3,
|
||||
CLOSE_SESSION_INPUT = 4,
|
||||
RESIZE_SESSION_TTY = 5,
|
||||
NEW_EVAL = 6,
|
||||
NEW_CONNECTION = 6,
|
||||
CONNECTION_OUTPUT = 7,
|
||||
CONNECTION_CLOSE = 8,
|
||||
NEW_EVAL = 9,
|
||||
SHARED_PROCESS_INIT = 10,
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +118,26 @@ export class ServerMessage extends jspb.Message {
|
||||
getIdentifySession(): command_pb.IdentifySessionMessage | undefined;
|
||||
setIdentifySession(value?: command_pb.IdentifySessionMessage): void;
|
||||
|
||||
hasConnectionFailure(): boolean;
|
||||
clearConnectionFailure(): void;
|
||||
getConnectionFailure(): command_pb.NewConnectionFailureMessage | undefined;
|
||||
setConnectionFailure(value?: command_pb.NewConnectionFailureMessage): void;
|
||||
|
||||
hasConnectionOutput(): boolean;
|
||||
clearConnectionOutput(): void;
|
||||
getConnectionOutput(): command_pb.ConnectionOutputMessage | undefined;
|
||||
setConnectionOutput(value?: command_pb.ConnectionOutputMessage): void;
|
||||
|
||||
hasConnectionClose(): boolean;
|
||||
clearConnectionClose(): void;
|
||||
getConnectionClose(): command_pb.ConnectionCloseMessage | undefined;
|
||||
setConnectionClose(value?: command_pb.ConnectionCloseMessage): void;
|
||||
|
||||
hasConnectionEstablished(): boolean;
|
||||
clearConnectionEstablished(): void;
|
||||
getConnectionEstablished(): command_pb.ConnectionEstablishedMessage | undefined;
|
||||
setConnectionEstablished(value?: command_pb.ConnectionEstablishedMessage): void;
|
||||
|
||||
hasEvalFailed(): boolean;
|
||||
clearEvalFailed(): void;
|
||||
getEvalFailed(): node_pb.EvalFailedMessage | undefined;
|
||||
@ -101,8 +150,8 @@ export class ServerMessage extends jspb.Message {
|
||||
|
||||
hasInit(): boolean;
|
||||
clearInit(): void;
|
||||
getInit(): InitMessage | undefined;
|
||||
setInit(value?: InitMessage): void;
|
||||
getInit(): WorkingInitMessage | undefined;
|
||||
setInit(value?: WorkingInitMessage): void;
|
||||
|
||||
getMsgCase(): ServerMessage.MsgCase;
|
||||
serializeBinary(): Uint8Array;
|
||||
@ -121,9 +170,13 @@ export namespace ServerMessage {
|
||||
sessionDone?: command_pb.SessionDoneMessage.AsObject,
|
||||
sessionOutput?: command_pb.SessionOutputMessage.AsObject,
|
||||
identifySession?: command_pb.IdentifySessionMessage.AsObject,
|
||||
connectionFailure?: command_pb.NewConnectionFailureMessage.AsObject,
|
||||
connectionOutput?: command_pb.ConnectionOutputMessage.AsObject,
|
||||
connectionClose?: command_pb.ConnectionCloseMessage.AsObject,
|
||||
connectionEstablished?: command_pb.ConnectionEstablishedMessage.AsObject,
|
||||
evalFailed?: node_pb.EvalFailedMessage.AsObject,
|
||||
evalDone?: node_pb.EvalDoneMessage.AsObject,
|
||||
init?: InitMessage.AsObject,
|
||||
init?: WorkingInitMessage.AsObject,
|
||||
}
|
||||
|
||||
export enum MsgCase {
|
||||
@ -132,13 +185,17 @@ export namespace ServerMessage {
|
||||
SESSION_DONE = 2,
|
||||
SESSION_OUTPUT = 3,
|
||||
IDENTIFY_SESSION = 4,
|
||||
EVAL_FAILED = 5,
|
||||
EVAL_DONE = 6,
|
||||
INIT = 7,
|
||||
CONNECTION_FAILURE = 5,
|
||||
CONNECTION_OUTPUT = 6,
|
||||
CONNECTION_CLOSE = 7,
|
||||
CONNECTION_ESTABLISHED = 8,
|
||||
EVAL_FAILED = 9,
|
||||
EVAL_DONE = 10,
|
||||
INIT = 11,
|
||||
}
|
||||
}
|
||||
|
||||
export class InitMessage extends jspb.Message {
|
||||
export class WorkingInitMessage extends jspb.Message {
|
||||
getHomeDirectory(): string;
|
||||
setHomeDirectory(value: string): void;
|
||||
|
||||
@ -151,26 +208,26 @@ export class InitMessage extends jspb.Message {
|
||||
getWorkingDirectory(): string;
|
||||
setWorkingDirectory(value: string): void;
|
||||
|
||||
getOperatingSystem(): InitMessage.OperatingSystem;
|
||||
setOperatingSystem(value: InitMessage.OperatingSystem): void;
|
||||
getOperatingSystem(): WorkingInitMessage.OperatingSystem;
|
||||
setOperatingSystem(value: WorkingInitMessage.OperatingSystem): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): InitMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: InitMessage): InitMessage.AsObject;
|
||||
toObject(includeInstance?: boolean): WorkingInitMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: WorkingInitMessage): WorkingInitMessage.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: InitMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): InitMessage;
|
||||
static deserializeBinaryFromReader(message: InitMessage, reader: jspb.BinaryReader): InitMessage;
|
||||
static serializeBinaryToWriter(message: WorkingInitMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): WorkingInitMessage;
|
||||
static deserializeBinaryFromReader(message: WorkingInitMessage, reader: jspb.BinaryReader): WorkingInitMessage;
|
||||
}
|
||||
|
||||
export namespace InitMessage {
|
||||
export namespace WorkingInitMessage {
|
||||
export type AsObject = {
|
||||
homeDirectory: string,
|
||||
tmpDirectory: string,
|
||||
dataDirectory: string,
|
||||
workingDirectory: string,
|
||||
operatingSystem: InitMessage.OperatingSystem,
|
||||
operatingSystem: WorkingInitMessage.OperatingSystem,
|
||||
}
|
||||
|
||||
export enum OperatingSystem {
|
||||
|
@ -11,10 +11,11 @@ var global = Function('return this')();
|
||||
|
||||
var command_pb = require('./command_pb.js');
|
||||
var node_pb = require('./node_pb.js');
|
||||
var vscode_pb = require('./vscode_pb.js');
|
||||
goog.exportSymbol('proto.ClientMessage', null, global);
|
||||
goog.exportSymbol('proto.InitMessage', null, global);
|
||||
goog.exportSymbol('proto.InitMessage.OperatingSystem', null, global);
|
||||
goog.exportSymbol('proto.ServerMessage', null, global);
|
||||
goog.exportSymbol('proto.WorkingInitMessage', null, global);
|
||||
goog.exportSymbol('proto.WorkingInitMessage.OperatingSystem', null, global);
|
||||
|
||||
/**
|
||||
* Generated by JsPbCodeGenerator.
|
||||
@ -41,7 +42,7 @@ if (goog.DEBUG && !COMPILED) {
|
||||
* @private {!Array<!Array<number>>}
|
||||
* @const
|
||||
*/
|
||||
proto.ClientMessage.oneofGroups_ = [[1,2,3,4,5,6]];
|
||||
proto.ClientMessage.oneofGroups_ = [[1,2,3,4,5,6,7,8,9,10]];
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
@ -53,7 +54,11 @@ proto.ClientMessage.MsgCase = {
|
||||
WRITE_TO_SESSION: 3,
|
||||
CLOSE_SESSION_INPUT: 4,
|
||||
RESIZE_SESSION_TTY: 5,
|
||||
NEW_EVAL: 6
|
||||
NEW_CONNECTION: 6,
|
||||
CONNECTION_OUTPUT: 7,
|
||||
CONNECTION_CLOSE: 8,
|
||||
NEW_EVAL: 9,
|
||||
SHARED_PROCESS_INIT: 10
|
||||
};
|
||||
|
||||
/**
|
||||
@ -96,7 +101,11 @@ proto.ClientMessage.toObject = function(includeInstance, msg) {
|
||||
writeToSession: (f = msg.getWriteToSession()) && command_pb.WriteToSessionMessage.toObject(includeInstance, f),
|
||||
closeSessionInput: (f = msg.getCloseSessionInput()) && command_pb.CloseSessionInputMessage.toObject(includeInstance, f),
|
||||
resizeSessionTty: (f = msg.getResizeSessionTty()) && command_pb.ResizeSessionTTYMessage.toObject(includeInstance, f),
|
||||
newEval: (f = msg.getNewEval()) && node_pb.NewEvalMessage.toObject(includeInstance, f)
|
||||
newConnection: (f = msg.getNewConnection()) && command_pb.NewConnectionMessage.toObject(includeInstance, f),
|
||||
connectionOutput: (f = msg.getConnectionOutput()) && command_pb.ConnectionOutputMessage.toObject(includeInstance, f),
|
||||
connectionClose: (f = msg.getConnectionClose()) && command_pb.ConnectionCloseMessage.toObject(includeInstance, f),
|
||||
newEval: (f = msg.getNewEval()) && node_pb.NewEvalMessage.toObject(includeInstance, f),
|
||||
sharedProcessInit: (f = msg.getSharedProcessInit()) && vscode_pb.SharedProcessInitMessage.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@ -159,10 +168,30 @@ proto.ClientMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
msg.setResizeSessionTty(value);
|
||||
break;
|
||||
case 6:
|
||||
var value = new command_pb.NewConnectionMessage;
|
||||
reader.readMessage(value,command_pb.NewConnectionMessage.deserializeBinaryFromReader);
|
||||
msg.setNewConnection(value);
|
||||
break;
|
||||
case 7:
|
||||
var value = new command_pb.ConnectionOutputMessage;
|
||||
reader.readMessage(value,command_pb.ConnectionOutputMessage.deserializeBinaryFromReader);
|
||||
msg.setConnectionOutput(value);
|
||||
break;
|
||||
case 8:
|
||||
var value = new command_pb.ConnectionCloseMessage;
|
||||
reader.readMessage(value,command_pb.ConnectionCloseMessage.deserializeBinaryFromReader);
|
||||
msg.setConnectionClose(value);
|
||||
break;
|
||||
case 9:
|
||||
var value = new node_pb.NewEvalMessage;
|
||||
reader.readMessage(value,node_pb.NewEvalMessage.deserializeBinaryFromReader);
|
||||
msg.setNewEval(value);
|
||||
break;
|
||||
case 10:
|
||||
var value = new vscode_pb.SharedProcessInitMessage;
|
||||
reader.readMessage(value,vscode_pb.SharedProcessInitMessage.deserializeBinaryFromReader);
|
||||
msg.setSharedProcessInit(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@ -241,14 +270,46 @@ proto.ClientMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
command_pb.ResizeSessionTTYMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getNewEval();
|
||||
f = this.getNewConnection();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
6,
|
||||
f,
|
||||
command_pb.NewConnectionMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getConnectionOutput();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
7,
|
||||
f,
|
||||
command_pb.ConnectionOutputMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getConnectionClose();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
8,
|
||||
f,
|
||||
command_pb.ConnectionCloseMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getNewEval();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
9,
|
||||
f,
|
||||
node_pb.NewEvalMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getSharedProcessInit();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
10,
|
||||
f,
|
||||
vscode_pb.SharedProcessInitMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -412,18 +473,108 @@ proto.ClientMessage.prototype.hasResizeSessionTty = function() {
|
||||
|
||||
|
||||
/**
|
||||
* optional NewEvalMessage new_eval = 6;
|
||||
* optional NewConnectionMessage new_connection = 6;
|
||||
* @return {proto.NewConnectionMessage}
|
||||
*/
|
||||
proto.ClientMessage.prototype.getNewConnection = function() {
|
||||
return /** @type{proto.NewConnectionMessage} */ (
|
||||
jspb.Message.getWrapperField(this, command_pb.NewConnectionMessage, 6));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.NewConnectionMessage|undefined} value */
|
||||
proto.ClientMessage.prototype.setNewConnection = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 6, proto.ClientMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ClientMessage.prototype.clearNewConnection = function() {
|
||||
this.setNewConnection(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ClientMessage.prototype.hasNewConnection = function() {
|
||||
return jspb.Message.getField(this, 6) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional ConnectionOutputMessage connection_output = 7;
|
||||
* @return {proto.ConnectionOutputMessage}
|
||||
*/
|
||||
proto.ClientMessage.prototype.getConnectionOutput = function() {
|
||||
return /** @type{proto.ConnectionOutputMessage} */ (
|
||||
jspb.Message.getWrapperField(this, command_pb.ConnectionOutputMessage, 7));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.ConnectionOutputMessage|undefined} value */
|
||||
proto.ClientMessage.prototype.setConnectionOutput = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 7, proto.ClientMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ClientMessage.prototype.clearConnectionOutput = function() {
|
||||
this.setConnectionOutput(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ClientMessage.prototype.hasConnectionOutput = function() {
|
||||
return jspb.Message.getField(this, 7) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional ConnectionCloseMessage connection_close = 8;
|
||||
* @return {proto.ConnectionCloseMessage}
|
||||
*/
|
||||
proto.ClientMessage.prototype.getConnectionClose = function() {
|
||||
return /** @type{proto.ConnectionCloseMessage} */ (
|
||||
jspb.Message.getWrapperField(this, command_pb.ConnectionCloseMessage, 8));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.ConnectionCloseMessage|undefined} value */
|
||||
proto.ClientMessage.prototype.setConnectionClose = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 8, proto.ClientMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ClientMessage.prototype.clearConnectionClose = function() {
|
||||
this.setConnectionClose(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ClientMessage.prototype.hasConnectionClose = function() {
|
||||
return jspb.Message.getField(this, 8) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional NewEvalMessage new_eval = 9;
|
||||
* @return {proto.NewEvalMessage}
|
||||
*/
|
||||
proto.ClientMessage.prototype.getNewEval = function() {
|
||||
return /** @type{proto.NewEvalMessage} */ (
|
||||
jspb.Message.getWrapperField(this, node_pb.NewEvalMessage, 6));
|
||||
jspb.Message.getWrapperField(this, node_pb.NewEvalMessage, 9));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.NewEvalMessage|undefined} value */
|
||||
proto.ClientMessage.prototype.setNewEval = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 6, proto.ClientMessage.oneofGroups_[0], value);
|
||||
jspb.Message.setOneofWrapperField(this, 9, proto.ClientMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
@ -437,7 +588,37 @@ proto.ClientMessage.prototype.clearNewEval = function() {
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ClientMessage.prototype.hasNewEval = function() {
|
||||
return jspb.Message.getField(this, 6) != null;
|
||||
return jspb.Message.getField(this, 9) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional SharedProcessInitMessage shared_process_init = 10;
|
||||
* @return {proto.SharedProcessInitMessage}
|
||||
*/
|
||||
proto.ClientMessage.prototype.getSharedProcessInit = function() {
|
||||
return /** @type{proto.SharedProcessInitMessage} */ (
|
||||
jspb.Message.getWrapperField(this, vscode_pb.SharedProcessInitMessage, 10));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.SharedProcessInitMessage|undefined} value */
|
||||
proto.ClientMessage.prototype.setSharedProcessInit = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 10, proto.ClientMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ClientMessage.prototype.clearSharedProcessInit = function() {
|
||||
this.setSharedProcessInit(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ClientMessage.prototype.hasSharedProcessInit = function() {
|
||||
return jspb.Message.getField(this, 10) != null;
|
||||
};
|
||||
|
||||
|
||||
@ -467,7 +648,7 @@ if (goog.DEBUG && !COMPILED) {
|
||||
* @private {!Array<!Array<number>>}
|
||||
* @const
|
||||
*/
|
||||
proto.ServerMessage.oneofGroups_ = [[1,2,3,4,5,6,7]];
|
||||
proto.ServerMessage.oneofGroups_ = [[1,2,3,4,5,6,7,8,9,10,11]];
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
@ -478,9 +659,13 @@ proto.ServerMessage.MsgCase = {
|
||||
SESSION_DONE: 2,
|
||||
SESSION_OUTPUT: 3,
|
||||
IDENTIFY_SESSION: 4,
|
||||
EVAL_FAILED: 5,
|
||||
EVAL_DONE: 6,
|
||||
INIT: 7
|
||||
CONNECTION_FAILURE: 5,
|
||||
CONNECTION_OUTPUT: 6,
|
||||
CONNECTION_CLOSE: 7,
|
||||
CONNECTION_ESTABLISHED: 8,
|
||||
EVAL_FAILED: 9,
|
||||
EVAL_DONE: 10,
|
||||
INIT: 11
|
||||
};
|
||||
|
||||
/**
|
||||
@ -522,9 +707,13 @@ proto.ServerMessage.toObject = function(includeInstance, msg) {
|
||||
sessionDone: (f = msg.getSessionDone()) && command_pb.SessionDoneMessage.toObject(includeInstance, f),
|
||||
sessionOutput: (f = msg.getSessionOutput()) && command_pb.SessionOutputMessage.toObject(includeInstance, f),
|
||||
identifySession: (f = msg.getIdentifySession()) && command_pb.IdentifySessionMessage.toObject(includeInstance, f),
|
||||
connectionFailure: (f = msg.getConnectionFailure()) && command_pb.NewConnectionFailureMessage.toObject(includeInstance, f),
|
||||
connectionOutput: (f = msg.getConnectionOutput()) && command_pb.ConnectionOutputMessage.toObject(includeInstance, f),
|
||||
connectionClose: (f = msg.getConnectionClose()) && command_pb.ConnectionCloseMessage.toObject(includeInstance, f),
|
||||
connectionEstablished: (f = msg.getConnectionEstablished()) && command_pb.ConnectionEstablishedMessage.toObject(includeInstance, f),
|
||||
evalFailed: (f = msg.getEvalFailed()) && node_pb.EvalFailedMessage.toObject(includeInstance, f),
|
||||
evalDone: (f = msg.getEvalDone()) && node_pb.EvalDoneMessage.toObject(includeInstance, f),
|
||||
init: (f = msg.getInit()) && proto.InitMessage.toObject(includeInstance, f)
|
||||
init: (f = msg.getInit()) && proto.WorkingInitMessage.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@ -582,18 +771,38 @@ proto.ServerMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
msg.setIdentifySession(value);
|
||||
break;
|
||||
case 5:
|
||||
var value = new command_pb.NewConnectionFailureMessage;
|
||||
reader.readMessage(value,command_pb.NewConnectionFailureMessage.deserializeBinaryFromReader);
|
||||
msg.setConnectionFailure(value);
|
||||
break;
|
||||
case 6:
|
||||
var value = new command_pb.ConnectionOutputMessage;
|
||||
reader.readMessage(value,command_pb.ConnectionOutputMessage.deserializeBinaryFromReader);
|
||||
msg.setConnectionOutput(value);
|
||||
break;
|
||||
case 7:
|
||||
var value = new command_pb.ConnectionCloseMessage;
|
||||
reader.readMessage(value,command_pb.ConnectionCloseMessage.deserializeBinaryFromReader);
|
||||
msg.setConnectionClose(value);
|
||||
break;
|
||||
case 8:
|
||||
var value = new command_pb.ConnectionEstablishedMessage;
|
||||
reader.readMessage(value,command_pb.ConnectionEstablishedMessage.deserializeBinaryFromReader);
|
||||
msg.setConnectionEstablished(value);
|
||||
break;
|
||||
case 9:
|
||||
var value = new node_pb.EvalFailedMessage;
|
||||
reader.readMessage(value,node_pb.EvalFailedMessage.deserializeBinaryFromReader);
|
||||
msg.setEvalFailed(value);
|
||||
break;
|
||||
case 6:
|
||||
case 10:
|
||||
var value = new node_pb.EvalDoneMessage;
|
||||
reader.readMessage(value,node_pb.EvalDoneMessage.deserializeBinaryFromReader);
|
||||
msg.setEvalDone(value);
|
||||
break;
|
||||
case 7:
|
||||
var value = new proto.InitMessage;
|
||||
reader.readMessage(value,proto.InitMessage.deserializeBinaryFromReader);
|
||||
case 11:
|
||||
var value = new proto.WorkingInitMessage;
|
||||
reader.readMessage(value,proto.WorkingInitMessage.deserializeBinaryFromReader);
|
||||
msg.setInit(value);
|
||||
break;
|
||||
default:
|
||||
@ -666,18 +875,50 @@ proto.ServerMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
command_pb.IdentifySessionMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getEvalFailed();
|
||||
f = this.getConnectionFailure();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
5,
|
||||
f,
|
||||
command_pb.NewConnectionFailureMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getConnectionOutput();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
6,
|
||||
f,
|
||||
command_pb.ConnectionOutputMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getConnectionClose();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
7,
|
||||
f,
|
||||
command_pb.ConnectionCloseMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getConnectionEstablished();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
8,
|
||||
f,
|
||||
command_pb.ConnectionEstablishedMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getEvalFailed();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
9,
|
||||
f,
|
||||
node_pb.EvalFailedMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
f = this.getEvalDone();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
6,
|
||||
10,
|
||||
f,
|
||||
node_pb.EvalDoneMessage.serializeBinaryToWriter
|
||||
);
|
||||
@ -685,9 +926,9 @@ proto.ServerMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
f = this.getInit();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
7,
|
||||
11,
|
||||
f,
|
||||
proto.InitMessage.serializeBinaryToWriter
|
||||
proto.WorkingInitMessage.serializeBinaryToWriter
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -823,18 +1064,138 @@ proto.ServerMessage.prototype.hasIdentifySession = function() {
|
||||
|
||||
|
||||
/**
|
||||
* optional EvalFailedMessage eval_failed = 5;
|
||||
* optional NewConnectionFailureMessage connection_failure = 5;
|
||||
* @return {proto.NewConnectionFailureMessage}
|
||||
*/
|
||||
proto.ServerMessage.prototype.getConnectionFailure = function() {
|
||||
return /** @type{proto.NewConnectionFailureMessage} */ (
|
||||
jspb.Message.getWrapperField(this, command_pb.NewConnectionFailureMessage, 5));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.NewConnectionFailureMessage|undefined} value */
|
||||
proto.ServerMessage.prototype.setConnectionFailure = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 5, proto.ServerMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ServerMessage.prototype.clearConnectionFailure = function() {
|
||||
this.setConnectionFailure(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ServerMessage.prototype.hasConnectionFailure = function() {
|
||||
return jspb.Message.getField(this, 5) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional ConnectionOutputMessage connection_output = 6;
|
||||
* @return {proto.ConnectionOutputMessage}
|
||||
*/
|
||||
proto.ServerMessage.prototype.getConnectionOutput = function() {
|
||||
return /** @type{proto.ConnectionOutputMessage} */ (
|
||||
jspb.Message.getWrapperField(this, command_pb.ConnectionOutputMessage, 6));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.ConnectionOutputMessage|undefined} value */
|
||||
proto.ServerMessage.prototype.setConnectionOutput = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 6, proto.ServerMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ServerMessage.prototype.clearConnectionOutput = function() {
|
||||
this.setConnectionOutput(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ServerMessage.prototype.hasConnectionOutput = function() {
|
||||
return jspb.Message.getField(this, 6) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional ConnectionCloseMessage connection_close = 7;
|
||||
* @return {proto.ConnectionCloseMessage}
|
||||
*/
|
||||
proto.ServerMessage.prototype.getConnectionClose = function() {
|
||||
return /** @type{proto.ConnectionCloseMessage} */ (
|
||||
jspb.Message.getWrapperField(this, command_pb.ConnectionCloseMessage, 7));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.ConnectionCloseMessage|undefined} value */
|
||||
proto.ServerMessage.prototype.setConnectionClose = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 7, proto.ServerMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ServerMessage.prototype.clearConnectionClose = function() {
|
||||
this.setConnectionClose(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ServerMessage.prototype.hasConnectionClose = function() {
|
||||
return jspb.Message.getField(this, 7) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional ConnectionEstablishedMessage connection_established = 8;
|
||||
* @return {proto.ConnectionEstablishedMessage}
|
||||
*/
|
||||
proto.ServerMessage.prototype.getConnectionEstablished = function() {
|
||||
return /** @type{proto.ConnectionEstablishedMessage} */ (
|
||||
jspb.Message.getWrapperField(this, command_pb.ConnectionEstablishedMessage, 8));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.ConnectionEstablishedMessage|undefined} value */
|
||||
proto.ServerMessage.prototype.setConnectionEstablished = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 8, proto.ServerMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
proto.ServerMessage.prototype.clearConnectionEstablished = function() {
|
||||
this.setConnectionEstablished(undefined);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this field is set.
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ServerMessage.prototype.hasConnectionEstablished = function() {
|
||||
return jspb.Message.getField(this, 8) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional EvalFailedMessage eval_failed = 9;
|
||||
* @return {proto.EvalFailedMessage}
|
||||
*/
|
||||
proto.ServerMessage.prototype.getEvalFailed = function() {
|
||||
return /** @type{proto.EvalFailedMessage} */ (
|
||||
jspb.Message.getWrapperField(this, node_pb.EvalFailedMessage, 5));
|
||||
jspb.Message.getWrapperField(this, node_pb.EvalFailedMessage, 9));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.EvalFailedMessage|undefined} value */
|
||||
proto.ServerMessage.prototype.setEvalFailed = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 5, proto.ServerMessage.oneofGroups_[0], value);
|
||||
jspb.Message.setOneofWrapperField(this, 9, proto.ServerMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
@ -848,23 +1209,23 @@ proto.ServerMessage.prototype.clearEvalFailed = function() {
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ServerMessage.prototype.hasEvalFailed = function() {
|
||||
return jspb.Message.getField(this, 5) != null;
|
||||
return jspb.Message.getField(this, 9) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional EvalDoneMessage eval_done = 6;
|
||||
* optional EvalDoneMessage eval_done = 10;
|
||||
* @return {proto.EvalDoneMessage}
|
||||
*/
|
||||
proto.ServerMessage.prototype.getEvalDone = function() {
|
||||
return /** @type{proto.EvalDoneMessage} */ (
|
||||
jspb.Message.getWrapperField(this, node_pb.EvalDoneMessage, 6));
|
||||
jspb.Message.getWrapperField(this, node_pb.EvalDoneMessage, 10));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.EvalDoneMessage|undefined} value */
|
||||
proto.ServerMessage.prototype.setEvalDone = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 6, proto.ServerMessage.oneofGroups_[0], value);
|
||||
jspb.Message.setOneofWrapperField(this, 10, proto.ServerMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
@ -878,23 +1239,23 @@ proto.ServerMessage.prototype.clearEvalDone = function() {
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ServerMessage.prototype.hasEvalDone = function() {
|
||||
return jspb.Message.getField(this, 6) != null;
|
||||
return jspb.Message.getField(this, 10) != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional InitMessage init = 7;
|
||||
* @return {proto.InitMessage}
|
||||
* optional WorkingInitMessage init = 11;
|
||||
* @return {proto.WorkingInitMessage}
|
||||
*/
|
||||
proto.ServerMessage.prototype.getInit = function() {
|
||||
return /** @type{proto.InitMessage} */ (
|
||||
jspb.Message.getWrapperField(this, proto.InitMessage, 7));
|
||||
return /** @type{proto.WorkingInitMessage} */ (
|
||||
jspb.Message.getWrapperField(this, proto.WorkingInitMessage, 11));
|
||||
};
|
||||
|
||||
|
||||
/** @param {proto.InitMessage|undefined} value */
|
||||
/** @param {proto.WorkingInitMessage|undefined} value */
|
||||
proto.ServerMessage.prototype.setInit = function(value) {
|
||||
jspb.Message.setOneofWrapperField(this, 7, proto.ServerMessage.oneofGroups_[0], value);
|
||||
jspb.Message.setOneofWrapperField(this, 11, proto.ServerMessage.oneofGroups_[0], value);
|
||||
};
|
||||
|
||||
|
||||
@ -908,7 +1269,7 @@ proto.ServerMessage.prototype.clearInit = function() {
|
||||
* @return{!boolean}
|
||||
*/
|
||||
proto.ServerMessage.prototype.hasInit = function() {
|
||||
return jspb.Message.getField(this, 7) != null;
|
||||
return jspb.Message.getField(this, 11) != null;
|
||||
};
|
||||
|
||||
|
||||
@ -923,12 +1284,12 @@ proto.ServerMessage.prototype.hasInit = function() {
|
||||
* @extends {jspb.Message}
|
||||
* @constructor
|
||||
*/
|
||||
proto.InitMessage = function(opt_data) {
|
||||
proto.WorkingInitMessage = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.InitMessage, jspb.Message);
|
||||
goog.inherits(proto.WorkingInitMessage, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.InitMessage.displayName = 'proto.InitMessage';
|
||||
proto.WorkingInitMessage.displayName = 'proto.WorkingInitMessage';
|
||||
}
|
||||
|
||||
|
||||
@ -943,8 +1304,8 @@ if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.InitMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.InitMessage.toObject(opt_includeInstance, this);
|
||||
proto.WorkingInitMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.WorkingInitMessage.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
@ -953,10 +1314,10 @@ proto.InitMessage.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.InitMessage} msg The msg instance to transform.
|
||||
* @param {!proto.WorkingInitMessage} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.InitMessage.toObject = function(includeInstance, msg) {
|
||||
proto.WorkingInitMessage.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
homeDirectory: msg.getHomeDirectory(),
|
||||
tmpDirectory: msg.getTmpDirectory(),
|
||||
@ -976,23 +1337,23 @@ proto.InitMessage.toObject = function(includeInstance, msg) {
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.InitMessage}
|
||||
* @return {!proto.WorkingInitMessage}
|
||||
*/
|
||||
proto.InitMessage.deserializeBinary = function(bytes) {
|
||||
proto.WorkingInitMessage.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.InitMessage;
|
||||
return proto.InitMessage.deserializeBinaryFromReader(msg, reader);
|
||||
var msg = new proto.WorkingInitMessage;
|
||||
return proto.WorkingInitMessage.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.InitMessage} msg The message object to deserialize into.
|
||||
* @param {!proto.WorkingInitMessage} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.InitMessage}
|
||||
* @return {!proto.WorkingInitMessage}
|
||||
*/
|
||||
proto.InitMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
proto.WorkingInitMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
@ -1016,7 +1377,7 @@ proto.InitMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
msg.setWorkingDirectory(value);
|
||||
break;
|
||||
case 5:
|
||||
var value = /** @type {!proto.InitMessage.OperatingSystem} */ (reader.readEnum());
|
||||
var value = /** @type {!proto.WorkingInitMessage.OperatingSystem} */ (reader.readEnum());
|
||||
msg.setOperatingSystem(value);
|
||||
break;
|
||||
default:
|
||||
@ -1031,10 +1392,10 @@ proto.InitMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
/**
|
||||
* Class method variant: serializes the given message to binary data
|
||||
* (in protobuf wire format), writing to the given BinaryWriter.
|
||||
* @param {!proto.InitMessage} message
|
||||
* @param {!proto.WorkingInitMessage} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.InitMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
proto.WorkingInitMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
message.serializeBinaryToWriter(writer);
|
||||
};
|
||||
|
||||
@ -1043,7 +1404,7 @@ proto.InitMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.InitMessage.prototype.serializeBinary = function() {
|
||||
proto.WorkingInitMessage.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
this.serializeBinaryToWriter(writer);
|
||||
return writer.getResultBuffer();
|
||||
@ -1055,7 +1416,7 @@ proto.InitMessage.prototype.serializeBinary = function() {
|
||||
* writing to the given BinaryWriter.
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.InitMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
proto.WorkingInitMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
var f = undefined;
|
||||
f = this.getHomeDirectory();
|
||||
if (f.length > 0) {
|
||||
@ -1097,10 +1458,10 @@ proto.InitMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
|
||||
/**
|
||||
* Creates a deep clone of this proto. No data is shared with the original.
|
||||
* @return {!proto.InitMessage} The clone.
|
||||
* @return {!proto.WorkingInitMessage} The clone.
|
||||
*/
|
||||
proto.InitMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.InitMessage} */ (jspb.Message.cloneMessage(this));
|
||||
proto.WorkingInitMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.WorkingInitMessage} */ (jspb.Message.cloneMessage(this));
|
||||
};
|
||||
|
||||
|
||||
@ -1108,13 +1469,13 @@ proto.InitMessage.prototype.cloneMessage = function() {
|
||||
* optional string home_directory = 1;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.InitMessage.prototype.getHomeDirectory = function() {
|
||||
proto.WorkingInitMessage.prototype.getHomeDirectory = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 1, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.InitMessage.prototype.setHomeDirectory = function(value) {
|
||||
proto.WorkingInitMessage.prototype.setHomeDirectory = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
};
|
||||
|
||||
@ -1123,13 +1484,13 @@ proto.InitMessage.prototype.setHomeDirectory = function(value) {
|
||||
* optional string tmp_directory = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.InitMessage.prototype.getTmpDirectory = function() {
|
||||
proto.WorkingInitMessage.prototype.getTmpDirectory = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.InitMessage.prototype.setTmpDirectory = function(value) {
|
||||
proto.WorkingInitMessage.prototype.setTmpDirectory = function(value) {
|
||||
jspb.Message.setField(this, 2, value);
|
||||
};
|
||||
|
||||
@ -1138,13 +1499,13 @@ proto.InitMessage.prototype.setTmpDirectory = function(value) {
|
||||
* optional string data_directory = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.InitMessage.prototype.getDataDirectory = function() {
|
||||
proto.WorkingInitMessage.prototype.getDataDirectory = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.InitMessage.prototype.setDataDirectory = function(value) {
|
||||
proto.WorkingInitMessage.prototype.setDataDirectory = function(value) {
|
||||
jspb.Message.setField(this, 3, value);
|
||||
};
|
||||
|
||||
@ -1153,28 +1514,28 @@ proto.InitMessage.prototype.setDataDirectory = function(value) {
|
||||
* optional string working_directory = 4;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.InitMessage.prototype.getWorkingDirectory = function() {
|
||||
proto.WorkingInitMessage.prototype.getWorkingDirectory = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 4, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.InitMessage.prototype.setWorkingDirectory = function(value) {
|
||||
proto.WorkingInitMessage.prototype.setWorkingDirectory = function(value) {
|
||||
jspb.Message.setField(this, 4, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional OperatingSystem operating_system = 5;
|
||||
* @return {!proto.InitMessage.OperatingSystem}
|
||||
* @return {!proto.WorkingInitMessage.OperatingSystem}
|
||||
*/
|
||||
proto.InitMessage.prototype.getOperatingSystem = function() {
|
||||
return /** @type {!proto.InitMessage.OperatingSystem} */ (jspb.Message.getFieldProto3(this, 5, 0));
|
||||
proto.WorkingInitMessage.prototype.getOperatingSystem = function() {
|
||||
return /** @type {!proto.WorkingInitMessage.OperatingSystem} */ (jspb.Message.getFieldProto3(this, 5, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {!proto.InitMessage.OperatingSystem} value */
|
||||
proto.InitMessage.prototype.setOperatingSystem = function(value) {
|
||||
/** @param {!proto.WorkingInitMessage.OperatingSystem} value */
|
||||
proto.WorkingInitMessage.prototype.setOperatingSystem = function(value) {
|
||||
jspb.Message.setField(this, 5, value);
|
||||
};
|
||||
|
||||
@ -1182,7 +1543,7 @@ proto.InitMessage.prototype.setOperatingSystem = function(value) {
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
proto.InitMessage.OperatingSystem = {
|
||||
proto.WorkingInitMessage.OperatingSystem = {
|
||||
WINDOWS: 0,
|
||||
LINUX: 1,
|
||||
MAC: 2
|
||||
|
@ -76,3 +76,32 @@ message TTYDimensions {
|
||||
uint32 height = 1;
|
||||
uint32 width = 2;
|
||||
}
|
||||
|
||||
// Initializes a new connection to a port or path
|
||||
message NewConnectionMessage {
|
||||
uint64 id = 1;
|
||||
uint64 port = 2;
|
||||
string path = 3;
|
||||
}
|
||||
|
||||
// Sent when a connection has successfully established
|
||||
message ConnectionEstablishedMessage {
|
||||
uint64 id = 1;
|
||||
}
|
||||
|
||||
// Sent when a connection fails
|
||||
message NewConnectionFailureMessage {
|
||||
uint64 id = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
// Sent for connection output
|
||||
message ConnectionOutputMessage {
|
||||
uint64 id = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
// Sent to close a connection
|
||||
message ConnectionCloseMessage {
|
||||
uint64 id = 1;
|
||||
}
|
||||
|
118
packages/protocol/src/proto/command_pb.d.ts
vendored
118
packages/protocol/src/proto/command_pb.d.ts
vendored
@ -286,3 +286,121 @@ export namespace TTYDimensions {
|
||||
}
|
||||
}
|
||||
|
||||
export class NewConnectionMessage extends jspb.Message {
|
||||
getId(): number;
|
||||
setId(value: number): void;
|
||||
|
||||
getPort(): number;
|
||||
setPort(value: number): void;
|
||||
|
||||
getPath(): string;
|
||||
setPath(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): NewConnectionMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: NewConnectionMessage): NewConnectionMessage.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: NewConnectionMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): NewConnectionMessage;
|
||||
static deserializeBinaryFromReader(message: NewConnectionMessage, reader: jspb.BinaryReader): NewConnectionMessage;
|
||||
}
|
||||
|
||||
export namespace NewConnectionMessage {
|
||||
export type AsObject = {
|
||||
id: number,
|
||||
port: number,
|
||||
path: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class ConnectionEstablishedMessage extends jspb.Message {
|
||||
getId(): number;
|
||||
setId(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): ConnectionEstablishedMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: ConnectionEstablishedMessage): ConnectionEstablishedMessage.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: ConnectionEstablishedMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): ConnectionEstablishedMessage;
|
||||
static deserializeBinaryFromReader(message: ConnectionEstablishedMessage, reader: jspb.BinaryReader): ConnectionEstablishedMessage;
|
||||
}
|
||||
|
||||
export namespace ConnectionEstablishedMessage {
|
||||
export type AsObject = {
|
||||
id: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class NewConnectionFailureMessage extends jspb.Message {
|
||||
getId(): number;
|
||||
setId(value: number): void;
|
||||
|
||||
getMessage(): string;
|
||||
setMessage(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): NewConnectionFailureMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: NewConnectionFailureMessage): NewConnectionFailureMessage.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: NewConnectionFailureMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): NewConnectionFailureMessage;
|
||||
static deserializeBinaryFromReader(message: NewConnectionFailureMessage, reader: jspb.BinaryReader): NewConnectionFailureMessage;
|
||||
}
|
||||
|
||||
export namespace NewConnectionFailureMessage {
|
||||
export type AsObject = {
|
||||
id: number,
|
||||
message: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class ConnectionOutputMessage extends jspb.Message {
|
||||
getId(): number;
|
||||
setId(value: number): void;
|
||||
|
||||
getData(): Uint8Array | string;
|
||||
getData_asU8(): Uint8Array;
|
||||
getData_asB64(): string;
|
||||
setData(value: Uint8Array | string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): ConnectionOutputMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: ConnectionOutputMessage): ConnectionOutputMessage.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: ConnectionOutputMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): ConnectionOutputMessage;
|
||||
static deserializeBinaryFromReader(message: ConnectionOutputMessage, reader: jspb.BinaryReader): ConnectionOutputMessage;
|
||||
}
|
||||
|
||||
export namespace ConnectionOutputMessage {
|
||||
export type AsObject = {
|
||||
id: number,
|
||||
data: Uint8Array | string,
|
||||
}
|
||||
}
|
||||
|
||||
export class ConnectionCloseMessage extends jspb.Message {
|
||||
getId(): number;
|
||||
setId(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): ConnectionCloseMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: ConnectionCloseMessage): ConnectionCloseMessage.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: ConnectionCloseMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): ConnectionCloseMessage;
|
||||
static deserializeBinaryFromReader(message: ConnectionCloseMessage, reader: jspb.BinaryReader): ConnectionCloseMessage;
|
||||
}
|
||||
|
||||
export namespace ConnectionCloseMessage {
|
||||
export type AsObject = {
|
||||
id: number,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,12 @@ var goog = jspb;
|
||||
var global = Function('return this')();
|
||||
|
||||
goog.exportSymbol('proto.CloseSessionInputMessage', null, global);
|
||||
goog.exportSymbol('proto.ConnectionCloseMessage', null, global);
|
||||
goog.exportSymbol('proto.ConnectionEstablishedMessage', null, global);
|
||||
goog.exportSymbol('proto.ConnectionOutputMessage', null, global);
|
||||
goog.exportSymbol('proto.IdentifySessionMessage', null, global);
|
||||
goog.exportSymbol('proto.NewConnectionFailureMessage', null, global);
|
||||
goog.exportSymbol('proto.NewConnectionMessage', null, global);
|
||||
goog.exportSymbol('proto.NewSessionFailureMessage', null, global);
|
||||
goog.exportSymbol('proto.NewSessionFailureMessage.Reason', null, global);
|
||||
goog.exportSymbol('proto.NewSessionMessage', null, global);
|
||||
@ -2155,4 +2160,931 @@ proto.TTYDimensions.prototype.setWidth = function(value) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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.NewConnectionMessage = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.NewConnectionMessage, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.NewConnectionMessage.displayName = 'proto.NewConnectionMessage';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.NewConnectionMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.NewConnectionMessage.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.NewConnectionMessage} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.NewConnectionMessage.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
id: msg.getId(),
|
||||
port: msg.getPort(),
|
||||
path: msg.getPath()
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.NewConnectionMessage}
|
||||
*/
|
||||
proto.NewConnectionMessage.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.NewConnectionMessage;
|
||||
return proto.NewConnectionMessage.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.NewConnectionMessage} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.NewConnectionMessage}
|
||||
*/
|
||||
proto.NewConnectionMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {number} */ (reader.readUint64());
|
||||
msg.setId(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {number} */ (reader.readUint64());
|
||||
msg.setPort(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setPath(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class method variant: serializes the given message to binary data
|
||||
* (in protobuf wire format), writing to the given BinaryWriter.
|
||||
* @param {!proto.NewConnectionMessage} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.NewConnectionMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
message.serializeBinaryToWriter(writer);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.NewConnectionMessage.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
this.serializeBinaryToWriter(writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format),
|
||||
* writing to the given BinaryWriter.
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.NewConnectionMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
var f = undefined;
|
||||
f = this.getId();
|
||||
if (f !== 0) {
|
||||
writer.writeUint64(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = this.getPort();
|
||||
if (f !== 0) {
|
||||
writer.writeUint64(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = this.getPath();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deep clone of this proto. No data is shared with the original.
|
||||
* @return {!proto.NewConnectionMessage} The clone.
|
||||
*/
|
||||
proto.NewConnectionMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.NewConnectionMessage} */ (jspb.Message.cloneMessage(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint64 id = 1;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.NewConnectionMessage.prototype.getId = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.NewConnectionMessage.prototype.setId = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint64 port = 2;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.NewConnectionMessage.prototype.getPort = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 2, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.NewConnectionMessage.prototype.setPort = function(value) {
|
||||
jspb.Message.setField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string path = 3;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.NewConnectionMessage.prototype.getPath = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 3, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.NewConnectionMessage.prototype.setPath = function(value) {
|
||||
jspb.Message.setField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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.ConnectionEstablishedMessage = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.ConnectionEstablishedMessage, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.ConnectionEstablishedMessage.displayName = 'proto.ConnectionEstablishedMessage';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.ConnectionEstablishedMessage.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.ConnectionEstablishedMessage} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
id: msg.getId()
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.ConnectionEstablishedMessage}
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.ConnectionEstablishedMessage;
|
||||
return proto.ConnectionEstablishedMessage.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.ConnectionEstablishedMessage} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.ConnectionEstablishedMessage}
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {number} */ (reader.readUint64());
|
||||
msg.setId(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class method variant: serializes the given message to binary data
|
||||
* (in protobuf wire format), writing to the given BinaryWriter.
|
||||
* @param {!proto.ConnectionEstablishedMessage} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
message.serializeBinaryToWriter(writer);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
this.serializeBinaryToWriter(writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format),
|
||||
* writing to the given BinaryWriter.
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
var f = undefined;
|
||||
f = this.getId();
|
||||
if (f !== 0) {
|
||||
writer.writeUint64(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deep clone of this proto. No data is shared with the original.
|
||||
* @return {!proto.ConnectionEstablishedMessage} The clone.
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.ConnectionEstablishedMessage} */ (jspb.Message.cloneMessage(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint64 id = 1;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.ConnectionEstablishedMessage.prototype.getId = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.ConnectionEstablishedMessage.prototype.setId = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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.NewConnectionFailureMessage = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.NewConnectionFailureMessage, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.NewConnectionFailureMessage.displayName = 'proto.NewConnectionFailureMessage';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.NewConnectionFailureMessage.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.NewConnectionFailureMessage} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
id: msg.getId(),
|
||||
message: msg.getMessage()
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.NewConnectionFailureMessage}
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.NewConnectionFailureMessage;
|
||||
return proto.NewConnectionFailureMessage.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.NewConnectionFailureMessage} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.NewConnectionFailureMessage}
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {number} */ (reader.readUint64());
|
||||
msg.setId(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setMessage(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class method variant: serializes the given message to binary data
|
||||
* (in protobuf wire format), writing to the given BinaryWriter.
|
||||
* @param {!proto.NewConnectionFailureMessage} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
message.serializeBinaryToWriter(writer);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
this.serializeBinaryToWriter(writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format),
|
||||
* writing to the given BinaryWriter.
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
var f = undefined;
|
||||
f = this.getId();
|
||||
if (f !== 0) {
|
||||
writer.writeUint64(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = this.getMessage();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deep clone of this proto. No data is shared with the original.
|
||||
* @return {!proto.NewConnectionFailureMessage} The clone.
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.NewConnectionFailureMessage} */ (jspb.Message.cloneMessage(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint64 id = 1;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.prototype.getId = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.NewConnectionFailureMessage.prototype.setId = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string message = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.NewConnectionFailureMessage.prototype.getMessage = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.NewConnectionFailureMessage.prototype.setMessage = function(value) {
|
||||
jspb.Message.setField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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.ConnectionOutputMessage = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.ConnectionOutputMessage, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.ConnectionOutputMessage.displayName = 'proto.ConnectionOutputMessage';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.ConnectionOutputMessage.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.ConnectionOutputMessage} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
id: msg.getId(),
|
||||
data: msg.getData_asB64()
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.ConnectionOutputMessage}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.ConnectionOutputMessage;
|
||||
return proto.ConnectionOutputMessage.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.ConnectionOutputMessage} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.ConnectionOutputMessage}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {number} */ (reader.readUint64());
|
||||
msg.setId(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {!Uint8Array} */ (reader.readBytes());
|
||||
msg.setData(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class method variant: serializes the given message to binary data
|
||||
* (in protobuf wire format), writing to the given BinaryWriter.
|
||||
* @param {!proto.ConnectionOutputMessage} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.ConnectionOutputMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
message.serializeBinaryToWriter(writer);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
this.serializeBinaryToWriter(writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format),
|
||||
* writing to the given BinaryWriter.
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
var f = undefined;
|
||||
f = this.getId();
|
||||
if (f !== 0) {
|
||||
writer.writeUint64(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = this.getData_asU8();
|
||||
if (f.length > 0) {
|
||||
writer.writeBytes(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deep clone of this proto. No data is shared with the original.
|
||||
* @return {!proto.ConnectionOutputMessage} The clone.
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.ConnectionOutputMessage} */ (jspb.Message.cloneMessage(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint64 id = 1;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.getId = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.ConnectionOutputMessage.prototype.setId = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional bytes data = 2;
|
||||
* @return {!(string|Uint8Array)}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.getData = function() {
|
||||
return /** @type {!(string|Uint8Array)} */ (jspb.Message.getFieldProto3(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional bytes data = 2;
|
||||
* This is a type-conversion wrapper around `getData()`
|
||||
* @return {string}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.getData_asB64 = function() {
|
||||
return /** @type {string} */ (jspb.Message.bytesAsB64(
|
||||
this.getData()));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional bytes data = 2;
|
||||
* Note that Uint8Array is not supported on all browsers.
|
||||
* @see http://caniuse.com/Uint8Array
|
||||
* This is a type-conversion wrapper around `getData()`
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.ConnectionOutputMessage.prototype.getData_asU8 = function() {
|
||||
return /** @type {!Uint8Array} */ (jspb.Message.bytesAsU8(
|
||||
this.getData()));
|
||||
};
|
||||
|
||||
|
||||
/** @param {!(string|Uint8Array)} value */
|
||||
proto.ConnectionOutputMessage.prototype.setData = function(value) {
|
||||
jspb.Message.setField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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.ConnectionCloseMessage = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.ConnectionCloseMessage, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.ConnectionCloseMessage.displayName = 'proto.ConnectionCloseMessage';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.ConnectionCloseMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.ConnectionCloseMessage.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.ConnectionCloseMessage} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.ConnectionCloseMessage.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
id: msg.getId()
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.ConnectionCloseMessage}
|
||||
*/
|
||||
proto.ConnectionCloseMessage.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.ConnectionCloseMessage;
|
||||
return proto.ConnectionCloseMessage.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.ConnectionCloseMessage} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.ConnectionCloseMessage}
|
||||
*/
|
||||
proto.ConnectionCloseMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {number} */ (reader.readUint64());
|
||||
msg.setId(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class method variant: serializes the given message to binary data
|
||||
* (in protobuf wire format), writing to the given BinaryWriter.
|
||||
* @param {!proto.ConnectionCloseMessage} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.ConnectionCloseMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
message.serializeBinaryToWriter(writer);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.ConnectionCloseMessage.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
this.serializeBinaryToWriter(writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format),
|
||||
* writing to the given BinaryWriter.
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.ConnectionCloseMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
var f = undefined;
|
||||
f = this.getId();
|
||||
if (f !== 0) {
|
||||
writer.writeUint64(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deep clone of this proto. No data is shared with the original.
|
||||
* @return {!proto.ConnectionCloseMessage} The clone.
|
||||
*/
|
||||
proto.ConnectionCloseMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.ConnectionCloseMessage} */ (jspb.Message.cloneMessage(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint64 id = 1;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.ConnectionCloseMessage.prototype.getId = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.ConnectionCloseMessage.prototype.setId = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
goog.object.extend(exports, proto);
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from "./client_pb";
|
||||
export * from "./command_pb";
|
||||
export * from "./node_pb";
|
||||
export * from "./vscode_pb";
|
||||
|
9
packages/protocol/src/proto/vscode.proto
Normal file
9
packages/protocol/src/proto/vscode.proto
Normal file
@ -0,0 +1,9 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message SharedProcessInitMessage {
|
||||
uint64 window_id = 1;
|
||||
string log_directory = 2;
|
||||
|
||||
// Maps to `"vs/platform/log/common/log".LogLevel`
|
||||
uint32 log_level = 3;
|
||||
}
|
33
packages/protocol/src/proto/vscode_pb.d.ts
vendored
Normal file
33
packages/protocol/src/proto/vscode_pb.d.ts
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// package:
|
||||
// file: vscode.proto
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
|
||||
export class SharedProcessInitMessage extends jspb.Message {
|
||||
getWindowId(): number;
|
||||
setWindowId(value: number): void;
|
||||
|
||||
getLogDirectory(): string;
|
||||
setLogDirectory(value: string): void;
|
||||
|
||||
getLogLevel(): number;
|
||||
setLogLevel(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): SharedProcessInitMessage.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: SharedProcessInitMessage): SharedProcessInitMessage.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: SharedProcessInitMessage, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): SharedProcessInitMessage;
|
||||
static deserializeBinaryFromReader(message: SharedProcessInitMessage, reader: jspb.BinaryReader): SharedProcessInitMessage;
|
||||
}
|
||||
|
||||
export namespace SharedProcessInitMessage {
|
||||
export type AsObject = {
|
||||
windowId: number,
|
||||
logDirectory: string,
|
||||
logLevel: number,
|
||||
}
|
||||
}
|
||||
|
226
packages/protocol/src/proto/vscode_pb.js
Normal file
226
packages/protocol/src/proto/vscode_pb.js
Normal file
@ -0,0 +1,226 @@
|
||||
/**
|
||||
* @fileoverview
|
||||
* @enhanceable
|
||||
* @public
|
||||
*/
|
||||
// GENERATED CODE -- DO NOT EDIT!
|
||||
|
||||
var jspb = require('google-protobuf');
|
||||
var goog = jspb;
|
||||
var global = Function('return this')();
|
||||
|
||||
goog.exportSymbol('proto.SharedProcessInitMessage', null, global);
|
||||
|
||||
/**
|
||||
* 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.SharedProcessInitMessage = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
};
|
||||
goog.inherits(proto.SharedProcessInitMessage, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.SharedProcessInitMessage.displayName = 'proto.SharedProcessInitMessage';
|
||||
}
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
/**
|
||||
* Creates an object representation of this proto suitable for use in Soy templates.
|
||||
* Field names that are reserved in JavaScript and will be renamed to pb_name.
|
||||
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
|
||||
* For the list of reserved names please see:
|
||||
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
return proto.SharedProcessInitMessage.toObject(opt_includeInstance, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static version of the {@see toObject} method.
|
||||
* @param {boolean|undefined} includeInstance Whether to include the JSPB
|
||||
* instance for transitional soy proto support:
|
||||
* http://goto/soy-param-migration
|
||||
* @param {!proto.SharedProcessInitMessage} msg The msg instance to transform.
|
||||
* @return {!Object}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
windowId: msg.getWindowId(),
|
||||
logDirectory: msg.getLogDirectory(),
|
||||
logLevel: msg.getLogLevel()
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
obj.$jspbMessageInstance = msg;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format).
|
||||
* @param {jspb.ByteSource} bytes The bytes to deserialize.
|
||||
* @return {!proto.SharedProcessInitMessage}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.deserializeBinary = function(bytes) {
|
||||
var reader = new jspb.BinaryReader(bytes);
|
||||
var msg = new proto.SharedProcessInitMessage;
|
||||
return proto.SharedProcessInitMessage.deserializeBinaryFromReader(msg, reader);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deserializes binary data (in protobuf wire format) from the
|
||||
* given reader into the given message object.
|
||||
* @param {!proto.SharedProcessInitMessage} msg The message object to deserialize into.
|
||||
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
|
||||
* @return {!proto.SharedProcessInitMessage}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.deserializeBinaryFromReader = function(msg, reader) {
|
||||
while (reader.nextField()) {
|
||||
if (reader.isEndGroup()) {
|
||||
break;
|
||||
}
|
||||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {number} */ (reader.readUint64());
|
||||
msg.setWindowId(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setLogDirectory(value);
|
||||
break;
|
||||
case 3:
|
||||
var value = /** @type {number} */ (reader.readUint32());
|
||||
msg.setLogLevel(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class method variant: serializes the given message to binary data
|
||||
* (in protobuf wire format), writing to the given BinaryWriter.
|
||||
* @param {!proto.SharedProcessInitMessage} message
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.SharedProcessInitMessage.serializeBinaryToWriter = function(message, writer) {
|
||||
message.serializeBinaryToWriter(writer);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format).
|
||||
* @return {!Uint8Array}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.prototype.serializeBinary = function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
this.serializeBinaryToWriter(writer);
|
||||
return writer.getResultBuffer();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serializes the message to binary data (in protobuf wire format),
|
||||
* writing to the given BinaryWriter.
|
||||
* @param {!jspb.BinaryWriter} writer
|
||||
*/
|
||||
proto.SharedProcessInitMessage.prototype.serializeBinaryToWriter = function (writer) {
|
||||
var f = undefined;
|
||||
f = this.getWindowId();
|
||||
if (f !== 0) {
|
||||
writer.writeUint64(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = this.getLogDirectory();
|
||||
if (f.length > 0) {
|
||||
writer.writeString(
|
||||
2,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = this.getLogLevel();
|
||||
if (f !== 0) {
|
||||
writer.writeUint32(
|
||||
3,
|
||||
f
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deep clone of this proto. No data is shared with the original.
|
||||
* @return {!proto.SharedProcessInitMessage} The clone.
|
||||
*/
|
||||
proto.SharedProcessInitMessage.prototype.cloneMessage = function() {
|
||||
return /** @type {!proto.SharedProcessInitMessage} */ (jspb.Message.cloneMessage(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint64 window_id = 1;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.prototype.getWindowId = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 1, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.SharedProcessInitMessage.prototype.setWindowId = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional string log_directory = 2;
|
||||
* @return {string}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.prototype.getLogDirectory = function() {
|
||||
return /** @type {string} */ (jspb.Message.getFieldProto3(this, 2, ""));
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} value */
|
||||
proto.SharedProcessInitMessage.prototype.setLogDirectory = function(value) {
|
||||
jspb.Message.setField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional uint32 log_level = 3;
|
||||
* @return {number}
|
||||
*/
|
||||
proto.SharedProcessInitMessage.prototype.getLogLevel = function() {
|
||||
return /** @type {number} */ (jspb.Message.getFieldProto3(this, 3, 0));
|
||||
};
|
||||
|
||||
|
||||
/** @param {number} value */
|
||||
proto.SharedProcessInitMessage.prototype.setLogLevel = function(value) {
|
||||
jspb.Message.setField(this, 3, value);
|
||||
};
|
||||
|
||||
|
||||
goog.object.extend(exports, proto);
|
@ -1,3 +1,5 @@
|
||||
import * as net from "net";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { TextEncoder, TextDecoder } from "text-encoding";
|
||||
import { createClient } from "./helpers";
|
||||
@ -5,7 +7,7 @@ import { createClient } from "./helpers";
|
||||
(<any>global).TextDecoder = TextDecoder;
|
||||
(<any>global).TextEncoder = TextEncoder;
|
||||
|
||||
describe("Command", () => {
|
||||
describe("spawn", () => {
|
||||
const client = createClient();
|
||||
|
||||
it("should execute command and return output", (done) => {
|
||||
@ -130,3 +132,59 @@ describe("Command", () => {
|
||||
proc.on("exit", () => done());
|
||||
});
|
||||
});
|
||||
|
||||
describe("createConnection", () => {
|
||||
const client = createClient();
|
||||
const tmpPath = path.join(os.tmpdir(), Math.random().toString());
|
||||
let server: net.Server;
|
||||
beforeAll(async () => {
|
||||
await new Promise((r) => {
|
||||
server = net.createServer().listen(tmpPath, () => {
|
||||
r();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("should connect to socket", (done) => {
|
||||
const socket = client.createConnection(tmpPath, () => {
|
||||
socket.end();
|
||||
socket.addListener("close", () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should get data from server", (done) => {
|
||||
server.once("connection", (socket: net.Socket) => {
|
||||
socket.write("hi how r u");
|
||||
});
|
||||
|
||||
const socket = client.createConnection(tmpPath);
|
||||
|
||||
socket.addListener("data", (data) => {
|
||||
expect(data.toString()).toEqual("hi how r u");
|
||||
socket.end();
|
||||
socket.addListener("close", () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send data to server", (done) => {
|
||||
const clientSocket = client.createConnection(tmpPath);
|
||||
clientSocket.write(Buffer.from("bananas"));
|
||||
server.once("connection", (socket: net.Socket) => {
|
||||
socket.addListener("data", (data) => {
|
||||
expect(data.toString()).toEqual("bananas");
|
||||
socket.end();
|
||||
clientSocket.addListener("end", () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -9,7 +9,7 @@ describe("Server", () => {
|
||||
});
|
||||
|
||||
it("should get init msg", (done) => {
|
||||
client.onInitData((data) => {
|
||||
client.initData.then((data) => {
|
||||
expect(data.dataDirectory).toEqual(dataDirectory);
|
||||
expect(data.workingDirectory).toEqual(workingDirectory);
|
||||
done();
|
||||
|
1
packages/server/.gitignore
vendored
1
packages/server/.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
out
|
||||
cli*
|
||||
build
|
||||
|
||||
# This file is generated when the binary is created.
|
||||
# We want to use the parent tsconfig so we can ignore it.
|
||||
|
@ -4,10 +4,12 @@
|
||||
"bin": "./out/cli.js",
|
||||
"files": [],
|
||||
"scripts": {
|
||||
"start": "ts-node -r tsconfig-paths/register src/cli.ts",
|
||||
"build:webpack": "rm -rf ./out && ../../node_modules/.bin/webpack --config ./webpack.config.js",
|
||||
"start": "NODE_ENV=development ts-node -r tsconfig-paths/register src/cli.ts",
|
||||
"build:webpack": "rm -rf ./out && export CLI=true && ../../node_modules/.bin/webpack --config ./webpack.config.js",
|
||||
"build:nexe": "node scripts/nexe.js",
|
||||
"build": "npm run build:webpack && npm run build:nexe"
|
||||
"build:bootstrap-fork": "cd ../vscode && npm run build:bootstrap-fork; cp ./bin/bootstrap-fork.js ../server/build/bootstrap-fork.js",
|
||||
"build:default-extensions": "cd ../../lib/vscode && npx gulp vscode-linux-arm && cd ../.. && cp -r ./lib/VSCode-linux-arm/resources/app/extensions/* ./packages/server/build/extensions/",
|
||||
"build": "npm run build:bootstrap-fork && npm run build:webpack && npm run build:nexe"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oclif/config": "^1.10.4",
|
||||
@ -16,6 +18,7 @@
|
||||
"express": "^4.16.4",
|
||||
"nexe": "^2.0.0-rc.34",
|
||||
"node-pty": "^0.8.0",
|
||||
"spdlog": "^0.7.2",
|
||||
"ws": "^6.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -23,6 +26,7 @@
|
||||
"@types/ws": "^6.0.1",
|
||||
"string-replace-webpack-plugin": "^0.1.3",
|
||||
"ts-node": "^7.0.1",
|
||||
"tsconfig-paths": "^3.7.0"
|
||||
"tsconfig-paths": "^3.7.0",
|
||||
"typescript": "^3.2.2"
|
||||
}
|
||||
}
|
||||
|
@ -28,14 +28,22 @@ nexe.compile({
|
||||
additionalFiles: [
|
||||
'./node_modules/node-pty/build/Release/pty',
|
||||
],
|
||||
}
|
||||
},
|
||||
"spdlog": {
|
||||
additionalFiles: [
|
||||
'spdlog.node',
|
||||
],
|
||||
},
|
||||
},
|
||||
targets: ["linux"],
|
||||
/**
|
||||
* To include native extensions, do NOT install node_modules for each one. They
|
||||
* are not required as each extension is built using webpack.
|
||||
*/
|
||||
resources: [path.join(__dirname, "../package.json")],
|
||||
resources: [
|
||||
path.join(__dirname, "../package.json"),
|
||||
path.join(__dirname, "../build/**"),
|
||||
],
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { SharedProcessInitMessage } from "@coder/protocol/src/proto";
|
||||
import { Command, flags } from "@oclif/command";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { createApp } from './server';
|
||||
import { requireModule } from "./vscode/bootstrapFork";
|
||||
import { createApp } from "./server";
|
||||
import { SharedProcess } from './vscode/sharedProcess';
|
||||
|
||||
export class Entry extends Command {
|
||||
|
||||
@ -17,23 +20,65 @@ export class Entry extends Command {
|
||||
open: flags.boolean({ char: "o", description: "Open in browser on startup" }),
|
||||
port: flags.integer({ char: "p", default: 8080, description: "Port to bind on" }),
|
||||
version: flags.version({ char: "v" }),
|
||||
|
||||
// Dev flags
|
||||
"bootstrap-fork": flags.string({ hidden: true }),
|
||||
};
|
||||
public static args = [{
|
||||
name: "workdir",
|
||||
description: "Specify working dir",
|
||||
default: () => process.cwd(),
|
||||
default: (): string => process.cwd(),
|
||||
}];
|
||||
|
||||
public async run(): Promise<void> {
|
||||
try {
|
||||
/**
|
||||
* Suuuper janky
|
||||
* Comes from - https://github.com/nexe/nexe/issues/524
|
||||
* Seems to cleanup by removing this path immediately
|
||||
* If any native module is added its assumed this pathname
|
||||
* will change.
|
||||
*/
|
||||
require("spdlog");
|
||||
const nodePath = path.join(process.cwd(), "e91a410b");
|
||||
fs.unlinkSync(path.join(nodePath, "spdlog.node"));
|
||||
fs.rmdirSync(nodePath);
|
||||
} catch (ex) {
|
||||
logger.warn("Failed to remove extracted dependency.", field("dependency", "spdlog"), field("error", ex.message));
|
||||
}
|
||||
|
||||
const { args, flags } = this.parse(Entry);
|
||||
|
||||
const dataDir = flags["data-dir"] || path.join(os.homedir(), `.vscode-online`);
|
||||
if (flags["bootstrap-fork"]) {
|
||||
const modulePath = flags["bootstrap-fork"];
|
||||
if (!modulePath) {
|
||||
logger.error("No module path specified to fork!");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
requireModule(modulePath);
|
||||
return;
|
||||
}
|
||||
|
||||
const dataDir = flags["data-dir"] || path.join(os.homedir(), ".vscode-online");
|
||||
const workingDir = args["workdir"];
|
||||
|
||||
logger.info("\u001B[1mvscode-remote v1.0.0");
|
||||
// TODO: fill in appropriate doc url
|
||||
logger.info("Additional documentation: https://coder.com/docs");
|
||||
logger.info("Initializing", field("data-dir", dataDir), field("working-dir", workingDir));
|
||||
const sharedProcess = new SharedProcess(dataDir);
|
||||
logger.info("Starting shared process...", field("socket", sharedProcess.socketPath));
|
||||
sharedProcess.onWillRestart(() => {
|
||||
logger.info("Restarting shared process...");
|
||||
|
||||
sharedProcess.ready.then(() => {
|
||||
logger.info("Shared process has restarted!");
|
||||
});
|
||||
});
|
||||
sharedProcess.ready.then(() => {
|
||||
logger.info("Shared process has started up!");
|
||||
});
|
||||
|
||||
const app = createApp((app) => {
|
||||
app.use((req, res, next) => {
|
||||
@ -43,17 +88,33 @@ export class Entry extends Command {
|
||||
|
||||
next();
|
||||
});
|
||||
if (process.env.CLI === "false" || !process.env.CLI) {
|
||||
const webpackConfig = require(path.join(__dirname, "..", "..", "web", "webpack.dev.config.js"));
|
||||
const compiler = require("webpack")(webpackConfig);
|
||||
app.use(require("webpack-dev-middleware")(compiler, {
|
||||
logger,
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
stats: webpackConfig.stats,
|
||||
}));
|
||||
app.use(require("webpack-hot-middleware")(compiler));
|
||||
}
|
||||
}, {
|
||||
dataDirectory: dataDir,
|
||||
workingDirectory: workingDir,
|
||||
});
|
||||
|
||||
logger.info("Starting webserver...", field("host", flags.host), field("port", flags.port))
|
||||
logger.info("Starting webserver...", field("host", flags.host), field("port", flags.port));
|
||||
app.server.listen(flags.port, flags.host);
|
||||
let clientId = 1;
|
||||
app.wss.on("connection", (ws, req) => {
|
||||
const id = clientId++;
|
||||
logger.info(`WebSocket opened \u001B[0m${req.url}`, field("client", id), field("ip", req.socket.remoteAddress));
|
||||
const spm = (<any>req).sharedProcessInit as SharedProcessInitMessage;
|
||||
if (!spm) {
|
||||
logger.warn("Received a socket without init data. Not sure how this happened.");
|
||||
|
||||
return;
|
||||
}
|
||||
logger.info(`WebSocket opened \u001B[0m${req.url}`, field("client", id), field("ip", req.socket.remoteAddress), field("window_id", spm.getWindowId()), field("log_directory", spm.getLogDirectory()));
|
||||
|
||||
ws.on("close", (code) => {
|
||||
logger.info(`WebSocket closed \u001B[0m${req.url}`, field("client", id), field("code", code));
|
||||
@ -73,9 +134,10 @@ export class Entry extends Command {
|
||||
logger.info(`http://localhost:${flags.port}/`);
|
||||
logger.info(" ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Entry.run(undefined, {
|
||||
root: process.env.BUILD_DIR as string,
|
||||
root: process.env.BUILD_DIR as string || __dirname,
|
||||
//@ts-ignore
|
||||
}).catch(require("@oclif/errors/handle"));
|
||||
|
70
packages/server/src/ipc.ts
Normal file
70
packages/server/src/ipc.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { EventEmitter } from "events";
|
||||
import { ChildProcess } from "child_process";
|
||||
|
||||
export interface IpcMessage {
|
||||
readonly event: string;
|
||||
readonly args: any[];
|
||||
}
|
||||
|
||||
export class StdioIpcHandler extends EventEmitter {
|
||||
|
||||
private isListening: boolean = false;
|
||||
|
||||
public constructor(
|
||||
private readonly childProcess?: ChildProcess,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public on(event: string, cb: (...args: any[]) => void): this {
|
||||
this.listen();
|
||||
return super.on(event, cb);
|
||||
}
|
||||
|
||||
public once(event: string, cb: (...args: any[]) => void): this {
|
||||
this.listen();
|
||||
return super.once(event, cb);
|
||||
}
|
||||
|
||||
public addListener(event: string, cb: (...args: any[]) => void): this {
|
||||
this.listen();
|
||||
return super.addListener(event, cb);
|
||||
}
|
||||
|
||||
public send(event: string, ...args: any[]): void {
|
||||
const msg: IpcMessage = {
|
||||
event,
|
||||
args,
|
||||
};
|
||||
const d = JSON.stringify(msg);
|
||||
if (this.childProcess) {
|
||||
this.childProcess.stdin.write(d + "\n");
|
||||
} else {
|
||||
process.stdout.write(d);
|
||||
}
|
||||
}
|
||||
|
||||
private listen(): void {
|
||||
if (this.isListening) {
|
||||
return;
|
||||
}
|
||||
const onData = (data: any) => {
|
||||
try {
|
||||
const d = JSON.parse(data.toString()) as IpcMessage;
|
||||
this.emit(d.event, ...d.args);
|
||||
} catch (ex) {
|
||||
if (!this.childProcess) {
|
||||
process.stderr.write(`Failed to parse incoming data: ${ex.message}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (this.childProcess) {
|
||||
this.childProcess.stdout.resume();
|
||||
this.childProcess.stdout.on("data", onData);
|
||||
} else {
|
||||
process.stdin.resume();
|
||||
process.stdin.on("data", onData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,12 +3,14 @@ import { Server, ServerOptions } from "@coder/protocol/src/node/server";
|
||||
import * as express from "express";
|
||||
import * as http from "http";
|
||||
import * as ws from "ws";
|
||||
import * as url from "url";
|
||||
import { ClientMessage, SharedProcessInitMessage } from '@coder/protocol/src/proto';
|
||||
|
||||
export const createApp = (registerMiddleware?: (app: express.Application) => void, options?: ServerOptions): {
|
||||
readonly express: express.Application;
|
||||
readonly server: http.Server;
|
||||
readonly wss: ws.Server;
|
||||
} => {
|
||||
} => {
|
||||
const app = express();
|
||||
if (registerMiddleware) {
|
||||
registerMiddleware(app);
|
||||
@ -16,21 +18,52 @@ export const createApp = (registerMiddleware?: (app: express.Application) => voi
|
||||
const server = http.createServer(app);
|
||||
const wss = new ws.Server({ server });
|
||||
|
||||
wss.on("connection", (ws: WebSocket) => {
|
||||
wss.shouldHandle = (req): boolean => {
|
||||
if (typeof req.url === "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
const sharedProcessInit = parsedUrl.query["shared_process_init"];
|
||||
if (typeof sharedProcessInit === "undefined" || Array.isArray(sharedProcessInit)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const msg = ClientMessage.deserializeBinary(Buffer.from(sharedProcessInit, "base64"));
|
||||
if (!msg.hasSharedProcessInit()) {
|
||||
return false;
|
||||
}
|
||||
const spm = msg.getSharedProcessInit()!;
|
||||
(<any>req).sharedProcessInit = spm;
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
wss.on("connection", (ws: WebSocket, req) => {
|
||||
const spm = (<any>req).sharedProcessInit as SharedProcessInitMessage;
|
||||
if (!spm) {
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const connection: ReadWriteConnection = {
|
||||
onMessage: (cb) => {
|
||||
onMessage: (cb): void => {
|
||||
ws.addEventListener("message", (event) => cb(event.data));
|
||||
},
|
||||
close: () => ws.close(),
|
||||
send: (data) => ws.send(data),
|
||||
onClose: (cb) => ws.addEventListener("close", () => cb()),
|
||||
close: (): void => ws.close(),
|
||||
send: (data): void => ws.send(data),
|
||||
onClose: (cb): void => ws.addEventListener("close", () => cb()),
|
||||
};
|
||||
|
||||
const server = new Server(connection, options);
|
||||
});
|
||||
|
||||
/**
|
||||
* We should static-serve the `web` package at this point
|
||||
* We should static-serve the `web` package at this point.
|
||||
*/
|
||||
app.get("/", (req, res, next) => {
|
||||
res.write("Example! :)");
|
||||
|
29
packages/server/src/vscode/bootstrapFork.ts
Normal file
29
packages/server/src/vscode/bootstrapFork.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import * as cp from "child_process";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
declare var __non_webpack_require__: typeof require;
|
||||
|
||||
export const requireModule = (modulePath: string): void => {
|
||||
process.env.AMD_ENTRYPOINT = modulePath;
|
||||
process.env.VSCODE_ALLOW_IO = "true";
|
||||
const content = fs.readFileSync(path.join(process.env.BUILD_DIR as string || path.join(__dirname, "../.."), "./build/bootstrap-fork.js"));
|
||||
eval(content.toString());
|
||||
};
|
||||
|
||||
/**
|
||||
* Uses the internal bootstrap-fork.js to load a module
|
||||
* @example
|
||||
* const cp = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain");
|
||||
* cp.stdout.on("data", (data) => console.log(data.toString("utf8")));
|
||||
* cp.stderr.on("data", (data) => console.log(data.toString("utf8")));
|
||||
* @param modulePath
|
||||
*/
|
||||
export const forkModule = (modulePath: string): cp.ChildProcess => {
|
||||
const args = ["--bootstrap-fork", modulePath];
|
||||
if (process.env.CLI === "true") {
|
||||
return cp.spawn(process.execPath, args);
|
||||
} else {
|
||||
return cp.spawn("npm", ["start", "--scripts-prepend-node-path", "--", ...args]);
|
||||
}
|
||||
};
|
106
packages/server/src/vscode/sharedProcess.ts
Normal file
106
packages/server/src/vscode/sharedProcess.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { ChildProcess } from "child_process";
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { forkModule } from "./bootstrapFork";
|
||||
import { StdioIpcHandler } from "../ipc";
|
||||
import { logger, field } from "@coder/logger/src";
|
||||
import { ParsedArgs } from "vs/platform/environment/common/environment";
|
||||
import { LogLevel } from "vs/platform/log/common/log";
|
||||
import { Emitter, Event } from '@coder/events/src';
|
||||
|
||||
export class SharedProcess {
|
||||
public readonly socketPath: string = path.join(os.tmpdir(), `.vscode-online${Math.random().toString()}`);
|
||||
private _ready: Promise<void> | undefined;
|
||||
private activeProcess: ChildProcess | undefined;
|
||||
private ipcHandler: StdioIpcHandler | undefined;
|
||||
private readonly willRestartEmitter: Emitter<void>;
|
||||
|
||||
public constructor(
|
||||
private readonly userDataDir: string,
|
||||
) {
|
||||
this.willRestartEmitter = new Emitter();
|
||||
|
||||
this.restart();
|
||||
}
|
||||
|
||||
public get onWillRestart(): Event<void> {
|
||||
return this.willRestartEmitter.event;
|
||||
}
|
||||
|
||||
public get ready(): Promise<void> {
|
||||
return this._ready!;
|
||||
}
|
||||
|
||||
public restart(): void {
|
||||
if (this.activeProcess) {
|
||||
this.willRestartEmitter.emit();
|
||||
}
|
||||
|
||||
if (this.activeProcess && !this.activeProcess.killed) {
|
||||
this.activeProcess.kill();
|
||||
}
|
||||
|
||||
let resolve: () => void;
|
||||
let reject: (err: Error) => void;
|
||||
|
||||
const extensionsDir = path.join(this.userDataDir, "extensions");
|
||||
const mkdir = (dir: string): void => {
|
||||
try {
|
||||
fs.mkdirSync(dir);
|
||||
} catch (ex) {
|
||||
if (ex.code !== "EEXIST") {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
};
|
||||
mkdir(this.userDataDir);
|
||||
mkdir(extensionsDir);
|
||||
|
||||
this._ready = new Promise<void>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
|
||||
let resolved: boolean = false;
|
||||
this.activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain");
|
||||
this.activeProcess.on("exit", () => {
|
||||
this.restart();
|
||||
});
|
||||
this.ipcHandler = new StdioIpcHandler(this.activeProcess);
|
||||
this.ipcHandler.once("handshake:hello", () => {
|
||||
const data: {
|
||||
sharedIPCHandle: string;
|
||||
args: ParsedArgs;
|
||||
logLevel: LogLevel;
|
||||
} = {
|
||||
args: {
|
||||
"builtin-extensions-dir": path.join(process.env.BUILD_DIR || path.join(__dirname, "../.."), "build/extensions"),
|
||||
"user-data-dir": this.userDataDir,
|
||||
"extensions-dir": extensionsDir,
|
||||
} as any,
|
||||
logLevel: 0,
|
||||
sharedIPCHandle: this.socketPath,
|
||||
};
|
||||
this.ipcHandler!.send("handshake:hey there", "", data);
|
||||
});
|
||||
this.ipcHandler.once("handshake:im ready", () => {
|
||||
resolved = true;
|
||||
resolve();
|
||||
});
|
||||
this.activeProcess.stderr.on("data", (data) => {
|
||||
if (!resolved) {
|
||||
reject(data.toString());
|
||||
} else {
|
||||
logger.named("SHRD PROC").debug("stderr", field("message", data.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.ipcHandler) {
|
||||
this.ipcHandler.send("handshake:goodbye");
|
||||
}
|
||||
this.ipcHandler = undefined;
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@ const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const merge = require("webpack-merge");
|
||||
const StringReplacePlugin = require("string-replace-webpack-plugin");
|
||||
const HappyPack = require("happypack");
|
||||
|
||||
module.exports = merge({
|
||||
devtool: 'none',
|
||||
devtool: "none",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
@ -35,14 +36,15 @@ module.exports = merge({
|
||||
__dirname: false,
|
||||
setImmediate: false
|
||||
},
|
||||
externals: ["node-pty"],
|
||||
externals: ["node-pty", "spdlog"],
|
||||
entry: "./packages/server/src/cli.ts",
|
||||
target: "node",
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
"process.env.BUILD_DIR": `"${__dirname}"`,
|
||||
"process.env.CLI": `"${process.env.CLI ? "true" : "false"}"`,
|
||||
}),
|
||||
],
|
||||
}, require("../../scripts/webpack.general.config"), {
|
||||
}, require("../../scripts/webpack.general.config")(), {
|
||||
mode: "development",
|
||||
});
|
||||
|
@ -418,6 +418,11 @@ binary-extensions@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14"
|
||||
integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==
|
||||
|
||||
bindings@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.1.tgz#21fc7c6d67c18516ec5aaa2815b145ff77b26ea5"
|
||||
integrity sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew==
|
||||
|
||||
bl@^1.0.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c"
|
||||
@ -1386,9 +1391,9 @@ fs.realpath@^1.0.0:
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
fsevents@^1.0.0:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
|
||||
integrity sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4"
|
||||
integrity sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==
|
||||
dependencies:
|
||||
nan "^2.9.2"
|
||||
node-pre-gyp "^0.10.0"
|
||||
@ -2152,9 +2157,9 @@ map-visit@^1.0.0:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
math-random@^1.0.1:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.3.tgz#5843d8307f8d2fd83de240701eeb2dc7bc77a104"
|
||||
integrity sha512-hULdrPg17lCyaJOrDwS4RSGQcc/MFyv1aujidohCsBq2zotkhIns8mMDQ7B8VnKG23xcpa+haU5MLDNyNzCesQ==
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
|
||||
integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
@ -2313,7 +2318,7 @@ nan@2.10.0:
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
|
||||
integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==
|
||||
|
||||
nan@^2.9.2:
|
||||
nan@^2.8.0, nan@^2.9.2:
|
||||
version "2.12.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
|
||||
integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==
|
||||
@ -3169,6 +3174,15 @@ source-map@~0.1.38:
|
||||
dependencies:
|
||||
amdefine ">=0.0.4"
|
||||
|
||||
spdlog@^0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.7.2.tgz#9298753d7694b9ee9bbfd7e01ea1e4c6ace1e64d"
|
||||
integrity sha512-rHfWCaWMD4NindDnql6rc6kn7Bs8JR92jhiUpCl3D6v+jYcQ6GozMLig0RliOOR8st5mU+IHLZnr15fBys5x/Q==
|
||||
dependencies:
|
||||
bindings "^1.3.0"
|
||||
mkdirp "^0.5.1"
|
||||
nan "^2.8.0"
|
||||
|
||||
split-string@^3.0.1, split-string@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
||||
@ -3464,6 +3478,11 @@ typescript@2.5.3:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d"
|
||||
integrity sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==
|
||||
|
||||
typescript@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5"
|
||||
integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==
|
||||
|
||||
uglify-es@^3.3.9:
|
||||
version "3.3.9"
|
||||
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
|
||||
|
1
packages/vscode/.gitignore
vendored
Normal file
1
packages/vscode/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
bin
|
@ -1,5 +1,12 @@
|
||||
{
|
||||
"name": "@coder/vscode",
|
||||
"description": "VS Code implementation of the browser-based IDE client.",
|
||||
"main": "src/index.ts"
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"build:bootstrap-fork": "../../node_modules/.bin/webpack --config ./webpack.config.bootstrap.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"spdlog": "^0.7.2",
|
||||
"string-replace-loader": "^2.1.1"
|
||||
}
|
||||
}
|
||||
|
133
packages/vscode/src/client.ts
Normal file
133
packages/vscode/src/client.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import "./fill/require";
|
||||
import "./fill/storageDatabase";
|
||||
import "./fill/windowsService";
|
||||
|
||||
import { fork } from "child_process";
|
||||
import { Client as IDEClient, IURI, IURIFactory } from "@coder/ide";
|
||||
import { logger } from "@coder/logger";
|
||||
|
||||
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
|
||||
import { LogLevel } from "vs/platform/log/common/log";
|
||||
import { toLocalISOString } from "vs/base/common/date";
|
||||
// import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
|
||||
import { URI } from "vs/base/common/uri";
|
||||
|
||||
import { Protocol, ISharedProcessInitData } from "./protocol";
|
||||
import * as paths from "./fill/paths";
|
||||
import "./firefox";
|
||||
|
||||
export class Client extends IDEClient {
|
||||
|
||||
private readonly sharedProcessLogger = logger.named("shr proc");
|
||||
private readonly windowId = parseInt(toLocalISOString(new Date()).replace(/[-:.TZ]/g, ""), 10);
|
||||
private readonly version = "hello"; // TODO: pull from package.json probably
|
||||
private readonly bootstrapForkLocation = "/bootstrap"; // TODO: location.
|
||||
public readonly protocolPromise: Promise<Protocol>;
|
||||
private protoResolve: ((protocol: Protocol) => void) | undefined;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
process.env.VSCODE_LOGS = "/tmp/vscode-online/logs"; // TODO: use tmpdir or get log directory from init data.
|
||||
this.protocolPromise = new Promise((resolve): void => {
|
||||
this.protoResolve = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
protected initialize(): Promise<void> {
|
||||
this.task("Start shared process", 5, async () => {
|
||||
const protocol = await this.forkSharedProcess();
|
||||
this.protoResolve!(protocol);
|
||||
}).catch(() => undefined);
|
||||
|
||||
registerContextMenuListener();
|
||||
|
||||
return this.task("Start workbench", 1000, async (initData) => {
|
||||
const { startup } = require("./startup");
|
||||
await startup({
|
||||
machineId: "1",
|
||||
windowId: this.windowId,
|
||||
logLevel: LogLevel.Info,
|
||||
mainPid: 1,
|
||||
appRoot: initData.dataDirectory,
|
||||
execPath: initData.tmpDirectory,
|
||||
userEnv: {},
|
||||
nodeCachedDataDir: initData.tmpDirectory,
|
||||
perfEntries: [],
|
||||
_: [],
|
||||
folderUri: URI.file(initData.dataDirectory),
|
||||
});
|
||||
|
||||
// TODO: Set up clipboard context.
|
||||
// const workbench = workbenchShell.workbench;
|
||||
// const contextKeys = workbench.workbenchParams.serviceCollection.get(IContextKeyService) as IContextKeyService;
|
||||
// const clipboardContextKey = new RawContextKey("nativeClipboard", this.clipboard.isSupported);
|
||||
// const bounded = clipboardContextKey.bindTo(contextKeys);
|
||||
// this.clipboard.onPermissionChange((enabled) => {
|
||||
// bounded.set(enabled);
|
||||
// });
|
||||
this.clipboard.initialize();
|
||||
}, this.initData);
|
||||
}
|
||||
|
||||
public async forkSharedProcess(): Promise<Protocol> {
|
||||
const childProcess = fork(this.bootstrapForkLocation, ["--shared"], {
|
||||
env: {
|
||||
"VSCODE_ALLOW_IO": "true",
|
||||
"AMD_ENTRYPOINT": "vs/code/electron-browser/sharedProcess/sharedProcessClient",
|
||||
},
|
||||
});
|
||||
|
||||
childProcess.stderr.on("data", (data) => {
|
||||
this.sharedProcessLogger.error("stderr: " + data);
|
||||
});
|
||||
|
||||
const protocol = Protocol.fromProcess(childProcess);
|
||||
await new Promise((resolve, reject): void => {
|
||||
protocol.onClose(() => {
|
||||
reject(new Error("unable to establish connection to shared process"));
|
||||
});
|
||||
|
||||
const listener = protocol.onMessage((message) => {
|
||||
const messageStr = message.toString();
|
||||
this.sharedProcessLogger.debug(messageStr);
|
||||
switch (messageStr) {
|
||||
case "handshake:hello":
|
||||
protocol.send(Buffer.from(JSON.stringify({
|
||||
// Using the version so if we get a new mount, it spins up a new
|
||||
// shared process.
|
||||
socketPath: `/tmp/vscode-online/shared-${this.version}.sock`,
|
||||
serviceUrl: "", // TODO
|
||||
logsDir: process.env.VSCODE_LOGS,
|
||||
windowId: this.windowId,
|
||||
logLevel: LogLevel.Info,
|
||||
} as ISharedProcessInitData)));
|
||||
break;
|
||||
case "handshake:ready":
|
||||
listener.dispose();
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return protocol;
|
||||
}
|
||||
|
||||
protected createUriFactory(): IURIFactory {
|
||||
return {
|
||||
// TODO: not sure why this is an error.
|
||||
// tslint:disable-next-line no-any
|
||||
create: <URI>(uri: IURI): URI => URI.from(uri) as any,
|
||||
file: (path: string): IURI => URI.file(path),
|
||||
parse: (raw: string): IURI => URI.parse(raw),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const client = new Client();
|
||||
|
||||
client.initData.then((initData) => {
|
||||
paths.appData = initData.dataDirectory;
|
||||
paths.defaultUserData = initData.dataDirectory;
|
||||
});
|
11
packages/vscode/src/fill/amd.ts
Normal file
11
packages/vscode/src/fill/amd.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { URI } from "vs/base/common/uri";
|
||||
|
||||
export const getPathFromAmdModule = (_: typeof require, relativePath: string): string => {
|
||||
if (process.mainModule && process.mainModule.filename) {
|
||||
const index = process.mainModule.filename.lastIndexOf("/");
|
||||
|
||||
return process.mainModule.filename.slice(0, index);
|
||||
}
|
||||
|
||||
return relativePath ? URI.parse(require.toUrl(relativePath)).fsPath : "";
|
||||
};
|
1
packages/vscode/src/fill/graceful-fs.ts
Normal file
1
packages/vscode/src/fill/graceful-fs.ts
Normal file
@ -0,0 +1 @@
|
||||
export const gracefulify = (): void => undefined;
|
@ -1,8 +1,13 @@
|
||||
module.exports = {
|
||||
getCurrentKeyboardLayout: (): null => {
|
||||
class NativeKeymap {
|
||||
|
||||
public getCurrentKeyboardLayout(): null {
|
||||
return null;
|
||||
},
|
||||
getKeyMap: (): undefined[] => {
|
||||
}
|
||||
|
||||
public getKeyMap(): undefined[] {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export = new NativeKeymap();
|
||||
|
@ -70,4 +70,4 @@ const ptyType: nodePtyType = {
|
||||
|
||||
};
|
||||
|
||||
module.exports = ptyType;
|
||||
exports = ptyType;
|
||||
|
2
packages/vscode/src/fill/package.ts
Normal file
2
packages/vscode/src/fill/package.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// TODO: obtain this in a reasonable way.
|
||||
export default { name: "vscode", version: "1.31.1" };
|
7
packages/vscode/src/fill/paths.ts
Normal file
7
packages/vscode/src/fill/paths.ts
Normal file
@ -0,0 +1,7 @@
|
||||
const paths = {
|
||||
appData: "/tmp",
|
||||
defaultUserData: "/tmp",
|
||||
};
|
||||
|
||||
export let getAppDataPath = (): string => paths.appData;
|
||||
export let getDefaultUserDataPath = (): string => paths.defaultUserData;
|
24
packages/vscode/src/fill/product.ts
Normal file
24
packages/vscode/src/fill/product.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { IProductConfiguration } from "vs/platform/node/product";
|
||||
|
||||
const product = {
|
||||
nameShort: "VSCode",
|
||||
nameLong: "vscode online",
|
||||
dataFolderName: ".vscode-online",
|
||||
extensionsGallery: {
|
||||
serviceUrl: "",
|
||||
},
|
||||
extensionExecutionEnvironments: {
|
||||
"wayou.vscode-todo-highlight": "worker",
|
||||
"vscodevim.vim": "worker",
|
||||
"coenraads.bracket-pair-colorizer": "worker",
|
||||
},
|
||||
fetchUrl: "",
|
||||
} as IProductConfiguration;
|
||||
|
||||
if (process.env['VSCODE_DEV']) {
|
||||
product.nameShort += ' Dev';
|
||||
product.nameLong += ' Dev';
|
||||
product.dataFolderName += '-dev';
|
||||
}
|
||||
|
||||
export default product;
|
3
packages/vscode/src/fill/require.ts
Normal file
3
packages/vscode/src/fill/require.ts
Normal file
@ -0,0 +1,3 @@
|
||||
// TODO: ?
|
||||
// tslint:disable-next-line no-any
|
||||
(global as any).requireToUrl = (path: string): string => `${location.protocol}//{location.host}/${path}`;
|
186
packages/vscode/src/fill/spdlog.ts
Normal file
186
packages/vscode/src/fill/spdlog.ts
Normal file
@ -0,0 +1,186 @@
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { appendFile, stat, readdir } from "fs";
|
||||
import { RotatingLogger as NodeRotatingLogger } from "spdlog";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { escapePath } from "@coder/protocol";
|
||||
|
||||
// TODO: It would be better to spawn an actual spdlog instance on the server and
|
||||
// use that for the logging. Or maybe create an instance when the server starts,
|
||||
// and just always use that one (make it part of the protocol).
|
||||
export class RotatingLogger implements NodeRotatingLogger {
|
||||
|
||||
private format = true;
|
||||
private buffer = "";
|
||||
private flushPromise: Promise<void> | undefined;
|
||||
private name: string;
|
||||
private logDirectory: string;
|
||||
private escapedLogDirectory: string;
|
||||
private fullFilePath: string;
|
||||
private fileName: string;
|
||||
private fileExt: string | undefined;
|
||||
private escapedFilePath: string;
|
||||
private filesize: number;
|
||||
private filecount: number;
|
||||
|
||||
public constructor(name: string, filePath: string, filesize: number, filecount: number) {
|
||||
this.name = name;
|
||||
this.filesize = filesize;
|
||||
this.filecount = filecount;
|
||||
|
||||
this.fullFilePath = filePath;
|
||||
const slashIndex = filePath.lastIndexOf("/");
|
||||
const dotIndex = filePath.lastIndexOf(".");
|
||||
this.logDirectory = slashIndex !== -1 ? filePath.substring(0, slashIndex) : "/";
|
||||
this.fileName = filePath.substring(slashIndex + 1, dotIndex !== -1 ? dotIndex : undefined);
|
||||
this.fileExt = dotIndex !== -1 ? filePath.substring(dotIndex + 1) : undefined;
|
||||
|
||||
this.escapedLogDirectory = escapePath(this.logDirectory);
|
||||
this.escapedFilePath = escapePath(filePath);
|
||||
|
||||
this.flushPromise = new Promise((resolve): void => {
|
||||
exec(`mkdir -p ${this.escapedLogDirectory}; touch ${this.escapedFilePath}`, async (error) => {
|
||||
if (!error) {
|
||||
try {
|
||||
await this.doFlush();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
logger.error(error.message, field("error", error));
|
||||
}
|
||||
this.flushPromise = undefined;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public trace(message: string): void {
|
||||
this.write("trace", message);
|
||||
}
|
||||
|
||||
public debug(message: string): void {
|
||||
this.write("debug", message);
|
||||
}
|
||||
|
||||
public info(message: string): void {
|
||||
this.write("info", message);
|
||||
}
|
||||
|
||||
public warn(message: string): void {
|
||||
this.write("warn", message);
|
||||
}
|
||||
|
||||
public error(message: string): void {
|
||||
this.write("error", message);
|
||||
}
|
||||
|
||||
public critical(message: string): void {
|
||||
this.write("critical", message);
|
||||
}
|
||||
|
||||
public setLevel(): void {
|
||||
// Should output everything.
|
||||
}
|
||||
|
||||
public clearFormatters(): void {
|
||||
this.format = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the buffer. Only one process runs at a time to prevent race
|
||||
* conditions.
|
||||
*/
|
||||
public flush(): Promise<void> {
|
||||
if (!this.flushPromise) {
|
||||
this.flushPromise = this.doFlush().then(() => {
|
||||
this.flushPromise = undefined;
|
||||
}).catch((error) => {
|
||||
this.flushPromise = undefined;
|
||||
logger.error(error.message, field("error", error));
|
||||
});
|
||||
}
|
||||
|
||||
return this.flushPromise;
|
||||
}
|
||||
|
||||
public drop(): void {
|
||||
this.buffer = "";
|
||||
}
|
||||
|
||||
private pad(num: number, length: number = 2, prefix: string = "0"): string {
|
||||
const str = num.toString();
|
||||
|
||||
return (length > str.length ? prefix.repeat(length - str.length) : "") + str;
|
||||
}
|
||||
|
||||
private write(severity: string, message: string): void {
|
||||
if (this.format) {
|
||||
const date = new Date();
|
||||
const dateStr = `${date.getFullYear()}-${this.pad(date.getMonth() + 1)}-${this.pad(date.getDate())}`
|
||||
+ ` ${this.pad(date.getHours())}:${this.pad(date.getMinutes())}:${this.pad(date.getSeconds())}.${this.pad(date.getMilliseconds(), 3)}`;
|
||||
this.buffer += `[${dateStr}] [${this.name}] [${severity}] `;
|
||||
}
|
||||
this.buffer += message;
|
||||
if (this.format) {
|
||||
this.buffer += "\n";
|
||||
}
|
||||
this.flush();
|
||||
}
|
||||
|
||||
private async rotate(): Promise<void> {
|
||||
const stats = await promisify(stat)(this.fullFilePath);
|
||||
if (stats.size < this.filesize) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reExt = typeof this.fileExt !== "undefined" ? `\\.${this.fileExt}` : "";
|
||||
const re = new RegExp(`^${this.fileName}(?:\\.(\\d+))?${reExt}$`);
|
||||
const orderedFiles: string[] = [];
|
||||
(await promisify(readdir)(this.logDirectory)).forEach((file) => {
|
||||
const match = re.exec(file);
|
||||
if (match) {
|
||||
orderedFiles[typeof match[1] !== "undefined" ? parseInt(match[1], 10) : 0] = file;
|
||||
}
|
||||
});
|
||||
|
||||
// Rename in reverse so we don't overwrite before renaming something.
|
||||
let count = 0;
|
||||
const command = orderedFiles.map((file) => {
|
||||
const fileExt = typeof this.fileExt !== "undefined" ? `.${this.fileExt}` : "";
|
||||
const newFile = `${this.logDirectory}/${this.fileName}.${++count}${fileExt}`;
|
||||
|
||||
return count >= this.filecount
|
||||
? `rm ${escapePath(this.logDirectory + "/" + file)}`
|
||||
: `mv ${escapePath(this.logDirectory + "/" + file)} ${escapePath(newFile)}`;
|
||||
}).reverse().concat([
|
||||
`touch ${escapePath(this.fullFilePath)}`,
|
||||
]).join(";");
|
||||
|
||||
await promisify(exec)(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the entire buffer, including anything added in the meantime, and
|
||||
* rotates the log if necessary.
|
||||
*/
|
||||
private async doFlush(): Promise<void> {
|
||||
const writeBuffer = async (): Promise<void> => {
|
||||
const toWrite = this.buffer;
|
||||
this.buffer = "";
|
||||
|
||||
await promisify(appendFile)(this.fullFilePath, toWrite);
|
||||
};
|
||||
|
||||
while (this.buffer.length > 0) {
|
||||
await writeBuffer();
|
||||
await this.rotate();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const setAsyncMode = (): void => {
|
||||
// Nothing to do.
|
||||
};
|
22
packages/vscode/src/fill/stdioElectron.ts
Normal file
22
packages/vscode/src/fill/stdioElectron.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { StdioIpcHandler } from "@coder/server/src/ipc";
|
||||
import { IpcRenderer } from "electron";
|
||||
|
||||
export * from "@coder/ide/src/fill/electron";
|
||||
|
||||
class StdioIpcRenderer extends StdioIpcHandler implements IpcRenderer {
|
||||
|
||||
public sendTo(windowId: number, channel: string, ...args: any[]): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
public sendToHost(channel: string, ...args: any[]): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
public eventNames(): string[] {
|
||||
return super.eventNames() as string[];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const ipcRenderer = new StdioIpcRenderer();
|
78
packages/vscode/src/fill/storageDatabase.ts
Normal file
78
packages/vscode/src/fill/storageDatabase.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { readFile, writeFile } from "fs";
|
||||
import { promisify } from "util";
|
||||
import { Event } from "vs/base/common/event";
|
||||
import * as storage from "vs/base/node/storage";
|
||||
|
||||
export class StorageDatabase implements storage.IStorageDatabase {
|
||||
|
||||
public readonly onDidChangeItemsExternal = Event.None;
|
||||
private items = new Map<string, string>();
|
||||
private fetched: boolean = false;
|
||||
|
||||
public constructor(private readonly path: string) {
|
||||
window.addEventListener("unload", () => {
|
||||
if (!navigator.sendBeacon) {
|
||||
throw new Error("cannot save state");
|
||||
}
|
||||
// TODO: Need to use navigator.sendBeacon instead of the web socket, or we
|
||||
// need to save when there is a change. Should we save as a sqlite3
|
||||
// database instead of JSON? Could send to the server the way the global
|
||||
// storage works. Or maybe fill `vscode-sqlite3` to do that.
|
||||
this.save();
|
||||
});
|
||||
}
|
||||
|
||||
public async getItems(): Promise<Map<string, string>> {
|
||||
if (this.fetched) {
|
||||
return this.items;
|
||||
}
|
||||
try {
|
||||
const contents = await promisify(readFile)(this.path, "utf8");
|
||||
const json = JSON.parse(contents);
|
||||
Object.keys(json).forEach((key) => {
|
||||
this.items.set(key, json[key]);
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code && error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
this.fetched = true;
|
||||
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public updateItems(request: storage.IUpdateRequest): Promise<void> {
|
||||
if (request.insert) {
|
||||
request.insert.forEach((value, key) => this.items.set(key, value));
|
||||
}
|
||||
|
||||
if (request.delete) {
|
||||
request.delete.forEach(key => this.items.delete(key));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public close(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public checkIntegrity(): Promise<string> {
|
||||
return Promise.resolve("ok");
|
||||
}
|
||||
|
||||
private save(): Promise<void> {
|
||||
const json: { [key: string]: string } = {};
|
||||
this.items.forEach((value, key) => {
|
||||
json[key] = value;
|
||||
});
|
||||
|
||||
return promisify(writeFile)(this.path, JSON.stringify(json));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
storage.SQLiteStorageDatabase = StorageDatabase;
|
274
packages/vscode/src/fill/windowsService.ts
Normal file
274
packages/vscode/src/fill/windowsService.ts
Normal file
@ -0,0 +1,274 @@
|
||||
import * as electron from "electron";
|
||||
import { Emitter } from "@coder/events";
|
||||
import * as windowsIpc from "vs/platform/windows/node/windowsIpc";
|
||||
import { IWindowsService, INativeOpenDialogOptions, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IMessageBoxResult, IDevToolsOptions, IEnterWorkspaceResult, CrashReporterStartOptions, INewWindowOptions } from "vs/platform/windows/common/windows";
|
||||
import { ParsedArgs } from "vs/platform/environment/common/environment";
|
||||
import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from "vs/platform/workspaces/common/workspaces";
|
||||
import { URI } from "vs/base/common/uri";
|
||||
import { IRecentlyOpened } from "vs/platform/history/common/history";
|
||||
import { ISerializableCommandAction } from "vs/platform/actions/common/actions";
|
||||
|
||||
// TODO: Might make sense to hook these straight in if we can.
|
||||
// import { WindowsService as VSWindowsService } from "vs/platform/windows/electron-main/windowsService";
|
||||
// import { WindowsManager } from "vs/code/electron-main/windows";
|
||||
|
||||
/**
|
||||
* Instead of going to the shared process, we'll directly run these methods on
|
||||
* the client. This setup means we can only control the current window.
|
||||
*/
|
||||
class WindowsService implements IWindowsService {
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public _serviceBrand: any;
|
||||
|
||||
private openEmitter = new Emitter<number>();
|
||||
private focusEmitter = new Emitter<number>();
|
||||
private blurEmitter = new Emitter<number>();
|
||||
private maximizeEmitter = new Emitter<number>();
|
||||
private unmaximizeEmitter = new Emitter<number>();
|
||||
private recentlyOpenedChangeEmitter = new Emitter<void>();
|
||||
|
||||
public onWindowOpen = this.openEmitter.event;
|
||||
public onWindowFocus = this.focusEmitter.event;
|
||||
public onWindowBlur = this.blurEmitter.event;
|
||||
public onWindowMaximize = this.maximizeEmitter.event;
|
||||
public onWindowUnmaximize = this.unmaximizeEmitter.event;
|
||||
public onRecentlyOpenedChange = this.recentlyOpenedChangeEmitter.event;
|
||||
|
||||
private window = new electron.BrowserWindow();
|
||||
|
||||
// Dialogs
|
||||
public pickFileFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public pickFileAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public pickFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public pickWorkspaceAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showMessageBox(_windowId: number, _options: MessageBoxOptions): Promise<IMessageBoxResult> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showSaveDialog(_windowId: number, _options: SaveDialogOptions): Promise<string> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showOpenDialog(_windowId: number, _options: OpenDialogOptions): Promise<string[]> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public reloadWindow(windowId: number, _args?: ParsedArgs): Promise<void> {
|
||||
return Promise.resolve(this.getWindowById(windowId).reload());
|
||||
}
|
||||
|
||||
public openDevTools(_windowId: number, _options?: IDevToolsOptions): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public toggleDevTools(_windowId: number): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public closeWorkspace(_windowId: number): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public enterWorkspace(_windowId: number, _path: string): Promise<IEnterWorkspaceResult> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public createAndEnterWorkspace(_windowId: number, _folders?: IWorkspaceFolderCreationData[], _path?: string): Promise<IEnterWorkspaceResult> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public saveAndEnterWorkspace(_windowId: number, _path: string): Promise<IEnterWorkspaceResult> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public toggleFullScreen(windowId: number): Promise<void> {
|
||||
const win = this.getWindowById(windowId);
|
||||
|
||||
return Promise.resolve(win.setFullScreen(!win.isFullScreen()));
|
||||
}
|
||||
|
||||
public setRepresentedFilename(windowId: number, fileName: string): Promise<void> {
|
||||
return Promise.resolve(this.getWindowById(windowId).setRepresentedFilename(fileName));
|
||||
}
|
||||
|
||||
public addRecentlyOpened(_files: URI[]): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public removeFromRecentlyOpened(_paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public clearRecentlyOpened(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public getRecentlyOpened(_windowId: number): Promise<IRecentlyOpened> {
|
||||
// TODO: properly implement.
|
||||
return Promise.resolve({
|
||||
workspaces: [],
|
||||
files: [],
|
||||
});
|
||||
}
|
||||
|
||||
public focusWindow(windowId: number): Promise<void> {
|
||||
return Promise.resolve(this.getWindowById(windowId).focus());
|
||||
}
|
||||
|
||||
public closeWindow(_windowId: number): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public isFocused(windowId: number): Promise<boolean> {
|
||||
return Promise.resolve(this.getWindowById(windowId).isFocused());
|
||||
}
|
||||
|
||||
public isMaximized(_windowId: number): Promise<boolean> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public maximizeWindow(_windowId: number): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public unmaximizeWindow(_windowId: number): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public minimizeWindow(_windowId: number): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public onWindowTitleDoubleClick(_windowId: number): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public setDocumentEdited(_windowId: number, _flag: boolean): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public quit(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public relaunch(_options: { addArgs?: string[], removeArgs?: string[] }): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
// macOS Native Tabs
|
||||
public newWindowTab(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showPreviousWindowTab(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showNextWindowTab(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public moveWindowTabToNewWindow(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public mergeAllWindowTabs(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public toggleWindowTabsBar(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
// macOS TouchBar
|
||||
public updateTouchBar(_windowId: number, _items: ISerializableCommandAction[][]): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
// Shared process
|
||||
public whenSharedProcessReady(): Promise<void> {
|
||||
// TODO: Update once shared process is tied in.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public toggleSharedProcess(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
// Global methods
|
||||
public openWindow(_windowId: number, _paths: URI[], _options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public openNewWindow(_options?: INewWindowOptions): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showWindow(windowId: number): Promise<void> {
|
||||
return Promise.resolve(this.getWindowById(windowId).show());
|
||||
}
|
||||
|
||||
public getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public getWindowCount(): Promise<number> {
|
||||
return Promise.resolve(1);
|
||||
}
|
||||
|
||||
public log(_severity: string, ..._messages: string[]): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public showItemInFolder(_path: string): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public getActiveWindowId(): Promise<number | undefined> {
|
||||
return Promise.resolve(1);
|
||||
}
|
||||
|
||||
public openExternal(_url: string): Promise<boolean> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public startCrashReporter(_config: CrashReporterStartOptions): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public openAboutDialog(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public resolveProxy(windowId: number, url: string): Promise<string | undefined> {
|
||||
return new Promise((resolve): void => {
|
||||
this.getWindowById(windowId).webContents.session.resolveProxy(url, (proxy) => {
|
||||
resolve(proxy);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get window by ID. For now this is always the current window.
|
||||
*/
|
||||
private getWindowById(_windowId: number): electron.BrowserWindow {
|
||||
return this.window;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
windowsIpc.WindowsChannelClient = WindowsService;
|
@ -1,38 +1 @@
|
||||
import { field, logger, time } from "@coder/logger";
|
||||
import { Client, IURI, setUriFactory } from "@coder/ide";
|
||||
import { URI } from "vs/base/common/uri";
|
||||
import "./firefox";
|
||||
|
||||
const load = (): Promise<void> => {
|
||||
return new Promise((resolve, reject): void => {
|
||||
setUriFactory({
|
||||
// TODO: not sure why this is an error.
|
||||
// tslint:disable-next-line no-any
|
||||
create: <URI>(uri: IURI): URI => URI.from(uri) as any,
|
||||
file: (path: string): IURI => URI.file(path),
|
||||
parse: (raw: string): IURI => URI.parse(raw),
|
||||
});
|
||||
|
||||
const client = new Client({
|
||||
mkDirs: [
|
||||
"~/vscode/extensions",
|
||||
"~/.config/User",
|
||||
],
|
||||
});
|
||||
|
||||
const importTime = time(1500);
|
||||
import(/* webpackPrefetch: true */ "./workbench").then((module) => {
|
||||
logger.info("Loaded workbench bundle", field("duration", importTime));
|
||||
const initTime = time(1500);
|
||||
|
||||
return module.initialize(client).then(() => {
|
||||
logger.info("Initialized workbench", field("duration", initTime));
|
||||
resolve();
|
||||
});
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export { load };
|
||||
export * from "./client";
|
||||
|
129
packages/vscode/src/protocol.ts
Normal file
129
packages/vscode/src/protocol.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { ChildProcess } from "child_process";
|
||||
import { EventEmitter } from "events";
|
||||
import { Protocol as VSProtocol } from "vs/base/parts/ipc/node/ipc.net";
|
||||
import { LogLevel } from "vs/platform/log/common/log";
|
||||
|
||||
export interface ISharedProcessInitData {
|
||||
socketPath: string;
|
||||
serviceUrl: string;
|
||||
logsDir: string;
|
||||
windowId: number;
|
||||
logLevel: LogLevel;
|
||||
}
|
||||
|
||||
export interface IStdio {
|
||||
onMessage: (cb: (data: string | Buffer) => void) => void;
|
||||
sendMessage: (data: string | Buffer) => void;
|
||||
onExit?: (cb: () => void) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of net.Socket that uses stdio streams.
|
||||
*/
|
||||
class Socket {
|
||||
|
||||
private readonly emitter: EventEmitter;
|
||||
|
||||
public constructor(private readonly stdio: IStdio, ignoreFirst: boolean = false) {
|
||||
this.emitter = new EventEmitter();
|
||||
|
||||
let first = true;
|
||||
stdio.onMessage((data) => {
|
||||
if (ignoreFirst && first) {
|
||||
first = false;
|
||||
|
||||
return;
|
||||
}
|
||||
this.emitter.emit("data", Buffer.from(data.toString()));
|
||||
});
|
||||
if (stdio.onExit) {
|
||||
stdio.onExit(() => {
|
||||
this.emitter.emit("close");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public removeListener(event: string, listener: () => void): void {
|
||||
this.emitter.removeListener(event, listener);
|
||||
}
|
||||
|
||||
public once(event: string, listener: () => void): void {
|
||||
this.emitter.once(event, listener);
|
||||
}
|
||||
|
||||
public on(event: string, listener: () => void): void {
|
||||
this.emitter.on(event, listener);
|
||||
}
|
||||
|
||||
public end(): void {
|
||||
// TODO: figure it out
|
||||
}
|
||||
|
||||
public get destroyed(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public write(data: string | Buffer): void {
|
||||
this.stdio.sendMessage(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A protocol around a process, stream, or worker.
|
||||
*/
|
||||
export class Protocol extends VSProtocol {
|
||||
|
||||
public static fromProcess(childProcess: ChildProcess): Protocol {
|
||||
return Protocol.fromStdio({
|
||||
onMessage: (cb): void => {
|
||||
childProcess.stdout.on("data", (data: string | Buffer) => {
|
||||
cb(data);
|
||||
});
|
||||
},
|
||||
sendMessage: (data): void => {
|
||||
childProcess.stdin.write(data);
|
||||
},
|
||||
onExit: (cb): void => {
|
||||
childProcess.on("exit", cb);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public static fromStream(
|
||||
inStream: { on: (event: "data", cb: (b: string | Buffer) => void) => void },
|
||||
outStream: { write: (b: string | Buffer) => void },
|
||||
): Protocol {
|
||||
return Protocol.fromStdio({
|
||||
onMessage: (cb): void => {
|
||||
inStream.on("data", (data) => {
|
||||
cb(data);
|
||||
});
|
||||
},
|
||||
sendMessage: (data): void => {
|
||||
outStream.write(data);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public static fromWorker(worker: {
|
||||
onmessage: (event: MessageEvent) => void;
|
||||
postMessage: (data: string, origin?: string | string[]) => void;
|
||||
}, ignoreFirst: boolean = false): Protocol {
|
||||
return Protocol.fromStdio({
|
||||
onMessage: (cb): void => {
|
||||
worker.onmessage = (event: MessageEvent): void => {
|
||||
cb(event.data);
|
||||
};
|
||||
},
|
||||
sendMessage: (data): void => {
|
||||
worker.postMessage(data.toString());
|
||||
},
|
||||
}, ignoreFirst);
|
||||
}
|
||||
|
||||
public static fromStdio(stdio: IStdio, ignoreFirst?: boolean): Protocol {
|
||||
return new Protocol(new Socket(stdio, ignoreFirst));
|
||||
}
|
||||
|
||||
}
|
151
packages/vscode/src/startup.ts
Normal file
151
packages/vscode/src/startup.ts
Normal file
@ -0,0 +1,151 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import "vs/loader";
|
||||
|
||||
// Base
|
||||
import "vs/base/common/strings";
|
||||
import "vs/base/common/errors";
|
||||
|
||||
// Configuration
|
||||
import "vs/workbench/services/configuration/common/configurationExtensionPoint";
|
||||
|
||||
// Editor
|
||||
import "vs/editor/editor.all";
|
||||
|
||||
// Platform
|
||||
import "vs/platform/widget/browser/contextScopedHistoryWidget";
|
||||
import "vs/platform/label/electron-browser/label.contribution";
|
||||
|
||||
// Menus/Actions
|
||||
import "vs/workbench/services/actions/electron-browser/menusExtensionPoint";
|
||||
|
||||
// Views
|
||||
import "vs/workbench/api/browser/viewsContainersExtensionPoint";
|
||||
import "vs/workbench/api/browser/viewsExtensionPoint";
|
||||
|
||||
// Localizations
|
||||
import "vs/workbench/parts/localizations/electron-browser/localizations.contribution";
|
||||
|
||||
// Workbench
|
||||
import "vs/workbench/browser/actions/toggleActivityBarVisibility";
|
||||
import "vs/workbench/browser/actions/toggleStatusbarVisibility";
|
||||
import "vs/workbench/browser/actions/toggleSidebarVisibility";
|
||||
import "vs/workbench/browser/actions/toggleSidebarPosition";
|
||||
import "vs/workbench/browser/actions/toggleEditorLayout";
|
||||
import "vs/workbench/browser/actions/toggleZenMode";
|
||||
import "vs/workbench/browser/actions/toggleCenteredLayout";
|
||||
import "vs/workbench/browser/actions/toggleTabsVisibility";
|
||||
import "vs/workbench/parts/preferences/electron-browser/preferences.contribution";
|
||||
import "vs/workbench/parts/preferences/browser/keybindingsEditorContribution";
|
||||
import "vs/workbench/parts/logs/electron-browser/logs.contribution";
|
||||
|
||||
import "vs/workbench/browser/parts/quickopen/quickopen.contribution";
|
||||
import "vs/workbench/parts/quickopen/browser/quickopen.contribution";
|
||||
import "vs/workbench/browser/parts/editor/editorPicker";
|
||||
import "vs/workbench/browser/parts/quickinput/quickInput.contribution";
|
||||
|
||||
import "vs/workbench/parts/files/electron-browser/explorerViewlet";
|
||||
import "vs/workbench/parts/files/electron-browser/fileActions.contribution";
|
||||
import "vs/workbench/parts/files/electron-browser/files.contribution";
|
||||
|
||||
import "vs/workbench/parts/backup/common/backup.contribution";
|
||||
|
||||
import "vs/workbench/parts/stats/node/stats.contribution";
|
||||
|
||||
import "vs/workbench/parts/splash/electron-browser/partsSplash.contribution";
|
||||
|
||||
import "vs/workbench/parts/search/electron-browser/search.contribution";
|
||||
import "vs/workbench/parts/search/browser/searchView";
|
||||
import "vs/workbench/parts/search/browser/openAnythingHandler";
|
||||
|
||||
import "vs/workbench/parts/scm/electron-browser/scm.contribution";
|
||||
import "vs/workbench/parts/scm/electron-browser/scmViewlet";
|
||||
|
||||
import "vs/workbench/parts/debug/electron-browser/debug.contribution";
|
||||
// import "vs/workbench/parts/debug/browser/debugQuickOpen";
|
||||
// import "vs/workbench/parts/debug/electron-browser/repl";
|
||||
// import "vs/workbench/parts/debug/browser/debugViewlet";
|
||||
|
||||
import "vs/workbench/parts/markers/electron-browser/markers.contribution";
|
||||
import "vs/workbench/parts/comments/electron-browser/comments.contribution";
|
||||
|
||||
import "vs/workbench/parts/html/electron-browser/html.contribution";
|
||||
|
||||
import "vs/workbench/parts/url/electron-browser/url.contribution";
|
||||
import "vs/workbench/parts/webview/electron-browser/webview.contribution";
|
||||
|
||||
import "vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution";
|
||||
|
||||
import "vs/workbench/parts/extensions/electron-browser/extensions.contribution";
|
||||
import "vs/workbench/parts/extensions/browser/extensionsQuickOpen";
|
||||
import "vs/workbench/parts/extensions/electron-browser/extensionsViewlet";
|
||||
|
||||
import "vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution";
|
||||
|
||||
import "vs/workbench/parts/output/electron-browser/output.contribution";
|
||||
import "vs/workbench/parts/output/browser/outputPanel";
|
||||
|
||||
import "vs/workbench/parts/terminal/electron-browser/terminal.contribution";
|
||||
import "vs/workbench/parts/terminal/browser/terminalQuickOpen";
|
||||
import "vs/workbench/parts/terminal/electron-browser/terminalPanel";
|
||||
|
||||
import "vs/workbench/electron-browser/workbench";
|
||||
|
||||
// import "vs/workbench/parts/relauncher/electron-browser/relauncher.contribution";
|
||||
|
||||
import "vs/workbench/parts/tasks/electron-browser/task.contribution";
|
||||
|
||||
import "vs/workbench/parts/emmet/browser/emmet.browser.contribution";
|
||||
import "vs/workbench/parts/emmet/electron-browser/emmet.contribution";
|
||||
|
||||
import "vs/workbench/parts/codeEditor/codeEditor.contribution";
|
||||
|
||||
import "vs/workbench/parts/execution/electron-browser/execution.contribution";
|
||||
|
||||
import "vs/workbench/parts/snippets/electron-browser/snippets.contribution";
|
||||
import "vs/workbench/parts/snippets/electron-browser/snippetsService";
|
||||
import "vs/workbench/parts/snippets/electron-browser/insertSnippet";
|
||||
import "vs/workbench/parts/snippets/electron-browser/configureSnippets";
|
||||
import "vs/workbench/parts/snippets/electron-browser/tabCompletion";
|
||||
|
||||
import "vs/workbench/parts/themes/electron-browser/themes.contribution";
|
||||
|
||||
// import "vs/workbench/parts/feedback/electron-browser/feedback.contribution";
|
||||
|
||||
import "vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution";
|
||||
|
||||
import "vs/workbench/parts/update/electron-browser/update.contribution";
|
||||
|
||||
// import "vs/workbench/parts/surveys/electron-browser/nps.contribution";
|
||||
// import "vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution";
|
||||
|
||||
import "vs/workbench/parts/performance/electron-browser/performance.contribution";
|
||||
|
||||
// import "vs/workbench/parts/cli/electron-browser/cli.contribution";
|
||||
|
||||
import "vs/workbench/api/electron-browser/extensionHost.contribution";
|
||||
|
||||
import "vs/workbench/electron-browser/main.contribution";
|
||||
import { startup } from "vs/workbench/electron-browser/main";
|
||||
|
||||
// import "vs/workbench/parts/themes/test/electron-browser/themes.test.contribution";
|
||||
|
||||
import "vs/workbench/parts/watermark/electron-browser/watermark";
|
||||
|
||||
import "vs/workbench/parts/welcome/overlay/browser/welcomeOverlay";
|
||||
|
||||
import "vs/workbench/parts/outline/electron-browser/outline.contribution";
|
||||
|
||||
import "vs/workbench/services/bulkEdit/electron-browser/bulkEditService";
|
||||
|
||||
import "vs/workbench/parts/experiments/electron-browser/experiments.contribution";
|
||||
|
||||
import { URI } from "vs/base/common/uri";
|
||||
|
||||
export {
|
||||
URI,
|
||||
startup,
|
||||
};
|
@ -1,59 +0,0 @@
|
||||
import { IStorageService, StorageScope } from "vs/platform/storage/common/storage";
|
||||
|
||||
export class StorageService implements IStorageService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private _globalObject: { [key: string]: any };
|
||||
private _workspaceObject: { [ key: string]: any };
|
||||
|
||||
public constructor(globalState: object, workspaceState: object) {
|
||||
this._globalObject = globalState;
|
||||
this._workspaceObject = workspaceState;
|
||||
}
|
||||
|
||||
public get globalObject() {
|
||||
return this._globalObject;
|
||||
}
|
||||
|
||||
public get workspaceObject() {
|
||||
return this._workspaceObject;
|
||||
}
|
||||
|
||||
public store(key: string, value: any, scope?: StorageScope): void {
|
||||
this.getObject(scope)[key] = value;
|
||||
}
|
||||
|
||||
public remove(key: string, scope?: StorageScope): void {
|
||||
delete this.getObject(scope)[key];
|
||||
}
|
||||
|
||||
public get(key: string, scope?: StorageScope, defaultValue?: string): string {
|
||||
return this.getObject(scope)[key] || defaultValue;
|
||||
}
|
||||
|
||||
public getInteger(key: string, scope?: StorageScope, defaultValue?: number): number {
|
||||
return parseInt(this.get(key, scope), 10) || defaultValue;
|
||||
}
|
||||
|
||||
public getBoolean(key: string, scope?: StorageScope, defaultValue?: boolean): boolean {
|
||||
const v = this.get(key, scope);
|
||||
if (typeof v !== "undefined") {
|
||||
return v === "true";
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private getObject(scope = StorageScope.GLOBAL): { [key: string]: any } {
|
||||
switch (scope) {
|
||||
case StorageScope.GLOBAL:
|
||||
return this._globalObject;
|
||||
case StorageScope.WORKSPACE:
|
||||
return this._workspaceObject;
|
||||
default:
|
||||
throw new Error("unsupported storage scope");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,224 +0,0 @@
|
||||
import { Client } from "@coder/ide";
|
||||
import { Emitter } from "@coder/events";
|
||||
import { logger } from "@coder/logger";
|
||||
|
||||
import { Protocol } from "vs/base/parts/ipc/node/ipc.net";
|
||||
import { IModelService } from "vs/editor/common/services/modelService";
|
||||
import { ICodeEditorService } from "vs/editor/browser/services/codeEditorService";
|
||||
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
|
||||
import { Workbench } from "vs/workbench/electron-browser/workbench";
|
||||
import { IDecorationsService } from "vs/workbench/services/decorations/browser/decorations";
|
||||
import { LogLevel } from "vs/platform/log/common/log";
|
||||
import { INotificationService, Severity } from "vs/platform/notification/common/notification";
|
||||
import { toLocalISOString } from "vs/base/common/date";
|
||||
import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
|
||||
|
||||
import { StorageService } from "./storageService";
|
||||
|
||||
let protoResolve: (protocol: Protocol) => void;
|
||||
export const protocolPromise = new Promise<Protocol>((res) => {
|
||||
protoResolve = res;
|
||||
});
|
||||
let storageResolve: (storageService: StorageService) => void;
|
||||
export const getStorageService = new Promise<StorageService>((res) => {
|
||||
storageResolve = res;
|
||||
});
|
||||
export let systemExtensionsLocation: string;
|
||||
export let forkedBinLocation: string;
|
||||
|
||||
const hasNativeClipboard = typeof navigator !== "undefined" && typeof (navigator as any).clipboard !== "undefined" && typeof (navigator as any).clipboard.readText !== "undefined";
|
||||
let isEnabled: boolean = false;
|
||||
const clipboardEnabledEmitter = new Emitter<boolean>();
|
||||
export const nativeClipboard: {
|
||||
readonly contextKey: RawContextKey<boolean>;
|
||||
readonly instance: {
|
||||
readText(): Promise<string>;
|
||||
writeText(value: string): Promise<void>;
|
||||
};
|
||||
readonly onChange: Event<boolean>;
|
||||
readonly isEnabled: boolean;
|
||||
} = {
|
||||
contextKey: new RawContextKey('nativeClipboard', hasNativeClipboard),
|
||||
instance: hasNativeClipboard ? (navigator as any).clipboard : undefined,
|
||||
get onChange(): Event<boolean> {
|
||||
return clipboardEnabledEmitter.event;
|
||||
},
|
||||
get isEnabled(): boolean {
|
||||
return isEnabled;
|
||||
}
|
||||
};
|
||||
|
||||
let workbench: Workbench;
|
||||
|
||||
function getModelService(): IModelService {
|
||||
return workbench.workbenchParams.serviceCollection.get<IModelService>(IModelService) as IModelService;
|
||||
}
|
||||
|
||||
function getCodeEditorService(): ICodeEditorService {
|
||||
return workbench.workbenchParams.serviceCollection.get(ICodeEditorService) as ICodeEditorService;
|
||||
}
|
||||
|
||||
function getNotificationService(): INotificationService {
|
||||
return workbench.workbenchParams.serviceCollection.get(INotificationService) as INotificationService;
|
||||
}
|
||||
|
||||
export const initialize = async (client: Client): Promise<void> => {
|
||||
window.addEventListener("contextmenu", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// TODO: Fetch configuration.
|
||||
const storageServicePromise = client.wrapTask("Set configurations", 5, async (state) => {
|
||||
const storageService = new StorageService(state.global, state.workspace);
|
||||
storageResolve(storageService);
|
||||
|
||||
return storageService;
|
||||
}, client.state);
|
||||
|
||||
// Set up window ID for logging. We'll also use a static logging directory
|
||||
// otherwise we'd have to get the log directory back from the currently
|
||||
// running shared process. This allows us to not wait for that. Each window
|
||||
// will still have its own logging within that directory.
|
||||
const windowId = parseInt(toLocalISOString(new Date()).replace(/[-:.TZ]/g, ""), 10);
|
||||
process.env.VSCODE_LOGS = "/tmp/vscode-logs";
|
||||
|
||||
client.wrapTask("Start shared process", 5, async (api, wush, mountPath) => {
|
||||
const session = wush.execute({
|
||||
command: "bash -c 'VSCODE_ALLOW_IO=true"
|
||||
+ " AMD_ENTRYPOINT=vs/code/electron-browser/sharedProcess/sharedProcessClient"
|
||||
+ ` nice -n -17 ${nodePath} ${bootstrapForkLocation} --client'`,
|
||||
});
|
||||
|
||||
const sharedProcessLogger = logger.named("shr proc");
|
||||
session.onStderr((data) => {
|
||||
sharedProcessLogger.error("stderr: " + data);
|
||||
});
|
||||
|
||||
session.onDone(() => {
|
||||
workbenchPromise.then(() => {
|
||||
getNotificationService().prompt(
|
||||
Severity.Error,
|
||||
"Shared process terminated unexpectedly.",
|
||||
[{
|
||||
label: "Reload IDE",
|
||||
run: (): void => {
|
||||
window.location.reload();
|
||||
},
|
||||
}],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
const protocol = Protocol.fromStdio({
|
||||
onMessage: (cb) => {
|
||||
session.onStdout((data) => {
|
||||
cb(Buffer.from(data as any));
|
||||
}, true);
|
||||
},
|
||||
sendMessage: (data) => {
|
||||
session.sendStdin(data);
|
||||
},
|
||||
});
|
||||
|
||||
await new Promise((resolve) => {
|
||||
const listener = protocol.onMessage((message) => {
|
||||
const messageStr = message.toString();
|
||||
sharedProcessLogger.debug(messageStr);
|
||||
switch (messageStr) {
|
||||
case "handshake:hello":
|
||||
protocol.send(Buffer.from(JSON.stringify({
|
||||
// Using the mount path so if we get a new mount, it spins up a new shared
|
||||
// process since it or the builtin extensions could contain changes.
|
||||
sharedIPCHandle: `/tmp/vscode-shared${mountPath.replace(/\//g, "-")}.sock`,
|
||||
serviceUrl: api.environment.appURL("extensions-api"),
|
||||
logsDir: process.env.VSCODE_LOGS,
|
||||
nodePath,
|
||||
bootstrapForkLocation,
|
||||
args: {},
|
||||
windowId,
|
||||
logLevel: LogLevel.Info,
|
||||
} as ISharedProcessInitData)));
|
||||
break;
|
||||
case "handshake:ready":
|
||||
listener.dispose();
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
protoResolve(protocol);
|
||||
}, client.api, client.wush, mountPromise);
|
||||
|
||||
const { startup, URI } = require('vs/workbench/workbench.main');
|
||||
|
||||
require("os").homedir = () => {
|
||||
// TODO: update this as well as folderURL
|
||||
return "/root";
|
||||
};
|
||||
require("path").posix = require("path");
|
||||
|
||||
registerContextMenuListener();
|
||||
|
||||
const workbenchPromise = client.wrapTask("Start workbench", 1000, async (workspace, mountPath) => {
|
||||
const workbenchShellPromise = startup({
|
||||
machineId: "1",
|
||||
windowId,
|
||||
logLevel: LogLevel.Info,
|
||||
mainPid: 1,
|
||||
appRoot: mountPath,
|
||||
execPath: "/tmp",
|
||||
userEnv: {},
|
||||
nodeCachedDataDir: "/tmp",
|
||||
perfEntries: [],
|
||||
_: undefined,
|
||||
folderUri: URI.file(workspace.mountUri.path),
|
||||
});
|
||||
|
||||
const workbenchShell = await workbenchShellPromise;
|
||||
workbench = workbenchShell.workbench;
|
||||
|
||||
const contextKeys = workbench.workbenchParams.serviceCollection.get(IContextKeyService) as IContextKeyService;
|
||||
const bounded = nativeClipboard.contextKey.bindTo(contextKeys);
|
||||
|
||||
const navigatorClip = (navigator as any).clipboard;
|
||||
const navigatorPerms = (navigator as any).permissions;
|
||||
if (navigatorClip && navigatorPerms) {
|
||||
navigatorPerms.query({
|
||||
name: "clipboard-read",
|
||||
}).then((permissionStatus) => {
|
||||
const updateStatus = () => {
|
||||
if (permissionStatus.state === "denied") {
|
||||
isEnabled = false;
|
||||
clipboardEnabledEmitter.emit(false);
|
||||
bounded.set(false);
|
||||
} else {
|
||||
isEnabled = true;
|
||||
clipboardEnabledEmitter.emit(true);
|
||||
bounded.set(true);
|
||||
}
|
||||
};
|
||||
|
||||
updateStatus();
|
||||
|
||||
permissionStatus.onchange = () => {
|
||||
updateStatus();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const decorations = workbench.workbenchParams.serviceCollection.get(IDecorationsService) as IDecorationsService;
|
||||
await registerCollaboratorDecorations(client, decorations);
|
||||
|
||||
return workbenchShell;
|
||||
}, client.mkDirs);
|
||||
|
||||
client.wrapTask("Set up saving state", 5, async () => {
|
||||
if (!navigator.sendBeacon) {
|
||||
throw new Error("cannot save state");
|
||||
}
|
||||
// TODO: save storageSevice.globalObject and storageService.workspaceObject
|
||||
});
|
||||
|
||||
await workbenchPromise;
|
||||
};
|
123
packages/vscode/webpack.config.bootstrap.js
Normal file
123
packages/vscode/webpack.config.bootstrap.js
Normal file
@ -0,0 +1,123 @@
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
|
||||
const root = path.resolve(__dirname, "..", "..");
|
||||
const fills = path.join(root, "packages", "ide", "src", "fill");
|
||||
const vscodeFills = path.join(root, "packages", "vscode", "src", "fill");
|
||||
|
||||
const merge = require("webpack-merge");
|
||||
|
||||
module.exports = (env) => {
|
||||
const afterCompileCommand = env && env.afterCompileCommand;
|
||||
return merge(require(path.join(root, "scripts", "webpack.general.config.js"))({
|
||||
typescriptCompilerOptions: {
|
||||
target: "es5",
|
||||
},
|
||||
}), {
|
||||
entry: path.join(root, "lib/vscode/src/bootstrap-fork.js"),
|
||||
mode: "development",
|
||||
target: "node",
|
||||
externals: ["node-pty", "spdlog"],
|
||||
output: {
|
||||
chunkFilename: "[name].bundle.js",
|
||||
path: path.resolve(__dirname, "./bin"),
|
||||
publicPath: "/",
|
||||
filename: "bootstrap-fork.js",
|
||||
libraryTarget: "commonjs",
|
||||
globalObject: "this",
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
loader: "string-replace-loader",
|
||||
test: /\.(js|ts)$/,
|
||||
options: {
|
||||
multiple: [
|
||||
{
|
||||
search: "require\\.toUrl\\(",
|
||||
replace: "requireToUrl(",
|
||||
flags: "g",
|
||||
},
|
||||
{
|
||||
search: "require\\.__\\$__nodeRequire",
|
||||
replace: "require",
|
||||
flags: "g",
|
||||
},
|
||||
],
|
||||
},
|
||||
}, {
|
||||
loader: "string-replace-loader",
|
||||
test: /vs\/loader\.js/,
|
||||
options: {
|
||||
multiple: [
|
||||
{
|
||||
search: "var recorder = moduleManager.getRecorder\\(\\);",
|
||||
replace: `
|
||||
var recorder = moduleManager.getRecorder();
|
||||
const context = require.context("../", true, /.*/);
|
||||
if (scriptSrc.indexOf("file:///") !== -1) {
|
||||
const vsSrc = scriptSrc.split("file:///")[1].split(".js")[0];
|
||||
if (vsSrc && vsSrc.startsWith("vs/")) {
|
||||
scriptSrc = \`node|./\${vsSrc}\`;
|
||||
}
|
||||
}
|
||||
`,
|
||||
flags: "g",
|
||||
},
|
||||
{
|
||||
search: "nodeRequire\\(",
|
||||
replace: "require(",
|
||||
flags: "g",
|
||||
},
|
||||
{
|
||||
search: "moduleExports_1 = require\\(",
|
||||
replace: "moduleExports_1 = context(",
|
||||
flags: "g",
|
||||
},
|
||||
],
|
||||
},
|
||||
}, {
|
||||
test: /\.wasm$/,
|
||||
type: "javascript/auto",
|
||||
}, {
|
||||
// Ignore a bunch of file types we don't have loaders for. Also ignore
|
||||
// test directories, some files with invalid JSON, and files we don't
|
||||
// actually require but throw warnings or errors. This all seems to be a
|
||||
// case of dynamic loading including things we won't require.
|
||||
// This also results in the bundle being significantly smaller which
|
||||
// makes uglify much faster.
|
||||
test: /(\/vs\/code\/electron-main\/)|(\/test\/)|(OSSREADME\.json$)|(\.(test\.ts|test\.js|d\.ts|qwoff|node|html|txt|exe|wuff|md|sh|scpt|less)$)/,
|
||||
use: ["ignore-loader"]
|
||||
}],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"gc-signals": path.join(fills, "empty.ts"),
|
||||
"native-keymap": path.join(fills, "native-keymap.ts"),
|
||||
"windows-process-tree": path.resolve(fills, "empty.ts"),
|
||||
|
||||
"electron": path.join(vscodeFills, "stdioElectron.ts"),
|
||||
"vs/platform/node/product": path.resolve(vscodeFills, "product.ts"),
|
||||
"vs/platform/node/package": path.resolve(vscodeFills, "package.ts"),
|
||||
"vs/base/node/paths": path.resolve(vscodeFills, "paths.ts"),
|
||||
"vs/base/common/amd": path.resolve(vscodeFills, "amd.ts"),
|
||||
"vs": path.resolve(root, "lib/vscode/src/vs"),
|
||||
},
|
||||
},
|
||||
resolveLoader: {
|
||||
alias: {
|
||||
"vs/css": path.resolve(vscodeFills, "css.js"),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProgressPlugin((percentage, msg) => {
|
||||
if (percentage === 1) {
|
||||
if (afterCompileCommand) {
|
||||
require("child_process").execSync(afterCompileCommand, {
|
||||
stdio: "inherit"
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
@ -2,3 +2,122 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
ajv-keywords@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
|
||||
integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=
|
||||
|
||||
ajv@^6.1.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96"
|
||||
integrity sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==
|
||||
dependencies:
|
||||
fast-deep-equal "^2.0.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||
|
||||
bindings@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.1.tgz#21fc7c6d67c18516ec5aaa2815b145ff77b26ea5"
|
||||
integrity sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew==
|
||||
|
||||
emojis-list@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
|
||||
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
|
||||
|
||||
fast-deep-equal@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
|
||||
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
|
||||
integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json5@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
||||
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
loader-utils@^1.1.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
|
||||
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^2.0.0"
|
||||
json5 "^1.0.1"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||
|
||||
mkdirp@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
nan@^2.8.0:
|
||||
version "2.12.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
|
||||
integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
schema-utils@^0.4.5:
|
||||
version "0.4.7"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
|
||||
integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==
|
||||
dependencies:
|
||||
ajv "^6.1.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
spdlog@^0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.7.2.tgz#9298753d7694b9ee9bbfd7e01ea1e4c6ace1e64d"
|
||||
integrity sha512-rHfWCaWMD4NindDnql6rc6kn7Bs8JR92jhiUpCl3D6v+jYcQ6GozMLig0RliOOR8st5mU+IHLZnr15fBys5x/Q==
|
||||
dependencies:
|
||||
bindings "^1.3.0"
|
||||
mkdirp "^0.5.1"
|
||||
nan "^2.8.0"
|
||||
|
||||
string-replace-loader@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-2.1.1.tgz#b72e7b57b6ef04efe615aff0ad989b5c14ca63d1"
|
||||
integrity sha512-0Nvw1LDclF45AFNuYPcD2Jvkv0mwb/dQSnJZMvhqGrT+zzmrpG3OJFD600qfQfNUd5aqfp7fCm2mQMfF7zLbyQ==
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
schema-utils "^0.4.5"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
@ -1,47 +1,2 @@
|
||||
import { logger, field, time } from "@coder/logger";
|
||||
import { load } from "@coder/vscode";
|
||||
import "./index.scss";
|
||||
|
||||
const loadTime = time(2500);
|
||||
logger.info("Loading IDE");
|
||||
|
||||
const overlay = document.getElementById("overlay");
|
||||
const logo = document.getElementById("logo");
|
||||
const msgElement = overlay
|
||||
? overlay.querySelector(".message") as HTMLElement
|
||||
: undefined;
|
||||
|
||||
if (overlay && logo) {
|
||||
overlay.addEventListener("mousemove", (event) => {
|
||||
const xPos = ((event.clientX - logo.offsetLeft) / 24).toFixed(2);
|
||||
const yPos = ((logo.offsetTop - event.clientY) / 24).toFixed(2);
|
||||
|
||||
logo.style.transform = `perspective(200px) rotateX(${yPos}deg) rotateY(${xPos}deg)`;
|
||||
});
|
||||
}
|
||||
|
||||
load().then(() => {
|
||||
if (overlay) {
|
||||
overlay.style.opacity = "0";
|
||||
overlay.addEventListener("transitionend", () => {
|
||||
overlay.remove();
|
||||
});
|
||||
}
|
||||
}).catch((error: Error) => {
|
||||
logger.error(error.message);
|
||||
if (overlay) {
|
||||
overlay.classList.add("error");
|
||||
}
|
||||
if (msgElement) {
|
||||
const button = document.createElement("div");
|
||||
button.className = "reload-button";
|
||||
button.innerText = "Reload";
|
||||
button.addEventListener("click", () => {
|
||||
location.reload();
|
||||
});
|
||||
msgElement.innerText = `Failed to load: ${error.message}.`;
|
||||
msgElement.parentElement!.appendChild(button);
|
||||
}
|
||||
}).finally(() => {
|
||||
logger.info("Load completed", field("duration", loadTime));
|
||||
});
|
||||
import "@coder/vscode";
|
||||
|
8
packages/web/tsconfig.json
Normal file
8
packages/web/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "esnext"],
|
||||
"importHelpers": true
|
||||
}
|
||||
}
|
103
packages/web/webpack.common.config.js
Normal file
103
packages/web/webpack.common.config.js
Normal file
@ -0,0 +1,103 @@
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
|
||||
const PreloadWebpackPlugin = require("preload-webpack-plugin");
|
||||
const HappyPack = require("happypack");
|
||||
const root = path.resolve(__dirname, "..", "..");
|
||||
const fills = path.join(root, "packages", "ide", "src", "fill");
|
||||
const vsFills = path.join(root, "packages", "vscode", "src", "fill");
|
||||
|
||||
const merge = require("webpack-merge");
|
||||
|
||||
module.exports = merge({
|
||||
devtool: "source-map",
|
||||
entry: "./packages/web/src/index.ts",
|
||||
output: {
|
||||
chunkFilename: "[name]-[hash:6].bundle.js",
|
||||
path: path.join(root, "dist"),
|
||||
filename: "[hash:6].bundle.js",
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
loader: "string-replace-loader",
|
||||
test: /\.(j|t)s/,
|
||||
options: {
|
||||
multiple: [{
|
||||
search: "require\\.toUrl\\(",
|
||||
replace: "requireToUrl(",
|
||||
flags: "g",
|
||||
}, {
|
||||
search: "require\\.__\\$__nodeRequire",
|
||||
replace: "require",
|
||||
flags: "g",
|
||||
}, {
|
||||
search: "\\.attributes\\[([^\\]]+)\\] = ([^;]+)",
|
||||
replace: ".setAttribute($1, $2)",
|
||||
flags: "g",
|
||||
}],
|
||||
},
|
||||
}],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"gc-signals": path.join(fills, "empty.ts"),
|
||||
"selenium-webdriver": path.join(fills, "empty.ts"),
|
||||
"vscode": path.join(fills, "empty.ts"),
|
||||
"vscode-fsevents": path.join(fills, "empty.ts"),
|
||||
"vsda": path.join(fills, "empty.ts"),
|
||||
"windows-foreground-love": path.join(fills, "empty.ts"),
|
||||
"windows-mutex": path.join(fills, "empty.ts"),
|
||||
"windows-process-tree": path.join(fills, "empty.ts"),
|
||||
"vscode-sqlite3": path.join(fills, "empty.ts"),
|
||||
"tls": path.join(fills, "empty.ts"),
|
||||
"native-is-elevated": path.join(fills, "empty.ts"),
|
||||
|
||||
"crypto": "crypto-browserify",
|
||||
"http": "http-browserify",
|
||||
|
||||
"child_process": path.join(fills, "child_process.ts"),
|
||||
"os": path.join(fills, "os.ts"),
|
||||
"fs": path.join(fills, "fs.ts"),
|
||||
"net": path.join(fills, "net.ts"),
|
||||
"util": path.join(fills, "util.ts"),
|
||||
"electron": path.join(fills, "electron.ts"),
|
||||
|
||||
"native-keymap": path.join(vsFills, "native-keymap.ts"),
|
||||
"node-pty": path.join(vsFills, "node-pty.ts"),
|
||||
"graceful-fs": path.join(vsFills, "graceful-fs.ts"),
|
||||
"spdlog": path.join(vsFills, "spdlog.ts"),
|
||||
|
||||
"vs/base/node/paths": path.join(vsFills, "paths.ts"),
|
||||
"vs/base/common/amd": path.join(vsFills, "amd.ts"),
|
||||
"vs/platform/node/product": path.join(vsFills, "product.ts"),
|
||||
"vs/platform/node/package": path.join(vsFills, "package.ts"),
|
||||
"vs": path.join(root, "lib", "vscode", "src", "vs"),
|
||||
},
|
||||
},
|
||||
resolveLoader: {
|
||||
alias: {
|
||||
"vs/css": path.join(vsFills, "css.js"),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: "packages/web/src/index.html",
|
||||
}),
|
||||
new PreloadWebpackPlugin({
|
||||
rel: "preload",
|
||||
as: "script",
|
||||
}),
|
||||
new HappyPack({
|
||||
id: "ts",
|
||||
threads: 2,
|
||||
loaders: [{
|
||||
path: "ts-loader",
|
||||
query: {
|
||||
happyPackMode: true,
|
||||
configFile: path.join(__dirname, "tsconfig.json"),
|
||||
},
|
||||
}],
|
||||
}),
|
||||
],
|
||||
target: "web",
|
||||
}, require(path.join(root, "scripts", "webpack.general.config.js"))());
|
13
packages/web/webpack.dev.config.js
Normal file
13
packages/web/webpack.dev.config.js
Normal file
@ -0,0 +1,13 @@
|
||||
const webpack = require("webpack");
|
||||
const merge = require("webpack-merge");
|
||||
|
||||
module.exports = merge(require("./webpack.common.config.js"), {
|
||||
devtool: "cheap-module-eval-source-map",
|
||||
entry: [
|
||||
"webpack-hot-middleware/client?reload=true&quiet=true",
|
||||
"./packages/web/src/index.ts"
|
||||
],
|
||||
plugins: [
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
]
|
||||
});
|
@ -1,178 +0,0 @@
|
||||
diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css
|
||||
index 651843fcc9..aa31b52cb9 100644
|
||||
--- a/src/vs/base/browser/ui/iconLabel/iconlabel.css
|
||||
+++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css
|
||||
@@ -32,6 +32,7 @@
|
||||
.monaco-icon-label > .monaco-icon-label-description-container {
|
||||
overflow: hidden; /* this causes the label/description to shrink first if decorations are enabled */
|
||||
text-overflow: ellipsis;
|
||||
+ margin-right: auto;
|
||||
}
|
||||
|
||||
.monaco-icon-label > .monaco-icon-label-description-container > .label-name {
|
||||
@@ -39,6 +40,12 @@
|
||||
white-space: pre; /* enable to show labels that include multiple whitespaces */
|
||||
}
|
||||
|
||||
+.monaco-icon-label > .decorations-wrapper {
|
||||
+ display: flex;
|
||||
+ flex-direction: row;
|
||||
+ padding-right: 12px;
|
||||
+}
|
||||
+
|
||||
.monaco-icon-label > .monaco-icon-label-description-container > .label-description {
|
||||
opacity: .7;
|
||||
margin-left: 0.5em;
|
||||
@@ -56,6 +63,5 @@
|
||||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
padding: 0 12px 0 5px;
|
||||
- margin-left: auto;
|
||||
text-align: center;
|
||||
}
|
||||
diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
|
||||
index 5a92b2e1f5..1d3c735e75 100644
|
||||
--- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
|
||||
+++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
|
||||
@@ -4,25 +4,130 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench > .part.activitybar {
|
||||
- width: 50px;
|
||||
+ width: 50px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .activitybar > .content {
|
||||
- height: 100%;
|
||||
- display: flex;
|
||||
- flex-direction: column;
|
||||
- justify-content: space-between;
|
||||
+ height: 100%;
|
||||
+ display: flex;
|
||||
+ flex-direction: column;
|
||||
+ justify-content: space-between;
|
||||
}
|
||||
|
||||
.monaco-workbench > .activitybar > .content .monaco-action-bar {
|
||||
- text-align: left;
|
||||
- background-color: inherit;
|
||||
+ text-align: left;
|
||||
+ background-color: inherit;
|
||||
}
|
||||
|
||||
.monaco-workbench > .activitybar .action-item:focus {
|
||||
- outline: 0 !important; /* activity bar indicates focus custom */
|
||||
+ outline: 0 !important; /* activity bar indicates focus custom */
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-label.toggle-more {
|
||||
- -webkit-mask: url('ellipsis-global.svg') no-repeat 50% 50%;
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+ -webkit-mask: url("ellipsis-global.svg") no-repeat 50% 50%;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar {
|
||||
+ flex: 1;
|
||||
+ display: flex;
|
||||
+ flex-direction: column;
|
||||
+ overflow: visible;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal {
|
||||
+ transition: 500ms color ease;
|
||||
+ opacity: 0.65;
|
||||
+ filter: brightness(115%);
|
||||
+ padding-top: 10px;
|
||||
+ padding-bottom: 10px;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal:hover {
|
||||
+ opacity: 1;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal.disabled {
|
||||
+ cursor: disabled;
|
||||
+ opacity: 0.45 !important;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal > .icon {
|
||||
+ text-align: center;
|
||||
+ display: block;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .toggle-terminal > .icon > svg {
|
||||
+ width: 29px;
|
||||
+ fill: currentColor;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .fasttime {
|
||||
+ transition: 500ms color ease;
|
||||
+ opacity: 0.65;
|
||||
+ filter: brightness(115%);
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .fasttime:hover {
|
||||
+ opacity: 1;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .fasttime.disabled {
|
||||
+ cursor: disabled;
|
||||
+ opacity: 0.45 !important;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .icon {
|
||||
+ text-align: center;
|
||||
+ display: block;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .icon > svg {
|
||||
+ width: 22px;
|
||||
+ fill: currentColor;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .text {
|
||||
+ font-size: 12px;
|
||||
+ text-align: center;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .fasttime > .text.unknown {
|
||||
+ font-size: 8px;
|
||||
+ opacity: 0;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar > .feedback {
|
||||
+ transition: 500ms color ease;
|
||||
+ padding-top: 10px;
|
||||
+ padding-bottom: 10px;
|
||||
+ margin-left: 0px;
|
||||
+ margin-top: auto;
|
||||
+ flex: 0;
|
||||
+ cursor: default;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .feedback > .icon {
|
||||
+ text-align: center;
|
||||
+ display: block;
|
||||
+ opacity: 0.65;
|
||||
+ filter: brightness(115%);
|
||||
+ cursor: pointer;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .feedback .feedback {
|
||||
+ position: initial;
|
||||
+ margin-left: 0px;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .feedback .feedback-dropdown {
|
||||
+ bottom: -63px;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .feedback:hover > .icon {
|
||||
+ opacity: 1;
|
||||
+}
|
||||
+
|
||||
+.monaco-workbench .activitybar > .content > .extras-bar .feedback > .icon > svg {
|
||||
+ width: 29px;
|
||||
+ fill: currentColor;
|
||||
+}
|
8257
scripts/vscode.patch
8257
scripts/vscode.patch
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
|
||||
const root = path.join(__dirname, "..");
|
||||
|
||||
module.exports = {
|
||||
module.exports = (options = {}) => ({
|
||||
context: root,
|
||||
devtool: "source-map",
|
||||
// entry: "./packages/app/src/index.ts",
|
||||
@ -67,15 +67,6 @@ module.exports = {
|
||||
path.join(root, "node_modules"),
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
hot: true,
|
||||
port: 3000,
|
||||
stats: {
|
||||
all: false, // Fallback for options not defined.
|
||||
errors: true,
|
||||
warnings: true,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new HappyPack({
|
||||
id: "ts",
|
||||
@ -84,6 +75,7 @@ module.exports = {
|
||||
path: "ts-loader",
|
||||
query: {
|
||||
happyPackMode: true,
|
||||
compilerOptions: options.typescriptCompilerOptions,
|
||||
},
|
||||
}],
|
||||
}),
|
||||
@ -101,4 +93,4 @@ module.exports = {
|
||||
errors: true,
|
||||
warnings: true,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -8,7 +8,6 @@
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"importHelpers": true,
|
||||
"experimentalDecorators": true,
|
||||
"plugins": [
|
||||
{
|
||||
|
@ -1,66 +0,0 @@
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
|
||||
const WriteFilePlugin = require("write-file-webpack-plugin");
|
||||
const PreloadWebpackPlugin = require("preload-webpack-plugin");
|
||||
const root = __dirname;
|
||||
const fills = path.join(root, "packages", "ide", "src", "fill");
|
||||
const vscodeFills = path.join(root, "packages", "vscode", "src", "fill");
|
||||
|
||||
const merge = require("webpack-merge");
|
||||
|
||||
module.exports = merge({
|
||||
devtool: "eval",
|
||||
entry: "./packages/web/src/index.ts",
|
||||
output: {
|
||||
chunkFilename: "[name]-[hash:6].bundle.js",
|
||||
path: path.join(root, "dist"),
|
||||
filename: "[hash:6].bundle.js",
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"native-keymap": path.join(vscodeFills, "native-keymap.ts"),
|
||||
"node-pty": path.join(vscodeFills, "node-pty.ts"),
|
||||
|
||||
"gc-signals": path.join(fills, "empty.ts"),
|
||||
"selenium-webdriver": path.join(fills, "empty.ts"),
|
||||
"vscode": path.join(fills, "empty.ts"),
|
||||
"vscode-fsevents": path.join(fills, "empty.ts"),
|
||||
"vsda": path.join(fills, "empty.ts"),
|
||||
"windows-foreground-love": path.join(fills, "empty.ts"),
|
||||
"windows-mutex": path.join(fills, "empty.ts"),
|
||||
"windows-process-tree": path.join(fills, "empty.ts"),
|
||||
|
||||
"crypto": "crypto-browserify",
|
||||
"http": "http-browserify",
|
||||
"os": "os-browserify",
|
||||
|
||||
"child_process": path.join(fills, "child_process.ts"),
|
||||
"fs": path.join(fills, "fs.ts"),
|
||||
"net": path.join(fills, "net.ts"),
|
||||
"util": path.join(fills, "util.ts"),
|
||||
|
||||
"electron": path.join(fills, "electron.ts"),
|
||||
|
||||
"vs": path.join(root, "lib", "vscode", "src", "vs"),
|
||||
},
|
||||
},
|
||||
resolveLoader: {
|
||||
alias: {
|
||||
"vs/css": path.join(vscodeFills, "css.js"),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: "packages/web/src/index.html",
|
||||
}),
|
||||
new PreloadWebpackPlugin({
|
||||
rel: "preload",
|
||||
as: "script",
|
||||
}),
|
||||
new WriteFilePlugin({
|
||||
exitOnErrors: false,
|
||||
}),
|
||||
],
|
||||
target: "web",
|
||||
}, require("./scripts/webpack.general.config.js"));
|
32
yarn.lock
32
yarn.lock
@ -3971,7 +3971,7 @@ querystring-es3@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||
integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
|
||||
|
||||
querystring@0.2.0:
|
||||
querystring@0.2.0, querystring@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||
@ -4312,7 +4312,7 @@ sax@^1.2.4:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
schema-utils@^0.4.4:
|
||||
schema-utils@^0.4.4, schema-utils@^0.4.5:
|
||||
version "0.4.7"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
|
||||
integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==
|
||||
@ -4726,6 +4726,14 @@ stream-shift@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
|
||||
integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=
|
||||
|
||||
string-replace-loader@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-2.1.1.tgz#b72e7b57b6ef04efe615aff0ad989b5c14ca63d1"
|
||||
integrity sha512-0Nvw1LDclF45AFNuYPcD2Jvkv0mwb/dQSnJZMvhqGrT+zzmrpG3OJFD600qfQfNUd5aqfp7fCm2mQMfF7zLbyQ==
|
||||
dependencies:
|
||||
loader-utils "^1.1.0"
|
||||
schema-utils "^0.4.5"
|
||||
|
||||
string-width@^1.0.1, string-width@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
@ -5342,6 +5350,16 @@ webpack-dev-middleware@3.4.0:
|
||||
range-parser "^1.0.3"
|
||||
webpack-log "^2.0.0"
|
||||
|
||||
webpack-dev-middleware@^3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.5.0.tgz#fff0a07b0461314fb6ca82df3642c2423f768429"
|
||||
integrity sha512-1Zie7+dMr4Vv3nGyhr8mxGQkzTQK1PTS8K3yJ4yB1mfRGwO1DzQibgmNfUqbEfQY6eEtEEUzC+o7vhpm/Sfn5w==
|
||||
dependencies:
|
||||
memory-fs "~0.4.1"
|
||||
mime "^2.3.1"
|
||||
range-parser "^1.0.3"
|
||||
webpack-log "^2.0.0"
|
||||
|
||||
webpack-dev-server@^3.1.14:
|
||||
version "3.1.14"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz#60fb229b997fc5a0a1fc6237421030180959d469"
|
||||
@ -5378,6 +5396,16 @@ webpack-dev-server@^3.1.14:
|
||||
webpack-log "^2.0.0"
|
||||
yargs "12.0.2"
|
||||
|
||||
webpack-hot-middleware@^2.24.3:
|
||||
version "2.24.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.24.3.tgz#5bb76259a8fc0d97463ab517640ba91d3382d4a6"
|
||||
integrity sha512-pPlmcdoR2Fn6UhYjAhp1g/IJy1Yc9hD+T6O9mjRcWV2pFbBjIFoJXhP0CoD0xPOhWJuWXuZXGBga9ybbOdzXpg==
|
||||
dependencies:
|
||||
ansi-html "0.0.7"
|
||||
html-entities "^1.2.0"
|
||||
querystring "^0.2.0"
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
webpack-log@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f"
|
||||
|
Reference in New Issue
Block a user