@orkestrel/terminal 0.0.14 → 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 +10 -11
- package/dist/src/core/index.cjs +115 -80
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +265 -150
- package/dist/src/core/index.d.ts +265 -150
- package/dist/src/core/index.js +115 -80
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +102 -78
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +122 -92
- package/dist/src/server/index.d.ts +122 -92
- package/dist/src/server/index.js +102 -78
- package/dist/src/server/index.js.map +1 -1
- package/package.json +18 -19
package/README.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# @orkestrel/terminal
|
|
2
2
|
|
|
3
|
-
The terminal side of a form,
|
|
4
|
-
the
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
elsewhere to this machine's keyboard over SSE. Built beside
|
|
3
|
+
> The terminal side of a form: a key decoder, a presentation theme, the pure per-field reducers,
|
|
4
|
+
> the headless broker that parks a live form until somebody elsewhere answers it, the SSE bridge
|
|
5
|
+
> that carries a parked form to a machine with a keyboard, and the manager that routes parked
|
|
6
|
+
> forms between named endpoints.
|
|
7
|
+
|
|
8
|
+
`@orkestrel/form` owns the document — the schema, the controls, the rules,
|
|
9
|
+
the values, and the settle-once `answer` promise — and this package declares
|
|
10
|
+
none of it a second time. Part of the `@orkestrel` line, built beside
|
|
12
11
|
`@orkestrel/console` (the shared style engine), `@orkestrel/contract`,
|
|
13
12
|
`@orkestrel/emitter`, `@orkestrel/database`, and `@orkestrel/sse`.
|
|
14
13
|
|
|
@@ -42,7 +41,7 @@ const values = await terminal.ask(
|
|
|
42
41
|
)
|
|
43
42
|
```
|
|
44
43
|
|
|
45
|
-
A bare return binds
|
|
44
|
+
A bare return binds absence, not the empty string, so `required` refuses it and
|
|
46
45
|
the walk asks again.
|
|
47
46
|
|
|
48
47
|
Park a live form and answer it from anywhere else:
|
|
@@ -56,7 +55,7 @@ prompt.emitter.on('pending', (form) => send(form)) // the wire-safe record
|
|
|
56
55
|
|
|
57
56
|
const form = createForm({ fields: [{ control: 'text', name: 'name' }] })
|
|
58
57
|
const id = prompt.park(form) // the id; you await the form's own `answer`
|
|
59
|
-
prompt.answer(id, { name: 'Ada' }) // fills and submits the
|
|
58
|
+
prompt.answer(id, { name: 'Ada' }) // fills and submits the authoritative form
|
|
60
59
|
const values = await form.answer // { name: 'Ada' }
|
|
61
60
|
```
|
|
62
61
|
|
package/dist/src/core/index.cjs
CHANGED
|
@@ -36,10 +36,11 @@ var CTRL_E = String.fromCharCode(5);
|
|
|
36
36
|
*/
|
|
37
37
|
var KEY_SS3 = `${_orkestrel_console.ESC}O`;
|
|
38
38
|
/**
|
|
39
|
-
* Holds the exact escape
|
|
40
|
-
* consults for the navigation
|
|
41
|
-
* form (`ESCOA`…) of the arrows, plus the `home` / `end` / `delete`
|
|
42
|
-
* 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.
|
|
43
44
|
*
|
|
44
45
|
* @remarks
|
|
45
46
|
* Terminals disagree on these: a cursor key is `ESC[A` (normal) or `ESCOA` (application mode),
|
|
@@ -65,10 +66,10 @@ var SEQUENCE_NAMES = Object.freeze({
|
|
|
65
66
|
[`${_orkestrel_console.CSI}8~`]: "end"
|
|
66
67
|
});
|
|
67
68
|
/**
|
|
68
|
-
* Holds the control
|
|
69
|
-
* consults for the one-byte keys and the two-byte CRLF
|
|
70
|
-
* canonical `name` and whether it is a `ctrl` combination. The
|
|
71
|
-
* 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.
|
|
72
73
|
*
|
|
73
74
|
* @remarks
|
|
74
75
|
* `return` / `newline` / `return + newline` all map to `return` (one canonical Enter name) — a
|
|
@@ -135,8 +136,9 @@ var CONTROL_NAMES = Object.freeze({
|
|
|
135
136
|
/** Names the default mask glyph {@link import('./helpers.js').createPasswordState} uses — `*`. */
|
|
136
137
|
var DEFAULT_MASK = "*";
|
|
137
138
|
/**
|
|
138
|
-
* Holds the
|
|
139
|
-
* 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.
|
|
140
142
|
*
|
|
141
143
|
* @remarks
|
|
142
144
|
* - `question` — the leading mark on a prompt's message line.
|
|
@@ -225,17 +227,18 @@ var DEFAULT_PROMPT_THEME = Object.freeze({
|
|
|
225
227
|
description: (0, _orkestrel_console.freezeStyle)({ attributes: ["dim"] })
|
|
226
228
|
})
|
|
227
229
|
});
|
|
228
|
-
/** Holds how long (ms) the {@link import('./types.js').PromptInterface} broker parks an unanswered
|
|
230
|
+
/** Holds how long (ms) the {@link import('./types.js').PromptInterface} broker parks an unanswered form before it expires — 5 minutes. */
|
|
229
231
|
var DEFAULT_PROMPT_TIMEOUT_MS = 3e5;
|
|
230
232
|
/** Holds how long (ms) the {@link import('./types.js').PromptClientInterface} waits before each reconnect attempt — 2 seconds. */
|
|
231
233
|
var DEFAULT_RECONNECT_DELAY_MS = 2e3;
|
|
232
234
|
/**
|
|
233
|
-
* Holds the SSE `event:` names the broker emits and the
|
|
234
|
-
*
|
|
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.
|
|
235
238
|
*
|
|
236
239
|
* @remarks
|
|
237
240
|
* - `pending` — a serialized {@link import('./types.js').PendingForm} to dispatch and answer.
|
|
238
|
-
* - `expire` — an `{ id }` payload: the broker expired or released a parked
|
|
241
|
+
* - `expire` — an `{ id }` payload: the broker expired or released a parked form (the client drops it).
|
|
239
242
|
* - `destroy` — the broker is going away; the client disconnects (no auto-reconnect) but stays reusable.
|
|
240
243
|
*/
|
|
241
244
|
var SSE_EVENTS = Object.freeze({
|
|
@@ -243,9 +246,12 @@ var SSE_EVENTS = Object.freeze({
|
|
|
243
246
|
expire: "expire",
|
|
244
247
|
destroy: "destroy"
|
|
245
248
|
});
|
|
246
|
-
/**
|
|
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
|
+
*/
|
|
247
253
|
var HEADER_TOKEN = "x-orkestrel-token";
|
|
248
|
-
/** Names the `Accept` header value that opens the broker's SSE stream
|
|
254
|
+
/** Names the `Accept` header value that opens the broker's SSE stream — `text/event-stream`. */
|
|
249
255
|
var ACCEPT_EVENT_STREAM = "text/event-stream";
|
|
250
256
|
/**
|
|
251
257
|
* Sets the maximum number of characters the {@link import('./types.js').PromptClientInterface} lets its
|
|
@@ -287,7 +293,7 @@ var TerminalError = class extends Error {
|
|
|
287
293
|
}
|
|
288
294
|
};
|
|
289
295
|
/**
|
|
290
|
-
* Narrows an unknown caught value to a {@link TerminalError}
|
|
296
|
+
* Narrows an unknown caught value to a {@link TerminalError}, so a caller can branch on its `code`.
|
|
291
297
|
*
|
|
292
298
|
* @param value - The value to test (typically a `catch` binding or a rejected prompt call)
|
|
293
299
|
* @returns True if `value` is a {@link TerminalError}; false otherwise
|
|
@@ -316,7 +322,8 @@ function isTerminalError(value) {
|
|
|
316
322
|
*/
|
|
317
323
|
var isPendingFormStatus = (0, _orkestrel_contract.literalOf)("pending", "answered", "expired");
|
|
318
324
|
/**
|
|
319
|
-
* Narrows an unknown wire value to a {@link PendingForm} envelope
|
|
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.
|
|
320
327
|
*
|
|
321
328
|
* @remarks
|
|
322
329
|
* This guard checks the transport record and proves only that `schema` is a record. The Form
|
|
@@ -336,7 +343,8 @@ function isPendingForm(value) {
|
|
|
336
343
|
}, ["from", "to"])(value);
|
|
337
344
|
}
|
|
338
345
|
/**
|
|
339
|
-
* Narrows an unknown value to a transport-neutral {@link WireEvent}
|
|
346
|
+
* Narrows an unknown value to a transport-neutral {@link WireEvent} — the guard a consumer's own
|
|
347
|
+
* transport applies to an inbound frame.
|
|
340
348
|
*
|
|
341
349
|
* @param value - The candidate wire event
|
|
342
350
|
* @returns True if the value carries an event name, serialized data, and an optional id; false otherwise
|
|
@@ -347,8 +355,8 @@ var isWireEvent = (0, _orkestrel_contract.recordOf)({
|
|
|
347
355
|
id: _orkestrel_contract.isString
|
|
348
356
|
}, ["id"]);
|
|
349
357
|
/**
|
|
350
|
-
* Narrows an unknown value to a {@link TerminalSnapshot} —
|
|
351
|
-
* untrusted persisted row.
|
|
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.
|
|
352
360
|
*
|
|
353
361
|
* @param value - The candidate snapshot read back from storage
|
|
354
362
|
* @returns True if the value carries a non-empty `id` and an optional numeric `timeout`; false otherwise
|
|
@@ -361,16 +369,16 @@ var isTerminalSnapshot = (0, _orkestrel_contract.recordOf)({
|
|
|
361
369
|
//#region src/core/helpers.ts
|
|
362
370
|
/**
|
|
363
371
|
* Decodes one keypress's bytes into a {@link KeyEvent} — total, never throws. A `Uint8Array` is
|
|
364
|
-
* read as UTF-8; the resulting string is matched against the known control bytes and the CRLF
|
|
365
|
-
*
|
|
366
|
-
*
|
|
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`
|
|
367
375
|
* preserved.
|
|
368
376
|
*
|
|
369
377
|
* @remarks
|
|
370
378
|
* - **Single control byte.** A one-character control input (`return` / `backspace` / `tab` /
|
|
371
379
|
* `escape` / `space`, or a Ctrl combo `c` / `d` / `u` / `a` / `e`), or the two-byte `\r\n`
|
|
372
380
|
* CRLF pair, is looked up in {@link CONTROL_NAMES}, carrying its `ctrl` flag.
|
|
373
|
-
* - **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
|
|
374
382
|
* `ESC[A` and `ESCOA` forms, plus `home` / `end` / `delete`) is looked up in
|
|
375
383
|
* {@link SEQUENCE_NAMES} and flagged `meta`.
|
|
376
384
|
* - **Printable character.** A single printable character becomes `name` = that character, with
|
|
@@ -425,8 +433,8 @@ function parseKey(input) {
|
|
|
425
433
|
};
|
|
426
434
|
}
|
|
427
435
|
/**
|
|
428
|
-
* Checks whether a single character is
|
|
429
|
-
*
|
|
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.
|
|
430
438
|
*
|
|
431
439
|
* @param character - The single character to test
|
|
432
440
|
* @returns True if the character is at or above space and is not DEL; false otherwise
|
|
@@ -488,7 +496,8 @@ function sanitizeDisplayText(text) {
|
|
|
488
496
|
return (0, _orkestrel_console.stripControls)((0, _orkestrel_console.strip)(text)).replaceAll(" ", "").replaceAll("\n", "").replaceAll("\r", "");
|
|
489
497
|
}
|
|
490
498
|
/**
|
|
491
|
-
* Sanitizes every terminal-readable string in a parsed form schema
|
|
499
|
+
* Sanitizes every terminal-readable string in a parsed form schema, keeping every identity and
|
|
500
|
+
* answer string verbatim and dropping field metadata.
|
|
492
501
|
*
|
|
493
502
|
* @remarks
|
|
494
503
|
* Display strings pass through {@link sanitizeDisplayText}: labels, help, placeholders, masks,
|
|
@@ -639,7 +648,8 @@ function renderSubmitHeader(styler, theme, message) {
|
|
|
639
648
|
return `${styler.render(theme.roles.success, theme.icons.success)} ${styler.render(theme.roles.message, message)}`;
|
|
640
649
|
}
|
|
641
650
|
/**
|
|
642
|
-
* Renders the styled failure line (`✖ message`) a form driver
|
|
651
|
+
* Renders the styled failure line (`✖ message`) a form driver writes for each refused field before
|
|
652
|
+
* it asks that field again.
|
|
643
653
|
*
|
|
644
654
|
* @param styler - The console styler that renders each role
|
|
645
655
|
* @param theme - The resolved prompt theme
|
|
@@ -650,7 +660,8 @@ function renderErrorLine(styler, theme, message) {
|
|
|
650
660
|
return `${styler.render(theme.roles.error, theme.icons.error)} ${styler.render(theme.roles.error, message)}`;
|
|
651
661
|
}
|
|
652
662
|
/**
|
|
653
|
-
* Builds the initial text-field
|
|
663
|
+
* Builds the initial text-field reducer state — the sanitized label, the declared default, the
|
|
664
|
+
* styler, and the resolved theme.
|
|
654
665
|
*
|
|
655
666
|
* @param field - The text field to render
|
|
656
667
|
* @param styler - The styler used to render the view
|
|
@@ -667,7 +678,8 @@ function createInputState(field, styler = (0, _orkestrel_console.createStyler)()
|
|
|
667
678
|
};
|
|
668
679
|
}
|
|
669
680
|
/**
|
|
670
|
-
* Renders a text-field
|
|
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.
|
|
671
683
|
*
|
|
672
684
|
* @param state - The text field's current reducer state
|
|
673
685
|
* @returns The rendered single-line view — header, pointer, and the typed value or the default
|
|
@@ -722,7 +734,8 @@ function reduceInput(state, key) {
|
|
|
722
734
|
};
|
|
723
735
|
}
|
|
724
736
|
/**
|
|
725
|
-
* Builds the initial password-field
|
|
737
|
+
* Builds the initial password-field reducer state — the text-field state, plus the mask glyph each
|
|
738
|
+
* typed character renders as.
|
|
726
739
|
*
|
|
727
740
|
* @param field - The password field to render
|
|
728
741
|
* @param styler - The styler used to render the view
|
|
@@ -739,7 +752,8 @@ function createPasswordState(field, styler = (0, _orkestrel_console.createStyler
|
|
|
739
752
|
};
|
|
740
753
|
}
|
|
741
754
|
/**
|
|
742
|
-
* Renders a password-field
|
|
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.
|
|
743
757
|
*
|
|
744
758
|
* @param state - The password field's current reducer state
|
|
745
759
|
* @returns The rendered view, with the mask repeated in place of the typed value
|
|
@@ -786,7 +800,8 @@ function reducePassword(state, key) {
|
|
|
786
800
|
};
|
|
787
801
|
}
|
|
788
802
|
/**
|
|
789
|
-
* Builds the initial confirm-field
|
|
803
|
+
* Builds the initial confirm-field reducer state — the sanitized label and the declared default
|
|
804
|
+
* answer.
|
|
790
805
|
*
|
|
791
806
|
* @param field - The confirm field to render
|
|
792
807
|
* @param styler - The styler used to render the view
|
|
@@ -802,7 +817,8 @@ function createConfirmState(field, styler = (0, _orkestrel_console.createStyler)
|
|
|
802
817
|
};
|
|
803
818
|
}
|
|
804
819
|
/**
|
|
805
|
-
* Renders a confirm-field
|
|
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.
|
|
806
822
|
*
|
|
807
823
|
* @param state - The confirm field's current reducer state
|
|
808
824
|
* @returns The rendered view — the header and the yes/no group with the default capitalized
|
|
@@ -845,7 +861,8 @@ function reduceConfirm(state, key) {
|
|
|
845
861
|
};
|
|
846
862
|
}
|
|
847
863
|
/**
|
|
848
|
-
* Builds the initial select-field
|
|
864
|
+
* Builds the initial select-field reducer state — the offered choices, with the focus pre-placed on
|
|
865
|
+
* the declared default.
|
|
849
866
|
*
|
|
850
867
|
* @param field - The select field to render
|
|
851
868
|
* @param styler - The styler used to render the view
|
|
@@ -864,7 +881,8 @@ function createSelectState(field, styler = (0, _orkestrel_console.createStyler)(
|
|
|
864
881
|
};
|
|
865
882
|
}
|
|
866
883
|
/**
|
|
867
|
-
* Renders a select-field
|
|
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.
|
|
868
886
|
*
|
|
869
887
|
* @param state - The select field's current reducer state
|
|
870
888
|
* @returns The rendered view — the header, then one row per choice with the focused row marked
|
|
@@ -878,7 +896,7 @@ function renderSelectView(state) {
|
|
|
878
896
|
}
|
|
879
897
|
/**
|
|
880
898
|
* Advances a select prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
881
|
-
* reducer. `up` / `down` (and `k` / `j`) move the focus,
|
|
899
|
+
* reducer. `up` / `down` (and `k` / `j`) move the focus, wrapping at the ends; return submits the
|
|
882
900
|
* focused choice's `value`; ctrl-c cancels. An empty choice list can never submit (a higher layer
|
|
883
901
|
* guards against it); any other key is ignored.
|
|
884
902
|
*
|
|
@@ -937,7 +955,8 @@ function reduceSelect(state, key) {
|
|
|
937
955
|
};
|
|
938
956
|
}
|
|
939
957
|
/**
|
|
940
|
-
* Builds the initial checkbox-field
|
|
958
|
+
* Builds the initial checkbox-field reducer state — the offered choices, with every value in the
|
|
959
|
+
* field's `default` list pre-checked.
|
|
941
960
|
*
|
|
942
961
|
* @param field - The checkbox field to render
|
|
943
962
|
* @param styler - The styler used to render the view
|
|
@@ -960,7 +979,8 @@ function createCheckboxState(field, styler = (0, _orkestrel_console.createStyler
|
|
|
960
979
|
};
|
|
961
980
|
}
|
|
962
981
|
/**
|
|
963
|
-
* Renders a checkbox-field
|
|
982
|
+
* Renders a checkbox-field reducer state as a multi-line styled view — one box per choice, and the
|
|
983
|
+
* selected count beneath them.
|
|
964
984
|
*
|
|
965
985
|
* @param state - The checkbox field's current reducer state
|
|
966
986
|
* @returns The rendered view — the header, one box per choice, and the selected count
|
|
@@ -1048,7 +1068,7 @@ function reduceCheckbox(state, key) {
|
|
|
1048
1068
|
}
|
|
1049
1069
|
/**
|
|
1050
1070
|
* Toggles `index` in a readonly index list — copy-on-write, returning the new sorted-by-insertion
|
|
1051
|
-
* list.
|
|
1071
|
+
* list; the primitive {@link reduceCheckbox} calls.
|
|
1052
1072
|
*
|
|
1053
1073
|
* @param indices - The ticked indices, in tick order
|
|
1054
1074
|
* @param index - The index to add when absent, or drop when present
|
|
@@ -1058,7 +1078,8 @@ function toggleIndex(indices, index) {
|
|
|
1058
1078
|
return indices.includes(index) ? indices.filter((i) => i !== index) : [...indices, index];
|
|
1059
1079
|
}
|
|
1060
1080
|
/**
|
|
1061
|
-
* Builds the initial editor-field
|
|
1081
|
+
* Builds the initial editor-field reducer state — the committed lines empty, and the declared
|
|
1082
|
+
* default held for a finish with nothing typed.
|
|
1062
1083
|
*
|
|
1063
1084
|
* @param field - The editor field to render
|
|
1064
1085
|
* @param styler - The styler used to render the view
|
|
@@ -1076,7 +1097,8 @@ function createEditorState(field, styler = (0, _orkestrel_console.createStyler)(
|
|
|
1076
1097
|
};
|
|
1077
1098
|
}
|
|
1078
1099
|
/**
|
|
1079
|
-
* Renders an editor-field
|
|
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.
|
|
1080
1102
|
*
|
|
1081
1103
|
* @param state - The editor field's current reducer state
|
|
1082
1104
|
* @returns The rendered view — the hinted header, the committed lines, and the line in progress
|
|
@@ -1089,8 +1111,8 @@ function renderEditorView(state) {
|
|
|
1089
1111
|
/**
|
|
1090
1112
|
* Advances an editor prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
1091
1113
|
* reducer. Printable characters extend the current line; backspace shrinks it; return commits the
|
|
1092
|
-
* current line and starts a fresh one; ctrl-d
|
|
1093
|
-
* 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.
|
|
1094
1116
|
*
|
|
1095
1117
|
* @param state - The editor field's current reducer state
|
|
1096
1118
|
* @param key - The decoded keypress to apply
|
|
@@ -1142,9 +1164,10 @@ function reduceEditor(state, key) {
|
|
|
1142
1164
|
};
|
|
1143
1165
|
}
|
|
1144
1166
|
/**
|
|
1145
|
-
* Applies a single line-editing {@link KeyEvent} to a text buffer — the editing shared by input
|
|
1146
|
-
* password
|
|
1147
|
-
* `space` appends a space; ctrl-u clears the line
|
|
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`.
|
|
1148
1171
|
*
|
|
1149
1172
|
* @param value - The buffer the field holds so far
|
|
1150
1173
|
* @param key - The decoded keypress to apply
|
|
@@ -1195,8 +1218,10 @@ function isAbortError(error) {
|
|
|
1195
1218
|
return (error instanceof DOMException || error instanceof Error) && error.name === "AbortError";
|
|
1196
1219
|
}
|
|
1197
1220
|
/**
|
|
1198
|
-
* Checks whether `url` is an
|
|
1199
|
-
* 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.
|
|
1200
1225
|
*
|
|
1201
1226
|
* @remarks
|
|
1202
1227
|
* A loopback host (`localhost`, `127.0.0.1`, `[::1]`) over `http://` is exempt (local
|
|
@@ -1223,7 +1248,8 @@ function isInsecureRemote(url) {
|
|
|
1223
1248
|
return hostname !== "localhost" && hostname !== "127.0.0.1" && hostname !== "[::1]";
|
|
1224
1249
|
}
|
|
1225
1250
|
/**
|
|
1226
|
-
* Serializes a parked {@link PendingForm} into a {@link WireEvent}
|
|
1251
|
+
* Serializes a parked {@link PendingForm} into a `pending` {@link WireEvent}, whose frame `id` is
|
|
1252
|
+
* the form's own id.
|
|
1227
1253
|
*
|
|
1228
1254
|
* @param form - The parked form's wire-safe record
|
|
1229
1255
|
* @returns The `pending` frame — the JSON-stringified record as `data`, and the form's own `id`
|
|
@@ -1236,7 +1262,8 @@ function serializePending(form) {
|
|
|
1236
1262
|
};
|
|
1237
1263
|
}
|
|
1238
1264
|
/**
|
|
1239
|
-
* Serializes a parked
|
|
1265
|
+
* Serializes a parked form's expiry or release into an `expire` {@link WireEvent}, whose `data` is
|
|
1266
|
+
* the JSON `{ id }` payload.
|
|
1240
1267
|
*
|
|
1241
1268
|
* @param id - The id of the parked form that expired or was released
|
|
1242
1269
|
* @returns The `expire` frame, carrying the JSON-stringified `{ id }` payload as `data`
|
|
@@ -1248,7 +1275,8 @@ function serializeExpire(id) {
|
|
|
1248
1275
|
};
|
|
1249
1276
|
}
|
|
1250
1277
|
/**
|
|
1251
|
-
* Serializes the {@link WireEvent} a broker or manager sends when it is going away
|
|
1278
|
+
* Serializes the `destroy` {@link WireEvent} a broker or manager sends when it is going away, which
|
|
1279
|
+
* carries no payload.
|
|
1252
1280
|
*
|
|
1253
1281
|
* @returns The `destroy` frame, whose `data` is empty because the signal carries no payload
|
|
1254
1282
|
*/
|
|
@@ -1261,8 +1289,9 @@ function serializeDestroy() {
|
|
|
1261
1289
|
//#endregion
|
|
1262
1290
|
//#region src/core/Prompt.ts
|
|
1263
1291
|
/**
|
|
1264
|
-
* Implements the headless form broker. It parks live forms, exposes their serialized schemas,
|
|
1265
|
-
* 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.
|
|
1266
1295
|
*
|
|
1267
1296
|
* @remarks
|
|
1268
1297
|
* A parked record carries one call to `serializeForm`. A failed fill or submit leaves the record
|
|
@@ -1452,8 +1481,9 @@ var Prompt = class {
|
|
|
1452
1481
|
//#endregion
|
|
1453
1482
|
//#region src/core/PromptClient.ts
|
|
1454
1483
|
/**
|
|
1455
|
-
* Implements the SSE form bridge. It ingests serialized forms from a remote broker
|
|
1456
|
-
* 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.
|
|
1457
1487
|
*
|
|
1458
1488
|
* @remarks
|
|
1459
1489
|
* - **Connect + reconnect.** {@link connect} opens the SSE stream and reconnects after a transport
|
|
@@ -1465,8 +1495,9 @@ var Prompt = class {
|
|
|
1465
1495
|
* - **Safe local form.** The rendering copy omits every wire `pattern`, because Form compiles a
|
|
1466
1496
|
* pattern during local evaluation. The broker's parked form retains it and remains authoritative.
|
|
1467
1497
|
* - **Refusal retry.** A structured `rejected` response seeds a new rendering form with the values
|
|
1468
|
-
*
|
|
1469
|
-
* counter truncates the loop; acceptance, expiry, and the broker's own teardown
|
|
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.
|
|
1470
1501
|
* - **Replay safety.** A replayed id is skipped while it is queued, rendering, or posting. Once an
|
|
1471
1502
|
* attempt ends, a later delivery of that id may be rendered again.
|
|
1472
1503
|
*
|
|
@@ -1757,24 +1788,24 @@ var PromptClient = class {
|
|
|
1757
1788
|
//#endregion
|
|
1758
1789
|
//#region src/core/TerminalManager.ts
|
|
1759
1790
|
/**
|
|
1760
|
-
* Registers named {@link PromptInterface} brokers
|
|
1761
|
-
*
|
|
1762
|
-
*
|
|
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.
|
|
1763
1794
|
*
|
|
1764
1795
|
* @remarks
|
|
1765
1796
|
* - **Registry.** `add(name, options?)` mints (or, if `name` is already mounted, returns the
|
|
1766
|
-
*
|
|
1797
|
+
* existing broker unchanged — idempotent, never clobbers a live/parked endpoint). Every mounted
|
|
1767
1798
|
* broker's `pending` / `answer` / `expire` events are re-emitted on the manager, attributed by
|
|
1768
1799
|
* `name`.
|
|
1769
|
-
* - **`ask`.** The target must already be mounted
|
|
1800
|
+
* - **`ask`.** The target must already be mounted through {@link add} — `ask` never auto-adds it;
|
|
1770
1801
|
* rejects `TARGET` for an unknown `to` (listing the known names). Rejects `DEADLOCK` when parking
|
|
1771
|
-
* `from → to` would close a cycle over the
|
|
1802
|
+
* `from → to` would close a cycle over the current in-flight edge set (walked transitively);
|
|
1772
1803
|
* otherwise parks the caller's live form through the target's broker and returns that form's own
|
|
1773
1804
|
* `answer` promise. Edge cleanup never alters the value or rejection the caller observes.
|
|
1774
1805
|
* - **Durable open / save.** `open(name)` restores an EMPTY broker from the `store` (parked
|
|
1775
1806
|
* Promises are process-bound and never resurrected); `save(name)` persists the endpoint's
|
|
1776
1807
|
* configured `timeout`.
|
|
1777
|
-
* - **Removal.** `remove` drops one endpoint, a batch (the array overload declared
|
|
1808
|
+
* - **Removal.** `remove` drops one endpoint, a batch (the array overload declared first), or every
|
|
1778
1809
|
* endpoint when called without an argument. It destroys each broker, which expires every form
|
|
1779
1810
|
* still parked on it. `destroy` is idempotent.
|
|
1780
1811
|
*
|
|
@@ -2001,9 +2032,10 @@ var TerminalManager = class {
|
|
|
2001
2032
|
//#region src/core/stores/MemoryTerminalStore.ts
|
|
2002
2033
|
/**
|
|
2003
2034
|
* Implements the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of
|
|
2004
|
-
* {@link TerminalSnapshot}
|
|
2005
|
-
* {@link import('../factories.js').createMemoryTerminalStore} builds
|
|
2006
|
-
* {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore}.
|
|
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.
|
|
2007
2039
|
*
|
|
2008
2040
|
* @remarks
|
|
2009
2041
|
* A plain `Map<string, TerminalSnapshot>` — the snapshot is already pure, self-contained CONFIG-only
|
|
@@ -2066,10 +2098,11 @@ var MemoryTerminalStore = class {
|
|
|
2066
2098
|
//#endregion
|
|
2067
2099
|
//#region src/core/stores/DatabaseTerminalStore.ts
|
|
2068
2100
|
/**
|
|
2069
|
-
* Implements a {@link TerminalStoreInterface} backed by one table of the `databases` layer — an
|
|
2070
|
-
* durable
|
|
2071
|
-
* `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
|
|
2072
|
-
* {@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.
|
|
2073
2106
|
*
|
|
2074
2107
|
* @remarks
|
|
2075
2108
|
* The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
|
|
@@ -2183,9 +2216,10 @@ function createPrompt(options) {
|
|
|
2183
2216
|
return new Prompt(options);
|
|
2184
2217
|
}
|
|
2185
2218
|
/**
|
|
2186
|
-
* Creates the SSE prompt {@link PromptClientInterface}
|
|
2187
|
-
* endpoint, dispatches each received form to a local
|
|
2188
|
-
* 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.
|
|
2189
2223
|
*
|
|
2190
2224
|
* @param options - See {@link PromptClientOptions} (`url` + `terminal` required)
|
|
2191
2225
|
* @returns A {@link PromptClientInterface}
|
|
@@ -2209,9 +2243,9 @@ function createPromptClient(options) {
|
|
|
2209
2243
|
return new PromptClient(options);
|
|
2210
2244
|
}
|
|
2211
2245
|
/**
|
|
2212
|
-
* Creates the multi-endpoint {@link TerminalManager} — a named registry of
|
|
2213
|
-
*
|
|
2214
|
-
*
|
|
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.
|
|
2215
2249
|
*
|
|
2216
2250
|
* @param options - See {@link TerminalManagerOptions}
|
|
2217
2251
|
* @returns A {@link TerminalManager}
|
|
@@ -2246,7 +2280,8 @@ function createMemoryTerminalStore() {
|
|
|
2246
2280
|
/**
|
|
2247
2281
|
* Creates a {@link TerminalStoreInterface} backed by one table of the `databases` layer — the
|
|
2248
2282
|
* driver-pluggable twin of {@link createMemoryTerminalStore}, storing each endpoint's config
|
|
2249
|
-
* snapshot as one opaque JSON column.
|
|
2283
|
+
* snapshot as one opaque JSON column. The default driver is an in-memory `@orkestrel/database`
|
|
2284
|
+
* driver.
|
|
2250
2285
|
*
|
|
2251
2286
|
* @param driver - The {@link DriverInterface} backing the table (default an in-memory driver)
|
|
2252
2287
|
* @returns A {@link TerminalStoreInterface}
|