2019-04-02 22:44:28 +00:00
|
|
|
import { Argument, Module as ProtoModule, WorkingInit } from "../proto";
|
2019-03-26 18:01:25 +00:00
|
|
|
import { OperatingSystem } from "../common/connection";
|
|
|
|
import { Module, ServerProxy } from "./proxy";
|
|
|
|
|
|
|
|
// tslint:disable no-any
|
|
|
|
|
2019-01-14 20:58:34 +00:00
|
|
|
/**
|
|
|
|
* Return true if we're in a browser environment (including web workers).
|
|
|
|
*/
|
|
|
|
export const isBrowserEnvironment = (): boolean => {
|
|
|
|
return typeof process === "undefined" || typeof process.stdout === "undefined";
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Escape a path. This prevents any issues with file names that have quotes,
|
|
|
|
* spaces, braces, etc.
|
|
|
|
*/
|
|
|
|
export const escapePath = (path: string): string => {
|
|
|
|
return `'${path.replace(/'/g, "'\\''")}'`;
|
|
|
|
};
|
2019-01-18 21:46:40 +00:00
|
|
|
|
|
|
|
export type IEncodingOptions = {
|
2019-03-26 18:01:25 +00:00
|
|
|
encoding?: BufferEncoding | null;
|
2019-01-18 21:46:40 +00:00
|
|
|
flag?: string;
|
|
|
|
mode?: string;
|
|
|
|
persistent?: boolean;
|
|
|
|
recursive?: boolean;
|
2019-03-26 18:01:25 +00:00
|
|
|
} | BufferEncoding | undefined | null;
|
2019-01-18 21:46:40 +00:00
|
|
|
|
|
|
|
export type IEncodingOptionsCallback = IEncodingOptions | ((err: NodeJS.ErrnoException, ...args: any[]) => void);
|
|
|
|
|
|
|
|
/**
|
2019-04-02 22:44:28 +00:00
|
|
|
* Convert an argument to proto.
|
2019-03-26 18:01:25 +00:00
|
|
|
* If sending a function is possible, provide `storeFunction`.
|
|
|
|
* If sending a proxy is possible, provide `storeProxy`.
|
2019-01-18 21:46:40 +00:00
|
|
|
*/
|
2019-04-02 22:44:28 +00:00
|
|
|
export const argumentToProto = (
|
2019-03-26 18:01:25 +00:00
|
|
|
value: any,
|
|
|
|
storeFunction?: (fn: () => void) => number,
|
|
|
|
storeProxy?: (proxy: ServerProxy) => number,
|
2019-04-02 22:44:28 +00:00
|
|
|
): Argument => {
|
|
|
|
const convert = (currentValue: any): Argument => {
|
|
|
|
const message = new Argument();
|
|
|
|
|
2019-03-26 18:01:25 +00:00
|
|
|
if (currentValue instanceof Error
|
|
|
|
|| (currentValue && typeof currentValue.message !== "undefined"
|
|
|
|
&& typeof currentValue.stack !== "undefined")) {
|
2019-04-02 22:44:28 +00:00
|
|
|
const arg = new Argument.ErrorValue();
|
|
|
|
arg.setMessage(currentValue.message);
|
|
|
|
arg.setStack(currentValue.stack);
|
|
|
|
arg.setCode(currentValue.code);
|
|
|
|
message.setError(arg);
|
|
|
|
} else if (currentValue instanceof Uint8Array || currentValue instanceof Buffer) {
|
|
|
|
const arg = new Argument.BufferValue();
|
|
|
|
arg.setData(currentValue);
|
|
|
|
message.setBuffer(arg);
|
|
|
|
} else if (Array.isArray(currentValue)) {
|
|
|
|
const arg = new Argument.ArrayValue();
|
|
|
|
arg.setDataList(currentValue.map(convert));
|
|
|
|
message.setArray(arg);
|
|
|
|
} else if (isProxy(currentValue)) {
|
2019-03-26 18:01:25 +00:00
|
|
|
if (!storeProxy) {
|
|
|
|
throw new Error("no way to serialize proxy");
|
|
|
|
}
|
2019-04-02 22:44:28 +00:00
|
|
|
const arg = new Argument.ProxyValue();
|
|
|
|
arg.setId(storeProxy(currentValue));
|
|
|
|
message.setProxy(arg);
|
2019-04-04 23:24:21 +00:00
|
|
|
} else if (currentValue instanceof Date
|
|
|
|
|| (currentValue && typeof currentValue.getTime === "function")) {
|
|
|
|
const arg = new Argument.DateValue();
|
|
|
|
arg.setDate(currentValue.toString());
|
|
|
|
message.setDate(arg);
|
2019-04-02 22:44:28 +00:00
|
|
|
} else if (currentValue !== null && typeof currentValue === "object") {
|
|
|
|
const arg = new Argument.ObjectValue();
|
|
|
|
const map = arg.getDataMap();
|
2019-03-26 18:01:25 +00:00
|
|
|
Object.keys(currentValue).forEach((key) => {
|
2019-04-02 22:44:28 +00:00
|
|
|
map.set(key, convert(currentValue[key]));
|
2019-03-26 18:01:25 +00:00
|
|
|
});
|
2019-04-02 22:44:28 +00:00
|
|
|
message.setObject(arg);
|
|
|
|
} else if (currentValue === null) {
|
|
|
|
message.setNull(new Argument.NullValue());
|
|
|
|
} else {
|
|
|
|
switch (typeof currentValue) {
|
|
|
|
case "undefined":
|
|
|
|
message.setUndefined(new Argument.UndefinedValue());
|
|
|
|
break;
|
|
|
|
case "function":
|
|
|
|
if (!storeFunction) {
|
|
|
|
throw new Error("no way to serialize function");
|
|
|
|
}
|
|
|
|
const arg = new Argument.FunctionValue();
|
|
|
|
arg.setId(storeFunction(currentValue));
|
|
|
|
message.setFunction(arg);
|
|
|
|
break;
|
|
|
|
case "number":
|
|
|
|
message.setNumber(currentValue);
|
|
|
|
break;
|
|
|
|
case "string":
|
|
|
|
message.setString(currentValue);
|
|
|
|
break;
|
|
|
|
case "boolean":
|
|
|
|
message.setBoolean(currentValue);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Error(`cannot convert ${typeof currentValue} to proto`);
|
2019-03-26 18:01:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-02 22:44:28 +00:00
|
|
|
return message;
|
2019-03-26 18:01:25 +00:00
|
|
|
};
|
2019-02-19 16:17:03 +00:00
|
|
|
|
2019-04-02 22:44:28 +00:00
|
|
|
return convert(value);
|
2019-01-18 21:46:40 +00:00
|
|
|
};
|
2019-03-26 18:01:25 +00:00
|
|
|
|
2019-02-19 16:17:03 +00:00
|
|
|
/**
|
2019-04-02 22:44:28 +00:00
|
|
|
* Convert proto to an argument.
|
2019-03-26 18:01:25 +00:00
|
|
|
* If running a remote callback is supported, provide `runCallback`.
|
|
|
|
* If using a remote proxy is supported, provide `createProxy`.
|
2019-02-19 16:17:03 +00:00
|
|
|
*/
|
2019-04-02 22:44:28 +00:00
|
|
|
export const protoToArgument = (
|
|
|
|
message?: Argument,
|
2019-03-26 18:01:25 +00:00
|
|
|
runCallback?: (id: number, args: any[]) => void,
|
|
|
|
createProxy?: (id: number) => ServerProxy,
|
|
|
|
): any => {
|
2019-04-02 22:44:28 +00:00
|
|
|
const convert = (currentMessage: Argument): any => {
|
|
|
|
switch (currentMessage.getMsgCase()) {
|
|
|
|
case Argument.MsgCase.ERROR:
|
|
|
|
const errorMessage = currentMessage.getError()!;
|
|
|
|
const error = new Error(errorMessage.getMessage());
|
|
|
|
(error as NodeJS.ErrnoException).code = errorMessage.getCode();
|
|
|
|
(error as any).originalStack = errorMessage.getStack();
|
2019-03-26 18:01:25 +00:00
|
|
|
|
|
|
|
return error;
|
2019-04-02 22:44:28 +00:00
|
|
|
case Argument.MsgCase.BUFFER:
|
|
|
|
return Buffer.from(currentMessage.getBuffer()!.getData() as Uint8Array);
|
|
|
|
case Argument.MsgCase.ARRAY:
|
|
|
|
return currentMessage.getArray()!.getDataList().map((a) => convert(a));
|
|
|
|
case Argument.MsgCase.PROXY:
|
|
|
|
if (!createProxy) {
|
|
|
|
throw new Error("no way to create proxy");
|
|
|
|
}
|
2019-03-26 18:01:25 +00:00
|
|
|
|
2019-04-02 22:44:28 +00:00
|
|
|
return createProxy(currentMessage.getProxy()!.getId());
|
2019-04-04 23:24:21 +00:00
|
|
|
case Argument.MsgCase.DATE:
|
|
|
|
return new Date(currentMessage.getDate()!.getDate());
|
2019-04-02 22:44:28 +00:00
|
|
|
case Argument.MsgCase.OBJECT:
|
|
|
|
const obj: { [Key: string]: any } = {};
|
|
|
|
currentMessage.getObject()!.getDataMap().forEach((argument, key) => {
|
|
|
|
obj[key] = convert(argument);
|
2019-03-26 18:01:25 +00:00
|
|
|
});
|
|
|
|
|
2019-04-02 22:44:28 +00:00
|
|
|
return obj;
|
|
|
|
case Argument.MsgCase.UNDEFINED:
|
2019-03-26 18:01:25 +00:00
|
|
|
return undefined;
|
2019-04-02 22:44:28 +00:00
|
|
|
case Argument.MsgCase.NULL:
|
|
|
|
return null;
|
|
|
|
case Argument.MsgCase.FUNCTION:
|
2019-03-26 18:01:25 +00:00
|
|
|
if (!runCallback) {
|
|
|
|
throw new Error("no way to run remote callback");
|
|
|
|
}
|
|
|
|
|
|
|
|
return (...args: any[]): void => {
|
2019-04-02 22:44:28 +00:00
|
|
|
return runCallback(currentMessage.getFunction()!.getId(), args);
|
2019-03-26 18:01:25 +00:00
|
|
|
};
|
2019-04-02 22:44:28 +00:00
|
|
|
case Argument.MsgCase.NUMBER:
|
|
|
|
return currentMessage.getNumber();
|
|
|
|
case Argument.MsgCase.STRING:
|
|
|
|
return currentMessage.getString();
|
|
|
|
case Argument.MsgCase.BOOLEAN:
|
|
|
|
return currentMessage.getBoolean();
|
|
|
|
default:
|
|
|
|
throw new Error("cannot convert unexpected proto to argument");
|
2019-02-19 16:17:03 +00:00
|
|
|
}
|
2019-02-27 20:40:57 +00:00
|
|
|
};
|
|
|
|
|
2019-04-02 22:44:28 +00:00
|
|
|
return message && convert(message);
|
2019-03-26 18:01:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export const protoToModule = (protoModule: ProtoModule): Module => {
|
|
|
|
switch (protoModule) {
|
|
|
|
case ProtoModule.CHILDPROCESS: return Module.ChildProcess;
|
|
|
|
case ProtoModule.FS: return Module.Fs;
|
|
|
|
case ProtoModule.NET: return Module.Net;
|
|
|
|
case ProtoModule.NODEPTY: return Module.NodePty;
|
|
|
|
case ProtoModule.SPDLOG: return Module.Spdlog;
|
|
|
|
case ProtoModule.TRASH: return Module.Trash;
|
|
|
|
default: throw new Error(`invalid module ${protoModule}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export const moduleToProto = (moduleName: Module): ProtoModule => {
|
|
|
|
switch (moduleName) {
|
|
|
|
case Module.ChildProcess: return ProtoModule.CHILDPROCESS;
|
|
|
|
case Module.Fs: return ProtoModule.FS;
|
|
|
|
case Module.Net: return ProtoModule.NET;
|
|
|
|
case Module.NodePty: return ProtoModule.NODEPTY;
|
|
|
|
case Module.Spdlog: return ProtoModule.SPDLOG;
|
|
|
|
case Module.Trash: return ProtoModule.TRASH;
|
|
|
|
default: throw new Error(`invalid module "${moduleName}"`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-04-02 22:44:28 +00:00
|
|
|
export const protoToOperatingSystem = (protoOp: WorkingInit.OperatingSystem): OperatingSystem => {
|
2019-03-26 18:01:25 +00:00
|
|
|
switch (protoOp) {
|
2019-04-02 22:44:28 +00:00
|
|
|
case WorkingInit.OperatingSystem.WINDOWS: return OperatingSystem.Windows;
|
|
|
|
case WorkingInit.OperatingSystem.LINUX: return OperatingSystem.Linux;
|
|
|
|
case WorkingInit.OperatingSystem.MAC: return OperatingSystem.Mac;
|
2019-03-26 18:01:25 +00:00
|
|
|
default: throw new Error(`unsupported operating system ${protoOp}`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-04-02 22:44:28 +00:00
|
|
|
export const platformToProto = (platform: NodeJS.Platform): WorkingInit.OperatingSystem => {
|
2019-03-26 18:01:25 +00:00
|
|
|
switch (platform) {
|
2019-04-02 22:44:28 +00:00
|
|
|
case "win32": return WorkingInit.OperatingSystem.WINDOWS;
|
|
|
|
case "linux": return WorkingInit.OperatingSystem.LINUX;
|
|
|
|
case "darwin": return WorkingInit.OperatingSystem.MAC;
|
2019-03-26 18:01:25 +00:00
|
|
|
default: throw new Error(`unrecognized platform "${platform}"`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export const isProxy = (value: any): value is ServerProxy => {
|
|
|
|
return value && typeof value === "object" && typeof value.onEvent === "function";
|
|
|
|
};
|
|
|
|
|
|
|
|
export const isPromise = (value: any): value is Promise<any> => {
|
|
|
|
return typeof value.then === "function" && typeof value.catch === "function";
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When spawning VS Code tries to preserve the environment but since it's in
|
|
|
|
* the browser, it doesn't work.
|
|
|
|
*/
|
|
|
|
export const preserveEnv = (options?: { env?: NodeJS.ProcessEnv } | null): void => {
|
|
|
|
if (options && options.env) {
|
|
|
|
options.env = { ...process.env, ...options.env };
|
|
|
|
}
|
2019-02-19 16:17:03 +00:00
|
|
|
};
|