mirror of
https://github.com/coder/code-server.git
synced 2026-05-11 06:47:25 +02:00
Refactor evaluations (#285)
* Replace evaluations with proxies and messages * Return proxies synchronously Otherwise events can be lost. * Ensure events cannot be missed * Refactor remaining fills * Use more up-to-date version of util For callbackify. * Wait for dispose to come back before removing This prevents issues with the "done" event not always being the last event fired. For example a socket might close and then end, but only if the caller called end. * Remove old node-pty tests * Fix emitting events twice on duplex streams * Preserve environment when spawning processes * Throw a better error if the proxy doesn't exist * Remove rimraf dependency from ide * Update net.Server.listening * Use exit event instead of killed Doesn't look like killed is even a thing. * Add response timeout to server * Fix trash * Require node-pty & spdlog after they get unpackaged This fixes an error when running in the binary. * Fix errors in down emitter preventing reconnecting * Fix disposing proxies when nothing listens to "error" event * Refactor event tests to use jest.fn() * Reject proxy call when disconnected Otherwise it'll wait for the timeout which is a waste of time since we already know the connection is dead. * Use nbin for binary packaging * Remove additional module requires * Attempt to remove require for local bootstrap-fork * Externalize fsevents
This commit is contained in:
103
packages/protocol/src/node/modules/child_process.ts
Normal file
103
packages/protocol/src/node/modules/child_process.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import * as cp from "child_process";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { preserveEnv } from "../../common/util";
|
||||
import { WritableProxy, ReadableProxy } from "./stream";
|
||||
|
||||
export type ForkProvider = (modulePath: string, args?: string[], options?: cp.ForkOptions) => cp.ChildProcess;
|
||||
|
||||
export class ChildProcessProxy implements ServerProxy {
|
||||
public constructor(private readonly process: cp.ChildProcess) {}
|
||||
|
||||
public async kill(signal?: string): Promise<void> {
|
||||
this.process.kill(signal);
|
||||
}
|
||||
|
||||
public async disconnect(): Promise<void> {
|
||||
this.process.disconnect();
|
||||
}
|
||||
|
||||
public async ref(): Promise<void> {
|
||||
this.process.ref();
|
||||
}
|
||||
|
||||
public async unref(): Promise<void> {
|
||||
this.process.unref();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async send(message: any): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.process.send(message, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async getPid(): Promise<number> {
|
||||
return this.process.pid;
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.process.on("close", cb);
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.kill();
|
||||
setTimeout(() => this.kill("SIGKILL"), 5000); // Double tap.
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.process.on("close", (code, signal) => cb("close", code, signal));
|
||||
this.process.on("disconnect", () => cb("disconnect"));
|
||||
this.process.on("error", (error) => cb("error", error));
|
||||
this.process.on("exit", (exitCode, signal) => cb("exit", exitCode, signal));
|
||||
this.process.on("message", (message) => cb("message", message));
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChildProcessProxies {
|
||||
childProcess: ChildProcessProxy;
|
||||
stdin?: WritableProxy;
|
||||
stdout?: ReadableProxy;
|
||||
stderr?: ReadableProxy;
|
||||
}
|
||||
|
||||
export class ChildProcessModuleProxy {
|
||||
public constructor(private readonly forkProvider?: ForkProvider) {}
|
||||
|
||||
public async exec(
|
||||
command: string,
|
||||
options?: { encoding?: string | null } & cp.ExecOptions | null,
|
||||
callback?: ((error: cp.ExecException | null, stdin: string | Buffer, stdout: string | Buffer) => void),
|
||||
): Promise<ChildProcessProxies> {
|
||||
preserveEnv(options);
|
||||
|
||||
return this.returnProxies(cp.exec(command, options, callback));
|
||||
}
|
||||
|
||||
public async fork(modulePath: string, args?: string[], options?: cp.ForkOptions): Promise<ChildProcessProxies> {
|
||||
preserveEnv(options);
|
||||
|
||||
return this.returnProxies((this.forkProvider || cp.fork)(modulePath, args, options));
|
||||
}
|
||||
|
||||
public async spawn(command: string, args?: string[], options?: cp.SpawnOptions): Promise<ChildProcessProxies> {
|
||||
preserveEnv(options);
|
||||
|
||||
return this.returnProxies(cp.spawn(command, args, options));
|
||||
}
|
||||
|
||||
private returnProxies(process: cp.ChildProcess): ChildProcessProxies {
|
||||
return {
|
||||
childProcess: new ChildProcessProxy(process),
|
||||
stdin: process.stdin && new WritableProxy(process.stdin),
|
||||
stdout: process.stdout && new ReadableProxy(process.stdout),
|
||||
stderr: process.stderr && new ReadableProxy(process.stderr),
|
||||
};
|
||||
}
|
||||
}
|
||||
250
packages/protocol/src/node/modules/fs.ts
Normal file
250
packages/protocol/src/node/modules/fs.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
import * as fs from "fs";
|
||||
import { promisify } from "util";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { IEncodingOptions } from "../../common/util";
|
||||
import { WritableProxy } from "./stream";
|
||||
|
||||
/**
|
||||
* A serializable version of fs.Stats.
|
||||
*/
|
||||
export interface Stats {
|
||||
dev: number;
|
||||
ino: number;
|
||||
mode: number;
|
||||
nlink: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
rdev: number;
|
||||
size: number;
|
||||
blksize: number;
|
||||
blocks: number;
|
||||
atimeMs: number;
|
||||
mtimeMs: number;
|
||||
ctimeMs: number;
|
||||
birthtimeMs: number;
|
||||
atime: Date | string;
|
||||
mtime: Date | string;
|
||||
ctime: Date | string;
|
||||
birthtime: Date | string;
|
||||
_isFile: boolean;
|
||||
_isDirectory: boolean;
|
||||
_isBlockDevice: boolean;
|
||||
_isCharacterDevice: boolean;
|
||||
_isSymbolicLink: boolean;
|
||||
_isFIFO: boolean;
|
||||
_isSocket: boolean;
|
||||
}
|
||||
|
||||
export class WriteStreamProxy extends WritableProxy<fs.WriteStream> {
|
||||
public async close(): Promise<void> {
|
||||
this.stream.close();
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
super.dispose();
|
||||
this.stream.close();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
super.onEvent(cb);
|
||||
this.stream.on("open", (fd) => cb("open", fd));
|
||||
}
|
||||
}
|
||||
|
||||
export class WatcherProxy implements ServerProxy {
|
||||
public constructor(private readonly watcher: fs.FSWatcher) {}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
this.watcher.close();
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.watcher.close();
|
||||
this.watcher.removeAllListeners();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.watcher.on("close", cb);
|
||||
this.watcher.on("error", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.watcher.on("change", (event, filename) => cb("change", event, filename));
|
||||
this.watcher.on("close", () => cb("close"));
|
||||
this.watcher.on("error", (error) => cb("error", error));
|
||||
}
|
||||
}
|
||||
|
||||
export class FsModuleProxy {
|
||||
public access(path: fs.PathLike, mode?: number): Promise<void> {
|
||||
return promisify(fs.access)(path, mode);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public appendFile(file: fs.PathLike | number, data: any, options?: fs.WriteFileOptions): Promise<void> {
|
||||
return promisify(fs.appendFile)(file, data, options);
|
||||
}
|
||||
|
||||
public chmod(path: fs.PathLike, mode: string | number): Promise<void> {
|
||||
return promisify(fs.chmod)(path, mode);
|
||||
}
|
||||
|
||||
public chown(path: fs.PathLike, uid: number, gid: number): Promise<void> {
|
||||
return promisify(fs.chown)(path, uid, gid);
|
||||
}
|
||||
|
||||
public close(fd: number): Promise<void> {
|
||||
return promisify(fs.close)(fd);
|
||||
}
|
||||
|
||||
public copyFile(src: fs.PathLike, dest: fs.PathLike, flags?: number): Promise<void> {
|
||||
return promisify(fs.copyFile)(src, dest, flags);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async createWriteStream(path: fs.PathLike, options?: any): Promise<WriteStreamProxy> {
|
||||
return new WriteStreamProxy(fs.createWriteStream(path, options));
|
||||
}
|
||||
|
||||
public exists(path: fs.PathLike): Promise<boolean> {
|
||||
return promisify(fs.exists)(path);
|
||||
}
|
||||
|
||||
public fchmod(fd: number, mode: string | number): Promise<void> {
|
||||
return promisify(fs.fchmod)(fd, mode);
|
||||
}
|
||||
|
||||
public fchown(fd: number, uid: number, gid: number): Promise<void> {
|
||||
return promisify(fs.fchown)(fd, uid, gid);
|
||||
}
|
||||
|
||||
public fdatasync(fd: number): Promise<void> {
|
||||
return promisify(fs.fdatasync)(fd);
|
||||
}
|
||||
|
||||
public async fstat(fd: number): Promise<Stats> {
|
||||
return this.makeStatsSerializable(await promisify(fs.fstat)(fd));
|
||||
}
|
||||
|
||||
public fsync(fd: number): Promise<void> {
|
||||
return promisify(fs.fsync)(fd);
|
||||
}
|
||||
|
||||
public ftruncate(fd: number, len?: number | null): Promise<void> {
|
||||
return promisify(fs.ftruncate)(fd, len);
|
||||
}
|
||||
|
||||
public futimes(fd: number, atime: string | number | Date, mtime: string | number | Date): Promise<void> {
|
||||
return promisify(fs.futimes)(fd, atime, mtime);
|
||||
}
|
||||
|
||||
public lchmod(path: fs.PathLike, mode: string | number): Promise<void> {
|
||||
return promisify(fs.lchmod)(path, mode);
|
||||
}
|
||||
|
||||
public lchown(path: fs.PathLike, uid: number, gid: number): Promise<void> {
|
||||
return promisify(fs.lchown)(path, uid, gid);
|
||||
}
|
||||
|
||||
public link(existingPath: fs.PathLike, newPath: fs.PathLike): Promise<void> {
|
||||
return promisify(fs.link)(existingPath, newPath);
|
||||
}
|
||||
|
||||
public async lstat(path: fs.PathLike): Promise<Stats> {
|
||||
return this.makeStatsSerializable(await promisify(fs.lstat)(path));
|
||||
}
|
||||
|
||||
public mkdir(path: fs.PathLike, mode: number | string | fs.MakeDirectoryOptions | undefined | null): Promise<void> {
|
||||
return promisify(fs.mkdir)(path, mode);
|
||||
}
|
||||
|
||||
public mkdtemp(prefix: string, options: IEncodingOptions): Promise<string | Buffer> {
|
||||
return promisify(fs.mkdtemp)(prefix, options);
|
||||
}
|
||||
|
||||
public open(path: fs.PathLike, flags: string | number, mode: string | number | undefined | null): Promise<number> {
|
||||
return promisify(fs.open)(path, flags, mode);
|
||||
}
|
||||
|
||||
public read(fd: number, length: number, position: number | null): Promise<{ bytesRead: number, buffer: Buffer }> {
|
||||
const buffer = new Buffer(length);
|
||||
|
||||
return promisify(fs.read)(fd, buffer, 0, length, position);
|
||||
}
|
||||
|
||||
public readFile(path: fs.PathLike | number, options: IEncodingOptions): Promise<string | Buffer> {
|
||||
return promisify(fs.readFile)(path, options);
|
||||
}
|
||||
|
||||
public readdir(path: fs.PathLike, options: IEncodingOptions): Promise<Buffer[] | fs.Dirent[] | string[]> {
|
||||
return promisify(fs.readdir)(path, options);
|
||||
}
|
||||
|
||||
public readlink(path: fs.PathLike, options: IEncodingOptions): Promise<string | Buffer> {
|
||||
return promisify(fs.readlink)(path, options);
|
||||
}
|
||||
|
||||
public realpath(path: fs.PathLike, options: IEncodingOptions): Promise<string | Buffer> {
|
||||
return promisify(fs.realpath)(path, options);
|
||||
}
|
||||
|
||||
public rename(oldPath: fs.PathLike, newPath: fs.PathLike): Promise<void> {
|
||||
return promisify(fs.rename)(oldPath, newPath);
|
||||
}
|
||||
|
||||
public rmdir(path: fs.PathLike): Promise<void> {
|
||||
return promisify(fs.rmdir)(path);
|
||||
}
|
||||
|
||||
public async stat(path: fs.PathLike): Promise<Stats> {
|
||||
return this.makeStatsSerializable(await promisify(fs.stat)(path));
|
||||
}
|
||||
|
||||
public symlink(target: fs.PathLike, path: fs.PathLike, type?: fs.symlink.Type | null): Promise<void> {
|
||||
return promisify(fs.symlink)(target, path, type);
|
||||
}
|
||||
|
||||
public truncate(path: fs.PathLike, len?: number | null): Promise<void> {
|
||||
return promisify(fs.truncate)(path, len);
|
||||
}
|
||||
|
||||
public unlink(path: fs.PathLike): Promise<void> {
|
||||
return promisify(fs.unlink)(path);
|
||||
}
|
||||
|
||||
public utimes(path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date): Promise<void> {
|
||||
return promisify(fs.utimes)(path, atime, mtime);
|
||||
}
|
||||
|
||||
public async write(fd: number, buffer: Buffer, offset?: number, length?: number, position?: number): Promise<{ bytesWritten: number, buffer: Buffer }> {
|
||||
return promisify(fs.write)(fd, buffer, offset, length, position);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public writeFile (path: fs.PathLike | number, data: any, options: IEncodingOptions): Promise<void> {
|
||||
return promisify(fs.writeFile)(path, data, options);
|
||||
}
|
||||
|
||||
public async watch(filename: fs.PathLike, options?: IEncodingOptions): Promise<WatcherProxy> {
|
||||
return new WatcherProxy(fs.watch(filename, options));
|
||||
}
|
||||
|
||||
private makeStatsSerializable(stats: fs.Stats): Stats {
|
||||
return {
|
||||
...stats,
|
||||
/**
|
||||
* We need to check if functions exist because nexe's implemented FS
|
||||
* lib doesnt implement fs.stats properly.
|
||||
*/
|
||||
_isBlockDevice: stats.isBlockDevice ? stats.isBlockDevice() : false,
|
||||
_isCharacterDevice: stats.isCharacterDevice ? stats.isCharacterDevice() : false,
|
||||
_isDirectory: stats.isDirectory(),
|
||||
_isFIFO: stats.isFIFO ? stats.isFIFO() : false,
|
||||
_isFile: stats.isFile(),
|
||||
_isSocket: stats.isSocket ? stats.isSocket() : false,
|
||||
_isSymbolicLink: stats.isSymbolicLink ? stats.isSymbolicLink() : false,
|
||||
};
|
||||
}
|
||||
}
|
||||
6
packages/protocol/src/node/modules/index.ts
Normal file
6
packages/protocol/src/node/modules/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export * from "./child_process";
|
||||
export * from "./fs";
|
||||
export * from "./net";
|
||||
export * from "./node-pty";
|
||||
export * from "./spdlog";
|
||||
export * from "./trash";
|
||||
90
packages/protocol/src/node/modules/net.ts
Normal file
90
packages/protocol/src/node/modules/net.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import * as net from "net";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { DuplexProxy } from "./stream";
|
||||
|
||||
export class NetSocketProxy extends DuplexProxy<net.Socket> {
|
||||
public async connect(options: number | string | net.SocketConnectOpts, host?: string): Promise<void> {
|
||||
this.stream.connect(options as any, host as any); // tslint:disable-line no-any this works fine
|
||||
}
|
||||
|
||||
public async unref(): Promise<void> {
|
||||
this.stream.unref();
|
||||
}
|
||||
|
||||
public async ref(): Promise<void> {
|
||||
this.stream.ref();
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.stream.removeAllListeners();
|
||||
this.stream.end();
|
||||
this.stream.destroy();
|
||||
this.stream.unref();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.stream.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
super.onEvent(cb);
|
||||
this.stream.on("connect", () => cb("connect"));
|
||||
this.stream.on("lookup", (error, address, family, host) => cb("lookup", error, address, family, host));
|
||||
this.stream.on("timeout", () => cb("timeout"));
|
||||
}
|
||||
}
|
||||
|
||||
export class NetServerProxy implements ServerProxy {
|
||||
public constructor(private readonly server: net.Server) {}
|
||||
|
||||
public async listen(handle?: net.ListenOptions | number | string, hostname?: string | number, backlog?: number): Promise<void> {
|
||||
this.server.listen(handle, hostname as any, backlog as any); // tslint:disable-line no-any this is fine
|
||||
}
|
||||
|
||||
public async ref(): Promise<void> {
|
||||
this.server.ref();
|
||||
}
|
||||
|
||||
public async unref(): Promise<void> {
|
||||
this.server.unref();
|
||||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
this.server.close();
|
||||
}
|
||||
|
||||
public async onConnection(cb: (proxy: NetSocketProxy) => void): Promise<void> {
|
||||
this.server.on("connection", (socket) => cb(new NetSocketProxy(socket)));
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.server.close();
|
||||
this.server.removeAllListeners();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.server.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.server.on("close", () => cb("close"));
|
||||
this.server.on("error", (error) => cb("error", error));
|
||||
this.server.on("listening", () => cb("listening"));
|
||||
}
|
||||
}
|
||||
|
||||
export class NetModuleProxy {
|
||||
public async createSocket(options?: net.SocketConstructorOpts): Promise<NetSocketProxy> {
|
||||
return new NetSocketProxy(new net.Socket(options));
|
||||
}
|
||||
|
||||
public async createConnection(target: string | number | net.NetConnectOpts, host?: string): Promise<NetSocketProxy> {
|
||||
return new NetSocketProxy(net.createConnection(target as any, host)); // tslint:disable-line no-any defeat stubborness
|
||||
}
|
||||
|
||||
public async createServer(options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean }): Promise<NetServerProxy> {
|
||||
return new NetServerProxy(net.createServer(options));
|
||||
}
|
||||
}
|
||||
75
packages/protocol/src/node/modules/node-pty.ts
Normal file
75
packages/protocol/src/node/modules/node-pty.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/// <reference path="../../../../../lib/vscode/src/typings/node-pty.d.ts" />
|
||||
import { EventEmitter } from "events";
|
||||
import * as pty from "node-pty";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { preserveEnv } from "../../common/util";
|
||||
|
||||
/**
|
||||
* Server-side IPty proxy.
|
||||
*/
|
||||
export class NodePtyProcessProxy implements ServerProxy {
|
||||
private readonly emitter = new EventEmitter();
|
||||
|
||||
public constructor(private readonly process: pty.IPty) {
|
||||
let name = process.process;
|
||||
setTimeout(() => { // Need to wait for the caller to listen to the event.
|
||||
this.emitter.emit("process", name);
|
||||
}, 1);
|
||||
const timer = setInterval(() => {
|
||||
if (process.process !== name) {
|
||||
name = process.process;
|
||||
this.emitter.emit("process", name);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
this.onDone(() => clearInterval(timer));
|
||||
}
|
||||
|
||||
public async getPid(): Promise<number> {
|
||||
return this.process.pid;
|
||||
}
|
||||
|
||||
public async getProcess(): Promise<string> {
|
||||
return this.process.process;
|
||||
}
|
||||
|
||||
public async kill(signal?: string): Promise<void> {
|
||||
this.process.kill(signal);
|
||||
}
|
||||
|
||||
public async resize(columns: number, rows: number): Promise<void> {
|
||||
this.process.resize(columns, rows);
|
||||
}
|
||||
|
||||
public async write(data: string): Promise<void> {
|
||||
this.process.write(data);
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.process.on("exit", cb);
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.kill();
|
||||
setTimeout(() => this.kill("SIGKILL"), 5000); // Double tap.
|
||||
this.emitter.removeAllListeners();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.emitter.on("process", (process) => cb("process", process));
|
||||
this.process.on("data", (data) => cb("data", data));
|
||||
this.process.on("exit", (exitCode, signal) => cb("exit", exitCode, signal));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side node-pty proxy.
|
||||
*/
|
||||
export class NodePtyModuleProxy {
|
||||
public async spawn(file: string, args: string[] | string, options: pty.IPtyForkOptions): Promise<NodePtyProcessProxy> {
|
||||
preserveEnv(options);
|
||||
|
||||
return new NodePtyProcessProxy(require("node-pty").spawn(file, args, options));
|
||||
}
|
||||
}
|
||||
46
packages/protocol/src/node/modules/spdlog.ts
Normal file
46
packages/protocol/src/node/modules/spdlog.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/// <reference path="../../../../../lib/vscode/src/typings/spdlog.d.ts" />
|
||||
import { EventEmitter } from "events";
|
||||
import * as spdlog from "spdlog";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
|
||||
export class RotatingLoggerProxy implements ServerProxy {
|
||||
private readonly emitter = new EventEmitter();
|
||||
|
||||
public constructor(private readonly logger: spdlog.RotatingLogger) {}
|
||||
|
||||
public async trace (message: string): Promise<void> { this.logger.trace(message); }
|
||||
public async debug (message: string): Promise<void> { this.logger.debug(message); }
|
||||
public async info (message: string): Promise<void> { this.logger.info(message); }
|
||||
public async warn (message: string): Promise<void> { this.logger.warn(message); }
|
||||
public async error (message: string): Promise<void> { this.logger.error(message); }
|
||||
public async critical (message: string): Promise<void> { this.logger.critical(message); }
|
||||
public async setLevel (level: number): Promise<void> { this.logger.setLevel(level); }
|
||||
public async clearFormatters (): Promise<void> { this.logger.clearFormatters(); }
|
||||
public async flush (): Promise<void> { this.logger.flush(); }
|
||||
public async drop (): Promise<void> { this.logger.drop(); }
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.emitter.on("dispose", cb);
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.flush();
|
||||
this.emitter.emit("dispose");
|
||||
this.emitter.removeAllListeners();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(_cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
// No events.
|
||||
}
|
||||
}
|
||||
|
||||
export class SpdlogModuleProxy {
|
||||
public async createLogger(name: string, filePath: string, fileSize: number, fileCount: number): Promise<RotatingLoggerProxy> {
|
||||
return new RotatingLoggerProxy(new (require("spdlog") as typeof import("spdlog")).RotatingLogger(name, filePath, fileSize, fileCount));
|
||||
}
|
||||
|
||||
public async setAsyncMode(bufferSize: number, flushInterval: number): Promise<void> {
|
||||
require("spdlog").setAsyncMode(bufferSize, flushInterval);
|
||||
}
|
||||
}
|
||||
107
packages/protocol/src/node/modules/stream.ts
Normal file
107
packages/protocol/src/node/modules/stream.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import * as stream from "stream";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
|
||||
export class WritableProxy<T extends stream.Writable = stream.Writable> implements ServerProxy {
|
||||
public constructor(protected readonly stream: T) {}
|
||||
|
||||
public async destroy(): Promise<void> {
|
||||
this.stream.destroy();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async end(data?: any, encoding?: string): Promise<void> {
|
||||
return new Promise((resolve): void => {
|
||||
this.stream.end(data, encoding, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async setDefaultEncoding(encoding: string): Promise<void> {
|
||||
this.stream.setDefaultEncoding(encoding);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async write(data: any, encoding?: string): Promise<void> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
this.stream.write(data, encoding, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.stream.end();
|
||||
this.stream.removeAllListeners();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.stream.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
// Sockets have an extra argument on "close".
|
||||
// tslint:disable-next-line no-any
|
||||
this.stream.on("close", (...args: any[]) => cb("close", ...args));
|
||||
this.stream.on("drain", () => cb("drain"));
|
||||
this.stream.on("error", (error) => cb("error", error));
|
||||
this.stream.on("finish", () => cb("finish"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This noise is because we can't do multiple extends and we also can't seem to
|
||||
* do `extends WritableProxy<T> implement ReadableProxy<T>` (for `DuplexProxy`).
|
||||
*/
|
||||
export interface IReadableProxy extends ServerProxy {
|
||||
destroy(): Promise<void>;
|
||||
setEncoding(encoding: string): Promise<void>;
|
||||
dispose(): Promise<void>;
|
||||
onDone(cb: () => void): Promise<void>;
|
||||
}
|
||||
|
||||
export class ReadableProxy<T extends stream.Readable = stream.Readable> implements IReadableProxy {
|
||||
public constructor(protected readonly stream: T) {}
|
||||
|
||||
public async destroy(): Promise<void> {
|
||||
this.stream.destroy();
|
||||
}
|
||||
|
||||
public async setEncoding(encoding: string): Promise<void> {
|
||||
this.stream.setEncoding(encoding);
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.stream.destroy();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.stream.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.stream.on("close", () => cb("close"));
|
||||
this.stream.on("data", (chunk) => cb("data", chunk));
|
||||
this.stream.on("end", () => cb("end"));
|
||||
this.stream.on("error", (error) => cb("error", error));
|
||||
}
|
||||
}
|
||||
|
||||
export class DuplexProxy<T extends stream.Duplex = stream.Duplex> extends WritableProxy<T> implements IReadableProxy {
|
||||
public async setEncoding(encoding: string): Promise<void> {
|
||||
this.stream.setEncoding(encoding);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
super.onEvent(cb);
|
||||
this.stream.on("data", (chunk) => cb("data", chunk));
|
||||
this.stream.on("end", () => cb("end"));
|
||||
}
|
||||
}
|
||||
7
packages/protocol/src/node/modules/trash.ts
Normal file
7
packages/protocol/src/node/modules/trash.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import * as trash from "trash";
|
||||
|
||||
export class TrashModuleProxy {
|
||||
public async trash(path: string, options?: trash.Options): Promise<void> {
|
||||
return trash(path, options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user