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:
@ -1,10 +1,5 @@
|
||||
{
|
||||
"name": "@coder/ide",
|
||||
"description": "Browser-based IDE client abstraction.",
|
||||
"main": "src/index.ts",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@types/rimraf": "^2.0.2",
|
||||
"rimraf": "^2.6.3"
|
||||
}
|
||||
"main": "src/index.ts"
|
||||
}
|
||||
|
@ -1,195 +1,4 @@
|
||||
import * as cp from "child_process";
|
||||
import * as net from "net";
|
||||
import * as stream from "stream";
|
||||
import { CallbackEmitter, ActiveEvalReadable, ActiveEvalWritable } from "@coder/protocol";
|
||||
import { Module } from "@coder/protocol";
|
||||
import { client } from "./client";
|
||||
import { promisify } from "util";
|
||||
|
||||
declare var __non_webpack_require__: typeof require;
|
||||
|
||||
class ChildProcess extends CallbackEmitter implements cp.ChildProcess {
|
||||
private _connected: boolean = false;
|
||||
private _killed: boolean = false;
|
||||
private _pid = -1;
|
||||
public readonly stdin: stream.Writable;
|
||||
public readonly stdout: stream.Readable;
|
||||
public readonly stderr: stream.Readable;
|
||||
// We need the explicit type otherwise TypeScript thinks it is (Writable | Readable)[].
|
||||
public readonly stdio: [stream.Writable, stream.Readable, stream.Readable] = [this.stdin, this.stdout, this.stderr];
|
||||
|
||||
// tslint:disable no-any
|
||||
public constructor(method: "exec", command: string, options?: { encoding?: string | null } & cp.ExecOptions | null, callback?: (...args: any[]) => void);
|
||||
public constructor(method: "fork", modulePath: string, options?: cp.ForkOptions, args?: string[]);
|
||||
public constructor(method: "spawn", command: string, options?: cp.SpawnOptions, args?: string[]);
|
||||
public constructor(method: "exec" | "spawn" | "fork", command: string, options: object = {}, callback?: string[] | ((...args: any[]) => void)) {
|
||||
// tslint:enable no-any
|
||||
super();
|
||||
|
||||
let args: string[] = [];
|
||||
if (Array.isArray(callback)) {
|
||||
args = callback;
|
||||
callback = undefined;
|
||||
}
|
||||
|
||||
this.ae = client.run((ae, command, method, args, options, callbackId) => {
|
||||
const cp = __non_webpack_require__("child_process") as typeof import("child_process");
|
||||
|
||||
ae.preserveEnv(options);
|
||||
|
||||
let childProcess: cp.ChildProcess;
|
||||
switch (method) {
|
||||
case "exec":
|
||||
childProcess = cp.exec(command, options, ae.maybeCallback(callbackId));
|
||||
break;
|
||||
case "spawn":
|
||||
childProcess = cp.spawn(command, args, options);
|
||||
break;
|
||||
case "fork":
|
||||
childProcess = ae.fork(command, args, options);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`invalid method ${method}`);
|
||||
}
|
||||
|
||||
ae.on("disconnect", () => childProcess.disconnect());
|
||||
ae.on("kill", (signal: string) => childProcess.kill(signal));
|
||||
ae.on("ref", () => childProcess.ref());
|
||||
ae.on("send", (message: string, callbackId: number) => childProcess.send(message, ae.maybeCallback(callbackId)));
|
||||
ae.on("unref", () => childProcess.unref());
|
||||
|
||||
ae.emit("pid", childProcess.pid);
|
||||
childProcess.on("close", (code, signal) => ae.emit("close", code, signal));
|
||||
childProcess.on("disconnect", () => ae.emit("disconnect"));
|
||||
childProcess.on("error", (error) => ae.emit("error", error));
|
||||
childProcess.on("exit", (code, signal) => ae.emit("exit", code, signal));
|
||||
childProcess.on("message", (message) => ae.emit("message", message));
|
||||
|
||||
if (childProcess.stdin) {
|
||||
const stdinAe = ae.createUnique("stdin");
|
||||
stdinAe.bindWritable(childProcess.stdin);
|
||||
}
|
||||
if (childProcess.stdout) {
|
||||
const stdoutAe = ae.createUnique("stdout");
|
||||
stdoutAe.bindReadable(childProcess.stdout);
|
||||
}
|
||||
if (childProcess.stderr) {
|
||||
const stderrAe = ae.createUnique("stderr");
|
||||
stderrAe.bindReadable(childProcess.stderr);
|
||||
}
|
||||
|
||||
return {
|
||||
onDidDispose: (cb): cp.ChildProcess => childProcess.on("close", cb),
|
||||
dispose: (): void => {
|
||||
childProcess.kill();
|
||||
setTimeout(() => childProcess.kill("SIGKILL"), 5000); // Double tap.
|
||||
},
|
||||
};
|
||||
}, command, method, args, options, this.storeCallback(callback));
|
||||
|
||||
this.ae.on("pid", (pid) => {
|
||||
this._pid = pid;
|
||||
this._connected = true;
|
||||
});
|
||||
|
||||
this.stdin = new ActiveEvalWritable(this.ae.createUnique("stdin"));
|
||||
this.stdout = new ActiveEvalReadable(this.ae.createUnique("stdout"));
|
||||
this.stderr = new ActiveEvalReadable(this.ae.createUnique("stderr"));
|
||||
|
||||
this.ae.on("close", (code, signal) => this.emit("close", code, signal));
|
||||
this.ae.on("disconnect", () => this.emit("disconnect"));
|
||||
this.ae.on("error", (error) => this.emit("error", error));
|
||||
this.ae.on("exit", (code, signal) => {
|
||||
this._connected = false;
|
||||
this._killed = true;
|
||||
this.emit("exit", code, signal);
|
||||
});
|
||||
this.ae.on("message", (message) => this.emit("message", message));
|
||||
}
|
||||
|
||||
public get pid(): number { return this._pid; }
|
||||
public get connected(): boolean { return this._connected; }
|
||||
public get killed(): boolean { return this._killed; }
|
||||
|
||||
public kill(): void { this.ae.emit("kill"); }
|
||||
public disconnect(): void { this.ae.emit("disconnect"); }
|
||||
public ref(): void { this.ae.emit("ref"); }
|
||||
public unref(): void { this.ae.emit("unref"); }
|
||||
|
||||
public send(
|
||||
message: any, // tslint:disable-line no-any to match spec
|
||||
sendHandle?: net.Socket | net.Server | ((error: Error) => void),
|
||||
options?: cp.MessageOptions | ((error: Error) => void),
|
||||
callback?: (error: Error) => void): boolean {
|
||||
if (typeof sendHandle === "function") {
|
||||
callback = sendHandle;
|
||||
sendHandle = undefined;
|
||||
} else if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
if (sendHandle || options) {
|
||||
throw new Error("sendHandle and options are not supported");
|
||||
}
|
||||
this.ae.emit("send", message, this.storeCallback(callback));
|
||||
|
||||
// Unfortunately this will always have to be true since we can't retrieve
|
||||
// the actual response synchronously.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class CP {
|
||||
public readonly ChildProcess = ChildProcess;
|
||||
|
||||
public exec = (
|
||||
command: string,
|
||||
options?: { encoding?: string | null } & cp.ExecOptions | null | ((error: cp.ExecException | null, stdout: string, stderr: string) => void) | ((error: cp.ExecException | null, stdout: Buffer, stderr: Buffer) => void),
|
||||
callback?: ((error: cp.ExecException | null, stdout: string, stderr: string) => void) | ((error: cp.ExecException | null, stdout: Buffer, stderr: Buffer) => void),
|
||||
): cp.ChildProcess => {
|
||||
if (typeof options === "function") {
|
||||
callback = options;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
return new ChildProcess("exec", command, options, callback);
|
||||
}
|
||||
|
||||
public fork = (modulePath: string, args?: string[] | cp.ForkOptions, options?: cp.ForkOptions): cp.ChildProcess => {
|
||||
if (args && !Array.isArray(args)) {
|
||||
options = args;
|
||||
args = undefined;
|
||||
}
|
||||
|
||||
return new ChildProcess("fork", modulePath, options, args);
|
||||
}
|
||||
|
||||
public spawn = (command: string, args?: string[] | cp.SpawnOptions, options?: cp.SpawnOptions): cp.ChildProcess => {
|
||||
if (args && !Array.isArray(args)) {
|
||||
options = args;
|
||||
args = undefined;
|
||||
}
|
||||
|
||||
return new ChildProcess("spawn", command, options, args);
|
||||
}
|
||||
}
|
||||
|
||||
const fillCp = new CP();
|
||||
// Methods that don't follow the standard callback pattern (an error followed
|
||||
// by a single result) need to provide a custom promisify function.
|
||||
Object.defineProperty(fillCp.exec, promisify.custom, {
|
||||
value: (
|
||||
command: string,
|
||||
options?: { encoding?: string | null } & cp.ExecOptions | null,
|
||||
): Promise<{ stdout: string | Buffer, stderr: string | Buffer }> => {
|
||||
return new Promise((resolve, reject): void => {
|
||||
fillCp.exec(command, options, (error: cp.ExecException | null, stdout: string | Buffer, stderr: string | Buffer) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve({ stdout, stderr });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
export = fillCp;
|
||||
export = client.modules[Module.ChildProcess];
|
||||
|
@ -61,7 +61,12 @@ class WebsocketConnection implements ReadWriteConnection {
|
||||
socket.addEventListener("close", (event) => {
|
||||
if (this.isUp) {
|
||||
this.isUp = false;
|
||||
this.downEmitter.emit(undefined);
|
||||
try {
|
||||
this.downEmitter.emit(undefined);
|
||||
} catch (error) {
|
||||
// Don't let errors here prevent restarting.
|
||||
logger.error(error.message);
|
||||
}
|
||||
}
|
||||
logger.warn(
|
||||
"Web socket closed",
|
||||
|
@ -1,12 +1,10 @@
|
||||
/// <reference path="../../../../lib/vscode/src/typings/electron.d.ts" />
|
||||
import { EventEmitter } from "events";
|
||||
import * as fs from "fs";
|
||||
import * as trash from "trash";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { IKey, Dialog as DialogBox } from "./dialog";
|
||||
import { clipboard } from "./clipboard";
|
||||
import { client } from "./client";
|
||||
|
||||
declare var __non_webpack_require__: typeof require;
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
(global as any).getOpenUrls = (): string[] => {
|
||||
@ -184,9 +182,7 @@ class Clipboard {
|
||||
|
||||
class Shell {
|
||||
public async moveItemToTrash(path: string): Promise<void> {
|
||||
await client.evaluate((helper, path) => {
|
||||
return helper.modules.trash(path);
|
||||
}, path);
|
||||
await trash(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,763 +1,4 @@
|
||||
import { EventEmitter } from "events";
|
||||
import * as fs from "fs";
|
||||
import * as stream from "stream";
|
||||
import { Client, IEncodingOptions, IEncodingOptionsCallback } from "@coder/protocol";
|
||||
import { Module } from "@coder/protocol";
|
||||
import { client } from "./client";
|
||||
import { promisify } from "util";
|
||||
|
||||
declare var __non_webpack_require__: typeof require;
|
||||
declare var _Buffer: typeof Buffer;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
class FS {
|
||||
public constructor(
|
||||
private readonly client: Client,
|
||||
) { }
|
||||
|
||||
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((_helper, path, mode) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.access)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
// 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((_helper, path, data, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.appendFile)(path, data, options);
|
||||
}, file, data, options).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public chmod = (path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, path, mode) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.chmod)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public chown = (path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, path, uid, gid) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.chown)(path, uid, gid);
|
||||
}, path, uid, gid).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public close = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, fd) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.close)(fd);
|
||||
}, fd).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, src, dest, flags) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_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);
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public createWriteStream = (path: fs.PathLike, options?: any): fs.WriteStream => {
|
||||
const ae = this.client.run((ae, path, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const str = fs.createWriteStream(path, options);
|
||||
ae.on("write", (d: string) => str.write(_Buffer.from(d, "utf8")));
|
||||
ae.on("close", () => str.close());
|
||||
ae.on("destroy", () => str.destroy());
|
||||
str.on("close", () => ae.emit("close"));
|
||||
str.on("open", (fd) => ae.emit("open", fd));
|
||||
str.on("error", (err) => ae.emit(err));
|
||||
|
||||
return {
|
||||
onDidDispose: (cb): fs.WriteStream => str.on("close", cb),
|
||||
dispose: (): void => str.close(),
|
||||
};
|
||||
}, path, options);
|
||||
|
||||
return new (class WriteStream extends stream.Writable implements fs.WriteStream {
|
||||
|
||||
private _bytesWritten: number = 0;
|
||||
|
||||
public constructor() {
|
||||
super({
|
||||
write: (data, encoding, cb): void => {
|
||||
this._bytesWritten += data.length;
|
||||
ae.emit("write", Buffer.from(data, encoding), encoding);
|
||||
cb();
|
||||
},
|
||||
});
|
||||
|
||||
ae.on("open", (fd: number) => this.emit("open", fd));
|
||||
ae.on("close", () => this.emit("close"));
|
||||
}
|
||||
|
||||
public get bytesWritten(): number {
|
||||
return this._bytesWritten;
|
||||
}
|
||||
|
||||
public get path(): string | Buffer {
|
||||
return "";
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
ae.emit("close");
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
ae.emit("destroy");
|
||||
}
|
||||
|
||||
}) as fs.WriteStream;
|
||||
}
|
||||
|
||||
public exists = (path: fs.PathLike, callback: (exists: boolean) => void): void => {
|
||||
this.client.evaluate((_helper, path) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.exists)(path);
|
||||
}, path).then((r) => {
|
||||
callback(r);
|
||||
}).catch(() => {
|
||||
callback(false);
|
||||
});
|
||||
}
|
||||
|
||||
public fchmod = (fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, fd, mode) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fchmod)(fd, mode);
|
||||
}, fd, mode).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public fchown = (fd: number, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, fd, uid, gid) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fchown)(fd, uid, gid);
|
||||
}, fd, uid, gid).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public fdatasync = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, fd) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fdatasync)(fd);
|
||||
}, fd).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public fstat = (fd: number, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
|
||||
this.client.evaluate((_helper, fd) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
const tslib = __non_webpack_require__("tslib") as typeof import("tslib");
|
||||
|
||||
return util.promisify(fs.fstat)(fd).then((stats) => {
|
||||
return tslib.__assign(stats, {
|
||||
_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,
|
||||
});
|
||||
});
|
||||
}, fd).then((stats) => {
|
||||
callback(undefined!, new Stats(stats));
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public fsync = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, fd) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.fsync)(fd);
|
||||
}, fd).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, fd, len) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.ftruncate)(fd, len);
|
||||
}, fd, len).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public futimes = (fd: number, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, fd, atime, mtime) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.futimes)(fd, atime, mtime);
|
||||
}, fd, atime, mtime).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public lchmod = (path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, path, mode) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.lchmod)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public lchown = (path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, path, uid, gid) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.lchown)(path, uid, gid);
|
||||
}, path, uid, gid).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public link = (existingPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, existingPath, newPath) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.link)(existingPath, newPath);
|
||||
}, existingPath, newPath).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public lstat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
|
||||
this.client.evaluate((_helper, path) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
const tslib = __non_webpack_require__("tslib") as typeof import("tslib");
|
||||
|
||||
return util.promisify(fs.lstat)(path).then((stats) => {
|
||||
return tslib.__assign(stats, {
|
||||
_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,
|
||||
});
|
||||
});
|
||||
}, path).then((stats) => {
|
||||
callback(undefined!, new Stats(stats));
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, path, mode) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.mkdir)(path, mode);
|
||||
}, path, mode).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, prefix, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.mkdtemp)(prefix, options);
|
||||
}, prefix, options).then((folder) => {
|
||||
callback!(undefined!, folder);
|
||||
}).catch((ex) => {
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, path, flags, mode) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.open)(path, flags, mode);
|
||||
}, path, flags, mode).then((fd) => {
|
||||
callback!(undefined!, fd);
|
||||
}).catch((ex) => {
|
||||
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((_helper, fd, length, position) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
const buffer = new _Buffer(length);
|
||||
|
||||
return util.promisify(fs.read)(fd, buffer, 0, length, position).then((resp) => {
|
||||
return {
|
||||
bytesRead: resp.bytesRead,
|
||||
content: resp.bytesRead < buffer.length ? buffer.slice(0, resp.bytesRead) : buffer,
|
||||
};
|
||||
});
|
||||
}, fd, length, position).then((resp) => {
|
||||
buffer.set(resp.content, offset);
|
||||
callback(undefined!, resp.bytesRead, resp.content as TBuffer);
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, path, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.readFile)(path, options).then((value) => value.toString());
|
||||
}, path, options).then((buffer) => {
|
||||
callback!(undefined!, buffer);
|
||||
}).catch((ex) => {
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, path, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.readdir)(path, options);
|
||||
}, path, options).then((files) => {
|
||||
callback!(undefined!, files);
|
||||
}).catch((ex) => {
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, path, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.readlink)(path, options);
|
||||
}, path, options).then((linkString) => {
|
||||
callback!(undefined!, linkString);
|
||||
}).catch((ex) => {
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, path, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.realpath)(path, options);
|
||||
}, path, options).then((resolvedPath) => {
|
||||
callback!(undefined!, resolvedPath);
|
||||
}).catch((ex) => {
|
||||
callback!(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
public rename = (oldPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, oldPath, newPath) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.rename)(oldPath, newPath);
|
||||
}, oldPath, newPath).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public rmdir = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, path) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.rmdir)(path);
|
||||
}, path).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public stat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
|
||||
this.client.evaluate((_helper, path) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
const tslib = __non_webpack_require__("tslib") as typeof import("tslib");
|
||||
|
||||
return util.promisify(fs.stat)(path).then((stats) => {
|
||||
return tslib.__assign(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,
|
||||
});
|
||||
});
|
||||
}, path).then((stats) => {
|
||||
callback(undefined!, new Stats(stats));
|
||||
}).catch((ex) => {
|
||||
callback(ex, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, target, path, type) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.symlink)(target, path, type);
|
||||
}, target, path, type).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
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((_helper, path, len) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.truncate)(path, len);
|
||||
}, path, len).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public unlink = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, path) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.unlink)(path);
|
||||
}, path).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public utimes = (path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void => {
|
||||
this.client.evaluate((_helper, path, atime, mtime) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.utimes)(path, atime, mtime);
|
||||
}, path, atime, mtime).then(() => {
|
||||
callback(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public write = <TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined | ((err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void), length: number | undefined | ((err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void), position: number | undefined | ((err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void), callback?: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void => {
|
||||
if (typeof offset === "function") {
|
||||
callback = offset;
|
||||
offset = undefined;
|
||||
}
|
||||
if (typeof length === "function") {
|
||||
callback = length;
|
||||
length = undefined;
|
||||
}
|
||||
if (typeof position === "function") {
|
||||
callback = position;
|
||||
position = undefined;
|
||||
}
|
||||
this.client.evaluate((_helper, fd, buffer, offset, length, position) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_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);
|
||||
}).catch((ex) => {
|
||||
callback!(ex, undefined!, undefined!);
|
||||
});
|
||||
}
|
||||
|
||||
// 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((_helper, path, data, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import("fs");
|
||||
const util = __non_webpack_require__("util") as typeof import("util");
|
||||
|
||||
return util.promisify(fs.writeFile)(path, data, options);
|
||||
}, path, data, options).then(() => {
|
||||
callback!(undefined!);
|
||||
}).catch((ex) => {
|
||||
callback!(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public watch = (filename: fs.PathLike, options?: IEncodingOptions | ((event: string, filename: string | Buffer) => void), listener?: ((event: string, filename: string | Buffer) => void)): fs.FSWatcher => {
|
||||
if (typeof options === "function") {
|
||||
listener = options;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
const ae = this.client.run((ae, filename, hasListener, options) => {
|
||||
const fs = __non_webpack_require__("fs") as typeof import ("fs");
|
||||
// tslint:disable-next-line no-any
|
||||
const watcher = fs.watch(filename, options as any, hasListener ? (event, filename): void => {
|
||||
ae.emit("listener", event, filename);
|
||||
} : undefined);
|
||||
watcher.on("change", (event, filename) => ae.emit("change", event, filename));
|
||||
watcher.on("error", (error) => ae.emit("error", error));
|
||||
ae.on("close", () => watcher.close());
|
||||
|
||||
return {
|
||||
onDidDispose: (cb): void => ae.on("close", cb),
|
||||
dispose: (): void => watcher.close(),
|
||||
};
|
||||
}, filename.toString(), !!listener, options);
|
||||
|
||||
return new class Watcher extends EventEmitter implements fs.FSWatcher {
|
||||
public constructor() {
|
||||
super();
|
||||
ae.on("change", (event: string, filename: string) => this.emit("change", event, filename));
|
||||
ae.on("error", (error: Error) => this.emit("error", error));
|
||||
ae.on("listener", (event: string, filename: string) => listener && listener(event, filename));
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
ae.emit("close");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface IStats {
|
||||
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;
|
||||
}
|
||||
|
||||
class Stats implements fs.Stats {
|
||||
public readonly atime: Date;
|
||||
public readonly mtime: Date;
|
||||
public readonly ctime: Date;
|
||||
public readonly birthtime: Date;
|
||||
|
||||
public constructor(private readonly stats: IStats) {
|
||||
this.atime = new Date(stats.atime);
|
||||
this.mtime = new Date(stats.mtime);
|
||||
this.ctime = new Date(stats.ctime);
|
||||
this.birthtime = new Date(stats.birthtime);
|
||||
}
|
||||
|
||||
public get dev(): number { return this.stats.dev; }
|
||||
public get ino(): number { return this.stats.ino; }
|
||||
public get mode(): number { return this.stats.mode; }
|
||||
public get nlink(): number { return this.stats.nlink; }
|
||||
public get uid(): number { return this.stats.uid; }
|
||||
public get gid(): number { return this.stats.gid; }
|
||||
public get rdev(): number { return this.stats.rdev; }
|
||||
public get size(): number { return this.stats.size; }
|
||||
public get blksize(): number { return this.stats.blksize; }
|
||||
public get blocks(): number { return this.stats.blocks; }
|
||||
public get atimeMs(): number { return this.stats.atimeMs; }
|
||||
public get mtimeMs(): number { return this.stats.mtimeMs; }
|
||||
public get ctimeMs(): number { return this.stats.ctimeMs; }
|
||||
public get birthtimeMs(): number { return this.stats.birthtimeMs; }
|
||||
public isFile(): boolean { return this.stats._isFile; }
|
||||
public isDirectory(): boolean { return this.stats._isDirectory; }
|
||||
public isBlockDevice(): boolean { return this.stats._isBlockDevice; }
|
||||
public isCharacterDevice(): boolean { return this.stats._isCharacterDevice; }
|
||||
public isSymbolicLink(): boolean { return this.stats._isSymbolicLink; }
|
||||
public isFIFO(): boolean { return this.stats._isFIFO; }
|
||||
public isSocket(): boolean { return this.stats._isSocket; }
|
||||
|
||||
public toObject(): object {
|
||||
return JSON.parse(JSON.stringify(this));
|
||||
}
|
||||
}
|
||||
|
||||
const fillFs = new FS(client);
|
||||
// Methods that don't follow the standard callback pattern (an error followed
|
||||
// by a single result) need to provide a custom promisify function.
|
||||
Object.defineProperty(fillFs.exists, promisify.custom, {
|
||||
value: (path: fs.PathLike): Promise<boolean> => new Promise((resolve): void => fillFs.exists(path, resolve)),
|
||||
});
|
||||
export = fillFs;
|
||||
export = client.modules[Module.Fs];
|
||||
|
@ -1,258 +1,4 @@
|
||||
import * as net from "net";
|
||||
import { CallbackEmitter, ActiveEvalDuplex, ActiveEvalHelper } from "@coder/protocol";
|
||||
import { Module } from "@coder/protocol";
|
||||
import { client } from "./client";
|
||||
|
||||
declare var __non_webpack_require__: typeof require;
|
||||
|
||||
class Socket extends ActiveEvalDuplex implements net.Socket {
|
||||
private _connecting: boolean = false;
|
||||
private _destroyed: boolean = false;
|
||||
|
||||
public constructor(options?: net.SocketConstructorOpts, ae?: ActiveEvalHelper) {
|
||||
super(ae || client.run((ae, options) => {
|
||||
const net = __non_webpack_require__("net") as typeof import("net");
|
||||
|
||||
return ae.bindSocket(new net.Socket(options));
|
||||
}, options));
|
||||
|
||||
this.ae.on("connect", () => {
|
||||
this._connecting = false;
|
||||
this.emit("connect");
|
||||
});
|
||||
this.ae.on("error", () => {
|
||||
this._connecting = false;
|
||||
this._destroyed = true;
|
||||
});
|
||||
this.ae.on("lookup", (error, address, family, host) => this.emit("lookup", error, address, family, host));
|
||||
this.ae.on("timeout", () => this.emit("timeout"));
|
||||
}
|
||||
|
||||
public connect(options: net.SocketConnectOpts | number | string, host?: string | Function, connectionListener?: Function): this {
|
||||
// This is to get around type issues with socket.connect as well as extract
|
||||
// the function wherever it might be.
|
||||
switch (typeof options) {
|
||||
case "string": options = { path: options }; break;
|
||||
case "number": options = { port: options }; break;
|
||||
}
|
||||
switch (typeof host) {
|
||||
case "function": connectionListener = host; break;
|
||||
case "string": (options as net.TcpSocketConnectOpts).host = host; break;
|
||||
}
|
||||
|
||||
this._connecting = true;
|
||||
this.ae.emit("connect", options, this.storeCallback(connectionListener));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public write(data: any, encoding?: string | Function, fd?: string | Function): boolean {
|
||||
let callback: Function | undefined;
|
||||
if (typeof encoding === "function") {
|
||||
callback = encoding;
|
||||
encoding = undefined;
|
||||
}
|
||||
if (typeof fd === "function") {
|
||||
callback = fd;
|
||||
fd = undefined;
|
||||
}
|
||||
this.ae.emit("write", data, encoding, fd, this.storeCallback(callback));
|
||||
|
||||
return true; // Always true since we can't get this synchronously.
|
||||
}
|
||||
|
||||
public get connecting(): boolean { return this._connecting; }
|
||||
public get destroyed(): boolean { return this._destroyed; }
|
||||
|
||||
public get bufferSize(): number { throw new Error("not implemented"); }
|
||||
public get bytesRead(): number { throw new Error("not implemented"); }
|
||||
public get bytesWritten(): number { throw new Error("not implemented"); }
|
||||
public get localAddress(): string { throw new Error("not implemented"); }
|
||||
public get localPort(): number { throw new Error("not implemented"); }
|
||||
public address(): net.AddressInfo | string { throw new Error("not implemented"); }
|
||||
|
||||
public setTimeout(timeout: number, callback?: Function): this { return this.emitReturnThis("setTimeout", timeout, this.storeCallback(callback)); }
|
||||
public setNoDelay(noDelay?: boolean): this { return this.emitReturnThis("setNoDelay", noDelay); }
|
||||
public setKeepAlive(enable?: boolean, initialDelay?: number): this { return this.emitReturnThis("setKeepAlive", enable, initialDelay); }
|
||||
public unref(): void { this.ae.emit("unref"); }
|
||||
public ref(): void { this.ae.emit("ref"); }
|
||||
}
|
||||
|
||||
class Server extends CallbackEmitter implements net.Server {
|
||||
private readonly sockets = new Map<number, Socket>();
|
||||
private _listening: boolean = false;
|
||||
|
||||
public constructor(options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean } | ((socket: Socket) => void), connectionListener?: (socket: Socket) => void) {
|
||||
super();
|
||||
|
||||
if (typeof options === "function") {
|
||||
connectionListener = options;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
this.ae = client.run((ae, options, callbackId) => {
|
||||
const net = __non_webpack_require__("net") as typeof import("net");
|
||||
|
||||
let connectionId = 0;
|
||||
const sockets = new Map<number, net.Socket>();
|
||||
const storeSocket = (socket: net.Socket): number => {
|
||||
const socketId = connectionId++;
|
||||
sockets.set(socketId, socket);
|
||||
const socketAe = ae.createUnique(socketId);
|
||||
const disposer = socketAe.bindSocket(socket);
|
||||
socket.on("close", () => {
|
||||
disposer.dispose();
|
||||
sockets.delete(socketId);
|
||||
});
|
||||
|
||||
return socketId;
|
||||
};
|
||||
|
||||
const callback = ae.maybeCallback(callbackId);
|
||||
let server = new net.Server(options, typeof callback !== "undefined" ? (socket): void => {
|
||||
callback(storeSocket(socket));
|
||||
} : undefined);
|
||||
|
||||
server.on("close", () => ae.emit("close"));
|
||||
server.on("connection", (socket) => ae.emit("connection", storeSocket(socket)));
|
||||
server.on("error", (error) => ae.emit("error", error));
|
||||
server.on("listening", () => ae.emit("listening"));
|
||||
|
||||
ae.on("close", (callbackId: number) => server.close(ae.maybeCallback(callbackId)));
|
||||
ae.on("listen", (handle?: net.ListenOptions | number | string) => server.listen(handle));
|
||||
ae.on("ref", () => server.ref());
|
||||
ae.on("unref", () => server.unref());
|
||||
|
||||
return {
|
||||
onDidDispose: (cb): net.Server => server.on("close", cb),
|
||||
dispose: (): void => {
|
||||
server.removeAllListeners();
|
||||
server.close();
|
||||
sockets.forEach((socket) => {
|
||||
socket.removeAllListeners();
|
||||
socket.end();
|
||||
socket.destroy();
|
||||
socket.unref();
|
||||
});
|
||||
sockets.clear();
|
||||
},
|
||||
};
|
||||
}, options || {}, this.storeCallback(connectionListener));
|
||||
|
||||
this.ae.on("close", () => {
|
||||
this._listening = false;
|
||||
this.emit("close");
|
||||
});
|
||||
|
||||
this.ae.on("connection", (socketId) => {
|
||||
const socketAe = this.ae.createUnique(socketId);
|
||||
const socket = new Socket(undefined, socketAe);
|
||||
this.sockets.set(socketId, socket);
|
||||
socket.on("close", () => this.sockets.delete(socketId));
|
||||
if (connectionListener) {
|
||||
connectionListener(socket);
|
||||
}
|
||||
this.emit("connection", socket);
|
||||
});
|
||||
|
||||
this.ae.on("error", (error) => {
|
||||
this._listening = false;
|
||||
this.emit("error", error);
|
||||
});
|
||||
|
||||
this.ae.on("listening", () => {
|
||||
this._listening = true;
|
||||
this.emit("listening");
|
||||
});
|
||||
}
|
||||
|
||||
public listen(handle?: net.ListenOptions | number | string, hostname?: string | number | Function, backlog?: number | Function, listeningListener?: Function): this {
|
||||
if (typeof handle === "undefined") {
|
||||
throw new Error("no handle");
|
||||
}
|
||||
|
||||
switch (typeof handle) {
|
||||
case "number": handle = { port: handle }; break;
|
||||
case "string": handle = { path: handle }; break;
|
||||
}
|
||||
switch (typeof hostname) {
|
||||
case "function": listeningListener = hostname; break;
|
||||
case "string": handle.host = hostname; break;
|
||||
case "number": handle.backlog = hostname; break;
|
||||
}
|
||||
switch (typeof backlog) {
|
||||
case "function": listeningListener = backlog; break;
|
||||
case "number": handle.backlog = backlog; break;
|
||||
}
|
||||
|
||||
if (listeningListener) {
|
||||
this.ae.on("listening", () => {
|
||||
listeningListener!();
|
||||
});
|
||||
}
|
||||
|
||||
this.ae.emit("listen", handle);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public close(callback?: Function): this {
|
||||
// close() doesn't fire the close event until all connections are also
|
||||
// closed, but it does prevent new connections.
|
||||
this._listening = false;
|
||||
this.ae.emit("close", this.storeCallback(callback));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public get connections(): number { return this.sockets.size; }
|
||||
public get listening(): boolean { return this._listening; }
|
||||
|
||||
public get maxConnections(): number { throw new Error("not implemented"); }
|
||||
public address(): net.AddressInfo | string { throw new Error("not implemented"); }
|
||||
|
||||
public ref(): this { return this.emitReturnThis("ref"); }
|
||||
public unref(): this { return this.emitReturnThis("unref"); }
|
||||
public getConnections(cb: (error: Error | null, count: number) => void): void { cb(null, this.sockets.size); }
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
private emitReturnThis(event: string, ...args: any[]): this {
|
||||
this.ae.emit(event, ...args);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
type NodeNet = typeof net;
|
||||
|
||||
/**
|
||||
* Implementation of net for the browser.
|
||||
*/
|
||||
class Net implements NodeNet {
|
||||
// @ts-ignore this is because Socket is missing things from the Stream
|
||||
// namespace but I'm unsure how best to provide them (finished,
|
||||
// finished.__promisify__, pipeline, and some others) or if it even matters.
|
||||
public readonly Socket = Socket;
|
||||
public readonly Server = Server;
|
||||
|
||||
public createConnection(target: string | number | net.NetConnectOpts, host?: string | Function, callback?: Function): net.Socket {
|
||||
const socket = new Socket();
|
||||
socket.connect(target, host, callback);
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
public createServer(
|
||||
options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean } | ((socket: net.Socket) => void),
|
||||
connectionListener?: (socket: net.Socket) => void,
|
||||
): net.Server {
|
||||
return new Server(options, connectionListener);
|
||||
}
|
||||
|
||||
public connect(): net.Socket { throw new Error("not implemented"); }
|
||||
public isIP(_input: string): number { throw new Error("not implemented"); }
|
||||
public isIPv4(_input: string): boolean { throw new Error("not implemented"); }
|
||||
public isIPv6(_input: string): boolean { throw new Error("not implemented"); }
|
||||
}
|
||||
|
||||
export = new Net();
|
||||
export = client.modules[Module.Net];
|
||||
|
4
packages/ide/src/fill/trash.ts
Normal file
4
packages/ide/src/fill/trash.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { Module } from "@coder/protocol";
|
||||
import { client } from "./client";
|
||||
|
||||
export = client.modules[Module.Trash].trash;
|
@ -1,74 +0,0 @@
|
||||
import { ChildProcess } from "child_process";
|
||||
import * as path from "path";
|
||||
import { Readable } from "stream";
|
||||
import * as util from "util";
|
||||
import { createClient } from "@coder/protocol/test";
|
||||
|
||||
const client = createClient();
|
||||
jest.mock("../src/fill/client", () => ({ client }));
|
||||
const cp = require("../src/fill/child_process") as typeof import("child_process");
|
||||
|
||||
describe("child_process", () => {
|
||||
const getStdout = async (proc: ChildProcess): Promise<string> => {
|
||||
return new Promise((r): Readable => proc.stdout.on("data", r))
|
||||
.then((s) => s.toString());
|
||||
};
|
||||
|
||||
describe("exec", () => {
|
||||
it("should get exec stdout", async () => {
|
||||
await expect(util.promisify(cp.exec)("echo test", { encoding: "utf8" }))
|
||||
.resolves.toEqual({
|
||||
stdout: "test\n",
|
||||
stderr: "",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("spawn", () => {
|
||||
it("should get spawn stdout", async () => {
|
||||
const proc = cp.spawn("echo", ["test"]);
|
||||
await expect(Promise.all([
|
||||
getStdout(proc),
|
||||
new Promise((r): ChildProcess => proc.on("exit", r)),
|
||||
]).then((values) => values[0])).resolves.toEqual("test\n");
|
||||
});
|
||||
|
||||
it("should cat", async () => {
|
||||
const proc = cp.spawn("cat", []);
|
||||
expect(proc.pid).toBe(-1);
|
||||
proc.stdin.write("banana");
|
||||
await expect(getStdout(proc)).resolves.toBe("banana");
|
||||
|
||||
proc.stdin.end();
|
||||
proc.kill();
|
||||
|
||||
expect(proc.pid).toBeGreaterThan(-1);
|
||||
await new Promise((r): ChildProcess => proc.on("exit", r));
|
||||
});
|
||||
|
||||
it("should print env", async () => {
|
||||
const proc = cp.spawn("env", [], {
|
||||
env: { hi: "donkey" },
|
||||
});
|
||||
|
||||
await expect(getStdout(proc)).resolves.toContain("hi=donkey\n");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fork", () => {
|
||||
it("should echo messages", async () => {
|
||||
const proc = cp.fork(path.join(__dirname, "forker.js"));
|
||||
|
||||
proc.send({ bananas: true });
|
||||
|
||||
await expect(new Promise((r): ChildProcess => proc.on("message", r)))
|
||||
.resolves.toMatchObject({
|
||||
bananas: true,
|
||||
});
|
||||
|
||||
proc.kill();
|
||||
|
||||
await new Promise((r): ChildProcess => proc.on("exit", r));
|
||||
});
|
||||
});
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
process.on("message", (data) => {
|
||||
process.send(data);
|
||||
});
|
@ -1,592 +0,0 @@
|
||||
import * as nativeFs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import * as util from "util";
|
||||
import * as rimraf from "rimraf";
|
||||
import { createClient } from "@coder/protocol/test";
|
||||
|
||||
const client = createClient();
|
||||
jest.mock("../src/fill/client", () => ({ client }));
|
||||
const fs = require("../src/fill/fs") as typeof import("fs");
|
||||
|
||||
describe("fs", () => {
|
||||
let i = 0;
|
||||
const coderDir = path.join(os.tmpdir(), "coder", "fs");
|
||||
const testFile = path.join(__dirname, "fs.test.ts");
|
||||
const tmpFile = (): string => path.join(coderDir, `${i++}`);
|
||||
const createTmpFile = async (): Promise<string> => {
|
||||
const tf = tmpFile();
|
||||
await util.promisify(nativeFs.writeFile)(tf, "");
|
||||
|
||||
return tf;
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
await util.promisify(nativeFs.mkdir)(path.dirname(coderDir));
|
||||
} catch (error) {
|
||||
if (error.code !== "EEXIST" && error.code !== "EISDIR") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
await util.promisify(rimraf)(coderDir);
|
||||
await util.promisify(nativeFs.mkdir)(coderDir);
|
||||
});
|
||||
|
||||
describe("access", () => {
|
||||
it("should access existing file", async () => {
|
||||
await expect(util.promisify(fs.access)(testFile))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to access nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.access)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("append", () => {
|
||||
it("should append to existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.appendFile)(file, "howdy"))
|
||||
.resolves.toBeUndefined();
|
||||
expect(await util.promisify(nativeFs.readFile)(file, "utf8"))
|
||||
.toEqual("howdy");
|
||||
});
|
||||
|
||||
it("should create then append to nonexistent file", async () => {
|
||||
const file = tmpFile();
|
||||
await expect(util.promisify(fs.appendFile)(file, "howdy"))
|
||||
.resolves.toBeUndefined();
|
||||
expect(await util.promisify(nativeFs.readFile)(file, "utf8"))
|
||||
.toEqual("howdy");
|
||||
});
|
||||
|
||||
it("should fail to append to file in nonexistent directory", async () => {
|
||||
const file = path.join(tmpFile(), "nope");
|
||||
await expect(util.promisify(fs.appendFile)(file, "howdy"))
|
||||
.rejects.toThrow("ENOENT");
|
||||
expect(await util.promisify(nativeFs.exists)(file))
|
||||
.toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("chmod", () => {
|
||||
it("should chmod existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.chmod)(file, "755"))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to chmod nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.chmod)(tmpFile(), "755"))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("chown", () => {
|
||||
it("should chown existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.chown)(file, 1, 1))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to chown nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.chown)(tmpFile(), 1, 1))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("close", () => {
|
||||
it("should close opened file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "r");
|
||||
await expect(util.promisify(fs.close)(fd))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to close non-opened file", async () => {
|
||||
await expect(util.promisify(fs.close)(99999999))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("copyFile", () => {
|
||||
it("should copy existing file", async () => {
|
||||
const source = await createTmpFile();
|
||||
const destination = tmpFile();
|
||||
await expect(util.promisify(fs.copyFile)(source, destination))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(util.promisify(fs.exists)(destination))
|
||||
.resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("should fail to copy nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.copyFile)(tmpFile(), tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("createWriteStream", () => {
|
||||
it("should write to file", async () => {
|
||||
const file = tmpFile();
|
||||
const content = "howdy\nhow\nr\nu";
|
||||
const stream = fs.createWriteStream(file);
|
||||
stream.on("open", (fd) => {
|
||||
expect(fd).toBeDefined();
|
||||
stream.write(content);
|
||||
stream.close();
|
||||
});
|
||||
await expect(new Promise((resolve): void => {
|
||||
stream.on("close", async () => {
|
||||
resolve(await util.promisify(nativeFs.readFile)(file, "utf8"));
|
||||
});
|
||||
})).resolves.toBe(content);
|
||||
});
|
||||
});
|
||||
|
||||
describe("exists", () => {
|
||||
it("should output file exists", async () => {
|
||||
await expect(util.promisify(fs.exists)(testFile))
|
||||
.resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("should output file does not exist", async () => {
|
||||
await expect(util.promisify(fs.exists)(tmpFile()))
|
||||
.resolves.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fchmod", () => {
|
||||
it("should fchmod existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "r");
|
||||
await expect(util.promisify(fs.fchmod)(fd, "755"))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to fchmod nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.fchmod)(2242342, "755"))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fchown", () => {
|
||||
it("should fchown existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "r");
|
||||
await expect(util.promisify(fs.fchown)(fd, 1, 1))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to fchown nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.fchown)(99999, 1, 1))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fdatasync", () => {
|
||||
it("should fdatasync existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "r");
|
||||
await expect(util.promisify(fs.fdatasync)(fd))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to fdatasync nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.fdatasync)(99999))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fstat", () => {
|
||||
it("should fstat existing file", async () => {
|
||||
const fd = await util.promisify(nativeFs.open)(testFile, "r");
|
||||
const stat = await util.promisify(nativeFs.fstat)(fd);
|
||||
await expect(util.promisify(fs.fstat)(fd))
|
||||
.resolves.toMatchObject({
|
||||
size: stat.size,
|
||||
});
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to fstat", async () => {
|
||||
await expect(util.promisify(fs.fstat)(9999))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("fsync", () => {
|
||||
it("should fsync existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "r");
|
||||
await expect(util.promisify(fs.fsync)(fd))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to fsync nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.fsync)(99999))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ftruncate", () => {
|
||||
it("should ftruncate existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "w");
|
||||
await expect(util.promisify(fs.ftruncate)(fd, 1))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to ftruncate nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.ftruncate)(99999, 9999))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("futimes", () => {
|
||||
it("should futimes existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "w");
|
||||
await expect(util.promisify(fs.futimes)(fd, 1, 1))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to futimes nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.futimes)(99999, 9999, 9999))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("lchmod", () => {
|
||||
it("should lchmod existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.lchmod)(file, "755"))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// TODO: Doesn't fail on my system?
|
||||
it("should fail to lchmod nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.lchmod)(tmpFile(), "755"))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("lchown", () => {
|
||||
it("should lchown existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.lchown)(file, 1, 1))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// TODO: Doesn't fail on my system?
|
||||
it("should fail to lchown nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.lchown)(tmpFile(), 1, 1))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("link", () => {
|
||||
it("should link existing file", async () => {
|
||||
const source = await createTmpFile();
|
||||
const destination = tmpFile();
|
||||
await expect(util.promisify(fs.link)(source, destination))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(util.promisify(fs.exists)(destination))
|
||||
.resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("should fail to link nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.link)(tmpFile(), tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("lstat", () => {
|
||||
it("should lstat existing file", async () => {
|
||||
const stat = await util.promisify(nativeFs.lstat)(testFile);
|
||||
await expect(util.promisify(fs.lstat)(testFile))
|
||||
.resolves.toMatchObject({
|
||||
size: stat.size,
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail to lstat non-existent file", async () => {
|
||||
await expect(util.promisify(fs.lstat)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("mkdir", () => {
|
||||
const target = tmpFile();
|
||||
it("should create nonexistent directory", async () => {
|
||||
await expect(util.promisify(fs.mkdir)(target))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to create existing directory", async () => {
|
||||
await expect(util.promisify(fs.mkdir)(target))
|
||||
.rejects.toThrow("EEXIST");
|
||||
});
|
||||
});
|
||||
|
||||
describe("mkdtemp", () => {
|
||||
it("should create temp dir", async () => {
|
||||
await expect(util.promisify(fs.mkdtemp)(coderDir + "/"))
|
||||
.resolves.toMatch(/^\/tmp\/coder\/fs\/[a-zA-Z0-9]{6}/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("open", () => {
|
||||
it("should open existing file", async () => {
|
||||
const fd = await util.promisify(fs.open)(testFile, "r");
|
||||
expect(fd).not.toBeNaN();
|
||||
await expect(util.promisify(fs.close)(fd))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to open nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.open)(tmpFile(), "r"))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("read", () => {
|
||||
it("should read existing file", async () => {
|
||||
const fd = await util.promisify(nativeFs.open)(testFile, "r");
|
||||
const stat = await util.promisify(nativeFs.fstat)(fd);
|
||||
const buffer = new Buffer(stat.size);
|
||||
let bytesRead = 0;
|
||||
let chunkSize = 2048;
|
||||
while (bytesRead < stat.size) {
|
||||
if ((bytesRead + chunkSize) > stat.size) {
|
||||
chunkSize = stat.size - bytesRead;
|
||||
}
|
||||
|
||||
await util.promisify(fs.read)(fd, buffer, bytesRead, chunkSize, bytesRead);
|
||||
bytesRead += chunkSize;
|
||||
}
|
||||
|
||||
const content = await util.promisify(nativeFs.readFile)(testFile, "utf8");
|
||||
expect(buffer.toString()).toEqual(content);
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to read nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.read)(99999, new Buffer(10), 9999, 999, 999))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("readFile", () => {
|
||||
it("should read existing file", async () => {
|
||||
const content = await util.promisify(nativeFs.readFile)(testFile, "utf8");
|
||||
await expect(util.promisify(fs.readFile)(testFile, "utf8"))
|
||||
.resolves.toEqual(content);
|
||||
});
|
||||
|
||||
it("should fail to read nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.readFile)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("readdir", () => {
|
||||
it("should read existing directory", async () => {
|
||||
const paths = await util.promisify(nativeFs.readdir)(coderDir);
|
||||
await expect(util.promisify(fs.readdir)(coderDir))
|
||||
.resolves.toEqual(paths);
|
||||
});
|
||||
|
||||
it("should fail to read nonexistent directory", async () => {
|
||||
await expect(util.promisify(fs.readdir)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("readlink", () => {
|
||||
it("should read existing link", async () => {
|
||||
const source = await createTmpFile();
|
||||
const destination = tmpFile();
|
||||
await util.promisify(nativeFs.symlink)(source, destination);
|
||||
await expect(util.promisify(fs.readlink)(destination))
|
||||
.resolves.toBe(source);
|
||||
});
|
||||
|
||||
it("should fail to read nonexistent link", async () => {
|
||||
await expect(util.promisify(fs.readlink)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("realpath", () => {
|
||||
it("should read real path of existing file", async () => {
|
||||
const source = await createTmpFile();
|
||||
const destination = tmpFile();
|
||||
nativeFs.symlinkSync(source, destination);
|
||||
await expect(util.promisify(fs.realpath)(destination))
|
||||
.resolves.toBe(source);
|
||||
});
|
||||
|
||||
it("should fail to read real path of nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.realpath)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("rename", () => {
|
||||
it("should rename existing file", async () => {
|
||||
const source = await createTmpFile();
|
||||
const destination = tmpFile();
|
||||
await expect(util.promisify(fs.rename)(source, destination))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(util.promisify(nativeFs.exists)(source))
|
||||
.resolves.toBe(false);
|
||||
await expect(util.promisify(nativeFs.exists)(destination))
|
||||
.resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("should fail to rename nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.rename)(tmpFile(), tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("rmdir", () => {
|
||||
it("should rmdir existing directory", async () => {
|
||||
const dir = tmpFile();
|
||||
await util.promisify(nativeFs.mkdir)(dir);
|
||||
await expect(util.promisify(fs.rmdir)(dir))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(util.promisify(nativeFs.exists)(dir))
|
||||
.resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("should fail to rmdir nonexistent directory", async () => {
|
||||
await expect(util.promisify(fs.rmdir)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("stat", () => {
|
||||
it("should stat existing file", async () => {
|
||||
const nativeStat = await util.promisify(nativeFs.stat)(testFile);
|
||||
const stat = await util.promisify(fs.stat)(testFile);
|
||||
expect(stat).toMatchObject({
|
||||
size: nativeStat.size,
|
||||
});
|
||||
expect(stat.isFile()).toBe(true);
|
||||
});
|
||||
|
||||
it("should stat existing folder", async () => {
|
||||
const dir = tmpFile();
|
||||
await util.promisify(nativeFs.mkdir)(dir);
|
||||
const nativeStat = await util.promisify(nativeFs.stat)(dir);
|
||||
const stat = await util.promisify(fs.stat)(dir);
|
||||
expect(stat).toMatchObject({
|
||||
size: nativeStat.size,
|
||||
});
|
||||
expect(stat.isDirectory()).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail to stat nonexistent file", async () => {
|
||||
const error = await util.promisify(fs.stat)(tmpFile()).catch((e) => e);
|
||||
expect(error.message).toContain("ENOENT");
|
||||
expect(error.code).toBe("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("symlink", () => {
|
||||
it("should symlink existing file", async () => {
|
||||
const source = await createTmpFile();
|
||||
const destination = tmpFile();
|
||||
await expect(util.promisify(fs.symlink)(source, destination))
|
||||
.resolves.toBeUndefined();
|
||||
expect(util.promisify(nativeFs.exists)(source))
|
||||
.resolves.toBe(true);
|
||||
});
|
||||
|
||||
// TODO: Seems to be happy to do this on my system?
|
||||
it("should fail to symlink nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.symlink)(tmpFile(), tmpFile()))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("truncate", () => {
|
||||
it("should truncate existing file", async () => {
|
||||
const file = tmpFile();
|
||||
await util.promisify(nativeFs.writeFile)(file, "hiiiiii");
|
||||
await expect(util.promisify(fs.truncate)(file, 2))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(util.promisify(nativeFs.readFile)(file, "utf8"))
|
||||
.resolves.toBe("hi");
|
||||
});
|
||||
|
||||
it("should fail to truncate nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.truncate)(tmpFile(), 0))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("unlink", () => {
|
||||
it("should unlink existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.unlink)(file))
|
||||
.resolves.toBeUndefined();
|
||||
expect(util.promisify(nativeFs.exists)(file))
|
||||
.resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("should fail to unlink nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.unlink)(tmpFile()))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("utimes", () => {
|
||||
it("should update times on existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.utimes)(file, 100, 100))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to update times on nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.utimes)(tmpFile(), 100, 100))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
describe("write", () => {
|
||||
it("should write to existing file", async () => {
|
||||
const file = await createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "w");
|
||||
await expect(util.promisify(fs.write)(fd, Buffer.from("hi")))
|
||||
.resolves.toBe(2);
|
||||
await expect(util.promisify(nativeFs.readFile)(file, "utf8"))
|
||||
.resolves.toBe("hi");
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to write to nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.write)(100000, Buffer.from("wowow")))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("writeFile", () => {
|
||||
it("should write file", async () => {
|
||||
const file = await createTmpFile();
|
||||
await expect(util.promisify(fs.writeFile)(file, "howdy"))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(util.promisify(nativeFs.readFile)(file, "utf8"))
|
||||
.resolves.toBe("howdy");
|
||||
});
|
||||
});
|
||||
});
|
@ -1,137 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
import * as nativeNet from "net";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import * as util from "util";
|
||||
import * as rimraf from "rimraf";
|
||||
import { createClient } from "@coder/protocol/test";
|
||||
|
||||
const client = createClient();
|
||||
jest.mock("../src/fill/client", () => ({ client }));
|
||||
const net = require("../src/fill/net") as typeof import("net");
|
||||
|
||||
describe("net", () => {
|
||||
let i = 0;
|
||||
const coderDir = path.join(os.tmpdir(), "coder", "net");
|
||||
const tmpFile = (): string => path.join(coderDir, `socket.${i++}`);
|
||||
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
await util.promisify(fs.mkdir)(path.dirname(coderDir));
|
||||
} catch (error) {
|
||||
if (error.code !== "EEXIST" && error.code !== "EISDIR") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
await util.promisify(rimraf)(coderDir);
|
||||
await util.promisify(fs.mkdir)(coderDir);
|
||||
});
|
||||
|
||||
describe("Socket", () => {
|
||||
const socketPath = tmpFile();
|
||||
let server: nativeNet.Server;
|
||||
|
||||
beforeAll(async () => {
|
||||
await new Promise((r): void => {
|
||||
server = nativeNet.createServer().listen(socketPath, r);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
it("should connect", async () => {
|
||||
await new Promise((resolve): void => {
|
||||
const socket = net.createConnection(socketPath, () => {
|
||||
socket.end();
|
||||
socket.addListener("close", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise((resolve): void => {
|
||||
const socket = new net.Socket();
|
||||
socket.connect(socketPath, () => {
|
||||
socket.end();
|
||||
socket.addListener("close", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should get data", (done) => {
|
||||
server.once("connection", (socket: nativeNet.Socket) => {
|
||||
socket.write("hi how r u");
|
||||
});
|
||||
|
||||
const socket = net.createConnection(socketPath);
|
||||
|
||||
socket.addListener("data", (data) => {
|
||||
expect(data.toString()).toEqual("hi how r u");
|
||||
socket.end();
|
||||
socket.addListener("close", () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send data", (done) => {
|
||||
const clientSocket = net.createConnection(socketPath);
|
||||
clientSocket.write(Buffer.from("bananas"));
|
||||
server.once("connection", (socket: nativeNet.Socket) => {
|
||||
socket.addListener("data", (data) => {
|
||||
expect(data.toString()).toEqual("bananas");
|
||||
socket.end();
|
||||
clientSocket.addListener("end", () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Server", () => {
|
||||
it("should listen", (done) => {
|
||||
const s = net.createServer();
|
||||
s.on("listening", () => s.close());
|
||||
s.on("close", () => done());
|
||||
s.listen(tmpFile());
|
||||
});
|
||||
|
||||
it("should get connection", async () => {
|
||||
let constructorListener: (() => void) | undefined;
|
||||
const s = net.createServer(() => {
|
||||
if (constructorListener) {
|
||||
constructorListener();
|
||||
}
|
||||
});
|
||||
|
||||
const socketPath = tmpFile();
|
||||
s.listen(socketPath);
|
||||
|
||||
const makeConnection = async (): Promise<void> => {
|
||||
net.createConnection(socketPath);
|
||||
await Promise.all([
|
||||
new Promise((resolve): void => {
|
||||
constructorListener = resolve;
|
||||
}),
|
||||
new Promise((resolve): void => {
|
||||
s.once("connection", (socket) => {
|
||||
socket.destroy();
|
||||
resolve();
|
||||
});
|
||||
}),
|
||||
]);
|
||||
};
|
||||
|
||||
await makeConnection();
|
||||
await makeConnection();
|
||||
|
||||
s.close();
|
||||
await new Promise((r): nativeNet.Server => s.on("close", r));
|
||||
});
|
||||
});
|
||||
});
|
@ -2,113 +2,3 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/events@*":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
||||
|
||||
"@types/glob@*":
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
|
||||
integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
|
||||
dependencies:
|
||||
"@types/events" "*"
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/minimatch@*":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||
|
||||
"@types/node@*":
|
||||
version "11.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.4.tgz#ceb0048a546db453f6248f2d1d95e937a6f00a14"
|
||||
integrity sha512-Zl8dGvAcEmadgs1tmSPcvwzO1YRsz38bVJQvH1RvRqSR9/5n61Q1ktcDL0ht3FXWR+ZpVmXVwN1LuH4Ax23NsA==
|
||||
|
||||
"@types/rimraf@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e"
|
||||
integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ==
|
||||
dependencies:
|
||||
"@types/glob" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
glob@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
rimraf@^2.6.3:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
Reference in New Issue
Block a user