@rhost/testkit 0.1.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +393 -5
- package/ROADMAP.md +241 -0
- package/dist/benchmark.d.ts +44 -0
- package/dist/benchmark.d.ts.map +1 -0
- package/dist/benchmark.js +118 -0
- package/dist/benchmark.js.map +1 -0
- package/dist/cli/deploy.d.ts +2 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +120 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/fmt.d.ts +2 -0
- package/dist/cli/fmt.d.ts.map +1 -0
- package/dist/cli/fmt.js +119 -0
- package/dist/cli/fmt.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +81 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +210 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/validate.d.ts +2 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +126 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/cli/watch.d.ts +2 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +136 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/client.d.ts +56 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +117 -30
- package/dist/client.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +1 -1
- package/dist/container.js.map +1 -1
- package/dist/deployer.d.ts +86 -0
- package/dist/deployer.d.ts.map +1 -0
- package/dist/deployer.js +154 -0
- package/dist/deployer.js.map +1 -0
- package/dist/expect.d.ts +27 -1
- package/dist/expect.d.ts.map +1 -1
- package/dist/expect.js +47 -2
- package/dist/expect.js.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/dist/preflight.d.ts +70 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +121 -0
- package/dist/preflight.js.map +1 -0
- package/dist/reporter.d.ts +2 -0
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +24 -1
- package/dist/reporter.js.map +1 -1
- package/dist/runner.d.ts +83 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +137 -8
- package/dist/runner.js.map +1 -1
- package/dist/snapshots.d.ts +84 -0
- package/dist/snapshots.d.ts.map +1 -0
- package/dist/snapshots.js +230 -0
- package/dist/snapshots.js.map +1 -0
- package/dist/validator/builtins.d.ts +18 -0
- package/dist/validator/builtins.d.ts.map +1 -0
- package/dist/validator/builtins.js +265 -0
- package/dist/validator/builtins.js.map +1 -0
- package/dist/validator/checker.d.ts +13 -0
- package/dist/validator/checker.d.ts.map +1 -0
- package/dist/validator/checker.js +111 -0
- package/dist/validator/checker.js.map +1 -0
- package/dist/validator/clobber.d.ts +7 -0
- package/dist/validator/clobber.d.ts.map +1 -0
- package/dist/validator/clobber.js +102 -0
- package/dist/validator/clobber.js.map +1 -0
- package/dist/validator/compat.d.ts +19 -0
- package/dist/validator/compat.d.ts.map +1 -0
- package/dist/validator/compat.js +58 -0
- package/dist/validator/compat.js.map +1 -0
- package/dist/validator/formatter.d.ts +21 -0
- package/dist/validator/formatter.d.ts.map +1 -0
- package/dist/validator/formatter.js +120 -0
- package/dist/validator/formatter.js.map +1 -0
- package/dist/validator/index.d.ts +57 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +133 -0
- package/dist/validator/index.js.map +1 -0
- package/dist/validator/parser.d.ts +16 -0
- package/dist/validator/parser.d.ts.map +1 -0
- package/dist/validator/parser.js +260 -0
- package/dist/validator/parser.js.map +1 -0
- package/dist/validator/tokenizer.d.ts +28 -0
- package/dist/validator/tokenizer.d.ts.map +1 -0
- package/dist/validator/tokenizer.js +174 -0
- package/dist/validator/tokenizer.js.map +1 -0
- package/dist/validator/types.d.ts +55 -0
- package/dist/validator/types.d.ts.map +1 -0
- package/dist/validator/types.js +6 -0
- package/dist/validator/types.js.map +1 -0
- package/dist/watcher.d.ts +44 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +297 -0
- package/dist/watcher.js.map +1 -0
- package/dist/world.d.ts +79 -0
- package/dist/world.d.ts.map +1 -1
- package/dist/world.js +167 -1
- package/dist/world.js.map +1 -1
- package/package.json +19 -3
package/dist/client.d.ts
CHANGED
|
@@ -24,6 +24,13 @@ export interface RhostClientOptions {
|
|
|
24
24
|
* Default: 0 (no delay)
|
|
25
25
|
*/
|
|
26
26
|
paceMs?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Milliseconds to wait after sending a command and before sending the
|
|
29
|
+
* sentinel. Useful when commands produce deferred output (e.g. via
|
|
30
|
+
* @trigger or @wait 0) that arrives after the main response.
|
|
31
|
+
* Default: 0
|
|
32
|
+
*/
|
|
33
|
+
commandSettleMs?: number;
|
|
27
34
|
/**
|
|
28
35
|
* Timeout in milliseconds for the raw TCP connection to be established.
|
|
29
36
|
* If the server accepts the socket but then stalls, the connect will be
|
|
@@ -31,6 +38,26 @@ export interface RhostClientOptions {
|
|
|
31
38
|
*/
|
|
32
39
|
connectTimeout?: number;
|
|
33
40
|
}
|
|
41
|
+
export interface PreviewOptions {
|
|
42
|
+
/**
|
|
43
|
+
* How to send the input to the server.
|
|
44
|
+
* - `'eval'` — wraps in `think`, returning the softcode result (default)
|
|
45
|
+
* - `'command'` — sends as a raw MUSH command, capturing all output lines
|
|
46
|
+
*/
|
|
47
|
+
mode?: 'eval' | 'command';
|
|
48
|
+
/**
|
|
49
|
+
* Label shown in the preview frame header.
|
|
50
|
+
* Defaults to the expression/command string (truncated if long).
|
|
51
|
+
*/
|
|
52
|
+
label?: string;
|
|
53
|
+
/** Timeout in ms. Defaults to the client's default timeout. */
|
|
54
|
+
timeout?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Write the preview to stdout automatically. Default: true.
|
|
57
|
+
* Set to false to suppress output and only use the return value.
|
|
58
|
+
*/
|
|
59
|
+
print?: boolean;
|
|
60
|
+
}
|
|
34
61
|
/**
|
|
35
62
|
* High-level client for interacting with a RhostMUSH server.
|
|
36
63
|
*
|
|
@@ -47,6 +74,7 @@ export declare class RhostClient {
|
|
|
47
74
|
private bannerTimeout;
|
|
48
75
|
private doStripAnsi;
|
|
49
76
|
private paceMs;
|
|
77
|
+
private commandSettleMs;
|
|
50
78
|
private connectTimeout;
|
|
51
79
|
constructor(options?: RhostClientOptions);
|
|
52
80
|
/**
|
|
@@ -79,11 +107,39 @@ export declare class RhostClient {
|
|
|
79
107
|
* const lines = await client.command('@pemit me=hello');
|
|
80
108
|
*/
|
|
81
109
|
command(cmd: string, timeout?: number): Promise<string[]>;
|
|
110
|
+
/**
|
|
111
|
+
* Evaluate an expression or run a command and print the raw server output
|
|
112
|
+
* to stdout exactly as a MUSH client would receive it — ANSI colours,
|
|
113
|
+
* formatting codes, and all.
|
|
114
|
+
*
|
|
115
|
+
* The output is framed in a labelled box so it is clearly demarcated in
|
|
116
|
+
* test output. The raw string is also returned so you can assert on it
|
|
117
|
+
* if needed.
|
|
118
|
+
*
|
|
119
|
+
* By default (`mode: 'eval'`) the input is wrapped in `think`, so it
|
|
120
|
+
* should be a softcode expression. Pass `mode: 'command'` to send a raw
|
|
121
|
+
* MUSH command instead (e.g. `'look here'`, `'score'`, `'@pemit me=hi'`).
|
|
122
|
+
*
|
|
123
|
+
* @example Softcode expression
|
|
124
|
+
* await client.preview('ansi(r,Hello!)');
|
|
125
|
+
* await client.preview('iter(lnum(1,5),##)');
|
|
126
|
+
*
|
|
127
|
+
* @example Raw command (room description, score screen, etc.)
|
|
128
|
+
* await client.preview('look here', { mode: 'command' });
|
|
129
|
+
* await client.preview('score', { mode: 'command' });
|
|
130
|
+
*
|
|
131
|
+
* @example Suppress auto-print and only use the return value
|
|
132
|
+
* const raw = await client.preview('ansi(b,test)', { print: false });
|
|
133
|
+
* expect(stripAnsi(raw)).toBe('test');
|
|
134
|
+
*/
|
|
135
|
+
preview(input: string, options?: PreviewOptions): Promise<string>;
|
|
82
136
|
/** Subscribe to every raw line received from the server. */
|
|
83
137
|
onLine(handler: (line: string) => void): void;
|
|
84
138
|
offLine(handler: (line: string) => void): void;
|
|
85
139
|
/** Send QUIT and close the TCP connection. */
|
|
86
140
|
disconnect(): Promise<void>;
|
|
141
|
+
private _collectEval;
|
|
142
|
+
private _collectCommand;
|
|
87
143
|
private readUntilMarker;
|
|
88
144
|
private drainBanner;
|
|
89
145
|
private makeId;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAQA,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,MAAM,WAAW,kBAAkB;IAC/B,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,MAAM,CAAS;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAQA,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,MAAM,WAAW,kBAAkB;IAC/B,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD,MAAM,WAAW,cAAc;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,qBAAa,WAAW;IACpB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,GAAE,kBAAuB;IAU5C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B;;;OAGG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9D;;;;;;;;;;OAUG;IACG,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIjE;;;;;;;OAOG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI/D;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB3E,4DAA4D;IAC5D,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI7C,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAI9C,8CAA8C;IACxC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAanB,YAAY;YA8BZ,eAAe;YA8Bf,eAAe;IAQ7B,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,MAAM;CAGjB"}
|
package/dist/client.js
CHANGED
|
@@ -28,6 +28,7 @@ class RhostClient {
|
|
|
28
28
|
this.defaultTimeout = options.timeout ?? 10000;
|
|
29
29
|
this.bannerTimeout = options.bannerTimeout ?? 300;
|
|
30
30
|
this.doStripAnsi = options.stripAnsi !== false;
|
|
31
|
+
this.commandSettleMs = options.commandSettleMs ?? 0;
|
|
31
32
|
this.paceMs = options.paceMs ?? 0;
|
|
32
33
|
this.connectTimeout = options.connectTimeout ?? 10000;
|
|
33
34
|
}
|
|
@@ -49,6 +50,12 @@ class RhostClient {
|
|
|
49
50
|
if (/[\n\r]/.test(password)) {
|
|
50
51
|
throw new RangeError('login: invalid password — must not contain newline or carriage return characters');
|
|
51
52
|
}
|
|
53
|
+
// The MUSH `connect` command is space-delimited: embedding a space or tab
|
|
54
|
+
// in the username would let the caller silently substitute a different
|
|
55
|
+
// character name and password (space-splitting injection).
|
|
56
|
+
if (/[ \t]/.test(username)) {
|
|
57
|
+
throw new RangeError('login: invalid username — must not contain spaces or tabs');
|
|
58
|
+
}
|
|
52
59
|
const sentinel = `RHOST_LOGIN_${this.makeId()}`;
|
|
53
60
|
this.conn.send(`connect ${username} ${password}`);
|
|
54
61
|
this.conn.send(`@pemit me=${sentinel}`);
|
|
@@ -66,6 +73,79 @@ class RhostClient {
|
|
|
66
73
|
* await client.eval('encode64(hello)') // => 'aGVsbG8='
|
|
67
74
|
*/
|
|
68
75
|
async eval(expression, timeout) {
|
|
76
|
+
return this._collectEval(expression, this.doStripAnsi, timeout);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Run a MUSHcode command and collect all output lines until the
|
|
80
|
+
* internal sentinel is received.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const lines = await client.command('look here');
|
|
84
|
+
* const lines = await client.command('@pemit me=hello');
|
|
85
|
+
*/
|
|
86
|
+
async command(cmd, timeout) {
|
|
87
|
+
return this._collectCommand(cmd, this.doStripAnsi, timeout);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Evaluate an expression or run a command and print the raw server output
|
|
91
|
+
* to stdout exactly as a MUSH client would receive it — ANSI colours,
|
|
92
|
+
* formatting codes, and all.
|
|
93
|
+
*
|
|
94
|
+
* The output is framed in a labelled box so it is clearly demarcated in
|
|
95
|
+
* test output. The raw string is also returned so you can assert on it
|
|
96
|
+
* if needed.
|
|
97
|
+
*
|
|
98
|
+
* By default (`mode: 'eval'`) the input is wrapped in `think`, so it
|
|
99
|
+
* should be a softcode expression. Pass `mode: 'command'` to send a raw
|
|
100
|
+
* MUSH command instead (e.g. `'look here'`, `'score'`, `'@pemit me=hi'`).
|
|
101
|
+
*
|
|
102
|
+
* @example Softcode expression
|
|
103
|
+
* await client.preview('ansi(r,Hello!)');
|
|
104
|
+
* await client.preview('iter(lnum(1,5),##)');
|
|
105
|
+
*
|
|
106
|
+
* @example Raw command (room description, score screen, etc.)
|
|
107
|
+
* await client.preview('look here', { mode: 'command' });
|
|
108
|
+
* await client.preview('score', { mode: 'command' });
|
|
109
|
+
*
|
|
110
|
+
* @example Suppress auto-print and only use the return value
|
|
111
|
+
* const raw = await client.preview('ansi(b,test)', { print: false });
|
|
112
|
+
* expect(stripAnsi(raw)).toBe('test');
|
|
113
|
+
*/
|
|
114
|
+
async preview(input, options = {}) {
|
|
115
|
+
const mode = options.mode ?? 'eval';
|
|
116
|
+
const timeout = options.timeout;
|
|
117
|
+
const doPrint = options.print !== false;
|
|
118
|
+
// Always collect raw output (never strip) for preview
|
|
119
|
+
const raw = mode === 'eval'
|
|
120
|
+
? await this._collectEval(input, false, timeout)
|
|
121
|
+
: (await this._collectCommand(input, false, timeout)).join('\n');
|
|
122
|
+
if (doPrint) {
|
|
123
|
+
const label = options.label ?? (input.length > 60 ? input.slice(0, 57) + '…' : input);
|
|
124
|
+
printPreviewFrame(label, raw, mode);
|
|
125
|
+
}
|
|
126
|
+
return raw;
|
|
127
|
+
}
|
|
128
|
+
/** Subscribe to every raw line received from the server. */
|
|
129
|
+
onLine(handler) {
|
|
130
|
+
this.conn.on('line', handler);
|
|
131
|
+
}
|
|
132
|
+
offLine(handler) {
|
|
133
|
+
this.conn.off('line', handler);
|
|
134
|
+
}
|
|
135
|
+
/** Send QUIT and close the TCP connection. */
|
|
136
|
+
async disconnect() {
|
|
137
|
+
try {
|
|
138
|
+
this.conn.send('QUIT');
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// already closed
|
|
142
|
+
}
|
|
143
|
+
await this.conn.close();
|
|
144
|
+
}
|
|
145
|
+
// -------------------------------------------------------------------------
|
|
146
|
+
// Private: core collect helpers (shared by eval/command/preview)
|
|
147
|
+
// -------------------------------------------------------------------------
|
|
148
|
+
async _collectEval(expression, strip, timeout) {
|
|
69
149
|
if (this.paceMs > 0) {
|
|
70
150
|
await new Promise((r) => setTimeout(r, this.paceMs));
|
|
71
151
|
}
|
|
@@ -80,54 +160,32 @@ class RhostClient {
|
|
|
80
160
|
const resultLines = [];
|
|
81
161
|
while (true) {
|
|
82
162
|
const line = await this.conn.lines.next(ms);
|
|
83
|
-
const clean =
|
|
84
|
-
if (clean.includes(endMarker))
|
|
163
|
+
const clean = strip ? stripAnsi(line) : line;
|
|
164
|
+
if ((strip ? clean : stripAnsi(line)).includes(endMarker))
|
|
85
165
|
break;
|
|
86
166
|
resultLines.push(clean);
|
|
87
167
|
}
|
|
88
168
|
return resultLines.join('\n');
|
|
89
169
|
}
|
|
90
|
-
|
|
91
|
-
* Run a MUSHcode command and collect all output lines until the
|
|
92
|
-
* internal sentinel is received.
|
|
93
|
-
*
|
|
94
|
-
* @example
|
|
95
|
-
* const lines = await client.command('look here');
|
|
96
|
-
* const lines = await client.command('@pemit me=hello');
|
|
97
|
-
*/
|
|
98
|
-
async command(cmd, timeout) {
|
|
170
|
+
async _collectCommand(cmd, strip, timeout) {
|
|
99
171
|
const id = this.makeId();
|
|
100
172
|
const endMarker = `RHOST_CMD_END_${id}`;
|
|
101
173
|
const ms = timeout ?? this.defaultTimeout;
|
|
102
174
|
this.conn.send(cmd);
|
|
175
|
+
if (this.commandSettleMs > 0) {
|
|
176
|
+
await new Promise(r => setTimeout(r, this.commandSettleMs));
|
|
177
|
+
}
|
|
103
178
|
this.conn.send(`@pemit me=${endMarker}`);
|
|
104
179
|
const lines = [];
|
|
105
180
|
while (true) {
|
|
106
181
|
const line = await this.conn.lines.next(ms);
|
|
107
|
-
const clean =
|
|
108
|
-
if (clean.includes(endMarker))
|
|
182
|
+
const clean = strip ? stripAnsi(line) : line;
|
|
183
|
+
if ((strip ? clean : stripAnsi(line)).includes(endMarker))
|
|
109
184
|
break;
|
|
110
185
|
lines.push(clean);
|
|
111
186
|
}
|
|
112
187
|
return lines;
|
|
113
188
|
}
|
|
114
|
-
/** Subscribe to every raw line received from the server. */
|
|
115
|
-
onLine(handler) {
|
|
116
|
-
this.conn.on('line', handler);
|
|
117
|
-
}
|
|
118
|
-
offLine(handler) {
|
|
119
|
-
this.conn.off('line', handler);
|
|
120
|
-
}
|
|
121
|
-
/** Send QUIT and close the TCP connection. */
|
|
122
|
-
async disconnect() {
|
|
123
|
-
try {
|
|
124
|
-
this.conn.send('QUIT');
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
// already closed
|
|
128
|
-
}
|
|
129
|
-
await this.conn.close();
|
|
130
|
-
}
|
|
131
189
|
// -------------------------------------------------------------------------
|
|
132
190
|
// Private helpers
|
|
133
191
|
// -------------------------------------------------------------------------
|
|
@@ -154,4 +212,33 @@ class RhostClient {
|
|
|
154
212
|
}
|
|
155
213
|
}
|
|
156
214
|
exports.RhostClient = RhostClient;
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Preview frame renderer
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
const USE_COLOR = process.stdout.isTTY !== false;
|
|
219
|
+
const c = (code, s) => USE_COLOR ? `\x1b[${code}m${s}\x1b[0m` : s;
|
|
220
|
+
function printPreviewFrame(label, content, mode) {
|
|
221
|
+
const termWidth = (process.stdout.columns ?? 80) - 2;
|
|
222
|
+
const frameColor = mode === 'eval' ? '36' : '33'; // cyan for eval, yellow for command
|
|
223
|
+
const modeTag = mode === 'eval' ? 'softcode' : 'command';
|
|
224
|
+
// Header line: ─── preview [softcode]: <label> ─────────────
|
|
225
|
+
const headerLeft = ` preview [${modeTag}]: `;
|
|
226
|
+
const headerFull = `${headerLeft}${label} `;
|
|
227
|
+
const headerPad = Math.max(0, termWidth - headerFull.length);
|
|
228
|
+
const header = c(frameColor, '─'.repeat(3) + headerFull + '─'.repeat(headerPad));
|
|
229
|
+
// Footer line: ─────────────────────────────────────────────
|
|
230
|
+
const footer = c(frameColor, '─'.repeat(termWidth));
|
|
231
|
+
process.stdout.write('\n' + header + '\n');
|
|
232
|
+
if (content === '') {
|
|
233
|
+
process.stdout.write(c('90', ' (empty output)\n'));
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Prefix each line with a subtle left margin
|
|
237
|
+
const lines = content.split('\n');
|
|
238
|
+
for (const line of lines) {
|
|
239
|
+
process.stdout.write(' ' + line + '\n');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
process.stdout.write(footer + '\n\n');
|
|
243
|
+
}
|
|
157
244
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AASA,8BAEC;AAXD,mCAAoC;AACpC,6CAA8C;AAE9C,oDAAoD;AACpD,yDAAyD;AACzD,gCAAgC;AAChC,MAAM,OAAO,GAAG,0DAA0D,CAAC;AAE3E,uDAAuD;AACvD,SAAgB,SAAS,CAAC,CAAS;IAC/B,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AASA,8BAEC;AAXD,mCAAoC;AACpC,6CAA8C;AAE9C,oDAAoD;AACpD,yDAAyD;AACzD,gCAAgC;AAChC,MAAM,OAAO,GAAG,0DAA0D,CAAC;AAE3E,uDAAuD;AACvD,SAAgB,SAAS,CAAC,CAAS;IAC/B,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAkED;;;;;;;;;GASG;AACH,MAAa,WAAW;IAUpB,YAAY,UAA8B,EAAE;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,2BAAc,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;QAClF,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;QAC/C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,GAAG,CAAC;QAClD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,SAAS,KAAK,KAAK,CAAC;QAC/C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACT,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7C,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,QAAgB,EAAE,QAAgB;QAC1C,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,UAAU,CAAC,kFAAkF,CAAC,CAAC;QAC7G,CAAC;QACD,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,UAAU,CAAC,kFAAkF,CAAC,CAAC;QAC7G,CAAC;QACD,0EAA0E;QAC1E,uEAAuE;QACvE,2DAA2D;QAC3D,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,UAAU,CAAC,2DAA2D,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,QAAQ,GAAG,eAAe,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,QAAQ,EAAE,CAAC,CAAC;QACxC,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,IAAI,CAAC,UAAkB,EAAE,OAAgB;QAC3C,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,OAAgB;QACvC,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,OAAO,CAAC,KAAa,EAAE,UAA0B,EAAE;QACrD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC;QACpC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC;QAExC,sDAAsD;QACtD,MAAM,GAAG,GACL,IAAI,KAAK,MAAM;YACX,CAAC,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC;YAChD,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEzE,IAAI,OAAO,EAAE,CAAC;YACV,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACtF,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,GAAG,CAAC;IACf,CAAC;IAED,4DAA4D;IAC5D,MAAM,CAAC,OAA+B;QAClC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,OAA+B;QACnC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,8CAA8C;IAC9C,KAAK,CAAC,UAAU;QACZ,IAAI,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACL,iBAAiB;QACrB,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,4EAA4E;IAC5E,iEAAiE;IACjE,4EAA4E;IAEpE,KAAK,CAAC,YAAY,CACtB,UAAkB,EAClB,KAAc,EACd,OAAgB;QAEhB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,oBAAoB,EAAE,EAAE,CAAC;QAC7C,MAAM,SAAS,GAAG,kBAAkB,EAAE,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC;QAE1C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,WAAW,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;QAEzC,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAE5C,MAAM,WAAW,GAAa,EAAE,CAAC;QACjC,OAAO,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,MAAM;YACjE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAEO,KAAK,CAAC,eAAe,CACzB,GAAW,EACX,KAAc,EACd,OAAgB;QAEhB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,iBAAiB,EAAE,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC;QAE1C,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,EAAE,CAAC,CAAC;QAEzC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,OAAO,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,MAAM;YACjE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QAED,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,4EAA4E;IAC5E,kBAAkB;IAClB,4EAA4E;IAEpE,KAAK,CAAC,eAAe,CAAC,MAAc,EAAE,SAAiB;QAC3D,OAAO,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACnD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACxD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,OAAO;QACvC,CAAC;IACL,CAAC;IAEO,WAAW,CAAC,MAAc;QAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,MAAM,OAAO,GAAG,GAAG,EAAE;gBACjB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;qBACvB,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;qBACrB,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAChC,CAAC,CAAC;YACF,OAAO,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,MAAM;QACV,OAAO,IAAA,mBAAU,GAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACrE,CAAC;CACJ;AAnOD,kCAmOC;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC;AACjD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,CAAS,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AAElF,SAAS,iBAAiB,CAAC,KAAa,EAAE,OAAe,EAAE,IAAwB;IAC/E,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,oCAAoC;IACtF,MAAM,OAAO,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAEzD,6DAA6D;IAC7D,MAAM,UAAU,GAAG,aAAa,OAAO,KAAK,CAAC;IAC7C,MAAM,UAAU,GAAG,GAAG,UAAU,GAAG,KAAK,GAAG,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IAEjF,6DAA6D;IAC7D,MAAM,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IAEpD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC;IAE3C,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACxD,CAAC;SAAM,CAAC;QACJ,6CAA6C;QAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QAC7C,CAAC;IACL,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;AAC1C,CAAC"}
|
package/dist/container.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C,OAAO;IAIP;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C,OAAO;IAIP;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,SAA6B,GAAG,cAAc;IAIpE;;;;;;;;OAQG;IACH,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,cAAc;IAUvD;;;;;;OAMG;IACG,KAAK,CAAC,cAAc,SAAU,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAYvE,oEAAoE;IAC9D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,kEAAkE;IAClE,iBAAiB,IAAI,uBAAuB;CAS/C"}
|
package/dist/container.js
CHANGED
|
@@ -61,7 +61,7 @@ class RhostContainer {
|
|
|
61
61
|
* Build it first with: `docker build -t rhostmush:latest .`
|
|
62
62
|
* (run from the rhostmush-docker repo root)
|
|
63
63
|
*/
|
|
64
|
-
static fromImage(image = 'rhostmush:latest') {
|
|
64
|
+
static fromImage(image = 'lcanady/rhostmush:latest') {
|
|
65
65
|
return new RhostContainer(async () => new testcontainers_1.GenericContainer(image));
|
|
66
66
|
}
|
|
67
67
|
/**
|
package/dist/container.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;GAcG;AACH,2CAA6B;AAC7B,mDAIwB;AASxB,MAAa,cAAc;IAIvB,YAAoB,OAAyB;QAHrC,YAAO,GAAgC,IAAI,CAAC;QAIhD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;GAcG;AACH,2CAA6B;AAC7B,mDAIwB;AASxB,MAAa,cAAc;IAIvB,YAAoB,OAAyB;QAHrC,YAAO,GAAgC,IAAI,CAAC;QAIhD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,0BAA0B;QAC/C,OAAO,IAAI,cAAc,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,iCAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;IACvE,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,UAAU,CAAC,WAAoB;QAClC,MAAM,IAAI,GAAG,WAAW;YACpB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;YAC3B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAErC,OAAO,IAAI,cAAc,CAAC,KAAK,IAAI,EAAE;YACjC,OAAO,iCAAgB,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACzD,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,CAAC,cAAc,GAAG,MAAO;QAChC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI;aACpB,gBAAgB,CAAC,IAAI,CAAC;aACtB,gBAAgB,CACb,qBAAI,CAAC,iBAAiB,EAAE,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAC9D;aACA,KAAK,EAAE,CAAC;QAEb,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACpC,CAAC;IAED,oEAAoE;IACpE,KAAK,CAAC,IAAI;QACN,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,kEAAkE;IAClE,iBAAiB;QACb,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtE,CAAC;QACD,OAAO;YACH,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YAC5B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC;SACzC,CAAC;IACN,CAAC;CACJ;AAvED,wCAuEC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { RhostClient } from './client';
|
|
2
|
+
/** A single parsed command from a softcode deploy file. */
|
|
3
|
+
export interface DeployCommand {
|
|
4
|
+
dbref: string;
|
|
5
|
+
attr: string;
|
|
6
|
+
value: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A snapshot of object attribute state captured before deployment.
|
|
10
|
+
* Shape: `{ [dbref]: { [attr]: value } }`
|
|
11
|
+
*/
|
|
12
|
+
export type DeploySnapshot = Record<string, Record<string, string>>;
|
|
13
|
+
export interface DeployOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Optional test function. Called after all commands are applied.
|
|
16
|
+
* Return `true` (or resolve) to indicate success; return `false` or throw to trigger rollback.
|
|
17
|
+
*/
|
|
18
|
+
test?: (client: RhostClient) => Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Dry-run mode: snapshot objects and report what would be applied,
|
|
21
|
+
* but do not send any commands to the server.
|
|
22
|
+
*/
|
|
23
|
+
dryRun?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface DeployResult {
|
|
26
|
+
/** Number of commands applied (0 in dry-run). */
|
|
27
|
+
applied: number;
|
|
28
|
+
/** Whether the test function was called. */
|
|
29
|
+
tested: boolean;
|
|
30
|
+
/** Result of the test function, or null if no test was provided. */
|
|
31
|
+
testPassed: boolean | null;
|
|
32
|
+
/** Whether a rollback was performed. */
|
|
33
|
+
rolledBack: boolean;
|
|
34
|
+
/** true when dryRun option was set. */
|
|
35
|
+
dryRun: boolean;
|
|
36
|
+
/** The snapshot taken before applying commands. */
|
|
37
|
+
snapshot: DeploySnapshot;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse a softcode deploy file into a list of `DeployCommand` objects.
|
|
41
|
+
*
|
|
42
|
+
* Supported line formats:
|
|
43
|
+
* `&ATTRNAME #NN=value` — set an attribute
|
|
44
|
+
*
|
|
45
|
+
* Lines starting with `#` or `@@` are treated as comments and ignored.
|
|
46
|
+
* Blank lines are ignored.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const cmds = parseDeployFile(fs.readFileSync('mycode.mush', 'utf8'));
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseDeployFile(content: string): DeployCommand[];
|
|
52
|
+
/**
|
|
53
|
+
* Capture the current state (all attributes and their values) of the given
|
|
54
|
+
* dbrefs. Returns a `DeploySnapshot` suitable for passing to `restoreSnapshot`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function snapshotObjects(client: RhostClient, dbrefs: string[]): Promise<DeploySnapshot>;
|
|
57
|
+
/**
|
|
58
|
+
* Restore object attribute state from a snapshot.
|
|
59
|
+
*
|
|
60
|
+
* For each dbref in the snapshot:
|
|
61
|
+
* - Attributes that changed are reset to their original value.
|
|
62
|
+
* - Attributes that did not exist before deployment (i.e. appear in
|
|
63
|
+
* `currentDbrefs` but not in `snapshot[dbref]`) are wiped.
|
|
64
|
+
*
|
|
65
|
+
* @param currentDbrefs Optional list of dbrefs currently tracked, used to
|
|
66
|
+
* detect and wipe attrs added during deployment.
|
|
67
|
+
*/
|
|
68
|
+
export declare function restoreSnapshot(client: RhostClient, snapshot: DeploySnapshot, currentDbrefs?: string[]): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Deploy softcode commands to the server with automatic rollback on failure.
|
|
71
|
+
*
|
|
72
|
+
* Flow:
|
|
73
|
+
* 1. Snapshot all target objects.
|
|
74
|
+
* 2. Apply each command (unless dryRun).
|
|
75
|
+
* 3. Call the optional test function.
|
|
76
|
+
* 4. If the test fails or throws, restore the snapshot (rollback).
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* const cmds = parseDeployFile(fs.readFileSync('mycode.mush', 'utf8'));
|
|
80
|
+
* const result = await deploy(client, cmds, {
|
|
81
|
+
* test: async (c) => (await c.eval('smoke_test()')).startsWith('OK'),
|
|
82
|
+
* });
|
|
83
|
+
* if (result.rolledBack) console.error('Deployment failed — rolled back.');
|
|
84
|
+
*/
|
|
85
|
+
export declare function deploy(client: RhostClient, commands: DeployCommand[], options?: DeployOptions): Promise<DeployResult>;
|
|
86
|
+
//# sourceMappingURL=deployer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployer.d.ts","sourceRoot":"","sources":["../src/deployer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAMvC,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IACzB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,oEAAoE;IACpE,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,wCAAwC;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,uCAAuC;IACvC,MAAM,EAAE,OAAO,CAAC;IAChB,mDAAmD;IACnD,QAAQ,EAAE,cAAc,CAAC;CAC5B;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAkBhE;AAMD;;;GAGG;AACH,wBAAsB,eAAe,CACjC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,cAAc,CAAC,CAWzB;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACjC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,cAAc,EACxB,aAAa,CAAC,EAAE,MAAM,EAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,MAAM,CACxB,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,aAAa,EAAE,EACzB,OAAO,GAAE,aAAkB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA4CvB"}
|
package/dist/deployer.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseDeployFile = parseDeployFile;
|
|
4
|
+
exports.snapshotObjects = snapshotObjects;
|
|
5
|
+
exports.restoreSnapshot = restoreSnapshot;
|
|
6
|
+
exports.deploy = deploy;
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// File parser
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/**
|
|
11
|
+
* Parse a softcode deploy file into a list of `DeployCommand` objects.
|
|
12
|
+
*
|
|
13
|
+
* Supported line formats:
|
|
14
|
+
* `&ATTRNAME #NN=value` — set an attribute
|
|
15
|
+
*
|
|
16
|
+
* Lines starting with `#` or `@@` are treated as comments and ignored.
|
|
17
|
+
* Blank lines are ignored.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const cmds = parseDeployFile(fs.readFileSync('mycode.mush', 'utf8'));
|
|
21
|
+
*/
|
|
22
|
+
function parseDeployFile(content) {
|
|
23
|
+
const commands = [];
|
|
24
|
+
for (const rawLine of content.split('\n')) {
|
|
25
|
+
const line = rawLine.trim();
|
|
26
|
+
if (!line)
|
|
27
|
+
continue;
|
|
28
|
+
if (line.startsWith('#') || line.startsWith('@@'))
|
|
29
|
+
continue;
|
|
30
|
+
// &ATTRNAME #DBREF=value
|
|
31
|
+
const m = line.match(/^&([A-Za-z0-9_]+)\s+(#\d+)=(.*)$/s);
|
|
32
|
+
if (m) {
|
|
33
|
+
commands.push({
|
|
34
|
+
dbref: m[2],
|
|
35
|
+
attr: m[1].toUpperCase(),
|
|
36
|
+
value: m[3],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return commands;
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Snapshot
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
/**
|
|
46
|
+
* Capture the current state (all attributes and their values) of the given
|
|
47
|
+
* dbrefs. Returns a `DeploySnapshot` suitable for passing to `restoreSnapshot`.
|
|
48
|
+
*/
|
|
49
|
+
async function snapshotObjects(client, dbrefs) {
|
|
50
|
+
const snapshot = {};
|
|
51
|
+
for (const dbref of dbrefs) {
|
|
52
|
+
const attrsStr = await client.eval(`lattr(${dbref})`);
|
|
53
|
+
const attrs = attrsStr ? attrsStr.split(' ').filter(Boolean) : [];
|
|
54
|
+
snapshot[dbref] = {};
|
|
55
|
+
for (const attr of attrs) {
|
|
56
|
+
snapshot[dbref][attr] = await client.eval(`get(${dbref}/${attr})`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return snapshot;
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Restore
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/**
|
|
65
|
+
* Restore object attribute state from a snapshot.
|
|
66
|
+
*
|
|
67
|
+
* For each dbref in the snapshot:
|
|
68
|
+
* - Attributes that changed are reset to their original value.
|
|
69
|
+
* - Attributes that did not exist before deployment (i.e. appear in
|
|
70
|
+
* `currentDbrefs` but not in `snapshot[dbref]`) are wiped.
|
|
71
|
+
*
|
|
72
|
+
* @param currentDbrefs Optional list of dbrefs currently tracked, used to
|
|
73
|
+
* detect and wipe attrs added during deployment.
|
|
74
|
+
*/
|
|
75
|
+
async function restoreSnapshot(client, snapshot, currentDbrefs) {
|
|
76
|
+
for (const [dbref, originalAttrs] of Object.entries(snapshot)) {
|
|
77
|
+
// Restore changed / deleted attrs
|
|
78
|
+
for (const [attr, value] of Object.entries(originalAttrs)) {
|
|
79
|
+
const current = await client.eval(`get(${dbref}/${attr})`);
|
|
80
|
+
if (current !== value) {
|
|
81
|
+
await client.command(`&${attr} ${dbref}=${value}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Wipe attrs added after the snapshot
|
|
85
|
+
if (currentDbrefs?.includes(dbref)) {
|
|
86
|
+
const nowStr = await client.eval(`lattr(${dbref})`);
|
|
87
|
+
const nowAttrs = nowStr ? nowStr.split(' ').filter(Boolean) : [];
|
|
88
|
+
for (const attr of nowAttrs) {
|
|
89
|
+
if (!(attr in originalAttrs)) {
|
|
90
|
+
await client.command(`@wipe ${dbref}/${attr}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Deploy pipeline
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
/**
|
|
100
|
+
* Deploy softcode commands to the server with automatic rollback on failure.
|
|
101
|
+
*
|
|
102
|
+
* Flow:
|
|
103
|
+
* 1. Snapshot all target objects.
|
|
104
|
+
* 2. Apply each command (unless dryRun).
|
|
105
|
+
* 3. Call the optional test function.
|
|
106
|
+
* 4. If the test fails or throws, restore the snapshot (rollback).
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* const cmds = parseDeployFile(fs.readFileSync('mycode.mush', 'utf8'));
|
|
110
|
+
* const result = await deploy(client, cmds, {
|
|
111
|
+
* test: async (c) => (await c.eval('smoke_test()')).startsWith('OK'),
|
|
112
|
+
* });
|
|
113
|
+
* if (result.rolledBack) console.error('Deployment failed — rolled back.');
|
|
114
|
+
*/
|
|
115
|
+
async function deploy(client, commands, options = {}) {
|
|
116
|
+
const { test, dryRun = false } = options;
|
|
117
|
+
// Unique dbrefs from the command list
|
|
118
|
+
const dbrefs = [...new Set(commands.map((c) => c.dbref))];
|
|
119
|
+
// 1. Snapshot
|
|
120
|
+
const snapshot = await snapshotObjects(client, dbrefs);
|
|
121
|
+
// 2. Apply (skip in dry-run)
|
|
122
|
+
if (!dryRun) {
|
|
123
|
+
for (const cmd of commands) {
|
|
124
|
+
await client.command(`&${cmd.attr} ${cmd.dbref}=${cmd.value}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const result = {
|
|
128
|
+
applied: dryRun ? 0 : commands.length,
|
|
129
|
+
tested: false,
|
|
130
|
+
testPassed: null,
|
|
131
|
+
rolledBack: false,
|
|
132
|
+
dryRun,
|
|
133
|
+
snapshot,
|
|
134
|
+
};
|
|
135
|
+
if (dryRun || !test)
|
|
136
|
+
return result;
|
|
137
|
+
// 3. Test
|
|
138
|
+
result.tested = true;
|
|
139
|
+
let testPassed = false;
|
|
140
|
+
try {
|
|
141
|
+
testPassed = await test(client);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
testPassed = false;
|
|
145
|
+
}
|
|
146
|
+
result.testPassed = testPassed;
|
|
147
|
+
// 4. Rollback if test failed
|
|
148
|
+
if (!testPassed) {
|
|
149
|
+
await restoreSnapshot(client, snapshot, dbrefs);
|
|
150
|
+
result.rolledBack = true;
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=deployer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployer.js","sourceRoot":"","sources":["../src/deployer.ts"],"names":[],"mappings":"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"}
|