mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-13 15:19:15 +00:00
85 lines
2.2 KiB
TypeScript
85 lines
2.2 KiB
TypeScript
import { EventEmitter } from "node:events";
|
|
|
|
/** An ssh2 exec channel: stdout on "data", stderr on `stderr`, then "close". */
|
|
export class FakeStream extends EventEmitter {
|
|
stderr = new EventEmitter();
|
|
written: string[] = [];
|
|
ended = false;
|
|
window: [number, number] | null = null;
|
|
|
|
write(data: string) {
|
|
this.written.push(data);
|
|
return true;
|
|
}
|
|
|
|
end() {
|
|
this.ended = true;
|
|
}
|
|
|
|
setWindow(rows: number, cols: number) {
|
|
this.window = [rows, cols];
|
|
}
|
|
|
|
/** Emits output and exits, on the next tick like a real channel. */
|
|
finish(stdout: string, code = 0, stderr = "") {
|
|
setImmediate(() => {
|
|
if (stdout) this.emit("data", Buffer.from(stdout));
|
|
if (stderr) this.stderr.emit("data", Buffer.from(stderr));
|
|
this.emit("close", code);
|
|
});
|
|
}
|
|
}
|
|
|
|
export type ExecReply =
|
|
| { stdout?: string; stderr?: string; code?: number }
|
|
| ((stream: FakeStream) => void);
|
|
|
|
/**
|
|
* An ssh2 Client that answers exec by the first matching pattern. A reply
|
|
* may be a function for a long-lived channel such as a console.
|
|
*/
|
|
export class FakeClient extends EventEmitter {
|
|
commands: string[] = [];
|
|
streams: FakeStream[] = [];
|
|
ended = false;
|
|
|
|
constructor(private replies: Array<[RegExp, ExecReply]> = []) {
|
|
super();
|
|
}
|
|
|
|
reply(pattern: RegExp, reply: ExecReply) {
|
|
this.replies.unshift([pattern, reply]);
|
|
}
|
|
|
|
exec(
|
|
command: string,
|
|
optionsOrCallback: unknown,
|
|
maybeCallback?: (err: Error | undefined, stream: FakeStream) => void,
|
|
) {
|
|
const callback = (
|
|
typeof optionsOrCallback === "function"
|
|
? optionsOrCallback
|
|
: maybeCallback
|
|
) as (err: Error | undefined, stream: FakeStream) => void;
|
|
this.commands.push(command);
|
|
const stream = new FakeStream();
|
|
this.streams.push(stream);
|
|
const match = this.replies.find(([pattern]) => pattern.test(command));
|
|
callback(undefined, stream);
|
|
if (!match) {
|
|
stream.finish("");
|
|
return this;
|
|
}
|
|
const reply = match[1];
|
|
if (typeof reply === "function") reply(stream);
|
|
else stream.finish(reply.stdout ?? "", reply.code ?? 0, reply.stderr ?? "");
|
|
return this;
|
|
}
|
|
|
|
end() {
|
|
if (this.ended) return;
|
|
this.ended = true;
|
|
this.emit("close");
|
|
}
|
|
}
|