@orkestrel/terminal 0.0.13 → 0.0.15
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 +11 -16
- package/dist/src/core/index.cjs +458 -281
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +728 -369
- package/dist/src/core/index.d.ts +728 -369
- package/dist/src/core/index.js +443 -264
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +268 -258
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +217 -201
- package/dist/src/server/index.d.ts +217 -201
- package/dist/src/server/index.js +257 -242
- package/dist/src/server/index.js.map +1 -1
- package/package.json +18 -20
package/dist/src/core/index.cjs
CHANGED
|
@@ -6,44 +6,41 @@ let _orkestrel_form = require("@orkestrel/form");
|
|
|
6
6
|
let _orkestrel_sse = require("@orkestrel/sse");
|
|
7
7
|
let _orkestrel_database = require("@orkestrel/database");
|
|
8
8
|
//#region src/core/constants.ts
|
|
9
|
-
/**
|
|
9
|
+
/** Names the carriage return byte (`\r`, U+000D) — Enter on most terminals. */
|
|
10
10
|
var RETURN = String.fromCharCode(13);
|
|
11
|
-
/**
|
|
11
|
+
/** Names the line feed byte (`\n`, U+000A) — Enter on some terminals / pasted input. */
|
|
12
12
|
var NEWLINE = String.fromCharCode(10);
|
|
13
|
-
/**
|
|
13
|
+
/** Names the tab byte (`\t`, U+0009). */
|
|
14
14
|
var TAB = String.fromCharCode(9);
|
|
15
|
-
/**
|
|
16
|
-
var ESCAPE = String.fromCharCode(27);
|
|
17
|
-
/** Backspace (BS, U+0008) — Ctrl+H / some terminals' Backspace. */
|
|
15
|
+
/** Names the backspace byte (BS, U+0008) — Ctrl+H / some terminals' Backspace. */
|
|
18
16
|
var BACKSPACE = String.fromCharCode(8);
|
|
19
|
-
/**
|
|
17
|
+
/** Names the delete byte (DEL, U+007F) — the usual Backspace byte on a Unix TTY. */
|
|
20
18
|
var DELETE = String.fromCharCode(127);
|
|
21
|
-
/**
|
|
19
|
+
/** Names the space byte (U+0020). */
|
|
22
20
|
var SPACE = " ";
|
|
23
|
-
/** Ctrl+C (ETX, U+0003) — interrupt / cancel. */
|
|
21
|
+
/** Names the Ctrl+C byte (ETX, U+0003) — interrupt / cancel. */
|
|
24
22
|
var CTRL_C = String.fromCharCode(3);
|
|
25
|
-
/** Ctrl+D (EOT, U+0004) — end-of-transmission / finish (the editor's commit key). */
|
|
23
|
+
/** Names the Ctrl+D byte (EOT, U+0004) — end-of-transmission / finish (the editor's commit key). */
|
|
26
24
|
var CTRL_D = String.fromCharCode(4);
|
|
27
|
-
/** Ctrl+U (NAK, U+0015) — clear the current line. */
|
|
25
|
+
/** Names the Ctrl+U byte (NAK, U+0015) — clear the current line. */
|
|
28
26
|
var CTRL_U = String.fromCharCode(21);
|
|
29
|
-
/** Ctrl+A (SOH, U+0001) — move to start of line. */
|
|
27
|
+
/** Names the Ctrl+A byte (SOH, U+0001) — move to start of line. */
|
|
30
28
|
var CTRL_A = String.fromCharCode(1);
|
|
31
|
-
/** Ctrl+E (ENQ, U+0005) — move to end of line. */
|
|
29
|
+
/** Names the Ctrl+E byte (ENQ, U+0005) — move to end of line. */
|
|
32
30
|
var CTRL_E = String.fromCharCode(5);
|
|
33
31
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
32
|
+
* Names the Single Shift Three lead (`ESCO`) — the alternate arrow-key prefix some terminals emit
|
|
33
|
+
* (`ESC O A`). Built from the console module's own {@link import('@orkestrel/console').ESC}; the
|
|
34
|
+
* navigation keys' CSI lead is that module's {@link import('@orkestrel/console').CSI}, which this
|
|
35
|
+
* package reuses rather than redeclaring.
|
|
38
36
|
*/
|
|
39
|
-
var
|
|
40
|
-
/** The Single Shift Three lead (`ESCO`) — the alternate arrow-key prefix some terminals emit (`ESC O A`). */
|
|
41
|
-
var KEY_SS3 = `O`;
|
|
37
|
+
var KEY_SS3 = `${_orkestrel_console.ESC}O`;
|
|
42
38
|
/**
|
|
43
|
-
*
|
|
44
|
-
* consults for the navigation
|
|
45
|
-
* form (`ESCOA`…) of the
|
|
46
|
-
* their numeric-tilde variants
|
|
39
|
+
* Holds the exact escape sequence to canonical key name table
|
|
40
|
+
* {@link import('./helpers.js').parseKey} consults for the navigation and editing keys. Covers the
|
|
41
|
+
* CSI form (`ESC[A`…) and the SS3 form (`ESCOA`…) of the arrows, plus the `home` / `end` / `delete`
|
|
42
|
+
* CSI sequences with their numeric-tilde variants. The source of truth for the multi-byte key
|
|
43
|
+
* decode; frozen.
|
|
47
44
|
*
|
|
48
45
|
* @remarks
|
|
49
46
|
* Terminals disagree on these: a cursor key is `ESC[A` (normal) or `ESCOA` (application mode),
|
|
@@ -52,32 +49,32 @@ var KEY_SS3 = `O`;
|
|
|
52
49
|
* sees the wire encoding.
|
|
53
50
|
*/
|
|
54
51
|
var SEQUENCE_NAMES = Object.freeze({
|
|
55
|
-
[`${
|
|
56
|
-
[`${
|
|
57
|
-
[`${
|
|
58
|
-
[`${
|
|
52
|
+
[`${_orkestrel_console.CSI}A`]: "up",
|
|
53
|
+
[`${_orkestrel_console.CSI}B`]: "down",
|
|
54
|
+
[`${_orkestrel_console.CSI}C`]: "right",
|
|
55
|
+
[`${_orkestrel_console.CSI}D`]: "left",
|
|
59
56
|
[`${KEY_SS3}A`]: "up",
|
|
60
57
|
[`${KEY_SS3}B`]: "down",
|
|
61
58
|
[`${KEY_SS3}C`]: "right",
|
|
62
59
|
[`${KEY_SS3}D`]: "left",
|
|
63
|
-
[`${
|
|
64
|
-
[`${
|
|
65
|
-
[`${
|
|
66
|
-
[`${
|
|
67
|
-
[`${
|
|
68
|
-
[`${
|
|
69
|
-
[`${
|
|
60
|
+
[`${_orkestrel_console.CSI}H`]: "home",
|
|
61
|
+
[`${_orkestrel_console.CSI}F`]: "end",
|
|
62
|
+
[`${_orkestrel_console.CSI}1~`]: "home",
|
|
63
|
+
[`${_orkestrel_console.CSI}4~`]: "end",
|
|
64
|
+
[`${_orkestrel_console.CSI}3~`]: "delete",
|
|
65
|
+
[`${_orkestrel_console.CSI}7~`]: "home",
|
|
66
|
+
[`${_orkestrel_console.CSI}8~`]: "end"
|
|
70
67
|
});
|
|
71
68
|
/**
|
|
72
|
-
*
|
|
73
|
-
* consults for the one-byte keys and the two-byte CRLF
|
|
74
|
-
* canonical `name` and whether it is a `ctrl` combination. The
|
|
75
|
-
* frozen.
|
|
69
|
+
* Holds the control byte (or CRLF pair) to key descriptor table
|
|
70
|
+
* {@link import('./helpers.js').parseKey} consults for the one-byte keys and the two-byte CRLF
|
|
71
|
+
* Enter chunk. Each entry carries the canonical `name` and whether it is a `ctrl` combination. The
|
|
72
|
+
* source of truth for that decode; frozen.
|
|
76
73
|
*
|
|
77
74
|
* @remarks
|
|
78
75
|
* `return` / `newline` / `return + newline` all map to `return` (one canonical Enter name) — a
|
|
79
76
|
* terminal or a paste can deliver Enter as `\r`, `\n`, or the `\r\n` pair in one chunk;
|
|
80
|
-
* `delete` / `backspace` both map to `backspace` (the
|
|
77
|
+
* `delete` / `backspace` both map to `backspace` (the Backspace bytes); the Ctrl combos
|
|
81
78
|
* (`c` / `d` / `u` / `a` / `e`) carry `ctrl: true` so a reducer can match
|
|
82
79
|
* `key.ctrl && key.name === 'c'`. `escape` / `tab` / `space` are plain named keys.
|
|
83
80
|
*/
|
|
@@ -99,7 +96,7 @@ var CONTROL_NAMES = Object.freeze({
|
|
|
99
96
|
name: "tab",
|
|
100
97
|
ctrl: false
|
|
101
98
|
}),
|
|
102
|
-
[
|
|
99
|
+
[_orkestrel_console.ESC]: Object.freeze({
|
|
103
100
|
name: "escape",
|
|
104
101
|
ctrl: false
|
|
105
102
|
}),
|
|
@@ -136,11 +133,12 @@ var CONTROL_NAMES = Object.freeze({
|
|
|
136
133
|
ctrl: true
|
|
137
134
|
})
|
|
138
135
|
});
|
|
139
|
-
/**
|
|
136
|
+
/** Names the default mask glyph {@link import('./helpers.js').createPasswordState} uses — `*`. */
|
|
140
137
|
var DEFAULT_MASK = "*";
|
|
141
138
|
/**
|
|
142
|
-
*
|
|
143
|
-
* the
|
|
139
|
+
* Holds the terminal-owned glyphs {@link DEFAULT_PROMPT_THEME} assembles its `icons` from, beside
|
|
140
|
+
* the console module's own success and error marks. Read only when the default theme is assembled;
|
|
141
|
+
* a view reads its resolved theme and never this constant. Frozen.
|
|
144
142
|
*
|
|
145
143
|
* @remarks
|
|
146
144
|
* - `question` — the leading mark on a prompt's message line.
|
|
@@ -157,7 +155,7 @@ var PROMPT_ICONS = Object.freeze({
|
|
|
157
155
|
unchecked: "☐"
|
|
158
156
|
});
|
|
159
157
|
/**
|
|
160
|
-
*
|
|
158
|
+
* Holds every {@link import('./types.js').PromptRole}, in one frozen list — the role axis's source of
|
|
161
159
|
* truth. {@link import('./helpers.js').createPromptTheme} walks it to merge a partial theme, and
|
|
162
160
|
* a consumer building a complete role map reads it rather than retyping every name.
|
|
163
161
|
*/
|
|
@@ -175,7 +173,7 @@ var PROMPT_ROLES = Object.freeze([
|
|
|
175
173
|
"description"
|
|
176
174
|
]);
|
|
177
175
|
/**
|
|
178
|
-
*
|
|
176
|
+
* Holds the {@link import('./types.js').PromptTheme} every prompt renders with unless its options supply
|
|
179
177
|
* another — the glyph set assembled from {@link PROMPT_ICONS} plus the console
|
|
180
178
|
* {@link import('@orkestrel/console').STATUS_ICONS} `success` / `error` marks, and the console
|
|
181
179
|
* {@link import('@orkestrel/console').Style} each role is painted with. Deeply frozen through the
|
|
@@ -229,30 +227,34 @@ var DEFAULT_PROMPT_THEME = Object.freeze({
|
|
|
229
227
|
description: (0, _orkestrel_console.freezeStyle)({ attributes: ["dim"] })
|
|
230
228
|
})
|
|
231
229
|
});
|
|
232
|
-
/**
|
|
230
|
+
/** Holds how long (ms) the {@link import('./types.js').PromptInterface} broker parks an unanswered form before it expires — 5 minutes. */
|
|
233
231
|
var DEFAULT_PROMPT_TIMEOUT_MS = 3e5;
|
|
234
|
-
/**
|
|
232
|
+
/** Holds how long (ms) the {@link import('./types.js').PromptClientInterface} waits before each reconnect attempt — 2 seconds. */
|
|
235
233
|
var DEFAULT_RECONNECT_DELAY_MS = 2e3;
|
|
236
234
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
235
|
+
* Holds the SSE `event:` names the broker emits and the
|
|
236
|
+
* {@link import('./types.js').PromptClientInterface} dispatches on — `pending`, `expire`, and
|
|
237
|
+
* `destroy`. Frozen; the source of truth for the wire event vocabulary.
|
|
239
238
|
*
|
|
240
239
|
* @remarks
|
|
241
240
|
* - `pending` — a serialized {@link import('./types.js').PendingForm} to dispatch and answer.
|
|
242
|
-
* - `expire` — an `{ id }` payload: the broker expired or released a parked
|
|
243
|
-
* - `
|
|
241
|
+
* - `expire` — an `{ id }` payload: the broker expired or released a parked form (the client drops it).
|
|
242
|
+
* - `destroy` — the broker is going away; the client disconnects (no auto-reconnect) but stays reusable.
|
|
244
243
|
*/
|
|
245
244
|
var SSE_EVENTS = Object.freeze({
|
|
246
245
|
pending: "pending",
|
|
247
246
|
expire: "expire",
|
|
248
|
-
|
|
247
|
+
destroy: "destroy"
|
|
249
248
|
});
|
|
250
|
-
/**
|
|
249
|
+
/**
|
|
250
|
+
* Names the auth-token request header the {@link import('./types.js').PromptClientInterface} sends
|
|
251
|
+
* when a `token` is configured — `x-orkestrel-token`.
|
|
252
|
+
*/
|
|
251
253
|
var HEADER_TOKEN = "x-orkestrel-token";
|
|
252
|
-
/**
|
|
254
|
+
/** Names the `Accept` header value that opens the broker's SSE stream — `text/event-stream`. */
|
|
253
255
|
var ACCEPT_EVENT_STREAM = "text/event-stream";
|
|
254
256
|
/**
|
|
255
|
-
*
|
|
257
|
+
* Sets the maximum number of characters the {@link import('./types.js').PromptClientInterface} lets its
|
|
256
258
|
* SSE parser buffer before treating the stream as hostile — 1 MiB, comfortably above any
|
|
257
259
|
* legitimate prompt payload. Passed as the `limit` to `createSSEParser` so an unterminated
|
|
258
260
|
* or oversized `data:` field cannot grow the buffer without bound (a memory-exhaustion guard).
|
|
@@ -261,7 +263,7 @@ var SSE_BUFFER_LIMIT = 1048576;
|
|
|
261
263
|
//#endregion
|
|
262
264
|
//#region src/core/errors.ts
|
|
263
265
|
/**
|
|
264
|
-
*
|
|
266
|
+
* Represents the error the terminal surfaces for its own refusals: parking on a destroyed or full broker, an
|
|
265
267
|
* unusable driver stream, a manager routing fault, or a ctrl-c cancellation. A parked form's own
|
|
266
268
|
* lifecycle failures reject through the form's `answer` with the form package's error, never with
|
|
267
269
|
* this one.
|
|
@@ -272,10 +274,17 @@ var SSE_BUFFER_LIMIT = 1048576;
|
|
|
272
274
|
* a caught value with {@link isTerminalError} and branch on `error.code`.
|
|
273
275
|
*/
|
|
274
276
|
var TerminalError = class extends Error {
|
|
275
|
-
/**
|
|
277
|
+
/** Holds the machine-readable condition — see {@link TerminalErrorCode}. */
|
|
276
278
|
code;
|
|
277
|
-
/**
|
|
279
|
+
/** Holds an optional context bag naming the offending values — see the class {@link TerminalError remarks}. */
|
|
278
280
|
context;
|
|
281
|
+
/**
|
|
282
|
+
* Builds one terminal refusal.
|
|
283
|
+
*
|
|
284
|
+
* @param code - The machine-readable {@link TerminalErrorCode} a caller branches on
|
|
285
|
+
* @param message - The human-readable reason
|
|
286
|
+
* @param context - The optional bag naming the offending values — see the class {@link TerminalError remarks}
|
|
287
|
+
*/
|
|
279
288
|
constructor(code, message, context) {
|
|
280
289
|
super(message);
|
|
281
290
|
this.name = "TerminalError";
|
|
@@ -284,10 +293,10 @@ var TerminalError = class extends Error {
|
|
|
284
293
|
}
|
|
285
294
|
};
|
|
286
295
|
/**
|
|
287
|
-
*
|
|
296
|
+
* Narrows an unknown caught value to a {@link TerminalError}, so a caller can branch on its `code`.
|
|
288
297
|
*
|
|
289
298
|
* @param value - The value to test (typically a `catch` binding or a rejected prompt call)
|
|
290
|
-
* @returns
|
|
299
|
+
* @returns True if `value` is a {@link TerminalError}; false otherwise
|
|
291
300
|
*
|
|
292
301
|
* @example
|
|
293
302
|
* ```ts
|
|
@@ -305,17 +314,23 @@ function isTerminalError(value) {
|
|
|
305
314
|
}
|
|
306
315
|
//#endregion
|
|
307
316
|
//#region src/core/validators.ts
|
|
308
|
-
/**
|
|
317
|
+
/**
|
|
318
|
+
* Narrows an unknown value to a {@link PendingFormStatus}.
|
|
319
|
+
*
|
|
320
|
+
* @param value - The candidate ticket status
|
|
321
|
+
* @returns True if the value is one of the declared ticket statuses; false otherwise
|
|
322
|
+
*/
|
|
309
323
|
var isPendingFormStatus = (0, _orkestrel_contract.literalOf)("pending", "answered", "expired");
|
|
310
324
|
/**
|
|
311
|
-
*
|
|
325
|
+
* Narrows an unknown wire value to a {@link PendingForm} envelope — the envelope alone, because the
|
|
326
|
+
* form package's `parseForm` owns the schema payload.
|
|
312
327
|
*
|
|
313
328
|
* @remarks
|
|
314
329
|
* This guard checks the transport record and proves only that `schema` is a record. The Form
|
|
315
330
|
* package's `parseForm` owns the schema payload and its semantic audit.
|
|
316
331
|
*
|
|
317
332
|
* @param value - The decoded wire value to inspect
|
|
318
|
-
* @returns
|
|
333
|
+
* @returns True if the value is a complete pending-form envelope; false otherwise
|
|
319
334
|
*/
|
|
320
335
|
function isPendingForm(value) {
|
|
321
336
|
return (0, _orkestrel_contract.recordOf)({
|
|
@@ -328,17 +343,24 @@ function isPendingForm(value) {
|
|
|
328
343
|
}, ["from", "to"])(value);
|
|
329
344
|
}
|
|
330
345
|
/**
|
|
331
|
-
*
|
|
346
|
+
* Narrows an unknown value to a transport-neutral {@link WireEvent} — the guard a consumer's own
|
|
347
|
+
* transport applies to an inbound frame.
|
|
332
348
|
*
|
|
333
349
|
* @param value - The candidate wire event
|
|
334
|
-
* @returns
|
|
350
|
+
* @returns True if the value carries an event name, serialized data, and an optional id; false otherwise
|
|
335
351
|
*/
|
|
336
352
|
var isWireEvent = (0, _orkestrel_contract.recordOf)({
|
|
337
353
|
event: _orkestrel_contract.isString,
|
|
338
354
|
data: _orkestrel_contract.isString,
|
|
339
355
|
id: _orkestrel_contract.isString
|
|
340
356
|
}, ["id"]);
|
|
341
|
-
/**
|
|
357
|
+
/**
|
|
358
|
+
* Narrows an unknown value to a {@link TerminalSnapshot} — a non-empty `id` and an optional numeric
|
|
359
|
+
* `timeout`, the read boundary a store applies to an untrusted persisted row.
|
|
360
|
+
*
|
|
361
|
+
* @param value - The candidate snapshot read back from storage
|
|
362
|
+
* @returns True if the value carries a non-empty `id` and an optional numeric `timeout`; false otherwise
|
|
363
|
+
*/
|
|
342
364
|
var isTerminalSnapshot = (0, _orkestrel_contract.recordOf)({
|
|
343
365
|
id: _orkestrel_contract.isNonEmptyString,
|
|
344
366
|
timeout: _orkestrel_contract.isNumber
|
|
@@ -346,24 +368,24 @@ var isTerminalSnapshot = (0, _orkestrel_contract.recordOf)({
|
|
|
346
368
|
//#endregion
|
|
347
369
|
//#region src/core/helpers.ts
|
|
348
370
|
/**
|
|
349
|
-
*
|
|
350
|
-
* read as UTF-8; the resulting string is matched against the known control bytes and the CRLF
|
|
351
|
-
*
|
|
352
|
-
*
|
|
371
|
+
* Decodes one keypress's bytes into a {@link KeyEvent} — total, never throws. A `Uint8Array` is
|
|
372
|
+
* read as UTF-8; the resulting string is matched against the known control bytes and the CRLF pair
|
|
373
|
+
* ({@link CONTROL_NAMES}) and escape sequences ({@link SEQUENCE_NAMES}), falling back to a single
|
|
374
|
+
* printable character. An unrecognized sequence carries no `name`, with the raw `sequence`
|
|
353
375
|
* preserved.
|
|
354
376
|
*
|
|
355
377
|
* @remarks
|
|
356
378
|
* - **Single control byte.** A one-character control input (`return` / `backspace` / `tab` /
|
|
357
379
|
* `escape` / `space`, or a Ctrl combo `c` / `d` / `u` / `a` / `e`), or the two-byte `\r\n`
|
|
358
380
|
* CRLF pair, is looked up in {@link CONTROL_NAMES}, carrying its `ctrl` flag.
|
|
359
|
-
* - **Escape sequence.** A multi-byte ESC sequence (`up` / `down` / `left` / `right` in
|
|
381
|
+
* - **Escape sequence.** A multi-byte ESC sequence (`up` / `down` / `left` / `right` in both the
|
|
360
382
|
* `ESC[A` and `ESCOA` forms, plus `home` / `end` / `delete`) is looked up in
|
|
361
383
|
* {@link SEQUENCE_NAMES} and flagged `meta`.
|
|
362
384
|
* - **Printable character.** A single printable character becomes `name` = that character, with
|
|
363
385
|
* `shift` set when it is an uppercase letter. A multi-code-point printable (an emoji, a pasted
|
|
364
386
|
* run) keeps its first code point as the name and the whole input as `sequence`.
|
|
365
|
-
* - **Unknown.** Anything else (an unrecognized escape, an empty input) yields
|
|
366
|
-
* total, so the driver never crashes on a stray byte.
|
|
387
|
+
* - **Unknown.** Anything else (an unrecognized escape, an empty input) yields an event with NO
|
|
388
|
+
* `name` — absence, never an empty string — total, so the driver never crashes on a stray byte.
|
|
367
389
|
*
|
|
368
390
|
* @param input - The raw keypress bytes, as a string or `Uint8Array`
|
|
369
391
|
* @returns The decoded {@link KeyEvent}
|
|
@@ -404,14 +426,19 @@ function parseKey(input) {
|
|
|
404
426
|
shift: first !== first.toLowerCase()
|
|
405
427
|
};
|
|
406
428
|
return {
|
|
407
|
-
name: "",
|
|
408
429
|
sequence,
|
|
409
430
|
ctrl: false,
|
|
410
431
|
meta: false,
|
|
411
432
|
shift: false
|
|
412
433
|
};
|
|
413
434
|
}
|
|
414
|
-
/**
|
|
435
|
+
/**
|
|
436
|
+
* Checks whether a single character is printable — the fallback test {@link parseKey} applies after
|
|
437
|
+
* the control bytes and the escape sequences, so the C0 controls and DEL are excluded.
|
|
438
|
+
*
|
|
439
|
+
* @param character - The single character to test
|
|
440
|
+
* @returns True if the character is at or above space and is not DEL; false otherwise
|
|
441
|
+
*/
|
|
415
442
|
function isPrintable(character) {
|
|
416
443
|
if (character.length === 0) return false;
|
|
417
444
|
const code = character.codePointAt(0);
|
|
@@ -419,7 +446,7 @@ function isPrintable(character) {
|
|
|
419
446
|
return code >= 32 && code !== 127;
|
|
420
447
|
}
|
|
421
448
|
/**
|
|
422
|
-
*
|
|
449
|
+
* Builds a complete {@link PromptTheme} by merging a partial one over
|
|
423
450
|
* {@link DEFAULT_PROMPT_THEME}, leaf by leaf — each supplied icon replaces that glyph, each
|
|
424
451
|
* supplied role replaces that {@link Style}, and everything else keeps its default. Each supplied
|
|
425
452
|
* style is snapshotted through the console module's own
|
|
@@ -454,7 +481,7 @@ function createPromptTheme(options) {
|
|
|
454
481
|
});
|
|
455
482
|
}
|
|
456
483
|
/**
|
|
457
|
-
*
|
|
484
|
+
* Sanitizes text for one single-line display slot. Composes console's ANSI {@link strip} and C0
|
|
458
485
|
* {@link stripControls} passes with removal of tab, line feed, and carriage return.
|
|
459
486
|
*
|
|
460
487
|
* @param text - The text to sanitize for a glyph or hint slot
|
|
@@ -469,7 +496,8 @@ function sanitizeDisplayText(text) {
|
|
|
469
496
|
return (0, _orkestrel_console.stripControls)((0, _orkestrel_console.strip)(text)).replaceAll(" ", "").replaceAll("\n", "").replaceAll("\r", "");
|
|
470
497
|
}
|
|
471
498
|
/**
|
|
472
|
-
*
|
|
499
|
+
* Sanitizes every terminal-readable string in a parsed form schema, keeping every identity and
|
|
500
|
+
* answer string verbatim and dropping field metadata.
|
|
473
501
|
*
|
|
474
502
|
* @remarks
|
|
475
503
|
* Display strings pass through {@link sanitizeDisplayText}: labels, help, placeholders, masks,
|
|
@@ -511,6 +539,7 @@ function sanitizeSchema(schema) {
|
|
|
511
539
|
switch (field.control) {
|
|
512
540
|
case "text":
|
|
513
541
|
case "editor":
|
|
542
|
+
case "number":
|
|
514
543
|
fields.push({
|
|
515
544
|
...field,
|
|
516
545
|
...shared,
|
|
@@ -524,22 +553,10 @@ function sanitizeSchema(schema) {
|
|
|
524
553
|
...field.mask !== void 0 ? { mask: sanitizeDisplayText(field.mask) } : {}
|
|
525
554
|
});
|
|
526
555
|
break;
|
|
527
|
-
case "number":
|
|
528
|
-
fields.push({
|
|
529
|
-
...field,
|
|
530
|
-
...shared,
|
|
531
|
-
...field.placeholder !== void 0 ? { placeholder: sanitizeDisplayText(field.placeholder) } : {}
|
|
532
|
-
});
|
|
533
|
-
break;
|
|
534
556
|
case "date":
|
|
535
557
|
case "time":
|
|
536
558
|
case "datetime":
|
|
537
559
|
case "color":
|
|
538
|
-
fields.push({
|
|
539
|
-
...field,
|
|
540
|
-
...shared
|
|
541
|
-
});
|
|
542
|
-
break;
|
|
543
560
|
case "confirm":
|
|
544
561
|
fields.push({
|
|
545
562
|
...field,
|
|
@@ -547,17 +564,6 @@ function sanitizeSchema(schema) {
|
|
|
547
564
|
});
|
|
548
565
|
break;
|
|
549
566
|
case "select":
|
|
550
|
-
fields.push({
|
|
551
|
-
...field,
|
|
552
|
-
...shared,
|
|
553
|
-
choices: field.choices.map((choice) => ({
|
|
554
|
-
...choice,
|
|
555
|
-
value: choice.value,
|
|
556
|
-
label: sanitizeDisplayText(choice.label),
|
|
557
|
-
...choice.help !== void 0 ? { help: sanitizeDisplayText(choice.help) } : {}
|
|
558
|
-
}))
|
|
559
|
-
});
|
|
560
|
-
break;
|
|
561
567
|
case "checkbox":
|
|
562
568
|
fields.push({
|
|
563
569
|
...field,
|
|
@@ -586,7 +592,7 @@ function sanitizeSchema(schema) {
|
|
|
586
592
|
};
|
|
587
593
|
}
|
|
588
594
|
/**
|
|
589
|
-
*
|
|
595
|
+
* Sanitizes every glyph a wire-supplied {@link PromptThemeOptions} carries for a single-line display
|
|
590
596
|
* slot. Only the icons need it: a role is guard-narrowed to a console {@link Style}, whose colors
|
|
591
597
|
* and attributes are fixed name sets, so no role can carry a byte a terminal would act on.
|
|
592
598
|
*
|
|
@@ -603,13 +609,21 @@ function sanitizeThemeIcons(theme) {
|
|
|
603
609
|
icons: sanitized
|
|
604
610
|
};
|
|
605
611
|
}
|
|
606
|
-
/**
|
|
607
|
-
|
|
612
|
+
/**
|
|
613
|
+
* Renders the styled question header (`? message`) — the leading line every active prompt view
|
|
614
|
+
* shares, themed by the `question` + `message` roles.
|
|
615
|
+
*
|
|
616
|
+
* @param styler - The console styler that renders each role
|
|
617
|
+
* @param theme - The resolved prompt theme
|
|
618
|
+
* @param message - The prompt's question text
|
|
619
|
+
* @returns The rendered question header
|
|
620
|
+
*/
|
|
621
|
+
function renderPromptHeader(styler, theme, message) {
|
|
608
622
|
return `${styler.render(theme.roles.question, theme.icons.question)} ${styler.render(theme.roles.message, message)}`;
|
|
609
623
|
}
|
|
610
624
|
/**
|
|
611
|
-
*
|
|
612
|
-
* when no hint is supplied.
|
|
625
|
+
* Renders a question header followed by a key hint painted with the `hint` role, or the header
|
|
626
|
+
* alone when no hint is supplied.
|
|
613
627
|
*
|
|
614
628
|
* @param styler - The console styler that renders each role
|
|
615
629
|
* @param theme - The resolved prompt theme
|
|
@@ -617,20 +631,37 @@ function promptHeader(styler, theme, message) {
|
|
|
617
631
|
* @param hint - The optional key hint to append
|
|
618
632
|
* @returns The rendered header with the optional hint
|
|
619
633
|
*/
|
|
620
|
-
function
|
|
621
|
-
const head =
|
|
634
|
+
function renderHintedHeader(styler, theme, message, hint) {
|
|
635
|
+
const head = renderPromptHeader(styler, theme, message);
|
|
622
636
|
return hint === void 0 ? head : `${head} ${styler.render(theme.roles.hint, hint)}`;
|
|
623
637
|
}
|
|
624
|
-
/**
|
|
625
|
-
|
|
638
|
+
/**
|
|
639
|
+
* Renders the styled submit line (`✔ message`) — the committed header an interactive prompt shows
|
|
640
|
+
* after it resolves, themed by the `success` + `message` roles.
|
|
641
|
+
*
|
|
642
|
+
* @param styler - The console styler that renders each role
|
|
643
|
+
* @param theme - The resolved prompt theme
|
|
644
|
+
* @param message - The settled prompt's question text
|
|
645
|
+
* @returns The rendered committed header
|
|
646
|
+
*/
|
|
647
|
+
function renderSubmitHeader(styler, theme, message) {
|
|
626
648
|
return `${styler.render(theme.roles.success, theme.icons.success)} ${styler.render(theme.roles.message, message)}`;
|
|
627
649
|
}
|
|
628
|
-
/**
|
|
629
|
-
|
|
650
|
+
/**
|
|
651
|
+
* Renders the styled failure line (`✖ message`) a form driver writes for each refused field before
|
|
652
|
+
* it asks that field again.
|
|
653
|
+
*
|
|
654
|
+
* @param styler - The console styler that renders each role
|
|
655
|
+
* @param theme - The resolved prompt theme
|
|
656
|
+
* @param message - The failure text, normally the field's label and the reason
|
|
657
|
+
* @returns The rendered failure line
|
|
658
|
+
*/
|
|
659
|
+
function renderErrorLine(styler, theme, message) {
|
|
630
660
|
return `${styler.render(theme.roles.error, theme.icons.error)} ${styler.render(theme.roles.error, message)}`;
|
|
631
661
|
}
|
|
632
662
|
/**
|
|
633
|
-
*
|
|
663
|
+
* Builds the initial text-field reducer state — the sanitized label, the declared default, the
|
|
664
|
+
* styler, and the resolved theme.
|
|
634
665
|
*
|
|
635
666
|
* @param field - The text field to render
|
|
636
667
|
* @param styler - The styler used to render the view
|
|
@@ -646,22 +677,32 @@ function createInputState(field, styler = (0, _orkestrel_console.createStyler)()
|
|
|
646
677
|
value: ""
|
|
647
678
|
};
|
|
648
679
|
}
|
|
649
|
-
/**
|
|
650
|
-
|
|
680
|
+
/**
|
|
681
|
+
* Renders a text-field reducer state as a styled view — the header, the pointer, and the typed
|
|
682
|
+
* value, or the default shown as a hint while nothing is typed.
|
|
683
|
+
*
|
|
684
|
+
* @param state - The text field's current reducer state
|
|
685
|
+
* @returns The rendered single-line view — header, pointer, and the typed value or the default
|
|
686
|
+
*/
|
|
687
|
+
function renderInputView(state) {
|
|
651
688
|
const content = state.value.length > 0 ? state.value : state.default;
|
|
652
689
|
const role = state.value.length > 0 ? state.theme.roles.content : state.theme.roles.hint;
|
|
653
690
|
const shown = state.styler.render(role, sanitizeDisplayText(content));
|
|
654
|
-
return `${
|
|
691
|
+
return `${renderPromptHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)} ${shown}`;
|
|
655
692
|
}
|
|
656
693
|
/**
|
|
657
|
-
*
|
|
694
|
+
* Advances an input prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
658
695
|
* reducer. Printable characters extend the value; backspace shrinks it; ctrl-u clears it; ctrl-c
|
|
659
696
|
* cancels; return produces the candidate value, with an empty line falling back to the default.
|
|
697
|
+
*
|
|
698
|
+
* @param state - The text field's current reducer state
|
|
699
|
+
* @param key - The decoded keypress to apply
|
|
700
|
+
* @returns The next step — the new state, the rendered view, the status, and the value on submit
|
|
660
701
|
*/
|
|
661
|
-
function
|
|
702
|
+
function reduceInput(state, key) {
|
|
662
703
|
if (key.ctrl && key.name === "c") return {
|
|
663
704
|
state,
|
|
664
|
-
view:
|
|
705
|
+
view: renderInputView(state),
|
|
665
706
|
status: "cancel"
|
|
666
707
|
};
|
|
667
708
|
if (key.name === "return") {
|
|
@@ -671,7 +712,7 @@ function inputReduce(state, key) {
|
|
|
671
712
|
...state,
|
|
672
713
|
value: answer
|
|
673
714
|
},
|
|
674
|
-
view: `${
|
|
715
|
+
view: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, sanitizeDisplayText(answer))}`,
|
|
675
716
|
status: "submit",
|
|
676
717
|
value: answer
|
|
677
718
|
};
|
|
@@ -679,7 +720,7 @@ function inputReduce(state, key) {
|
|
|
679
720
|
const value = editLine(state.value, key);
|
|
680
721
|
if (value === void 0) return {
|
|
681
722
|
state,
|
|
682
|
-
view:
|
|
723
|
+
view: renderInputView(state),
|
|
683
724
|
status: "active"
|
|
684
725
|
};
|
|
685
726
|
const next = {
|
|
@@ -688,12 +729,13 @@ function inputReduce(state, key) {
|
|
|
688
729
|
};
|
|
689
730
|
return {
|
|
690
731
|
state: next,
|
|
691
|
-
view:
|
|
732
|
+
view: renderInputView(next),
|
|
692
733
|
status: "active"
|
|
693
734
|
};
|
|
694
735
|
}
|
|
695
736
|
/**
|
|
696
|
-
*
|
|
737
|
+
* Builds the initial password-field reducer state — the text-field state, plus the mask glyph each
|
|
738
|
+
* typed character renders as.
|
|
697
739
|
*
|
|
698
740
|
* @param field - The password field to render
|
|
699
741
|
* @param styler - The styler used to render the view
|
|
@@ -709,32 +751,42 @@ function createPasswordState(field, styler = (0, _orkestrel_console.createStyler
|
|
|
709
751
|
value: ""
|
|
710
752
|
};
|
|
711
753
|
}
|
|
712
|
-
/**
|
|
713
|
-
|
|
754
|
+
/**
|
|
755
|
+
* Renders a password-field reducer state as a styled view, with the value replaced by the mask
|
|
756
|
+
* repeated so the secret is never echoed.
|
|
757
|
+
*
|
|
758
|
+
* @param state - The password field's current reducer state
|
|
759
|
+
* @returns The rendered view, with the mask repeated in place of the typed value
|
|
760
|
+
*/
|
|
761
|
+
function renderPasswordView(state) {
|
|
714
762
|
const masked = state.styler.render(state.theme.roles.content, state.mask.repeat(state.value.length));
|
|
715
|
-
return `${
|
|
763
|
+
return `${renderPromptHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)} ${masked}`;
|
|
716
764
|
}
|
|
717
765
|
/**
|
|
718
|
-
*
|
|
719
|
-
* reducer. Identical line-editing to {@link
|
|
766
|
+
* Advances a password prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
767
|
+
* reducer. Identical line-editing to {@link reduceInput} (printable extends, backspace shrinks,
|
|
720
768
|
* ctrl-u clears, ctrl-c cancels) but the view masks the value. Return produces the candidate value.
|
|
769
|
+
*
|
|
770
|
+
* @param state - The password field's current reducer state
|
|
771
|
+
* @param key - The decoded keypress to apply
|
|
772
|
+
* @returns The next step — the new state, the masked view, the status, and the value on submit
|
|
721
773
|
*/
|
|
722
|
-
function
|
|
774
|
+
function reducePassword(state, key) {
|
|
723
775
|
if (key.ctrl && key.name === "c") return {
|
|
724
776
|
state,
|
|
725
|
-
view:
|
|
777
|
+
view: renderPasswordView(state),
|
|
726
778
|
status: "cancel"
|
|
727
779
|
};
|
|
728
780
|
if (key.name === "return") return {
|
|
729
781
|
state,
|
|
730
|
-
view: `${
|
|
782
|
+
view: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, state.mask.repeat(state.value.length))}`,
|
|
731
783
|
status: "submit",
|
|
732
784
|
value: state.value
|
|
733
785
|
};
|
|
734
786
|
const value = editLine(state.value, key);
|
|
735
787
|
if (value === void 0) return {
|
|
736
788
|
state,
|
|
737
|
-
view:
|
|
789
|
+
view: renderPasswordView(state),
|
|
738
790
|
status: "active"
|
|
739
791
|
};
|
|
740
792
|
const next = {
|
|
@@ -743,12 +795,13 @@ function passwordReduce(state, key) {
|
|
|
743
795
|
};
|
|
744
796
|
return {
|
|
745
797
|
state: next,
|
|
746
|
-
view:
|
|
798
|
+
view: renderPasswordView(next),
|
|
747
799
|
status: "active"
|
|
748
800
|
};
|
|
749
801
|
}
|
|
750
802
|
/**
|
|
751
|
-
*
|
|
803
|
+
* Builds the initial confirm-field reducer state — the sanitized label and the declared default
|
|
804
|
+
* answer.
|
|
752
805
|
*
|
|
753
806
|
* @param field - The confirm field to render
|
|
754
807
|
* @param styler - The styler used to render the view
|
|
@@ -764,43 +817,52 @@ function createConfirmState(field, styler = (0, _orkestrel_console.createStyler)
|
|
|
764
817
|
};
|
|
765
818
|
}
|
|
766
819
|
/**
|
|
767
|
-
*
|
|
820
|
+
* Renders a confirm-field reducer state as a styled view — the header and the yes/no group, with
|
|
821
|
+
* the default letter capitalized and painted by the `selected` role.
|
|
822
|
+
*
|
|
823
|
+
* @param state - The confirm field's current reducer state
|
|
824
|
+
* @returns The rendered view — the header and the yes/no group with the default capitalized
|
|
768
825
|
*/
|
|
769
|
-
function
|
|
770
|
-
const head =
|
|
826
|
+
function renderConfirmView(state) {
|
|
827
|
+
const head = renderPromptHeader(state.styler, state.theme, state.message);
|
|
771
828
|
const answer = state.default ? `${state.styler.render(state.theme.roles.selected, "Y")}${state.styler.render(state.theme.roles.hint, "/n")}` : `${state.styler.render(state.theme.roles.hint, "y/")}${state.styler.render(state.theme.roles.selected, "N")}`;
|
|
772
829
|
return `${head} ${state.styler.render(state.theme.roles.hint, "(")}${answer}${state.styler.render(state.theme.roles.hint, ")")}`;
|
|
773
830
|
}
|
|
774
831
|
/**
|
|
775
|
-
*
|
|
832
|
+
* Advances a confirm prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<boolean>`
|
|
776
833
|
* reducer. `y` / `Y` submits `true`, `n` / `N` submits `false`, return on an empty line submits
|
|
777
834
|
* the `default`, ctrl-c cancels; any other key is ignored (stays active).
|
|
835
|
+
*
|
|
836
|
+
* @param state - The confirm field's current reducer state
|
|
837
|
+
* @param key - The decoded keypress to apply
|
|
838
|
+
* @returns The next step — the state, the rendered view, the status, and the answer on submit
|
|
778
839
|
*/
|
|
779
|
-
function
|
|
840
|
+
function reduceConfirm(state, key) {
|
|
780
841
|
if (key.ctrl && key.name === "c") return {
|
|
781
842
|
state,
|
|
782
|
-
view:
|
|
843
|
+
view: renderConfirmView(state),
|
|
783
844
|
status: "cancel"
|
|
784
845
|
};
|
|
785
846
|
let answer;
|
|
786
|
-
const choice = key.name
|
|
847
|
+
const choice = key.name?.toLowerCase();
|
|
787
848
|
if (key.name === "return") answer = state.default;
|
|
788
849
|
else if (choice === "y") answer = true;
|
|
789
850
|
else if (choice === "n") answer = false;
|
|
790
851
|
if (answer === void 0) return {
|
|
791
852
|
state,
|
|
792
|
-
view:
|
|
853
|
+
view: renderConfirmView(state),
|
|
793
854
|
status: "active"
|
|
794
855
|
};
|
|
795
856
|
return {
|
|
796
857
|
state,
|
|
797
|
-
view: `${
|
|
858
|
+
view: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, answer ? "yes" : "no")}`,
|
|
798
859
|
status: "submit",
|
|
799
860
|
value: answer
|
|
800
861
|
};
|
|
801
862
|
}
|
|
802
863
|
/**
|
|
803
|
-
*
|
|
864
|
+
* Builds the initial select-field reducer state — the offered choices, with the focus pre-placed on
|
|
865
|
+
* the declared default.
|
|
804
866
|
*
|
|
805
867
|
* @param field - The select field to render
|
|
806
868
|
* @param styler - The styler used to render the view
|
|
@@ -818,30 +880,40 @@ function createSelectState(field, styler = (0, _orkestrel_console.createStyler)(
|
|
|
818
880
|
focused: index >= 0 ? index : 0
|
|
819
881
|
};
|
|
820
882
|
}
|
|
821
|
-
/**
|
|
822
|
-
|
|
883
|
+
/**
|
|
884
|
+
* Renders a select-field reducer state as a multi-line styled view — one row per choice, with the
|
|
885
|
+
* focused row marked and its help shown.
|
|
886
|
+
*
|
|
887
|
+
* @param state - The select field's current reducer state
|
|
888
|
+
* @returns The rendered view — the header, then one row per choice with the focused row marked
|
|
889
|
+
*/
|
|
890
|
+
function renderSelectView(state) {
|
|
823
891
|
const lines = state.choices.map((choice, index) => {
|
|
824
892
|
const active = index === state.focused;
|
|
825
893
|
return `${active ? state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer) : " "} ${active ? state.styler.render(state.theme.roles.selected, state.theme.icons.selected) : state.styler.render(state.theme.roles.muted, state.theme.icons.dot)} ${active ? state.styler.render(state.theme.roles.focus, choice.label) : state.styler.render(state.theme.roles.content, choice.label)}${choice.help === void 0 ? "" : ` ${state.styler.render(state.theme.roles.description, choice.help)}`}`;
|
|
826
894
|
});
|
|
827
|
-
return [
|
|
895
|
+
return [renderPromptHeader(state.styler, state.theme, state.message), ...lines].join("\n");
|
|
828
896
|
}
|
|
829
897
|
/**
|
|
830
|
-
*
|
|
831
|
-
* reducer. `up` / `down` (and `k` / `j`) move the focus,
|
|
898
|
+
* Advances a select prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
899
|
+
* reducer. `up` / `down` (and `k` / `j`) move the focus, wrapping at the ends; return submits the
|
|
832
900
|
* focused choice's `value`; ctrl-c cancels. An empty choice list can never submit (a higher layer
|
|
833
901
|
* guards against it); any other key is ignored.
|
|
902
|
+
*
|
|
903
|
+
* @param state - The select field's current reducer state
|
|
904
|
+
* @param key - The decoded keypress to apply
|
|
905
|
+
* @returns The next step — the state, the rendered view, the status, and the chosen value on submit
|
|
834
906
|
*/
|
|
835
|
-
function
|
|
907
|
+
function reduceSelect(state, key) {
|
|
836
908
|
if (key.ctrl && key.name === "c") return {
|
|
837
909
|
state,
|
|
838
|
-
view:
|
|
910
|
+
view: renderSelectView(state),
|
|
839
911
|
status: "cancel"
|
|
840
912
|
};
|
|
841
913
|
const count = state.choices.length;
|
|
842
914
|
if (count === 0) return {
|
|
843
915
|
state,
|
|
844
|
-
view:
|
|
916
|
+
view: renderSelectView(state),
|
|
845
917
|
status: "active"
|
|
846
918
|
};
|
|
847
919
|
if (key.name === "up" || key.name === "k") {
|
|
@@ -851,7 +923,7 @@ function selectReduce(state, key) {
|
|
|
851
923
|
};
|
|
852
924
|
return {
|
|
853
925
|
state: next,
|
|
854
|
-
view:
|
|
926
|
+
view: renderSelectView(next),
|
|
855
927
|
status: "active"
|
|
856
928
|
};
|
|
857
929
|
}
|
|
@@ -862,7 +934,7 @@ function selectReduce(state, key) {
|
|
|
862
934
|
};
|
|
863
935
|
return {
|
|
864
936
|
state: next,
|
|
865
|
-
view:
|
|
937
|
+
view: renderSelectView(next),
|
|
866
938
|
status: "active"
|
|
867
939
|
};
|
|
868
940
|
}
|
|
@@ -871,19 +943,20 @@ function selectReduce(state, key) {
|
|
|
871
943
|
const value = choice?.value ?? "";
|
|
872
944
|
return {
|
|
873
945
|
state,
|
|
874
|
-
view: `${
|
|
946
|
+
view: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, choice?.label ?? "")}`,
|
|
875
947
|
status: "submit",
|
|
876
948
|
value
|
|
877
949
|
};
|
|
878
950
|
}
|
|
879
951
|
return {
|
|
880
952
|
state,
|
|
881
|
-
view:
|
|
953
|
+
view: renderSelectView(state),
|
|
882
954
|
status: "active"
|
|
883
955
|
};
|
|
884
956
|
}
|
|
885
957
|
/**
|
|
886
|
-
*
|
|
958
|
+
* Builds the initial checkbox-field reducer state — the offered choices, with every value in the
|
|
959
|
+
* field's `default` list pre-checked.
|
|
887
960
|
*
|
|
888
961
|
* @param field - The checkbox field to render
|
|
889
962
|
* @param styler - The styler used to render the view
|
|
@@ -905,8 +978,14 @@ function createCheckboxState(field, styler = (0, _orkestrel_console.createStyler
|
|
|
905
978
|
checked
|
|
906
979
|
};
|
|
907
980
|
}
|
|
908
|
-
/**
|
|
909
|
-
|
|
981
|
+
/**
|
|
982
|
+
* Renders a checkbox-field reducer state as a multi-line styled view — one box per choice, and the
|
|
983
|
+
* selected count beneath them.
|
|
984
|
+
*
|
|
985
|
+
* @param state - The checkbox field's current reducer state
|
|
986
|
+
* @returns The rendered view — the header, one box per choice, and the selected count
|
|
987
|
+
*/
|
|
988
|
+
function renderCheckboxView(state) {
|
|
910
989
|
const lines = state.choices.map((choice, index) => {
|
|
911
990
|
const active = index === state.focused;
|
|
912
991
|
const ticked = state.checked.includes(index);
|
|
@@ -914,21 +993,25 @@ function checkboxView(state) {
|
|
|
914
993
|
});
|
|
915
994
|
const summary = state.styler.render(state.theme.roles.hint, `${state.checked.length} selected`);
|
|
916
995
|
return [
|
|
917
|
-
|
|
996
|
+
renderPromptHeader(state.styler, state.theme, state.message),
|
|
918
997
|
...lines,
|
|
919
998
|
summary
|
|
920
999
|
].join("\n");
|
|
921
1000
|
}
|
|
922
1001
|
/**
|
|
923
|
-
*
|
|
1002
|
+
* Advances a checkbox prompt by one {@link KeyEvent} — the pure
|
|
924
1003
|
* `(state, key) → PromptStep<readonly string[]>` reducer. `up` / `down` (and `k` / `j`) move the
|
|
925
1004
|
* focus (wrapping); `space` toggles the focused index in the checked set; return submits the
|
|
926
1005
|
* checked values in choice order; ctrl-c cancels. The form applies selection-count rules.
|
|
1006
|
+
*
|
|
1007
|
+
* @param state - The checkbox field's current reducer state
|
|
1008
|
+
* @param key - The decoded keypress to apply
|
|
1009
|
+
* @returns The next step — the state, the rendered view, the status, and the ticked values on submit
|
|
927
1010
|
*/
|
|
928
|
-
function
|
|
1011
|
+
function reduceCheckbox(state, key) {
|
|
929
1012
|
if (key.ctrl && key.name === "c") return {
|
|
930
1013
|
state,
|
|
931
|
-
view:
|
|
1014
|
+
view: renderCheckboxView(state),
|
|
932
1015
|
status: "cancel"
|
|
933
1016
|
};
|
|
934
1017
|
const count = state.choices.length;
|
|
@@ -939,7 +1022,7 @@ function checkboxReduce(state, key) {
|
|
|
939
1022
|
};
|
|
940
1023
|
return {
|
|
941
1024
|
state: next,
|
|
942
|
-
view:
|
|
1025
|
+
view: renderCheckboxView(next),
|
|
943
1026
|
status: "active"
|
|
944
1027
|
};
|
|
945
1028
|
}
|
|
@@ -950,7 +1033,7 @@ function checkboxReduce(state, key) {
|
|
|
950
1033
|
};
|
|
951
1034
|
return {
|
|
952
1035
|
state: next,
|
|
953
|
-
view:
|
|
1036
|
+
view: renderCheckboxView(next),
|
|
954
1037
|
status: "active"
|
|
955
1038
|
};
|
|
956
1039
|
}
|
|
@@ -962,7 +1045,7 @@ function checkboxReduce(state, key) {
|
|
|
962
1045
|
};
|
|
963
1046
|
return {
|
|
964
1047
|
state: next,
|
|
965
|
-
view:
|
|
1048
|
+
view: renderCheckboxView(next),
|
|
966
1049
|
status: "active"
|
|
967
1050
|
};
|
|
968
1051
|
}
|
|
@@ -972,23 +1055,31 @@ function checkboxReduce(state, key) {
|
|
|
972
1055
|
const summary = ordered.map((index) => state.choices[index]?.label).filter((name) => name !== void 0).join(", ");
|
|
973
1056
|
return {
|
|
974
1057
|
state,
|
|
975
|
-
view: `${
|
|
1058
|
+
view: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, summary)}`,
|
|
976
1059
|
status: "submit",
|
|
977
1060
|
value: values
|
|
978
1061
|
};
|
|
979
1062
|
}
|
|
980
1063
|
return {
|
|
981
1064
|
state,
|
|
982
|
-
view:
|
|
1065
|
+
view: renderCheckboxView(state),
|
|
983
1066
|
status: "active"
|
|
984
1067
|
};
|
|
985
1068
|
}
|
|
986
|
-
/**
|
|
1069
|
+
/**
|
|
1070
|
+
* Toggles `index` in a readonly index list — copy-on-write, returning the new sorted-by-insertion
|
|
1071
|
+
* list; the primitive {@link reduceCheckbox} calls.
|
|
1072
|
+
*
|
|
1073
|
+
* @param indices - The ticked indices, in tick order
|
|
1074
|
+
* @param index - The index to add when absent, or drop when present
|
|
1075
|
+
* @returns A new list carrying the toggled membership; the input is never mutated
|
|
1076
|
+
*/
|
|
987
1077
|
function toggleIndex(indices, index) {
|
|
988
1078
|
return indices.includes(index) ? indices.filter((i) => i !== index) : [...indices, index];
|
|
989
1079
|
}
|
|
990
1080
|
/**
|
|
991
|
-
*
|
|
1081
|
+
* Builds the initial editor-field reducer state — the committed lines empty, and the declared
|
|
1082
|
+
* default held for a finish with nothing typed.
|
|
992
1083
|
*
|
|
993
1084
|
* @param field - The editor field to render
|
|
994
1085
|
* @param styler - The styler used to render the view
|
|
@@ -1006,23 +1097,31 @@ function createEditorState(field, styler = (0, _orkestrel_console.createStyler)(
|
|
|
1006
1097
|
};
|
|
1007
1098
|
}
|
|
1008
1099
|
/**
|
|
1009
|
-
*
|
|
1100
|
+
* Renders an editor-field reducer state as a multi-line styled view — the finish hint, the
|
|
1101
|
+
* committed lines, and the line in progress.
|
|
1102
|
+
*
|
|
1103
|
+
* @param state - The editor field's current reducer state
|
|
1104
|
+
* @returns The rendered view — the hinted header, the committed lines, and the line in progress
|
|
1010
1105
|
*/
|
|
1011
|
-
function
|
|
1012
|
-
const head =
|
|
1106
|
+
function renderEditorView(state) {
|
|
1107
|
+
const head = renderHintedHeader(state.styler, state.theme, state.message, "(Ctrl+D to finish)");
|
|
1013
1108
|
const pointer = state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer);
|
|
1014
1109
|
return [head, ...[...state.lines.map((line) => state.styler.render(state.theme.roles.content, line)), `${pointer} ${state.styler.render(state.theme.roles.content, state.current)}`]].join("\n");
|
|
1015
1110
|
}
|
|
1016
1111
|
/**
|
|
1017
|
-
*
|
|
1112
|
+
* Advances an editor prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
1018
1113
|
* reducer. Printable characters extend the current line; backspace shrinks it; return commits the
|
|
1019
|
-
* current line and starts a fresh one; ctrl-d
|
|
1020
|
-
* default when empty
|
|
1114
|
+
* current line and starts a fresh one; ctrl-d finishes, joining every line and falling back to the
|
|
1115
|
+
* default when empty; ctrl-c cancels. The form validates the candidate after the driver fills it.
|
|
1116
|
+
*
|
|
1117
|
+
* @param state - The editor field's current reducer state
|
|
1118
|
+
* @param key - The decoded keypress to apply
|
|
1119
|
+
* @returns The next step — the state, the rendered view, the status, and the joined text on submit
|
|
1021
1120
|
*/
|
|
1022
|
-
function
|
|
1121
|
+
function reduceEditor(state, key) {
|
|
1023
1122
|
if (key.ctrl && key.name === "c") return {
|
|
1024
1123
|
state,
|
|
1025
|
-
view:
|
|
1124
|
+
view: renderEditorView(state),
|
|
1026
1125
|
status: "cancel"
|
|
1027
1126
|
};
|
|
1028
1127
|
if (key.ctrl && key.name === "d") {
|
|
@@ -1031,7 +1130,7 @@ function editorReduce(state, key) {
|
|
|
1031
1130
|
const answer = joined.length > 0 ? joined : state.default;
|
|
1032
1131
|
return {
|
|
1033
1132
|
state,
|
|
1034
|
-
view: `${
|
|
1133
|
+
view: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, `${String(lines.length)} line${lines.length === 1 ? "" : "s"}`)}`,
|
|
1035
1134
|
status: "submit",
|
|
1036
1135
|
value: answer
|
|
1037
1136
|
};
|
|
@@ -1044,14 +1143,14 @@ function editorReduce(state, key) {
|
|
|
1044
1143
|
};
|
|
1045
1144
|
return {
|
|
1046
1145
|
state: next,
|
|
1047
|
-
view:
|
|
1146
|
+
view: renderEditorView(next),
|
|
1048
1147
|
status: "active"
|
|
1049
1148
|
};
|
|
1050
1149
|
}
|
|
1051
1150
|
const current = editLine(state.current, key);
|
|
1052
1151
|
if (current === void 0) return {
|
|
1053
1152
|
state,
|
|
1054
|
-
view:
|
|
1153
|
+
view: renderEditorView(state),
|
|
1055
1154
|
status: "active"
|
|
1056
1155
|
};
|
|
1057
1156
|
const next = {
|
|
@@ -1060,48 +1159,69 @@ function editorReduce(state, key) {
|
|
|
1060
1159
|
};
|
|
1061
1160
|
return {
|
|
1062
1161
|
state: next,
|
|
1063
|
-
view:
|
|
1162
|
+
view: renderEditorView(next),
|
|
1064
1163
|
status: "active"
|
|
1065
1164
|
};
|
|
1066
1165
|
}
|
|
1067
1166
|
/**
|
|
1068
|
-
*
|
|
1069
|
-
* password
|
|
1070
|
-
* `space` appends a space; ctrl-u clears the line
|
|
1071
|
-
*
|
|
1167
|
+
* Applies a single line-editing {@link KeyEvent} to a text buffer — the editing shared by input,
|
|
1168
|
+
* password, and editor. A printable key appends its character; `backspace` drops the last
|
|
1169
|
+
* character; `space` appends a space; ctrl-u clears the line; a key that edits nothing returns
|
|
1170
|
+
* `undefined`.
|
|
1171
|
+
*
|
|
1172
|
+
* @param value - The buffer the field holds so far
|
|
1173
|
+
* @param key - The decoded keypress to apply
|
|
1174
|
+
* @returns The new buffer, or `undefined` when the key does not edit the line, so the caller can
|
|
1175
|
+
* leave the state untouched
|
|
1072
1176
|
*/
|
|
1073
1177
|
function editLine(value, key) {
|
|
1074
1178
|
if (key.ctrl && key.name === "u") return "";
|
|
1075
1179
|
if (key.name === "backspace") return value.slice(0, -1);
|
|
1076
1180
|
if (key.name === "space") return `${value} `;
|
|
1077
|
-
if (!key.ctrl && !key.meta && [...key.name].length === 1 && isPrintable(key.name)) return `${value}${key.sequence}`;
|
|
1181
|
+
if (!key.ctrl && !key.meta && key.name !== void 0 && [...key.name].length === 1 && isPrintable(key.name)) return `${value}${key.sequence}`;
|
|
1078
1182
|
}
|
|
1079
1183
|
/**
|
|
1080
|
-
*
|
|
1081
|
-
* wrapper that arms `callback` after `ms` and returns a {@link
|
|
1184
|
+
* Implements the default {@link import('./types.js').TimerHandler} — a thin host `setTimeout` / `clearTimeout`
|
|
1185
|
+
* wrapper that arms `callback` after `ms` and returns a {@link TimerCancelFunction}. The deadline seam
|
|
1082
1186
|
* behind both the {@link import('./Prompt.js').Prompt} broker (its expiry) and the
|
|
1083
1187
|
* {@link import('./PromptClient.js').PromptClient} (its reconnect backoff); a test injects a
|
|
1084
1188
|
* deterministic timer instead, so neither entity touches real time.
|
|
1189
|
+
*
|
|
1190
|
+
* @param callback - The deadline callback to arm
|
|
1191
|
+
* @param ms - How long to wait before firing it, in milliseconds
|
|
1192
|
+
* @returns The {@link TimerCancelFunction} that clears the armed deadline
|
|
1085
1193
|
*/
|
|
1086
1194
|
function defaultTimer(callback, ms) {
|
|
1087
1195
|
const handle = setTimeout(callback, ms);
|
|
1088
1196
|
return () => clearTimeout(handle);
|
|
1089
1197
|
}
|
|
1090
|
-
/**
|
|
1198
|
+
/**
|
|
1199
|
+
* Implements the default {@link import('./types.js').FetchHandler} — the global `fetch`, adapted to
|
|
1200
|
+
* the minimal injected shape the {@link import('./PromptClient.js').PromptClient} uses.
|
|
1201
|
+
*
|
|
1202
|
+
* @param input - The request URL
|
|
1203
|
+
* @param init - The request init the client sets — method, headers, body, and abort signal
|
|
1204
|
+
* @returns The host `fetch` promise for that request
|
|
1205
|
+
*/
|
|
1091
1206
|
function globalFetch(input, init) {
|
|
1092
1207
|
return fetch(input, init);
|
|
1093
1208
|
}
|
|
1094
1209
|
/**
|
|
1095
|
-
*
|
|
1210
|
+
* Checks whether a caught value is an `AbortError` — the {@link import('./PromptClient.js').PromptClient}
|
|
1096
1211
|
* distinguishes a deliberate `disconnect` / teardown (an aborted `fetch`) from a real fault, so it
|
|
1097
1212
|
* exits its connect loop quietly instead of emitting `error` / reconnecting.
|
|
1213
|
+
*
|
|
1214
|
+
* @param error - The caught value to test
|
|
1215
|
+
* @returns True if the value is a host `Error` or `DOMException` named `AbortError`; false otherwise
|
|
1098
1216
|
*/
|
|
1099
1217
|
function isAbortError(error) {
|
|
1100
1218
|
return (error instanceof DOMException || error instanceof Error) && error.name === "AbortError";
|
|
1101
1219
|
}
|
|
1102
1220
|
/**
|
|
1103
|
-
*
|
|
1104
|
-
* loopback address. Pure string parsing (no `URL` global), so it stays total on malformed input
|
|
1221
|
+
* Checks whether `url` is an insecure remote endpoint — a plain `http://` URL whose host is not a
|
|
1222
|
+
* loopback address. Pure string parsing (no `URL` global), so it stays total on malformed input;
|
|
1223
|
+
* the {@link import('./PromptClient.js').PromptClient} warns once when a `token` would cross such
|
|
1224
|
+
* an endpoint in cleartext.
|
|
1105
1225
|
*
|
|
1106
1226
|
* @remarks
|
|
1107
1227
|
* A loopback host (`localhost`, `127.0.0.1`, `[::1]`) over `http://` is exempt (local
|
|
@@ -1109,7 +1229,7 @@ function isAbortError(error) {
|
|
|
1109
1229
|
* An `https://` URL (or any non-`http://` scheme) is never flagged.
|
|
1110
1230
|
*
|
|
1111
1231
|
* @param url - The candidate endpoint URL
|
|
1112
|
-
* @returns
|
|
1232
|
+
* @returns True if `url` is a non-loopback `http://` endpoint; false otherwise
|
|
1113
1233
|
*
|
|
1114
1234
|
* @example
|
|
1115
1235
|
* ```ts
|
|
@@ -1127,7 +1247,13 @@ function isInsecureRemote(url) {
|
|
|
1127
1247
|
const hostname = host.startsWith("[") ? host.slice(0, host.indexOf("]") + 1) : host.split(":")[0] ?? "";
|
|
1128
1248
|
return hostname !== "localhost" && hostname !== "127.0.0.1" && hostname !== "[::1]";
|
|
1129
1249
|
}
|
|
1130
|
-
/**
|
|
1250
|
+
/**
|
|
1251
|
+
* Serializes a parked {@link PendingForm} into a `pending` {@link WireEvent}, whose frame `id` is
|
|
1252
|
+
* the form's own id.
|
|
1253
|
+
*
|
|
1254
|
+
* @param form - The parked form's wire-safe record
|
|
1255
|
+
* @returns The `pending` frame — the JSON-stringified record as `data`, and the form's own `id`
|
|
1256
|
+
*/
|
|
1131
1257
|
function serializePending(form) {
|
|
1132
1258
|
return {
|
|
1133
1259
|
event: "pending",
|
|
@@ -1135,25 +1261,37 @@ function serializePending(form) {
|
|
|
1135
1261
|
id: form.id
|
|
1136
1262
|
};
|
|
1137
1263
|
}
|
|
1138
|
-
/**
|
|
1264
|
+
/**
|
|
1265
|
+
* Serializes a parked form's expiry or release into an `expire` {@link WireEvent}, whose `data` is
|
|
1266
|
+
* the JSON `{ id }` payload.
|
|
1267
|
+
*
|
|
1268
|
+
* @param id - The id of the parked form that expired or was released
|
|
1269
|
+
* @returns The `expire` frame, carrying the JSON-stringified `{ id }` payload as `data`
|
|
1270
|
+
*/
|
|
1139
1271
|
function serializeExpire(id) {
|
|
1140
1272
|
return {
|
|
1141
1273
|
event: "expire",
|
|
1142
1274
|
data: JSON.stringify({ id })
|
|
1143
1275
|
};
|
|
1144
1276
|
}
|
|
1145
|
-
/**
|
|
1146
|
-
|
|
1277
|
+
/**
|
|
1278
|
+
* Serializes the `destroy` {@link WireEvent} a broker or manager sends when it is going away, which
|
|
1279
|
+
* carries no payload.
|
|
1280
|
+
*
|
|
1281
|
+
* @returns The `destroy` frame, whose `data` is empty because the signal carries no payload
|
|
1282
|
+
*/
|
|
1283
|
+
function serializeDestroy() {
|
|
1147
1284
|
return {
|
|
1148
|
-
event: "
|
|
1285
|
+
event: "destroy",
|
|
1149
1286
|
data: ""
|
|
1150
1287
|
};
|
|
1151
1288
|
}
|
|
1152
1289
|
//#endregion
|
|
1153
1290
|
//#region src/core/Prompt.ts
|
|
1154
1291
|
/**
|
|
1155
|
-
*
|
|
1156
|
-
* remote answers to the authoritative form
|
|
1292
|
+
* Implements the headless form broker. It parks live forms, exposes their serialized schemas,
|
|
1293
|
+
* applies remote answers to the authoritative form, and abandons a parked form on timeout, release,
|
|
1294
|
+
* or teardown.
|
|
1157
1295
|
*
|
|
1158
1296
|
* @remarks
|
|
1159
1297
|
* A parked record carries one call to `serializeForm`. A failed fill or submit leaves the record
|
|
@@ -1343,8 +1481,9 @@ var Prompt = class {
|
|
|
1343
1481
|
//#endregion
|
|
1344
1482
|
//#region src/core/PromptClient.ts
|
|
1345
1483
|
/**
|
|
1346
|
-
*
|
|
1347
|
-
* local terminal,
|
|
1484
|
+
* Implements the SSE form bridge. It ingests serialized forms from a remote broker without waiting
|
|
1485
|
+
* on a render, drives one form at a time through a local terminal, posts each answer back, and asks
|
|
1486
|
+
* again when the authoritative form refuses one.
|
|
1348
1487
|
*
|
|
1349
1488
|
* @remarks
|
|
1350
1489
|
* - **Connect + reconnect.** {@link connect} opens the SSE stream and reconnects after a transport
|
|
@@ -1352,12 +1491,13 @@ var Prompt = class {
|
|
|
1352
1491
|
* {@link disconnect} deliberately stopped it.
|
|
1353
1492
|
* - **Ingest + render.** Each `pending` envelope passes through `isPendingForm`, Form's `parseForm`,
|
|
1354
1493
|
* and terminal's `sanitizeSchema`, then enters a serial render queue. The SSE reader never awaits
|
|
1355
|
-
* that queue, so `expire` and `
|
|
1494
|
+
* that queue, so `expire` and `destroy` remain live while a person is answering.
|
|
1356
1495
|
* - **Safe local form.** The rendering copy omits every wire `pattern`, because Form compiles a
|
|
1357
1496
|
* pattern during local evaluation. The broker's parked form retains it and remains authoritative.
|
|
1358
1497
|
* - **Refusal retry.** A structured `rejected` response seeds a new rendering form with the values
|
|
1359
|
-
*
|
|
1360
|
-
* counter truncates the loop; acceptance, expiry, and
|
|
1498
|
+
* the refused attempt submitted, applies every {@link FieldError} through `invalidate`, and asks
|
|
1499
|
+
* again. No retry counter truncates the loop; acceptance, expiry, and the broker's own teardown
|
|
1500
|
+
* are its bounds.
|
|
1361
1501
|
* - **Replay safety.** A replayed id is skipped while it is queued, rendering, or posting. Once an
|
|
1362
1502
|
* attempt ends, a later delivery of that id may be rendered again.
|
|
1363
1503
|
*
|
|
@@ -1495,7 +1635,7 @@ var PromptClient = class {
|
|
|
1495
1635
|
if ((0, _orkestrel_contract.isRecord)(parsed) && (0, _orkestrel_contract.isString)(parsed.id)) this.#expire(parsed.id);
|
|
1496
1636
|
return;
|
|
1497
1637
|
}
|
|
1498
|
-
if (event.event === SSE_EVENTS.
|
|
1638
|
+
if (event.event === SSE_EVENTS.destroy) {
|
|
1499
1639
|
this.disconnect();
|
|
1500
1640
|
this.#interrupt();
|
|
1501
1641
|
}
|
|
@@ -1648,25 +1788,24 @@ var PromptClient = class {
|
|
|
1648
1788
|
//#endregion
|
|
1649
1789
|
//#region src/core/TerminalManager.ts
|
|
1650
1790
|
/**
|
|
1651
|
-
*
|
|
1652
|
-
*
|
|
1653
|
-
*
|
|
1654
|
-
* asks.
|
|
1791
|
+
* Registers named {@link PromptInterface} brokers, one per endpoint, so several parties can `ask`
|
|
1792
|
+
* forms of each other by name with a `from` → `to` attribution edge on every parked form, and
|
|
1793
|
+
* refuses `DEADLOCK` on a transitive cycle across every in-flight ask.
|
|
1655
1794
|
*
|
|
1656
1795
|
* @remarks
|
|
1657
1796
|
* - **Registry.** `add(name, options?)` mints (or, if `name` is already mounted, returns the
|
|
1658
|
-
*
|
|
1797
|
+
* existing broker unchanged — idempotent, never clobbers a live/parked endpoint). Every mounted
|
|
1659
1798
|
* broker's `pending` / `answer` / `expire` events are re-emitted on the manager, attributed by
|
|
1660
1799
|
* `name`.
|
|
1661
|
-
* - **`ask`.** The target must already be mounted
|
|
1800
|
+
* - **`ask`.** The target must already be mounted through {@link add} — `ask` never auto-adds it;
|
|
1662
1801
|
* rejects `TARGET` for an unknown `to` (listing the known names). Rejects `DEADLOCK` when parking
|
|
1663
|
-
* `from → to` would close a cycle over the
|
|
1802
|
+
* `from → to` would close a cycle over the current in-flight edge set (walked transitively);
|
|
1664
1803
|
* otherwise parks the caller's live form through the target's broker and returns that form's own
|
|
1665
1804
|
* `answer` promise. Edge cleanup never alters the value or rejection the caller observes.
|
|
1666
1805
|
* - **Durable open / save.** `open(name)` restores an EMPTY broker from the `store` (parked
|
|
1667
1806
|
* Promises are process-bound and never resurrected); `save(name)` persists the endpoint's
|
|
1668
1807
|
* configured `timeout`.
|
|
1669
|
-
* - **Removal.** `remove` drops one endpoint, a batch (
|
|
1808
|
+
* - **Removal.** `remove` drops one endpoint, a batch (the array overload declared first), or every
|
|
1670
1809
|
* endpoint when called without an argument. It destroys each broker, which expires every form
|
|
1671
1810
|
* still parked on it. `destroy` is idempotent.
|
|
1672
1811
|
*
|
|
@@ -1711,7 +1850,7 @@ var TerminalManager = class {
|
|
|
1711
1850
|
return this.#terminals.get(name);
|
|
1712
1851
|
}
|
|
1713
1852
|
terminals() {
|
|
1714
|
-
return [...this.#terminals.
|
|
1853
|
+
return [...this.#terminals.values()];
|
|
1715
1854
|
}
|
|
1716
1855
|
add(name, options) {
|
|
1717
1856
|
if (this.#destroyed) throw new TerminalError("DESTROYED", "manager destroyed");
|
|
@@ -1743,7 +1882,7 @@ var TerminalManager = class {
|
|
|
1743
1882
|
ask(from, to, form) {
|
|
1744
1883
|
const broker = this.#terminals.get(to);
|
|
1745
1884
|
if (broker === void 0) {
|
|
1746
|
-
const known = this.
|
|
1885
|
+
const known = [...this.#terminals.keys()];
|
|
1747
1886
|
return Promise.reject(new TerminalError("TARGET", `unknown terminal '${to}' (known: ${known.length > 0 ? known.join(", ") : "none"})`, {
|
|
1748
1887
|
to,
|
|
1749
1888
|
known
|
|
@@ -1781,7 +1920,7 @@ var TerminalManager = class {
|
|
|
1781
1920
|
const broker = this.#terminals.get(to);
|
|
1782
1921
|
if (broker === void 0) return {
|
|
1783
1922
|
success: false,
|
|
1784
|
-
error: { reason: "
|
|
1923
|
+
error: { reason: "target" }
|
|
1785
1924
|
};
|
|
1786
1925
|
const result = broker.answer(id, values);
|
|
1787
1926
|
if (result.success) this.#edges.delete(id);
|
|
@@ -1814,8 +1953,8 @@ var TerminalManager = class {
|
|
|
1814
1953
|
return;
|
|
1815
1954
|
}
|
|
1816
1955
|
if ((0, _orkestrel_contract.isArray)(names)) {
|
|
1817
|
-
let removed =
|
|
1818
|
-
for (const name of names) if (this.#removeOne(name)) removed =
|
|
1956
|
+
let removed = true;
|
|
1957
|
+
for (const name of names) if (!this.#removeOne(name)) removed = false;
|
|
1819
1958
|
return removed;
|
|
1820
1959
|
}
|
|
1821
1960
|
return this.#removeOne(names);
|
|
@@ -1890,16 +2029,17 @@ var TerminalManager = class {
|
|
|
1890
2029
|
}
|
|
1891
2030
|
};
|
|
1892
2031
|
//#endregion
|
|
1893
|
-
//#region src/core/MemoryTerminalStore.ts
|
|
2032
|
+
//#region src/core/stores/MemoryTerminalStore.ts
|
|
1894
2033
|
/**
|
|
1895
|
-
*
|
|
1896
|
-
* {@link TerminalSnapshot}
|
|
1897
|
-
* {@link import('
|
|
1898
|
-
* {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore}.
|
|
2034
|
+
* Implements the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of
|
|
2035
|
+
* {@link TerminalSnapshot} records keyed by endpoint id, the default store
|
|
2036
|
+
* {@link import('../factories.js').createMemoryTerminalStore} builds and the exact twin of
|
|
2037
|
+
* {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore}. It carries no idle expiry and
|
|
2038
|
+
* no eviction.
|
|
1899
2039
|
*
|
|
1900
2040
|
* @remarks
|
|
1901
|
-
* A plain `Map<string, TerminalSnapshot>`
|
|
1902
|
-
*
|
|
2041
|
+
* A plain `Map<string, TerminalSnapshot>` — the snapshot is already pure, self-contained CONFIG-only
|
|
2042
|
+
* JSON, so the memory tier needs no encoding. There is NO idle-TTL and NO
|
|
1903
2043
|
* eviction: a persisted config lives until an explicit `delete`. A durable backend (JSON / SQLite /
|
|
1904
2044
|
* IndexedDB) swaps in through the SAME interface without touching the manager — its
|
|
1905
2045
|
* driver-pluggable twin is {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore} (the
|
|
@@ -1909,13 +2049,13 @@ var TerminalManager = class {
|
|
|
1909
2049
|
* - **`set` inserts / replaces under the snapshot's OWN `id`** (no separate id param).
|
|
1910
2050
|
* - **`delete` drops a snapshot by id**; an absent id is a no-op (no throw).
|
|
1911
2051
|
*
|
|
1912
|
-
* The public surface is EXACTLY `get` / `set` / `delete` — no extra members
|
|
1913
|
-
*
|
|
2052
|
+
* The public surface is EXACTLY `get` / `set` / `delete` — no extra members, so the class and
|
|
2053
|
+
* {@link TerminalStoreInterface} carry the same methods. Hydration is a caller concern: `open` always
|
|
1914
2054
|
* restores an EMPTY broker — parked Promises are process-bound and never resurrected.
|
|
1915
2055
|
*
|
|
1916
2056
|
* @example
|
|
1917
2057
|
* ```ts
|
|
1918
|
-
* import { createMemoryTerminalStore } from '@
|
|
2058
|
+
* import { createMemoryTerminalStore } from '@orkestrel/terminal'
|
|
1919
2059
|
*
|
|
1920
2060
|
* const store = createMemoryTerminalStore()
|
|
1921
2061
|
* await store.set({ id: 'shell', timeout: 5000 }) // persist a config
|
|
@@ -1925,32 +2065,51 @@ var TerminalManager = class {
|
|
|
1925
2065
|
*/
|
|
1926
2066
|
var MemoryTerminalStore = class {
|
|
1927
2067
|
#snapshots = /* @__PURE__ */ new Map();
|
|
2068
|
+
/**
|
|
2069
|
+
* Resolves the persisted snapshot for `id`.
|
|
2070
|
+
*
|
|
2071
|
+
* @param id - The endpoint name the snapshot is keyed by
|
|
2072
|
+
* @returns The stored snapshot, or `undefined` when none is held
|
|
2073
|
+
*/
|
|
1928
2074
|
get(id) {
|
|
1929
2075
|
return Promise.resolve(this.#snapshots.get(id));
|
|
1930
2076
|
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Inserts or replaces under the snapshot's OWN `id` (no separate id param).
|
|
2079
|
+
*
|
|
2080
|
+
* @param snapshot - The config snapshot to persist, carrying its own `id`
|
|
2081
|
+
* @returns A promise that settles once the snapshot is held
|
|
2082
|
+
*/
|
|
1931
2083
|
set(snapshot) {
|
|
1932
2084
|
this.#snapshots.set(snapshot.id, snapshot);
|
|
1933
2085
|
return Promise.resolve();
|
|
1934
2086
|
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Drops a snapshot by id; an absent id is a no-op (no throw).
|
|
2089
|
+
*
|
|
2090
|
+
* @param id - The endpoint name to drop
|
|
2091
|
+
* @returns A promise that settles once the snapshot is gone
|
|
2092
|
+
*/
|
|
1935
2093
|
delete(id) {
|
|
1936
2094
|
this.#snapshots.delete(id);
|
|
1937
2095
|
return Promise.resolve();
|
|
1938
2096
|
}
|
|
1939
2097
|
};
|
|
1940
2098
|
//#endregion
|
|
1941
|
-
//#region src/core/DatabaseTerminalStore.ts
|
|
2099
|
+
//#region src/core/stores/DatabaseTerminalStore.ts
|
|
1942
2100
|
/**
|
|
1943
|
-
*
|
|
1944
|
-
* durable
|
|
1945
|
-
* `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
|
|
1946
|
-
* {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}.
|
|
2101
|
+
* Implements a {@link TerminalStoreInterface} backed by one table of the `databases` layer — an
|
|
2102
|
+
* endpoint's durable config state is a row, so persistence reduces to keyed point-access (`get` /
|
|
2103
|
+
* `set` / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
|
|
2104
|
+
* {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}. A stored `snapshot` is narrowed
|
|
2105
|
+
* with {@link import('../validators.js').isTerminalSnapshot} on read.
|
|
1947
2106
|
*
|
|
1948
2107
|
* @remarks
|
|
1949
2108
|
* The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
|
|
1950
2109
|
* JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /
|
|
1951
2110
|
* IndexedDB backend swaps in WITHOUT touching the manager — the same seam as
|
|
1952
2111
|
* {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}. The driver defaults to memory
|
|
1953
|
-
* ({@link import('
|
|
2112
|
+
* ({@link import('../factories.js').createDatabaseTerminalStore} passes `createMemoryDriver()`), so
|
|
1954
2113
|
* it ALSO works in memory out of the box; you opt into the durable plumbing by passing a JSON /
|
|
1955
2114
|
* SQLite / IndexedDB driver.
|
|
1956
2115
|
*
|
|
@@ -1962,18 +2121,19 @@ var MemoryTerminalStore = class {
|
|
|
1962
2121
|
* - **`set(snapshot)` upserts under the snapshot's OWN `id`** (no separate id param) — it writes
|
|
1963
2122
|
* the row `{ id: snapshot.id, snapshot }`.
|
|
1964
2123
|
* - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to
|
|
1965
|
-
* a {@link TerminalSnapshot} ({@link import('
|
|
1966
|
-
*
|
|
2124
|
+
* a {@link TerminalSnapshot} ({@link import('../validators.js').isTerminalSnapshot} — the total
|
|
2125
|
+
* guard that narrows an untrusted storage read), or `undefined` if none is stored.
|
|
1967
2126
|
* - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).
|
|
1968
2127
|
*
|
|
1969
2128
|
* There is NO idle-TTL / eviction — a persisted config lives until an explicit `delete`. The public
|
|
1970
|
-
* surface is EXACTLY `get` / `set` / `delete` — no extra members
|
|
1971
|
-
* {@link TerminalStoreInterface}
|
|
2129
|
+
* surface is EXACTLY `get` / `set` / `delete` — no extra members, so the class and
|
|
2130
|
+
* {@link TerminalStoreInterface} carry the same methods. Hydration stays a caller concern: `open` always restores an EMPTY
|
|
1972
2131
|
* broker — parked Promises are process-bound and never resurrected.
|
|
1973
2132
|
*
|
|
1974
2133
|
* @example
|
|
1975
2134
|
* ```ts
|
|
1976
|
-
* import { createDatabaseTerminalStore
|
|
2135
|
+
* import { createDatabaseTerminalStore } from '@orkestrel/terminal'
|
|
2136
|
+
* import { createMemoryDriver } from '@orkestrel/database'
|
|
1977
2137
|
*
|
|
1978
2138
|
* const store = createDatabaseTerminalStore(createMemoryDriver()) // a durable driver swaps in here
|
|
1979
2139
|
* await store.set({ id: 'shell', timeout: 5000 }) // persist the config (one JSON column)
|
|
@@ -1984,7 +2144,7 @@ var MemoryTerminalStore = class {
|
|
|
1984
2144
|
var DatabaseTerminalStore = class {
|
|
1985
2145
|
#table;
|
|
1986
2146
|
/**
|
|
1987
|
-
*
|
|
2147
|
+
* Wraps a table as a terminal store.
|
|
1988
2148
|
*
|
|
1989
2149
|
* @param table - The {@link TableInterface} holding the snapshots — its row is the
|
|
1990
2150
|
* {@link TerminalSnapshotRow} `{ id; snapshot }` shape (the snapshot one opaque JSON column)
|
|
@@ -1992,20 +2152,37 @@ var DatabaseTerminalStore = class {
|
|
|
1992
2152
|
constructor(table) {
|
|
1993
2153
|
this.#table = table;
|
|
1994
2154
|
}
|
|
1995
|
-
/**
|
|
2155
|
+
/**
|
|
2156
|
+
* Resolves the persisted snapshot for `id`, narrowing the opaque JSON column back to a
|
|
2157
|
+
* `TerminalSnapshot`.
|
|
2158
|
+
*
|
|
2159
|
+
* @param id - The endpoint name the row is keyed by
|
|
2160
|
+
* @returns The stored snapshot, or `undefined` when none is held or the column is off-shape
|
|
2161
|
+
*/
|
|
1996
2162
|
async get(id) {
|
|
1997
2163
|
const row = await this.#table.get(id);
|
|
1998
2164
|
if (row === void 0) return void 0;
|
|
1999
2165
|
return isTerminalSnapshot(row.snapshot) ? row.snapshot : void 0;
|
|
2000
2166
|
}
|
|
2001
|
-
/**
|
|
2167
|
+
/**
|
|
2168
|
+
* Inserts or replaces under the snapshot's OWN `id` (no separate id param) — the row is
|
|
2169
|
+
* `{ id, snapshot }`.
|
|
2170
|
+
*
|
|
2171
|
+
* @param snapshot - The config snapshot to persist, carrying its own `id`
|
|
2172
|
+
* @returns A promise that settles once the row is written
|
|
2173
|
+
*/
|
|
2002
2174
|
async set(snapshot) {
|
|
2003
2175
|
await this.#table.set({
|
|
2004
2176
|
id: snapshot.id,
|
|
2005
2177
|
snapshot
|
|
2006
2178
|
});
|
|
2007
2179
|
}
|
|
2008
|
-
/**
|
|
2180
|
+
/**
|
|
2181
|
+
* Drops a snapshot by id; an absent id is a no-op (no throw).
|
|
2182
|
+
*
|
|
2183
|
+
* @param id - The endpoint name to drop
|
|
2184
|
+
* @returns A promise that settles once the row is gone
|
|
2185
|
+
*/
|
|
2009
2186
|
async delete(id) {
|
|
2010
2187
|
await this.#table.remove(id);
|
|
2011
2188
|
}
|
|
@@ -2013,7 +2190,7 @@ var DatabaseTerminalStore = class {
|
|
|
2013
2190
|
//#endregion
|
|
2014
2191
|
//#region src/core/factories.ts
|
|
2015
2192
|
/**
|
|
2016
|
-
*
|
|
2193
|
+
* Creates the headless {@link PromptInterface} broker. It parks live forms and applies remote
|
|
2017
2194
|
* answers to the authoritative instances.
|
|
2018
2195
|
*
|
|
2019
2196
|
* @param options - See {@link PromptOptions}
|
|
@@ -2026,7 +2203,7 @@ var DatabaseTerminalStore = class {
|
|
|
2026
2203
|
*
|
|
2027
2204
|
* @example
|
|
2028
2205
|
* ```ts
|
|
2029
|
-
* import { createPrompt } from '@
|
|
2206
|
+
* import { createPrompt } from '@orkestrel/terminal'
|
|
2030
2207
|
* import { createForm } from '@orkestrel/form'
|
|
2031
2208
|
*
|
|
2032
2209
|
* const prompt = createPrompt()
|
|
@@ -2039,9 +2216,10 @@ function createPrompt(options) {
|
|
|
2039
2216
|
return new Prompt(options);
|
|
2040
2217
|
}
|
|
2041
2218
|
/**
|
|
2042
|
-
*
|
|
2043
|
-
* endpoint, dispatches each received form to a local
|
|
2044
|
-
* and POSTs the answer back. Universal — `fetch`
|
|
2219
|
+
* Creates the SSE prompt {@link PromptClientInterface} bridge — it connects to a remote broker's
|
|
2220
|
+
* SSE endpoint, dispatches each received form to a local
|
|
2221
|
+
* {@link import('./types.js').TerminalInterface}, and POSTs the answer back. Universal — `fetch`
|
|
2222
|
+
* and SSE are web standards.
|
|
2045
2223
|
*
|
|
2046
2224
|
* @param options - See {@link PromptClientOptions} (`url` + `terminal` required)
|
|
2047
2225
|
* @returns A {@link PromptClientInterface}
|
|
@@ -2051,11 +2229,11 @@ function createPrompt(options) {
|
|
|
2051
2229
|
* ends; it reconnects with the `delay` backoff unless `reconnect` is `false` / the client was
|
|
2052
2230
|
* `destroy`ed. Inject `options.fetch` (a scripted `fetch`) and `options.timer` to drive it
|
|
2053
2231
|
* deterministically in tests — no real network.
|
|
2054
|
-
* -
|
|
2232
|
+
* - **Wire narrowing.** Every decoded prompt is guard-narrowed before dispatch (never an `as`).
|
|
2055
2233
|
*
|
|
2056
2234
|
* @example
|
|
2057
2235
|
* ```ts
|
|
2058
|
-
* import { createPromptClient } from '@
|
|
2236
|
+
* import { createPromptClient } from '@orkestrel/terminal'
|
|
2059
2237
|
*
|
|
2060
2238
|
* const client = createPromptClient({ url: 'http://host/prompts', terminal })
|
|
2061
2239
|
* await client.connect()
|
|
@@ -2065,16 +2243,16 @@ function createPromptClient(options) {
|
|
|
2065
2243
|
return new PromptClient(options);
|
|
2066
2244
|
}
|
|
2067
2245
|
/**
|
|
2068
|
-
*
|
|
2069
|
-
*
|
|
2070
|
-
*
|
|
2246
|
+
* Creates the multi-endpoint {@link TerminalManager} — a named registry of {@link PromptInterface}
|
|
2247
|
+
* brokers so several parties can `ask` forms of each other by name, with a transitive cycle check
|
|
2248
|
+
* that refuses `DEADLOCK` across every in-flight ask.
|
|
2071
2249
|
*
|
|
2072
2250
|
* @param options - See {@link TerminalManagerOptions}
|
|
2073
2251
|
* @returns A {@link TerminalManager}
|
|
2074
2252
|
*
|
|
2075
2253
|
* @example
|
|
2076
2254
|
* ```ts
|
|
2077
|
-
* import { createTerminalManager } from '@
|
|
2255
|
+
* import { createTerminalManager } from '@orkestrel/terminal'
|
|
2078
2256
|
*
|
|
2079
2257
|
* const manager = createTerminalManager()
|
|
2080
2258
|
* manager.add('agent')
|
|
@@ -2084,14 +2262,14 @@ function createTerminalManager(options) {
|
|
|
2084
2262
|
return new TerminalManager(options);
|
|
2085
2263
|
}
|
|
2086
2264
|
/**
|
|
2087
|
-
*
|
|
2265
|
+
* Creates the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of endpoint
|
|
2088
2266
|
* config snapshots, the default store backing a {@link TerminalManagerInterface}'s `open` / `save`.
|
|
2089
2267
|
*
|
|
2090
2268
|
* @returns A {@link TerminalStoreInterface}
|
|
2091
2269
|
*
|
|
2092
2270
|
* @example
|
|
2093
2271
|
* ```ts
|
|
2094
|
-
* import { createMemoryTerminalStore } from '@
|
|
2272
|
+
* import { createMemoryTerminalStore } from '@orkestrel/terminal'
|
|
2095
2273
|
*
|
|
2096
2274
|
* const store = createMemoryTerminalStore()
|
|
2097
2275
|
* ```
|
|
@@ -2100,16 +2278,17 @@ function createMemoryTerminalStore() {
|
|
|
2100
2278
|
return new MemoryTerminalStore();
|
|
2101
2279
|
}
|
|
2102
2280
|
/**
|
|
2103
|
-
*
|
|
2281
|
+
* Creates a {@link TerminalStoreInterface} backed by one table of the `databases` layer — the
|
|
2104
2282
|
* driver-pluggable twin of {@link createMemoryTerminalStore}, storing each endpoint's config
|
|
2105
|
-
* snapshot as one opaque JSON column.
|
|
2283
|
+
* snapshot as one opaque JSON column. The default driver is an in-memory `@orkestrel/database`
|
|
2284
|
+
* driver.
|
|
2106
2285
|
*
|
|
2107
2286
|
* @param driver - The {@link DriverInterface} backing the table (default an in-memory driver)
|
|
2108
2287
|
* @returns A {@link TerminalStoreInterface}
|
|
2109
2288
|
*
|
|
2110
2289
|
* @example
|
|
2111
2290
|
* ```ts
|
|
2112
|
-
* import { createDatabaseTerminalStore } from '@
|
|
2291
|
+
* import { createDatabaseTerminalStore } from '@orkestrel/terminal'
|
|
2113
2292
|
*
|
|
2114
2293
|
* const store = createDatabaseTerminalStore() // in-memory by default
|
|
2115
2294
|
* ```
|
|
@@ -2139,9 +2318,7 @@ exports.DEFAULT_PROMPT_TIMEOUT_MS = DEFAULT_PROMPT_TIMEOUT_MS;
|
|
|
2139
2318
|
exports.DEFAULT_RECONNECT_DELAY_MS = DEFAULT_RECONNECT_DELAY_MS;
|
|
2140
2319
|
exports.DELETE = DELETE;
|
|
2141
2320
|
exports.DatabaseTerminalStore = DatabaseTerminalStore;
|
|
2142
|
-
exports.ESCAPE = ESCAPE;
|
|
2143
2321
|
exports.HEADER_TOKEN = HEADER_TOKEN;
|
|
2144
|
-
exports.KEY_CSI = KEY_CSI;
|
|
2145
2322
|
exports.KEY_SS3 = KEY_SS3;
|
|
2146
2323
|
exports.MemoryTerminalStore = MemoryTerminalStore;
|
|
2147
2324
|
exports.NEWLINE = NEWLINE;
|
|
@@ -2157,10 +2334,6 @@ exports.SSE_EVENTS = SSE_EVENTS;
|
|
|
2157
2334
|
exports.TAB = TAB;
|
|
2158
2335
|
exports.TerminalError = TerminalError;
|
|
2159
2336
|
exports.TerminalManager = TerminalManager;
|
|
2160
|
-
exports.checkboxReduce = checkboxReduce;
|
|
2161
|
-
exports.checkboxView = checkboxView;
|
|
2162
|
-
exports.confirmReduce = confirmReduce;
|
|
2163
|
-
exports.confirmView = confirmView;
|
|
2164
2337
|
exports.createCheckboxState = createCheckboxState;
|
|
2165
2338
|
exports.createConfirmState = createConfirmState;
|
|
2166
2339
|
exports.createDatabaseTerminalStore = createDatabaseTerminalStore;
|
|
@@ -2175,13 +2348,7 @@ exports.createSelectState = createSelectState;
|
|
|
2175
2348
|
exports.createTerminalManager = createTerminalManager;
|
|
2176
2349
|
exports.defaultTimer = defaultTimer;
|
|
2177
2350
|
exports.editLine = editLine;
|
|
2178
|
-
exports.editorReduce = editorReduce;
|
|
2179
|
-
exports.editorView = editorView;
|
|
2180
|
-
exports.errorLine = errorLine;
|
|
2181
2351
|
exports.globalFetch = globalFetch;
|
|
2182
|
-
exports.hintedHeader = hintedHeader;
|
|
2183
|
-
exports.inputReduce = inputReduce;
|
|
2184
|
-
exports.inputView = inputView;
|
|
2185
2352
|
exports.isAbortError = isAbortError;
|
|
2186
2353
|
exports.isInsecureRemote = isInsecureRemote;
|
|
2187
2354
|
exports.isPendingForm = isPendingForm;
|
|
@@ -2191,18 +2358,28 @@ exports.isTerminalError = isTerminalError;
|
|
|
2191
2358
|
exports.isTerminalSnapshot = isTerminalSnapshot;
|
|
2192
2359
|
exports.isWireEvent = isWireEvent;
|
|
2193
2360
|
exports.parseKey = parseKey;
|
|
2194
|
-
exports.
|
|
2195
|
-
exports.
|
|
2196
|
-
exports.
|
|
2361
|
+
exports.reduceCheckbox = reduceCheckbox;
|
|
2362
|
+
exports.reduceConfirm = reduceConfirm;
|
|
2363
|
+
exports.reduceEditor = reduceEditor;
|
|
2364
|
+
exports.reduceInput = reduceInput;
|
|
2365
|
+
exports.reducePassword = reducePassword;
|
|
2366
|
+
exports.reduceSelect = reduceSelect;
|
|
2367
|
+
exports.renderCheckboxView = renderCheckboxView;
|
|
2368
|
+
exports.renderConfirmView = renderConfirmView;
|
|
2369
|
+
exports.renderEditorView = renderEditorView;
|
|
2370
|
+
exports.renderErrorLine = renderErrorLine;
|
|
2371
|
+
exports.renderHintedHeader = renderHintedHeader;
|
|
2372
|
+
exports.renderInputView = renderInputView;
|
|
2373
|
+
exports.renderPasswordView = renderPasswordView;
|
|
2374
|
+
exports.renderPromptHeader = renderPromptHeader;
|
|
2375
|
+
exports.renderSelectView = renderSelectView;
|
|
2376
|
+
exports.renderSubmitHeader = renderSubmitHeader;
|
|
2197
2377
|
exports.sanitizeDisplayText = sanitizeDisplayText;
|
|
2198
2378
|
exports.sanitizeSchema = sanitizeSchema;
|
|
2199
2379
|
exports.sanitizeThemeIcons = sanitizeThemeIcons;
|
|
2200
|
-
exports.
|
|
2201
|
-
exports.selectView = selectView;
|
|
2380
|
+
exports.serializeDestroy = serializeDestroy;
|
|
2202
2381
|
exports.serializeExpire = serializeExpire;
|
|
2203
2382
|
exports.serializePending = serializePending;
|
|
2204
|
-
exports.serializeShutdown = serializeShutdown;
|
|
2205
|
-
exports.submitHeader = submitHeader;
|
|
2206
2383
|
exports.toggleIndex = toggleIndex;
|
|
2207
2384
|
|
|
2208
2385
|
//# sourceMappingURL=index.cjs.map
|