@orkestrel/terminal 0.0.1
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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/src/core/index.cjs +1739 -0
- package/dist/src/core/index.cjs.map +1 -0
- package/dist/src/core/index.d.cts +1273 -0
- package/dist/src/core/index.d.ts +1273 -0
- package/dist/src/core/index.js +1651 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/server/index.cjs +478 -0
- package/dist/src/server/index.cjs.map +1 -0
- package/dist/src/server/index.d.cts +321 -0
- package/dist/src/server/index.d.ts +321 -0
- package/dist/src/server/index.js +458 -0
- package/dist/src/server/index.js.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1,1273 @@
|
|
|
1
|
+
import { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
2
|
+
import { EmitterHooks } from '@orkestrel/emitter';
|
|
3
|
+
import { EmitterInterface } from '@orkestrel/emitter';
|
|
4
|
+
import { Guard } from '@orkestrel/contract';
|
|
5
|
+
import { StylerInterface } from '@orkestrel/console';
|
|
6
|
+
|
|
7
|
+
/** The `Accept` header value that opens the broker's SSE stream. */
|
|
8
|
+
export declare const ACCEPT_EVENT_STREAM = "text/event-stream";
|
|
9
|
+
|
|
10
|
+
/** Matches an alphanumeric string (letters and digits only). The `alphanumeric` rule tests against this. */
|
|
11
|
+
export declare const ALPHANUMERIC_PATTERN: RegExp;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Append a rule-backed {@link Validator} to `validators` when the rule is enabled — a `false` /
|
|
15
|
+
* `undefined` `check` is skipped, a function `check` is added verbatim (the custom override), and
|
|
16
|
+
* a primitive `check` is wrapped via {@link buildRuleValidator}. Mutates `validators` in place.
|
|
17
|
+
*/
|
|
18
|
+
export declare function appendRule(validators: Validator[], rule: string, check: boolean | number | string | Validator | undefined): void;
|
|
19
|
+
|
|
20
|
+
/** Backspace (BS, U+0008) — Ctrl+H / some terminals' Backspace. */
|
|
21
|
+
export declare const BACKSPACE: string;
|
|
22
|
+
|
|
23
|
+
/** Wrap a named rule + its primitive check into a {@link Validator} (returns `true` or the rule's message). */
|
|
24
|
+
export declare function buildRuleValidator(rule: string, check: boolean | number | string): Validator;
|
|
25
|
+
|
|
26
|
+
/** A choice item in a {@link CheckboxOptions} prompt — a {@link PromptChoice} plus an optional initial `checked` state. */
|
|
27
|
+
export declare interface CheckboxChoice {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly value: string;
|
|
30
|
+
readonly description?: string;
|
|
31
|
+
readonly checked?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Options for a multi-selection {@link import('./helpers.js').checkboxReduce} prompt.
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* `choices` accepts bare strings or full {@link CheckboxChoice} objects (with an optional
|
|
39
|
+
* initial `checked`); {@link import('./helpers.js').createCheckboxState} normalizes them.
|
|
40
|
+
* `min` / `max` gate submission — a submit with fewer than `min` or more than `max` selected
|
|
41
|
+
* is rejected (the prompt stays active with the reason in the view).
|
|
42
|
+
*/
|
|
43
|
+
export declare interface CheckboxOptions {
|
|
44
|
+
readonly message: string;
|
|
45
|
+
readonly choices: readonly (string | CheckboxChoice)[];
|
|
46
|
+
readonly min?: number;
|
|
47
|
+
readonly max?: number;
|
|
48
|
+
readonly styler?: StylerInterface;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Advance a checkbox prompt by one {@link KeyEvent} — the pure
|
|
53
|
+
* `(state, key) → PromptStep<readonly string[]>` reducer. `up` / `down` (and `k` / `j`) move the
|
|
54
|
+
* focus (wrapping); `space` toggles the focused index in the checked set; return submits the
|
|
55
|
+
* checked values in CHOICE order — gated by `min` / `max` (an out-of-range count stays active with
|
|
56
|
+
* the reason in the view); ctrl-c cancels.
|
|
57
|
+
*/
|
|
58
|
+
export declare function checkboxReduce(state: CheckboxState, key: KeyEvent): PromptStep<readonly string[], CheckboxState>;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The immutable state of a checkbox prompt — the normalized choices, the styler, the `focused`
|
|
62
|
+
* index, the set of `checked` indices, the optional `min` / `max` gate, and the current `error`.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* Built by {@link import('./helpers.js').createCheckboxState}; advanced by
|
|
66
|
+
* {@link import('./helpers.js').checkboxReduce}. `up` / `down` move `focus` (wrapping); `space`
|
|
67
|
+
* toggles the focused index in `checked`; `return` submits the checked values in choice order
|
|
68
|
+
* (rejected, with `error`, when the count is outside `[min, max]`). `checked` is modelled as a
|
|
69
|
+
* readonly index array (plain JSON data, copy-on-write — no `Set` to clone).
|
|
70
|
+
*/
|
|
71
|
+
export declare interface CheckboxState {
|
|
72
|
+
readonly message: string;
|
|
73
|
+
readonly choices: readonly CheckboxChoice[];
|
|
74
|
+
readonly styler: StylerInterface;
|
|
75
|
+
readonly focused: number;
|
|
76
|
+
readonly checked: readonly number[];
|
|
77
|
+
readonly min?: number;
|
|
78
|
+
readonly max?: number;
|
|
79
|
+
readonly error?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Render a {@link CheckboxState} as a MULTI-LINE styled view — the header, one box per choice (focused + checked marked), a count, and any error. */
|
|
83
|
+
export declare function checkboxView(state: CheckboxState): string;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Compose several {@link Validator}s into ONE short-circuiting validator — it runs them in order
|
|
87
|
+
* and returns the FIRST error message, or `true` when all pass. The empty composition always
|
|
88
|
+
* passes.
|
|
89
|
+
*/
|
|
90
|
+
export declare function composeValidators(...validators: Validator[]): Validator;
|
|
91
|
+
|
|
92
|
+
/** Options for a yes/no {@link import('./helpers.js').confirmReduce} confirmation prompt. */
|
|
93
|
+
export declare interface ConfirmOptions {
|
|
94
|
+
readonly message: string;
|
|
95
|
+
readonly default?: boolean;
|
|
96
|
+
readonly styler?: StylerInterface;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Advance a confirm prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<boolean>`
|
|
101
|
+
* reducer. `y` / `Y` submits `true`, `n` / `N` submits `false`, return on an empty line submits
|
|
102
|
+
* the `default`, ctrl-c cancels; any other key is ignored (stays active).
|
|
103
|
+
*/
|
|
104
|
+
export declare function confirmReduce(state: ConfirmState, key: KeyEvent): PromptStep<boolean, ConfirmState>;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The immutable state of a confirm prompt — its message, the default answer, and the styler.
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* Built by {@link import('./helpers.js').createConfirmState}; advanced by
|
|
111
|
+
* {@link import('./helpers.js').confirmReduce}. `y` / `Y` submits `true`, `n` / `N` submits
|
|
112
|
+
* `false`, and `enter` takes the `default` (rendered with the active letter capitalized in the
|
|
113
|
+
* `(Y/n)` hint).
|
|
114
|
+
*/
|
|
115
|
+
export declare interface ConfirmState {
|
|
116
|
+
readonly message: string;
|
|
117
|
+
readonly default: boolean;
|
|
118
|
+
readonly styler: StylerInterface;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Render a {@link ConfirmState} as a styled view — the header plus a `(Y/n)` hint with the default letter emphasized. */
|
|
122
|
+
export declare function confirmView(state: ConfirmState): string;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The single control BYTE → key descriptor table {@link import('./helpers.js').parseKey}
|
|
126
|
+
* consults for the one-byte keys. Each entry carries the canonical `name` and whether it is a
|
|
127
|
+
* `ctrl` combination. The source of truth for the single-byte key decode; frozen.
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* `return` / `newline` map to `return` (one canonical Enter name); `delete` / `backspace` both
|
|
131
|
+
* map to `backspace` (the two Backspace bytes); the Ctrl combos (`c` / `d` / `u` / `a` / `e`)
|
|
132
|
+
* carry `ctrl: true` so a reducer can match `key.ctrl && key.name === 'c'`. `escape` / `tab` /
|
|
133
|
+
* `space` are plain named keys.
|
|
134
|
+
*/
|
|
135
|
+
export declare const CONTROL_NAMES: Readonly<Record<string, {
|
|
136
|
+
readonly name: string;
|
|
137
|
+
readonly ctrl: boolean;
|
|
138
|
+
}>>;
|
|
139
|
+
|
|
140
|
+
/** Build the initial {@link CheckboxState} from {@link CheckboxOptions} — normalizing choices, seeding the checked set, carrying min/max. */
|
|
141
|
+
export declare function createCheckboxState(options: CheckboxOptions): CheckboxState;
|
|
142
|
+
|
|
143
|
+
/** Build the initial {@link ConfirmState} from {@link ConfirmOptions} — defaulting the answer to `false`. */
|
|
144
|
+
export declare function createConfirmState(options: ConfirmOptions): ConfirmState;
|
|
145
|
+
|
|
146
|
+
/** Build the initial {@link EditorState} from {@link EditorOptions} — resolving the validator + styler, seeding empty lines. */
|
|
147
|
+
export declare function createEditorState(options: EditorOptions): EditorState;
|
|
148
|
+
|
|
149
|
+
/** Build the initial {@link InputState} from {@link InputOptions} — resolving the validator + styler, seeding an empty value. */
|
|
150
|
+
export declare function createInputState(options: InputOptions): InputState;
|
|
151
|
+
|
|
152
|
+
/** Build the initial {@link PasswordState} from {@link PasswordOptions} — resolving the validator + styler + mask. */
|
|
153
|
+
export declare function createPasswordState(options: PasswordOptions): PasswordState;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create the headless prompt {@link PromptInterface} BROKER — it parks each prompt call as a
|
|
157
|
+
* pending prompt and resolves it when {@link PromptInterface.answer} arrives (or rejects on
|
|
158
|
+
* timeout). The tri-surface's headless arm: subscribe `emitter.on('pending', …)` to forward each
|
|
159
|
+
* prompt to whoever can answer (an SSE transport, a remote terminal), then route the answer back
|
|
160
|
+
* through `answer(id, value)`.
|
|
161
|
+
*
|
|
162
|
+
* @param options - See {@link PromptOptions}
|
|
163
|
+
* @returns A {@link PromptInterface}
|
|
164
|
+
*
|
|
165
|
+
* @remarks
|
|
166
|
+
* - **Park-as-Promise.** `await prompt.input({ message })` blocks until `answer(id, value)` accepts
|
|
167
|
+
* a matching value; the value is validated (text forms) and type-checked to the form first.
|
|
168
|
+
* - **Timeout → expire → reject (deterministic).** An unanswered prompt expires after
|
|
169
|
+
* `options.timeout` (default {@link import('./constants.js').DEFAULT_PROMPT_TIMEOUT_MS}) and its
|
|
170
|
+
* Promise rejects with a {@link import('./errors.js').TerminalError}; inject `options.timer` to
|
|
171
|
+
* drive expiry without real time.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* import { createPrompt } from '@src/core'
|
|
176
|
+
*
|
|
177
|
+
* const prompt = createPrompt()
|
|
178
|
+
* prompt.emitter.on('pending', (pending) => send(pending)) // forward to a remote client
|
|
179
|
+
* const name = await prompt.input({ message: 'Your name', validate: { required: true } })
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
export declare function createPrompt(options?: PromptOptions): PromptInterface;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Create the SSE prompt {@link PromptClientInterface} BRIDGE — it connects to a remote broker's SSE
|
|
186
|
+
* endpoint, dispatches each received prompt to a LOCAL {@link import('./types.js').PromptFormInterface}
|
|
187
|
+
* terminal (so a human at this machine answers a prompt parked elsewhere), and POSTs the answer
|
|
188
|
+
* back. Universal — `fetch` / SSE are web-standard.
|
|
189
|
+
*
|
|
190
|
+
* @param options - See {@link PromptClientOptions} (`url` + `terminal` required)
|
|
191
|
+
* @returns A {@link PromptClientInterface}
|
|
192
|
+
*
|
|
193
|
+
* @remarks
|
|
194
|
+
* - **Connect + reconnect.** `await client.connect()` streams remote prompts until the stream
|
|
195
|
+
* ends; it reconnects with the `delay` backoff unless `reconnect` is `false` / the client was
|
|
196
|
+
* `destroy`ed. Inject `options.fetch` (a scripted `fetch`) and `options.timer` to drive it
|
|
197
|
+
* deterministically in tests — no real network.
|
|
198
|
+
* - **§14 wire narrowing.** Every decoded prompt is guard-narrowed before dispatch (never an `as`).
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* import { createPromptClient } from '@src/core'
|
|
203
|
+
*
|
|
204
|
+
* const client = createPromptClient({ url: 'http://host/prompts', terminal })
|
|
205
|
+
* await client.connect()
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
export declare function createPromptClient(options: PromptClientOptions): PromptClientInterface;
|
|
209
|
+
|
|
210
|
+
/** Build the initial {@link SelectState} from {@link SelectOptions} — normalizing choices and pre-focusing the default. */
|
|
211
|
+
export declare function createSelectState(options: SelectOptions): SelectState;
|
|
212
|
+
|
|
213
|
+
/** Ctrl+A (SOH, U+0001) — move to start of line. */
|
|
214
|
+
export declare const CTRL_A: string;
|
|
215
|
+
|
|
216
|
+
/** Ctrl+C (ETX, U+0003) — interrupt / cancel. */
|
|
217
|
+
export declare const CTRL_C: string;
|
|
218
|
+
|
|
219
|
+
/** Ctrl+D (EOT, U+0004) — end-of-transmission / finish (the editor's commit key). */
|
|
220
|
+
export declare const CTRL_D: string;
|
|
221
|
+
|
|
222
|
+
/** Ctrl+E (ENQ, U+0005) — move to end of line. */
|
|
223
|
+
export declare const CTRL_E: string;
|
|
224
|
+
|
|
225
|
+
/** Ctrl+U (NAK, U+0015) — clear the current line. */
|
|
226
|
+
export declare const CTRL_U: string;
|
|
227
|
+
|
|
228
|
+
/** The default mask glyph a {@link import('./types.js').PasswordState} renders each input character as — `*`. */
|
|
229
|
+
export declare const DEFAULT_MASK = "*";
|
|
230
|
+
|
|
231
|
+
/** How long (ms) the {@link import('./types.js').PromptInterface} broker parks an unanswered prompt before it expires — 5 minutes. */
|
|
232
|
+
export declare const DEFAULT_PROMPT_TIMEOUT_MS = 300000;
|
|
233
|
+
|
|
234
|
+
/** How long (ms) the {@link import('./types.js').PromptClientInterface} waits before each reconnect attempt — 2 seconds. */
|
|
235
|
+
export declare const DEFAULT_RECONNECT_DELAY_MS = 2000;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The default {@link import('./types.js').TimerHandler} — a thin host `setTimeout` / `clearTimeout`
|
|
239
|
+
* wrapper that arms `callback` after `ms` and returns a {@link TimerCancel}. The deadline seam
|
|
240
|
+
* behind both the {@link import('./Prompt.js').Prompt} broker (its expiry) and the
|
|
241
|
+
* {@link import('./PromptClient.js').PromptClient} (its reconnect backoff); a test injects a
|
|
242
|
+
* deterministic timer instead, so neither entity touches real time.
|
|
243
|
+
*/
|
|
244
|
+
export declare function defaultTimer(callback: () => void, ms: number): TimerCancel;
|
|
245
|
+
|
|
246
|
+
/** Delete (DEL, U+007F) — the usual Backspace byte on a Unix TTY. */
|
|
247
|
+
export declare const DELETE: string;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Dispatch a {@link PendingPrompt} to the matching {@link PromptFormInterface} method — the bridge
|
|
251
|
+
* step a {@link PromptClient} runs to drive a LOCAL terminal with a prompt issued elsewhere.
|
|
252
|
+
* Reconstructs typed options from the wire-safe {@link PendingPrompt.options} (every field
|
|
253
|
+
* §14-narrowed, never an `as`; the validator rebuilt from rules via {@link reconstructValidationRules}),
|
|
254
|
+
* then calls the matching prompt form and returns its resolved value.
|
|
255
|
+
*
|
|
256
|
+
* @param terminal - The local {@link PromptFormInterface} to drive
|
|
257
|
+
* @param pending - The decoded pending prompt to dispatch
|
|
258
|
+
* @returns The prompt's resolved value (a `string` / `boolean` / `readonly string[]` per form)
|
|
259
|
+
*/
|
|
260
|
+
export declare function dispatchPendingPrompt(terminal: PromptFormInterface, pending: PendingPrompt): Promise<string | boolean | readonly string[]>;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Apply a single line-editing {@link KeyEvent} to a text buffer — the editing shared by input /
|
|
264
|
+
* password / editor. A printable key appends its character; `backspace` drops the last character;
|
|
265
|
+
* `space` appends a space; ctrl-u clears the line. Returns the new buffer, or `undefined` when the
|
|
266
|
+
* key does not edit the line (so the caller can leave the state untouched).
|
|
267
|
+
*/
|
|
268
|
+
export declare function editLine(value: string, key: KeyEvent): string | undefined;
|
|
269
|
+
|
|
270
|
+
/** Options for a multi-line {@link import('./helpers.js').editorReduce} editor prompt (terminated by ctrl-d). */
|
|
271
|
+
export declare interface EditorOptions {
|
|
272
|
+
readonly message: string;
|
|
273
|
+
readonly default?: string;
|
|
274
|
+
readonly validate?: Validator | ValidationRules;
|
|
275
|
+
readonly styler?: StylerInterface;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Advance an editor prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
280
|
+
* reducer. Printable characters extend the current line; backspace shrinks it; return commits the
|
|
281
|
+
* current line and starts a fresh one; ctrl-d FINISHES (joining all lines, falling back to the
|
|
282
|
+
* default when empty) through the validator; ctrl-c cancels. An invalid finish stays active with
|
|
283
|
+
* the error.
|
|
284
|
+
*/
|
|
285
|
+
export declare function editorReduce(state: EditorState, key: KeyEvent): PromptStep<string, EditorState>;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* The immutable state of an editor prompt — the committed `lines`, the in-progress `current`
|
|
289
|
+
* line, plus the resolved validator + styler, the default, and the current `error`.
|
|
290
|
+
*
|
|
291
|
+
* @remarks
|
|
292
|
+
* Built by {@link import('./helpers.js').createEditorState}; advanced by
|
|
293
|
+
* {@link import('./helpers.js').editorReduce}. A printable key appends to `current`; `return`
|
|
294
|
+
* commits `current` to `lines` and starts a fresh line; ctrl-d finishes, submitting
|
|
295
|
+
* `lines + current` joined by newlines (or `default` when empty). The whole text is validated
|
|
296
|
+
* on finish.
|
|
297
|
+
*/
|
|
298
|
+
export declare interface EditorState {
|
|
299
|
+
readonly message: string;
|
|
300
|
+
readonly default: string;
|
|
301
|
+
readonly validator: Validator;
|
|
302
|
+
readonly styler: StylerInterface;
|
|
303
|
+
readonly lines: readonly string[];
|
|
304
|
+
readonly current: string;
|
|
305
|
+
readonly error?: string;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Render an {@link EditorState} as a MULTI-LINE styled view — the header (with a Ctrl+D hint), the committed lines, the in-progress line, and any error. */
|
|
309
|
+
export declare function editorView(state: EditorState): string;
|
|
310
|
+
|
|
311
|
+
/** Matches an email address — a non-trivial `local@domain.tld` shape. The `email` rule tests against this. */
|
|
312
|
+
export declare const EMAIL_PATTERN: RegExp;
|
|
313
|
+
|
|
314
|
+
/** The styled error line (`✖ message`) — appended beneath a prompt view when the last submit failed validation. */
|
|
315
|
+
export declare function errorLine(styler: StylerInterface, message: string): string;
|
|
316
|
+
|
|
317
|
+
/** Escape (ESC, U+001B) — the lone byte, and the lead byte of every CSI / SS3 sequence. */
|
|
318
|
+
export declare const ESCAPE: string;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Evaluate ONE built-in validation rule against `input`, returning its error message when the
|
|
322
|
+
* rule fails or `undefined` when it passes. The atomic check {@link buildRuleValidator} wraps
|
|
323
|
+
* into a {@link Validator}. Pure.
|
|
324
|
+
*
|
|
325
|
+
* @remarks
|
|
326
|
+
* A function `check` is the custom-override path: it is called and its `true` ⇒ pass, a string
|
|
327
|
+
* ⇒ that message, anything else ⇒ the generic {@link RULE_MESSAGES.invalid}. A primitive `check`
|
|
328
|
+
* runs the named built-in: `required` (non-empty trimmed), `minimum` / `maximum` (length
|
|
329
|
+
* bounds, the message interpolated with the count), `pattern` (a regex source), and the
|
|
330
|
+
* `email` / `url` / `numeric` / `integer` / `alphanumeric` pattern tests.
|
|
331
|
+
*
|
|
332
|
+
* @param rule - The rule name (`'required'`, `'minimum'`, …)
|
|
333
|
+
* @param check - The configured value (a primitive toggle / bound / pattern, or a custom {@link Validator})
|
|
334
|
+
* @param input - The input string to test
|
|
335
|
+
* @returns The error message when the rule fails, else `undefined`
|
|
336
|
+
*/
|
|
337
|
+
export declare function evaluateRule(rule: string, check: boolean | number | string | Validator, input: string): string | undefined;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* A minimal `fetch` — the subset of the global `fetch` the {@link PromptClient} uses (open the
|
|
341
|
+
* SSE stream, POST an answer). Injected so a test drives the client with a controlled
|
|
342
|
+
* `Response` (a scripted SSE `ReadableStream`) instead of a real network.
|
|
343
|
+
*/
|
|
344
|
+
export declare type FetchHandler = (input: string, init?: FetchInit) => Promise<Response>;
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The request init the {@link PromptClient} passes to its {@link FetchHandler} — the `fetch`
|
|
348
|
+
* `RequestInit` fields it actually sets (method / headers / body / abort signal).
|
|
349
|
+
*/
|
|
350
|
+
export declare interface FetchInit {
|
|
351
|
+
readonly method?: string;
|
|
352
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
353
|
+
readonly body?: string;
|
|
354
|
+
readonly signal?: AbortSignal;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** The min/max gate for a checkbox submit — the rejection message when `count` is out of range, else `undefined`. */
|
|
358
|
+
export declare function gateSelection(count: number, min?: number, max?: number): string | undefined;
|
|
359
|
+
|
|
360
|
+
/** The default {@link import('./types.js').FetchHandler} — the global `fetch`, adapted to the minimal injected shape the {@link import('./PromptClient.js').PromptClient} uses. */
|
|
361
|
+
export declare function globalFetch(input: string, init?: FetchInit): Promise<Response>;
|
|
362
|
+
|
|
363
|
+
/** The auth-token request header the {@link import('./types.js').PromptClient} sends when a `token` is configured. */
|
|
364
|
+
export declare const HEADER_TOKEN = "x-orkestrel-token";
|
|
365
|
+
|
|
366
|
+
/** Options for a single-line text {@link import('./helpers.js').inputReduce} prompt. */
|
|
367
|
+
export declare interface InputOptions {
|
|
368
|
+
readonly message: string;
|
|
369
|
+
readonly default?: string;
|
|
370
|
+
readonly validate?: Validator | ValidationRules;
|
|
371
|
+
readonly styler?: StylerInterface;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Advance an input prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
376
|
+
* reducer. Printable characters extend the value; backspace shrinks it; ctrl-u clears it; ctrl-c
|
|
377
|
+
* cancels; return submits (the empty line falls back to the default) through the validator — an
|
|
378
|
+
* invalid submit stays active with the error in the view.
|
|
379
|
+
*/
|
|
380
|
+
export declare function inputReduce(state: InputState, key: KeyEvent): PromptStep<string, InputState>;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The immutable state of a text input prompt — its options, the resolved validator + styler,
|
|
384
|
+
* the accumulated `value`, and the current `error` (when the last submit failed validation).
|
|
385
|
+
*
|
|
386
|
+
* @remarks
|
|
387
|
+
* Built by {@link import('./helpers.js').createInputState}; advanced by
|
|
388
|
+
* {@link import('./helpers.js').inputReduce}. `value` accumulates printable characters and
|
|
389
|
+
* shrinks on backspace; `error` holds the validation message shown in the view after a
|
|
390
|
+
* rejected submit (cleared on the next keystroke).
|
|
391
|
+
*/
|
|
392
|
+
export declare interface InputState {
|
|
393
|
+
readonly message: string;
|
|
394
|
+
readonly default: string;
|
|
395
|
+
readonly validator: Validator;
|
|
396
|
+
readonly styler: StylerInterface;
|
|
397
|
+
readonly value: string;
|
|
398
|
+
readonly error?: string;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** Render an {@link InputState} as a styled view — the header, the typed value (or dimmed default hint), and any error. */
|
|
402
|
+
export declare function inputView(state: InputState): string;
|
|
403
|
+
|
|
404
|
+
/** Matches an integer (optional sign). The `integer` rule tests against this. */
|
|
405
|
+
export declare const INTEGER_PATTERN: RegExp;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Whether a caught value is an `AbortError` — the {@link import('./PromptClient.js').PromptClient}
|
|
409
|
+
* distinguishes a deliberate `disconnect` / teardown (an aborted `fetch`) from a real fault, so it
|
|
410
|
+
* exits its connect loop quietly instead of emitting `error` / reconnecting.
|
|
411
|
+
*/
|
|
412
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
413
|
+
|
|
414
|
+
/** Narrow an unknown value to a {@link CheckboxChoice} — the `recordOf` shape inlined so no non-exported member lingers (§5). */
|
|
415
|
+
export declare function isCheckboxChoice(value: unknown): value is CheckboxChoice;
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Whether `url` is an INSECURE remote endpoint — a plain `http://` URL whose host is NOT a
|
|
419
|
+
* loopback address. Pure string parsing (no `URL` global), so it stays total on malformed input.
|
|
420
|
+
*
|
|
421
|
+
* @remarks
|
|
422
|
+
* A loopback host (`localhost`, `127.0.0.1`, `[::1]`) over `http://` is exempt (local
|
|
423
|
+
* development has no network hop to eavesdrop on); every other `http://` host is insecure.
|
|
424
|
+
* An `https://` URL (or any non-`http://` scheme) is never flagged.
|
|
425
|
+
*
|
|
426
|
+
* @param url - The candidate endpoint URL
|
|
427
|
+
* @returns `true` when `url` is a non-loopback `http://` endpoint
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```ts
|
|
431
|
+
* isInsecureRemote('http://example.com') // true
|
|
432
|
+
* isInsecureRemote('http://localhost:3000') // false
|
|
433
|
+
* isInsecureRemote('https://example.com') // false
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
export declare function isInsecureRemote(url: string): boolean;
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Narrow an unknown wire value to a {@link PendingPrompt} — the §14 guard a {@link PromptClient}
|
|
440
|
+
* applies to each decoded SSE `pending` payload before dispatching it (never an `as`).
|
|
441
|
+
*/
|
|
442
|
+
export declare const isPendingPrompt: Guard<PendingPrompt>;
|
|
443
|
+
|
|
444
|
+
/** Narrow an unknown value to a {@link PendingPromptStatus}. */
|
|
445
|
+
export declare const isPendingPromptStatus: Guard<PendingPromptStatus>;
|
|
446
|
+
|
|
447
|
+
/** Whether a single character is a printable (non-control) character — used by {@link parseKey}'s char fallback. */
|
|
448
|
+
export declare function isPrintable(character: string): boolean;
|
|
449
|
+
|
|
450
|
+
/** Narrow an unknown value to a {@link PromptChoice} — the `recordOf` shape inlined so no non-exported member lingers (§5). */
|
|
451
|
+
export declare function isPromptChoice(value: unknown): value is PromptChoice;
|
|
452
|
+
|
|
453
|
+
/** Narrow an unknown value to a {@link PromptType} — one of the six prompt forms. */
|
|
454
|
+
export declare const isPromptType: Guard<PromptType>;
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Narrow an unknown caught value to a {@link TerminalError}.
|
|
458
|
+
*
|
|
459
|
+
* @param value - The value to test (typically a `catch` binding or a rejected prompt call)
|
|
460
|
+
* @returns `true` when `value` is a {@link TerminalError}
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```ts
|
|
464
|
+
* try {
|
|
465
|
+
* const name = await prompt.input({ message: 'Your name' })
|
|
466
|
+
* } catch (error) {
|
|
467
|
+
* if (isTerminalError(error) && error.code === 'EXPIRE') retryLater()
|
|
468
|
+
* }
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
export declare function isTerminalError(value: unknown): value is TerminalError;
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* The Control Sequence Introducer lead (`ESC[`) for the navigation keys — the prefix of the
|
|
475
|
+
* arrow / home / end / delete sequences {@link SEQUENCE_NAMES} is keyed by. Named `KEY_CSI`
|
|
476
|
+
* (not `CSI`) so it never collides with the console module's SGR `CSI` (both barrel through
|
|
477
|
+
* `@src/core`).
|
|
478
|
+
*/
|
|
479
|
+
export declare const KEY_CSI: string;
|
|
480
|
+
|
|
481
|
+
/** The Single Shift Three lead (`ESCO`) — the alternate arrow-key prefix some terminals emit (`ESC O A`). */
|
|
482
|
+
export declare const KEY_SS3: string;
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* One decoded keypress — the universal, TTY-agnostic representation of a single key, the
|
|
486
|
+
* output of {@link import('./helpers.js').parseKey}. A reducer reads `name` (and the
|
|
487
|
+
* modifier flags) to decide its transition; the raw `sequence` is preserved so a printable
|
|
488
|
+
* character round-trips and an unknown escape is never lost.
|
|
489
|
+
*
|
|
490
|
+
* @remarks
|
|
491
|
+
* - `name` — the canonical key name: a control / navigation key (`return`, `backspace`,
|
|
492
|
+
* `tab`, `escape`, `up` / `down` / `left` / `right`, `space`, `home`, `end`, `delete`),
|
|
493
|
+
* a named ctrl combo (`c` with `ctrl` true for ctrl-c, likewise `d` / `u` / `a` / `e`),
|
|
494
|
+
* or the printable character itself (`'a'`, `'7'`, `'?'`). An UNRECOGNIZED sequence
|
|
495
|
+
* yields `name: ''` (empty) — never a throw (the decoder is total).
|
|
496
|
+
* - `sequence` — the exact input bytes as a string (a `Uint8Array` is decoded UTF-8). The
|
|
497
|
+
* driver writes this verbatim for a printable key; a reducer that needs the literal char
|
|
498
|
+
* reads it.
|
|
499
|
+
* - `ctrl` / `meta` / `shift` — the modifier flags. `ctrl` is `true` for a C0 control byte
|
|
500
|
+
* (ctrl-c / ctrl-d / ctrl-u / ctrl-a / ctrl-e and the like); `meta` is `true` for an
|
|
501
|
+
* ESC-prefixed (Alt) sequence; `shift` is `true` for an uppercase-letter printable.
|
|
502
|
+
*/
|
|
503
|
+
export declare interface KeyEvent {
|
|
504
|
+
readonly name: string;
|
|
505
|
+
readonly sequence: string;
|
|
506
|
+
readonly ctrl: boolean;
|
|
507
|
+
readonly meta: boolean;
|
|
508
|
+
readonly shift: boolean;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Line feed (`\n`, U+000A) — Enter on some terminals / pasted input. */
|
|
512
|
+
export declare const NEWLINE: string;
|
|
513
|
+
|
|
514
|
+
/** Normalize a checkbox choice input into a full {@link CheckboxChoice} (a bare string becomes both name and value). */
|
|
515
|
+
export declare function normalizeCheckboxChoice(choice: string | CheckboxChoice): CheckboxChoice;
|
|
516
|
+
|
|
517
|
+
/** Normalize a select choice input into a full {@link PromptChoice} (a bare string becomes both name and value). */
|
|
518
|
+
export declare function normalizeChoice(choice: string | PromptChoice): PromptChoice;
|
|
519
|
+
|
|
520
|
+
/** Matches a numeric value (integer or decimal, optional sign). The `numeric` rule tests against this. */
|
|
521
|
+
export declare const NUMERIC_PATTERN: RegExp;
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* One parked prompt's runtime state inside the broker — the wire-safe {@link PendingPrompt} record
|
|
525
|
+
* it exposes, plus the live machinery that settles that prompt's Promise. `respond` is the per-form
|
|
526
|
+
* gate-and-resolve closure: it validates + type-checks an answer and (on accept) resolves the parked
|
|
527
|
+
* Promise, returning whether it accepted; it closes over the form's precisely-typed `resolve`, so no
|
|
528
|
+
* per-form generic leaks into `answer`. `expire` rejects the parked Promise; `cancel` clears the
|
|
529
|
+
* injected expiry timer ({@link TimerCancel}).
|
|
530
|
+
*/
|
|
531
|
+
export declare interface Parked {
|
|
532
|
+
readonly prompt: PendingPrompt;
|
|
533
|
+
readonly respond: (value: unknown) => unknown;
|
|
534
|
+
readonly expire: () => void;
|
|
535
|
+
readonly cancel: TimerCancel;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Decode one keypress's bytes into a {@link KeyEvent} — total, never throws. A `Uint8Array` is
|
|
540
|
+
* read as UTF-8; the resulting string is matched against the known control bytes
|
|
541
|
+
* ({@link CONTROL_NAMES}) and escape sequences ({@link SEQUENCE_NAMES}), falling back to a
|
|
542
|
+
* single printable character. An unrecognized sequence yields `name: ''` with the raw `sequence`
|
|
543
|
+
* preserved.
|
|
544
|
+
*
|
|
545
|
+
* @remarks
|
|
546
|
+
* - **Single control byte.** A one-character control input (`return` / `backspace` / `tab` /
|
|
547
|
+
* `escape` / `space`, or a Ctrl combo `c` / `d` / `u` / `a` / `e`) is looked up in
|
|
548
|
+
* {@link CONTROL_NAMES}, carrying its `ctrl` flag.
|
|
549
|
+
* - **Escape sequence.** A multi-byte ESC sequence (`up` / `down` / `left` / `right` in BOTH the
|
|
550
|
+
* `ESC[A` and `ESCOA` forms, plus `home` / `end` / `delete`) is looked up in
|
|
551
|
+
* {@link SEQUENCE_NAMES} and flagged `meta`.
|
|
552
|
+
* - **Printable character.** A single printable character becomes `name` = that character, with
|
|
553
|
+
* `shift` set when it is an uppercase letter. A multi-code-point printable (an emoji, a pasted
|
|
554
|
+
* run) keeps its first code point as the name and the whole input as `sequence`.
|
|
555
|
+
* - **Unknown.** Anything else (an unrecognized escape, an empty input) yields `name: ''` —
|
|
556
|
+
* total, so the driver never crashes on a stray byte.
|
|
557
|
+
*
|
|
558
|
+
* @param input - The raw keypress bytes, as a string or `Uint8Array`
|
|
559
|
+
* @returns The decoded {@link KeyEvent}
|
|
560
|
+
*
|
|
561
|
+
* @example
|
|
562
|
+
* ```ts
|
|
563
|
+
* parseKey('\r') // { name: 'return', sequence: '\r', ctrl: false, meta: false, shift: false }
|
|
564
|
+
* parseKey('\x1b[A') // { name: 'up', sequence: '\x1b[A', ctrl: false, meta: true, shift: false }
|
|
565
|
+
* parseKey('A') // { name: 'A', sequence: 'A', ctrl: false, meta: false, shift: true }
|
|
566
|
+
* parseKey('\x03') // { name: 'c', sequence: '\x03', ctrl: true, meta: false, shift: false }
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
export declare function parseKey(input: string | Uint8Array): KeyEvent;
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Parse a JSON wire string TOTAL — a malformed / empty payload yields `undefined` (the caller's
|
|
573
|
+
* guard then rejects it), never a throw. The {@link import('./PromptClient.js').PromptClient}
|
|
574
|
+
* decodes every SSE `data` field through this before §14-narrowing it.
|
|
575
|
+
*/
|
|
576
|
+
export declare function parseWireJSON(text: string): unknown;
|
|
577
|
+
|
|
578
|
+
/** The always-passing {@link Validator} — the resolved validator when no rules were supplied. */
|
|
579
|
+
export declare function passing(_input: string): true;
|
|
580
|
+
|
|
581
|
+
/** Options for a masked password {@link import('./helpers.js').passwordReduce} prompt. */
|
|
582
|
+
export declare interface PasswordOptions {
|
|
583
|
+
readonly message: string;
|
|
584
|
+
readonly mask?: string;
|
|
585
|
+
readonly validate?: Validator | ValidationRules;
|
|
586
|
+
readonly styler?: StylerInterface;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Advance a password prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
591
|
+
* reducer. Identical line-editing to {@link inputReduce} (printable extends, backspace shrinks,
|
|
592
|
+
* ctrl-u clears, ctrl-c cancels) but the view masks the value; return submits through the
|
|
593
|
+
* validator (no default fallback — a password has no echoed default).
|
|
594
|
+
*/
|
|
595
|
+
export declare function passwordReduce(state: PasswordState, key: KeyEvent): PromptStep<string, PasswordState>;
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* The immutable state of a password prompt — like {@link InputState} but with a `mask`
|
|
599
|
+
* character the view renders in place of each input character.
|
|
600
|
+
*
|
|
601
|
+
* @remarks
|
|
602
|
+
* Built by {@link import('./helpers.js').createPasswordState}; advanced by
|
|
603
|
+
* {@link import('./helpers.js').passwordReduce}. The `value` is the real (unmasked) input;
|
|
604
|
+
* the view shows `mask` repeated `value.length` times.
|
|
605
|
+
*/
|
|
606
|
+
export declare interface PasswordState {
|
|
607
|
+
readonly message: string;
|
|
608
|
+
readonly mask: string;
|
|
609
|
+
readonly validator: Validator;
|
|
610
|
+
readonly styler: StylerInterface;
|
|
611
|
+
readonly value: string;
|
|
612
|
+
readonly error?: string;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** Render a {@link PasswordState} as a styled view — the header, the value masked to `mask` repeated, and any error. */
|
|
616
|
+
export declare function passwordView(state: PasswordState): string;
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* One prompt PARKED by the broker — an id-keyed, wire-safe record of a {@link PromptFormInterface}
|
|
620
|
+
* call awaiting a remote answer. The value a `pending` listener receives and the broker serializes
|
|
621
|
+
* over SSE to a {@link PromptClient}.
|
|
622
|
+
*
|
|
623
|
+
* @remarks
|
|
624
|
+
* - `id` — the unique id (minted via `crypto.randomUUID()`); the key for {@link PromptInterface.answer}.
|
|
625
|
+
* - `form` — which prompt form was called ({@link PromptType}); the discriminant a client
|
|
626
|
+
* dispatches on (named for its axis — the prompt form — never `kind` / `type`).
|
|
627
|
+
* - `message` — the prompt's question (lifted out of `options` for direct display).
|
|
628
|
+
* - `options` — the WIRE-SAFE options (a `validate` FUNCTION dropped; the declarative
|
|
629
|
+
* {@link ValidationRules} data + `choices` / `default` / `mask` kept — see
|
|
630
|
+
* {@link import('./helpers.js').serializePromptOptions}). A client reconstructs the validator
|
|
631
|
+
* from the rules via {@link resolveValidation}.
|
|
632
|
+
* - `status` — the current {@link PendingPromptStatus}.
|
|
633
|
+
* - `time` — the creation timestamp (ms since epoch).
|
|
634
|
+
*/
|
|
635
|
+
export declare interface PendingPrompt {
|
|
636
|
+
readonly id: string;
|
|
637
|
+
readonly form: PromptType;
|
|
638
|
+
readonly message: string;
|
|
639
|
+
readonly options: Readonly<Record<string, unknown>>;
|
|
640
|
+
readonly status: PendingPromptStatus;
|
|
641
|
+
readonly time: number;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* The lifecycle status of a parked {@link PendingPrompt} — where a brokered prompt stands.
|
|
646
|
+
* Names its axis (the pending prompt's progression), never `kind` (AGENTS §4.4).
|
|
647
|
+
*
|
|
648
|
+
* @remarks
|
|
649
|
+
* - `pending` — parked, awaiting an {@link PromptInterface.answer} (the Promise is unresolved).
|
|
650
|
+
* - `answered` — answered and accepted (the Promise resolved with the validated value).
|
|
651
|
+
* - `expired` — timed out (or torn down by `destroy`) before an answer (the Promise rejected).
|
|
652
|
+
*/
|
|
653
|
+
export declare type PendingPromptStatus = 'pending' | 'answered' | 'expired';
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* The headless prompt BROKER (observable §13) — parks each {@link PromptInterface} call as a
|
|
657
|
+
* pending prompt and returns a Promise that resolves when the prompt is {@link answer}ed (or
|
|
658
|
+
* rejects on timeout / teardown). The tri-surface's headless arm: there is no terminal here, so a
|
|
659
|
+
* transport forwards each `pending` event to whoever can answer, and {@link answer} resolves the
|
|
660
|
+
* parked Promise — for environments where direct user access is unavailable (a headless server on
|
|
661
|
+
* stdio, a browser with no TTY, a prompt issued on one machine answered on another).
|
|
662
|
+
*
|
|
663
|
+
* @remarks
|
|
664
|
+
* - **Park-as-Promise.** Each `input` / `password` / `confirm` / `select` / `checkbox` / `editor`
|
|
665
|
+
* mints an id (`crypto.randomUUID()`), parks a wire-safe {@link PendingPrompt}, emits `pending`,
|
|
666
|
+
* and returns an unresolved Promise. The prompt's options are serialized
|
|
667
|
+
* ({@link serializePromptOptions}) so a transport can forward them as-is.
|
|
668
|
+
* - **Answer validates + type-checks.** {@link answer} runs the prompt's per-form gate: it
|
|
669
|
+
* type-checks `value` to the form (string / boolean / string[]) AND, for the text forms, runs
|
|
670
|
+
* the validator resolved from the original `validate` rules. A rejected answer returns `false`
|
|
671
|
+
* and the prompt stays `pending`; an accepted answer resolves the Promise, emits `answer`, and
|
|
672
|
+
* removes the prompt.
|
|
673
|
+
* - **Timeout → expire → reject.** An unanswered prompt expires after `timeout` ms (via the
|
|
674
|
+
* INJECTED timer): `expire` fires and the parked Promise rejects with a {@link TerminalError}
|
|
675
|
+
* (`code: 'EXPIRE'`). {@link destroy} expires every still-pending prompt the same way.
|
|
676
|
+
* - **Deterministic.** The timer is injectable ({@link import('./types.js').TimerHandler}); a test
|
|
677
|
+
* supplies one that fires on demand, so expiry is driven without real time.
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```ts
|
|
681
|
+
* const prompt = createPrompt({ timeout: 60_000 })
|
|
682
|
+
* prompt.emitter.on('pending', (pending) => broadcast(pending)) // forward to a client
|
|
683
|
+
*
|
|
684
|
+
* const name = await prompt.input({ message: 'Your name' }) // parks; resolves on answer()
|
|
685
|
+
* // ...elsewhere, a client POSTs the answer back:
|
|
686
|
+
* prompt.answer(id, 'Ada') // resolves the awaited input() above
|
|
687
|
+
* ```
|
|
688
|
+
*/
|
|
689
|
+
export declare class Prompt implements PromptInterface {
|
|
690
|
+
#private;
|
|
691
|
+
constructor(options?: PromptOptions);
|
|
692
|
+
get emitter(): EmitterInterface<PromptEventMap>;
|
|
693
|
+
get count(): number;
|
|
694
|
+
pending(): readonly PendingPrompt[];
|
|
695
|
+
pending(id: string): PendingPrompt | undefined;
|
|
696
|
+
answer(id: string, value: unknown): boolean;
|
|
697
|
+
input(options: InputOptions): Promise<string>;
|
|
698
|
+
password(options: PasswordOptions): Promise<string>;
|
|
699
|
+
confirm(options: ConfirmOptions): Promise<boolean>;
|
|
700
|
+
select(options: SelectOptions): Promise<string>;
|
|
701
|
+
checkbox(options: CheckboxOptions): Promise<readonly string[]>;
|
|
702
|
+
editor(options: EditorOptions): Promise<string>;
|
|
703
|
+
destroy(): void;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* The prompt-view icon glyphs the reducers render the prompt line and choice rows with. PLAIN
|
|
708
|
+
* glyphs — color is applied by the {@link import('./types.js').PromptState}'s
|
|
709
|
+
* {@link import('@orkestrel/console').StylerInterface} at render time, never baked into the
|
|
710
|
+
* constant (AGENTS — styling orthogonal to data; the rework fixes the terminal's ANSI-in-the-icon).
|
|
711
|
+
* Frozen.
|
|
712
|
+
*
|
|
713
|
+
* @remarks
|
|
714
|
+
* - `question` — the leading mark on a prompt's message line.
|
|
715
|
+
* - `pointer` — the cursor before the input / the focused choice row.
|
|
716
|
+
* - `dot` / `selected` — an unfocused / focused row marker in a select list.
|
|
717
|
+
* - `checked` / `unchecked` — a checked / unchecked box in a checkbox list.
|
|
718
|
+
*/
|
|
719
|
+
export declare const PROMPT_ICONS: Readonly<{
|
|
720
|
+
question: "?";
|
|
721
|
+
pointer: "›";
|
|
722
|
+
dot: "○";
|
|
723
|
+
selected: "●";
|
|
724
|
+
checked: "☑";
|
|
725
|
+
unchecked: "☐";
|
|
726
|
+
}>;
|
|
727
|
+
|
|
728
|
+
/** A choice item in a {@link SelectOptions} prompt — its displayed `name`, its resolved `value`, and an optional one-line `description`. */
|
|
729
|
+
export declare interface PromptChoice {
|
|
730
|
+
readonly name: string;
|
|
731
|
+
readonly value: string;
|
|
732
|
+
readonly description?: string;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* The SSE prompt BRIDGE (observable §13) — the client-side counterpart to
|
|
737
|
+
* {@link import('./Prompt.js').Prompt}. Connects to a remote broker's SSE endpoint, receives
|
|
738
|
+
* serialized pending prompts, dispatches EACH to a LOCAL {@link PromptFormInterface} terminal (so
|
|
739
|
+
* a human at THIS machine answers a prompt parked elsewhere), and POSTs the answer back.
|
|
740
|
+
* Universal — `fetch` + SSE are web-standard, so it runs in a browser or on a server; the
|
|
741
|
+
* injected `fetch` / timer make it fully deterministic in tests.
|
|
742
|
+
*
|
|
743
|
+
* @remarks
|
|
744
|
+
* - **Connect + reconnect.** {@link connect} opens the SSE stream (via the injected `fetch` + the
|
|
745
|
+
* core `SSEParser`) and loops, reconnecting after the stream drops with the `delay` backoff
|
|
746
|
+
* (driven by the injected timer) — unless `reconnect` is `false`, the client was
|
|
747
|
+
* {@link destroy}ed, or the drop was a deliberate {@link disconnect} (an abort).
|
|
748
|
+
* - **Dispatch + answer.** Each decoded `pending` event is narrowed to a `PendingPrompt` (§14 —
|
|
749
|
+
* never an `as`), dispatched to `terminal`, and its resolved value POSTed back to `url`.
|
|
750
|
+
* Dispatch is strictly SERIAL: the read loop drives the terminal for ONE prompt at a time and
|
|
751
|
+
* only reads/dispatches the next event after the current prompt fully settles (its answer
|
|
752
|
+
* POSTed). A prompt id redelivered by the broker AFTER its prior dispatch has settled is
|
|
753
|
+
* dispatched again — the client does not dedupe across completion.
|
|
754
|
+
* - **Server signals.** An `expire` event (the broker dropped a parked prompt) emits `expire`; a
|
|
755
|
+
* `shutdown` event calls {@link disconnect} (not {@link destroy}) — the client stops streaming
|
|
756
|
+
* without auto-reconnect but stays reusable; a later {@link connect} recovers it.
|
|
757
|
+
* - **Lean events (§13).** `connect` / `disconnect` / `expire` / `error` — errors are `unknown`.
|
|
758
|
+
* `disconnect` fires exactly once per connected-to-disconnected transition, whether triggered by
|
|
759
|
+
* the server ending the stream cleanly or by a deliberate {@link disconnect} / {@link destroy}.
|
|
760
|
+
*
|
|
761
|
+
* @example
|
|
762
|
+
* ```ts
|
|
763
|
+
* const client = createPromptClient({
|
|
764
|
+
* url: 'http://localhost:3001/prompts',
|
|
765
|
+
* terminal: createTerminal(), // a local PromptFormInterface (T-c)
|
|
766
|
+
* on: { connect: () => log('connected') },
|
|
767
|
+
* })
|
|
768
|
+
* await client.connect() // streams remote prompts to the local terminal, POSTs answers back
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
export declare class PromptClient implements PromptClientInterface {
|
|
772
|
+
#private;
|
|
773
|
+
readonly url: string;
|
|
774
|
+
constructor(options: PromptClientOptions);
|
|
775
|
+
get emitter(): EmitterInterface<PromptClientEventMap>;
|
|
776
|
+
get connected(): boolean;
|
|
777
|
+
connect(): Promise<void>;
|
|
778
|
+
disconnect(): void;
|
|
779
|
+
destroy(): void;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* The client's event map (AGENTS §13) — lean, errors `unknown`, no listener-error event.
|
|
784
|
+
*
|
|
785
|
+
* @remarks
|
|
786
|
+
* - `connect` — the SSE stream opened.
|
|
787
|
+
* - `disconnect` — the SSE stream closed (the server ended it, or {@link PromptClientInterface.disconnect}).
|
|
788
|
+
* - `expire` — the remote broker signalled a parked prompt expired (carries its `id`).
|
|
789
|
+
* - `error` — a connection / dispatch / POST fault (errors are `unknown`).
|
|
790
|
+
*/
|
|
791
|
+
export declare type PromptClientEventMap = {
|
|
792
|
+
connect: [];
|
|
793
|
+
disconnect: [];
|
|
794
|
+
expire: [id: string];
|
|
795
|
+
error: [error: unknown];
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* The SSE prompt BRIDGE (observable §13) — the client-side counterpart to {@link PromptInterface}.
|
|
800
|
+
* Connects to a remote broker's SSE endpoint, receives serialized {@link PendingPrompt}s,
|
|
801
|
+
* dispatches EACH to a local {@link PromptFormInterface} terminal, and POSTs the answer back —
|
|
802
|
+
* so a human at this machine answers prompts a broker parked elsewhere.
|
|
803
|
+
*
|
|
804
|
+
* @remarks
|
|
805
|
+
* - **Connect.** {@link connect} opens the SSE stream (via the injected `fetch` + the core
|
|
806
|
+
* `SSEParser`) and resolves when the stream ends; it reconnects with the `delay` backoff
|
|
807
|
+
* unless `reconnect` is `false` or the client was {@link destroy}ed.
|
|
808
|
+
* - **Dispatch + answer.** Each decoded prompt is narrowed (§14) and dispatched to `terminal`;
|
|
809
|
+
* the resolved value POSTs back to `url`.
|
|
810
|
+
* - **`connected`** reflects whether the stream is currently open.
|
|
811
|
+
*/
|
|
812
|
+
export declare interface PromptClientInterface {
|
|
813
|
+
readonly emitter: EmitterInterface<PromptClientEventMap>;
|
|
814
|
+
readonly url: string;
|
|
815
|
+
readonly connected: boolean;
|
|
816
|
+
connect(): Promise<void>;
|
|
817
|
+
disconnect(): void;
|
|
818
|
+
destroy(): void;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Options for {@link import('./factories.js').createPromptClient} / the {@link PromptClientInterface}.
|
|
823
|
+
*
|
|
824
|
+
* @remarks
|
|
825
|
+
* - `url` — the remote broker's SSE endpoint (GET opens the stream; answers POST back to it).
|
|
826
|
+
* - `terminal` — the LOCAL {@link PromptFormInterface} each remote prompt is dispatched to, so a
|
|
827
|
+
* human at THIS machine answers a prompt issued elsewhere.
|
|
828
|
+
* - `token` — an optional auth token, sent as the
|
|
829
|
+
* {@link import('./constants.js').HEADER_TOKEN} header on every request.
|
|
830
|
+
* - `reconnect` — whether to reconnect after the stream drops (default `true`).
|
|
831
|
+
* - `delay` — ms to wait before each reconnect attempt (default
|
|
832
|
+
* {@link import('./constants.js').DEFAULT_RECONNECT_DELAY_MS}).
|
|
833
|
+
* - `on` — initial {@link PromptClientEventMap} listeners (AGENTS §8/§13).
|
|
834
|
+
* - `error` — the emitter's listener-error handler (AGENTS §13).
|
|
835
|
+
* - `fetch` — the injected {@link FetchHandler} (default the global `fetch`); supply a scripted
|
|
836
|
+
* fetch to drive the client deterministically in tests.
|
|
837
|
+
* - `timer` — the injected {@link TimerHandler} for the reconnect backoff (default the host
|
|
838
|
+
* `setTimeout`); supply a deterministic timer to drive reconnection without real time.
|
|
839
|
+
*/
|
|
840
|
+
export declare interface PromptClientOptions {
|
|
841
|
+
readonly url: string;
|
|
842
|
+
readonly terminal: PromptFormInterface;
|
|
843
|
+
readonly token?: string;
|
|
844
|
+
readonly reconnect?: boolean;
|
|
845
|
+
readonly delay?: number;
|
|
846
|
+
readonly on?: EmitterHooks<PromptClientEventMap>;
|
|
847
|
+
readonly error?: EmitterErrorHandler;
|
|
848
|
+
readonly fetch?: FetchHandler;
|
|
849
|
+
readonly timer?: TimerHandler;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* The broker's event map (AGENTS §13) — lean, errors `unknown`, no listener-error event.
|
|
854
|
+
*
|
|
855
|
+
* @remarks
|
|
856
|
+
* - `pending` — a prompt was parked (carries the wire-safe {@link PendingPrompt}); a transport
|
|
857
|
+
* forwards it to remote clients.
|
|
858
|
+
* - `answer` — a parked prompt was answered (carries its `id` + the accepted `value`).
|
|
859
|
+
* - `expire` — a parked prompt timed out (or was torn down) unanswered (carries its `id`).
|
|
860
|
+
*/
|
|
861
|
+
export declare type PromptEventMap = {
|
|
862
|
+
pending: [prompt: PendingPrompt];
|
|
863
|
+
answer: [id: string, value: unknown];
|
|
864
|
+
expire: [id: string];
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* The shared ASYNC prompt contract — the six prompt forms as Promise-returning methods. The
|
|
869
|
+
* ONE vocabulary BOTH the headless {@link PromptInterface} broker (this module) and the server
|
|
870
|
+
* `Terminal` driver (T-c) implement, so a prompt issued through this surface resolves the same
|
|
871
|
+
* way on every surface (local TTY, headless broker, remote bridge).
|
|
872
|
+
*
|
|
873
|
+
* @remarks
|
|
874
|
+
* Each method takes the prompt's `*Options` and resolves to that form's value type:
|
|
875
|
+
* - `input` / `password` / `select` / `editor` → a `string`
|
|
876
|
+
* - `confirm` → a `boolean`
|
|
877
|
+
* - `checkbox` → a `readonly string[]` (the checked values, in choice order)
|
|
878
|
+
*
|
|
879
|
+
* This is a behavioral CONTRACT (the method surface), not an observable entity — the broker and
|
|
880
|
+
* the driver add their own emitter / lifecycle on top. The {@link PromptClient} dispatches a
|
|
881
|
+
* remote {@link PendingPrompt} to a LOCAL `PromptFormInterface` (so a human at this machine
|
|
882
|
+
* answers a prompt parked elsewhere).
|
|
883
|
+
*/
|
|
884
|
+
export declare interface PromptFormInterface {
|
|
885
|
+
input(options: InputOptions): Promise<string>;
|
|
886
|
+
password(options: PasswordOptions): Promise<string>;
|
|
887
|
+
confirm(options: ConfirmOptions): Promise<boolean>;
|
|
888
|
+
select(options: SelectOptions): Promise<string>;
|
|
889
|
+
checkbox(options: CheckboxOptions): Promise<readonly string[]>;
|
|
890
|
+
editor(options: EditorOptions): Promise<string>;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/** The styled prompt-message header (`? message`) — the leading line every active prompt view shares. */
|
|
894
|
+
export declare function promptHeader(styler: StylerInterface, message: string): string;
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* The headless prompt BROKER (observable §13) — implements {@link PromptFormInterface} by
|
|
898
|
+
* PARKING each call as a {@link PendingPrompt} and returning a Promise that resolves when the
|
|
899
|
+
* prompt is {@link answer}ed (or rejects on timeout). The local-TTY / headless / remote
|
|
900
|
+
* tri-surface's headless arm: no terminal here — a transport forwards each `pending` to whoever
|
|
901
|
+
* can answer, and {@link answer} resolves the parked Promise.
|
|
902
|
+
*
|
|
903
|
+
* @remarks
|
|
904
|
+
* - **Park-as-Promise.** Each `input` / `password` / … call mints an id, parks a
|
|
905
|
+
* {@link PendingPrompt}, emits `pending`, and returns an unresolved Promise.
|
|
906
|
+
* - **Answer validates.** {@link answer} validates `value` against the prompt's resolved
|
|
907
|
+
* validator AND type-checks it to the prompt form before accepting; a bad answer is rejected
|
|
908
|
+
* (returns `false`, the prompt stays `pending`).
|
|
909
|
+
* - **Timeout → expire → reject.** An unanswered prompt expires after `timeout` ms — `expire`
|
|
910
|
+
* fires and the parked Promise rejects (a {@link import('./errors.js').TerminalError}). The
|
|
911
|
+
* timer is injectable for deterministic tests.
|
|
912
|
+
* - **Accessors (§9.1).** `pending()` lists the parked prompts; `pending(id)` looks one up.
|
|
913
|
+
*/
|
|
914
|
+
export declare interface PromptInterface extends PromptFormInterface {
|
|
915
|
+
readonly emitter: EmitterInterface<PromptEventMap>;
|
|
916
|
+
readonly count: number;
|
|
917
|
+
pending(): readonly PendingPrompt[];
|
|
918
|
+
pending(id: string): PendingPrompt | undefined;
|
|
919
|
+
answer(id: string, value: unknown): boolean;
|
|
920
|
+
destroy(): void;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Options for {@link import('./factories.js').createPrompt} / the {@link PromptInterface} broker.
|
|
925
|
+
*
|
|
926
|
+
* @remarks
|
|
927
|
+
* - `on` — initial {@link PromptEventMap} listeners (AGENTS §8/§13).
|
|
928
|
+
* - `error` — the emitter's listener-error handler (AGENTS §13).
|
|
929
|
+
* - `timeout` — ms a parked prompt waits before it expires + its Promise rejects (default
|
|
930
|
+
* {@link import('./constants.js').DEFAULT_PROMPT_TIMEOUT_MS}).
|
|
931
|
+
* - `timer` — the injected {@link TimerHandler} (default the host `setTimeout`); supply a
|
|
932
|
+
* deterministic timer to drive expiry in tests without real time.
|
|
933
|
+
*/
|
|
934
|
+
export declare interface PromptOptions {
|
|
935
|
+
readonly on?: EmitterHooks<PromptEventMap>;
|
|
936
|
+
readonly error?: EmitterErrorHandler;
|
|
937
|
+
readonly timeout?: number;
|
|
938
|
+
readonly timer?: TimerHandler;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* The discriminant of a {@link PromptStep} — where the prompt stands after a key. `active`:
|
|
943
|
+
* keep prompting (the input was consumed, or rejected by validation). `submit`: the prompt
|
|
944
|
+
* resolved with its `value`. `cancel`: the user aborted (ctrl-c). Names its axis (the prompt's
|
|
945
|
+
* progression), never `kind` (AGENTS §4.4).
|
|
946
|
+
*/
|
|
947
|
+
export declare type PromptStatus = 'active' | 'submit' | 'cancel';
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* The result of one reducer step — the next `state`, the rendered `view`, the `status`, and,
|
|
951
|
+
* on `submit`, the resolved `value`. The whole contract between a pure prompt reducer and the
|
|
952
|
+
* impure driver: the driver applies the next `state`, writes the `view`, and — when `status`
|
|
953
|
+
* is `submit` — reads `value`.
|
|
954
|
+
*
|
|
955
|
+
* @typeParam T - The prompt's resolved value type (`string` for input / password / select /
|
|
956
|
+
* editor, `boolean` for confirm, `readonly string[]` for checkbox).
|
|
957
|
+
* @typeParam S - The prompt's concrete state shape ({@link InputState}, {@link SelectState}, …) —
|
|
958
|
+
* carried directly so `state` stays precisely typed with no union narrowing or assertion.
|
|
959
|
+
*
|
|
960
|
+
* @remarks
|
|
961
|
+
* - `state` — the next immutable prompt state (feed it to the next reduce call). On `submit` /
|
|
962
|
+
* `cancel` it is the final state.
|
|
963
|
+
* - `view` — the styled string to render NOW (possibly MULTI-LINE for `select` / `checkbox`),
|
|
964
|
+
* built through the state's {@link StylerInterface}. On an invalid `submit` it carries the
|
|
965
|
+
* error; the driver re-renders it each step.
|
|
966
|
+
* - `status` — the {@link PromptStatus}: `active` to continue, `submit` when resolved, `cancel`
|
|
967
|
+
* on abort.
|
|
968
|
+
* - `value` — present ONLY on a `submit` step, carrying the prompt's resolved value.
|
|
969
|
+
*/
|
|
970
|
+
export declare interface PromptStep<T, S> {
|
|
971
|
+
readonly state: S;
|
|
972
|
+
readonly view: string;
|
|
973
|
+
readonly status: PromptStatus;
|
|
974
|
+
readonly value?: T;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* The six prompt KINDS this core provides — the value family a higher-level broker (T-b)
|
|
979
|
+
* dispatches on. Names the prompt axis; a named value set (not a toggle), so it stays a union.
|
|
980
|
+
*/
|
|
981
|
+
export declare type PromptType = 'input' | 'password' | 'confirm' | 'select' | 'checkbox' | 'editor';
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Rebuild a wire-decoded `validate` payload into a {@link ValidationRules} bag — the inverse of
|
|
985
|
+
* {@link serializeValidationRules}. Keeps only the primitive rule values (`boolean` / `number` /
|
|
986
|
+
* `string`) a serialized prompt could carry; an empty / non-record / all-dropped payload yields
|
|
987
|
+
* `undefined` (no rules to apply). The client feeds the result back through {@link resolveValidation}.
|
|
988
|
+
*/
|
|
989
|
+
export declare function reconstructValidationRules(value: unknown): ValidationRules | undefined;
|
|
990
|
+
|
|
991
|
+
/** Read a `choices` option as a list of bare strings / full choices — each element narrowed by `guard`, off-shape elements stringified. */
|
|
992
|
+
export declare function resolveChoices<TChoice extends PromptChoice | CheckboxChoice>(options: Readonly<Record<string, unknown>>, guard: Guard<TChoice>): readonly (string | TChoice)[];
|
|
993
|
+
|
|
994
|
+
/** Read one option by key, narrowed by `guard` — `undefined` when absent or off-shape (§14, never an `as`). */
|
|
995
|
+
export declare function resolveOption<T>(options: Readonly<Record<string, unknown>>, key: string, guard: Guard<T>): T | undefined;
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Compile a {@link Validator} or declarative {@link ValidationRules} (or nothing) into ONE
|
|
999
|
+
* composed {@link Validator}. A bare validator passes through; rules are appended in the fixed
|
|
1000
|
+
* order (required → minimum → maximum → pattern → email → url → numeric → integer → alphanumeric
|
|
1001
|
+
* → custom) and composed; absent / empty input yields an always-passing validator. Pure.
|
|
1002
|
+
*
|
|
1003
|
+
* @remarks
|
|
1004
|
+
* Unlike a prior variant (which returned `Validator | undefined`), this ALWAYS returns a
|
|
1005
|
+
* `Validator` — an absent or empty rule set yields a validator that returns `true` for every
|
|
1006
|
+
* input. That keeps a prompt's state unconditional (it always holds a real validator to apply on
|
|
1007
|
+
* submit), with no `undefined` branch at the call site.
|
|
1008
|
+
*
|
|
1009
|
+
* @param validate - A custom {@link Validator}, a {@link ValidationRules} bag, or `undefined`
|
|
1010
|
+
* @returns The composed {@link Validator} (always-passing when nothing was supplied)
|
|
1011
|
+
*
|
|
1012
|
+
* @example
|
|
1013
|
+
* ```ts
|
|
1014
|
+
* const v = resolveValidation({ required: true, minimum: 3 })
|
|
1015
|
+
* v('') // 'This field is required'
|
|
1016
|
+
* v('ab') // 'Must be at least 3 characters'
|
|
1017
|
+
* v('abc') // true
|
|
1018
|
+
* ```
|
|
1019
|
+
*/
|
|
1020
|
+
export declare function resolveValidation(validate?: Validator | ValidationRules): Validator;
|
|
1021
|
+
|
|
1022
|
+
/** Carriage return (`\r`, U+000D) — Enter on most terminals. */
|
|
1023
|
+
export declare const RETURN: string;
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Each built-in validation rule's default error message — what the composed {@link
|
|
1027
|
+
* import('./types.js').Validator} returns when that rule fails (the `minimum` / `maximum`
|
|
1028
|
+
* messages are interpolated with the configured length at build time). Frozen; the source of
|
|
1029
|
+
* truth for the rule-failure copy.
|
|
1030
|
+
*/
|
|
1031
|
+
export declare const RULE_MESSAGES: Readonly<{
|
|
1032
|
+
required: "This field is required";
|
|
1033
|
+
minimum: "Must be at least {count} characters";
|
|
1034
|
+
maximum: "Must be at most {count} characters";
|
|
1035
|
+
pattern: "Must match pattern: {pattern}";
|
|
1036
|
+
email: "Must be a valid email address";
|
|
1037
|
+
url: "Must be a valid URL";
|
|
1038
|
+
numeric: "Must be a numeric value";
|
|
1039
|
+
integer: "Must be an integer";
|
|
1040
|
+
alphanumeric: "Must contain only letters and digits";
|
|
1041
|
+
invalid: "Invalid input";
|
|
1042
|
+
}>;
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Sanitize a list of resolved choices' human-readable labels (`name` + `description`) with
|
|
1046
|
+
* {@link stripControls} — shared by the `select` and `checkbox` branches of
|
|
1047
|
+
* {@link dispatchPendingPrompt} so a remote-supplied choice can never inject raw control bytes
|
|
1048
|
+
* into the local terminal's rendered view.
|
|
1049
|
+
*
|
|
1050
|
+
* @param choices - The resolved choices (bare strings or full {@link PromptChoice} /
|
|
1051
|
+
* {@link CheckboxChoice} objects) as returned by {@link resolveChoices}
|
|
1052
|
+
* @returns The same choices with every `name` / `description` control-stripped
|
|
1053
|
+
*/
|
|
1054
|
+
export declare function sanitizeChoiceLabels<TChoice extends PromptChoice | CheckboxChoice>(choices: readonly (string | TChoice)[]): readonly (string | TChoice)[];
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Options for a single-selection {@link import('./helpers.js').selectReduce} prompt.
|
|
1058
|
+
*
|
|
1059
|
+
* @remarks
|
|
1060
|
+
* `choices` accepts bare strings (used as both `name` and `value`) or full {@link PromptChoice}
|
|
1061
|
+
* objects; {@link import('./helpers.js').createSelectState} normalizes them. `default` pre-focuses
|
|
1062
|
+
* the choice whose `value` matches it.
|
|
1063
|
+
*/
|
|
1064
|
+
export declare interface SelectOptions {
|
|
1065
|
+
readonly message: string;
|
|
1066
|
+
readonly choices: readonly (string | PromptChoice)[];
|
|
1067
|
+
readonly default?: string;
|
|
1068
|
+
readonly styler?: StylerInterface;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Advance a select prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
|
|
1073
|
+
* reducer. `up` / `down` (and `k` / `j`) move the focus, WRAPPING at the ends; return submits the
|
|
1074
|
+
* focused choice's `value`; ctrl-c cancels. An empty choice list can never submit (a higher layer
|
|
1075
|
+
* guards against it); any other key is ignored.
|
|
1076
|
+
*/
|
|
1077
|
+
export declare function selectReduce(state: SelectState, key: KeyEvent): PromptStep<string, SelectState>;
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* The immutable state of a select prompt — the normalized choices, the styler, and the
|
|
1081
|
+
* `focused` index (the highlighted row).
|
|
1082
|
+
*
|
|
1083
|
+
* @remarks
|
|
1084
|
+
* Built by {@link import('./helpers.js').createSelectState}; advanced by
|
|
1085
|
+
* {@link import('./helpers.js').selectReduce}. `up` / `down` move `focused` (wrapping at the
|
|
1086
|
+
* ends); `return` submits the focused choice's `value`.
|
|
1087
|
+
*/
|
|
1088
|
+
export declare interface SelectState {
|
|
1089
|
+
readonly message: string;
|
|
1090
|
+
readonly choices: readonly PromptChoice[];
|
|
1091
|
+
readonly styler: StylerInterface;
|
|
1092
|
+
readonly focused: number;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/** Render a {@link SelectState} as a MULTI-LINE styled view — the header followed by one row per choice, the focused row marked. */
|
|
1096
|
+
export declare function selectView(state: SelectState): string;
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* The exact escape SEQUENCE → canonical key NAME table {@link import('./helpers.js').parseKey}
|
|
1100
|
+
* consults for the navigation / editing keys. Covers BOTH the CSI form (`ESC[A`…) and the SS3
|
|
1101
|
+
* form (`ESCOA`…) of the four arrows, plus the `home` / `end` / `delete` CSI sequences (with
|
|
1102
|
+
* their numeric-tilde variants). The source of truth for the multi-byte key decode; frozen.
|
|
1103
|
+
*
|
|
1104
|
+
* @remarks
|
|
1105
|
+
* Terminals disagree on these: a cursor key is `ESC[A` (normal) or `ESCOA` (application mode),
|
|
1106
|
+
* and Home / End / Delete each have a letter form (`ESC[H` / `ESC[F`) and a numeric form
|
|
1107
|
+
* (`ESC[1~` / `ESC[4~` / `ESC[3~`). Every accepted spelling maps to one name so a reducer never
|
|
1108
|
+
* sees the wire encoding.
|
|
1109
|
+
*/
|
|
1110
|
+
export declare const SEQUENCE_NAMES: Readonly<Record<string, string>>;
|
|
1111
|
+
|
|
1112
|
+
/** Strip functions from a `choices` option — each choice keeps its plain fields; a bare string passes through. */
|
|
1113
|
+
export declare function serializeChoices(choices: unknown): readonly unknown[];
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* Produce the WIRE-SAFE form of a prompt's options — the {@link PendingPrompt.options} a broker
|
|
1117
|
+
* serializes over SSE. Drops everything that cannot cross the wire (a `styler`, and any
|
|
1118
|
+
* function-valued `validate` rule), while KEEPING the declarative data a remote client needs to
|
|
1119
|
+
* reconstruct the prompt: the {@link ValidationRules} as data, plus `message` / `choices` /
|
|
1120
|
+
* `default` / `mask` / `min` / `max`. This is WHY T-a's validation is rules-as-data: a `Validator`
|
|
1121
|
+
* FUNCTION can't be serialized, but its declarative rules can — the client rebuilds the validator
|
|
1122
|
+
* from them via {@link resolveValidation}.
|
|
1123
|
+
*
|
|
1124
|
+
* @remarks
|
|
1125
|
+
* - **Functions are dropped.** A top-level function value (e.g. a bare-`Validator` `validate`) is
|
|
1126
|
+
* omitted entirely; the `styler` (a fluent function-bearing object) is dropped by key.
|
|
1127
|
+
* - **`validate` rules are flattened.** A {@link ValidationRules} bag is copied with each
|
|
1128
|
+
* function rule replaced by `true` (the rule's INTENT survives as the built-in check; its
|
|
1129
|
+
* custom function does not). A bare-function `validate` is dropped (no declarative form to keep).
|
|
1130
|
+
* - **`choices` are function-stripped.** Each choice keeps its plain fields (`name` / `value` /
|
|
1131
|
+
* `description` / `checked`); a bare string passes through.
|
|
1132
|
+
*
|
|
1133
|
+
* @param options - The raw prompt options bag (may hold functions / a styler)
|
|
1134
|
+
* @returns A JSON-safe options record — only serializable, declarative data
|
|
1135
|
+
*/
|
|
1136
|
+
export declare function serializePromptOptions(options: object): Readonly<Record<string, unknown>>;
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Flatten a `validate` option to its wire-safe {@link ValidationRules} DATA — a function rule
|
|
1140
|
+
* becomes `true` (its built-in check survives; its body cannot cross the wire). A bare-function
|
|
1141
|
+
* `validate` (no rules object) has no declarative form, so it yields `undefined` (dropped).
|
|
1142
|
+
*/
|
|
1143
|
+
export declare function serializeValidationRules(validate: unknown): Readonly<Record<string, unknown>> | undefined;
|
|
1144
|
+
|
|
1145
|
+
/** Space (U+0020). */
|
|
1146
|
+
export declare const SPACE = " ";
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* The maximum number of characters the {@link import('./types.js').PromptClient} lets its
|
|
1150
|
+
* SSE parser buffer before treating the stream as hostile — 1 MiB, comfortably above any
|
|
1151
|
+
* legitimate prompt payload. Passed as the `limit` to `createSSEParser` so an unterminated
|
|
1152
|
+
* or oversized `data:` field cannot grow the buffer without bound (a memory-exhaustion guard).
|
|
1153
|
+
*/
|
|
1154
|
+
export declare const SSE_BUFFER_LIMIT = 1048576;
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* The SSE `event:` names the broker emits and the {@link import('./types.js').PromptClient}
|
|
1158
|
+
* dispatches on. Frozen; the source of truth for the wire event vocabulary.
|
|
1159
|
+
*
|
|
1160
|
+
* @remarks
|
|
1161
|
+
* - `pending` — a serialized {@link import('./types.js').PendingPrompt} to dispatch + answer.
|
|
1162
|
+
* - `expire` — an `{ id }` payload: the broker expired a parked prompt (the client drops it).
|
|
1163
|
+
* - `shutdown` — the broker is going away; the client disconnects (no auto-reconnect) but stays reusable.
|
|
1164
|
+
*/
|
|
1165
|
+
export declare const SSE_EVENTS: Readonly<{
|
|
1166
|
+
pending: "pending";
|
|
1167
|
+
expire: "expire";
|
|
1168
|
+
shutdown: "shutdown";
|
|
1169
|
+
}>;
|
|
1170
|
+
|
|
1171
|
+
/** The styled submit line (`✔ message`) — the committed header an interactive prompt shows once resolved. */
|
|
1172
|
+
export declare function submitHeader(styler: StylerInterface, message: string): string;
|
|
1173
|
+
|
|
1174
|
+
/** Tab (`\t`, U+0009). */
|
|
1175
|
+
export declare const TAB: string;
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* An error a {@link import('./Prompt.js').Prompt} broker rejects a parked prompt's Promise with.
|
|
1179
|
+
*
|
|
1180
|
+
* @remarks
|
|
1181
|
+
* Carries a {@link TerminalErrorCode} and an optional `context` bag (the prompt `id`). Thrown —
|
|
1182
|
+
* as a Promise rejection on the awaited prompt call — when a parked prompt is not answered before
|
|
1183
|
+
* its `timeout`, or when the broker is `destroy`ed while the prompt is still `pending` (both
|
|
1184
|
+
* `EXPIRE`); or when the user aborts an interactive server-`Terminal` prompt with ctrl-c
|
|
1185
|
+
* (`CANCEL`). Narrow a caught value with {@link isTerminalError} and branch on `error.code`.
|
|
1186
|
+
*/
|
|
1187
|
+
export declare class TerminalError extends Error {
|
|
1188
|
+
/** The machine-readable condition — see {@link TerminalErrorCode}. */
|
|
1189
|
+
readonly code: TerminalErrorCode;
|
|
1190
|
+
/** An optional context bag naming the offending prompt id. */
|
|
1191
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
1192
|
+
constructor(code: TerminalErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* The machine-readable condition carried by a {@link import('./errors.js').TerminalError} — the
|
|
1197
|
+
* axis a `catch` branches on. Names its axis (the failure condition), never `kind` (AGENTS §4.4).
|
|
1198
|
+
*
|
|
1199
|
+
* @remarks
|
|
1200
|
+
* - `EXPIRE` — a parked broker prompt was not answered before its `timeout` (or the broker was
|
|
1201
|
+
* `destroy`ed while it was still pending); the prompt's Promise rejects with this.
|
|
1202
|
+
* - `CANCEL` — the user aborted an interactive prompt (ctrl-c) at the server `Terminal` (T-c)
|
|
1203
|
+
* driver; the awaited prompt call rejects with this so a caller can branch on `error.code`.
|
|
1204
|
+
*/
|
|
1205
|
+
export declare type TerminalErrorCode = 'EXPIRE' | 'CANCEL';
|
|
1206
|
+
|
|
1207
|
+
/** Cancel a pending {@link TimerHandler} deadline — idempotent, safe to call after the timer fired. */
|
|
1208
|
+
export declare type TimerCancel = () => void;
|
|
1209
|
+
|
|
1210
|
+
/**
|
|
1211
|
+
* One injected timer — arms a deadline `callback` to fire after `ms`, returning a
|
|
1212
|
+
* {@link TimerCancel} that cancels it. The broker's timeout seam: the default wraps the host
|
|
1213
|
+
* `setTimeout` / `clearTimeout`; a test injects a deterministic timer that captures the callback
|
|
1214
|
+
* and fires it on demand (no real time, no global fake-timer patching).
|
|
1215
|
+
*/
|
|
1216
|
+
export declare type TimerHandler = (callback: () => void, ms: number) => TimerCancel;
|
|
1217
|
+
|
|
1218
|
+
/** Toggle `index` in a readonly index list — copy-on-write, returning the new sorted-by-insertion list. */
|
|
1219
|
+
export declare function toggleIndex(indices: readonly number[], index: number): readonly number[];
|
|
1220
|
+
|
|
1221
|
+
/** Matches an HTTP(S) URL. The `url` rule tests against this. */
|
|
1222
|
+
export declare const URL_PATTERN: RegExp;
|
|
1223
|
+
|
|
1224
|
+
/**
|
|
1225
|
+
* Declarative validation rules for a text prompt — each key toggles (or overrides) one
|
|
1226
|
+
* built-in check. {@link import('./helpers.js').resolveValidation} compiles a `ValidationRules`
|
|
1227
|
+
* (or a bare {@link Validator}) into ONE composed {@link Validator}.
|
|
1228
|
+
*
|
|
1229
|
+
* @remarks
|
|
1230
|
+
* Each rule is EITHER a primitive that turns on the built-in check, OR a {@link Validator}
|
|
1231
|
+
* that replaces it with custom logic:
|
|
1232
|
+
*
|
|
1233
|
+
* - `required` — `true` ⇒ input must be non-empty after trimming.
|
|
1234
|
+
* - `minimum` — a number ⇒ input must be at least that many characters.
|
|
1235
|
+
* - `maximum` — a number ⇒ input must be at most that many characters.
|
|
1236
|
+
* - `pattern` — a string ⇒ input must match that regex source.
|
|
1237
|
+
* - `email` — `true` ⇒ input must look like an email address.
|
|
1238
|
+
* - `url` — `true` ⇒ input must look like an HTTP(S) URL.
|
|
1239
|
+
* - `numeric` — `true` ⇒ input must be a number (integer or decimal).
|
|
1240
|
+
* - `integer` — `true` ⇒ input must be an integer.
|
|
1241
|
+
* - `alphanumeric` — `true` ⇒ input must contain only letters and digits.
|
|
1242
|
+
* - `custom` — an arbitrary {@link Validator} escape hatch.
|
|
1243
|
+
*
|
|
1244
|
+
* Rules compose in the fixed order above; the FIRST failing rule short-circuits and its
|
|
1245
|
+
* message is returned. A `false` / `undefined` rule is skipped.
|
|
1246
|
+
*/
|
|
1247
|
+
export declare interface ValidationRules {
|
|
1248
|
+
readonly required?: boolean | Validator;
|
|
1249
|
+
readonly minimum?: number | Validator;
|
|
1250
|
+
readonly maximum?: number | Validator;
|
|
1251
|
+
readonly pattern?: string | Validator;
|
|
1252
|
+
readonly email?: boolean | Validator;
|
|
1253
|
+
readonly url?: boolean | Validator;
|
|
1254
|
+
readonly numeric?: boolean | Validator;
|
|
1255
|
+
readonly integer?: boolean | Validator;
|
|
1256
|
+
readonly alphanumeric?: boolean | Validator;
|
|
1257
|
+
readonly custom?: Validator;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* A single input validator — given the current input string, returns `true` when the input
|
|
1262
|
+
* is valid, or an error MESSAGE string when it is not. The atomic unit the validation engine
|
|
1263
|
+
* composes and the prompts apply on submit.
|
|
1264
|
+
*
|
|
1265
|
+
* @remarks
|
|
1266
|
+
* A `Validator` is total and pure — it never throws and never mutates. The `true | string`
|
|
1267
|
+
* shape (not `boolean`) is deliberate: an invalid result CARRIES its message, so a prompt
|
|
1268
|
+
* can render the exact reason. {@link import('./helpers.js').composeValidators} runs several
|
|
1269
|
+
* in order and returns the FIRST error (short-circuiting), so the most specific rule wins.
|
|
1270
|
+
*/
|
|
1271
|
+
export declare type Validator = (input: string) => true | string;
|
|
1272
|
+
|
|
1273
|
+
export { }
|