mirror of
https://github.com/coder/code-server.git
synced 2026-05-05 20:15:19 +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:
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user