@orkestrel/terminal 0.0.16 → 0.0.18

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/Prompt.ts","../../../src/core/PromptClient.ts","../../../src/core/TerminalManager.ts","../../../src/core/stores/MemoryTerminalStore.ts","../../../src/core/stores/DatabaseTerminalStore.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { PromptRole, PromptTheme } from './types.js'\nimport { CSI, ESC, freezeStyle, STATUS_ICONS } from '@orkestrel/console'\n\n// The constant DATA the pure terminal core reads: key decoding, rendering, and transport defaults.\n// UPPER_SNAKE, `Object.freeze`d, every member exported. Control bytes are built with\n// `String.fromCharCode` so no raw control character appears in source, and the escape lead and the\n// CSI prefix come from `@orkestrel/console`, which publishes both.\n\n// === Control bytes (named, no raw control characters in source)\n\n/** Names the carriage return byte (`\\r`, U+000D) — Enter on most terminals. */\nexport const RETURN = String.fromCharCode(13)\n/** Names the line feed byte (`\\n`, U+000A) — Enter on some terminals / pasted input. */\nexport const NEWLINE = String.fromCharCode(10)\n/** Names the tab byte (`\\t`, U+0009). */\nexport const TAB = String.fromCharCode(9)\n/** Names the backspace byte (BS, U+0008) — Ctrl+H / some terminals' Backspace. */\nexport const BACKSPACE = String.fromCharCode(8)\n/** Names the delete byte (DEL, U+007F) — the usual Backspace byte on a Unix TTY. */\nexport const DELETE = String.fromCharCode(127)\n/** Names the space byte (U+0020). */\nexport const SPACE = ' '\n/** Names the Ctrl+C byte (ETX, U+0003) — interrupt / cancel. */\nexport const CTRL_C = String.fromCharCode(3)\n/** Names the Ctrl+D byte (EOT, U+0004) — end-of-transmission / finish (the editor's commit key). */\nexport const CTRL_D = String.fromCharCode(4)\n/** Names the Ctrl+U byte (NAK, U+0015) — clear the current line. */\nexport const CTRL_U = String.fromCharCode(21)\n/** Names the Ctrl+A byte (SOH, U+0001) — move to start of line. */\nexport const CTRL_A = String.fromCharCode(1)\n/** Names the Ctrl+E byte (ENQ, U+0005) — move to end of line. */\nexport const CTRL_E = String.fromCharCode(5)\n\n/**\n * Names the Single Shift Three lead (`ESCO`) — the alternate arrow-key prefix some terminals emit\n * (`ESC O A`). Built from the console module's own {@link import('@orkestrel/console').ESC}; the\n * navigation keys' CSI lead is that module's {@link import('@orkestrel/console').CSI}, which this\n * package reuses rather than redeclaring.\n */\nexport const KEY_SS3 = `${ESC}O`\n\n/**\n * Holds the exact escape sequence to canonical key name table\n * {@link import('./helpers.js').parseKey} consults for the navigation and editing keys. Covers the\n * CSI form (`ESC[A`…) and the SS3 form (`ESCOA`…) of the arrows, plus the `home` / `end` / `delete`\n * CSI sequences with their numeric-tilde variants. The source of truth for the multi-byte key\n * decode; frozen.\n *\n * @remarks\n * Terminals disagree on these: a cursor key is `ESC[A` (normal) or `ESCOA` (application mode),\n * and Home / End / Delete each have a letter form (`ESC[H` / `ESC[F`) and a numeric form\n * (`ESC[1~` / `ESC[4~` / `ESC[3~`). Every accepted spelling maps to one name so a reducer never\n * sees the wire encoding.\n */\nexport const SEQUENCE_NAMES: Readonly<Record<string, string>> = Object.freeze({\n\t[`${CSI}A`]: 'up',\n\t[`${CSI}B`]: 'down',\n\t[`${CSI}C`]: 'right',\n\t[`${CSI}D`]: 'left',\n\t[`${KEY_SS3}A`]: 'up',\n\t[`${KEY_SS3}B`]: 'down',\n\t[`${KEY_SS3}C`]: 'right',\n\t[`${KEY_SS3}D`]: 'left',\n\t[`${CSI}H`]: 'home',\n\t[`${CSI}F`]: 'end',\n\t[`${CSI}1~`]: 'home',\n\t[`${CSI}4~`]: 'end',\n\t[`${CSI}3~`]: 'delete',\n\t[`${CSI}7~`]: 'home',\n\t[`${CSI}8~`]: 'end',\n})\n\n/**\n * Holds the control byte (or CRLF pair) to key descriptor table\n * {@link import('./helpers.js').parseKey} consults for the one-byte keys and the two-byte CRLF\n * Enter chunk. Each entry carries the canonical `name` and whether it is a `ctrl` combination. The\n * source of truth for that decode; frozen.\n *\n * @remarks\n * `return` / `newline` / `return + newline` all map to `return` (one canonical Enter name) — a\n * terminal or a paste can deliver Enter as `\\r`, `\\n`, or the `\\r\\n` pair in one chunk;\n * `delete` / `backspace` both map to `backspace` (the Backspace bytes); the Ctrl combos\n * (`c` / `d` / `u` / `a` / `e`) carry `ctrl: true` so a reducer can match\n * `key.ctrl && key.name === 'c'`. `escape` / `tab` / `space` are plain named keys.\n */\nexport const CONTROL_NAMES: Readonly<\n\tRecord<string, { readonly name: string; readonly ctrl: boolean }>\n> = Object.freeze({\n\t[RETURN]: Object.freeze({ name: 'return', ctrl: false }),\n\t[NEWLINE]: Object.freeze({ name: 'return', ctrl: false }),\n\t[`${RETURN}${NEWLINE}`]: Object.freeze({ name: 'return', ctrl: false }),\n\t[TAB]: Object.freeze({ name: 'tab', ctrl: false }),\n\t[ESC]: Object.freeze({ name: 'escape', ctrl: false }),\n\t[BACKSPACE]: Object.freeze({ name: 'backspace', ctrl: false }),\n\t[DELETE]: Object.freeze({ name: 'backspace', ctrl: false }),\n\t[SPACE]: Object.freeze({ name: 'space', ctrl: false }),\n\t[CTRL_C]: Object.freeze({ name: 'c', ctrl: true }),\n\t[CTRL_D]: Object.freeze({ name: 'd', ctrl: true }),\n\t[CTRL_U]: Object.freeze({ name: 'u', ctrl: true }),\n\t[CTRL_A]: Object.freeze({ name: 'a', ctrl: true }),\n\t[CTRL_E]: Object.freeze({ name: 'e', ctrl: true }),\n})\n\n// === Prompt defaults\n\n/** Names the default mask glyph {@link import('./helpers.js').createPasswordState} uses — `*`. */\nexport const DEFAULT_MASK = '*'\n\n// === Prompt-view icons\n\n/**\n * Holds the terminal-owned glyphs {@link DEFAULT_PROMPT_THEME} assembles its `icons` from, beside\n * the console module's own success and error marks. Read only when the default theme is assembled;\n * a view reads its resolved theme and never this constant. Frozen.\n *\n * @remarks\n * - `question` — the leading mark on a prompt's message line.\n * - `pointer` — the cursor before the input / the focused choice row.\n * - `dot` / `selected` — an unfocused / focused row marker in a select list.\n * - `checked` / `unchecked` — a checked / unchecked box in a checkbox list.\n */\nexport const PROMPT_ICONS = Object.freeze({\n\tquestion: '?',\n\tpointer: '›',\n\tdot: '○',\n\tselected: '●',\n\tchecked: '☑',\n\tunchecked: '☐',\n})\n\n// === The default prompt theme\n\n/**\n * Holds every {@link import('./types.js').PromptRole}, in one frozen list — the role axis's source of\n * truth. {@link import('./helpers.js').createPromptTheme} walks it to merge a partial theme, and\n * a consumer building a complete role map reads it rather than retyping every name.\n */\nexport const PROMPT_ROLES: readonly PromptRole[] = Object.freeze([\n\t'question',\n\t'pointer',\n\t'message',\n\t'content',\n\t'success',\n\t'error',\n\t'selected',\n\t'focus',\n\t'hint',\n\t'muted',\n\t'description',\n])\n\n/**\n * Holds the {@link import('./types.js').PromptTheme} every prompt renders with unless its options supply\n * another — the glyph set assembled from {@link PROMPT_ICONS} plus the console\n * {@link import('@orkestrel/console').STATUS_ICONS} `success` / `error` marks, and the console\n * {@link import('@orkestrel/console').Style} each role is painted with. Deeply frozen through the\n * console module's own {@link import('@orkestrel/console').freezeStyle}; the baseline\n * {@link import('./helpers.js').createPromptTheme} merges a partial theme over.\n *\n * @remarks\n * The default roles reproduce the views' historical coloring exactly: `question` / `pointer` cyan,\n * `message` / `focus` bold, `success` / `selected` green, `error` red, `hint` / `muted` /\n * `description` dim, and `content` the EMPTY style — an empty style renders bare text, so primary\n * content keeps the bytes it had before it became themeable.\n * Two roles sharing a style today are still two roles — re-mapping one leaves the other alone.\n */\nexport const DEFAULT_PROMPT_THEME: PromptTheme = Object.freeze({\n\ticons: Object.freeze({\n\t\tquestion: PROMPT_ICONS.question,\n\t\tpointer: PROMPT_ICONS.pointer,\n\t\tdot: PROMPT_ICONS.dot,\n\t\tselected: PROMPT_ICONS.selected,\n\t\tchecked: PROMPT_ICONS.checked,\n\t\tunchecked: PROMPT_ICONS.unchecked,\n\t\tsuccess: STATUS_ICONS.success,\n\t\terror: STATUS_ICONS.error,\n\t}),\n\troles: Object.freeze({\n\t\tquestion: freezeStyle({ foreground: 'cyan', attributes: [] }),\n\t\tpointer: freezeStyle({ foreground: 'cyan', attributes: [] }),\n\t\tmessage: freezeStyle({ attributes: ['bold'] }),\n\t\tcontent: freezeStyle({ attributes: [] }),\n\t\tsuccess: freezeStyle({ foreground: 'green', attributes: [] }),\n\t\terror: freezeStyle({ foreground: 'red', attributes: [] }),\n\t\tselected: freezeStyle({ foreground: 'green', attributes: [] }),\n\t\tfocus: freezeStyle({ attributes: ['bold'] }),\n\t\thint: freezeStyle({ attributes: ['dim'] }),\n\t\tmuted: freezeStyle({ attributes: ['dim'] }),\n\t\tdescription: freezeStyle({ attributes: ['dim'] }),\n\t}),\n})\n\n// === Broker + SSE-bridge defaults\n\n/** Holds how long (ms) the {@link import('./types.js').PromptInterface} broker parks an unanswered form before it expires — 5 minutes. */\nexport const DEFAULT_PROMPT_TIMEOUT_MS = 300_000\n\n/** Holds how long (ms) the {@link import('./types.js').PromptClientInterface} waits before each reconnect attempt — 2 seconds. */\nexport const DEFAULT_RECONNECT_DELAY_MS = 2_000\n\n/**\n * Holds the SSE `event:` names the broker emits and the\n * {@link import('./types.js').PromptClientInterface} dispatches on — `pending`, `expire`, and\n * `destroy`. Frozen; the source of truth for the wire event vocabulary.\n *\n * @remarks\n * - `pending` — a serialized {@link import('./types.js').PendingForm} to dispatch and answer.\n * - `expire` — an `{ id }` payload: the broker expired or released a parked form (the client drops it).\n * - `destroy` — the broker is going away; the client disconnects (no auto-reconnect) but stays reusable.\n */\nexport const SSE_EVENTS = Object.freeze({\n\tpending: 'pending',\n\texpire: 'expire',\n\tdestroy: 'destroy',\n})\n\n/**\n * Names the auth-token request header the {@link import('./types.js').PromptClientInterface} sends\n * when a `token` is configured — `x-orkestrel-token`.\n */\nexport const HEADER_TOKEN = 'x-orkestrel-token'\n\n/** Names the `Accept` header value that opens the broker's SSE stream — `text/event-stream`. */\nexport const ACCEPT_EVENT_STREAM = 'text/event-stream'\n\n/**\n * Sets the maximum number of characters the {@link import('./types.js').PromptClientInterface} lets its\n * SSE parser buffer before treating the stream as hostile — 1 MiB, comfortably above any\n * legitimate prompt payload. Passed as the `limit` to `createSSEParser` so an unterminated\n * or oversized `data:` field cannot grow the buffer without bound (a memory-exhaustion guard).\n */\nexport const SSE_BUFFER_LIMIT = 1_048_576\n","import type { TerminalErrorCode } from './types.js'\nimport { isInstance } from '@orkestrel/contract'\n\n// `.claude/rules/typescript.md` § Errors and outcomes: a real error type, not a sentinel. Callers\n// branch on the machine-readable\n// `error.code` rather than parsing the message, and the guard narrows with `instanceof`,\n// mirroring the agents-module errors.\n\n/**\n * Represents the error the terminal surfaces for its own refusals: parking on a destroyed or full broker, an\n * unusable driver stream, a manager routing fault, or a ctrl-c cancellation. A parked form's own\n * lifecycle failures reject through the form's `answer` with the form package's error, never with\n * this one.\n *\n * @remarks\n * Carries a {@link TerminalErrorCode} and an optional `context` bag naming the offending values:\n * `{ cap }` on `LIMIT`, `{ to, known }` on `TARGET`, and `{ from, to, path }` on `DEADLOCK`. Narrow\n * a caught value with {@link isTerminalError} and branch on `error.code`.\n */\nexport class TerminalError extends Error {\n\t/** Holds the machine-readable condition — see {@link TerminalErrorCode}. */\n\treadonly code: TerminalErrorCode\n\t/** Holds an optional context bag naming the offending values — see the class {@link TerminalError remarks}. */\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\t/**\n\t * Builds one terminal refusal.\n\t *\n\t * @param code - The machine-readable {@link TerminalErrorCode} a caller branches on\n\t * @param message - The human-readable reason\n\t * @param context - The optional bag naming the offending values — see the class {@link TerminalError remarks}\n\t */\n\tconstructor(\n\t\tcode: TerminalErrorCode,\n\t\tmessage: string,\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'TerminalError'\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Narrows an unknown caught value to a {@link TerminalError}, so a caller can branch on its `code`.\n *\n * @param value - The value to test (typically a `catch` binding or a rejected prompt call)\n * @returns True if `value` is a {@link TerminalError}; false otherwise\n *\n * @example\n * ```ts\n * try {\n * \tawait terminal.ask(form)\n * } catch (error) {\n * \tif (isTerminalError(error) && error.code === 'CANCEL') {\n * \t\t// the person aborted\n * \t}\n * }\n * ```\n */\nexport function isTerminalError(value: unknown): value is TerminalError {\n\treturn isInstance(value, TerminalError)\n}\n","import type { PendingForm, PendingFormStatus, TerminalSnapshot, WireEvent } from './types.js'\nimport type { Guard } from '@orkestrel/contract'\nimport {\n\tisNonEmptyString,\n\tisNumber,\n\tisRecord,\n\tisString,\n\tliteralOf,\n\trecordOf,\n} from '@orkestrel/contract'\n\n/**\n * Narrows an unknown value to a {@link PendingFormStatus}.\n *\n * @param value - The candidate ticket status\n * @returns True if the value is one of the declared ticket statuses; false otherwise\n */\nexport const isPendingFormStatus: Guard<PendingFormStatus> = literalOf(\n\t'pending',\n\t'answered',\n\t'expired',\n)\n\n/**\n * Narrows an unknown wire value to a {@link PendingForm} envelope — the envelope alone, because the\n * form package's `parseForm` owns the schema payload.\n *\n * @remarks\n * This guard checks the transport record and proves only that `schema` is a record. The Form\n * package's `parseForm` owns the schema payload and its semantic audit.\n *\n * @param value - The decoded wire value to inspect\n * @returns True if the value is a complete pending-form envelope; false otherwise\n */\nexport function isPendingForm(value: unknown): value is PendingForm {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isNonEmptyString,\n\t\t\tschema: isRecord,\n\t\t\tstatus: isPendingFormStatus,\n\t\t\ttime: isNumber,\n\t\t\tfrom: isString,\n\t\t\tto: isString,\n\t\t},\n\t\t['from', 'to'],\n\t)(value)\n}\n\n/**\n * Narrows an unknown value to a transport-neutral {@link WireEvent} — the guard a consumer's own\n * transport applies to an inbound frame.\n *\n * @param value - The candidate wire event\n * @returns True if the value carries an event name, serialized data, and an optional id; false otherwise\n */\nexport const isWireEvent: Guard<WireEvent> = recordOf(\n\t{ event: isString, data: isString, id: isString },\n\t['id'],\n)\n\n/**\n * Narrows an unknown value to a {@link TerminalSnapshot} — a non-empty `id` and an optional numeric\n * `timeout`, the read boundary a store applies to an untrusted persisted row.\n *\n * @param value - The candidate snapshot read back from storage\n * @returns True if the value carries a non-empty `id` and an optional numeric `timeout`; false otherwise\n */\nexport const isTerminalSnapshot: Guard<TerminalSnapshot> = recordOf(\n\t{ id: isNonEmptyString, timeout: isNumber },\n\t['timeout'],\n)\n","import type {\n\tCheckboxState,\n\tConfirmState,\n\tEditorState,\n\tFetchInit,\n\tInputState,\n\tKeyEvent,\n\tPasswordState,\n\tPendingForm,\n\tPromptIcon,\n\tPromptRole,\n\tPromptStep,\n\tPromptTheme,\n\tPromptThemeOptions,\n\tSelectState,\n\tTimerCancelFunction,\n\tWireEvent,\n} from './types.js'\nimport type { Style, StylerInterface } from '@orkestrel/console'\nimport type {\n\tCheckboxField,\n\tConfirmField,\n\tEditorField,\n\tFormField,\n\tFormSchema,\n\tPasswordField,\n\tSelectField,\n\tTextField,\n} from '@orkestrel/form'\nimport {\n\tCONTROL_NAMES,\n\tDEFAULT_MASK,\n\tDEFAULT_PROMPT_THEME,\n\tPROMPT_ROLES,\n\tSEQUENCE_NAMES,\n} from './constants.js'\nimport { isError, isString } from '@orkestrel/contract'\nimport { createStyler, freezeStyle, strip, stripControls } from '@orkestrel/console'\n\n// The PURE prompt core implementation — all EXPORTED, all pure, all unit-tested:\n// the key decoder, schema sanitization, per-field view renderers, and the `create*State`\n// factories with their `reduce*` reducers. No `node:*`, no I/O, no events. Form owns validation and\n// settlement; these reducers only turn keys into candidate field values.\n\n// === Key decoding\n\n/**\n * Decodes one keypress's bytes into a {@link KeyEvent} — total, never throws. A `Uint8Array` is\n * read as UTF-8; the resulting string is matched against the known control bytes and the CRLF pair\n * ({@link CONTROL_NAMES}) and escape sequences ({@link SEQUENCE_NAMES}), falling back to a single\n * printable character. An unrecognized sequence carries no `name`, with the raw `sequence`\n * preserved.\n *\n * @remarks\n * - **Single control byte.** A one-character control input (`return` / `backspace` / `tab` /\n * `escape` / `space`, or a Ctrl combo `c` / `d` / `u` / `a` / `e`), or the two-byte `\\r\\n`\n * CRLF pair, is looked up in {@link CONTROL_NAMES}, carrying its `ctrl` flag.\n * - **Escape sequence.** A multi-byte ESC sequence (`up` / `down` / `left` / `right` in both the\n * `ESC[A` and `ESCOA` forms, plus `home` / `end` / `delete`) is looked up in\n * {@link SEQUENCE_NAMES} and flagged `meta`.\n * - **Printable character.** A single printable character becomes `name` = that character, with\n * `shift` set when it is an uppercase letter. A multi-code-point printable (an emoji, a pasted\n * run) keeps its first code point as the name and the whole input as `sequence`.\n * - **Unknown.** Anything else (an unrecognized escape, an empty input) yields an event with NO\n * `name` — absence, never an empty string — total, so the driver never crashes on a stray byte.\n *\n * @param input - The raw keypress bytes, as a string or `Uint8Array`\n * @returns The decoded {@link KeyEvent}\n *\n * @example\n * ```ts\n * parseKey('\\r') // { name: 'return', sequence: '\\r', ctrl: false, meta: false, shift: false }\n * parseKey('\\r\\n') // { name: 'return', sequence: '\\r\\n', ctrl: false, meta: false, shift: false }\n * parseKey('\\x1b[A') // { name: 'up', sequence: '\\x1b[A', ctrl: false, meta: true, shift: false }\n * parseKey('A') // { name: 'A', sequence: 'A', ctrl: false, meta: false, shift: true }\n * parseKey('\\x03') // { name: 'c', sequence: '\\x03', ctrl: true, meta: false, shift: false }\n * ```\n */\nexport function parseKey(input: string | Uint8Array): KeyEvent {\n\tconst sequence = isString(input) ? input : new TextDecoder().decode(input)\n\n\t// A known multi-byte escape sequence (arrows / home / end / delete) — flagged `meta`.\n\tconst sequenceName = SEQUENCE_NAMES[sequence]\n\tif (sequenceName !== undefined) {\n\t\treturn { name: sequenceName, sequence, ctrl: false, meta: true, shift: false }\n\t}\n\n\t// A known single control byte (return / backspace / tab / escape / space / a ctrl combo),\n\t// or the two-byte CRLF pair.\n\tconst control = CONTROL_NAMES[sequence]\n\tif (control !== undefined) {\n\t\treturn { name: control.name, sequence, ctrl: control.ctrl, meta: false, shift: false }\n\t}\n\n\t// A printable character — one or more code points, the first naming the key.\n\tconst points = [...sequence]\n\tconst first = points[0]\n\tif (first !== undefined && isPrintable(first)) {\n\t\treturn { name: first, sequence, ctrl: false, meta: false, shift: first !== first.toLowerCase() }\n\t}\n\n\t// Anything else (an unrecognized escape, an empty input) — total, never a throw. The name is\n\t// OMITTED rather than emptied, so a caller reads absence instead of a sentinel.\n\treturn { sequence, ctrl: false, meta: false, shift: false }\n}\n\n/**\n * Checks whether a single character is printable — the fallback test {@link parseKey} applies after\n * the control bytes and the escape sequences, so the C0 controls and DEL are excluded.\n *\n * @param character - The single character to test\n * @returns True if the character is at or above space and is not DEL; false otherwise\n */\nexport function isPrintable(character: string): boolean {\n\tif (character.length === 0) return false\n\tconst code = character.codePointAt(0)\n\tif (code === undefined) return false\n\t// Exclude the C0 controls (0–31) and DEL (127); everything at or above space is printable.\n\treturn code >= 32 && code !== 127\n}\n\n// === Prompt theme\n\n/**\n * Builds a complete {@link PromptTheme} by merging a partial one over\n * {@link DEFAULT_PROMPT_THEME}, leaf by leaf — each supplied icon replaces that glyph, each\n * supplied role replaces that {@link Style}, and everything else keeps its default. Each supplied\n * style is snapshotted through the console module's own\n * {@link import('@orkestrel/console').freezeStyle}, so the result is deeply frozen and a caller\n * mutating its own attribute list afterwards cannot reach into a built theme.\n *\n * @param options - The partial theme to merge, or `undefined` for the defaults\n * @returns The resolved, deeply frozen theme every prompt state carries\n *\n * @example\n * ```ts\n * createPromptTheme() // the defaults\n * createPromptTheme({\n * \ticons: { pointer: '=>' },\n * \troles: { message: { foreground: 'magenta', attributes: ['bold'] } },\n * })\n * ```\n */\nexport function createPromptTheme(options?: PromptThemeOptions): PromptTheme {\n\tconst icons: Record<PromptIcon, string> = { ...DEFAULT_PROMPT_THEME.icons, ...options?.icons }\n\tconst roles: Record<PromptRole, Style> = { ...DEFAULT_PROMPT_THEME.roles }\n\tfor (const role of PROMPT_ROLES) {\n\t\tconst style = options?.roles?.[role]\n\t\tif (style !== undefined) roles[role] = freezeStyle(style)\n\t}\n\treturn Object.freeze({ icons: Object.freeze(icons), roles: Object.freeze(roles) })\n}\n\n/**\n * Sanitizes text for one single-line display slot. Composes console's ANSI {@link strip} and C0\n * {@link stripControls} passes with removal of tab, line feed, and carriage return.\n *\n * @param text - The text to sanitize for a glyph or hint slot\n * @returns The text with ANSI sequences, every C0 control character, and DEL removed\n *\n * @example\n * ```ts\n * sanitizeDisplayText('Q\\rOVERWRITE\\nNEXT\\tX') // 'QOVERWRITENEXTX'\n * ```\n */\nexport function sanitizeDisplayText(text: string): string {\n\treturn stripControls(strip(text)).replaceAll('\\t', '').replaceAll('\\n', '').replaceAll('\\r', '')\n}\n\n/**\n * Sanitizes every terminal-readable string in a parsed form schema, keeping every identity and\n * answer string verbatim and dropping field metadata.\n *\n * @remarks\n * Display strings pass through {@link sanitizeDisplayText}: labels, help, placeholders, masks,\n * choice labels and help, file accept entries, and pattern sources. Identity and answer strings\n * stay verbatim: schema, group, and field names, group references, choice values, and defaults.\n * Rewriting those would sever the rendering copy from the authoritative form. Field metadata is\n * removed because terminal neither renders nor interprets it. Pattern sources are sanitized as\n * text only and are never compiled or executed here.\n *\n * @param schema - A schema already accepted by the Form package's `parseForm`\n * @returns A new schema with terminal-readable strings sanitized and field metadata omitted\n *\n * @example\n * ```ts\n * sanitizeSchema({ fields: [{ control: 'text', name: 'na\\u001bme', label: 'N\\u0000ame' }] })\n * // { fields: [{ control: 'text', name: 'na\\u001bme', label: 'Name' }] }\n * ```\n */\nexport function sanitizeSchema(schema: FormSchema): FormSchema {\n\tconst groups = schema.groups?.map((group) => ({\n\t\tname: group.name,\n\t\tlabel: sanitizeDisplayText(group.label),\n\t\t...(group.help !== undefined ? { help: sanitizeDisplayText(group.help) } : {}),\n\t}))\n\tconst fields: FormField[] = []\n\tfor (const source of schema.fields) {\n\t\tconst { meta: _meta, ...field } = source\n\t\tconst rule =\n\t\t\tfield.rule === undefined\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\t...field.rule,\n\t\t\t\t\t\t...(field.rule.pattern !== undefined\n\t\t\t\t\t\t\t? { pattern: sanitizeDisplayText(field.rule.pattern) }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t}\n\t\tconst shared = {\n\t\t\tname: field.name,\n\t\t\t...(field.label !== undefined ? { label: sanitizeDisplayText(field.label) } : {}),\n\t\t\t...(field.help !== undefined ? { help: sanitizeDisplayText(field.help) } : {}),\n\t\t\t...(field.group !== undefined ? { group: field.group } : {}),\n\t\t\t...(rule !== undefined ? { rule } : {}),\n\t\t}\n\n\t\tswitch (field.control) {\n\t\t\tcase 'text':\n\t\t\tcase 'editor':\n\t\t\tcase 'number': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\t...(field.placeholder !== undefined\n\t\t\t\t\t\t? { placeholder: sanitizeDisplayText(field.placeholder) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'password': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\t...(field.mask !== undefined ? { mask: sanitizeDisplayText(field.mask) } : {}),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'date':\n\t\t\tcase 'time':\n\t\t\tcase 'datetime':\n\t\t\tcase 'color':\n\t\t\tcase 'confirm': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'select':\n\t\t\tcase 'checkbox': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\tchoices: field.choices.map((choice) => ({\n\t\t\t\t\t\t...choice,\n\t\t\t\t\t\tvalue: choice.value,\n\t\t\t\t\t\tlabel: sanitizeDisplayText(choice.label),\n\t\t\t\t\t\t...(choice.help !== undefined ? { help: sanitizeDisplayText(choice.help) } : {}),\n\t\t\t\t\t})),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'file': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\t...(field.accept !== undefined ? { accept: field.accept.map(sanitizeDisplayText) } : {}),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\t...(schema.name !== undefined ? { name: schema.name } : {}),\n\t\t...(schema.label !== undefined ? { label: sanitizeDisplayText(schema.label) } : {}),\n\t\t...(schema.help !== undefined ? { help: sanitizeDisplayText(schema.help) } : {}),\n\t\t...(groups !== undefined ? { groups } : {}),\n\t\tfields,\n\t}\n}\n\n/**\n * Sanitizes every glyph a wire-supplied {@link PromptThemeOptions} carries for a single-line display\n * slot. Only the icons need it: a role is guard-narrowed to a console {@link Style}, whose colors\n * and attributes are fixed name sets, so no role can carry a byte a terminal would act on.\n *\n * @param theme - The narrowed theme options a remote prompt supplied\n * @returns The same theme with every supplied glyph sanitized for a single-line display slot\n */\nexport function sanitizeThemeIcons(theme: PromptThemeOptions): PromptThemeOptions {\n\tconst icons = theme.icons\n\tif (icons === undefined) return theme\n\tconst sanitized: Record<string, string> = {}\n\tfor (const [icon, glyph] of Object.entries(icons)) sanitized[icon] = sanitizeDisplayText(glyph)\n\treturn { ...theme, icons: sanitized }\n}\n\n// === Shared view helpers\n\n/**\n * Renders the styled question header (`? message`) — the leading line every active prompt view\n * shares, themed by the `question` + `message` roles.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The prompt's question text\n * @returns The rendered question header\n */\nexport function renderPromptHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n): string {\n\treturn `${styler.render(theme.roles.question, theme.icons.question)} ${styler.render(theme.roles.message, message)}`\n}\n\n/**\n * Renders a question header followed by a key hint painted with the `hint` role, or the header\n * alone when no hint is supplied.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The prompt's question text\n * @param hint - The optional key hint to append\n * @returns The rendered header with the optional hint\n */\nexport function renderHintedHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n\thint?: string,\n): string {\n\tconst head = renderPromptHeader(styler, theme, message)\n\treturn hint === undefined ? head : `${head} ${styler.render(theme.roles.hint, hint)}`\n}\n\n/**\n * Renders the styled submit line (`✔ message`) — the committed header an interactive prompt shows\n * after it resolves, themed by the `success` + `message` roles.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The settled prompt's question text\n * @returns The rendered committed header\n */\nexport function renderSubmitHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n): string {\n\treturn `${styler.render(theme.roles.success, theme.icons.success)} ${styler.render(theme.roles.message, message)}`\n}\n\n/**\n * Renders the styled failure line (`✖ message`) a form driver writes for each refused field before\n * it asks that field again.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The failure text, normally the field's label and the reason\n * @returns The rendered failure line\n */\nexport function renderErrorLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n): string {\n\treturn `${styler.render(theme.roles.error, theme.icons.error)} ${styler.render(theme.roles.error, message)}`\n}\n\n// === Input prompt\n\n/**\n * Builds the initial text-field reducer state — the sanitized label, the declared default, the\n * styler, and the resolved theme.\n *\n * @param field - The text field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createInputState(\n\tfield: TextField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): InputState {\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tdefault: field.default ?? '',\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tvalue: '',\n\t}\n}\n\n/**\n * Renders a text-field reducer state as a styled view — the header, the pointer, and the typed\n * value, or the default shown as a hint while nothing is typed.\n *\n * @param state - The text field's current reducer state\n * @returns The rendered single-line view — header, pointer, and the typed value or the default\n */\nexport function renderInputView(state: InputState): string {\n\tconst content = state.value.length > 0 ? state.value : state.default\n\tconst role = state.value.length > 0 ? state.theme.roles.content : state.theme.roles.hint\n\tconst shown = state.styler.render(role, sanitizeDisplayText(content))\n\treturn `${renderPromptHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)} ${shown}`\n}\n\n/**\n * Advances an input prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. Printable characters extend the value; backspace shrinks it; ctrl-u clears it; ctrl-c\n * cancels; return produces the candidate value, with an empty line falling back to the default.\n *\n * @param state - The text field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the new state, the rendered view, the status, and the value on submit\n */\nexport function reduceInput(state: InputState, key: KeyEvent): PromptStep<string, InputState> {\n\tif (key.ctrl && key.name === 'c') return { state, view: renderInputView(state), status: 'cancel' }\n\n\tif (key.name === 'return') {\n\t\tconst answer = state.value.length > 0 ? state.value : state.default\n\t\tconst next = { ...state, value: answer }\n\t\treturn {\n\t\t\tstate: next,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, sanitizeDisplayText(answer))}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: answer,\n\t\t}\n\t}\n\n\tconst value = editLine(state.value, key)\n\tif (value === undefined) return { state, view: renderInputView(state), status: 'active' }\n\tconst next = { ...state, value }\n\treturn { state: next, view: renderInputView(next), status: 'active' }\n}\n\n// === Password prompt\n\n/**\n * Builds the initial password-field reducer state — the text-field state, plus the mask glyph each\n * typed character renders as.\n *\n * @param field - The password field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createPasswordState(\n\tfield: PasswordField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): PasswordState {\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tmask: sanitizeDisplayText(field.mask ?? DEFAULT_MASK),\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tvalue: '',\n\t}\n}\n\n/**\n * Renders a password-field reducer state as a styled view, with the value replaced by the mask\n * repeated so the secret is never echoed.\n *\n * @param state - The password field's current reducer state\n * @returns The rendered view, with the mask repeated in place of the typed value\n */\nexport function renderPasswordView(state: PasswordState): string {\n\tconst masked = state.styler.render(\n\t\tstate.theme.roles.content,\n\t\tstate.mask.repeat(state.value.length),\n\t)\n\treturn `${renderPromptHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)} ${masked}`\n}\n\n/**\n * Advances a password prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. Identical line-editing to {@link reduceInput} (printable extends, backspace shrinks,\n * ctrl-u clears, ctrl-c cancels) but the view masks the value. Return produces the candidate value.\n *\n * @param state - The password field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the new state, the masked view, the status, and the value on submit\n */\nexport function reducePassword(\n\tstate: PasswordState,\n\tkey: KeyEvent,\n): PromptStep<string, PasswordState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderPasswordView(state), status: 'cancel' }\n\n\tif (key.name === 'return') {\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, state.mask.repeat(state.value.length))}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: state.value,\n\t\t}\n\t}\n\n\tconst value = editLine(state.value, key)\n\tif (value === undefined) return { state, view: renderPasswordView(state), status: 'active' }\n\tconst next = { ...state, value }\n\treturn { state: next, view: renderPasswordView(next), status: 'active' }\n}\n\n// === Confirm prompt\n\n/**\n * Builds the initial confirm-field reducer state — the sanitized label and the declared default\n * answer.\n *\n * @param field - The confirm field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createConfirmState(\n\tfield: ConfirmField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): ConfirmState {\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tdefault: field.default ?? false,\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t}\n}\n\n/**\n * Renders a confirm-field reducer state as a styled view — the header and the yes/no group, with\n * the default letter capitalized and painted by the `selected` role.\n *\n * @param state - The confirm field's current reducer state\n * @returns The rendered view — the header and the yes/no group with the default capitalized\n */\nexport function renderConfirmView(state: ConfirmState): string {\n\tconst head = renderPromptHeader(state.styler, state.theme, state.message)\n\tconst answer = state.default\n\t\t? `${state.styler.render(state.theme.roles.selected, 'Y')}${state.styler.render(state.theme.roles.hint, '/n')}`\n\t\t: `${state.styler.render(state.theme.roles.hint, 'y/')}${state.styler.render(state.theme.roles.selected, 'N')}`\n\treturn `${head} ${state.styler.render(state.theme.roles.hint, '(')}${answer}${state.styler.render(state.theme.roles.hint, ')')}`\n}\n\n/**\n * Advances a confirm prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<boolean>`\n * reducer. `y` / `Y` submits `true`, `n` / `N` submits `false`, return on an empty line submits\n * the `default`, ctrl-c cancels; any other key is ignored (stays active).\n *\n * @param state - The confirm field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the answer on submit\n */\nexport function reduceConfirm(\n\tstate: ConfirmState,\n\tkey: KeyEvent,\n): PromptStep<boolean, ConfirmState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderConfirmView(state), status: 'cancel' }\n\n\tlet answer: boolean | undefined\n\tconst choice = key.name?.toLowerCase()\n\tif (key.name === 'return') answer = state.default\n\telse if (choice === 'y') answer = true\n\telse if (choice === 'n') answer = false\n\n\tif (answer === undefined) return { state, view: renderConfirmView(state), status: 'active' }\n\treturn {\n\t\tstate,\n\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, answer ? 'yes' : 'no')}`,\n\t\tstatus: 'submit',\n\t\tvalue: answer,\n\t}\n}\n\n// === Select prompt\n\n/**\n * Builds the initial select-field reducer state — the offered choices, with the focus pre-placed on\n * the declared default.\n *\n * @param field - The select field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createSelectState(\n\tfield: SelectField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): SelectState {\n\tconst choices = [...field.choices]\n\tconst index = choices.findIndex((choice) => choice.value === field.default)\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tchoices,\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tfocused: index >= 0 ? index : 0,\n\t}\n}\n\n/**\n * Renders a select-field reducer state as a multi-line styled view — one row per choice, with the\n * focused row marked and its help shown.\n *\n * @param state - The select field's current reducer state\n * @returns The rendered view — the header, then one row per choice with the focused row marked\n */\nexport function renderSelectView(state: SelectState): string {\n\tconst lines = state.choices.map((choice, index) => {\n\t\tconst active = index === state.focused\n\t\tconst pointer = active\n\t\t\t? state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)\n\t\t\t: ' '\n\t\tconst marker = active\n\t\t\t? state.styler.render(state.theme.roles.selected, state.theme.icons.selected)\n\t\t\t: state.styler.render(state.theme.roles.muted, state.theme.icons.dot)\n\t\tconst label = active\n\t\t\t? state.styler.render(state.theme.roles.focus, choice.label)\n\t\t\t: state.styler.render(state.theme.roles.content, choice.label)\n\t\tconst description =\n\t\t\tchoice.help === undefined\n\t\t\t\t? ''\n\t\t\t\t: ` ${state.styler.render(state.theme.roles.description, choice.help)}`\n\t\treturn `${pointer} ${marker} ${label}${description}`\n\t})\n\treturn [renderPromptHeader(state.styler, state.theme, state.message), ...lines].join('\\n')\n}\n\n/**\n * Advances a select prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. `up` / `down` (and `k` / `j`) move the focus, wrapping at the ends; return submits the\n * focused choice's `value`; ctrl-c cancels. An empty choice list can never submit (a higher layer\n * guards against it); any other key is ignored.\n *\n * @param state - The select field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the chosen value on submit\n */\nexport function reduceSelect(state: SelectState, key: KeyEvent): PromptStep<string, SelectState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderSelectView(state), status: 'cancel' }\n\n\tconst count = state.choices.length\n\tif (count === 0) return { state, view: renderSelectView(state), status: 'active' }\n\n\tif (key.name === 'up' || key.name === 'k') {\n\t\tconst next = { ...state, focused: (state.focused - 1 + count) % count }\n\t\treturn { state: next, view: renderSelectView(next), status: 'active' }\n\t}\n\tif (key.name === 'down' || key.name === 'j') {\n\t\tconst next = { ...state, focused: (state.focused + 1) % count }\n\t\treturn { state: next, view: renderSelectView(next), status: 'active' }\n\t}\n\tif (key.name === 'return') {\n\t\tconst choice = state.choices[state.focused]\n\t\tconst value = choice?.value ?? ''\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, choice?.label ?? '')}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue,\n\t\t}\n\t}\n\treturn { state, view: renderSelectView(state), status: 'active' }\n}\n\n// === Checkbox prompt\n\n/**\n * Builds the initial checkbox-field reducer state — the offered choices, with every value in the\n * field's `default` list pre-checked.\n *\n * @param field - The checkbox field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createCheckboxState(\n\tfield: CheckboxField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): CheckboxState {\n\tconst choices = [...field.choices]\n\tconst checked: readonly number[] = choices.reduce<number[]>((indices, choice, index) => {\n\t\tif (field.default?.includes(choice.value) === true) indices.push(index)\n\t\treturn indices\n\t}, [])\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tchoices,\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tfocused: 0,\n\t\tchecked,\n\t}\n}\n\n/**\n * Renders a checkbox-field reducer state as a multi-line styled view — one box per choice, and the\n * selected count beneath them.\n *\n * @param state - The checkbox field's current reducer state\n * @returns The rendered view — the header, one box per choice, and the selected count\n */\nexport function renderCheckboxView(state: CheckboxState): string {\n\tconst lines = state.choices.map((choice, index) => {\n\t\tconst active = index === state.focused\n\t\tconst ticked = state.checked.includes(index)\n\t\tconst pointer = active\n\t\t\t? state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)\n\t\t\t: ' '\n\t\tconst box = ticked\n\t\t\t? state.styler.render(state.theme.roles.selected, state.theme.icons.checked)\n\t\t\t: state.styler.render(state.theme.roles.muted, state.theme.icons.unchecked)\n\t\tconst label = active\n\t\t\t? state.styler.render(state.theme.roles.focus, choice.label)\n\t\t\t: state.styler.render(state.theme.roles.content, choice.label)\n\t\tconst description =\n\t\t\tchoice.help === undefined\n\t\t\t\t? ''\n\t\t\t\t: ` ${state.styler.render(state.theme.roles.description, choice.help)}`\n\t\treturn `${pointer} ${box} ${label}${description}`\n\t})\n\tconst summary = state.styler.render(state.theme.roles.hint, `${state.checked.length} selected`)\n\tconst body = [\n\t\trenderPromptHeader(state.styler, state.theme, state.message),\n\t\t...lines,\n\t\tsummary,\n\t].join('\\n')\n\treturn body\n}\n\n/**\n * Advances a checkbox prompt by one {@link KeyEvent} — the pure\n * `(state, key) → PromptStep<readonly string[]>` reducer. `up` / `down` (and `k` / `j`) move the\n * focus (wrapping); `space` toggles the focused index in the checked set; return submits the\n * checked values in choice order; ctrl-c cancels. The form applies selection-count rules.\n *\n * @param state - The checkbox field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the ticked values on submit\n */\nexport function reduceCheckbox(\n\tstate: CheckboxState,\n\tkey: KeyEvent,\n): PromptStep<readonly string[], CheckboxState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderCheckboxView(state), status: 'cancel' }\n\n\tconst count = state.choices.length\n\n\tif ((key.name === 'up' || key.name === 'k') && count > 0) {\n\t\tconst next = {\n\t\t\t...state,\n\t\t\tfocused: (state.focused - 1 + count) % count,\n\t\t}\n\t\treturn { state: next, view: renderCheckboxView(next), status: 'active' }\n\t}\n\tif ((key.name === 'down' || key.name === 'j') && count > 0) {\n\t\tconst next = { ...state, focused: (state.focused + 1) % count }\n\t\treturn { state: next, view: renderCheckboxView(next), status: 'active' }\n\t}\n\tif (key.name === 'space' && count > 0) {\n\t\tconst checked = toggleIndex(state.checked, state.focused)\n\t\tconst next = { ...state, checked }\n\t\treturn { state: next, view: renderCheckboxView(next), status: 'active' }\n\t}\n\tif (key.name === 'return') {\n\t\tconst ordered = [...state.checked].sort((a, b) => a - b)\n\t\tconst values = ordered\n\t\t\t.map((index) => state.choices[index]?.value)\n\t\t\t.filter((value): value is string => value !== undefined)\n\t\tconst summary = ordered\n\t\t\t.map((index) => state.choices[index]?.label)\n\t\t\t.filter((name): name is string => name !== undefined)\n\t\t\t.join(', ')\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, summary)}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: values,\n\t\t}\n\t}\n\treturn { state, view: renderCheckboxView(state), status: 'active' }\n}\n\n/**\n * Toggles `index` in a readonly index list — copy-on-write, returning the new sorted-by-insertion\n * list; the primitive {@link reduceCheckbox} calls.\n *\n * @param indices - The ticked indices, in tick order\n * @param index - The index to add when absent, or drop when present\n * @returns A new list carrying the toggled membership; the input is never mutated\n */\nexport function toggleIndex(indices: readonly number[], index: number): readonly number[] {\n\treturn indices.includes(index) ? indices.filter((i) => i !== index) : [...indices, index]\n}\n\n// === Editor prompt\n\n/**\n * Builds the initial editor-field reducer state — the committed lines empty, and the declared\n * default held for a finish with nothing typed.\n *\n * @param field - The editor field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createEditorState(\n\tfield: EditorField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): EditorState {\n\tconst lines: readonly string[] = []\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tdefault: field.default ?? '',\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tlines,\n\t\tcurrent: '',\n\t}\n}\n\n/**\n * Renders an editor-field reducer state as a multi-line styled view — the finish hint, the\n * committed lines, and the line in progress.\n *\n * @param state - The editor field's current reducer state\n * @returns The rendered view — the hinted header, the committed lines, and the line in progress\n */\nexport function renderEditorView(state: EditorState): string {\n\tconst head = renderHintedHeader(state.styler, state.theme, state.message, '(Ctrl+D to finish)')\n\tconst pointer = state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)\n\tconst committed = state.lines.map((line) => state.styler.render(state.theme.roles.content, line))\n\tconst body = [\n\t\t...committed,\n\t\t`${pointer} ${state.styler.render(state.theme.roles.content, state.current)}`,\n\t]\n\treturn [head, ...body].join('\\n')\n}\n\n/**\n * Advances an editor prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. Printable characters extend the current line; backspace shrinks it; return commits the\n * current line and starts a fresh one; ctrl-d finishes, joining every line and falling back to the\n * default when empty; ctrl-c cancels. The form validates the candidate after the driver fills it.\n *\n * @param state - The editor field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the joined text on submit\n */\nexport function reduceEditor(state: EditorState, key: KeyEvent): PromptStep<string, EditorState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderEditorView(state), status: 'cancel' }\n\n\tif (key.ctrl && key.name === 'd') {\n\t\tconst lines = state.current.length > 0 ? [...state.lines, state.current] : state.lines\n\t\tconst joined = lines.join('\\n')\n\t\tconst answer = joined.length > 0 ? joined : state.default\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, `${String(lines.length)} line${lines.length === 1 ? '' : 's'}`)}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: answer,\n\t\t}\n\t}\n\n\tif (key.name === 'return') {\n\t\tconst next = {\n\t\t\t...state,\n\t\t\tlines: [...state.lines, state.current],\n\t\t\tcurrent: '',\n\t\t}\n\t\treturn { state: next, view: renderEditorView(next), status: 'active' }\n\t}\n\n\tconst current = editLine(state.current, key)\n\tif (current === undefined) return { state, view: renderEditorView(state), status: 'active' }\n\tconst next = { ...state, current }\n\treturn { state: next, view: renderEditorView(next), status: 'active' }\n}\n\n// === Shared reducer helpers\n\n/**\n * Applies a single line-editing {@link KeyEvent} to a text buffer — the editing shared by input,\n * password, and editor. A printable key appends its character; `backspace` drops the last\n * character; `space` appends a space; ctrl-u clears the line; a key that edits nothing returns\n * `undefined`.\n *\n * @param value - The buffer the field holds so far\n * @param key - The decoded keypress to apply\n * @returns The new buffer, or `undefined` when the key does not edit the line, so the caller can\n * leave the state untouched\n */\nexport function editLine(value: string, key: KeyEvent): string | undefined {\n\tif (key.ctrl && key.name === 'u') return ''\n\tif (key.name === 'backspace') return value.slice(0, -1)\n\tif (key.name === 'space') return `${value} `\n\t// A printable key that is not a control / navigation key — `name` is the literal character, and an\n\t// undecoded key carries none at all. Count CODE POINTS (not UTF-16 units) so an astral printable\n\t// (an emoji, a surrogate pair — `name.length` 2 but ONE code point) appends instead of being\n\t// dropped, while a multi-char control name (`up`, `return`) is still rejected.\n\tif (\n\t\t!key.ctrl &&\n\t\t!key.meta &&\n\t\tkey.name !== undefined &&\n\t\t[...key.name].length === 1 &&\n\t\tisPrintable(key.name)\n\t) {\n\t\treturn `${value}${key.sequence}`\n\t}\n\treturn undefined\n}\n\n// === Broker + bridge wiring helpers\n\n/**\n * Implements the default {@link import('./types.js').TimerHandler} — a thin host `setTimeout` / `clearTimeout`\n * wrapper that arms `callback` after `ms` and returns a {@link TimerCancelFunction}. The deadline seam\n * behind both the {@link import('./Prompt.js').Prompt} broker (its expiry) and the\n * {@link import('./PromptClient.js').PromptClient} (its reconnect backoff); a test injects a\n * deterministic timer instead, so neither entity touches real time.\n *\n * @param callback - The deadline callback to arm\n * @param ms - How long to wait before firing it, in milliseconds\n * @returns The {@link TimerCancelFunction} that clears the armed deadline\n */\nexport function defaultTimer(callback: () => void, ms: number): TimerCancelFunction {\n\tconst handle = setTimeout(callback, ms)\n\treturn () => clearTimeout(handle)\n}\n\n/**\n * Implements the default {@link import('./types.js').FetchHandler} — the global `fetch`, adapted to\n * the minimal injected shape the {@link import('./PromptClient.js').PromptClient} uses.\n *\n * @param input - The request URL\n * @param init - The request init the client sets — method, headers, body, and abort signal\n * @returns The host `fetch` promise for that request\n */\nexport function globalFetch(input: string, init?: FetchInit): Promise<Response> {\n\treturn fetch(input, init)\n}\n\n/**\n * Checks whether a caught value is an `AbortError` — the {@link import('./PromptClient.js').PromptClient}\n * distinguishes a deliberate `disconnect` / teardown (an aborted `fetch`) from a real fault, so it\n * exits its connect loop quietly instead of emitting `error` / reconnecting.\n *\n * @param error - The caught value to test\n * @returns True if the value is a host `Error` or `DOMException` named `AbortError`; false otherwise\n */\nexport function isAbortError(error: unknown): boolean {\n\treturn isError(error) && error.name === 'AbortError'\n}\n\n/**\n * Checks whether `url` is an insecure remote endpoint — a plain `http://` URL whose host is not a\n * loopback address. Pure string parsing (no `URL` global), so it stays total on malformed input;\n * the {@link import('./PromptClient.js').PromptClient} warns once when a `token` would cross such\n * an endpoint in cleartext.\n *\n * @remarks\n * A loopback host (`localhost`, `127.0.0.1`, `[::1]`) over `http://` is exempt (local\n * development has no network hop to eavesdrop on); every other `http://` host is insecure.\n * An `https://` URL (or any non-`http://` scheme) is never flagged.\n *\n * @param url - The candidate endpoint URL\n * @returns True if `url` is a non-loopback `http://` endpoint; false otherwise\n *\n * @example\n * ```ts\n * isInsecureRemote('http://example.com') // true\n * isInsecureRemote('http://localhost:3000') // false\n * isInsecureRemote('https://example.com') // false\n * ```\n */\nexport function isInsecureRemote(url: string): boolean {\n\tconst prefix = 'http://'\n\tif (!url.startsWith(prefix)) return false\n\tconst rest = url.slice(prefix.length)\n\tconst hostEnd = rest.search(/[/?#]/)\n\tconst authority = hostEnd === -1 ? rest : rest.slice(0, hostEnd)\n\tconst host = authority.includes('@') ? authority.slice(authority.indexOf('@') + 1) : authority\n\tconst hostname = host.startsWith('[')\n\t\t? host.slice(0, host.indexOf(']') + 1)\n\t\t: (host.split(':')[0] ?? '')\n\treturn hostname !== 'localhost' && hostname !== '127.0.0.1' && hostname !== '[::1]'\n}\n\n// === Terminal manager wire seams (transport-neutral, no http dependency)\n\n/**\n * Serializes a parked {@link PendingForm} into a `pending` {@link WireEvent}, whose frame `id` is\n * the form's own id.\n *\n * @param form - The parked form's wire-safe record\n * @returns The `pending` frame — the JSON-stringified record as `data`, and the form's own `id`\n */\nexport function serializePending(form: PendingForm): WireEvent {\n\treturn { event: 'pending', data: JSON.stringify(form), id: form.id }\n}\n\n/**\n * Serializes a parked form's expiry or release into an `expire` {@link WireEvent}, whose `data` is\n * the JSON `{ id }` payload.\n *\n * @param id - The id of the parked form that expired or was released\n * @returns The `expire` frame, carrying the JSON-stringified `{ id }` payload as `data`\n */\nexport function serializeExpire(id: string): WireEvent {\n\treturn { event: 'expire', data: JSON.stringify({ id }) }\n}\n\n/**\n * Serializes the `destroy` {@link WireEvent} a broker or manager sends when it is going away, which\n * carries no payload.\n *\n * @returns The `destroy` frame, whose `data` is empty because the signal carries no payload\n */\nexport function serializeDestroy(): WireEvent {\n\treturn { event: 'destroy', data: '' }\n}\n","import type {\n\tAnswerError,\n\tParkedForm,\n\tParkRequest,\n\tPendingForm,\n\tPromptEventMap,\n\tPromptInterface,\n\tPromptOptions,\n\tTimerHandler,\n} from './types.js'\nimport type { Result } from '@orkestrel/contract'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { FieldError, FormInterface, FormResult, FormValues } from '@orkestrel/form'\nimport { DEFAULT_PROMPT_TIMEOUT_MS } from './constants.js'\nimport { TerminalError } from './errors.js'\nimport { defaultTimer } from './helpers.js'\nimport { attempt, isArray, isString } from '@orkestrel/contract'\nimport { Emitter } from '@orkestrel/emitter'\nimport { isFieldError, isFormError, serializeForm } from '@orkestrel/form'\n\n/**\n * Implements the headless form broker. It parks live forms, exposes their serialized schemas,\n * applies remote answers to the authoritative form, and abandons a parked form on timeout, release,\n * or teardown.\n *\n * @remarks\n * A parked record carries one call to `serializeForm`. A failed fill or submit leaves the record\n * parked for another answer. A successful submit settles the form once, emits `answer`, and removes\n * the record. Timeout, `stop`, and teardown abandon unsettled forms through their own `destroy`\n * method.\n *\n * @example\n * ```ts\n * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })\n * const prompt = createPrompt()\n * const id = prompt.park(form)\n * prompt.answer(id, { name: 'Ada' })\n * await form.answer // { name: 'Ada' }\n * ```\n */\nexport class Prompt implements PromptInterface {\n\treadonly #timeout: number\n\treadonly #timer: TimerHandler\n\treadonly #cap: number | undefined\n\treadonly #parked = new Map<string, ParkedForm>()\n\treadonly #emitter: Emitter<PromptEventMap>\n\t#destroyed = false\n\n\tconstructor(options?: PromptOptions) {\n\t\tthis.#timeout = options?.timeout ?? DEFAULT_PROMPT_TIMEOUT_MS\n\t\tthis.#timer = options?.timer ?? defaultTimer\n\t\tthis.#cap = options?.cap\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options?.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options?.error !== undefined ? { error: options.error } : {}),\n\t\t})\n\t}\n\n\tget emitter(): EmitterInterface<PromptEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget count(): number {\n\t\treturn this.#parked.size\n\t}\n\n\tpending(): readonly PendingForm[]\n\tpending(id: string): PendingForm | undefined\n\tpending(id?: string): readonly PendingForm[] | PendingForm | undefined {\n\t\tif (id !== undefined) return this.#parked.get(id)?.pending\n\t\tconst forms: PendingForm[] = []\n\t\tfor (const parked of this.#parked.values()) forms.push(parked.pending)\n\t\treturn forms\n\t}\n\n\tpark(form: FormInterface, request?: ParkRequest): string {\n\t\tif (this.#destroyed) {\n\t\t\tform.destroy()\n\t\t\tthrow new TerminalError('EXPIRE', 'The broker has been destroyed')\n\t\t}\n\t\tif (this.#cap !== undefined && this.#parked.size >= this.#cap) {\n\t\t\tform.destroy()\n\t\t\tthrow new TerminalError('LIMIT', `The parked-form cap (${String(this.#cap)}) was reached`, {\n\t\t\t\tcap: this.#cap,\n\t\t\t})\n\t\t}\n\n\t\tconst id = crypto.randomUUID()\n\t\tconst pending: PendingForm = {\n\t\t\tid,\n\t\t\tschema: serializeForm(form.schema),\n\t\t\tstatus: 'pending',\n\t\t\ttime: Date.now(),\n\t\t\t...(request?.from !== undefined ? { from: request.from } : {}),\n\t\t\t...(request?.to !== undefined ? { to: request.to } : {}),\n\t\t}\n\t\tconst cancel = this.#timer(() => this.#expire(id), this.#timeout)\n\t\tthis.#parked.set(id, { form, pending, cancel })\n\t\tthis.#emitter.emit('pending', pending)\n\t\treturn id\n\t}\n\n\tanswer(id: string, values: FormValues): Result<FormValues, AnswerError> {\n\t\tconst outcome = attempt(() => this.#answer(id, values))\n\t\tif (outcome.success) return outcome.value\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\treason: 'rejected',\n\t\t\t\terrors: [{ field: 'form', message: 'The form rejected the answer' }],\n\t\t\t},\n\t\t}\n\t}\n\n\tstop(ids: readonly string[]): boolean\n\tstop(id: string): boolean\n\tstop(): void\n\tstop(ids?: string | readonly string[]): boolean | void {\n\t\tif (ids === undefined) {\n\t\t\tfor (const id of [...this.#parked.keys()]) this.#expire(id)\n\t\t\treturn\n\t\t}\n\t\tif (isArray(ids)) {\n\t\t\tlet stopped = true\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst parked = this.#parked.get(id)\n\t\t\t\tif (parked === undefined || parked.pending.status !== 'pending') stopped = false\n\t\t\t}\n\t\t\tfor (const id of ids) this.#expire(id)\n\t\t\treturn stopped\n\t\t}\n\t\treturn this.#expire(ids)\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tfor (const id of [...this.#parked.keys()]) this.#expire(id)\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#answer(id: string, values: FormValues): Result<FormValues, AnswerError> {\n\t\tconst parked = this.#parked.get(id)\n\t\tif (parked === undefined || parked.pending.status !== 'pending') {\n\t\t\treturn { success: false, error: { reason: 'unknown' } }\n\t\t}\n\n\t\tconst outcome = attempt(() => this.#submit(parked.form, values))\n\t\tif (!outcome.success) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { reason: 'rejected', errors: this.#errors(outcome.error, parked.form) },\n\t\t\t}\n\t\t}\n\t\tif (!outcome.value.success) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { reason: 'rejected', errors: outcome.value.error },\n\t\t\t}\n\t\t}\n\n\t\tparked.cancel()\n\t\tconst answered: ParkedForm = {\n\t\t\t...parked,\n\t\t\tpending: { ...parked.pending, status: 'answered' },\n\t\t}\n\t\tthis.#parked.set(id, answered)\n\t\tthis.#emitter.emit('answer', id, outcome.value.value)\n\t\tthis.#parked.delete(id)\n\t\treturn outcome.value\n\t}\n\n\t#submit(form: FormInterface, values: FormValues): FormResult {\n\t\tfor (const [field, value] of Object.entries(values)) {\n\t\t\tconst outcome = attempt(() => form.fill(field, value))\n\t\t\tif (!outcome.success) {\n\t\t\t\treturn { success: false, error: this.#errors(outcome.error, form, field) }\n\t\t\t}\n\t\t}\n\t\treturn form.submit()\n\t}\n\n\t#errors(error: unknown, form: FormInterface, field?: string): readonly FieldError[] {\n\t\tif (isFormError(error)) {\n\t\t\tif (isFieldError(error.context)) return [error.context]\n\t\t\tconst named = error.context?.field\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tfield: isString(named) ? named : this.#field(form, field),\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t{\n\t\t\t\tfield: this.#field(form, field),\n\t\t\t\tmessage: 'The form rejected the answer',\n\t\t\t},\n\t\t]\n\t}\n\n\t#field(form: FormInterface, field?: string): string {\n\t\treturn field ?? form.schema.fields[0]?.name ?? form.schema.name ?? 'form'\n\t}\n\n\t#expire(id: string): boolean {\n\t\tconst parked = this.#parked.get(id)\n\t\tif (parked === undefined || parked.pending.status !== 'pending') return false\n\t\tparked.cancel()\n\t\tconst expired: ParkedForm = {\n\t\t\t...parked,\n\t\t\tpending: { ...parked.pending, status: 'expired' },\n\t\t}\n\t\tthis.#parked.set(id, expired)\n\t\tparked.form.destroy()\n\t\tthis.#emitter.emit('expire', id)\n\t\tthis.#parked.delete(id)\n\t\treturn true\n\t}\n}\n","import type {\n\tFetchHandler,\n\tPromptClientEventMap,\n\tPromptClientInterface,\n\tPromptClientOptions,\n\tTimerCancelFunction,\n\tTimerHandler,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { FieldError, FormInterface, FormSchema, FormValues } from '@orkestrel/form'\nimport type { SSEEvent } from '@orkestrel/sse'\nimport {\n\tACCEPT_EVENT_STREAM,\n\tDEFAULT_RECONNECT_DELAY_MS,\n\tHEADER_TOKEN,\n\tSSE_BUFFER_LIMIT,\n\tSSE_EVENTS,\n} from './constants.js'\nimport {\n\tdefaultTimer,\n\tglobalFetch,\n\tisAbortError,\n\tisInsecureRemote,\n\tsanitizeDisplayText,\n\tsanitizeSchema,\n} from './helpers.js'\nimport { isPendingForm } from './validators.js'\nimport { arrayOf, isBoolean, isRecord, isString, parseJSON } from '@orkestrel/contract'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createForm, isFieldError, parseForm } from '@orkestrel/form'\nimport { createSSEParser } from '@orkestrel/sse'\n\n/**\n * Implements the SSE form bridge. It ingests serialized forms from a remote broker without waiting\n * on a render, drives one form at a time through a local terminal, posts each answer back, and asks\n * again when the authoritative form refuses one.\n *\n * @remarks\n * - **Connect + reconnect.** {@link connect} opens the SSE stream and reconnects after a transport\n * drop with the injected backoff unless reconnect is disabled, the client was destroyed, or\n * {@link disconnect} deliberately stopped it.\n * - **Ingest + render.** Each `pending` envelope passes through `isPendingForm`, Form's `parseForm`,\n * and terminal's `sanitizeSchema`, then enters a serial render queue. The SSE reader never awaits\n * that queue, so `expire` and `destroy` remain live while a person is answering.\n * - **Safe local form.** The rendering copy omits every wire `pattern`, because Form compiles a\n * pattern during local evaluation. The broker's parked form retains it and remains authoritative.\n * - **Refusal retry.** A structured `rejected` response seeds a new rendering form with the values\n * the refused attempt submitted, applies every {@link FieldError} through `invalidate`, and asks\n * again. No retry counter truncates the loop; acceptance, expiry, and the broker's own teardown\n * are its bounds.\n * - **Replay safety.** A replayed id is skipped while it is queued, rendering, or posting. Once an\n * attempt ends, a later delivery of that id may be rendered again.\n *\n * @example\n * ```ts\n * const client = createPromptClient({\n * \turl: 'http://localhost:3001/prompts',\n * \tterminal: createTerminal(),\n * })\n * await client.connect()\n * ```\n */\nexport class PromptClient implements PromptClientInterface {\n\treadonly url: string\n\treadonly #terminal: PromptClientOptions['terminal']\n\treadonly #token: string | undefined\n\treadonly #reconnect: boolean\n\treadonly #delay: number\n\treadonly #fetch: FetchHandler\n\treadonly #timer: TimerHandler\n\treadonly #emitter: Emitter<PromptClientEventMap>\n\t#controller: AbortController | undefined\n\t#backoff: TimerCancelFunction | undefined\n\t#wake: (() => void) | undefined\n\t#connecting = false\n\t#connected = false\n\t#destroyed = false\n\t#draining = false\n\t#warnedInsecureToken = false\n\treadonly #seen = new Set<string>()\n\treadonly #queue = new Map<string, FormSchema>()\n\t#active:\n\t\t| { readonly id: string; readonly form: FormInterface; readonly stopped: boolean }\n\t\t| undefined\n\n\tconstructor(options: PromptClientOptions) {\n\t\tthis.url = options.url\n\t\tthis.#terminal = options.terminal\n\t\tthis.#token = options.token\n\t\tthis.#reconnect = options.reconnect ?? true\n\t\tthis.#delay = options.delay ?? DEFAULT_RECONNECT_DELAY_MS\n\t\tthis.#fetch = options.fetch ?? globalFetch\n\t\tthis.#timer = options.timer ?? defaultTimer\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options.error !== undefined ? { error: options.error } : {}),\n\t\t})\n\t}\n\n\tget emitter(): EmitterInterface<PromptClientEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget connected(): boolean {\n\t\treturn this.#connected\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.#destroyed || this.#connecting) return\n\t\tthis.#connecting = true\n\t\twhile (this.#connecting && !this.#destroyed) {\n\t\t\ttry {\n\t\t\t\tawait this.#stream()\n\t\t\t} catch (error) {\n\t\t\t\tthis.#markDisconnected()\n\t\t\t\tif (this.#destroyed || isAbortError(error)) return\n\t\t\t\tthis.#emitter.emit('error', error)\n\t\t\t}\n\t\t\tif (!this.#reconnect || !this.#connecting || this.#destroyed) return\n\t\t\tawait this.#wait(this.#delay)\n\t\t}\n\t}\n\n\tdisconnect(): void {\n\t\tthis.#connecting = false\n\t\tthis.#controller?.abort()\n\t\tthis.#controller = undefined\n\t\tthis.#backoff?.()\n\t\tthis.#backoff = undefined\n\t\tconst wake = this.#wake\n\t\tthis.#wake = undefined\n\t\twake?.()\n\t\tthis.#markDisconnected()\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tthis.disconnect()\n\t\tthis.#interrupt()\n\t\tthis.#emitter.destroy()\n\t}\n\n\tasync #stream(): Promise<void> {\n\t\tif (this.#token !== undefined && isInsecureRemote(this.url) && !this.#warnedInsecureToken) {\n\t\t\tthis.#warnedInsecureToken = true\n\t\t\tthis.#emitter.emit(\n\t\t\t\t'error',\n\t\t\t\tnew Error('auth token sent as cleartext over insecure http; use https'),\n\t\t\t)\n\t\t}\n\t\tconst controller = new AbortController()\n\t\tthis.#controller = controller\n\t\tconst response = await this.#fetch(this.url, {\n\t\t\theaders: this.#headers({ Accept: ACCEPT_EVENT_STREAM }),\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\tif (!response.ok) throw new Error(`broker returned ${String(response.status)}`)\n\t\tconst body = response.body\n\t\tif (body === null) throw new Error('broker sent no stream')\n\n\t\tthis.#connected = true\n\t\tthis.#emitter.emit('connect')\n\n\t\tconst reader = body.getReader()\n\t\tconst decoder = new TextDecoder()\n\t\tconst parser = createSSEParser({ limit: SSE_BUFFER_LIMIT })\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\t\t\t\tfor (const event of parser.parse(decoder.decode(value, { stream: true }))) {\n\t\t\t\t\tthis.#handle(event)\n\t\t\t\t\tif (!this.#connecting) break\n\t\t\t\t}\n\t\t\t\tif (!this.#connecting) break\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t\tthis.#markDisconnected()\n\t}\n\n\t#handle(event: SSEEvent): void {\n\t\tif (event.event === SSE_EVENTS.pending) {\n\t\t\tconst parsed = parseJSON(event.data)\n\t\t\tif (!isPendingForm(parsed) || this.#seen.has(parsed.id)) return\n\t\t\tconst schema = parseForm(parsed.schema)\n\t\t\tif (schema === undefined) {\n\t\t\t\tthis.#emitter.emit(\n\t\t\t\t\t'error',\n\t\t\t\t\tnew Error(`broker sent an invalid form schema for ${parsed.id}`),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#seen.add(parsed.id)\n\t\t\tthis.#queue.set(parsed.id, sanitizeSchema(schema))\n\t\t\tvoid this.#drain()\n\t\t\treturn\n\t\t}\n\t\tif (event.event === SSE_EVENTS.expire) {\n\t\t\tconst parsed = parseJSON(event.data)\n\t\t\tif (isRecord(parsed) && isString(parsed.id)) this.#expire(parsed.id)\n\t\t\treturn\n\t\t}\n\t\tif (event.event === SSE_EVENTS.destroy) {\n\t\t\tthis.disconnect()\n\t\t\tthis.#interrupt()\n\t\t}\n\t}\n\n\tasync #drain(): Promise<void> {\n\t\tif (this.#draining) return\n\t\tthis.#draining = true\n\t\ttry {\n\t\t\twhile (!this.#destroyed) {\n\t\t\t\tlet queued: readonly [string, FormSchema] | undefined\n\t\t\t\tfor (const entry of this.#queue) {\n\t\t\t\t\tqueued = entry\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif (queued === undefined) return\n\t\t\t\tconst [id, schema] = queued\n\t\t\t\tthis.#queue.delete(id)\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#render(id, schema)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst active = this.#active\n\t\t\t\t\tif (active?.id !== id || !active.stopped) this.#emitter.emit('error', error)\n\t\t\t\t} finally {\n\t\t\t\t\tconst active = this.#active\n\t\t\t\t\tif (active?.id === id) {\n\t\t\t\t\t\tactive.form.destroy()\n\t\t\t\t\t\tthis.#active = undefined\n\t\t\t\t\t}\n\t\t\t\t\tif (!this.#queue.has(id)) this.#seen.delete(id)\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#draining = false\n\t\t}\n\t}\n\n\tasync #render(id: string, schema: FormSchema): Promise<void> {\n\t\tlet values: FormValues | undefined\n\t\tlet errors: readonly FieldError[] = []\n\t\twhile (this.#seen.has(id) && !this.#destroyed) {\n\t\t\tconst form = this.#createRenderingForm(schema, values)\n\t\t\tthis.#active = { id, form, stopped: false }\n\t\t\tfor (const error of errors) form.invalidate(error.field, sanitizeDisplayText(error.message))\n\t\t\tconst submitted = await this.#terminal.ask(form)\n\t\t\tconst active = this.#active\n\t\t\tif (active?.id !== id || active.stopped) return\n\t\t\tconst rejected = await this.#post(id, submitted)\n\t\t\tconst posted = this.#active\n\t\t\tif (posted?.id !== id || posted.stopped || rejected === undefined) return\n\t\t\tvalues = submitted\n\t\t\terrors = rejected\n\t\t\tform.destroy()\n\t\t\tthis.#active = undefined\n\t\t}\n\t}\n\n\t#createRenderingForm(schema: FormSchema, values?: FormValues): FormInterface {\n\t\tconst fields = schema.fields.map((field) => {\n\t\t\tif (field.rule?.pattern === undefined) return field\n\t\t\tconst { pattern: _pattern, ...rule } = field.rule\n\t\t\treturn { ...field, rule }\n\t\t})\n\t\tconst form = createForm({ ...schema, fields }, values === undefined ? undefined : { values })\n\t\tvoid form.answer.catch(() => undefined)\n\t\treturn form\n\t}\n\n\tasync #post(id: string, values: FormValues): Promise<readonly FieldError[] | undefined> {\n\t\tconst response = await this.#fetch(this.url, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: this.#headers({ 'Content-Type': 'application/json' }),\n\t\t\tbody: JSON.stringify({ id, values }),\n\t\t})\n\t\tif (!response.ok) this.#emitter.emit('error', new Error(`broker rejected answer ${id}`))\n\t\tconst parsed = parseJSON(await response.text())\n\t\tif (!isRecord(parsed) || !isBoolean(parsed.success)) {\n\t\t\tthis.#emitter.emit('error', new Error(`broker returned an invalid answer result for ${id}`))\n\t\t\treturn undefined\n\t\t}\n\t\tif (parsed.success) return undefined\n\t\tconst error = parsed.error\n\t\tif (!isRecord(error) || !isString(error.reason)) {\n\t\t\tthis.#emitter.emit('error', new Error(`broker returned an invalid answer error for ${id}`))\n\t\t\treturn undefined\n\t\t}\n\t\tif (error.reason === 'unknown') return undefined\n\t\tif (error.reason === 'rejected' && arrayOf(isFieldError)(error.errors)) return error.errors\n\t\tthis.#emitter.emit('error', new Error(`broker returned an invalid answer refusal for ${id}`))\n\t\treturn undefined\n\t}\n\n\t#expire(id: string): void {\n\t\tconst active = this.#active\n\t\tif (active?.id === id) {\n\t\t\tthis.#active = { ...active, stopped: true }\n\t\t\tthis.#seen.delete(id)\n\t\t\tactive.form.destroy()\n\t\t} else if (this.#queue.delete(id)) this.#seen.delete(id)\n\t\tthis.#emitter.emit('expire', id)\n\t}\n\n\t#interrupt(): void {\n\t\tthis.#queue.clear()\n\t\tthis.#seen.clear()\n\t\tconst active = this.#active\n\t\tif (active === undefined) return\n\t\tthis.#active = { ...active, stopped: true }\n\t\tactive.form.destroy()\n\t}\n\n\t#markDisconnected(): void {\n\t\tif (!this.#connected) return\n\t\tthis.#connected = false\n\t\tthis.#emitter.emit('disconnect')\n\t}\n\n\t#headers(base: Record<string, string>): Record<string, string> {\n\t\tif (this.#token !== undefined) return { ...base, [HEADER_TOKEN]: this.#token }\n\t\treturn { ...base }\n\t}\n\n\t#wait(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst settle = this.#createSettler(resolve)\n\t\t\tthis.#wake = settle\n\t\t\tthis.#backoff = this.#timer(settle, ms)\n\t\t})\n\t}\n\n\t#createSettler(resolve: () => void): () => void {\n\t\treturn () => {\n\t\t\tthis.#backoff = undefined\n\t\t\tthis.#wake = undefined\n\t\t\tresolve()\n\t\t}\n\t}\n}\n","import type {\n\tPendingForm,\n\tPromptInterface,\n\tPromptOptions,\n\tTerminalAnswerError,\n\tTerminalManagerEventMap,\n\tTerminalManagerInterface,\n\tTerminalManagerOptions,\n\tTerminalSnapshot,\n\tTerminalStoreInterface,\n\tTimerHandler,\n} from './types.js'\nimport type { Result } from '@orkestrel/contract'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { FormInterface, FormValues } from '@orkestrel/form'\nimport { TerminalError } from './errors.js'\nimport { createPrompt } from './factories.js'\nimport { Emitter } from '@orkestrel/emitter'\nimport { isArray } from '@orkestrel/contract'\n\n/**\n * Registers named {@link PromptInterface} brokers, one per endpoint, so several parties can `ask`\n * forms of each other by name with a `from` → `to` attribution edge on every parked form, and\n * refuses `DEADLOCK` on a transitive cycle across every in-flight ask.\n *\n * @remarks\n * - **Registry.** `add(name, options?)` mints (or, if `name` is already mounted, returns the\n * existing broker unchanged — idempotent, never clobbers a live/parked endpoint). Every mounted\n * broker's `pending` / `answer` / `expire` events are re-emitted on the manager, attributed by\n * `name`.\n * - **`ask`.** The target must already be mounted through {@link add} — `ask` never auto-adds it;\n * rejects `TARGET` for an unknown `to` (listing the known names). Rejects `DEADLOCK` when parking\n * `from → to` would close a cycle over the current in-flight edge set (walked transitively);\n * otherwise parks the caller's live form through the target's broker and returns that form's own\n * `answer` promise. Edge cleanup never alters the value or rejection the caller observes.\n * - **Durable open / save.** `open(name)` restores an EMPTY broker from the `store` (parked\n * Promises are process-bound and never resurrected); `save(name)` persists the endpoint's\n * configured `timeout`.\n * - **Removal.** `remove` drops one endpoint, a batch (the array overload declared first), or every\n * endpoint when called without an argument. It destroys each broker, which expires every form\n * still parked on it. `destroy` is idempotent.\n *\n * @example\n * ```ts\n * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })\n * const manager = new TerminalManager()\n * manager.add('agent')\n * const answer = manager.ask('user', 'agent', form)\n * manager.answer('agent', manager.pending('agent')[0].id, { name: 'Ada' })\n * await answer // { name: 'Ada' }\n * ```\n */\nexport class TerminalManager implements TerminalManagerInterface {\n\treadonly #terminals = new Map<string, PromptInterface>()\n\treadonly #config = new Map<string, PromptOptions>()\n\t// The handlers subscribed on a mounted broker's emitter — kept so `remove` can `off` them\n\t// explicitly (on top of the broker's own `destroy`, which already renders its emitter inert).\n\treadonly #listeners = new Map<\n\t\tstring,\n\t\t{\n\t\t\treadonly pending: (form: PendingForm) => void\n\t\t\treadonly answer: (id: string, values: FormValues) => void\n\t\t\treadonly expire: (id: string) => void\n\t\t}\n\t>()\n\t// In-flight `ask` edges, keyed by the parked form's id — the deadlock graph. `from` asked\n\t// `to`; cleanup on settle (answer / expire / destroy / remove) removes EXACTLY the edge that\n\t// call created.\n\treadonly #edges = new Map<string, { readonly from: string; readonly to: string }>()\n\treadonly #store: TerminalStoreInterface | undefined\n\treadonly #timeout: number | undefined\n\treadonly #timer: TimerHandler | undefined\n\treadonly #cap: number | undefined\n\treadonly #emitter: Emitter<TerminalManagerEventMap>\n\t#destroyed = false\n\n\tconstructor(options?: TerminalManagerOptions) {\n\t\tthis.#store = options?.store\n\t\tthis.#timeout = options?.timeout\n\t\tthis.#timer = options?.timer\n\t\tthis.#cap = options?.cap\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options?.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options?.error !== undefined ? { error: options.error } : {}),\n\t\t})\n\t}\n\n\tget emitter(): EmitterInterface<TerminalManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget count(): number {\n\t\treturn this.#terminals.size\n\t}\n\n\t// === Accessors\n\n\tterminal(name: string): PromptInterface | undefined {\n\t\treturn this.#terminals.get(name)\n\t}\n\n\tterminals(): readonly PromptInterface[] {\n\t\treturn [...this.#terminals.values()]\n\t}\n\n\t// === Registry\n\n\tadd(name: string, options?: PromptOptions): PromptInterface {\n\t\tif (this.#destroyed) throw new TerminalError('DESTROYED', 'manager destroyed')\n\t\tconst existing = this.#terminals.get(name)\n\t\tif (existing !== undefined) return existing\n\t\tconst timeout = options?.timeout ?? this.#timeout\n\t\tconst timer = options?.timer ?? this.#timer\n\t\tconst cap = options?.cap ?? this.#cap\n\t\tconst promptOptions: PromptOptions = {\n\t\t\t...(options?.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options?.error !== undefined ? { error: options.error } : {}),\n\t\t\t...(timeout !== undefined ? { timeout } : {}),\n\t\t\t...(timer !== undefined ? { timer } : {}),\n\t\t\t...(cap !== undefined ? { cap } : {}),\n\t\t}\n\t\tconst broker = createPrompt(promptOptions)\n\t\tconst listeners = {\n\t\t\tpending: this.#createPendingListener(),\n\t\t\tanswer: this.#createAnswerListener(name),\n\t\t\texpire: this.#createExpireListener(name),\n\t\t}\n\t\tbroker.emitter.on('pending', listeners.pending)\n\t\tbroker.emitter.on('answer', listeners.answer)\n\t\tbroker.emitter.on('expire', listeners.expire)\n\t\tthis.#terminals.set(name, broker)\n\t\tthis.#config.set(name, { ...options })\n\t\tthis.#listeners.set(name, listeners)\n\t\treturn broker\n\t}\n\n\t// === Ask\n\n\task(from: string, to: string, form: FormInterface): Promise<FormValues> {\n\t\tconst broker = this.#terminals.get(to)\n\t\tif (broker === undefined) {\n\t\t\tconst known = [...this.#terminals.keys()]\n\t\t\treturn Promise.reject(\n\t\t\t\tnew TerminalError(\n\t\t\t\t\t'TARGET',\n\t\t\t\t\t`unknown terminal '${to}' (known: ${known.length > 0 ? known.join(', ') : 'none'})`,\n\t\t\t\t\t{ to, known },\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tconst cycle = this.#findCycle(from, to)\n\t\tif (cycle !== undefined) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew TerminalError(\n\t\t\t\t\t'DEADLOCK',\n\t\t\t\t\t`ask ${from} -> ${to} would deadlock: ${cycle.join(' -> ')}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tfrom,\n\t\t\t\t\t\tto,\n\t\t\t\t\t\tpath: cycle,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tconst id = broker.park(form, { from, to })\n\t\tif (broker.pending(id) !== undefined) {\n\t\t\tthis.#edges.set(id, { from, to })\n\t\t\tform.answer.then(this.#createEdgeClear(id), this.#createEdgeClear(id))\n\t\t}\n\t\treturn form.answer\n\t}\n\n\t// === Pending accessors\n\n\tpending(): readonly PendingForm[]\n\tpending(to: string): readonly PendingForm[]\n\tpending(to?: string): readonly PendingForm[] {\n\t\tif (to !== undefined) {\n\t\t\tconst broker = this.#terminals.get(to)\n\t\t\treturn broker === undefined ? [] : broker.pending()\n\t\t}\n\t\tconst result: PendingForm[] = []\n\t\tfor (const broker of this.#terminals.values()) result.push(...broker.pending())\n\t\treturn result\n\t}\n\n\t// === Answer\n\n\tanswer(to: string, id: string, values: FormValues): Result<FormValues, TerminalAnswerError> {\n\t\tconst broker = this.#terminals.get(to)\n\t\tif (broker === undefined) return { success: false, error: { reason: 'target' } }\n\t\tconst result = broker.answer(id, values)\n\t\tif (result.success) this.#edges.delete(id)\n\t\treturn result\n\t}\n\n\t// === Durable open / save\n\n\tasync open(name: string): Promise<PromptInterface | undefined> {\n\t\tif (this.#destroyed) throw new TerminalError('DESTROYED', 'manager destroyed')\n\t\tconst existing = this.#terminals.get(name)\n\t\tif (existing !== undefined) return existing\n\t\tif (this.#store === undefined) return undefined\n\t\tconst snapshot = await this.#store.get(name)\n\t\tif (this.#destroyed) throw new TerminalError('DESTROYED', 'manager destroyed')\n\t\tif (snapshot === undefined) return undefined\n\t\treturn this.add(name, snapshot.timeout !== undefined ? { timeout: snapshot.timeout } : {})\n\t}\n\n\tasync save(name: string): Promise<boolean> {\n\t\tconst broker = this.#terminals.get(name)\n\t\tif (this.#store === undefined || broker === undefined) return false\n\t\tconst config = this.#config.get(name)\n\t\tconst snapshot: TerminalSnapshot = {\n\t\t\tid: name,\n\t\t\t...(config?.timeout !== undefined ? { timeout: config.timeout } : {}),\n\t\t}\n\t\tawait this.#store.set(snapshot)\n\t\treturn true\n\t}\n\n\t// === Removal (the array overload declared FIRST)\n\n\tremove(names: readonly string[]): boolean\n\tremove(name: string): boolean\n\tremove(): void\n\tremove(names?: string | readonly string[]): boolean | void {\n\t\tif (names === undefined) {\n\t\t\tfor (const name of [...this.#terminals.keys()]) this.#removeOne(name)\n\t\t\treturn\n\t\t}\n\t\tif (isArray(names)) {\n\t\t\tlet removed = true\n\t\t\tfor (const name of names) {\n\t\t\t\tif (!this.#removeOne(name)) removed = false\n\t\t\t}\n\t\t\treturn removed\n\t\t}\n\t\treturn this.#removeOne(names)\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tthis.remove()\n\t\tthis.#edges.clear()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t// === Private helpers\n\n\t#createPendingListener(): (form: PendingForm) => void {\n\t\treturn (form) => this.#emitter.emit('pending', form)\n\t}\n\n\t#createAnswerListener(name: string): (id: string, values: FormValues) => void {\n\t\treturn (id, values) => {\n\t\t\tthis.#edges.delete(id)\n\t\t\tthis.#emitter.emit('answer', name, id, values)\n\t\t}\n\t}\n\n\t#createExpireListener(name: string): (id: string) => void {\n\t\treturn (id) => {\n\t\t\tthis.#edges.delete(id)\n\t\t\tthis.#emitter.emit('expire', name, id)\n\t\t}\n\t}\n\n\t#createEdgeClear(id: string): () => void {\n\t\treturn () => {\n\t\t\tthis.#edges.delete(id)\n\t\t}\n\t}\n\n\t// Drop one endpoint: destroy its broker FIRST (its expire loop re-emits `expire` for every\n\t// still-parked form through the manager's listeners — still attached at this point, so\n\t// each settles on the manager emitter too), THEN unsubscribe the manager's listeners and\n\t// remove it from every registry map. `false` when `name` was not mounted.\n\t#removeOne(name: string): boolean {\n\t\tconst broker = this.#terminals.get(name)\n\t\tif (broker === undefined) return false\n\t\tbroker.destroy()\n\t\tconst listeners = this.#listeners.get(name)\n\t\tif (listeners !== undefined) {\n\t\t\tbroker.emitter.off('pending', listeners.pending)\n\t\t\tbroker.emitter.off('answer', listeners.answer)\n\t\t\tbroker.emitter.off('expire', listeners.expire)\n\t\t}\n\t\tthis.#terminals.delete(name)\n\t\tthis.#config.delete(name)\n\t\tthis.#listeners.delete(name)\n\t\tthis.#clearEdges(name)\n\t\treturn true\n\t}\n\n\t#clearEdges(to: string): void {\n\t\tfor (const [id, edge] of this.#edges) {\n\t\t\tif (edge.to === to) this.#edges.delete(id)\n\t\t}\n\t}\n\n\t// Walk the in-flight edge graph forward from `to`, looking for `from` — a hit means parking\n\t// `from -> to` would close a cycle. Returns the closing cycle path (`from` first and last),\n\t// or `undefined` when no cycle would form.\n\t#findCycle(from: string, to: string): readonly string[] | undefined {\n\t\tif (from === to) return [from, to]\n\t\tconst visited = new Set<string>([to])\n\t\tconst queue: Array<readonly string[]> = [[to]]\n\t\twhile (queue.length > 0) {\n\t\t\tconst path = queue.shift()\n\t\t\tif (path === undefined) break\n\t\t\tconst last = path[path.length - 1]\n\t\t\tif (last === undefined) continue\n\t\t\tfor (const edge of this.#edges.values()) {\n\t\t\t\tif (edge.from !== last) continue\n\t\t\t\tif (edge.to === from) return [from, ...path, from]\n\t\t\t\tif (visited.has(edge.to)) continue\n\t\t\t\tvisited.add(edge.to)\n\t\t\t\tqueue.push([...path, edge.to])\n\t\t\t}\n\t\t}\n\t\treturn undefined\n\t}\n}\n","import type { TerminalSnapshot, TerminalStoreInterface } from '../types.js'\n\n/**\n * Implements the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of\n * {@link TerminalSnapshot} records keyed by endpoint id, the default store\n * {@link import('../factories.js').createMemoryTerminalStore} builds and the exact twin of\n * {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore}. It carries no idle expiry and\n * no eviction.\n *\n * @remarks\n * A plain `Map<string, TerminalSnapshot>` — the snapshot is already pure, self-contained CONFIG-only\n * JSON, so the memory tier needs no encoding. There is NO idle-TTL and NO\n * eviction: a persisted config lives until an explicit `delete`. A durable backend (JSON / SQLite /\n * IndexedDB) swaps in through the SAME interface without touching the manager — its\n * driver-pluggable twin is {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore} (the\n * snapshot as one opaque JSON column).\n *\n * - **`get` resolves the persisted snapshot for an id**, or `undefined` if none is stored.\n * - **`set` inserts / replaces under the snapshot's OWN `id`** (no separate id param).\n * - **`delete` drops a snapshot by id**; an absent id is a no-op (no throw).\n *\n * The public surface is EXACTLY `get` / `set` / `delete` — no extra members, so the class and\n * {@link TerminalStoreInterface} carry the same methods. Hydration is a caller concern: `open` always\n * restores an EMPTY broker — parked Promises are process-bound and never resurrected.\n *\n * @example\n * ```ts\n * import { createMemoryTerminalStore } from '@orkestrel/terminal'\n *\n * const store = createMemoryTerminalStore()\n * await store.set({ id: 'shell', timeout: 5000 }) // persist a config\n * const snapshot = await store.get('shell')\n * await store.delete('shell') // drop it\n * ```\n */\nexport class MemoryTerminalStore implements TerminalStoreInterface {\n\treadonly #snapshots = new Map<string, TerminalSnapshot>()\n\n\t/**\n\t * Resolves the persisted snapshot for `id`.\n\t *\n\t * @param id - The endpoint name the snapshot is keyed by\n\t * @returns The stored snapshot, or `undefined` when none is held\n\t */\n\tget(id: string): Promise<TerminalSnapshot | undefined> {\n\t\treturn Promise.resolve(this.#snapshots.get(id))\n\t}\n\n\t/**\n\t * Inserts or replaces under the snapshot's OWN `id` (no separate id param).\n\t *\n\t * @param snapshot - The config snapshot to persist, carrying its own `id`\n\t * @returns A promise that settles once the snapshot is held\n\t */\n\tset(snapshot: TerminalSnapshot): Promise<void> {\n\t\tthis.#snapshots.set(snapshot.id, snapshot)\n\t\treturn Promise.resolve()\n\t}\n\n\t/**\n\t * Drops a snapshot by id; an absent id is a no-op (no throw).\n\t *\n\t * @param id - The endpoint name to drop\n\t * @returns A promise that settles once the snapshot is gone\n\t */\n\tdelete(id: string): Promise<void> {\n\t\tthis.#snapshots.delete(id)\n\t\treturn Promise.resolve()\n\t}\n}\n","import type { TerminalSnapshot, TerminalSnapshotRow, TerminalStoreInterface } from '../types.js'\nimport type { TableInterface } from '@orkestrel/database'\nimport { isTerminalSnapshot } from '../validators.js'\n\n/**\n * Implements a {@link TerminalStoreInterface} backed by one table of the `databases` layer — an\n * endpoint's durable config state is a row, so persistence reduces to keyed point-access (`get` /\n * `set` / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`\n * {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}. A stored `snapshot` is narrowed\n * with {@link import('../validators.js').isTerminalSnapshot} on read.\n *\n * @remarks\n * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,\n * JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /\n * IndexedDB backend swaps in WITHOUT touching the manager — the same seam as\n * {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}. The driver defaults to memory\n * ({@link import('../factories.js').createDatabaseTerminalStore} passes `createMemoryDriver()`), so\n * it ALSO works in memory out of the box; you opt into the durable plumbing by passing a JSON /\n * SQLite / IndexedDB driver.\n *\n * The {@link TerminalSnapshot} is stored as ONE OPAQUE JSON COLUMN — the table is a row of\n * `{ id; snapshot }` ({@link TerminalSnapshotRow}). The snapshot is already a COMPLETE,\n * self-contained, pure-JSON CONFIG payload (no live broker state), so storing it whole is lossless\n * AND keeps the row type flat (`snapshot` reads back as `unknown`).\n *\n * - **`set(snapshot)` upserts under the snapshot's OWN `id`** (no separate id param) — it writes\n * the row `{ id: snapshot.id, snapshot }`.\n * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to\n * a {@link TerminalSnapshot} ({@link import('../validators.js').isTerminalSnapshot} — the total\n * guard that narrows an untrusted storage read), or `undefined` if none is stored.\n * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).\n *\n * There is NO idle-TTL / eviction — a persisted config lives until an explicit `delete`. The public\n * surface is EXACTLY `get` / `set` / `delete` — no extra members, so the class and\n * {@link TerminalStoreInterface} carry the same methods. Hydration stays a caller concern: `open` always restores an EMPTY\n * broker — parked Promises are process-bound and never resurrected.\n *\n * @example\n * ```ts\n * import { createDatabaseTerminalStore } from '@orkestrel/terminal'\n * import { createMemoryDriver } from '@orkestrel/database'\n *\n * const store = createDatabaseTerminalStore(createMemoryDriver()) // a durable driver swaps in here\n * await store.set({ id: 'shell', timeout: 5000 }) // persist the config (one JSON column)\n * const snapshot = await store.get('shell')\n * await store.delete('shell') // drop it\n * ```\n */\nexport class DatabaseTerminalStore implements TerminalStoreInterface {\n\treadonly #table: TableInterface<TerminalSnapshotRow>\n\n\t/**\n\t * Wraps a table as a terminal store.\n\t *\n\t * @param table - The {@link TableInterface} holding the snapshots — its row is the\n\t * {@link TerminalSnapshotRow} `{ id; snapshot }` shape (the snapshot one opaque JSON column)\n\t */\n\tconstructor(table: TableInterface<TerminalSnapshotRow>) {\n\t\tthis.#table = table\n\t}\n\n\t/**\n\t * Resolves the persisted snapshot for `id`, narrowing the opaque JSON column back to a\n\t * `TerminalSnapshot`.\n\t *\n\t * @param id - The endpoint name the row is keyed by\n\t * @returns The stored snapshot, or `undefined` when none is held or the column is off-shape\n\t */\n\tasync get(id: string): Promise<TerminalSnapshot | undefined> {\n\t\tconst row = await this.#table.get(id)\n\t\tif (row === undefined) return undefined\n\t\t// The snapshot crosses back as an untrusted storage read (a structured clone / a JSON row),\n\t\t// so narrow the opaque JSON column with the boundary guard rather than a cast;\n\t\t// a malformed blob resolves `undefined`, never a broken config.\n\t\treturn isTerminalSnapshot(row.snapshot) ? row.snapshot : undefined\n\t}\n\n\t/**\n\t * Inserts or replaces under the snapshot's OWN `id` (no separate id param) — the row is\n\t * `{ id, snapshot }`.\n\t *\n\t * @param snapshot - The config snapshot to persist, carrying its own `id`\n\t * @returns A promise that settles once the row is written\n\t */\n\tasync set(snapshot: TerminalSnapshot): Promise<void> {\n\t\tawait this.#table.set({ id: snapshot.id, snapshot })\n\t}\n\n\t/**\n\t * Drops a snapshot by id; an absent id is a no-op (no throw).\n\t *\n\t * @param id - The endpoint name to drop\n\t * @returns A promise that settles once the row is gone\n\t */\n\tasync delete(id: string): Promise<void> {\n\t\tawait this.#table.remove(id)\n\t}\n}\n","import type {\n\tPromptClientInterface,\n\tPromptClientOptions,\n\tPromptInterface,\n\tPromptOptions,\n\tTerminalManagerInterface,\n\tTerminalManagerOptions,\n\tTerminalSnapshotRow,\n\tTerminalStoreInterface,\n} from './types.js'\nimport type { DriverInterface, TableInterface } from '@orkestrel/database'\nimport { Prompt } from './Prompt.js'\nimport { PromptClient } from './PromptClient.js'\nimport { TerminalManager } from './TerminalManager.js'\nimport { MemoryTerminalStore } from './stores/MemoryTerminalStore.js'\nimport { DatabaseTerminalStore } from './stores/DatabaseTerminalStore.js'\nimport { createDatabase, createMemoryDriver } from '@orkestrel/database'\nimport { rawShape, stringShape } from '@orkestrel/contract'\n\n/**\n * Creates the headless {@link PromptInterface} broker. It parks live forms and applies remote\n * answers to the authoritative instances.\n *\n * @param options - See {@link PromptOptions}\n * @returns A {@link PromptInterface}\n *\n * @remarks\n * The caller awaits the parked form's own `answer`. Timeout, `stop`, or teardown destroys the form,\n * so that promise rejects with the Form package's `ABANDONED` error. Inject `options.timer` to\n * drive expiry without real time.\n *\n * @example\n * ```ts\n * import { createPrompt } from '@orkestrel/terminal'\n * import { createForm } from '@orkestrel/form'\n *\n * const prompt = createPrompt()\n * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })\n * const id = prompt.park(form)\n * prompt.answer(id, { name: 'Ada' })\n * ```\n */\nexport function createPrompt(options?: PromptOptions): PromptInterface {\n\treturn new Prompt(options)\n}\n\n/**\n * Creates the SSE prompt {@link PromptClientInterface} bridge — it connects to a remote broker's\n * SSE endpoint, dispatches each received form to a local\n * {@link import('./types.js').TerminalInterface}, and POSTs the answer back. Universal — `fetch`\n * and SSE are web standards.\n *\n * @param options - See {@link PromptClientOptions} (`url` + `terminal` required)\n * @returns A {@link PromptClientInterface}\n *\n * @remarks\n * - **Connect + reconnect.** `await client.connect()` streams remote prompts until the stream\n * ends; it reconnects with the `delay` backoff unless `reconnect` is `false` / the client was\n * `destroy`ed. Inject `options.fetch` (a scripted `fetch`) and `options.timer` to drive it\n * deterministically in tests — no real network.\n * - **Wire narrowing.** Every decoded prompt is guard-narrowed before dispatch (never an `as`).\n *\n * @example\n * ```ts\n * import { createPromptClient } from '@orkestrel/terminal'\n *\n * const client = createPromptClient({ url: 'http://host/prompts', terminal })\n * await client.connect()\n * ```\n */\nexport function createPromptClient(options: PromptClientOptions): PromptClientInterface {\n\treturn new PromptClient(options)\n}\n\n/**\n * Creates the multi-endpoint {@link TerminalManager} — a named registry of {@link PromptInterface}\n * brokers so several parties can `ask` forms of each other by name, with a transitive cycle check\n * that refuses `DEADLOCK` across every in-flight ask.\n *\n * @param options - See {@link TerminalManagerOptions}\n * @returns A {@link TerminalManager}\n *\n * @example\n * ```ts\n * import { createTerminalManager } from '@orkestrel/terminal'\n *\n * const manager = createTerminalManager()\n * manager.add('agent')\n * ```\n */\nexport function createTerminalManager(options?: TerminalManagerOptions): TerminalManagerInterface {\n\treturn new TerminalManager(options)\n}\n\n/**\n * Creates the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of endpoint\n * config snapshots, the default store backing a {@link TerminalManagerInterface}'s `open` / `save`.\n *\n * @returns A {@link TerminalStoreInterface}\n *\n * @example\n * ```ts\n * import { createMemoryTerminalStore } from '@orkestrel/terminal'\n *\n * const store = createMemoryTerminalStore()\n * ```\n */\nexport function createMemoryTerminalStore(): TerminalStoreInterface {\n\treturn new MemoryTerminalStore()\n}\n\n/**\n * Creates a {@link TerminalStoreInterface} backed by one table of the `databases` layer — the\n * driver-pluggable twin of {@link createMemoryTerminalStore}, storing each endpoint's config\n * snapshot as one opaque JSON column. The default driver is an in-memory `@orkestrel/database`\n * driver.\n *\n * @param driver - The {@link DriverInterface} backing the table (default an in-memory driver)\n * @returns A {@link TerminalStoreInterface}\n *\n * @example\n * ```ts\n * import { createDatabaseTerminalStore } from '@orkestrel/terminal'\n *\n * const store = createDatabaseTerminalStore() // in-memory by default\n * ```\n */\nexport function createDatabaseTerminalStore(\n\tdriver: DriverInterface = createMemoryDriver(),\n): TerminalStoreInterface {\n\t// The snapshot is stored as ONE OPAQUE JSON column (`rawShape`), so the row infers FLAT —\n\t// `{ id: string; snapshot: unknown }` = TerminalSnapshotRow.\n\tconst columns = { id: stringShape(), snapshot: rawShape({}) }\n\tconst database = createDatabase({ driver, tables: { terminals: columns } })\n\tconst table: TableInterface<TerminalSnapshotRow> = database.table('terminals')\n\treturn new DatabaseTerminalStore(table)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["ESC","CSI","STATUS_ICONS","freezeStyle","isInstance","literalOf","recordOf","isNonEmptyString","isRecord","isNumber","isString","isString","freezeStyle","stripControls","strip","createStyler","isError","Emitter","serializeForm","attempt","isArray","isFormError","isFieldError","isString","Emitter","createSSEParser","parseJSON","parseForm","isRecord","isString","createForm","isBoolean","arrayOf","isFieldError","Emitter","isArray","createMemoryDriver","stringShape","rawShape","createDatabase"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/validators.ts","../../../src/core/helpers.ts","../../../src/core/Prompt.ts","../../../src/core/PromptClient.ts","../../../src/core/TerminalManager.ts","../../../src/core/stores/MemoryTerminalStore.ts","../../../src/core/stores/DatabaseTerminalStore.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { PromptRole, PromptTheme } from './types.js'\nimport { CSI, ESC, freezeStyle, STATUS_ICONS } from '@orkestrel/console'\n\n// The constant DATA the pure terminal core reads: key decoding, rendering, and transport defaults.\n// UPPER_SNAKE, `Object.freeze`d, every member exported. Control bytes are built with\n// `String.fromCharCode` so no raw control character appears in source, and the escape lead and the\n// CSI prefix come from `@orkestrel/console`, which publishes both.\n\n// === Control bytes (named, no raw control characters in source)\n\n/** Names the carriage return byte (`\\r`, U+000D) — Enter on most terminals. */\nexport const RETURN = String.fromCharCode(13)\n/** Names the line feed byte (`\\n`, U+000A) — Enter on some terminals / pasted input. */\nexport const NEWLINE = String.fromCharCode(10)\n/** Names the tab byte (`\\t`, U+0009). */\nexport const TAB = String.fromCharCode(9)\n/** Names the backspace byte (BS, U+0008) — Ctrl+H / some terminals' Backspace. */\nexport const BACKSPACE = String.fromCharCode(8)\n/** Names the delete byte (DEL, U+007F) — the usual Backspace byte on a Unix TTY. */\nexport const DELETE = String.fromCharCode(127)\n/** Names the space byte (U+0020). */\nexport const SPACE = ' '\n/** Names the Ctrl+C byte (ETX, U+0003) — interrupt / cancel. */\nexport const CTRL_C = String.fromCharCode(3)\n/** Names the Ctrl+D byte (EOT, U+0004) — end-of-transmission / finish (the editor's commit key). */\nexport const CTRL_D = String.fromCharCode(4)\n/** Names the Ctrl+U byte (NAK, U+0015) — clear the current line. */\nexport const CTRL_U = String.fromCharCode(21)\n/** Names the Ctrl+A byte (SOH, U+0001) — move to start of line. */\nexport const CTRL_A = String.fromCharCode(1)\n/** Names the Ctrl+E byte (ENQ, U+0005) — move to end of line. */\nexport const CTRL_E = String.fromCharCode(5)\n\n/**\n * Names the Single Shift Three lead (`ESCO`) — the alternate arrow-key prefix some terminals emit\n * (`ESC O A`). Built from the console module's own {@link import('@orkestrel/console').ESC}; the\n * navigation keys' CSI lead is that module's {@link import('@orkestrel/console').CSI}, which this\n * package reuses rather than redeclaring.\n */\nexport const KEY_SS3 = `${ESC}O`\n\n/**\n * Holds the exact escape sequence to canonical key name table\n * {@link import('./helpers.js').parseKey} consults for the navigation and editing keys. Covers the\n * CSI form (`ESC[A`…) and the SS3 form (`ESCOA`…) of the arrows, plus the `home` / `end` / `delete`\n * CSI sequences with their numeric-tilde variants. The source of truth for the multi-byte key\n * decode; frozen.\n *\n * @remarks\n * Terminals disagree on these: a cursor key is `ESC[A` (normal) or `ESCOA` (application mode),\n * and Home / End / Delete each have a letter form (`ESC[H` / `ESC[F`) and a numeric form\n * (`ESC[1~` / `ESC[4~` / `ESC[3~`). Every accepted spelling maps to one name so a reducer never\n * sees the wire encoding.\n */\nexport const SEQUENCE_NAMES: Readonly<Record<string, string>> = Object.freeze({\n\t[`${CSI}A`]: 'up',\n\t[`${CSI}B`]: 'down',\n\t[`${CSI}C`]: 'right',\n\t[`${CSI}D`]: 'left',\n\t[`${KEY_SS3}A`]: 'up',\n\t[`${KEY_SS3}B`]: 'down',\n\t[`${KEY_SS3}C`]: 'right',\n\t[`${KEY_SS3}D`]: 'left',\n\t[`${CSI}H`]: 'home',\n\t[`${CSI}F`]: 'end',\n\t[`${CSI}1~`]: 'home',\n\t[`${CSI}4~`]: 'end',\n\t[`${CSI}3~`]: 'delete',\n\t[`${CSI}7~`]: 'home',\n\t[`${CSI}8~`]: 'end',\n})\n\n/**\n * Holds the control byte (or CRLF pair) to key descriptor table\n * {@link import('./helpers.js').parseKey} consults for the one-byte keys and the two-byte CRLF\n * Enter chunk. Each entry carries the canonical `name` and whether it is a `ctrl` combination. The\n * source of truth for that decode; frozen.\n *\n * @remarks\n * `return` / `newline` / `return + newline` all map to `return` (one canonical Enter name) — a\n * terminal or a paste can deliver Enter as `\\r`, `\\n`, or the `\\r\\n` pair in one chunk;\n * `delete` / `backspace` both map to `backspace` (the Backspace bytes); the Ctrl combos\n * (`c` / `d` / `u` / `a` / `e`) carry `ctrl: true` so a reducer can match\n * `key.ctrl && key.name === 'c'`. `escape` / `tab` / `space` are plain named keys.\n */\nexport const CONTROL_NAMES: Readonly<\n\tRecord<string, { readonly name: string; readonly ctrl: boolean }>\n> = Object.freeze({\n\t[RETURN]: Object.freeze({ name: 'return', ctrl: false }),\n\t[NEWLINE]: Object.freeze({ name: 'return', ctrl: false }),\n\t[`${RETURN}${NEWLINE}`]: Object.freeze({ name: 'return', ctrl: false }),\n\t[TAB]: Object.freeze({ name: 'tab', ctrl: false }),\n\t[ESC]: Object.freeze({ name: 'escape', ctrl: false }),\n\t[BACKSPACE]: Object.freeze({ name: 'backspace', ctrl: false }),\n\t[DELETE]: Object.freeze({ name: 'backspace', ctrl: false }),\n\t[SPACE]: Object.freeze({ name: 'space', ctrl: false }),\n\t[CTRL_C]: Object.freeze({ name: 'c', ctrl: true }),\n\t[CTRL_D]: Object.freeze({ name: 'd', ctrl: true }),\n\t[CTRL_U]: Object.freeze({ name: 'u', ctrl: true }),\n\t[CTRL_A]: Object.freeze({ name: 'a', ctrl: true }),\n\t[CTRL_E]: Object.freeze({ name: 'e', ctrl: true }),\n})\n\n// === Prompt defaults\n\n/** Names the default mask glyph {@link import('./helpers.js').createPasswordState} uses — `*`. */\nexport const DEFAULT_MASK = '*'\n\n// === Prompt-view icons\n\n/**\n * Holds the terminal-owned glyphs {@link DEFAULT_PROMPT_THEME} assembles its `icons` from, beside\n * the console module's own success and error marks. Read only when the default theme is assembled;\n * a view reads its resolved theme and never this constant. Frozen.\n *\n * @remarks\n * - `question` — the leading mark on a prompt's message line.\n * - `pointer` — the cursor before the input / the focused choice row.\n * - `dot` / `selected` — an unfocused / focused row marker in a select list.\n * - `checked` / `unchecked` — a checked / unchecked box in a checkbox list.\n */\nexport const PROMPT_ICONS = Object.freeze({\n\tquestion: '?',\n\tpointer: '›',\n\tdot: '○',\n\tselected: '●',\n\tchecked: '☑',\n\tunchecked: '☐',\n})\n\n// === The default prompt theme\n\n/**\n * Holds every {@link import('./types.js').PromptRole}, in one frozen list — the role axis's source of\n * truth. {@link import('./helpers.js').createPromptTheme} walks it to merge a partial theme, and\n * a consumer building a complete role map reads it rather than retyping every name.\n */\nexport const PROMPT_ROLES: readonly PromptRole[] = Object.freeze([\n\t'question',\n\t'pointer',\n\t'message',\n\t'content',\n\t'success',\n\t'error',\n\t'selected',\n\t'focus',\n\t'hint',\n\t'muted',\n\t'description',\n])\n\n/**\n * Holds the {@link import('./types.js').PromptTheme} every prompt renders with unless its options supply\n * another — the glyph set assembled from {@link PROMPT_ICONS} plus the console\n * {@link import('@orkestrel/console').STATUS_ICONS} `success` / `error` marks, and the console\n * {@link import('@orkestrel/console').Style} each role is painted with. Deeply frozen through the\n * console module's own {@link import('@orkestrel/console').freezeStyle}; the baseline\n * {@link import('./helpers.js').createPromptTheme} merges a partial theme over.\n *\n * @remarks\n * The default roles reproduce the views' historical coloring exactly: `question` / `pointer` cyan,\n * `message` / `focus` bold, `success` / `selected` green, `error` red, `hint` / `muted` /\n * `description` dim, and `content` the EMPTY style — an empty style renders bare text, so primary\n * content keeps the bytes it had before it became themeable.\n * Two roles sharing a style today are still two roles — re-mapping one leaves the other alone.\n */\nexport const DEFAULT_PROMPT_THEME: PromptTheme = Object.freeze({\n\ticons: Object.freeze({\n\t\tquestion: PROMPT_ICONS.question,\n\t\tpointer: PROMPT_ICONS.pointer,\n\t\tdot: PROMPT_ICONS.dot,\n\t\tselected: PROMPT_ICONS.selected,\n\t\tchecked: PROMPT_ICONS.checked,\n\t\tunchecked: PROMPT_ICONS.unchecked,\n\t\tsuccess: STATUS_ICONS.success,\n\t\terror: STATUS_ICONS.error,\n\t}),\n\troles: Object.freeze({\n\t\tquestion: freezeStyle({ foreground: 'cyan', attributes: [] }),\n\t\tpointer: freezeStyle({ foreground: 'cyan', attributes: [] }),\n\t\tmessage: freezeStyle({ attributes: ['bold'] }),\n\t\tcontent: freezeStyle({ attributes: [] }),\n\t\tsuccess: freezeStyle({ foreground: 'green', attributes: [] }),\n\t\terror: freezeStyle({ foreground: 'red', attributes: [] }),\n\t\tselected: freezeStyle({ foreground: 'green', attributes: [] }),\n\t\tfocus: freezeStyle({ attributes: ['bold'] }),\n\t\thint: freezeStyle({ attributes: ['dim'] }),\n\t\tmuted: freezeStyle({ attributes: ['dim'] }),\n\t\tdescription: freezeStyle({ attributes: ['dim'] }),\n\t}),\n})\n\n// === Broker + SSE-bridge defaults\n\n/** Holds how long (ms) the {@link import('./types.js').PromptInterface} broker parks an unanswered form before it expires — 5 minutes. */\nexport const DEFAULT_PROMPT_TIMEOUT_MS = 300_000\n\n/** Holds how long (ms) the {@link import('./types.js').PromptClientInterface} waits before each reconnect attempt — 2 seconds. */\nexport const DEFAULT_RECONNECT_DELAY_MS = 2_000\n\n/**\n * Holds the SSE `event:` names the broker emits and the\n * {@link import('./types.js').PromptClientInterface} dispatches on — `pending`, `expire`, and\n * `destroy`. Frozen; the source of truth for the wire event vocabulary.\n *\n * @remarks\n * - `pending` — a serialized {@link import('./types.js').PendingForm} to dispatch and answer.\n * - `expire` — an `{ id }` payload: the broker expired or released a parked form (the client drops it).\n * - `destroy` — the broker is going away; the client disconnects (no auto-reconnect) but stays reusable.\n */\nexport const SSE_EVENTS = Object.freeze({\n\tpending: 'pending',\n\texpire: 'expire',\n\tdestroy: 'destroy',\n})\n\n/**\n * Names the auth-token request header the {@link import('./types.js').PromptClientInterface} sends\n * when a `token` is configured — `x-orkestrel-token`.\n */\nexport const HEADER_TOKEN = 'x-orkestrel-token'\n\n/** Names the `Accept` header value that opens the broker's SSE stream — `text/event-stream`. */\nexport const ACCEPT_EVENT_STREAM = 'text/event-stream'\n\n/**\n * Sets the maximum number of characters the {@link import('./types.js').PromptClientInterface} lets its\n * SSE parser buffer before treating the stream as hostile — 1 MiB, comfortably above any\n * legitimate prompt payload. Passed as the `limit` to `createSSEParser` so an unterminated\n * or oversized `data:` field cannot grow the buffer without bound (a memory-exhaustion guard).\n */\nexport const SSE_BUFFER_LIMIT = 1_048_576\n","import type { TerminalErrorCode } from './types.js'\nimport { isInstance } from '@orkestrel/contract'\n\n// `.claude/rules/typescript.md` § Errors and outcomes: a real error type, not a sentinel. Callers\n// branch on the machine-readable\n// `error.code` rather than parsing the message, and the guard narrows with `instanceof`,\n// mirroring the agents-module errors.\n\n/**\n * Represents the error the terminal surfaces for its own refusals: parking on a destroyed or full broker, an\n * unusable driver stream, a manager routing fault, or a ctrl-c cancellation. A parked form's own\n * lifecycle failures reject through the form's `answer` with the form package's error, never with\n * this one.\n *\n * @remarks\n * Carries a {@link TerminalErrorCode} and an optional `context` bag naming the offending values:\n * `{ cap }` on `LIMIT`, `{ to, known }` on `TARGET`, and `{ from, to, path }` on `DEADLOCK`. Narrow\n * a caught value with {@link isTerminalError} and branch on `error.code`.\n */\nexport class TerminalError extends Error {\n\t/** Holds the machine-readable condition — see {@link TerminalErrorCode}. */\n\treadonly code: TerminalErrorCode\n\t/** Holds an optional context bag naming the offending values — see the class {@link TerminalError remarks}. */\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\t/**\n\t * Builds one terminal refusal.\n\t *\n\t * @param code - The machine-readable {@link TerminalErrorCode} a caller branches on\n\t * @param message - The human-readable reason\n\t * @param context - The optional bag naming the offending values — see the class {@link TerminalError remarks}\n\t */\n\tconstructor(\n\t\tcode: TerminalErrorCode,\n\t\tmessage: string,\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'TerminalError'\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Narrows an unknown caught value to a {@link TerminalError}, so a caller can branch on its `code`.\n *\n * @param value - The value to test (typically a `catch` binding or a rejected prompt call)\n * @returns True if `value` is a {@link TerminalError}; false otherwise\n *\n * @example\n * ```ts\n * try {\n * \tawait terminal.ask(form)\n * } catch (error) {\n * \tif (isTerminalError(error) && error.code === 'CANCEL') {\n * \t\t// the person aborted\n * \t}\n * }\n * ```\n */\nexport function isTerminalError(value: unknown): value is TerminalError {\n\treturn isInstance(value, TerminalError)\n}\n","import type { PendingForm, PendingFormStatus, TerminalSnapshot, WireEvent } from './types.js'\nimport type { Guard } from '@orkestrel/contract'\nimport {\n\tisNonEmptyString,\n\tisNumber,\n\tisRecord,\n\tisString,\n\tliteralOf,\n\trecordOf,\n} from '@orkestrel/contract'\n\n/**\n * Narrows an unknown value to a {@link PendingFormStatus}.\n *\n * @param value - The candidate ticket status\n * @returns True if the value is one of the declared ticket statuses; false otherwise\n */\nexport const isPendingFormStatus: Guard<PendingFormStatus> = literalOf(\n\t'pending',\n\t'answered',\n\t'expired',\n)\n\n/**\n * Narrows an unknown wire value to a {@link PendingForm} envelope — the envelope alone, because the\n * form package's `parseForm` owns the schema payload.\n *\n * @remarks\n * This guard checks the transport record and proves only that `schema` is a record. The Form\n * package's `parseForm` owns the schema payload and its semantic audit.\n *\n * @param value - The decoded wire value to inspect\n * @returns True if the value is a complete pending-form envelope; false otherwise\n */\nexport function isPendingForm(value: unknown): value is PendingForm {\n\treturn recordOf(\n\t\t{\n\t\t\tid: isNonEmptyString,\n\t\t\tschema: isRecord,\n\t\t\tstatus: isPendingFormStatus,\n\t\t\ttime: isNumber,\n\t\t\tfrom: isString,\n\t\t\tto: isString,\n\t\t},\n\t\t['from', 'to'],\n\t)(value)\n}\n\n/**\n * Narrows an unknown value to a transport-neutral {@link WireEvent} — the guard a consumer's own\n * transport applies to an inbound frame.\n *\n * @param value - The candidate wire event\n * @returns True if the value carries an event name, serialized data, and an optional id; false otherwise\n */\nexport const isWireEvent: Guard<WireEvent> = recordOf(\n\t{ event: isString, data: isString, id: isString },\n\t['id'],\n)\n\n/**\n * Narrows an unknown value to a {@link TerminalSnapshot} — a non-empty `id` and an optional numeric\n * `timeout`, the read boundary a store applies to an untrusted persisted row.\n *\n * @param value - The candidate snapshot read back from storage\n * @returns True if the value carries a non-empty `id` and an optional numeric `timeout`; false otherwise\n */\nexport const isTerminalSnapshot: Guard<TerminalSnapshot> = recordOf(\n\t{ id: isNonEmptyString, timeout: isNumber },\n\t['timeout'],\n)\n","import type {\n\tCheckboxState,\n\tConfirmState,\n\tEditorState,\n\tFetchInit,\n\tInputState,\n\tKeyEvent,\n\tPasswordState,\n\tPendingForm,\n\tPromptIcon,\n\tPromptRole,\n\tPromptStep,\n\tPromptTheme,\n\tPromptThemeOptions,\n\tSelectState,\n\tTimerCancelFunction,\n\tWireEvent,\n} from './types.js'\nimport type { Style, StylerInterface } from '@orkestrel/console'\nimport type {\n\tCheckboxField,\n\tConfirmField,\n\tEditorField,\n\tFormField,\n\tFormSchema,\n\tPasswordField,\n\tSelectField,\n\tTextField,\n} from '@orkestrel/form'\nimport {\n\tCONTROL_NAMES,\n\tDEFAULT_MASK,\n\tDEFAULT_PROMPT_THEME,\n\tPROMPT_ROLES,\n\tSEQUENCE_NAMES,\n} from './constants.js'\nimport { isError, isString } from '@orkestrel/contract'\nimport { createStyler, freezeStyle, strip, stripControls } from '@orkestrel/console'\n\n// The PURE prompt core implementation — all EXPORTED, all pure, all unit-tested:\n// the key decoder, schema sanitization, per-field view renderers, and the `create*State`\n// factories with their `reduce*` reducers. No `node:*`, no I/O, no events. Form owns validation and\n// settlement; these reducers only turn keys into candidate field values.\n\n// === Key decoding\n\n/**\n * Decodes one keypress's bytes into a {@link KeyEvent} — total, never throws. A `Uint8Array` is\n * read as UTF-8; the resulting string is matched against the known control bytes and the CRLF pair\n * ({@link CONTROL_NAMES}) and escape sequences ({@link SEQUENCE_NAMES}), falling back to a single\n * printable character. An unrecognized sequence carries no `name`, with the raw `sequence`\n * preserved.\n *\n * @remarks\n * - **Single control byte.** A one-character control input (`return` / `backspace` / `tab` /\n * `escape` / `space`, or a Ctrl combo `c` / `d` / `u` / `a` / `e`), or the two-byte `\\r\\n`\n * CRLF pair, is looked up in {@link CONTROL_NAMES}, carrying its `ctrl` flag.\n * - **Escape sequence.** A multi-byte ESC sequence (`up` / `down` / `left` / `right` in both the\n * `ESC[A` and `ESCOA` forms, plus `home` / `end` / `delete`) is looked up in\n * {@link SEQUENCE_NAMES} and flagged `meta`.\n * - **Printable character.** A single printable character becomes `name` = that character, with\n * `shift` set when it is an uppercase letter. A multi-code-point printable (an emoji, a pasted\n * run) keeps its first code point as the name and the whole input as `sequence`.\n * - **Unknown.** Anything else (an unrecognized escape, an empty input) yields an event with NO\n * `name` — absence, never an empty string — total, so the driver never crashes on a stray byte.\n *\n * @param input - The raw keypress bytes, as a string or `Uint8Array`\n * @returns The decoded {@link KeyEvent}\n *\n * @example\n * ```ts\n * parseKey('\\r') // { name: 'return', sequence: '\\r', ctrl: false, meta: false, shift: false }\n * parseKey('\\r\\n') // { name: 'return', sequence: '\\r\\n', ctrl: false, meta: false, shift: false }\n * parseKey('\\x1b[A') // { name: 'up', sequence: '\\x1b[A', ctrl: false, meta: true, shift: false }\n * parseKey('A') // { name: 'A', sequence: 'A', ctrl: false, meta: false, shift: true }\n * parseKey('\\x03') // { name: 'c', sequence: '\\x03', ctrl: true, meta: false, shift: false }\n * ```\n */\nexport function parseKey(input: string | Uint8Array): KeyEvent {\n\tconst sequence = isString(input) ? input : new TextDecoder().decode(input)\n\n\t// A known multi-byte escape sequence (arrows / home / end / delete) — flagged `meta`.\n\tconst sequenceName = SEQUENCE_NAMES[sequence]\n\tif (sequenceName !== undefined) {\n\t\treturn { name: sequenceName, sequence, ctrl: false, meta: true, shift: false }\n\t}\n\n\t// A known single control byte (return / backspace / tab / escape / space / a ctrl combo),\n\t// or the two-byte CRLF pair.\n\tconst control = CONTROL_NAMES[sequence]\n\tif (control !== undefined) {\n\t\treturn { name: control.name, sequence, ctrl: control.ctrl, meta: false, shift: false }\n\t}\n\n\t// A printable character — one or more code points, the first naming the key.\n\tconst points = [...sequence]\n\tconst first = points[0]\n\tif (first !== undefined && isPrintable(first)) {\n\t\treturn { name: first, sequence, ctrl: false, meta: false, shift: first !== first.toLowerCase() }\n\t}\n\n\t// Anything else (an unrecognized escape, an empty input) — total, never a throw. The name is\n\t// OMITTED rather than emptied, so a caller reads absence instead of a sentinel.\n\treturn { sequence, ctrl: false, meta: false, shift: false }\n}\n\n/**\n * Checks whether a single character is printable — the fallback test {@link parseKey} applies after\n * the control bytes and the escape sequences, so the C0 controls and DEL are excluded.\n *\n * @param character - The single character to test\n * @returns True if the character is at or above space and is not DEL; false otherwise\n */\nexport function isPrintable(character: string): boolean {\n\tif (character.length === 0) return false\n\tconst code = character.codePointAt(0)\n\tif (code === undefined) return false\n\t// Exclude the C0 controls (0–31) and DEL (127); everything at or above space is printable.\n\treturn code >= 32 && code !== 127\n}\n\n// === Prompt theme\n\n/**\n * Builds a complete {@link PromptTheme} by merging a partial one over\n * {@link DEFAULT_PROMPT_THEME}, leaf by leaf — each supplied icon replaces that glyph, each\n * supplied role replaces that {@link Style}, and everything else keeps its default. Each supplied\n * style is snapshotted through the console module's own\n * {@link import('@orkestrel/console').freezeStyle}, so the result is deeply frozen and a caller\n * mutating its own attribute list afterwards cannot reach into a built theme.\n *\n * @param options - The partial theme to merge, or `undefined` for the defaults\n * @returns The resolved, deeply frozen theme every prompt state carries\n *\n * @example\n * ```ts\n * createPromptTheme() // the defaults\n * createPromptTheme({\n * \ticons: { pointer: '=>' },\n * \troles: { message: { foreground: 'magenta', attributes: ['bold'] } },\n * })\n * ```\n */\nexport function createPromptTheme(options?: PromptThemeOptions): PromptTheme {\n\tconst icons: Record<PromptIcon, string> = { ...DEFAULT_PROMPT_THEME.icons, ...options?.icons }\n\tconst roles: Record<PromptRole, Style> = { ...DEFAULT_PROMPT_THEME.roles }\n\tfor (const role of PROMPT_ROLES) {\n\t\tconst style = options?.roles?.[role]\n\t\tif (style !== undefined) roles[role] = freezeStyle(style)\n\t}\n\treturn Object.freeze({ icons: Object.freeze(icons), roles: Object.freeze(roles) })\n}\n\n/**\n * Sanitizes text for one single-line display slot. Composes console's ANSI {@link strip} and C0\n * {@link stripControls} passes with removal of tab, line feed, and carriage return.\n *\n * @param text - The text to sanitize for a glyph or hint slot\n * @returns The text with ANSI sequences, every C0 control character, and DEL removed\n *\n * @example\n * ```ts\n * sanitizeDisplayText('Q\\rOVERWRITE\\nNEXT\\tX') // 'QOVERWRITENEXTX'\n * ```\n */\nexport function sanitizeDisplayText(text: string): string {\n\treturn stripControls(strip(text)).replaceAll('\\t', '').replaceAll('\\n', '').replaceAll('\\r', '')\n}\n\n/**\n * Sanitizes every terminal-readable string in a parsed form schema, keeping every identity and\n * answer string verbatim and dropping field metadata.\n *\n * @remarks\n * Display strings pass through {@link sanitizeDisplayText}: labels, help, placeholders, masks,\n * choice labels and help, file accept entries, and pattern sources. Identity and answer strings\n * stay verbatim: schema, group, and field names, group references, choice values, and defaults.\n * Rewriting those would sever the rendering copy from the authoritative form. Field metadata is\n * removed because terminal neither renders nor interprets it. Pattern sources are sanitized as\n * text only and are never compiled or executed here.\n *\n * @param schema - A schema already accepted by the Form package's `parseForm`\n * @returns A new schema with terminal-readable strings sanitized and field metadata omitted\n *\n * @example\n * ```ts\n * sanitizeSchema({ fields: [{ control: 'text', name: 'na\\u001bme', label: 'N\\u0000ame' }] })\n * // { fields: [{ control: 'text', name: 'na\\u001bme', label: 'Name' }] }\n * ```\n */\nexport function sanitizeSchema(schema: FormSchema): FormSchema {\n\tconst groups = schema.groups?.map((group) => ({\n\t\tname: group.name,\n\t\tlabel: sanitizeDisplayText(group.label),\n\t\t...(group.help !== undefined ? { help: sanitizeDisplayText(group.help) } : {}),\n\t}))\n\tconst fields: FormField[] = []\n\tfor (const source of schema.fields) {\n\t\tconst { meta: _meta, ...field } = source\n\t\tconst rule =\n\t\t\tfield.rule === undefined\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\t...field.rule,\n\t\t\t\t\t\t...(field.rule.pattern !== undefined\n\t\t\t\t\t\t\t? { pattern: sanitizeDisplayText(field.rule.pattern) }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t}\n\t\tconst shared = {\n\t\t\tname: field.name,\n\t\t\t...(field.label !== undefined ? { label: sanitizeDisplayText(field.label) } : {}),\n\t\t\t...(field.help !== undefined ? { help: sanitizeDisplayText(field.help) } : {}),\n\t\t\t...(field.group !== undefined ? { group: field.group } : {}),\n\t\t\t...(rule !== undefined ? { rule } : {}),\n\t\t}\n\n\t\tswitch (field.control) {\n\t\t\tcase 'text':\n\t\t\tcase 'editor':\n\t\t\tcase 'number': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\t...(field.placeholder !== undefined\n\t\t\t\t\t\t? { placeholder: sanitizeDisplayText(field.placeholder) }\n\t\t\t\t\t\t: {}),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'password': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\t...(field.mask !== undefined ? { mask: sanitizeDisplayText(field.mask) } : {}),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'date':\n\t\t\tcase 'time':\n\t\t\tcase 'datetime':\n\t\t\tcase 'color':\n\t\t\tcase 'confirm': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'select':\n\t\t\tcase 'checkbox': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\tchoices: field.choices.map((choice) => ({\n\t\t\t\t\t\t...choice,\n\t\t\t\t\t\tvalue: choice.value,\n\t\t\t\t\t\tlabel: sanitizeDisplayText(choice.label),\n\t\t\t\t\t\t...(choice.help !== undefined ? { help: sanitizeDisplayText(choice.help) } : {}),\n\t\t\t\t\t})),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'file': {\n\t\t\t\tfields.push({\n\t\t\t\t\t...field,\n\t\t\t\t\t...shared,\n\t\t\t\t\t...(field.accept !== undefined ? { accept: field.accept.map(sanitizeDisplayText) } : {}),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\t...(schema.name !== undefined ? { name: schema.name } : {}),\n\t\t...(schema.label !== undefined ? { label: sanitizeDisplayText(schema.label) } : {}),\n\t\t...(schema.help !== undefined ? { help: sanitizeDisplayText(schema.help) } : {}),\n\t\t...(groups !== undefined ? { groups } : {}),\n\t\tfields,\n\t}\n}\n\n/**\n * Sanitizes every glyph a wire-supplied {@link PromptThemeOptions} carries for a single-line display\n * slot. Only the icons need it: a role is guard-narrowed to a console {@link Style}, whose colors\n * and attributes are fixed name sets, so no role can carry a byte a terminal would act on.\n *\n * @param theme - The narrowed theme options a remote prompt supplied\n * @returns The same theme with every supplied glyph sanitized for a single-line display slot\n */\nexport function sanitizeThemeIcons(theme: PromptThemeOptions): PromptThemeOptions {\n\tconst icons = theme.icons\n\tif (icons === undefined) return theme\n\tconst sanitized: Record<string, string> = {}\n\tfor (const [icon, glyph] of Object.entries(icons)) sanitized[icon] = sanitizeDisplayText(glyph)\n\treturn { ...theme, icons: sanitized }\n}\n\n// === Shared view helpers\n\n/**\n * Renders the styled question header (`? message`) — the leading line every active prompt view\n * shares, themed by the `question` + `message` roles.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The prompt's question text\n * @returns The rendered question header\n */\nexport function renderPromptHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n): string {\n\treturn `${styler.render(theme.roles.question, theme.icons.question)} ${styler.render(theme.roles.message, message)}`\n}\n\n/**\n * Renders a question header followed by a key hint painted with the `hint` role, or the header\n * alone when no hint is supplied.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The prompt's question text\n * @param hint - The optional key hint to append\n * @returns The rendered header with the optional hint\n */\nexport function renderHintedHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n\thint?: string,\n): string {\n\tconst head = renderPromptHeader(styler, theme, message)\n\treturn hint === undefined ? head : `${head} ${styler.render(theme.roles.hint, hint)}`\n}\n\n/**\n * Renders the styled submit line (`✔ message`) — the committed header an interactive prompt shows\n * after it resolves, themed by the `success` + `message` roles.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The settled prompt's question text\n * @returns The rendered committed header\n */\nexport function renderSubmitHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n): string {\n\treturn `${styler.render(theme.roles.success, theme.icons.success)} ${styler.render(theme.roles.message, message)}`\n}\n\n/**\n * Renders the styled failure line (`✖ message`) a form driver writes for each refused field before\n * it asks that field again.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param message - The failure text, normally the field's label and the reason\n * @returns The rendered failure line\n */\nexport function renderErrorLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tmessage: string,\n): string {\n\treturn `${styler.render(theme.roles.error, theme.icons.error)} ${styler.render(theme.roles.error, message)}`\n}\n\n// === Input prompt\n\n/**\n * Builds the initial text-field reducer state — the sanitized label, the declared default, the\n * styler, and the resolved theme.\n *\n * @param field - The text field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createInputState(\n\tfield: TextField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): InputState {\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tdefault: field.default ?? '',\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tvalue: '',\n\t}\n}\n\n/**\n * Renders a text-field reducer state as a styled view — the header, the pointer, and the typed\n * value, or the default shown as a hint while nothing is typed.\n *\n * @param state - The text field's current reducer state\n * @returns The rendered single-line view — header, pointer, and the typed value or the default\n */\nexport function renderInputView(state: InputState): string {\n\tconst content = state.value.length > 0 ? state.value : state.default\n\tconst role = state.value.length > 0 ? state.theme.roles.content : state.theme.roles.hint\n\tconst shown = state.styler.render(role, sanitizeDisplayText(content))\n\treturn `${renderPromptHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)} ${shown}`\n}\n\n/**\n * Advances an input prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. Printable characters extend the value; backspace shrinks it; ctrl-u clears it; ctrl-c\n * cancels; return produces the candidate value, with an empty line falling back to the default.\n *\n * @param state - The text field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the new state, the rendered view, the status, and the value on submit\n */\nexport function reduceInput(state: InputState, key: KeyEvent): PromptStep<string, InputState> {\n\tif (key.ctrl && key.name === 'c') return { state, view: renderInputView(state), status: 'cancel' }\n\n\tif (key.name === 'return') {\n\t\tconst answer = state.value.length > 0 ? state.value : state.default\n\t\tconst next = { ...state, value: answer }\n\t\treturn {\n\t\t\tstate: next,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, sanitizeDisplayText(answer))}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: answer,\n\t\t}\n\t}\n\n\tconst value = editLine(state.value, key)\n\tif (value === undefined) return { state, view: renderInputView(state), status: 'active' }\n\tconst next = { ...state, value }\n\treturn { state: next, view: renderInputView(next), status: 'active' }\n}\n\n// === Password prompt\n\n/**\n * Builds the initial password-field reducer state — the text-field state, plus the mask glyph each\n * typed character renders as.\n *\n * @param field - The password field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createPasswordState(\n\tfield: PasswordField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): PasswordState {\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tmask: sanitizeDisplayText(field.mask ?? DEFAULT_MASK),\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tvalue: '',\n\t}\n}\n\n/**\n * Renders a password-field reducer state as a styled view, with the value replaced by the mask\n * repeated so the secret is never echoed.\n *\n * @param state - The password field's current reducer state\n * @returns The rendered view, with the mask repeated in place of the typed value\n */\nexport function renderPasswordView(state: PasswordState): string {\n\tconst masked = state.styler.render(\n\t\tstate.theme.roles.content,\n\t\tstate.mask.repeat(state.value.length),\n\t)\n\treturn `${renderPromptHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)} ${masked}`\n}\n\n/**\n * Advances a password prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. Identical line-editing to {@link reduceInput} (printable extends, backspace shrinks,\n * ctrl-u clears, ctrl-c cancels) but the view masks the value. Return produces the candidate value.\n *\n * @param state - The password field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the new state, the masked view, the status, and the value on submit\n */\nexport function reducePassword(\n\tstate: PasswordState,\n\tkey: KeyEvent,\n): PromptStep<string, PasswordState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderPasswordView(state), status: 'cancel' }\n\n\tif (key.name === 'return') {\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, state.mask.repeat(state.value.length))}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: state.value,\n\t\t}\n\t}\n\n\tconst value = editLine(state.value, key)\n\tif (value === undefined) return { state, view: renderPasswordView(state), status: 'active' }\n\tconst next = { ...state, value }\n\treturn { state: next, view: renderPasswordView(next), status: 'active' }\n}\n\n// === Confirm prompt\n\n/**\n * Builds the initial confirm-field reducer state — the sanitized label and the declared default\n * answer.\n *\n * @param field - The confirm field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createConfirmState(\n\tfield: ConfirmField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): ConfirmState {\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tdefault: field.default ?? false,\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t}\n}\n\n/**\n * Renders a confirm-field reducer state as a styled view — the header and the yes/no group, with\n * the default letter capitalized and painted by the `selected` role.\n *\n * @param state - The confirm field's current reducer state\n * @returns The rendered view — the header and the yes/no group with the default capitalized\n */\nexport function renderConfirmView(state: ConfirmState): string {\n\tconst head = renderPromptHeader(state.styler, state.theme, state.message)\n\tconst answer = state.default\n\t\t? `${state.styler.render(state.theme.roles.selected, 'Y')}${state.styler.render(state.theme.roles.hint, '/n')}`\n\t\t: `${state.styler.render(state.theme.roles.hint, 'y/')}${state.styler.render(state.theme.roles.selected, 'N')}`\n\treturn `${head} ${state.styler.render(state.theme.roles.hint, '(')}${answer}${state.styler.render(state.theme.roles.hint, ')')}`\n}\n\n/**\n * Advances a confirm prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<boolean>`\n * reducer. `y` / `Y` submits `true`, `n` / `N` submits `false`, return on an empty line submits\n * the `default`, ctrl-c cancels; any other key is ignored (stays active).\n *\n * @param state - The confirm field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the answer on submit\n */\nexport function reduceConfirm(\n\tstate: ConfirmState,\n\tkey: KeyEvent,\n): PromptStep<boolean, ConfirmState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderConfirmView(state), status: 'cancel' }\n\n\tlet answer: boolean | undefined\n\tconst choice = key.name?.toLowerCase()\n\tif (key.name === 'return') answer = state.default\n\telse if (choice === 'y') answer = true\n\telse if (choice === 'n') answer = false\n\n\tif (answer === undefined) return { state, view: renderConfirmView(state), status: 'active' }\n\treturn {\n\t\tstate,\n\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, answer ? 'yes' : 'no')}`,\n\t\tstatus: 'submit',\n\t\tvalue: answer,\n\t}\n}\n\n// === Select prompt\n\n/**\n * Builds the initial select-field reducer state — the offered choices, with the focus pre-placed on\n * the declared default.\n *\n * @param field - The select field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createSelectState(\n\tfield: SelectField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): SelectState {\n\tconst choices = [...field.choices]\n\tconst index = choices.findIndex((choice) => choice.value === field.default)\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tchoices,\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tfocused: index >= 0 ? index : 0,\n\t}\n}\n\n/**\n * Renders a select-field reducer state as a multi-line styled view — one row per choice, with the\n * focused row marked and its help shown.\n *\n * @param state - The select field's current reducer state\n * @returns The rendered view — the header, then one row per choice with the focused row marked\n */\nexport function renderSelectView(state: SelectState): string {\n\tconst lines = state.choices.map((choice, index) => {\n\t\tconst active = index === state.focused\n\t\tconst pointer = active\n\t\t\t? state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)\n\t\t\t: ' '\n\t\tconst marker = active\n\t\t\t? state.styler.render(state.theme.roles.selected, state.theme.icons.selected)\n\t\t\t: state.styler.render(state.theme.roles.muted, state.theme.icons.dot)\n\t\tconst label = active\n\t\t\t? state.styler.render(state.theme.roles.focus, choice.label)\n\t\t\t: state.styler.render(state.theme.roles.content, choice.label)\n\t\tconst description =\n\t\t\tchoice.help === undefined\n\t\t\t\t? ''\n\t\t\t\t: ` ${state.styler.render(state.theme.roles.description, choice.help)}`\n\t\treturn `${pointer} ${marker} ${label}${description}`\n\t})\n\treturn [renderPromptHeader(state.styler, state.theme, state.message), ...lines].join('\\n')\n}\n\n/**\n * Advances a select prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. `up` / `down` (and `k` / `j`) move the focus, wrapping at the ends; return submits the\n * focused choice's `value`; ctrl-c cancels. An empty choice list can never submit (a higher layer\n * guards against it); any other key is ignored.\n *\n * @param state - The select field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the chosen value on submit\n */\nexport function reduceSelect(state: SelectState, key: KeyEvent): PromptStep<string, SelectState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderSelectView(state), status: 'cancel' }\n\n\tconst count = state.choices.length\n\tif (count === 0) return { state, view: renderSelectView(state), status: 'active' }\n\n\tif (key.name === 'up' || key.name === 'k') {\n\t\tconst next = { ...state, focused: (state.focused - 1 + count) % count }\n\t\treturn { state: next, view: renderSelectView(next), status: 'active' }\n\t}\n\tif (key.name === 'down' || key.name === 'j') {\n\t\tconst next = { ...state, focused: (state.focused + 1) % count }\n\t\treturn { state: next, view: renderSelectView(next), status: 'active' }\n\t}\n\tif (key.name === 'return') {\n\t\tconst choice = state.choices[state.focused]\n\t\tconst value = choice?.value ?? ''\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, choice?.label ?? '')}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue,\n\t\t}\n\t}\n\treturn { state, view: renderSelectView(state), status: 'active' }\n}\n\n// === Checkbox prompt\n\n/**\n * Builds the initial checkbox-field reducer state — the offered choices, with every value in the\n * field's `default` list pre-checked.\n *\n * @param field - The checkbox field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createCheckboxState(\n\tfield: CheckboxField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): CheckboxState {\n\tconst choices = [...field.choices]\n\tconst checked: readonly number[] = choices.reduce<number[]>((indices, choice, index) => {\n\t\tif (field.default?.includes(choice.value) === true) indices.push(index)\n\t\treturn indices\n\t}, [])\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tchoices,\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tfocused: 0,\n\t\tchecked,\n\t}\n}\n\n/**\n * Renders a checkbox-field reducer state as a multi-line styled view — one box per choice, and the\n * selected count beneath them.\n *\n * @param state - The checkbox field's current reducer state\n * @returns The rendered view — the header, one box per choice, and the selected count\n */\nexport function renderCheckboxView(state: CheckboxState): string {\n\tconst lines = state.choices.map((choice, index) => {\n\t\tconst active = index === state.focused\n\t\tconst ticked = state.checked.includes(index)\n\t\tconst pointer = active\n\t\t\t? state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)\n\t\t\t: ' '\n\t\tconst box = ticked\n\t\t\t? state.styler.render(state.theme.roles.selected, state.theme.icons.checked)\n\t\t\t: state.styler.render(state.theme.roles.muted, state.theme.icons.unchecked)\n\t\tconst label = active\n\t\t\t? state.styler.render(state.theme.roles.focus, choice.label)\n\t\t\t: state.styler.render(state.theme.roles.content, choice.label)\n\t\tconst description =\n\t\t\tchoice.help === undefined\n\t\t\t\t? ''\n\t\t\t\t: ` ${state.styler.render(state.theme.roles.description, choice.help)}`\n\t\treturn `${pointer} ${box} ${label}${description}`\n\t})\n\tconst summary = state.styler.render(state.theme.roles.hint, `${state.checked.length} selected`)\n\tconst body = [\n\t\trenderPromptHeader(state.styler, state.theme, state.message),\n\t\t...lines,\n\t\tsummary,\n\t].join('\\n')\n\treturn body\n}\n\n/**\n * Advances a checkbox prompt by one {@link KeyEvent} — the pure\n * `(state, key) → PromptStep<readonly string[]>` reducer. `up` / `down` (and `k` / `j`) move the\n * focus (wrapping); `space` toggles the focused index in the checked set; return submits the\n * checked values in choice order; ctrl-c cancels. The form applies selection-count rules.\n *\n * @param state - The checkbox field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the ticked values on submit\n */\nexport function reduceCheckbox(\n\tstate: CheckboxState,\n\tkey: KeyEvent,\n): PromptStep<readonly string[], CheckboxState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderCheckboxView(state), status: 'cancel' }\n\n\tconst count = state.choices.length\n\n\tif ((key.name === 'up' || key.name === 'k') && count > 0) {\n\t\tconst next = {\n\t\t\t...state,\n\t\t\tfocused: (state.focused - 1 + count) % count,\n\t\t}\n\t\treturn { state: next, view: renderCheckboxView(next), status: 'active' }\n\t}\n\tif ((key.name === 'down' || key.name === 'j') && count > 0) {\n\t\tconst next = { ...state, focused: (state.focused + 1) % count }\n\t\treturn { state: next, view: renderCheckboxView(next), status: 'active' }\n\t}\n\tif (key.name === 'space' && count > 0) {\n\t\tconst checked = toggleIndex(state.checked, state.focused)\n\t\tconst next = { ...state, checked }\n\t\treturn { state: next, view: renderCheckboxView(next), status: 'active' }\n\t}\n\tif (key.name === 'return') {\n\t\tconst ordered = [...state.checked].sort((a, b) => a - b)\n\t\tconst values = ordered\n\t\t\t.map((index) => state.choices[index]?.value)\n\t\t\t.filter((value): value is string => value !== undefined)\n\t\tconst summary = ordered\n\t\t\t.map((index) => state.choices[index]?.label)\n\t\t\t.filter((name): name is string => name !== undefined)\n\t\t\t.join(', ')\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, summary)}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: values,\n\t\t}\n\t}\n\treturn { state, view: renderCheckboxView(state), status: 'active' }\n}\n\n/**\n * Toggles `index` in a readonly index list — copy-on-write, returning the new sorted-by-insertion\n * list; the primitive {@link reduceCheckbox} calls.\n *\n * @param indices - The ticked indices, in tick order\n * @param index - The index to add when absent, or drop when present\n * @returns A new list carrying the toggled membership; the input is never mutated\n */\nexport function toggleIndex(indices: readonly number[], index: number): readonly number[] {\n\treturn indices.includes(index) ? indices.filter((i) => i !== index) : [...indices, index]\n}\n\n// === Editor prompt\n\n/**\n * Builds the initial editor-field reducer state — the committed lines empty, and the declared\n * default held for a finish with nothing typed.\n *\n * @param field - The editor field to render\n * @param styler - The styler used to render the view\n * @param theme - The optional terminal theme\n * @returns The initial immutable key state\n */\nexport function createEditorState(\n\tfield: EditorField,\n\tstyler: StylerInterface = createStyler(),\n\ttheme?: PromptThemeOptions,\n): EditorState {\n\tconst lines: readonly string[] = []\n\treturn {\n\t\tmessage: sanitizeDisplayText(field.label ?? field.name),\n\t\tdefault: field.default ?? '',\n\t\tstyler,\n\t\ttheme: createPromptTheme(theme),\n\t\tlines,\n\t\tcurrent: '',\n\t}\n}\n\n/**\n * Renders an editor-field reducer state as a multi-line styled view — the finish hint, the\n * committed lines, and the line in progress.\n *\n * @param state - The editor field's current reducer state\n * @returns The rendered view — the hinted header, the committed lines, and the line in progress\n */\nexport function renderEditorView(state: EditorState): string {\n\tconst head = renderHintedHeader(state.styler, state.theme, state.message, '(Ctrl+D to finish)')\n\tconst pointer = state.styler.render(state.theme.roles.pointer, state.theme.icons.pointer)\n\tconst committed = state.lines.map((line) => state.styler.render(state.theme.roles.content, line))\n\tconst body = [\n\t\t...committed,\n\t\t`${pointer} ${state.styler.render(state.theme.roles.content, state.current)}`,\n\t]\n\treturn [head, ...body].join('\\n')\n}\n\n/**\n * Advances an editor prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`\n * reducer. Printable characters extend the current line; backspace shrinks it; return commits the\n * current line and starts a fresh one; ctrl-d finishes, joining every line and falling back to the\n * default when empty; ctrl-c cancels. The form validates the candidate after the driver fills it.\n *\n * @param state - The editor field's current reducer state\n * @param key - The decoded keypress to apply\n * @returns The next step — the state, the rendered view, the status, and the joined text on submit\n */\nexport function reduceEditor(state: EditorState, key: KeyEvent): PromptStep<string, EditorState> {\n\tif (key.ctrl && key.name === 'c')\n\t\treturn { state, view: renderEditorView(state), status: 'cancel' }\n\n\tif (key.ctrl && key.name === 'd') {\n\t\tconst lines = state.current.length > 0 ? [...state.lines, state.current] : state.lines\n\t\tconst joined = lines.join('\\n')\n\t\tconst answer = joined.length > 0 ? joined : state.default\n\t\treturn {\n\t\t\tstate,\n\t\t\tview: `${renderSubmitHeader(state.styler, state.theme, state.message)} ${state.styler.render(state.theme.roles.hint, `${String(lines.length)} line${lines.length === 1 ? '' : 's'}`)}`,\n\t\t\tstatus: 'submit',\n\t\t\tvalue: answer,\n\t\t}\n\t}\n\n\tif (key.name === 'return') {\n\t\tconst next = {\n\t\t\t...state,\n\t\t\tlines: [...state.lines, state.current],\n\t\t\tcurrent: '',\n\t\t}\n\t\treturn { state: next, view: renderEditorView(next), status: 'active' }\n\t}\n\n\tconst current = editLine(state.current, key)\n\tif (current === undefined) return { state, view: renderEditorView(state), status: 'active' }\n\tconst next = { ...state, current }\n\treturn { state: next, view: renderEditorView(next), status: 'active' }\n}\n\n// === Shared reducer helpers\n\n/**\n * Applies a single line-editing {@link KeyEvent} to a text buffer — the editing shared by input,\n * password, and editor. A printable key appends its character; `backspace` drops the last\n * character; `space` appends a space; ctrl-u clears the line; a key that edits nothing returns\n * `undefined`.\n *\n * @param value - The buffer the field holds so far\n * @param key - The decoded keypress to apply\n * @returns The new buffer, or `undefined` when the key does not edit the line, so the caller can\n * leave the state untouched\n */\nexport function editLine(value: string, key: KeyEvent): string | undefined {\n\tif (key.ctrl && key.name === 'u') return ''\n\tif (key.name === 'backspace') return value.slice(0, -1)\n\tif (key.name === 'space') return `${value} `\n\t// A printable key that is not a control / navigation key — `name` is the literal character, and an\n\t// undecoded key carries none at all. Count CODE POINTS (not UTF-16 units) so an astral printable\n\t// (an emoji, a surrogate pair — `name.length` 2 but ONE code point) appends instead of being\n\t// dropped, while a multi-char control name (`up`, `return`) is still rejected.\n\tif (\n\t\t!key.ctrl &&\n\t\t!key.meta &&\n\t\tkey.name !== undefined &&\n\t\t[...key.name].length === 1 &&\n\t\tisPrintable(key.name)\n\t) {\n\t\treturn `${value}${key.sequence}`\n\t}\n\treturn undefined\n}\n\n// === Broker + bridge wiring helpers\n\n/**\n * Implements the default {@link import('./types.js').TimerHandler} — a thin host `setTimeout` / `clearTimeout`\n * wrapper that arms `callback` after `ms` and returns a {@link TimerCancelFunction}. The deadline seam\n * behind both the {@link import('./Prompt.js').Prompt} broker (its expiry) and the\n * {@link import('./PromptClient.js').PromptClient} (its reconnect backoff); a test injects a\n * deterministic timer instead, so neither entity touches real time.\n *\n * @param callback - The deadline callback to arm\n * @param ms - How long to wait before firing it, in milliseconds\n * @returns The {@link TimerCancelFunction} that clears the armed deadline\n */\nexport function defaultTimer(callback: () => void, ms: number): TimerCancelFunction {\n\tconst handle = setTimeout(callback, ms)\n\treturn () => clearTimeout(handle)\n}\n\n/**\n * Implements the default {@link import('./types.js').FetchHandler} — the global `fetch`, adapted to\n * the minimal injected shape the {@link import('./PromptClient.js').PromptClient} uses.\n *\n * @param input - The request URL\n * @param init - The request init the client sets — method, headers, body, and abort signal\n * @returns The host `fetch` promise for that request\n */\nexport function globalFetch(input: string, init?: FetchInit): Promise<Response> {\n\treturn fetch(input, init)\n}\n\n/**\n * Checks whether a caught value is an `AbortError` — the {@link import('./PromptClient.js').PromptClient}\n * distinguishes a deliberate `disconnect` / teardown (an aborted `fetch`) from a real fault, so it\n * exits its connect loop quietly instead of emitting `error` / reconnecting.\n *\n * @param error - The caught value to test\n * @returns True if the value is a host `Error` or `DOMException` named `AbortError`; false otherwise\n */\nexport function isAbortError(error: unknown): boolean {\n\treturn isError(error) && error.name === 'AbortError'\n}\n\n/**\n * Checks whether `url` is an insecure remote endpoint — a plain `http://` URL whose host is not a\n * loopback address. Pure string parsing (no `URL` global), so it stays total on malformed input;\n * the {@link import('./PromptClient.js').PromptClient} warns once when a `token` would cross such\n * an endpoint in cleartext.\n *\n * @remarks\n * A loopback host (`localhost`, `127.0.0.1`, `[::1]`) over `http://` is exempt (local\n * development has no network hop to eavesdrop on); every other `http://` host is insecure.\n * An `https://` URL (or any non-`http://` scheme) is never flagged.\n *\n * @param url - The candidate endpoint URL\n * @returns True if `url` is a non-loopback `http://` endpoint; false otherwise\n *\n * @example\n * ```ts\n * isInsecureRemote('http://example.com') // true\n * isInsecureRemote('http://localhost:3000') // false\n * isInsecureRemote('https://example.com') // false\n * ```\n */\nexport function isInsecureRemote(url: string): boolean {\n\tconst prefix = 'http://'\n\tif (!url.startsWith(prefix)) return false\n\tconst rest = url.slice(prefix.length)\n\tconst hostEnd = rest.search(/[/?#]/)\n\tconst authority = hostEnd === -1 ? rest : rest.slice(0, hostEnd)\n\tconst host = authority.includes('@') ? authority.slice(authority.indexOf('@') + 1) : authority\n\tconst hostname = host.startsWith('[')\n\t\t? host.slice(0, host.indexOf(']') + 1)\n\t\t: (host.split(':')[0] ?? '')\n\treturn hostname !== 'localhost' && hostname !== '127.0.0.1' && hostname !== '[::1]'\n}\n\n// === Terminal manager wire seams (transport-neutral, no http dependency)\n\n/**\n * Serializes a parked {@link PendingForm} into a `pending` {@link WireEvent}, whose frame `id` is\n * the form's own id.\n *\n * @param form - The parked form's wire-safe record\n * @returns The `pending` frame — the JSON-stringified record as `data`, and the form's own `id`\n */\nexport function serializePending(form: PendingForm): WireEvent {\n\treturn { event: 'pending', data: JSON.stringify(form), id: form.id }\n}\n\n/**\n * Serializes a parked form's expiry or release into an `expire` {@link WireEvent}, whose `data` is\n * the JSON `{ id }` payload.\n *\n * @param id - The id of the parked form that expired or was released\n * @returns The `expire` frame, carrying the JSON-stringified `{ id }` payload as `data`\n */\nexport function serializeExpire(id: string): WireEvent {\n\treturn { event: 'expire', data: JSON.stringify({ id }) }\n}\n\n/**\n * Serializes the `destroy` {@link WireEvent} a broker or manager sends when it is going away, which\n * carries no payload.\n *\n * @returns The `destroy` frame, whose `data` is empty because the signal carries no payload\n */\nexport function serializeDestroy(): WireEvent {\n\treturn { event: 'destroy', data: '' }\n}\n","import type {\n\tAnswerError,\n\tParkedForm,\n\tParkRequest,\n\tPendingForm,\n\tPromptEventMap,\n\tPromptInterface,\n\tPromptOptions,\n\tTimerHandler,\n} from './types.js'\nimport type { Result } from '@orkestrel/contract'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { FieldError, FormInterface, FormResult, FormValues } from '@orkestrel/form'\nimport { DEFAULT_PROMPT_TIMEOUT_MS } from './constants.js'\nimport { TerminalError } from './errors.js'\nimport { defaultTimer } from './helpers.js'\nimport { attempt, isArray, isString } from '@orkestrel/contract'\nimport { Emitter } from '@orkestrel/emitter'\nimport { isFieldError, isFormError, serializeForm } from '@orkestrel/form'\n\n/**\n * Implements the headless form broker. It parks live forms, exposes their serialized schemas,\n * applies remote answers to the authoritative form, and abandons a parked form on timeout, release,\n * or teardown.\n *\n * @remarks\n * A parked record carries one call to `serializeForm`. A failed fill or submit leaves the record\n * parked for another answer. A successful submit settles the form once, emits `answer`, and removes\n * the record. Timeout, `stop`, and teardown abandon unsettled forms through their own `destroy`\n * method.\n *\n * @example\n * ```ts\n * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })\n * const prompt = createPrompt()\n * const id = prompt.park(form)\n * prompt.answer(id, { name: 'Ada' })\n * await form.answer // { name: 'Ada' }\n * ```\n */\nexport class Prompt implements PromptInterface {\n\treadonly #timeout: number\n\treadonly #timer: TimerHandler\n\treadonly #cap: number | undefined\n\treadonly #parked = new Map<string, ParkedForm>()\n\treadonly #emitter: Emitter<PromptEventMap>\n\t#destroyed = false\n\n\tconstructor(options?: PromptOptions) {\n\t\tthis.#timeout = options?.timeout ?? DEFAULT_PROMPT_TIMEOUT_MS\n\t\tthis.#timer = options?.timer ?? defaultTimer\n\t\tthis.#cap = options?.cap\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options?.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options?.error !== undefined ? { error: options.error } : {}),\n\t\t})\n\t}\n\n\tget emitter(): EmitterInterface<PromptEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget count(): number {\n\t\treturn this.#parked.size\n\t}\n\n\tpending(): readonly PendingForm[]\n\tpending(id: string): PendingForm | undefined\n\tpending(id?: string): readonly PendingForm[] | PendingForm | undefined {\n\t\tif (id !== undefined) return this.#parked.get(id)?.pending\n\t\tconst forms: PendingForm[] = []\n\t\tfor (const parked of this.#parked.values()) forms.push(parked.pending)\n\t\treturn forms\n\t}\n\n\tpark(form: FormInterface, request?: ParkRequest): string {\n\t\tif (this.#destroyed) {\n\t\t\tform.destroy()\n\t\t\tthrow new TerminalError('EXPIRE', 'The broker has been destroyed')\n\t\t}\n\t\tif (this.#cap !== undefined && this.#parked.size >= this.#cap) {\n\t\t\tform.destroy()\n\t\t\tthrow new TerminalError('LIMIT', `The parked-form cap (${String(this.#cap)}) was reached`, {\n\t\t\t\tcap: this.#cap,\n\t\t\t})\n\t\t}\n\n\t\tconst id = crypto.randomUUID()\n\t\tconst pending: PendingForm = {\n\t\t\tid,\n\t\t\tschema: serializeForm(form.schema),\n\t\t\tstatus: 'pending',\n\t\t\ttime: Date.now(),\n\t\t\t...(request?.from !== undefined ? { from: request.from } : {}),\n\t\t\t...(request?.to !== undefined ? { to: request.to } : {}),\n\t\t}\n\t\tconst cancel = this.#timer(() => this.#expire(id), this.#timeout)\n\t\tthis.#parked.set(id, { form, pending, cancel })\n\t\tthis.#emitter.emit('pending', pending)\n\t\treturn id\n\t}\n\n\tanswer(id: string, values: FormValues): Result<FormValues, AnswerError> {\n\t\tconst outcome = attempt(() => this.#answer(id, values))\n\t\tif (outcome.success) return outcome.value\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\treason: 'rejected',\n\t\t\t\terrors: [{ field: 'form', message: 'The form rejected the answer' }],\n\t\t\t},\n\t\t}\n\t}\n\n\tstop(ids: readonly string[]): boolean\n\tstop(id: string): boolean\n\tstop(): void\n\tstop(ids?: string | readonly string[]): boolean | void {\n\t\tif (ids === undefined) {\n\t\t\tfor (const id of [...this.#parked.keys()]) this.#expire(id)\n\t\t\treturn\n\t\t}\n\t\tif (isArray(ids)) {\n\t\t\tlet stopped = true\n\t\t\tfor (const id of ids) {\n\t\t\t\tconst parked = this.#parked.get(id)\n\t\t\t\tif (parked === undefined || parked.pending.status !== 'pending') stopped = false\n\t\t\t}\n\t\t\tfor (const id of ids) this.#expire(id)\n\t\t\treturn stopped\n\t\t}\n\t\treturn this.#expire(ids)\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tfor (const id of [...this.#parked.keys()]) this.#expire(id)\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#answer(id: string, values: FormValues): Result<FormValues, AnswerError> {\n\t\tconst parked = this.#parked.get(id)\n\t\tif (parked === undefined || parked.pending.status !== 'pending') {\n\t\t\treturn { success: false, error: { reason: 'unknown' } }\n\t\t}\n\n\t\tconst outcome = attempt(() => this.#submit(parked.form, values))\n\t\tif (!outcome.success) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { reason: 'rejected', errors: this.#errors(outcome.error, parked.form) },\n\t\t\t}\n\t\t}\n\t\tif (!outcome.value.success) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { reason: 'rejected', errors: outcome.value.error },\n\t\t\t}\n\t\t}\n\n\t\tparked.cancel()\n\t\tconst answered: ParkedForm = {\n\t\t\t...parked,\n\t\t\tpending: { ...parked.pending, status: 'answered' },\n\t\t}\n\t\tthis.#parked.set(id, answered)\n\t\tthis.#emitter.emit('answer', id, outcome.value.value)\n\t\tthis.#parked.delete(id)\n\t\treturn outcome.value\n\t}\n\n\t#submit(form: FormInterface, values: FormValues): FormResult {\n\t\tfor (const [field, value] of Object.entries(values)) {\n\t\t\tconst outcome = attempt(() => form.fill(field, value))\n\t\t\tif (!outcome.success) {\n\t\t\t\treturn { success: false, error: this.#errors(outcome.error, form, field) }\n\t\t\t}\n\t\t}\n\t\treturn form.submit()\n\t}\n\n\t#errors(error: unknown, form: FormInterface, field?: string): readonly FieldError[] {\n\t\tif (isFormError(error)) {\n\t\t\tif (isFieldError(error.context)) return [error.context]\n\t\t\tconst named = error.context?.field\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tfield: isString(named) ? named : this.#field(form, field),\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t},\n\t\t\t]\n\t\t}\n\t\treturn [\n\t\t\t{\n\t\t\t\tfield: this.#field(form, field),\n\t\t\t\tmessage: 'The form rejected the answer',\n\t\t\t},\n\t\t]\n\t}\n\n\t#field(form: FormInterface, field?: string): string {\n\t\treturn field ?? form.schema.fields[0]?.name ?? form.schema.name ?? 'form'\n\t}\n\n\t#expire(id: string): boolean {\n\t\tconst parked = this.#parked.get(id)\n\t\tif (parked === undefined || parked.pending.status !== 'pending') return false\n\t\tparked.cancel()\n\t\tconst expired: ParkedForm = {\n\t\t\t...parked,\n\t\t\tpending: { ...parked.pending, status: 'expired' },\n\t\t}\n\t\tthis.#parked.set(id, expired)\n\t\tparked.form.destroy()\n\t\tthis.#emitter.emit('expire', id)\n\t\tthis.#parked.delete(id)\n\t\treturn true\n\t}\n}\n","import type {\n\tFetchHandler,\n\tPromptClientEventMap,\n\tPromptClientInterface,\n\tPromptClientOptions,\n\tTimerCancelFunction,\n\tTimerHandler,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { FieldError, FormInterface, FormSchema, FormValues } from '@orkestrel/form'\nimport type { SSEEvent } from '@orkestrel/sse'\nimport {\n\tACCEPT_EVENT_STREAM,\n\tDEFAULT_RECONNECT_DELAY_MS,\n\tHEADER_TOKEN,\n\tSSE_BUFFER_LIMIT,\n\tSSE_EVENTS,\n} from './constants.js'\nimport {\n\tdefaultTimer,\n\tglobalFetch,\n\tisAbortError,\n\tisInsecureRemote,\n\tsanitizeDisplayText,\n\tsanitizeSchema,\n} from './helpers.js'\nimport { isPendingForm } from './validators.js'\nimport { arrayOf, isBoolean, isRecord, isString, parseJSON } from '@orkestrel/contract'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createForm, isFieldError, parseForm } from '@orkestrel/form'\nimport { createSSEParser } from '@orkestrel/sse'\n\n/**\n * Implements the SSE form bridge. It ingests serialized forms from a remote broker without waiting\n * on a render, drives one form at a time through a local terminal, posts each answer back, and asks\n * again when the authoritative form refuses one.\n *\n * @remarks\n * - **Connect + reconnect.** {@link connect} opens the SSE stream and reconnects after a transport\n * drop with the injected backoff unless reconnect is disabled, the client was destroyed, or\n * {@link disconnect} deliberately stopped it.\n * - **Ingest + render.** Each `pending` envelope passes through `isPendingForm`, Form's `parseForm`,\n * and terminal's `sanitizeSchema`, then enters a serial render queue. The SSE reader never awaits\n * that queue, so `expire` and `destroy` remain live while a person is answering.\n * - **Safe local form.** The rendering copy omits every wire `pattern`, because Form compiles a\n * pattern during local evaluation. The broker's parked form retains it and remains authoritative.\n * - **Refusal retry.** A structured `rejected` response seeds a new rendering form with the values\n * the refused attempt submitted, applies every {@link FieldError} through `invalidate`, and asks\n * again. No retry counter truncates the loop; acceptance, expiry, and the broker's own teardown\n * are its bounds.\n * - **Replay safety.** A replayed id is skipped while it is queued, rendering, or posting. Once an\n * attempt ends, a later delivery of that id may be rendered again.\n *\n * @example\n * ```ts\n * const client = createPromptClient({\n * \turl: 'http://localhost:3001/prompts',\n * \tterminal: createTerminal(),\n * })\n * await client.connect()\n * ```\n */\nexport class PromptClient implements PromptClientInterface {\n\treadonly url: string\n\treadonly #terminal: PromptClientOptions['terminal']\n\treadonly #token: string | undefined\n\treadonly #reconnect: boolean\n\treadonly #delay: number\n\treadonly #fetch: FetchHandler\n\treadonly #timer: TimerHandler\n\treadonly #emitter: Emitter<PromptClientEventMap>\n\t#controller: AbortController | undefined\n\t#backoff: TimerCancelFunction | undefined\n\t#wake: (() => void) | undefined\n\t#connecting = false\n\t#connected = false\n\t#destroyed = false\n\t#draining = false\n\t#warnedInsecureToken = false\n\treadonly #seen = new Set<string>()\n\treadonly #queue = new Map<string, FormSchema>()\n\t#active:\n\t\t| { readonly id: string; readonly form: FormInterface; readonly stopped: boolean }\n\t\t| undefined\n\n\tconstructor(options: PromptClientOptions) {\n\t\tthis.url = options.url\n\t\tthis.#terminal = options.terminal\n\t\tthis.#token = options.token\n\t\tthis.#reconnect = options.reconnect ?? true\n\t\tthis.#delay = options.delay ?? DEFAULT_RECONNECT_DELAY_MS\n\t\tthis.#fetch = options.fetch ?? globalFetch\n\t\tthis.#timer = options.timer ?? defaultTimer\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options.error !== undefined ? { error: options.error } : {}),\n\t\t})\n\t}\n\n\tget emitter(): EmitterInterface<PromptClientEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget connected(): boolean {\n\t\treturn this.#connected\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.#destroyed || this.#connecting) return\n\t\tthis.#connecting = true\n\t\twhile (this.#connecting && !this.#destroyed) {\n\t\t\ttry {\n\t\t\t\tawait this.#stream()\n\t\t\t} catch (error) {\n\t\t\t\tthis.#markDisconnected()\n\t\t\t\tif (this.#destroyed || isAbortError(error)) return\n\t\t\t\tthis.#emitter.emit('error', error)\n\t\t\t}\n\t\t\tif (!this.#reconnect || !this.#connecting || this.#destroyed) return\n\t\t\tawait this.#wait(this.#delay)\n\t\t}\n\t}\n\n\tdisconnect(): void {\n\t\tthis.#connecting = false\n\t\tthis.#controller?.abort()\n\t\tthis.#controller = undefined\n\t\tthis.#backoff?.()\n\t\tthis.#backoff = undefined\n\t\tconst wake = this.#wake\n\t\tthis.#wake = undefined\n\t\twake?.()\n\t\tthis.#markDisconnected()\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tthis.disconnect()\n\t\tthis.#interrupt()\n\t\tthis.#emitter.destroy()\n\t}\n\n\tasync #stream(): Promise<void> {\n\t\tif (this.#token !== undefined && isInsecureRemote(this.url) && !this.#warnedInsecureToken) {\n\t\t\tthis.#warnedInsecureToken = true\n\t\t\tthis.#emitter.emit(\n\t\t\t\t'error',\n\t\t\t\tnew Error('auth token sent as cleartext over insecure http; use https'),\n\t\t\t)\n\t\t}\n\t\tconst controller = new AbortController()\n\t\tthis.#controller = controller\n\t\tconst response = await this.#fetch(this.url, {\n\t\t\theaders: this.#headers({ Accept: ACCEPT_EVENT_STREAM }),\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\tif (!response.ok) throw new Error(`broker returned ${String(response.status)}`)\n\t\tconst body = response.body\n\t\tif (body === null) throw new Error('broker sent no stream')\n\n\t\tthis.#connected = true\n\t\tthis.#emitter.emit('connect')\n\n\t\tconst reader = body.getReader()\n\t\tconst decoder = new TextDecoder()\n\t\tconst parser = createSSEParser({ limit: SSE_BUFFER_LIMIT })\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read()\n\t\t\t\tif (done) break\n\t\t\t\tfor (const event of parser.parse(decoder.decode(value, { stream: true }))) {\n\t\t\t\t\tthis.#handle(event)\n\t\t\t\t\tif (!this.#connecting) break\n\t\t\t\t}\n\t\t\t\tif (!this.#connecting) break\n\t\t\t}\n\t\t} finally {\n\t\t\treader.releaseLock()\n\t\t}\n\t\tthis.#markDisconnected()\n\t}\n\n\t#handle(event: SSEEvent): void {\n\t\tif (event.event === SSE_EVENTS.pending) {\n\t\t\tconst parsed = parseJSON(event.data)\n\t\t\tif (!isPendingForm(parsed) || this.#seen.has(parsed.id)) return\n\t\t\tconst schema = parseForm(parsed.schema)\n\t\t\tif (schema === undefined) {\n\t\t\t\tthis.#emitter.emit(\n\t\t\t\t\t'error',\n\t\t\t\t\tnew Error(`broker sent an invalid form schema for ${parsed.id}`),\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#seen.add(parsed.id)\n\t\t\tthis.#queue.set(parsed.id, sanitizeSchema(schema))\n\t\t\tvoid this.#drain()\n\t\t\treturn\n\t\t}\n\t\tif (event.event === SSE_EVENTS.expire) {\n\t\t\tconst parsed = parseJSON(event.data)\n\t\t\tif (isRecord(parsed) && isString(parsed.id)) this.#expire(parsed.id)\n\t\t\treturn\n\t\t}\n\t\tif (event.event === SSE_EVENTS.destroy) {\n\t\t\tthis.disconnect()\n\t\t\tthis.#interrupt()\n\t\t}\n\t}\n\n\tasync #drain(): Promise<void> {\n\t\tif (this.#draining) return\n\t\tthis.#draining = true\n\t\ttry {\n\t\t\twhile (!this.#destroyed) {\n\t\t\t\tlet queued: readonly [string, FormSchema] | undefined\n\t\t\t\tfor (const entry of this.#queue) {\n\t\t\t\t\tqueued = entry\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif (queued === undefined) return\n\t\t\t\tconst [id, schema] = queued\n\t\t\t\tthis.#queue.delete(id)\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#render(id, schema)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst active = this.#active\n\t\t\t\t\tif (active?.id !== id || !active.stopped) this.#emitter.emit('error', error)\n\t\t\t\t} finally {\n\t\t\t\t\tconst active = this.#active\n\t\t\t\t\tif (active?.id === id) {\n\t\t\t\t\t\tactive.form.destroy()\n\t\t\t\t\t\tthis.#active = undefined\n\t\t\t\t\t}\n\t\t\t\t\tif (!this.#queue.has(id)) this.#seen.delete(id)\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#draining = false\n\t\t}\n\t}\n\n\tasync #render(id: string, schema: FormSchema): Promise<void> {\n\t\tlet values: FormValues | undefined\n\t\tlet errors: readonly FieldError[] = []\n\t\twhile (this.#seen.has(id) && !this.#destroyed) {\n\t\t\tconst form = this.#createRenderingForm(schema, values)\n\t\t\tthis.#active = { id, form, stopped: false }\n\t\t\tfor (const error of errors) form.invalidate(error.field, sanitizeDisplayText(error.message))\n\t\t\tconst submitted = await this.#terminal.ask(form)\n\t\t\tconst active = this.#active\n\t\t\tif (active?.id !== id || active.stopped) return\n\t\t\tconst rejected = await this.#post(id, submitted)\n\t\t\tconst posted = this.#active\n\t\t\tif (posted?.id !== id || posted.stopped || rejected === undefined) return\n\t\t\tvalues = submitted\n\t\t\terrors = rejected\n\t\t\tform.destroy()\n\t\t\tthis.#active = undefined\n\t\t}\n\t}\n\n\t#createRenderingForm(schema: FormSchema, values?: FormValues): FormInterface {\n\t\tconst fields = schema.fields.map((field) => {\n\t\t\tif (field.rule?.pattern === undefined) return field\n\t\t\tconst { pattern: _pattern, ...rule } = field.rule\n\t\t\treturn { ...field, rule }\n\t\t})\n\t\tconst form = createForm({ ...schema, fields }, values === undefined ? undefined : { values })\n\t\tvoid form.answer.catch(() => undefined)\n\t\treturn form\n\t}\n\n\tasync #post(id: string, values: FormValues): Promise<readonly FieldError[] | undefined> {\n\t\tconst response = await this.#fetch(this.url, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: this.#headers({ 'Content-Type': 'application/json' }),\n\t\t\tbody: JSON.stringify({ id, values }),\n\t\t})\n\t\tif (!response.ok) this.#emitter.emit('error', new Error(`broker rejected answer ${id}`))\n\t\tconst parsed = parseJSON(await response.text())\n\t\tif (!isRecord(parsed) || !isBoolean(parsed.success)) {\n\t\t\tthis.#emitter.emit('error', new Error(`broker returned an invalid answer result for ${id}`))\n\t\t\treturn undefined\n\t\t}\n\t\tif (parsed.success) return undefined\n\t\tconst error = parsed.error\n\t\tif (!isRecord(error) || !isString(error.reason)) {\n\t\t\tthis.#emitter.emit('error', new Error(`broker returned an invalid answer error for ${id}`))\n\t\t\treturn undefined\n\t\t}\n\t\tif (error.reason === 'unknown') return undefined\n\t\tif (error.reason === 'rejected' && arrayOf(isFieldError)(error.errors)) return error.errors\n\t\tthis.#emitter.emit('error', new Error(`broker returned an invalid answer refusal for ${id}`))\n\t\treturn undefined\n\t}\n\n\t#expire(id: string): void {\n\t\tconst active = this.#active\n\t\tif (active?.id === id) {\n\t\t\tthis.#active = { ...active, stopped: true }\n\t\t\tthis.#seen.delete(id)\n\t\t\tactive.form.destroy()\n\t\t} else if (this.#queue.delete(id)) this.#seen.delete(id)\n\t\tthis.#emitter.emit('expire', id)\n\t}\n\n\t#interrupt(): void {\n\t\tthis.#queue.clear()\n\t\tthis.#seen.clear()\n\t\tconst active = this.#active\n\t\tif (active === undefined) return\n\t\tthis.#active = { ...active, stopped: true }\n\t\tactive.form.destroy()\n\t}\n\n\t#markDisconnected(): void {\n\t\tif (!this.#connected) return\n\t\tthis.#connected = false\n\t\tthis.#emitter.emit('disconnect')\n\t}\n\n\t#headers(base: Record<string, string>): Record<string, string> {\n\t\tif (this.#token !== undefined) return { ...base, [HEADER_TOKEN]: this.#token }\n\t\treturn { ...base }\n\t}\n\n\t#wait(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst settle = this.#createSettler(resolve)\n\t\t\tthis.#wake = settle\n\t\t\tthis.#backoff = this.#timer(settle, ms)\n\t\t})\n\t}\n\n\t#createSettler(resolve: () => void): () => void {\n\t\treturn () => {\n\t\t\tthis.#backoff = undefined\n\t\t\tthis.#wake = undefined\n\t\t\tresolve()\n\t\t}\n\t}\n}\n","import type {\n\tPendingForm,\n\tPromptInterface,\n\tPromptOptions,\n\tTerminalAnswerError,\n\tTerminalManagerEventMap,\n\tTerminalManagerInterface,\n\tTerminalManagerOptions,\n\tTerminalSnapshot,\n\tTerminalStoreInterface,\n\tTimerHandler,\n} from './types.js'\nimport type { Result } from '@orkestrel/contract'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { FormInterface, FormValues } from '@orkestrel/form'\nimport { TerminalError } from './errors.js'\nimport { createPrompt } from './factories.js'\nimport { Emitter } from '@orkestrel/emitter'\nimport { isArray } from '@orkestrel/contract'\n\n/**\n * Registers named {@link PromptInterface} brokers, one per endpoint, so several parties can `ask`\n * forms of each other by name with a `from` → `to` attribution edge on every parked form, and\n * refuses `DEADLOCK` on a transitive cycle across every in-flight ask.\n *\n * @remarks\n * - **Registry.** `add(name, options?)` mints (or, if `name` is already mounted, returns the\n * existing broker unchanged — idempotent, never clobbers a live/parked endpoint). Every mounted\n * broker's `pending` / `answer` / `expire` events are re-emitted on the manager, attributed by\n * `name`.\n * - **`ask`.** The target must already be mounted through {@link add} — `ask` never auto-adds it;\n * rejects `TARGET` for an unknown `to` (listing the known names). Rejects `DEADLOCK` when parking\n * `from → to` would close a cycle over the current in-flight edge set (walked transitively);\n * otherwise parks the caller's live form through the target's broker and returns that form's own\n * `answer` promise. Edge cleanup never alters the value or rejection the caller observes.\n * - **Durable open / save.** `open(name)` restores an EMPTY broker from the `store` (parked\n * Promises are process-bound and never resurrected); `save(name)` persists the endpoint's\n * configured `timeout`.\n * - **Removal.** `remove` drops one endpoint, a batch (the array overload declared first), or every\n * endpoint when called without an argument. It destroys each broker, which expires every form\n * still parked on it. `destroy` is idempotent.\n *\n * @example\n * ```ts\n * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })\n * const manager = new TerminalManager()\n * manager.add('agent')\n * const answer = manager.ask('user', 'agent', form)\n * manager.answer('agent', manager.pending('agent')[0].id, { name: 'Ada' })\n * await answer // { name: 'Ada' }\n * ```\n */\nexport class TerminalManager implements TerminalManagerInterface {\n\treadonly #terminals = new Map<string, PromptInterface>()\n\treadonly #config = new Map<string, PromptOptions>()\n\t// The handlers subscribed on a mounted broker's emitter — kept so `remove` can `off` them\n\t// explicitly (on top of the broker's own `destroy`, which already renders its emitter inert).\n\treadonly #listeners = new Map<\n\t\tstring,\n\t\t{\n\t\t\treadonly pending: (form: PendingForm) => void\n\t\t\treadonly answer: (id: string, values: FormValues) => void\n\t\t\treadonly expire: (id: string) => void\n\t\t}\n\t>()\n\t// In-flight `ask` edges, keyed by the parked form's id — the deadlock graph. `from` asked\n\t// `to`; cleanup on settle (answer / expire / destroy / remove) removes EXACTLY the edge that\n\t// call created.\n\treadonly #edges = new Map<string, { readonly from: string; readonly to: string }>()\n\treadonly #store: TerminalStoreInterface | undefined\n\treadonly #timeout: number | undefined\n\treadonly #timer: TimerHandler | undefined\n\treadonly #cap: number | undefined\n\treadonly #emitter: Emitter<TerminalManagerEventMap>\n\t#destroyed = false\n\n\tconstructor(options?: TerminalManagerOptions) {\n\t\tthis.#store = options?.store\n\t\tthis.#timeout = options?.timeout\n\t\tthis.#timer = options?.timer\n\t\tthis.#cap = options?.cap\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options?.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options?.error !== undefined ? { error: options.error } : {}),\n\t\t})\n\t}\n\n\tget emitter(): EmitterInterface<TerminalManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget count(): number {\n\t\treturn this.#terminals.size\n\t}\n\n\t// === Accessors\n\n\tterminal(name: string): PromptInterface | undefined {\n\t\treturn this.#terminals.get(name)\n\t}\n\n\tterminals(): readonly PromptInterface[] {\n\t\treturn [...this.#terminals.values()]\n\t}\n\n\t// === Registry\n\n\tadd(name: string, options?: PromptOptions): PromptInterface {\n\t\tif (this.#destroyed) throw new TerminalError('DESTROYED', 'manager destroyed')\n\t\tconst existing = this.#terminals.get(name)\n\t\tif (existing !== undefined) return existing\n\t\tconst timeout = options?.timeout ?? this.#timeout\n\t\tconst timer = options?.timer ?? this.#timer\n\t\tconst cap = options?.cap ?? this.#cap\n\t\tconst promptOptions: PromptOptions = {\n\t\t\t...(options?.on !== undefined ? { on: options.on } : {}),\n\t\t\t...(options?.error !== undefined ? { error: options.error } : {}),\n\t\t\t...(timeout !== undefined ? { timeout } : {}),\n\t\t\t...(timer !== undefined ? { timer } : {}),\n\t\t\t...(cap !== undefined ? { cap } : {}),\n\t\t}\n\t\tconst broker = createPrompt(promptOptions)\n\t\tconst listeners = {\n\t\t\tpending: this.#createPendingListener(),\n\t\t\tanswer: this.#createAnswerListener(name),\n\t\t\texpire: this.#createExpireListener(name),\n\t\t}\n\t\tbroker.emitter.on('pending', listeners.pending)\n\t\tbroker.emitter.on('answer', listeners.answer)\n\t\tbroker.emitter.on('expire', listeners.expire)\n\t\tthis.#terminals.set(name, broker)\n\t\tthis.#config.set(name, { ...options })\n\t\tthis.#listeners.set(name, listeners)\n\t\treturn broker\n\t}\n\n\t// === Ask\n\n\task(from: string, to: string, form: FormInterface): Promise<FormValues> {\n\t\tconst broker = this.#terminals.get(to)\n\t\tif (broker === undefined) {\n\t\t\tconst known = [...this.#terminals.keys()]\n\t\t\treturn Promise.reject(\n\t\t\t\tnew TerminalError(\n\t\t\t\t\t'TARGET',\n\t\t\t\t\t`unknown terminal '${to}' (known: ${known.length > 0 ? known.join(', ') : 'none'})`,\n\t\t\t\t\t{ to, known },\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tconst cycle = this.#findCycle(from, to)\n\t\tif (cycle !== undefined) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew TerminalError(\n\t\t\t\t\t'DEADLOCK',\n\t\t\t\t\t`ask ${from} -> ${to} would deadlock: ${cycle.join(' -> ')}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tfrom,\n\t\t\t\t\t\tto,\n\t\t\t\t\t\tpath: cycle,\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\t\tconst id = broker.park(form, { from, to })\n\t\tif (broker.pending(id) !== undefined) {\n\t\t\tthis.#edges.set(id, { from, to })\n\t\t\tform.answer.then(this.#createEdgeClear(id), this.#createEdgeClear(id))\n\t\t}\n\t\treturn form.answer\n\t}\n\n\t// === Pending accessors\n\n\tpending(): readonly PendingForm[]\n\tpending(to: string): readonly PendingForm[]\n\tpending(to?: string): readonly PendingForm[] {\n\t\tif (to !== undefined) {\n\t\t\tconst broker = this.#terminals.get(to)\n\t\t\treturn broker === undefined ? [] : broker.pending()\n\t\t}\n\t\tconst result: PendingForm[] = []\n\t\tfor (const broker of this.#terminals.values()) result.push(...broker.pending())\n\t\treturn result\n\t}\n\n\t// === Answer\n\n\tanswer(to: string, id: string, values: FormValues): Result<FormValues, TerminalAnswerError> {\n\t\tconst broker = this.#terminals.get(to)\n\t\tif (broker === undefined) return { success: false, error: { reason: 'target' } }\n\t\tconst result = broker.answer(id, values)\n\t\tif (result.success) this.#edges.delete(id)\n\t\treturn result\n\t}\n\n\t// === Durable open / save\n\n\tasync open(name: string): Promise<PromptInterface | undefined> {\n\t\tif (this.#destroyed) throw new TerminalError('DESTROYED', 'manager destroyed')\n\t\tconst existing = this.#terminals.get(name)\n\t\tif (existing !== undefined) return existing\n\t\tif (this.#store === undefined) return undefined\n\t\tconst snapshot = await this.#store.get(name)\n\t\tif (this.#destroyed) throw new TerminalError('DESTROYED', 'manager destroyed')\n\t\tif (snapshot === undefined) return undefined\n\t\treturn this.add(name, snapshot.timeout !== undefined ? { timeout: snapshot.timeout } : {})\n\t}\n\n\tasync save(name: string): Promise<boolean> {\n\t\tconst broker = this.#terminals.get(name)\n\t\tif (this.#store === undefined || broker === undefined) return false\n\t\tconst config = this.#config.get(name)\n\t\tconst snapshot: TerminalSnapshot = {\n\t\t\tid: name,\n\t\t\t...(config?.timeout !== undefined ? { timeout: config.timeout } : {}),\n\t\t}\n\t\tawait this.#store.set(snapshot)\n\t\treturn true\n\t}\n\n\t// === Removal (the array overload declared FIRST)\n\n\tremove(names: readonly string[]): boolean\n\tremove(name: string): boolean\n\tremove(): void\n\tremove(names?: string | readonly string[]): boolean | void {\n\t\tif (names === undefined) {\n\t\t\tfor (const name of [...this.#terminals.keys()]) this.#removeOne(name)\n\t\t\treturn\n\t\t}\n\t\tif (isArray(names)) {\n\t\t\tlet removed = true\n\t\t\tfor (const name of names) {\n\t\t\t\tif (!this.#removeOne(name)) removed = false\n\t\t\t}\n\t\t\treturn removed\n\t\t}\n\t\treturn this.#removeOne(names)\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\tthis.remove()\n\t\tthis.#edges.clear()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t// === Private helpers\n\n\t#createPendingListener(): (form: PendingForm) => void {\n\t\treturn (form) => this.#emitter.emit('pending', form)\n\t}\n\n\t#createAnswerListener(name: string): (id: string, values: FormValues) => void {\n\t\treturn (id, values) => {\n\t\t\tthis.#edges.delete(id)\n\t\t\tthis.#emitter.emit('answer', name, id, values)\n\t\t}\n\t}\n\n\t#createExpireListener(name: string): (id: string) => void {\n\t\treturn (id) => {\n\t\t\tthis.#edges.delete(id)\n\t\t\tthis.#emitter.emit('expire', name, id)\n\t\t}\n\t}\n\n\t#createEdgeClear(id: string): () => void {\n\t\treturn () => {\n\t\t\tthis.#edges.delete(id)\n\t\t}\n\t}\n\n\t// Drop one endpoint: destroy its broker FIRST (its expire loop re-emits `expire` for every\n\t// still-parked form through the manager's listeners — still attached at this point, so\n\t// each settles on the manager emitter too), THEN unsubscribe the manager's listeners and\n\t// remove it from every registry map. `false` when `name` was not mounted.\n\t#removeOne(name: string): boolean {\n\t\tconst broker = this.#terminals.get(name)\n\t\tif (broker === undefined) return false\n\t\tbroker.destroy()\n\t\tconst listeners = this.#listeners.get(name)\n\t\tif (listeners !== undefined) {\n\t\t\tbroker.emitter.off('pending', listeners.pending)\n\t\t\tbroker.emitter.off('answer', listeners.answer)\n\t\t\tbroker.emitter.off('expire', listeners.expire)\n\t\t}\n\t\tthis.#terminals.delete(name)\n\t\tthis.#config.delete(name)\n\t\tthis.#listeners.delete(name)\n\t\tthis.#clearEdges(name)\n\t\treturn true\n\t}\n\n\t#clearEdges(to: string): void {\n\t\tfor (const [id, edge] of this.#edges) {\n\t\t\tif (edge.to === to) this.#edges.delete(id)\n\t\t}\n\t}\n\n\t// Walk the in-flight edge graph forward from `to`, looking for `from` — a hit means parking\n\t// `from -> to` would close a cycle. Returns the closing cycle path (`from` first and last),\n\t// or `undefined` when no cycle would form.\n\t#findCycle(from: string, to: string): readonly string[] | undefined {\n\t\tif (from === to) return [from, to]\n\t\tconst visited = new Set<string>([to])\n\t\tconst queue: Array<readonly string[]> = [[to]]\n\t\twhile (queue.length > 0) {\n\t\t\tconst path = queue.shift()\n\t\t\tif (path === undefined) break\n\t\t\tconst last = path[path.length - 1]\n\t\t\tif (last === undefined) continue\n\t\t\tfor (const edge of this.#edges.values()) {\n\t\t\t\tif (edge.from !== last) continue\n\t\t\t\tif (edge.to === from) return [from, ...path, from]\n\t\t\t\tif (visited.has(edge.to)) continue\n\t\t\t\tvisited.add(edge.to)\n\t\t\t\tqueue.push([...path, edge.to])\n\t\t\t}\n\t\t}\n\t\treturn undefined\n\t}\n}\n","import type { TerminalSnapshot, TerminalStoreInterface } from '../types.js'\n\n/**\n * Implements the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of\n * {@link TerminalSnapshot} records keyed by endpoint id, the default store\n * {@link import('../factories.js').createMemoryTerminalStore} builds and the exact twin of\n * {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore}. It carries no idle expiry and\n * no eviction.\n *\n * @remarks\n * A plain `Map<string, TerminalSnapshot>` — the snapshot is already pure, self-contained CONFIG-only\n * JSON, so the memory tier needs no encoding. There is NO idle-TTL and NO\n * eviction: a persisted config lives until an explicit `delete`. A durable backend (JSON / SQLite /\n * IndexedDB) swaps in through the SAME interface without touching the manager — its\n * driver-pluggable twin is {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore} (the\n * snapshot as one opaque JSON column).\n *\n * - **`get` resolves the persisted snapshot for an id**, or `undefined` if none is stored.\n * - **`set` inserts / replaces under the snapshot's OWN `id`** (no separate id param).\n * - **`delete` drops a snapshot by id**; an absent id is a no-op (no throw).\n *\n * The public surface is EXACTLY `get` / `set` / `delete` — no extra members, so the class and\n * {@link TerminalStoreInterface} carry the same methods. Hydration is a caller concern: `open` always\n * restores an EMPTY broker — parked Promises are process-bound and never resurrected.\n *\n * @example\n * ```ts\n * import { createMemoryTerminalStore } from '@orkestrel/terminal'\n *\n * const store = createMemoryTerminalStore()\n * await store.set({ id: 'shell', timeout: 5000 }) // persist a config\n * const snapshot = await store.get('shell')\n * await store.delete('shell') // drop it\n * ```\n */\nexport class MemoryTerminalStore implements TerminalStoreInterface {\n\treadonly #snapshots = new Map<string, TerminalSnapshot>()\n\n\t/**\n\t * Resolves the persisted snapshot for `id`.\n\t *\n\t * @param id - The endpoint name the snapshot is keyed by\n\t * @returns The stored snapshot, or `undefined` when none is held\n\t */\n\tget(id: string): Promise<TerminalSnapshot | undefined> {\n\t\treturn Promise.resolve(this.#snapshots.get(id))\n\t}\n\n\t/**\n\t * Inserts or replaces under the snapshot's OWN `id` (no separate id param).\n\t *\n\t * @param snapshot - The config snapshot to persist, carrying its own `id`\n\t * @returns A promise that settles once the snapshot is held\n\t */\n\tset(snapshot: TerminalSnapshot): Promise<void> {\n\t\tthis.#snapshots.set(snapshot.id, snapshot)\n\t\treturn Promise.resolve()\n\t}\n\n\t/**\n\t * Drops a snapshot by id; an absent id is a no-op (no throw).\n\t *\n\t * @param id - The endpoint name to drop\n\t * @returns A promise that settles once the snapshot is gone\n\t */\n\tdelete(id: string): Promise<void> {\n\t\tthis.#snapshots.delete(id)\n\t\treturn Promise.resolve()\n\t}\n}\n","import type { TerminalSnapshot, TerminalSnapshotRow, TerminalStoreInterface } from '../types.js'\nimport type { TableInterface } from '@orkestrel/database'\nimport { isTerminalSnapshot } from '../validators.js'\n\n/**\n * Implements a {@link TerminalStoreInterface} backed by one table of the `databases` layer — an\n * endpoint's durable config state is a row, so persistence reduces to keyed point-access (`get` /\n * `set` / `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`\n * {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}. A stored `snapshot` is narrowed\n * with {@link import('../validators.js').isTerminalSnapshot} on read.\n *\n * @remarks\n * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,\n * JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /\n * IndexedDB backend swaps in WITHOUT touching the manager — the same seam as\n * {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}. The driver defaults to memory\n * ({@link import('../factories.js').createDatabaseTerminalStore} passes `createMemoryDriver()`), so\n * it ALSO works in memory out of the box; you opt into the durable plumbing by passing a JSON /\n * SQLite / IndexedDB driver.\n *\n * The {@link TerminalSnapshot} is stored as ONE OPAQUE JSON COLUMN — the table is a row of\n * `{ id; snapshot }` ({@link TerminalSnapshotRow}). The snapshot is already a COMPLETE,\n * self-contained, pure-JSON CONFIG payload (no live broker state), so storing it whole is lossless\n * AND keeps the row type flat (`snapshot` reads back as `unknown`).\n *\n * - **`set(snapshot)` upserts under the snapshot's OWN `id`** (no separate id param) — it writes\n * the row `{ id: snapshot.id, snapshot }`.\n * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to\n * a {@link TerminalSnapshot} ({@link import('../validators.js').isTerminalSnapshot} — the total\n * guard that narrows an untrusted storage read), or `undefined` if none is stored.\n * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).\n *\n * There is NO idle-TTL / eviction — a persisted config lives until an explicit `delete`. The public\n * surface is EXACTLY `get` / `set` / `delete` — no extra members, so the class and\n * {@link TerminalStoreInterface} carry the same methods. Hydration stays a caller concern: `open` always restores an EMPTY\n * broker — parked Promises are process-bound and never resurrected.\n *\n * @example\n * ```ts\n * import { createDatabaseTerminalStore } from '@orkestrel/terminal'\n * import { createMemoryDriver } from '@orkestrel/database'\n *\n * const store = createDatabaseTerminalStore(createMemoryDriver()) // a durable driver swaps in here\n * await store.set({ id: 'shell', timeout: 5000 }) // persist the config (one JSON column)\n * const snapshot = await store.get('shell')\n * await store.delete('shell') // drop it\n * ```\n */\nexport class DatabaseTerminalStore implements TerminalStoreInterface {\n\treadonly #table: TableInterface<TerminalSnapshotRow>\n\n\t/**\n\t * Wraps a table as a terminal store.\n\t *\n\t * @param table - The {@link TableInterface} holding the snapshots — its row is the\n\t * {@link TerminalSnapshotRow} `{ id; snapshot }` shape (the snapshot one opaque JSON column)\n\t */\n\tconstructor(table: TableInterface<TerminalSnapshotRow>) {\n\t\tthis.#table = table\n\t}\n\n\t/**\n\t * Resolves the persisted snapshot for `id`, narrowing the opaque JSON column back to a\n\t * `TerminalSnapshot`.\n\t *\n\t * @param id - The endpoint name the row is keyed by\n\t * @returns The stored snapshot, or `undefined` when none is held or the column is off-shape\n\t */\n\tasync get(id: string): Promise<TerminalSnapshot | undefined> {\n\t\tconst row = await this.#table.get(id)\n\t\tif (row === undefined) return undefined\n\t\t// The snapshot crosses back as an untrusted storage read (a structured clone / a JSON row),\n\t\t// so narrow the opaque JSON column with the boundary guard rather than a cast;\n\t\t// a malformed blob resolves `undefined`, never a broken config.\n\t\treturn isTerminalSnapshot(row.snapshot) ? row.snapshot : undefined\n\t}\n\n\t/**\n\t * Inserts or replaces under the snapshot's OWN `id` (no separate id param) — the row is\n\t * `{ id, snapshot }`.\n\t *\n\t * @param snapshot - The config snapshot to persist, carrying its own `id`\n\t * @returns A promise that settles once the row is written\n\t */\n\tasync set(snapshot: TerminalSnapshot): Promise<void> {\n\t\tawait this.#table.set({ id: snapshot.id, snapshot })\n\t}\n\n\t/**\n\t * Drops a snapshot by id; an absent id is a no-op (no throw).\n\t *\n\t * @param id - The endpoint name to drop\n\t * @returns A promise that settles once the row is gone\n\t */\n\tasync delete(id: string): Promise<void> {\n\t\tawait this.#table.remove(id)\n\t}\n}\n","import type {\n\tPromptClientInterface,\n\tPromptClientOptions,\n\tPromptInterface,\n\tPromptOptions,\n\tTerminalManagerInterface,\n\tTerminalManagerOptions,\n\tTerminalSnapshotRow,\n\tTerminalStoreInterface,\n} from './types.js'\nimport type { DriverInterface, TableInterface } from '@orkestrel/database'\nimport { Prompt } from './Prompt.js'\nimport { PromptClient } from './PromptClient.js'\nimport { TerminalManager } from './TerminalManager.js'\nimport { MemoryTerminalStore } from './stores/MemoryTerminalStore.js'\nimport { DatabaseTerminalStore } from './stores/DatabaseTerminalStore.js'\nimport { createDatabase, createMemoryDriver } from '@orkestrel/database'\nimport { rawShape, stringShape } from '@orkestrel/contract'\n\n/**\n * Creates the headless {@link PromptInterface} broker. It parks live forms and applies remote\n * answers to the authoritative instances.\n *\n * @param options - See {@link PromptOptions}\n * @returns A {@link PromptInterface}\n *\n * @remarks\n * The caller awaits the parked form's own `answer`. Timeout, `stop`, or teardown destroys the form,\n * so that promise rejects with the Form package's `ABANDONED` error. Inject `options.timer` to\n * drive expiry without real time.\n *\n * @example\n * ```ts\n * import { createPrompt } from '@orkestrel/terminal'\n * import { createForm } from '@orkestrel/form'\n *\n * const prompt = createPrompt()\n * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })\n * const id = prompt.park(form)\n * prompt.answer(id, { name: 'Ada' })\n * ```\n */\nexport function createPrompt(options?: PromptOptions): PromptInterface {\n\treturn new Prompt(options)\n}\n\n/**\n * Creates the SSE prompt {@link PromptClientInterface} bridge — it connects to a remote broker's\n * SSE endpoint, dispatches each received form to a local\n * {@link import('./types.js').TerminalInterface}, and POSTs the answer back. Universal — `fetch`\n * and SSE are web standards.\n *\n * @param options - See {@link PromptClientOptions} (`url` + `terminal` required)\n * @returns A {@link PromptClientInterface}\n *\n * @remarks\n * - **Connect + reconnect.** `await client.connect()` streams remote prompts until the stream\n * ends; it reconnects with the `delay` backoff unless `reconnect` is `false` / the client was\n * `destroy`ed. Inject `options.fetch` (a scripted `fetch`) and `options.timer` to drive it\n * deterministically in tests — no real network.\n * - **Wire narrowing.** Every decoded prompt is guard-narrowed before dispatch (never an `as`).\n *\n * @example\n * ```ts\n * import { createPromptClient } from '@orkestrel/terminal'\n *\n * const client = createPromptClient({ url: 'http://host/prompts', terminal })\n * await client.connect()\n * ```\n */\nexport function createPromptClient(options: PromptClientOptions): PromptClientInterface {\n\treturn new PromptClient(options)\n}\n\n/**\n * Creates the multi-endpoint {@link TerminalManager} — a named registry of {@link PromptInterface}\n * brokers so several parties can `ask` forms of each other by name, with a transitive cycle check\n * that refuses `DEADLOCK` across every in-flight ask.\n *\n * @param options - See {@link TerminalManagerOptions}\n * @returns A {@link TerminalManager}\n *\n * @example\n * ```ts\n * import { createTerminalManager } from '@orkestrel/terminal'\n *\n * const manager = createTerminalManager()\n * manager.add('agent')\n * ```\n */\nexport function createTerminalManager(options?: TerminalManagerOptions): TerminalManagerInterface {\n\treturn new TerminalManager(options)\n}\n\n/**\n * Creates the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of endpoint\n * config snapshots, the default store backing a {@link TerminalManagerInterface}'s `open` / `save`.\n *\n * @returns A {@link TerminalStoreInterface}\n *\n * @example\n * ```ts\n * import { createMemoryTerminalStore } from '@orkestrel/terminal'\n *\n * const store = createMemoryTerminalStore()\n * ```\n */\nexport function createMemoryTerminalStore(): TerminalStoreInterface {\n\treturn new MemoryTerminalStore()\n}\n\n/**\n * Creates a {@link TerminalStoreInterface} backed by one table of the `databases` layer — the\n * driver-pluggable twin of {@link createMemoryTerminalStore}, storing each endpoint's config\n * snapshot as one opaque JSON column. The default driver is an in-memory `@orkestrel/database`\n * driver.\n *\n * @param driver - The {@link DriverInterface} backing the table (default an in-memory driver)\n * @returns A {@link TerminalStoreInterface}\n *\n * @example\n * ```ts\n * import { createDatabaseTerminalStore } from '@orkestrel/terminal'\n *\n * const store = createDatabaseTerminalStore() // in-memory by default\n * ```\n */\nexport function createDatabaseTerminalStore(\n\tdriver: DriverInterface = createMemoryDriver(),\n): TerminalStoreInterface {\n\t// The snapshot is stored as ONE OPAQUE JSON column (`rawShape`), so the row infers FLAT —\n\t// `{ id: string; snapshot: unknown }` = TerminalSnapshotRow.\n\tconst columns = { id: stringShape(), snapshot: rawShape({}) }\n\tconst database = createDatabase({ driver, tables: { terminals: columns } })\n\tconst table: TableInterface<TerminalSnapshotRow> = database.table('terminals')\n\treturn new DatabaseTerminalStore(table)\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/server/constants.ts","../../../src/server/helpers.ts","../../../src/server/Terminal.ts","../../../src/server/factories.ts"],"sourcesContent":["// Server-terminals constants — the cursor and line-clear ANSI sequences the interactive `Terminal`\n// driver writes to redraw a field view IN PLACE, plus the copy the walk shows around those views:\n// the per-control format hints, the readline-fallback prompts, and the marks for a locked field and\n// an unavailable choice. UPPER_SNAKE, every member exported. Every sequence is built from the\n// console module's own `CSI` prefix, so no raw control character appears in source and this module\n// declares no second copy of that primitive.\n\nimport type { FieldControl } from '@orkestrel/form'\nimport { CSI } from '@orkestrel/console'\n\n/**\n * Holds the cursor-up sequence template (`ESC[{count}A`) —\n * {@link import('./helpers.js').renderCursorUp} interpolates the `{count}` placeholder with the\n * number of lines to climb. Kept as a template so the count stays out of the constant.\n */\nexport const CSI_UP = `${CSI}{count}A`\n\n/**\n * Hides the cursor (`ESC[?25l`) — written before the driver starts redrawing a prompt so the cursor\n * does not flicker across the view during an in-place re-render; paired with {@link CURSOR_SHOW}.\n */\nexport const CURSOR_HIDE = `${CSI}?25l`\n\n/** Shows the cursor (`ESC[?25h`) — restores the cursor after a prompt resolves / cancels (the {@link CURSOR_HIDE} pair). */\nexport const CURSOR_SHOW = `${CSI}?25h`\n\n/**\n * Erases from the cursor down to the end of the screen (`ESC[J`) — wipes the whole previous view,\n * which a `select` or `checkbox` can spread over several lines, in one write before the new view is\n * rendered, so a redraw never leaves orphaned rows behind.\n */\nexport const CLEAR_DOWN = `${CSI}J`\n\n/**\n * Holds the numbered-list prompt the non-TTY {@link import('./Terminal.js').Terminal} `select` fallback\n * appends — a piped (non-terminal) stream cannot navigate with arrow keys, so the choices are\n * printed numbered and the user types one number on a single readline line.\n */\nexport const FALLBACK_SELECT_HINT = 'Enter a number'\n\n/** Holds the comma-separated multi-select hint the non-TTY `checkbox` fallback shows (the user types one or more numbers). */\nexport const FALLBACK_CHECKBOX_HINT = 'Enter numbers separated by commas'\n\n/** Holds the hint the non-TTY `editor` fallback shows — a piped stream has no ctrl-d, so end of input finishes the block. */\nexport const FALLBACK_EDITOR_HINT = '(end of input finishes)'\n\n/** Holds the hint the non-TTY `confirm` fallback shows — a piped stream sends a whole line, so the answer is typed rather than pressed. */\nexport const FALLBACK_CONFIRM_HINT = '(y/n)'\n\n/**\n * Holds the format cue appended to a field's label for each control the walk reads as a line of text —\n * the terminal has no date picker, no color well, and no file chooser, so the accepted shape is\n * stated instead. A control with no entry needs none: `text` and `editor` accept any line,\n * `password` masks one, and `confirm`, `select`, and `checkbox` are answered by key rather than by\n * format. The form's own rules still decide whether the typed value is acceptable.\n */\nexport const CONTROL_HINTS: Readonly<Partial<Record<FieldControl, string>>> = Object.freeze({\n\tnumber: '(number)',\n\tdate: '(YYYY-MM-DD)',\n\ttime: '(HH:MM)',\n\tdatetime: '(YYYY-MM-DDTHH:MM)',\n\tcolor: '(#rrggbb)',\n\tfile: '(path)',\n})\n\n/** Holds the instruction a `file` field with `multiple` shows before its entries — one path per line, and a blank line ends the list. */\nexport const FILE_HINT = 'One path per line, blank to finish'\n\n/**\n * Holds the lead on the line listing an open `select`'s offered values, which a typed answer can\n * ignore.\n */\nexport const SUGGESTION_LEAD = 'Suggestions'\n\n/**\n * Holds the lead on the line listing the choices a `select` or `checkbox` shows but refuses, so a\n * reader sees why one is missing from the list it heads.\n */\nexport const UNAVAILABLE_LEAD = 'Unavailable'\n\n/** Holds the mark on a locked field's line — the walk renders its value and moves on, because the form refuses an edit there. */\nexport const LOCKED_MARK = '(locked)'\n\n/**\n * States what a field is told when the walk read an answer the control cannot hold — a word typed into a\n * `number`, an off-list value typed into an open `select` whose choice is refused. The value binds\n * as absence and this message is invalidated onto the field, so the walk re-asks it with the reason\n * on screen.\n */\nexport const REFUSAL_MESSAGE = 'Enter a value this field accepts'\n","// Pure helpers for the server terminal driver — every function here is exported and unit-tested.\n// The families here are all total: the stream-boundary guards that narrow `process.stdin` / any\n// injected stream without an assertion; the cursor math the driver uses to redraw a field view IN\n// PLACE (count a view's lines, build the cursor-up sequence, assemble the reposition-and-clear\n// prefix); and the projections and line renderers the whole-form walk needs that no reducer owns —\n// a control read as text, a value shown read-only, the choices a field actually offers, and the\n// group, locked, suggestion, unavailable, and numbered-list lines. The impure driver only feeds\n// bytes into the reducers and writes the strings these helpers build. The output boundary is\n// narrowed by the console module's own `isStreamTarget`, which this module does not redeclare.\n\nimport type { FieldChoice, FieldValue, FormField, TextField } from '@orkestrel/form'\nimport type { PromptTheme } from '@src/core'\nimport type { StylerInterface } from '@orkestrel/console'\nimport type { InputStreamInterface } from './types.js'\nimport { RETURN } from '@src/core'\nimport {\n\tCLEAR_DOWN,\n\tCONTROL_HINTS,\n\tCSI_UP,\n\tLOCKED_MARK,\n\tSUGGESTION_LEAD,\n\tUNAVAILABLE_LEAD,\n} from './constants.js'\nimport { isBoolean, isFunction, isNumber, isObject, isString } from '@orkestrel/contract'\n\n/**\n * Checks whether `value` is a usable {@link InputStreamInterface} — a record with callable `on` /\n * `off` `'data'` subscription methods. A total type guard: it never throws and returns `false` for\n * anything off-shape, so it narrows the one unavoidable input boundary (the real `process.stdin`,\n * or a fake TTY a test injects) to the exact slice the driver reads, never an assertion.\n *\n * @remarks\n * Only `on` / `off` are required (the irreducible event seam); `setRawMode` / `resume` / `pause` /\n * `isTTY` are optional on {@link InputStreamInterface}, so their absence does not disqualify a stream\n * — a piped, non-TTY stream is still a valid input, one the driver reads through the readline\n * fallback rather than raw mode.\n *\n * @param value - Any value crossing the boundary (a process stream, an injected fake, `unknown`)\n * @returns True if `value` has callable `on` and `off`; false otherwise\n */\nexport function isInputStream(value: unknown): value is InputStreamInterface {\n\treturn (\n\t\tisObject(value) &&\n\t\t'on' in value &&\n\t\tisFunction(value.on) &&\n\t\t'off' in value &&\n\t\tisFunction(value.off)\n\t)\n}\n\n/**\n * Checks whether `value` is a Node {@link NodeJS.ReadableStream} — a total structural guard\n * checking for the callable `read` / `pipe` / `on` that `node:readline`'s `createInterface`\n * requires as its `input`. The non-TTY fallback narrows the resolved input stream through this\n * before handing it to readline, never through an assertion, so a real piped `process.stdin` (or a\n * `PassThrough` a test injects) crosses into the readline boundary honestly. Never throws; returns\n * `false` for a minimal fake that isn't a full readable.\n *\n * @param value - The resolved input stream (or any value crossing the boundary)\n * @returns True if `value` has the readable methods readline needs; false otherwise\n */\nexport function isReadable(value: unknown): value is NodeJS.ReadableStream {\n\treturn (\n\t\tisObject(value) &&\n\t\t'read' in value &&\n\t\tisFunction(value.read) &&\n\t\t'pipe' in value &&\n\t\tisFunction(value.pipe)\n\t)\n}\n\n/**\n * Checks whether an input stream can be driven in raw mode — it reports `isTTY === true` and\n * exposes a callable `setRawMode`. The {@link import('./Terminal.js').Terminal} probes this to\n * choose its path: true selects the interactive raw-mode fields, with arrow-key navigation and a\n * live re-render; false selects the `node:readline` line-input fallback, because a piped or\n * non-terminal stream cannot enter raw mode. Total — never throws.\n *\n * @param input - The resolved {@link InputStreamInterface}\n * @returns True if the stream is a TTY with `setRawMode`; false otherwise\n */\nexport function supportsRawMode(input: InputStreamInterface): boolean {\n\treturn input.isTTY === true && isFunction(input.setRawMode)\n}\n\n/**\n * Counts the terminal lines a rendered prompt `view` occupies — one more than its newline count, so\n * a view with no newline is a single line and a view with N newlines spans N+1 lines. The basis of\n * the in-place re-render: the driver records the line count of the view it wrote so the next redraw\n * knows how far up to move the cursor before overwriting. Total; an empty string is one empty line.\n *\n * @param view - The rendered (possibly multi-line, possibly ANSI-styled) view string\n * @returns The number of lines the view spans (always at least 1)\n */\nexport function lineCount(view: string): number {\n\tlet lines = 1\n\tfor (const character of view) {\n\t\tif (character === '\\n') lines += 1\n\t}\n\treturn lines\n}\n\n/**\n * Returns the cursor-up control sequence that moves the cursor up `count` lines (`ESC[{count}A`),\n * or the empty string when `count` is zero or negative, because no movement is needed and `ESC[0A`\n * is a wasted write. The pure step the in-place re-render uses to climb back over the previous view\n * before clearing it. Total.\n *\n * @param count - How many lines to move the cursor up\n * @returns The `ESC[{count}A` sequence, or `''` when `count <= 0`\n */\nexport function renderCursorUp(count: number): string {\n\tif (count <= 0) return ''\n\treturn `${CSI_UP.replace('{count}', String(count))}`\n}\n\n/**\n * Returns the full reposition-and-clear prefix to write before re-rendering a prompt view in place\n * — given the line count of the previous view, it moves the cursor up over those lines, returns it\n * to column 0, and erases everything from there to the end of the screen, so the next view is drawn\n * on a clean region and a taller previous view leaves no orphaned rows. Pure; the driver writes\n * this immediately followed by the new view.\n *\n * @remarks\n * For the first render `previousLines` is `1` (the cursor sits on the line the prompt opened on) so\n * the prefix is a carriage return + clear-down — the prompt draws from the current line. For a\n * subsequent render it climbs `previousLines - 1` lines (the cursor is on the last line of the prior\n * view) before clearing. Keeping the math here (not in the driver) makes the re-render unit-testable\n * without a real terminal.\n *\n * @param previousLines - The line count of the view on screen (from {@link lineCount})\n * @returns The control-sequence prefix to write before the new view\n */\nexport function redrawPrefix(previousLines: number): string {\n\treturn `${renderCursorUp(previousLines - 1)}${RETURN}${CLEAR_DOWN}`\n}\n\n/**\n * Projects any field the walk reads as a line of text into the {@link TextField} the text reducer\n * takes — `text` itself, and the controls a terminal has no widget for: `number`, `date`, `time`,\n * `datetime`, `color`, and one `file` entry. The label carries that control's format cue from\n * {@link CONTROL_HINTS}, and a declared `default` becomes the line a bare return submits. The\n * projection carries no rule, because the authoritative form still evaluates the answer this line\n * binds; it exists only so one reducer covers every one of them.\n *\n * @param field - The field being read\n * @returns The text field the reducer renders for it\n *\n * @example\n * ```ts\n * fieldToText({ control: 'date', name: 'born', label: 'Birthday' })\n * // { control: 'text', name: 'born', label: 'Birthday (YYYY-MM-DD)' }\n * ```\n */\nexport function fieldToText(field: FormField): TextField {\n\tconst label = field.label ?? field.name\n\tconst hint = CONTROL_HINTS[field.control]\n\tconst seed = 'default' in field ? field.default : undefined\n\tconst text = isNumber(seed) ? String(seed) : seed\n\treturn {\n\t\tcontrol: 'text',\n\t\tname: field.name,\n\t\tlabel: hint === undefined ? label : `${label} ${hint}`,\n\t\t...(isString(text) ? { default: text } : {}),\n\t}\n}\n\n/**\n * Projects one held answer into the text a read-only line shows — a scalar as itself, a boolean as\n * `yes` / `no` (the word the confirm reducer commits), and a list joined by commas. Absence renders\n * as nothing, because a locked field nobody has answered has nothing to show.\n *\n * @param value - The answer the form holds for a field, or absence\n * @returns The text to render for it\n */\nexport function valueToText(value: FieldValue | undefined): string {\n\tif (value === undefined) return ''\n\tif (isString(value)) return value\n\tif (isNumber(value)) return String(value)\n\tif (isBoolean(value)) return value ? 'yes' : 'no'\n\treturn value.join(', ')\n}\n\n/**\n * Returns the choices a `select` or `checkbox` field actually offers — the form refuses a disabled\n * choice's value at every door, including a fill, so the walk never puts one in front of the\n * cursor. Pair with {@link filterDisabled} to tell the reader what was withheld.\n *\n * @param choices - The field's declared choices\n * @returns The choices the walk offers, in declared order\n */\nexport function filterEnabled(choices: readonly FieldChoice[]): readonly FieldChoice[] {\n\treturn choices.filter((choice) => choice.disabled !== true)\n}\n\n/**\n * Returns the choices a `select` or `checkbox` field shows but refuses — the complement of\n * {@link filterEnabled}, rendered by {@link renderUnavailableLine} above the list so a reader sees\n * why a declared choice is missing from it.\n *\n * @param choices - The field's declared choices\n * @returns The refused choices, in declared order\n */\nexport function filterDisabled(choices: readonly FieldChoice[]): readonly FieldChoice[] {\n\treturn choices.filter((choice) => choice.disabled === true)\n}\n\n/**\n * Renders the section header the walk writes when it enters a new field group, painted by the\n * `message` role.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param label - The group's declared label, falling back to its own name\n * @returns The rendered section header\n */\nexport function renderGroupHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tlabel: string,\n): string {\n\treturn styler.render(theme.roles.message, label)\n}\n\n/**\n * Renders the read-only line a locked field shows — its label, the {@link LOCKED_MARK}, and the\n * answer the form already holds. The walk writes this instead of a prompt, because the field is\n * still validated and still submitted but must not be edited here.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param label - The field's label\n * @param value - The held answer, from {@link valueToText}\n * @returns The rendered line, with no trailing space when there is nothing to show\n */\nexport function renderLockedLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tlabel: string,\n\tvalue: string,\n): string {\n\tconst head = `${styler.render(theme.roles.muted, theme.icons.dot)} ${styler.render(theme.roles.message, label)} ${styler.render(theme.roles.hint, LOCKED_MARK)}`\n\treturn value.length === 0 ? head : `${head} ${styler.render(theme.roles.content, value)}`\n}\n\n/**\n * Renders the line listing an open select's offered values above its text prompt — a suggestion\n * list, because an open select admits an answer the list does not offer.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param choices - The choices the open select offers, from {@link filterEnabled}\n * @returns The rendered suggestion line\n */\nexport function renderSuggestionLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tchoices: readonly FieldChoice[],\n): string {\n\tconst values = choices.map((choice) => choice.value).join(', ')\n\treturn styler.render(theme.roles.hint, `${SUGGESTION_LEAD}: ${values}`)\n}\n\n/**\n * Renders the line naming the choices a field shows but refuses, written above the list the walk\n * drives.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param choices - The refused choices, from {@link filterDisabled}\n * @returns The rendered unavailable line\n */\nexport function renderUnavailableLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tchoices: readonly FieldChoice[],\n): string {\n\tconst labels = choices.map((choice) => choice.label).join(', ')\n\treturn styler.render(theme.roles.muted, `${UNAVAILABLE_LEAD}: ${labels}`)\n}\n\n/**\n * Renders the numbered choice list the non-TTY fallback prints — a piped stream cannot navigate\n * with arrow keys, so each offered choice is printed with the number the reader types back. One\n * line per choice, with no trailing newline.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param choices - The choices the walk offers, from {@link filterEnabled}\n * @returns The rendered list\n */\nexport function renderNumberedList(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tchoices: readonly FieldChoice[],\n): string {\n\treturn choices\n\t\t.map(\n\t\t\t(choice, index) =>\n\t\t\t\t` ${styler.render(theme.roles.muted, `${String(index + 1)})`)} ${styler.render(theme.roles.content, choice.label)}`,\n\t\t)\n\t\t.join('\\n')\n}\n","import type {\n\tFieldChoice,\n\tFieldError,\n\tFieldValue,\n\tFormField,\n\tFormInterface,\n\tFormValues,\n} from '@orkestrel/form'\nimport type {\n\tCheckboxField,\n\tConfirmField,\n\tEditorField,\n\tFileField,\n\tPasswordField,\n\tSelectField,\n} from '@orkestrel/form'\nimport type { KeyEvent, PromptStep, PromptTheme, TerminalInterface } from '@src/core'\nimport type { StylerInterface } from '@orkestrel/console'\nimport type { StreamTargetInterface } from '@orkestrel/console/server'\nimport type { Interface } from 'node:readline'\nimport type { InputStreamInterface, TerminalOptions } from './types.js'\nimport {\n\tcreateCheckboxState,\n\tcreateConfirmState,\n\tcreateEditorState,\n\tcreateInputState,\n\tcreatePasswordState,\n\tcreatePromptTheme,\n\tcreateSelectState,\n\tNEWLINE,\n\tparseKey,\n\treduceCheckbox,\n\treduceConfirm,\n\treduceEditor,\n\treduceInput,\n\treducePassword,\n\treduceSelect,\n\trenderErrorLine,\n\trenderHintedHeader,\n\trenderPromptHeader,\n\tsanitizeDisplayText,\n\tTerminalError,\n} from '@src/core'\nimport { matchesAnswer, parseValue } from '@orkestrel/form'\nimport { createStyler } from '@orkestrel/console'\nimport { isStreamTarget } from '@orkestrel/console/server'\nimport { createInterface } from 'node:readline'\nimport { stdin, stdout } from 'node:process'\nimport {\n\tCURSOR_HIDE,\n\tCURSOR_SHOW,\n\tFALLBACK_CHECKBOX_HINT,\n\tFALLBACK_CONFIRM_HINT,\n\tFALLBACK_EDITOR_HINT,\n\tFALLBACK_SELECT_HINT,\n\tFILE_HINT,\n\tREFUSAL_MESSAGE,\n} from './constants.js'\nimport {\n\tfieldToText,\n\tfilterDisabled,\n\tfilterEnabled,\n\tisInputStream,\n\tisReadable,\n\tlineCount,\n\tredrawPrefix,\n\trenderGroupHeader,\n\trenderLockedLine,\n\trenderNumberedList,\n\trenderSuggestionLine,\n\trenderUnavailableLine,\n\tsupportsRawMode,\n\tvalueToText,\n} from './helpers.js'\n\n/**\n * Implements {@link TerminalInterface} for a human at this machine's keyboard — the interactive\n * form driver, and the only impure part of the terminal stack. {@link ask} walks one form's fields\n * in schema order, feeds raw-mode stdin bytes through `parseKey` into the matching pure reducer,\n * renders each returned view in place, binds every answer through the form's own `fill`, and\n * re-asks what the form refused. It owns no form logic: the schema, the rules, the values, and the\n * settlement all belong to the form it is given, and this class owns only raw mode, the cursor, and\n * the re-render.\n *\n * @remarks\n * See {@link TerminalInterface} for the driving contract. The walk itself:\n *\n * - **Controls and reducers.** `text`, `number`, `date`, `time`, `datetime`, `color`, and\n * each `file` entry are read as one line of text through {@link fieldToText}, which appends that\n * control's format cue to the label. `password`, `confirm`, `editor`, `select`, and `checkbox`\n * each drive their own reducer. An open `select` is a suggestion list plus a typed line, because\n * `open` means the answer need not come from the list.\n * - **The binding projects through `matchesAnswer`.** Every answer is filled as\n * `fill(name, matchesAnswer(value) ? value : undefined)` after `parseValue` has coerced it to the\n * control's own shape, so a bare return on a field with no default binds as absence and the\n * form's `required` rule refuses it. A typed answer the control cannot hold binds as absence and\n * invalidates the field, so the walk asks again with the reason on screen.\n * - **Visibility is honored.** A `hidden` field and a field in `form.disabled` are skipped; a\n * `locked` field renders read-only; entering a new group writes its label as a section header.\n * - **Refusal re-asks.** After the walk the form is submitted. A refusal re-walks only the erroring\n * fields the walk can edit and submits again. When every erroring field is one the walk cannot\n * edit — hidden, locked, or disabled — the form is abandoned instead, because asking again could\n * not change the answer.\n * - **Raw-mode leak-free.** Raw mode is entered once per field and always cleaned up: on submit, on\n * cancel, on a throw, and when the form is abandoned under an active read.\n * - **Non-TTY fallback.** When `input` is not a TTY, the same walk runs over `node:readline` line\n * input: `select` and `checkbox` print a numbered list, and `editor` reads to end of input.\n */\nexport class Terminal implements TerminalInterface {\n\treadonly #input: InputStreamInterface\n\treadonly #output: StreamTargetInterface\n\treadonly #styler: StylerInterface\n\treadonly #theme: PromptTheme\n\treadonly #handlers = new Map<object, (chunk: string | Uint8Array) => void>()\n\treadonly #queue: string[] = []\n\t#interface: Interface | undefined\n\t#taker: ((line: string | undefined) => void) | undefined\n\t#ended = false\n\n\tconstructor(options?: TerminalOptions) {\n\t\t// Resolve each stream through its guard: a present, well-shaped injected stream is used as is;\n\t\t// otherwise the real process stream — no assertion, and an absent option falls through.\n\t\tthis.#input = isInputStream(options?.input) ? options.input : stdin\n\t\tthis.#output = isStreamTarget(options?.output) ? options.output : stdout\n\t\tthis.#styler = createStyler()\n\t\tthis.#theme = createPromptTheme(options?.theme)\n\t}\n\n\t// === The contract\n\n\tasync ask(form: FormInterface): Promise<FormValues> {\n\t\ttry {\n\t\t\tthis.#report(form, form.errors)\n\t\t\tawait this.#walk(form, form.schema.fields)\n\t\t\tfor (;;) {\n\t\t\t\tif (form.status !== 'editing') return await form.answer\n\t\t\t\tconst result = form.submit()\n\t\t\t\tif (result.success) return await form.answer\n\t\t\t\tthis.#report(form, result.error)\n\t\t\t\tconst again = this.#collectEditable(form, result.error)\n\t\t\t\t// Nothing the walk can edit, or an input stream that has already ended: asking again would\n\t\t\t\t// render the same fields and read the same answers forever, so abandon the form instead.\n\t\t\t\t// The failures are on screen either way, so the reader sees what could not be answered.\n\t\t\t\tif (again.length === 0 || this.#ended) {\n\t\t\t\t\tform.destroy()\n\t\t\t\t\treturn await form.answer\n\t\t\t\t}\n\t\t\t\tawait this.#walk(form, again)\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#close()\n\t\t}\n\t}\n\n\t// === The walk\n\n\t/**\n\t * Asks `fields` in the order given, skipping what the walk must not touch and rendering what it\n\t * must not edit. Returns early the moment the form stops being editable, so an abandoned form\n\t * ends the walk between fields as well as under an active read.\n\t */\n\tasync #walk(form: FormInterface, fields: readonly FormField[]): Promise<void> {\n\t\tlet group: string | undefined\n\t\tfor (const field of fields) {\n\t\t\tif (form.status !== 'editing') return\n\t\t\tif (field.hidden === true) continue\n\t\t\tif (form.disabled.has(field.name)) continue\n\t\t\tif (field.group !== group) {\n\t\t\t\tgroup = field.group\n\t\t\t\tif (group !== undefined) this.#writeGroup(form, group)\n\t\t\t}\n\t\t\tif (field.locked === true) {\n\t\t\t\tthis.#writeLocked(form, field)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst raw = await this.#read(form, field)\n\t\t\tif (form.status !== 'editing') return\n\t\t\tthis.#bind(form, field, raw)\n\t\t}\n\t}\n\n\t/** Writes a group's label as a section header, resolving the schema's declared label and falling back to the group's own name. */\n\t#writeGroup(form: FormInterface, name: string): void {\n\t\tconst label = form.schema.groups?.find((group) => group.name === name)?.label ?? name\n\t\tthis.#output.write(\n\t\t\t`${NEWLINE}${renderGroupHeader(this.#styler, this.#theme, sanitizeDisplayText(label))}${NEWLINE}`,\n\t\t)\n\t}\n\n\t/** Renders a locked field read-only — its label, its mark, and the answer the form already holds. */\n\t#writeLocked(form: FormInterface, field: FormField): void {\n\t\tconst line = renderLockedLine(\n\t\t\tthis.#styler,\n\t\t\tthis.#theme,\n\t\t\tsanitizeDisplayText(field.label ?? field.name),\n\t\t\tsanitizeDisplayText(valueToText(form.values[field.name])),\n\t\t)\n\t\tthis.#output.write(`${line}${NEWLINE}`)\n\t}\n\n\t/** Reads one field through the reducer its control names, resolving the raw answer the binding projects. */\n\t#read(form: FormInterface, field: FormField): Promise<FieldValue | undefined> {\n\t\tif (field.control === 'password') return this.#askPassword(form, field)\n\t\tif (field.control === 'confirm') return this.#confirm(form, field)\n\t\tif (field.control === 'editor') return this.#askEditor(form, field)\n\t\tif (field.control === 'select') return this.#select(form, field)\n\t\tif (field.control === 'checkbox') return this.#checkbox(form, field)\n\t\tif (field.control === 'file') return this.#file(form, field)\n\t\treturn this.#askText(form, field)\n\t}\n\n\t/**\n\t * Binds one raw answer to the form — the single binding, and the only place this driver writes a\n\t * value. `parseValue` coerces the raw answer to the control's own shape and `matchesAnswer`\n\t * projects a blank one to absence, which is what keeps `required` refusing a bare return. A raw\n\t * answer the control cannot hold binds as absence and invalidates the field, so the field comes\n\t * back on the next pass carrying the reason rather than vanishing silently.\n\t */\n\t#bind(form: FormInterface, field: FormField, raw: FieldValue | undefined): void {\n\t\tform.touch(field.name)\n\t\tconst value = raw === undefined ? undefined : parseValue(field, raw)\n\t\tform.fill(field.name, matchesAnswer(value) ? value : undefined)\n\t\tif (value === undefined && matchesAnswer(raw)) form.invalidate(field.name, REFUSAL_MESSAGE)\n\t}\n\n\t/** Collects the erroring fields the walk can ask again — every field the walk skips or renders read-only is excluded, because a second pass could not change its answer. */\n\t#collectEditable(form: FormInterface, errors: readonly FieldError[]): readonly FormField[] {\n\t\tconst fields: FormField[] = []\n\t\tfor (const error of errors) {\n\t\t\tconst field = form.field(error.field)\n\t\t\tif (field === undefined) continue\n\t\t\tif (field.hidden === true || field.locked === true) continue\n\t\t\tif (form.disabled.has(field.name)) continue\n\t\t\tif (fields.some((held) => held.name === field.name)) continue\n\t\t\tfields.push(field)\n\t\t}\n\t\treturn fields\n\t}\n\n\t/** Writes every supplied failure against its field's label. */\n\t#report(form: FormInterface, errors: readonly FieldError[]): void {\n\t\tfor (const error of errors) {\n\t\t\tconst label = sanitizeDisplayText(form.field(error.field)?.label ?? error.field)\n\t\t\tconst message = sanitizeDisplayText(error.message)\n\t\t\tconst line = renderErrorLine(this.#styler, this.#theme, `${label}: ${message}`)\n\t\t\tthis.#output.write(`${line}${NEWLINE}`)\n\t\t}\n\t}\n\n\t// === The controls\n\n\t/** Reads a field as one line of text — `text` itself and the controls a terminal has no widget for. */\n\t#askText(form: FormInterface, field: FormField): Promise<string> {\n\t\tconst state = createInputState(fieldToText(field), this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceInput)\n\t\treturn this.#line(form, state.message, state.default)\n\t}\n\n\t/** Reads a secret — masked live in raw mode, and read without echo through readline on a stream that cannot enter it. */\n\t#askPassword(form: FormInterface, field: PasswordField): Promise<string> {\n\t\tconst state = createPasswordState(field, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reducePassword)\n\t\treturn this.#prompt(form, renderPromptHeader(this.#styler, this.#theme, state.message))\n\t}\n\n\t/**\n\t * Reads a yes or no. The fallback accepts `y` / `yes` / `n` / `no` in any case and takes the\n\t * field's default for a bare line; anything else is returned as typed, so the binding refuses it\n\t * and the walk asks again rather than silently reading it as no.\n\t */\n\tasync #confirm(form: FormInterface, field: ConfirmField): Promise<FieldValue> {\n\t\tconst state = createConfirmState(field, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceConfirm)\n\t\tconst header = renderHintedHeader(\n\t\t\tthis.#styler,\n\t\t\tthis.#theme,\n\t\t\tstate.message,\n\t\t\tFALLBACK_CONFIRM_HINT,\n\t\t)\n\t\tconst line = await this.#prompt(form, header)\n\t\tconst answer = line.trim().toLowerCase()\n\t\tif (answer.length === 0) return state.default\n\t\tif (answer === 'y' || answer === 'yes') return true\n\t\tif (answer === 'n' || answer === 'no') return false\n\t\treturn line.trim()\n\t}\n\n\t/** Reads text over many lines — ctrl-d finishes in raw mode, and end of input finishes on a piped stream. */\n\t#askEditor(form: FormInterface, field: EditorField): Promise<string> {\n\t\tconst state = createEditorState(field, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceEditor)\n\t\treturn this.#block(form, state.message, state.default)\n\t}\n\n\t/**\n\t * Reads one choice. A disabled choice is named above the list and never offered, because the form\n\t * refuses its value at every door. An open select is a suggestion list plus a typed line, since\n\t * `open` admits an answer the list does not offer; a closed select with nothing left to offer\n\t * resolves absence and lets the form's own rules report it.\n\t */\n\tasync #select(form: FormInterface, field: SelectField): Promise<FieldValue | undefined> {\n\t\tconst choices = filterEnabled(field.choices)\n\t\tthis.#writeUnavailable(field.choices)\n\t\tif (field.open === true) {\n\t\t\tif (choices.length > 0) {\n\t\t\t\tconst display = choices.map((choice) => ({\n\t\t\t\t\t...choice,\n\t\t\t\t\tvalue: sanitizeDisplayText(choice.value),\n\t\t\t\t}))\n\t\t\t\tthis.#output.write(`${renderSuggestionLine(this.#styler, this.#theme, display)}${NEWLINE}`)\n\t\t\t}\n\t\t\treturn this.#askText(form, field)\n\t\t}\n\t\tif (choices.length === 0) return undefined\n\t\tconst state = createSelectState({ ...field, choices }, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceSelect)\n\t\tthis.#writeList(state.message, choices)\n\t\tconst line = await this.#prompt(form, this.#formatHint(`${FALLBACK_SELECT_HINT}:`))\n\t\treturn choices[Number.parseInt(line.trim(), 10) - 1]?.value\n\t}\n\n\t/**\n\t * Reads any number of choices. Disabled choices are named above the list and never offered, so a\n\t * box the form would refuse can never be ticked. An empty answer is an answered \"none of them\",\n\t * which is what `matchesAnswer` says an empty list means.\n\t */\n\tasync #checkbox(form: FormInterface, field: CheckboxField): Promise<FieldValue> {\n\t\tconst choices = filterEnabled(field.choices)\n\t\tthis.#writeUnavailable(field.choices)\n\t\tconst state = createCheckboxState({ ...field, choices }, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceCheckbox)\n\t\tthis.#writeList(state.message, choices)\n\t\tconst line = await this.#prompt(form, this.#formatHint(`${FALLBACK_CHECKBOX_HINT}:`))\n\t\treturn line\n\t\t\t.split(',')\n\t\t\t.map((part) => choices[Number.parseInt(part.trim(), 10) - 1]?.value)\n\t\t\t.filter((value): value is string => value !== undefined)\n\t}\n\n\t/**\n\t * Reads file paths — names only, because bytes never enter a form. A `multiple` field collects one\n\t * path per line until a blank one, and a single field takes one. No path at all is absence rather\n\t * than an empty list, because the reader answered a blank line.\n\t */\n\tasync #file(form: FormInterface, field: FileField): Promise<FieldValue | undefined> {\n\t\tif (field.multiple === true) this.#output.write(`${this.#formatHint(FILE_HINT)}${NEWLINE}`)\n\t\tconst paths: string[] = []\n\t\tfor (;;) {\n\t\t\tconst entry = await this.#askText(form, field)\n\t\t\tif (entry.length === 0) break\n\t\t\tpaths.push(entry)\n\t\t\tif (field.multiple !== true || this.#ended) break\n\t\t}\n\t\treturn paths.length > 0 ? paths : undefined\n\t}\n\n\t/** Names the choices a field shows but refuses, above the list the walk drives. */\n\t#writeUnavailable(choices: readonly FieldChoice[]): void {\n\t\tconst refused = filterDisabled(choices)\n\t\tif (refused.length === 0) return\n\t\tthis.#output.write(`${renderUnavailableLine(this.#styler, this.#theme, refused)}${NEWLINE}`)\n\t}\n\n\t/** Writes a field's header above its numbered choice list — the non-TTY presentation of a choice field. */\n\t#writeList(message: string, choices: readonly FieldChoice[]): void {\n\t\tconst header = renderPromptHeader(this.#styler, this.#theme, message)\n\t\tconst list = renderNumberedList(this.#styler, this.#theme, choices)\n\t\tthis.#output.write(`${header}${NEWLINE}${list}${NEWLINE}`)\n\t}\n\n\t/** Paints one line of supplementary instruction through the `hint` role. */\n\t#formatHint(text: string): string {\n\t\treturn this.#styler.render(this.#theme.roles.hint, text)\n\t}\n\n\t// === The raw-mode kernel\n\n\t/**\n\t * Isolates the irreducible Node raw-mode primitive — the ONLY place raw mode is touched. Switches the input\n\t * into raw mode (each keypress delivered immediately, no echo), resumes its flow, and subscribes\n\t * `handler` to `'data'`. The paired {@link #leaveRaw} operation unsubscribes the exact handler,\n\t * leaves raw mode, and pauses the stream on submit, cancel, abandon, or throw.\n\t */\n\t#enterRaw(token: object, handler: (chunk: string | Uint8Array) => void): void {\n\t\tthis.#handlers.set(token, handler)\n\t\tthis.#input.setRawMode?.(true)\n\t\tthis.#input.resume?.()\n\t\tthis.#input.on('data', handler)\n\t}\n\n\t#leaveRaw(token: object): void {\n\t\tconst handler = this.#handlers.get(token)\n\t\tif (handler === undefined) return\n\t\tthis.#handlers.delete(token)\n\t\tthis.#input.off('data', handler)\n\t\tthis.#input.setRawMode?.(false)\n\t\tthis.#input.pause?.()\n\t}\n\n\t/**\n\t * Drives ONE field over raw-mode stdin — the generic engine every interactive control shares.\n\t * Renders the reducer's initial view, enters raw mode once, and on each keypress runs `parseKey` →\n\t * `reduce` → an in-place re-render; on `submit` it cleans up and resolves the reducer's value, on\n\t * `cancel` (ctrl-c) it cleans up and rejects a {@link TerminalError} coded `CANCEL`. Abandoning\n\t * the form ends the read too, through the rejection of its own `answer`. Raw mode is entered\n\t * exactly once and cleaned up on every exit path, so no raw mode and no `'data'` listener ever\n\t * leaks. The cursor is hidden for the duration and restored on exit.\n\t */\n\t#drive<T, S>(\n\t\tform: FormInterface,\n\t\tinitial: S,\n\t\treduce: (state: S, key: KeyEvent) => PromptStep<T, S>,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\t// Render the first view, tracking how many lines it spans so the next redraw climbs over them.\n\t\t\tthis.#output.write(CURSOR_HIDE)\n\t\t\tlet firstView: string\n\t\t\ttry {\n\t\t\t\tfirstView = reduce(initial, parseKey('')).view\n\t\t\t\tthis.#output.write(firstView)\n\t\t\t} catch (error) {\n\t\t\t\t// Raw mode was never entered yet, but the cursor was hidden — restore it before rejecting.\n\t\t\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\t\t\treject(error)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst token = {}\n\t\t\tconst handler = this.#createHandler(\n\t\t\t\ttoken,\n\t\t\t\tinitial,\n\t\t\t\tlineCount(firstView),\n\t\t\t\treduce,\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t)\n\t\t\tthis.#enterRaw(token, handler)\n\t\t\t// An abandoned form ends the read: its own `answer` rejects, so release the terminal and fail\n\t\t\t// this field with the form's error. A read that already finished holds no token and is a no-op.\n\t\t\tvoid form.answer.catch((error: unknown) => this.#interrupt(token, reject, error))\n\t\t})\n\t}\n\n\t#createHandler<T, S>(\n\t\ttoken: object,\n\t\tinitial: S,\n\t\tinitialLines: number,\n\t\treduce: (state: S, key: KeyEvent) => PromptStep<T, S>,\n\t\tresolve: (value: T | PromiseLike<T>) => void,\n\t\treject: (reason?: unknown) => void,\n\t): (chunk: string | Uint8Array) => void {\n\t\tlet state = initial\n\t\tlet lines = initialLines\n\t\treturn (chunk) => {\n\t\t\ttry {\n\t\t\t\tconst step = reduce(state, parseKey(chunk))\n\t\t\t\tstate = step.state\n\t\t\t\tif (step.status === 'active') {\n\t\t\t\t\tthis.#render(step.view, lines)\n\t\t\t\t\tlines = lineCount(step.view)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// Terminal step (submit / cancel): paint the final committed view, then tear down.\n\t\t\t\tthis.#render(step.view, lines)\n\t\t\t\tthis.#leaveRaw(token)\n\t\t\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\t\t\tif (step.status === 'cancel') reject(new TerminalError('CANCEL', 'Prompt cancelled'))\n\t\t\t\telse if (step.status === 'submit' && step.value !== undefined) resolve(step.value)\n\t\t\t\telse reject(new TerminalError('DRIVER', 'submit produced no value'))\n\t\t\t} catch (error) {\n\t\t\t\t// A throw inside the reducer/styler: tear down raw mode + the listener, restore the cursor\n\t\t\t\t// exactly as the normal exit path does, then reject.\n\t\t\t\tthis.#leaveRaw(token)\n\t\t\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\t\t\treject(error)\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Ends an active read that the form outlived — releases raw mode exactly as a submit does, then fails the read with the form's own error. */\n\t#interrupt(token: object, reject: (reason?: unknown) => void, error: unknown): void {\n\t\tif (!this.#handlers.has(token)) return\n\t\tthis.#leaveRaw(token)\n\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\treject(error)\n\t}\n\n\t/** Redraws a field view in place — climbs over the previous view's `previousLines`, clears, and writes the new view (the pure cursor-math is {@link redrawPrefix}). */\n\t#render(view: string, previousLines: number): void {\n\t\tthis.#output.write(`${redrawPrefix(previousLines)}${view}`)\n\t}\n\n\t// === Non-TTY fallbacks (node:readline line input)\n\n\t/** Reads one line for a text-shaped field, taking the field's default when the line is bare. */\n\tasync #line(form: FormInterface, message: string, seed: string): Promise<string> {\n\t\tconst answer = await this.#prompt(form, renderPromptHeader(this.#styler, this.#theme, message))\n\t\treturn answer.length > 0 ? answer : seed\n\t}\n\n\t/**\n\t * Reads a whole block for an `editor` field — a piped stream has no ctrl-d keypress, so end of\n\t * input is the terminator. It drains every remaining line, which is why nothing after an `editor`\n\t * field can be asked on the same stream.\n\t */\n\tasync #block(form: FormInterface, message: string, seed: string): Promise<string> {\n\t\tconst header = renderHintedHeader(this.#styler, this.#theme, message, FALLBACK_EDITOR_HINT)\n\t\tthis.#output.write(`${header}${NEWLINE}`)\n\t\tconst collected: string[] = []\n\t\tfor (;;) {\n\t\t\tconst line = await this.#next(form)\n\t\t\tif (line === undefined) break\n\t\t\tcollected.push(line)\n\t\t}\n\t\tconst text = collected.join('\\n')\n\t\treturn text.length > 0 ? text : seed\n\t}\n\n\t/** Writes one field's header and reads back the line the reader answers it with, or `''` once the stream has ended. */\n\tasync #prompt(form: FormInterface, header: string): Promise<string> {\n\t\tthis.#output.write(`${header} `)\n\t\tconst line = await this.#next(form)\n\t\treturn line ?? ''\n\t}\n\n\t/**\n\t * Takes the next line the input carries — from the buffer when the reader has already read ahead,\n\t * and otherwise by waiting for one. It resolves ABSENCE once the stream has ended, so a walk over\n\t * an exhausted stream settles instead of waiting forever, and it rejects when the form is\n\t * abandoned, so an unanswerable read ends with the form rather than outliving it.\n\t */\n\t#next(form: FormInterface): Promise<string | undefined> {\n\t\tconst buffered = this.#queue.shift()\n\t\tif (buffered !== undefined) return Promise.resolve(buffered)\n\t\tif (this.#ended) return Promise.resolve(undefined)\n\t\tthis.#startReader()\n\t\treturn new Promise<string | undefined>((resolve, reject) => {\n\t\t\tthis.#taker = resolve\n\t\t\tvoid form.answer.catch((error: unknown) => {\n\t\t\t\tif (this.#taker !== resolve) return\n\t\t\t\tthis.#taker = undefined\n\t\t\t\treject(error)\n\t\t\t})\n\t\t})\n\t}\n\n\t/**\n\t * Opens the walk's ONE readline interface, or keeps the open one. A whole form is many questions\n\t * over one stream, and an interface reads ahead: a fresh interface per question would swallow\n\t * every line it read past the one it was asked for, so the walk holds a single reader and buffers\n\t * what arrives early. It is closed when the walk ends, so the process is free to exit.\n\t */\n\t#startReader(): void {\n\t\tif (this.#interface !== undefined) return\n\t\tconst rl = createInterface(this.#openReadline())\n\t\trl.on('line', (line) => this.#accept(line))\n\t\trl.on('close', () => this.#finish())\n\t\tthis.#interface = rl\n\t}\n\n\t/** Hands a line to whoever is waiting for it, or buffers it for the next question. */\n\t#accept(line: string): void {\n\t\tconst taker = this.#taker\n\t\tif (taker === undefined) {\n\t\t\tthis.#queue.push(line)\n\t\t\treturn\n\t\t}\n\t\tthis.#taker = undefined\n\t\ttaker(line)\n\t}\n\n\t/** Records that no further line can arrive and releases whoever was waiting for one. */\n\t#finish(): void {\n\t\tthis.#ended = true\n\t\tconst taker = this.#taker\n\t\tif (taker === undefined) return\n\t\tthis.#taker = undefined\n\t\ttaker(undefined)\n\t}\n\n\t/** Closes the walk's reader and releases a waiting question — the walk is over, so the stream must stop holding the process open. */\n\t#close(): void {\n\t\tconst taker = this.#taker\n\t\tthis.#taker = undefined\n\t\tif (taker !== undefined) taker(undefined)\n\t\tconst rl = this.#interface\n\t\tif (rl === undefined) return\n\t\tthis.#interface = undefined\n\t\t// Drop the close listener first: this close is the walk ending, not the stream ending, and\n\t\t// recording it as end of input would refuse the next walk its input.\n\t\trl.removeAllListeners('close')\n\t\trl.close()\n\t}\n\n\t/**\n\t * Narrows the resolved input to the `node:readline` `createInterface` boundary, never an assertion.\n\t * The fallback only runs on a real piped `process.stdin` (or a `PassThrough` a test injects), both\n\t * genuine readables; a minimal non-readable fake reaching here means the walk was driven with a\n\t * stream it cannot use, which fails loudly rather than silently. `terminal: false` leaves readline\n\t * as a line decoder and nothing else: the walk writes every prompt itself, through the same output\n\t * every other line goes to, so nothing is written twice and a secret is never echoed.\n\t */\n\t#openReadline(): { input: NodeJS.ReadableStream; terminal: boolean } {\n\t\t// Bind to a local first — a guard narrows a local, not a `#private` field access.\n\t\tconst input = this.#input\n\t\tif (!isReadable(input))\n\t\t\tthrow new TerminalError('DRIVER', 'Terminal fallback requires a readable input stream')\n\t\treturn { input, terminal: false }\n\t}\n}\n","import type { TerminalInterface } from '@src/core'\nimport type { TerminalOptions } from './types.js'\nimport { Terminal } from './Terminal.js'\n\n/**\n * Creates the interactive terminal form driver — the local-keyboard arm of the terminal trio,\n * beside the core headless `createPrompt` broker and the SSE `createPromptClient` bridge. Where the\n * broker parks a live form until somebody elsewhere answers it, a `Terminal` answers one here: it\n * walks the form's fields in schema order, drives each control's pure reducer over raw-mode stdin,\n * binds every answer through the form's own `fill`, and submits. It is the only impure part of the\n * terminal stack.\n *\n * @param options - See {@link TerminalOptions}\n * @returns A {@link TerminalInterface} whose `ask` drives one whole form over the resolved streams\n *\n * @remarks\n * - **The form is the unit.** `ask` takes the caller's live form and returns its settled values. The\n * returned promise is that form's own `answer`, so a caller holding the form can await either one.\n * - **Every control renders.** The line-read controls are read as a line of text with their format cue,\n * `password` masks, `confirm` takes a key, `editor` takes a block, `select` and `checkbox` drive a\n * list, and an open `select` accepts a value its list does not offer.\n * - **The form decides.** A blank answer binds as absence, the form evaluates, and a refusal\n * re-walks only the fields the walk can still edit.\n * - **Injectable and guard-narrowed.** `input` and `output` default to `process.stdin` and\n * `process.stdout` but accept any stream of the declared minimal shape, resolved through their\n * guards rather than an assertion, so a test drives a whole form with a fake TTY that emits\n * scripted key chunks and records every rendered byte.\n * - **Non-TTY fallback.** A piped stream cannot enter raw mode, so the same walk runs over\n * `node:readline` line input.\n *\n * @example Ask one form at this keyboard\n * ```ts\n * import { createForm } from '@orkestrel/form'\n * import { isTerminalError } from '@orkestrel/terminal'\n * import { createTerminal } from '@orkestrel/terminal/server'\n *\n * const terminal = createTerminal() // process.stdin / process.stdout by default\n * const form = createForm({\n * \tlabel: 'Sign up',\n * \tfields: [\n * \t\t{ control: 'text', name: 'name', label: 'Your name', rule: { required: true, minimum: 2 } },\n * \t\t{ control: 'text', name: 'email', label: 'Email', rule: { required: true, email: true } },\n * \t\t{ control: 'password', name: 'token', label: 'Token' },\n * \t\t{ control: 'confirm', name: 'terms', label: 'Accept the terms', rule: { required: true } },\n * \t\t{\n * \t\t\tcontrol: 'select',\n * \t\t\tname: 'role',\n * \t\t\tlabel: 'Role',\n * \t\t\tchoices: [\n * \t\t\t\t{ value: 'admin', label: 'Admin' },\n * \t\t\t\t{ value: 'viewer', label: 'Viewer', help: 'read-only' },\n * \t\t\t],\n * \t\t},\n * \t],\n * })\n *\n * try {\n * \tconst values = await terminal.ask(form)\n * \tdeploy(values)\n * } catch (error) {\n * \t// Ctrl-c: the walk ended, and the form is still `editing` for whoever owns it.\n * \tif (isTerminalError(error) && error.code === 'CANCEL') form.destroy()\n * }\n * ```\n */\nexport function createTerminal(options?: TerminalOptions): TerminalInterface {\n\treturn new Terminal(options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["CSI","isObject","isFunction","RETURN","isNumber","isString","isBoolean","stdin","isStreamTarget","stdout","createStyler","createPromptTheme","NEWLINE","sanitizeDisplayText","parseValue","matchesAnswer","renderErrorLine","createInputState","reduceInput","createPasswordState","reducePassword","renderPromptHeader","createConfirmState","reduceConfirm","renderHintedHeader","createEditorState","reduceEditor","createSelectState","reduceSelect","createCheckboxState","reduceCheckbox","parseKey","TerminalError","createInterface"],"sources":["../../../src/server/constants.ts","../../../src/server/helpers.ts","../../../src/server/Terminal.ts","../../../src/server/factories.ts"],"sourcesContent":["// Server-terminals constants — the cursor and line-clear ANSI sequences the interactive `Terminal`\n// driver writes to redraw a field view IN PLACE, plus the copy the walk shows around those views:\n// the per-control format hints, the readline-fallback prompts, and the marks for a locked field and\n// an unavailable choice. UPPER_SNAKE, every member exported. Every sequence is built from the\n// console module's own `CSI` prefix, so no raw control character appears in source and this module\n// declares no second copy of that primitive.\n\nimport type { FieldControl } from '@orkestrel/form'\nimport { CSI } from '@orkestrel/console'\n\n/**\n * Holds the cursor-up sequence template (`ESC[{count}A`) —\n * {@link import('./helpers.js').renderCursorUp} interpolates the `{count}` placeholder with the\n * number of lines to climb. Kept as a template so the count stays out of the constant.\n */\nexport const CSI_UP = `${CSI}{count}A`\n\n/**\n * Hides the cursor (`ESC[?25l`) — written before the driver starts redrawing a prompt so the cursor\n * does not flicker across the view during an in-place re-render; paired with {@link CURSOR_SHOW}.\n */\nexport const CURSOR_HIDE = `${CSI}?25l`\n\n/** Shows the cursor (`ESC[?25h`) — restores the cursor after a prompt resolves / cancels (the {@link CURSOR_HIDE} pair). */\nexport const CURSOR_SHOW = `${CSI}?25h`\n\n/**\n * Erases from the cursor down to the end of the screen (`ESC[J`) — wipes the whole previous view,\n * which a `select` or `checkbox` can spread over several lines, in one write before the new view is\n * rendered, so a redraw never leaves orphaned rows behind.\n */\nexport const CLEAR_DOWN = `${CSI}J`\n\n/**\n * Holds the numbered-list prompt the non-TTY {@link import('./Terminal.js').Terminal} `select` fallback\n * appends — a piped (non-terminal) stream cannot navigate with arrow keys, so the choices are\n * printed numbered and the user types one number on a single readline line.\n */\nexport const FALLBACK_SELECT_HINT = 'Enter a number'\n\n/** Holds the comma-separated multi-select hint the non-TTY `checkbox` fallback shows (the user types one or more numbers). */\nexport const FALLBACK_CHECKBOX_HINT = 'Enter numbers separated by commas'\n\n/** Holds the hint the non-TTY `editor` fallback shows — a piped stream has no ctrl-d, so end of input finishes the block. */\nexport const FALLBACK_EDITOR_HINT = '(end of input finishes)'\n\n/** Holds the hint the non-TTY `confirm` fallback shows — a piped stream sends a whole line, so the answer is typed rather than pressed. */\nexport const FALLBACK_CONFIRM_HINT = '(y/n)'\n\n/**\n * Holds the format cue appended to a field's label for each control the walk reads as a line of text —\n * the terminal has no date picker, no color well, and no file chooser, so the accepted shape is\n * stated instead. A control with no entry needs none: `text` and `editor` accept any line,\n * `password` masks one, and `confirm`, `select`, and `checkbox` are answered by key rather than by\n * format. The form's own rules still decide whether the typed value is acceptable.\n */\nexport const CONTROL_HINTS: Readonly<Partial<Record<FieldControl, string>>> = Object.freeze({\n\tnumber: '(number)',\n\tdate: '(YYYY-MM-DD)',\n\ttime: '(HH:MM)',\n\tdatetime: '(YYYY-MM-DDTHH:MM)',\n\tcolor: '(#rrggbb)',\n\tfile: '(path)',\n})\n\n/** Holds the instruction a `file` field with `multiple` shows before its entries — one path per line, and a blank line ends the list. */\nexport const FILE_HINT = 'One path per line, blank to finish'\n\n/**\n * Holds the lead on the line listing an open `select`'s offered values, which a typed answer can\n * ignore.\n */\nexport const SUGGESTION_LEAD = 'Suggestions'\n\n/**\n * Holds the lead on the line listing the choices a `select` or `checkbox` shows but refuses, so a\n * reader sees why one is missing from the list it heads.\n */\nexport const UNAVAILABLE_LEAD = 'Unavailable'\n\n/** Holds the mark on a locked field's line — the walk renders its value and moves on, because the form refuses an edit there. */\nexport const LOCKED_MARK = '(locked)'\n\n/**\n * States what a field is told when the walk read an answer the control cannot hold — a word typed into a\n * `number`, an off-list value typed into an open `select` whose choice is refused. The value binds\n * as absence and this message is invalidated onto the field, so the walk re-asks it with the reason\n * on screen.\n */\nexport const REFUSAL_MESSAGE = 'Enter a value this field accepts'\n","// Pure helpers for the server terminal driver — every function here is exported and unit-tested.\n// The families here are all total: the stream-boundary guards that narrow `process.stdin` / any\n// injected stream without an assertion; the cursor math the driver uses to redraw a field view IN\n// PLACE (count a view's lines, build the cursor-up sequence, assemble the reposition-and-clear\n// prefix); and the projections and line renderers the whole-form walk needs that no reducer owns —\n// a control read as text, a value shown read-only, the choices a field actually offers, and the\n// group, locked, suggestion, unavailable, and numbered-list lines. The impure driver only feeds\n// bytes into the reducers and writes the strings these helpers build. The output boundary is\n// narrowed by the console module's own `isStreamTarget`, which this module does not redeclare.\n\nimport type { FieldChoice, FieldValue, FormField, TextField } from '@orkestrel/form'\nimport type { PromptTheme } from '@src/core'\nimport type { StylerInterface } from '@orkestrel/console'\nimport type { InputStreamInterface } from './types.js'\nimport { RETURN } from '@src/core'\nimport {\n\tCLEAR_DOWN,\n\tCONTROL_HINTS,\n\tCSI_UP,\n\tLOCKED_MARK,\n\tSUGGESTION_LEAD,\n\tUNAVAILABLE_LEAD,\n} from './constants.js'\nimport { isBoolean, isFunction, isNumber, isObject, isString } from '@orkestrel/contract'\n\n/**\n * Checks whether `value` is a usable {@link InputStreamInterface} — a record with callable `on` /\n * `off` `'data'` subscription methods. A total type guard: it never throws and returns `false` for\n * anything off-shape, so it narrows the one unavoidable input boundary (the real `process.stdin`,\n * or a fake TTY a test injects) to the exact slice the driver reads, never an assertion.\n *\n * @remarks\n * Only `on` / `off` are required (the irreducible event seam); `setRawMode` / `resume` / `pause` /\n * `isTTY` are optional on {@link InputStreamInterface}, so their absence does not disqualify a stream\n * — a piped, non-TTY stream is still a valid input, one the driver reads through the readline\n * fallback rather than raw mode.\n *\n * @param value - Any value crossing the boundary (a process stream, an injected fake, `unknown`)\n * @returns True if `value` has callable `on` and `off`; false otherwise\n */\nexport function isInputStream(value: unknown): value is InputStreamInterface {\n\treturn (\n\t\tisObject(value) &&\n\t\t'on' in value &&\n\t\tisFunction(value.on) &&\n\t\t'off' in value &&\n\t\tisFunction(value.off)\n\t)\n}\n\n/**\n * Checks whether `value` is a Node {@link NodeJS.ReadableStream} — a total structural guard\n * checking for the callable `read` / `pipe` / `on` that `node:readline`'s `createInterface`\n * requires as its `input`. The non-TTY fallback narrows the resolved input stream through this\n * before handing it to readline, never through an assertion, so a real piped `process.stdin` (or a\n * `PassThrough` a test injects) crosses into the readline boundary honestly. Never throws; returns\n * `false` for a minimal fake that isn't a full readable.\n *\n * @param value - The resolved input stream (or any value crossing the boundary)\n * @returns True if `value` has the readable methods readline needs; false otherwise\n */\nexport function isReadable(value: unknown): value is NodeJS.ReadableStream {\n\treturn (\n\t\tisObject(value) &&\n\t\t'read' in value &&\n\t\tisFunction(value.read) &&\n\t\t'pipe' in value &&\n\t\tisFunction(value.pipe)\n\t)\n}\n\n/**\n * Checks whether an input stream can be driven in raw mode — it reports `isTTY === true` and\n * exposes a callable `setRawMode`. The {@link import('./Terminal.js').Terminal} probes this to\n * choose its path: true selects the interactive raw-mode fields, with arrow-key navigation and a\n * live re-render; false selects the `node:readline` line-input fallback, because a piped or\n * non-terminal stream cannot enter raw mode. Total — never throws.\n *\n * @param input - The resolved {@link InputStreamInterface}\n * @returns True if the stream is a TTY with `setRawMode`; false otherwise\n */\nexport function supportsRawMode(input: InputStreamInterface): boolean {\n\treturn input.isTTY === true && isFunction(input.setRawMode)\n}\n\n/**\n * Counts the terminal lines a rendered prompt `view` occupies — one more than its newline count, so\n * a view with no newline is a single line and a view with N newlines spans N+1 lines. The basis of\n * the in-place re-render: the driver records the line count of the view it wrote so the next redraw\n * knows how far up to move the cursor before overwriting. Total; an empty string is one empty line.\n *\n * @param view - The rendered (possibly multi-line, possibly ANSI-styled) view string\n * @returns The number of lines the view spans (always at least 1)\n */\nexport function lineCount(view: string): number {\n\tlet lines = 1\n\tfor (const character of view) {\n\t\tif (character === '\\n') lines += 1\n\t}\n\treturn lines\n}\n\n/**\n * Returns the cursor-up control sequence that moves the cursor up `count` lines (`ESC[{count}A`),\n * or the empty string when `count` is zero or negative, because no movement is needed and `ESC[0A`\n * is a wasted write. The pure step the in-place re-render uses to climb back over the previous view\n * before clearing it. Total.\n *\n * @param count - How many lines to move the cursor up\n * @returns The `ESC[{count}A` sequence, or `''` when `count <= 0`\n */\nexport function renderCursorUp(count: number): string {\n\tif (count <= 0) return ''\n\treturn `${CSI_UP.replace('{count}', String(count))}`\n}\n\n/**\n * Returns the full reposition-and-clear prefix to write before re-rendering a prompt view in place\n * — given the line count of the previous view, it moves the cursor up over those lines, returns it\n * to column 0, and erases everything from there to the end of the screen, so the next view is drawn\n * on a clean region and a taller previous view leaves no orphaned rows. Pure; the driver writes\n * this immediately followed by the new view.\n *\n * @remarks\n * For the first render `previousLines` is `1` (the cursor sits on the line the prompt opened on) so\n * the prefix is a carriage return + clear-down — the prompt draws from the current line. For a\n * subsequent render it climbs `previousLines - 1` lines (the cursor is on the last line of the prior\n * view) before clearing. Keeping the math here (not in the driver) makes the re-render unit-testable\n * without a real terminal.\n *\n * @param previousLines - The line count of the view on screen (from {@link lineCount})\n * @returns The control-sequence prefix to write before the new view\n */\nexport function redrawPrefix(previousLines: number): string {\n\treturn `${renderCursorUp(previousLines - 1)}${RETURN}${CLEAR_DOWN}`\n}\n\n/**\n * Projects any field the walk reads as a line of text into the {@link TextField} the text reducer\n * takes — `text` itself, and the controls a terminal has no widget for: `number`, `date`, `time`,\n * `datetime`, `color`, and one `file` entry. The label carries that control's format cue from\n * {@link CONTROL_HINTS}, and a declared `default` becomes the line a bare return submits. The\n * projection carries no rule, because the authoritative form still evaluates the answer this line\n * binds; it exists only so one reducer covers every one of them.\n *\n * @param field - The field being read\n * @returns The text field the reducer renders for it\n *\n * @example\n * ```ts\n * fieldToText({ control: 'date', name: 'born', label: 'Birthday' })\n * // { control: 'text', name: 'born', label: 'Birthday (YYYY-MM-DD)' }\n * ```\n */\nexport function fieldToText(field: FormField): TextField {\n\tconst label = field.label ?? field.name\n\tconst hint = CONTROL_HINTS[field.control]\n\tconst seed = 'default' in field ? field.default : undefined\n\tconst text = isNumber(seed) ? String(seed) : seed\n\treturn {\n\t\tcontrol: 'text',\n\t\tname: field.name,\n\t\tlabel: hint === undefined ? label : `${label} ${hint}`,\n\t\t...(isString(text) ? { default: text } : {}),\n\t}\n}\n\n/**\n * Projects one held answer into the text a read-only line shows — a scalar as itself, a boolean as\n * `yes` / `no` (the word the confirm reducer commits), and a list joined by commas. Absence renders\n * as nothing, because a locked field nobody has answered has nothing to show.\n *\n * @param value - The answer the form holds for a field, or absence\n * @returns The text to render for it\n */\nexport function valueToText(value: FieldValue | undefined): string {\n\tif (value === undefined) return ''\n\tif (isString(value)) return value\n\tif (isNumber(value)) return String(value)\n\tif (isBoolean(value)) return value ? 'yes' : 'no'\n\treturn value.join(', ')\n}\n\n/**\n * Returns the choices a `select` or `checkbox` field actually offers — the form refuses a disabled\n * choice's value at every door, including a fill, so the walk never puts one in front of the\n * cursor. Pair with {@link filterDisabled} to tell the reader what was withheld.\n *\n * @param choices - The field's declared choices\n * @returns The choices the walk offers, in declared order\n */\nexport function filterEnabled(choices: readonly FieldChoice[]): readonly FieldChoice[] {\n\treturn choices.filter((choice) => choice.disabled !== true)\n}\n\n/**\n * Returns the choices a `select` or `checkbox` field shows but refuses — the complement of\n * {@link filterEnabled}, rendered by {@link renderUnavailableLine} above the list so a reader sees\n * why a declared choice is missing from it.\n *\n * @param choices - The field's declared choices\n * @returns The refused choices, in declared order\n */\nexport function filterDisabled(choices: readonly FieldChoice[]): readonly FieldChoice[] {\n\treturn choices.filter((choice) => choice.disabled === true)\n}\n\n/**\n * Renders the section header the walk writes when it enters a new field group, painted by the\n * `message` role.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param label - The group's declared label, falling back to its own name\n * @returns The rendered section header\n */\nexport function renderGroupHeader(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tlabel: string,\n): string {\n\treturn styler.render(theme.roles.message, label)\n}\n\n/**\n * Renders the read-only line a locked field shows — its label, the {@link LOCKED_MARK}, and the\n * answer the form already holds. The walk writes this instead of a prompt, because the field is\n * still validated and still submitted but must not be edited here.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param label - The field's label\n * @param value - The held answer, from {@link valueToText}\n * @returns The rendered line, with no trailing space when there is nothing to show\n */\nexport function renderLockedLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tlabel: string,\n\tvalue: string,\n): string {\n\tconst head = `${styler.render(theme.roles.muted, theme.icons.dot)} ${styler.render(theme.roles.message, label)} ${styler.render(theme.roles.hint, LOCKED_MARK)}`\n\treturn value.length === 0 ? head : `${head} ${styler.render(theme.roles.content, value)}`\n}\n\n/**\n * Renders the line listing an open select's offered values above its text prompt — a suggestion\n * list, because an open select admits an answer the list does not offer.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param choices - The choices the open select offers, from {@link filterEnabled}\n * @returns The rendered suggestion line\n */\nexport function renderSuggestionLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tchoices: readonly FieldChoice[],\n): string {\n\tconst values = choices.map((choice) => choice.value).join(', ')\n\treturn styler.render(theme.roles.hint, `${SUGGESTION_LEAD}: ${values}`)\n}\n\n/**\n * Renders the line naming the choices a field shows but refuses, written above the list the walk\n * drives.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param choices - The refused choices, from {@link filterDisabled}\n * @returns The rendered unavailable line\n */\nexport function renderUnavailableLine(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tchoices: readonly FieldChoice[],\n): string {\n\tconst labels = choices.map((choice) => choice.label).join(', ')\n\treturn styler.render(theme.roles.muted, `${UNAVAILABLE_LEAD}: ${labels}`)\n}\n\n/**\n * Renders the numbered choice list the non-TTY fallback prints — a piped stream cannot navigate\n * with arrow keys, so each offered choice is printed with the number the reader types back. One\n * line per choice, with no trailing newline.\n *\n * @param styler - The console styler that renders each role\n * @param theme - The resolved prompt theme\n * @param choices - The choices the walk offers, from {@link filterEnabled}\n * @returns The rendered list\n */\nexport function renderNumberedList(\n\tstyler: StylerInterface,\n\ttheme: PromptTheme,\n\tchoices: readonly FieldChoice[],\n): string {\n\treturn choices\n\t\t.map(\n\t\t\t(choice, index) =>\n\t\t\t\t` ${styler.render(theme.roles.muted, `${String(index + 1)})`)} ${styler.render(theme.roles.content, choice.label)}`,\n\t\t)\n\t\t.join('\\n')\n}\n","import type {\n\tFieldChoice,\n\tFieldError,\n\tFieldValue,\n\tFormField,\n\tFormInterface,\n\tFormValues,\n} from '@orkestrel/form'\nimport type {\n\tCheckboxField,\n\tConfirmField,\n\tEditorField,\n\tFileField,\n\tPasswordField,\n\tSelectField,\n} from '@orkestrel/form'\nimport type { KeyEvent, PromptStep, PromptTheme, TerminalInterface } from '@src/core'\nimport type { StylerInterface } from '@orkestrel/console'\nimport type { StreamTargetInterface } from '@orkestrel/console/server'\nimport type { Interface } from 'node:readline'\nimport type { InputStreamInterface, TerminalOptions } from './types.js'\nimport {\n\tcreateCheckboxState,\n\tcreateConfirmState,\n\tcreateEditorState,\n\tcreateInputState,\n\tcreatePasswordState,\n\tcreatePromptTheme,\n\tcreateSelectState,\n\tNEWLINE,\n\tparseKey,\n\treduceCheckbox,\n\treduceConfirm,\n\treduceEditor,\n\treduceInput,\n\treducePassword,\n\treduceSelect,\n\trenderErrorLine,\n\trenderHintedHeader,\n\trenderPromptHeader,\n\tsanitizeDisplayText,\n\tTerminalError,\n} from '@src/core'\nimport { matchesAnswer, parseValue } from '@orkestrel/form'\nimport { createStyler } from '@orkestrel/console'\nimport { isStreamTarget } from '@orkestrel/console/server'\nimport { createInterface } from 'node:readline'\nimport { stdin, stdout } from 'node:process'\nimport {\n\tCURSOR_HIDE,\n\tCURSOR_SHOW,\n\tFALLBACK_CHECKBOX_HINT,\n\tFALLBACK_CONFIRM_HINT,\n\tFALLBACK_EDITOR_HINT,\n\tFALLBACK_SELECT_HINT,\n\tFILE_HINT,\n\tREFUSAL_MESSAGE,\n} from './constants.js'\nimport {\n\tfieldToText,\n\tfilterDisabled,\n\tfilterEnabled,\n\tisInputStream,\n\tisReadable,\n\tlineCount,\n\tredrawPrefix,\n\trenderGroupHeader,\n\trenderLockedLine,\n\trenderNumberedList,\n\trenderSuggestionLine,\n\trenderUnavailableLine,\n\tsupportsRawMode,\n\tvalueToText,\n} from './helpers.js'\n\n/**\n * Implements {@link TerminalInterface} for a human at this machine's keyboard — the interactive\n * form driver, and the only impure part of the terminal stack. {@link ask} walks one form's fields\n * in schema order, feeds raw-mode stdin bytes through `parseKey` into the matching pure reducer,\n * renders each returned view in place, binds every answer through the form's own `fill`, and\n * re-asks what the form refused. It owns no form logic: the schema, the rules, the values, and the\n * settlement all belong to the form it is given, and this class owns only raw mode, the cursor, and\n * the re-render.\n *\n * @remarks\n * See {@link TerminalInterface} for the driving contract. The walk itself:\n *\n * - **Controls and reducers.** `text`, `number`, `date`, `time`, `datetime`, `color`, and\n * each `file` entry are read as one line of text through {@link fieldToText}, which appends that\n * control's format cue to the label. `password`, `confirm`, `editor`, `select`, and `checkbox`\n * each drive their own reducer. An open `select` is a suggestion list plus a typed line, because\n * `open` means the answer need not come from the list.\n * - **The binding projects through `matchesAnswer`.** Every answer is filled as\n * `fill(name, matchesAnswer(value) ? value : undefined)` after `parseValue` has coerced it to the\n * control's own shape, so a bare return on a field with no default binds as absence and the\n * form's `required` rule refuses it. A typed answer the control cannot hold binds as absence and\n * invalidates the field, so the walk asks again with the reason on screen.\n * - **Visibility is honored.** A `hidden` field and a field in `form.disabled` are skipped; a\n * `locked` field renders read-only; entering a new group writes its label as a section header.\n * - **Refusal re-asks.** After the walk the form is submitted. A refusal re-walks only the erroring\n * fields the walk can edit and submits again. When every erroring field is one the walk cannot\n * edit — hidden, locked, or disabled — the form is abandoned instead, because asking again could\n * not change the answer.\n * - **Raw-mode leak-free.** Raw mode is entered once per field and always cleaned up: on submit, on\n * cancel, on a throw, and when the form is abandoned under an active read.\n * - **Non-TTY fallback.** When `input` is not a TTY, the same walk runs over `node:readline` line\n * input: `select` and `checkbox` print a numbered list, and `editor` reads to end of input.\n */\nexport class Terminal implements TerminalInterface {\n\treadonly #input: InputStreamInterface\n\treadonly #output: StreamTargetInterface\n\treadonly #styler: StylerInterface\n\treadonly #theme: PromptTheme\n\treadonly #handlers = new Map<object, (chunk: string | Uint8Array) => void>()\n\treadonly #queue: string[] = []\n\t#interface: Interface | undefined\n\t#taker: ((line: string | undefined) => void) | undefined\n\t#ended = false\n\n\tconstructor(options?: TerminalOptions) {\n\t\t// Resolve each stream through its guard: a present, well-shaped injected stream is used as is;\n\t\t// otherwise the real process stream — no assertion, and an absent option falls through.\n\t\tthis.#input = isInputStream(options?.input) ? options.input : stdin\n\t\tthis.#output = isStreamTarget(options?.output) ? options.output : stdout\n\t\tthis.#styler = createStyler()\n\t\tthis.#theme = createPromptTheme(options?.theme)\n\t}\n\n\t// === The contract\n\n\tasync ask(form: FormInterface): Promise<FormValues> {\n\t\ttry {\n\t\t\tthis.#report(form, form.errors)\n\t\t\tawait this.#walk(form, form.schema.fields)\n\t\t\tfor (;;) {\n\t\t\t\tif (form.status !== 'editing') return await form.answer\n\t\t\t\tconst result = form.submit()\n\t\t\t\tif (result.success) return await form.answer\n\t\t\t\tthis.#report(form, result.error)\n\t\t\t\tconst again = this.#collectEditable(form, result.error)\n\t\t\t\t// Nothing the walk can edit, or an input stream that has already ended: asking again would\n\t\t\t\t// render the same fields and read the same answers forever, so abandon the form instead.\n\t\t\t\t// The failures are on screen either way, so the reader sees what could not be answered.\n\t\t\t\tif (again.length === 0 || this.#ended) {\n\t\t\t\t\tform.destroy()\n\t\t\t\t\treturn await form.answer\n\t\t\t\t}\n\t\t\t\tawait this.#walk(form, again)\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#close()\n\t\t}\n\t}\n\n\t// === The walk\n\n\t/**\n\t * Asks `fields` in the order given, skipping what the walk must not touch and rendering what it\n\t * must not edit. Returns early the moment the form stops being editable, so an abandoned form\n\t * ends the walk between fields as well as under an active read.\n\t */\n\tasync #walk(form: FormInterface, fields: readonly FormField[]): Promise<void> {\n\t\tlet group: string | undefined\n\t\tfor (const field of fields) {\n\t\t\tif (form.status !== 'editing') return\n\t\t\tif (field.hidden === true) continue\n\t\t\tif (form.disabled.has(field.name)) continue\n\t\t\tif (field.group !== group) {\n\t\t\t\tgroup = field.group\n\t\t\t\tif (group !== undefined) this.#writeGroup(form, group)\n\t\t\t}\n\t\t\tif (field.locked === true) {\n\t\t\t\tthis.#writeLocked(form, field)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst raw = await this.#read(form, field)\n\t\t\tif (form.status !== 'editing') return\n\t\t\tthis.#bind(form, field, raw)\n\t\t}\n\t}\n\n\t/** Writes a group's label as a section header, resolving the schema's declared label and falling back to the group's own name. */\n\t#writeGroup(form: FormInterface, name: string): void {\n\t\tconst label = form.schema.groups?.find((group) => group.name === name)?.label ?? name\n\t\tthis.#output.write(\n\t\t\t`${NEWLINE}${renderGroupHeader(this.#styler, this.#theme, sanitizeDisplayText(label))}${NEWLINE}`,\n\t\t)\n\t}\n\n\t/** Renders a locked field read-only — its label, its mark, and the answer the form already holds. */\n\t#writeLocked(form: FormInterface, field: FormField): void {\n\t\tconst line = renderLockedLine(\n\t\t\tthis.#styler,\n\t\t\tthis.#theme,\n\t\t\tsanitizeDisplayText(field.label ?? field.name),\n\t\t\tsanitizeDisplayText(valueToText(form.values[field.name])),\n\t\t)\n\t\tthis.#output.write(`${line}${NEWLINE}`)\n\t}\n\n\t/** Reads one field through the reducer its control names, resolving the raw answer the binding projects. */\n\t#read(form: FormInterface, field: FormField): Promise<FieldValue | undefined> {\n\t\tif (field.control === 'password') return this.#askPassword(form, field)\n\t\tif (field.control === 'confirm') return this.#confirm(form, field)\n\t\tif (field.control === 'editor') return this.#askEditor(form, field)\n\t\tif (field.control === 'select') return this.#select(form, field)\n\t\tif (field.control === 'checkbox') return this.#checkbox(form, field)\n\t\tif (field.control === 'file') return this.#file(form, field)\n\t\treturn this.#askText(form, field)\n\t}\n\n\t/**\n\t * Binds one raw answer to the form — the single binding, and the only place this driver writes a\n\t * value. `parseValue` coerces the raw answer to the control's own shape and `matchesAnswer`\n\t * projects a blank one to absence, which is what keeps `required` refusing a bare return. A raw\n\t * answer the control cannot hold binds as absence and invalidates the field, so the field comes\n\t * back on the next pass carrying the reason rather than vanishing silently.\n\t */\n\t#bind(form: FormInterface, field: FormField, raw: FieldValue | undefined): void {\n\t\tform.touch(field.name)\n\t\tconst value = raw === undefined ? undefined : parseValue(field, raw)\n\t\tform.fill(field.name, matchesAnswer(value) ? value : undefined)\n\t\tif (value === undefined && matchesAnswer(raw)) form.invalidate(field.name, REFUSAL_MESSAGE)\n\t}\n\n\t/** Collects the erroring fields the walk can ask again — every field the walk skips or renders read-only is excluded, because a second pass could not change its answer. */\n\t#collectEditable(form: FormInterface, errors: readonly FieldError[]): readonly FormField[] {\n\t\tconst fields: FormField[] = []\n\t\tfor (const error of errors) {\n\t\t\tconst field = form.field(error.field)\n\t\t\tif (field === undefined) continue\n\t\t\tif (field.hidden === true || field.locked === true) continue\n\t\t\tif (form.disabled.has(field.name)) continue\n\t\t\tif (fields.some((held) => held.name === field.name)) continue\n\t\t\tfields.push(field)\n\t\t}\n\t\treturn fields\n\t}\n\n\t/** Writes every supplied failure against its field's label. */\n\t#report(form: FormInterface, errors: readonly FieldError[]): void {\n\t\tfor (const error of errors) {\n\t\t\tconst label = sanitizeDisplayText(form.field(error.field)?.label ?? error.field)\n\t\t\tconst message = sanitizeDisplayText(error.message)\n\t\t\tconst line = renderErrorLine(this.#styler, this.#theme, `${label}: ${message}`)\n\t\t\tthis.#output.write(`${line}${NEWLINE}`)\n\t\t}\n\t}\n\n\t// === The controls\n\n\t/** Reads a field as one line of text — `text` itself and the controls a terminal has no widget for. */\n\t#askText(form: FormInterface, field: FormField): Promise<string> {\n\t\tconst state = createInputState(fieldToText(field), this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceInput)\n\t\treturn this.#line(form, state.message, state.default)\n\t}\n\n\t/** Reads a secret — masked live in raw mode, and read without echo through readline on a stream that cannot enter it. */\n\t#askPassword(form: FormInterface, field: PasswordField): Promise<string> {\n\t\tconst state = createPasswordState(field, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reducePassword)\n\t\treturn this.#prompt(form, renderPromptHeader(this.#styler, this.#theme, state.message))\n\t}\n\n\t/**\n\t * Reads a yes or no. The fallback accepts `y` / `yes` / `n` / `no` in any case and takes the\n\t * field's default for a bare line; anything else is returned as typed, so the binding refuses it\n\t * and the walk asks again rather than silently reading it as no.\n\t */\n\tasync #confirm(form: FormInterface, field: ConfirmField): Promise<FieldValue> {\n\t\tconst state = createConfirmState(field, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceConfirm)\n\t\tconst header = renderHintedHeader(\n\t\t\tthis.#styler,\n\t\t\tthis.#theme,\n\t\t\tstate.message,\n\t\t\tFALLBACK_CONFIRM_HINT,\n\t\t)\n\t\tconst line = await this.#prompt(form, header)\n\t\tconst answer = line.trim().toLowerCase()\n\t\tif (answer.length === 0) return state.default\n\t\tif (answer === 'y' || answer === 'yes') return true\n\t\tif (answer === 'n' || answer === 'no') return false\n\t\treturn line.trim()\n\t}\n\n\t/** Reads text over many lines — ctrl-d finishes in raw mode, and end of input finishes on a piped stream. */\n\t#askEditor(form: FormInterface, field: EditorField): Promise<string> {\n\t\tconst state = createEditorState(field, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceEditor)\n\t\treturn this.#block(form, state.message, state.default)\n\t}\n\n\t/**\n\t * Reads one choice. A disabled choice is named above the list and never offered, because the form\n\t * refuses its value at every door. An open select is a suggestion list plus a typed line, since\n\t * `open` admits an answer the list does not offer; a closed select with nothing left to offer\n\t * resolves absence and lets the form's own rules report it.\n\t */\n\tasync #select(form: FormInterface, field: SelectField): Promise<FieldValue | undefined> {\n\t\tconst choices = filterEnabled(field.choices)\n\t\tthis.#writeUnavailable(field.choices)\n\t\tif (field.open === true) {\n\t\t\tif (choices.length > 0) {\n\t\t\t\tconst display = choices.map((choice) => ({\n\t\t\t\t\t...choice,\n\t\t\t\t\tvalue: sanitizeDisplayText(choice.value),\n\t\t\t\t}))\n\t\t\t\tthis.#output.write(`${renderSuggestionLine(this.#styler, this.#theme, display)}${NEWLINE}`)\n\t\t\t}\n\t\t\treturn this.#askText(form, field)\n\t\t}\n\t\tif (choices.length === 0) return undefined\n\t\tconst state = createSelectState({ ...field, choices }, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceSelect)\n\t\tthis.#writeList(state.message, choices)\n\t\tconst line = await this.#prompt(form, this.#formatHint(`${FALLBACK_SELECT_HINT}:`))\n\t\treturn choices[Number.parseInt(line.trim(), 10) - 1]?.value\n\t}\n\n\t/**\n\t * Reads any number of choices. Disabled choices are named above the list and never offered, so a\n\t * box the form would refuse can never be ticked. An empty answer is an answered \"none of them\",\n\t * which is what `matchesAnswer` says an empty list means.\n\t */\n\tasync #checkbox(form: FormInterface, field: CheckboxField): Promise<FieldValue> {\n\t\tconst choices = filterEnabled(field.choices)\n\t\tthis.#writeUnavailable(field.choices)\n\t\tconst state = createCheckboxState({ ...field, choices }, this.#styler, this.#theme)\n\t\tif (supportsRawMode(this.#input)) return this.#drive(form, state, reduceCheckbox)\n\t\tthis.#writeList(state.message, choices)\n\t\tconst line = await this.#prompt(form, this.#formatHint(`${FALLBACK_CHECKBOX_HINT}:`))\n\t\treturn line\n\t\t\t.split(',')\n\t\t\t.map((part) => choices[Number.parseInt(part.trim(), 10) - 1]?.value)\n\t\t\t.filter((value): value is string => value !== undefined)\n\t}\n\n\t/**\n\t * Reads file paths — names only, because bytes never enter a form. A `multiple` field collects one\n\t * path per line until a blank one, and a single field takes one. No path at all is absence rather\n\t * than an empty list, because the reader answered a blank line.\n\t */\n\tasync #file(form: FormInterface, field: FileField): Promise<FieldValue | undefined> {\n\t\tif (field.multiple === true) this.#output.write(`${this.#formatHint(FILE_HINT)}${NEWLINE}`)\n\t\tconst paths: string[] = []\n\t\tfor (;;) {\n\t\t\tconst entry = await this.#askText(form, field)\n\t\t\tif (entry.length === 0) break\n\t\t\tpaths.push(entry)\n\t\t\tif (field.multiple !== true || this.#ended) break\n\t\t}\n\t\treturn paths.length > 0 ? paths : undefined\n\t}\n\n\t/** Names the choices a field shows but refuses, above the list the walk drives. */\n\t#writeUnavailable(choices: readonly FieldChoice[]): void {\n\t\tconst refused = filterDisabled(choices)\n\t\tif (refused.length === 0) return\n\t\tthis.#output.write(`${renderUnavailableLine(this.#styler, this.#theme, refused)}${NEWLINE}`)\n\t}\n\n\t/** Writes a field's header above its numbered choice list — the non-TTY presentation of a choice field. */\n\t#writeList(message: string, choices: readonly FieldChoice[]): void {\n\t\tconst header = renderPromptHeader(this.#styler, this.#theme, message)\n\t\tconst list = renderNumberedList(this.#styler, this.#theme, choices)\n\t\tthis.#output.write(`${header}${NEWLINE}${list}${NEWLINE}`)\n\t}\n\n\t/** Paints one line of supplementary instruction through the `hint` role. */\n\t#formatHint(text: string): string {\n\t\treturn this.#styler.render(this.#theme.roles.hint, text)\n\t}\n\n\t// === The raw-mode kernel\n\n\t/**\n\t * Isolates the irreducible Node raw-mode primitive — the ONLY place raw mode is touched. Switches the input\n\t * into raw mode (each keypress delivered immediately, no echo), resumes its flow, and subscribes\n\t * `handler` to `'data'`. The paired {@link #leaveRaw} operation unsubscribes the exact handler,\n\t * leaves raw mode, and pauses the stream on submit, cancel, abandon, or throw.\n\t */\n\t#enterRaw(token: object, handler: (chunk: string | Uint8Array) => void): void {\n\t\tthis.#handlers.set(token, handler)\n\t\tthis.#input.setRawMode?.(true)\n\t\tthis.#input.resume?.()\n\t\tthis.#input.on('data', handler)\n\t}\n\n\t#leaveRaw(token: object): void {\n\t\tconst handler = this.#handlers.get(token)\n\t\tif (handler === undefined) return\n\t\tthis.#handlers.delete(token)\n\t\tthis.#input.off('data', handler)\n\t\tthis.#input.setRawMode?.(false)\n\t\tthis.#input.pause?.()\n\t}\n\n\t/**\n\t * Drives ONE field over raw-mode stdin — the generic engine every interactive control shares.\n\t * Renders the reducer's initial view, enters raw mode once, and on each keypress runs `parseKey` →\n\t * `reduce` → an in-place re-render; on `submit` it cleans up and resolves the reducer's value, on\n\t * `cancel` (ctrl-c) it cleans up and rejects a {@link TerminalError} coded `CANCEL`. Abandoning\n\t * the form ends the read too, through the rejection of its own `answer`. Raw mode is entered\n\t * exactly once and cleaned up on every exit path, so no raw mode and no `'data'` listener ever\n\t * leaks. The cursor is hidden for the duration and restored on exit.\n\t */\n\t#drive<T, S>(\n\t\tform: FormInterface,\n\t\tinitial: S,\n\t\treduce: (state: S, key: KeyEvent) => PromptStep<T, S>,\n\t): Promise<T> {\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\t// Render the first view, tracking how many lines it spans so the next redraw climbs over them.\n\t\t\tthis.#output.write(CURSOR_HIDE)\n\t\t\tlet firstView: string\n\t\t\ttry {\n\t\t\t\tfirstView = reduce(initial, parseKey('')).view\n\t\t\t\tthis.#output.write(firstView)\n\t\t\t} catch (error) {\n\t\t\t\t// Raw mode was never entered yet, but the cursor was hidden — restore it before rejecting.\n\t\t\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\t\t\treject(error)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst token = {}\n\t\t\tconst handler = this.#createHandler(\n\t\t\t\ttoken,\n\t\t\t\tinitial,\n\t\t\t\tlineCount(firstView),\n\t\t\t\treduce,\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t)\n\t\t\tthis.#enterRaw(token, handler)\n\t\t\t// An abandoned form ends the read: its own `answer` rejects, so release the terminal and fail\n\t\t\t// this field with the form's error. A read that already finished holds no token and is a no-op.\n\t\t\tvoid form.answer.catch((error: unknown) => this.#interrupt(token, reject, error))\n\t\t})\n\t}\n\n\t#createHandler<T, S>(\n\t\ttoken: object,\n\t\tinitial: S,\n\t\tinitialLines: number,\n\t\treduce: (state: S, key: KeyEvent) => PromptStep<T, S>,\n\t\tresolve: (value: T | PromiseLike<T>) => void,\n\t\treject: (reason?: unknown) => void,\n\t): (chunk: string | Uint8Array) => void {\n\t\tlet state = initial\n\t\tlet lines = initialLines\n\t\treturn (chunk) => {\n\t\t\ttry {\n\t\t\t\tconst step = reduce(state, parseKey(chunk))\n\t\t\t\tstate = step.state\n\t\t\t\tif (step.status === 'active') {\n\t\t\t\t\tthis.#render(step.view, lines)\n\t\t\t\t\tlines = lineCount(step.view)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// Terminal step (submit / cancel): paint the final committed view, then tear down.\n\t\t\t\tthis.#render(step.view, lines)\n\t\t\t\tthis.#leaveRaw(token)\n\t\t\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\t\t\tif (step.status === 'cancel') reject(new TerminalError('CANCEL', 'Prompt cancelled'))\n\t\t\t\telse if (step.status === 'submit' && step.value !== undefined) resolve(step.value)\n\t\t\t\telse reject(new TerminalError('DRIVER', 'submit produced no value'))\n\t\t\t} catch (error) {\n\t\t\t\t// A throw inside the reducer/styler: tear down raw mode + the listener, restore the cursor\n\t\t\t\t// exactly as the normal exit path does, then reject.\n\t\t\t\tthis.#leaveRaw(token)\n\t\t\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\t\t\treject(error)\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Ends an active read that the form outlived — releases raw mode exactly as a submit does, then fails the read with the form's own error. */\n\t#interrupt(token: object, reject: (reason?: unknown) => void, error: unknown): void {\n\t\tif (!this.#handlers.has(token)) return\n\t\tthis.#leaveRaw(token)\n\t\tthis.#output.write(`${CURSOR_SHOW}${NEWLINE}`)\n\t\treject(error)\n\t}\n\n\t/** Redraws a field view in place — climbs over the previous view's `previousLines`, clears, and writes the new view (the pure cursor-math is {@link redrawPrefix}). */\n\t#render(view: string, previousLines: number): void {\n\t\tthis.#output.write(`${redrawPrefix(previousLines)}${view}`)\n\t}\n\n\t// === Non-TTY fallbacks (node:readline line input)\n\n\t/** Reads one line for a text-shaped field, taking the field's default when the line is bare. */\n\tasync #line(form: FormInterface, message: string, seed: string): Promise<string> {\n\t\tconst answer = await this.#prompt(form, renderPromptHeader(this.#styler, this.#theme, message))\n\t\treturn answer.length > 0 ? answer : seed\n\t}\n\n\t/**\n\t * Reads a whole block for an `editor` field — a piped stream has no ctrl-d keypress, so end of\n\t * input is the terminator. It drains every remaining line, which is why nothing after an `editor`\n\t * field can be asked on the same stream.\n\t */\n\tasync #block(form: FormInterface, message: string, seed: string): Promise<string> {\n\t\tconst header = renderHintedHeader(this.#styler, this.#theme, message, FALLBACK_EDITOR_HINT)\n\t\tthis.#output.write(`${header}${NEWLINE}`)\n\t\tconst collected: string[] = []\n\t\tfor (;;) {\n\t\t\tconst line = await this.#next(form)\n\t\t\tif (line === undefined) break\n\t\t\tcollected.push(line)\n\t\t}\n\t\tconst text = collected.join('\\n')\n\t\treturn text.length > 0 ? text : seed\n\t}\n\n\t/** Writes one field's header and reads back the line the reader answers it with, or `''` once the stream has ended. */\n\tasync #prompt(form: FormInterface, header: string): Promise<string> {\n\t\tthis.#output.write(`${header} `)\n\t\tconst line = await this.#next(form)\n\t\treturn line ?? ''\n\t}\n\n\t/**\n\t * Takes the next line the input carries — from the buffer when the reader has already read ahead,\n\t * and otherwise by waiting for one. It resolves ABSENCE once the stream has ended, so a walk over\n\t * an exhausted stream settles instead of waiting forever, and it rejects when the form is\n\t * abandoned, so an unanswerable read ends with the form rather than outliving it.\n\t */\n\t#next(form: FormInterface): Promise<string | undefined> {\n\t\tconst buffered = this.#queue.shift()\n\t\tif (buffered !== undefined) return Promise.resolve(buffered)\n\t\tif (this.#ended) return Promise.resolve(undefined)\n\t\tthis.#startReader()\n\t\treturn new Promise<string | undefined>((resolve, reject) => {\n\t\t\tthis.#taker = resolve\n\t\t\tvoid form.answer.catch((error: unknown) => {\n\t\t\t\tif (this.#taker !== resolve) return\n\t\t\t\tthis.#taker = undefined\n\t\t\t\treject(error)\n\t\t\t})\n\t\t})\n\t}\n\n\t/**\n\t * Opens the walk's ONE readline interface, or keeps the open one. A whole form is many questions\n\t * over one stream, and an interface reads ahead: a fresh interface per question would swallow\n\t * every line it read past the one it was asked for, so the walk holds a single reader and buffers\n\t * what arrives early. It is closed when the walk ends, so the process is free to exit.\n\t */\n\t#startReader(): void {\n\t\tif (this.#interface !== undefined) return\n\t\tconst rl = createInterface(this.#openReadline())\n\t\trl.on('line', (line) => this.#accept(line))\n\t\trl.on('close', () => this.#finish())\n\t\tthis.#interface = rl\n\t}\n\n\t/** Hands a line to whoever is waiting for it, or buffers it for the next question. */\n\t#accept(line: string): void {\n\t\tconst taker = this.#taker\n\t\tif (taker === undefined) {\n\t\t\tthis.#queue.push(line)\n\t\t\treturn\n\t\t}\n\t\tthis.#taker = undefined\n\t\ttaker(line)\n\t}\n\n\t/** Records that no further line can arrive and releases whoever was waiting for one. */\n\t#finish(): void {\n\t\tthis.#ended = true\n\t\tconst taker = this.#taker\n\t\tif (taker === undefined) return\n\t\tthis.#taker = undefined\n\t\ttaker(undefined)\n\t}\n\n\t/** Closes the walk's reader and releases a waiting question — the walk is over, so the stream must stop holding the process open. */\n\t#close(): void {\n\t\tconst taker = this.#taker\n\t\tthis.#taker = undefined\n\t\tif (taker !== undefined) taker(undefined)\n\t\tconst rl = this.#interface\n\t\tif (rl === undefined) return\n\t\tthis.#interface = undefined\n\t\t// Drop the close listener first: this close is the walk ending, not the stream ending, and\n\t\t// recording it as end of input would refuse the next walk its input.\n\t\trl.removeAllListeners('close')\n\t\trl.close()\n\t}\n\n\t/**\n\t * Narrows the resolved input to the `node:readline` `createInterface` boundary, never an assertion.\n\t * The fallback only runs on a real piped `process.stdin` (or a `PassThrough` a test injects), both\n\t * genuine readables; a minimal non-readable fake reaching here means the walk was driven with a\n\t * stream it cannot use, which fails loudly rather than silently. `terminal: false` leaves readline\n\t * as a line decoder and nothing else: the walk writes every prompt itself, through the same output\n\t * every other line goes to, so nothing is written twice and a secret is never echoed.\n\t */\n\t#openReadline(): { input: NodeJS.ReadableStream; terminal: boolean } {\n\t\t// Bind to a local first — a guard narrows a local, not a `#private` field access.\n\t\tconst input = this.#input\n\t\tif (!isReadable(input))\n\t\t\tthrow new TerminalError('DRIVER', 'Terminal fallback requires a readable input stream')\n\t\treturn { input, terminal: false }\n\t}\n}\n","import type { TerminalInterface } from '@src/core'\nimport type { TerminalOptions } from './types.js'\nimport { Terminal } from './Terminal.js'\n\n/**\n * Creates the interactive terminal form driver — the local-keyboard arm of the terminal trio,\n * beside the core headless `createPrompt` broker and the SSE `createPromptClient` bridge. Where the\n * broker parks a live form until somebody elsewhere answers it, a `Terminal` answers one here: it\n * walks the form's fields in schema order, drives each control's pure reducer over raw-mode stdin,\n * binds every answer through the form's own `fill`, and submits. It is the only impure part of the\n * terminal stack.\n *\n * @param options - See {@link TerminalOptions}\n * @returns A {@link TerminalInterface} whose `ask` drives one whole form over the resolved streams\n *\n * @remarks\n * - **The form is the unit.** `ask` takes the caller's live form and returns its settled values. The\n * returned promise is that form's own `answer`, so a caller holding the form can await either one.\n * - **Every control renders.** The line-read controls are read as a line of text with their format cue,\n * `password` masks, `confirm` takes a key, `editor` takes a block, `select` and `checkbox` drive a\n * list, and an open `select` accepts a value its list does not offer.\n * - **The form decides.** A blank answer binds as absence, the form evaluates, and a refusal\n * re-walks only the fields the walk can still edit.\n * - **Injectable and guard-narrowed.** `input` and `output` default to `process.stdin` and\n * `process.stdout` but accept any stream of the declared minimal shape, resolved through their\n * guards rather than an assertion, so a test drives a whole form with a fake TTY that emits\n * scripted key chunks and records every rendered byte.\n * - **Non-TTY fallback.** A piped stream cannot enter raw mode, so the same walk runs over\n * `node:readline` line input.\n *\n * @example Ask one form at this keyboard\n * ```ts\n * import { createForm } from '@orkestrel/form'\n * import { isTerminalError } from '@orkestrel/terminal'\n * import { createTerminal } from '@orkestrel/terminal/server'\n *\n * const terminal = createTerminal() // process.stdin / process.stdout by default\n * const form = createForm({\n * \tlabel: 'Sign up',\n * \tfields: [\n * \t\t{ control: 'text', name: 'name', label: 'Your name', rule: { required: true, minimum: 2 } },\n * \t\t{ control: 'text', name: 'email', label: 'Email', rule: { required: true, email: true } },\n * \t\t{ control: 'password', name: 'token', label: 'Token' },\n * \t\t{ control: 'confirm', name: 'terms', label: 'Accept the terms', rule: { required: true } },\n * \t\t{\n * \t\t\tcontrol: 'select',\n * \t\t\tname: 'role',\n * \t\t\tlabel: 'Role',\n * \t\t\tchoices: [\n * \t\t\t\t{ value: 'admin', label: 'Admin' },\n * \t\t\t\t{ value: 'viewer', label: 'Viewer', help: 'read-only' },\n * \t\t\t],\n * \t\t},\n * \t],\n * })\n *\n * try {\n * \tconst values = await terminal.ask(form)\n * \tdeploy(values)\n * } catch (error) {\n * \t// Ctrl-c: the walk ended, and the form is still `editing` for whoever owns it.\n * \tif (isTerminalError(error) && error.code === 'CANCEL') form.destroy()\n * }\n * ```\n */\nexport function createTerminal(options?: TerminalOptions): TerminalInterface {\n\treturn new Terminal(options)\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAa,SAAS,GAAGA,mBAAAA,IAAI;;;;;AAM7B,IAAa,cAAc,GAAGA,mBAAAA,IAAI;;AAGlC,IAAa,cAAc,GAAGA,mBAAAA,IAAI;;;;;;AAOlC,IAAa,aAAa,GAAGA,mBAAAA,IAAI;;;;;;AAOjC,IAAa,uBAAuB;;AAGpC,IAAa,yBAAyB;;AAGtC,IAAa,uBAAuB;;AAGpC,IAAa,wBAAwB;;;;;;;;AASrC,IAAa,gBAAiE,OAAO,OAAO;CAC3F,QAAQ;CACR,MAAM;CACN,MAAM;CACN,UAAU;CACV,OAAO;CACP,MAAM;AACP,CAAC;;AAGD,IAAa,YAAY;;;;;AAMzB,IAAa,kBAAkB;;;;;AAM/B,IAAa,mBAAmB;;AAGhC,IAAa,cAAc;;;;;;;AAQ3B,IAAa,kBAAkB;;;;;;;;;;;;;;;;;;ACjD/B,SAAgB,cAAc,OAA+C;CAC5E,QAAA,GACCC,oBAAAA,SAAAA,CAAS,KAAK,KACd,QAAQ,UAAA,GACRC,oBAAAA,WAAAA,CAAW,MAAM,EAAE,KACnB,SAAS,UAAA,GACTA,oBAAAA,WAAAA,CAAW,MAAM,GAAG;AAEtB;;;;;;;;;;;;AAaA,SAAgB,WAAW,OAAgD;CAC1E,QAAA,GACCD,oBAAAA,SAAAA,CAAS,KAAK,KACd,UAAU,UAAA,GACVC,oBAAAA,WAAAA,CAAW,MAAM,IAAI,KACrB,UAAU,UAAA,GACVA,oBAAAA,WAAAA,CAAW,MAAM,IAAI;AAEvB;;;;;;;;;;;AAYA,SAAgB,gBAAgB,OAAsC;CACrE,OAAO,MAAM,UAAU,SAAA,GAAQA,oBAAAA,WAAAA,CAAW,MAAM,UAAU;AAC3D;;;;;;;;;;AAWA,SAAgB,UAAU,MAAsB;CAC/C,IAAI,QAAQ;CACZ,KAAK,MAAM,aAAa,MACvB,IAAI,cAAc,MAAM,SAAS;CAElC,OAAO;AACR;;;;;;;;;;AAWA,SAAgB,eAAe,OAAuB;CACrD,IAAI,SAAS,GAAG,OAAO;CACvB,OAAO,GAAG,OAAO,QAAQ,WAAW,OAAO,KAAK,CAAC;AAClD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aAAa,eAA+B;CAC3D,OAAO,GAAG,eAAe,gBAAgB,CAAC,IAAIC,UAAAA,SAAS;AACxD;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,YAAY,OAA6B;CACxD,MAAM,QAAQ,MAAM,SAAS,MAAM;CACnC,MAAM,OAAO,cAAc,MAAM;CACjC,MAAM,OAAO,aAAa,QAAQ,MAAM,UAAU,KAAA;CAClD,MAAM,QAAA,GAAOC,oBAAAA,SAAAA,CAAS,IAAI,IAAI,OAAO,IAAI,IAAI;CAC7C,OAAO;EACN,SAAS;EACT,MAAM,MAAM;EACZ,OAAO,SAAS,KAAA,IAAY,QAAQ,GAAG,MAAM,GAAG;EAChD,IAAA,GAAIC,oBAAAA,SAAAA,CAAS,IAAI,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;CAC3C;AACD;;;;;;;;;AAUA,SAAgB,YAAY,OAAuC;CAClE,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,KAAA,GAAIA,oBAAAA,SAAAA,CAAS,KAAK,GAAG,OAAO;CAC5B,KAAA,GAAID,oBAAAA,SAAAA,CAAS,KAAK,GAAG,OAAO,OAAO,KAAK;CACxC,KAAA,GAAIE,oBAAAA,UAAAA,CAAU,KAAK,GAAG,OAAO,QAAQ,QAAQ;CAC7C,OAAO,MAAM,KAAK,IAAI;AACvB;;;;;;;;;AAUA,SAAgB,cAAc,SAAyD;CACtF,OAAO,QAAQ,QAAQ,WAAW,OAAO,aAAa,IAAI;AAC3D;;;;;;;;;AAUA,SAAgB,eAAe,SAAyD;CACvF,OAAO,QAAQ,QAAQ,WAAW,OAAO,aAAa,IAAI;AAC3D;;;;;;;;;;AAWA,SAAgB,kBACf,QACA,OACA,OACS;CACT,OAAO,OAAO,OAAO,MAAM,MAAM,SAAS,KAAK;AAChD;;;;;;;;;;;;AAaA,SAAgB,iBACf,QACA,OACA,OACA,OACS;CACT,MAAM,OAAO,GAAG,OAAO,OAAO,MAAM,MAAM,OAAO,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,OAAO,MAAM,MAAM,SAAS,KAAK,EAAE,GAAG,OAAO,OAAO,MAAM,MAAM,MAAM,WAAW;CAC7J,OAAO,MAAM,WAAW,IAAI,OAAO,GAAG,KAAK,GAAG,OAAO,OAAO,MAAM,MAAM,SAAS,KAAK;AACvF;;;;;;;;;;AAWA,SAAgB,qBACf,QACA,OACA,SACS;CACT,MAAM,SAAS,QAAQ,KAAK,WAAW,OAAO,KAAK,CAAC,CAAC,KAAK,IAAI;CAC9D,OAAO,OAAO,OAAO,MAAM,MAAM,MAAM,GAAG,gBAAgB,IAAI,QAAQ;AACvE;;;;;;;;;;AAWA,SAAgB,sBACf,QACA,OACA,SACS;CACT,MAAM,SAAS,QAAQ,KAAK,WAAW,OAAO,KAAK,CAAC,CAAC,KAAK,IAAI;CAC9D,OAAO,OAAO,OAAO,MAAM,MAAM,OAAO,GAAG,iBAAiB,IAAI,QAAQ;AACzE;;;;;;;;;;;AAYA,SAAgB,mBACf,QACA,OACA,SACS;CACT,OAAO,QACL,KACC,QAAQ,UACR,KAAK,OAAO,OAAO,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,OAAO,MAAM,MAAM,SAAS,OAAO,KAAK,GACnH,CAAC,CACA,KAAK,IAAI;AACZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AClMA,IAAa,WAAb,MAAmD;CAClD;CACA;CACA;CACA;CACA,4BAAqB,IAAI,IAAkD;CAC3E,SAA4B,CAAC;CAC7B;CACA;CACA,SAAS;CAET,YAAY,SAA2B;EAGtC,KAAK,SAAS,cAAc,SAAS,KAAK,IAAI,QAAQ,QAAQC,aAAAA;EAC9D,KAAK,WAAA,GAAUC,0BAAAA,eAAAA,CAAe,SAAS,MAAM,IAAI,QAAQ,SAASC,aAAAA;EAClE,KAAK,WAAA,GAAUC,mBAAAA,aAAAA,CAAa;EAC5B,KAAK,UAAA,GAASC,UAAAA,kBAAAA,CAAkB,SAAS,KAAK;CAC/C;CAIA,MAAM,IAAI,MAA0C;EACnD,IAAI;GACH,KAAK,QAAQ,MAAM,KAAK,MAAM;GAC9B,MAAM,KAAK,MAAM,MAAM,KAAK,OAAO,MAAM;GACzC,SAAS;IACR,IAAI,KAAK,WAAW,WAAW,OAAO,MAAM,KAAK;IACjD,MAAM,SAAS,KAAK,OAAO;IAC3B,IAAI,OAAO,SAAS,OAAO,MAAM,KAAK;IACtC,KAAK,QAAQ,MAAM,OAAO,KAAK;IAC/B,MAAM,QAAQ,KAAK,iBAAiB,MAAM,OAAO,KAAK;IAItD,IAAI,MAAM,WAAW,KAAK,KAAK,QAAQ;KACtC,KAAK,QAAQ;KACb,OAAO,MAAM,KAAK;IACnB;IACA,MAAM,KAAK,MAAM,MAAM,KAAK;GAC7B;EACD,UAAU;GACT,KAAK,OAAO;EACb;CACD;;;;;;CASA,MAAM,MAAM,MAAqB,QAA6C;EAC7E,IAAI;EACJ,KAAK,MAAM,SAAS,QAAQ;GAC3B,IAAI,KAAK,WAAW,WAAW;GAC/B,IAAI,MAAM,WAAW,MAAM;GAC3B,IAAI,KAAK,SAAS,IAAI,MAAM,IAAI,GAAG;GACnC,IAAI,MAAM,UAAU,OAAO;IAC1B,QAAQ,MAAM;IACd,IAAI,UAAU,KAAA,GAAW,KAAK,YAAY,MAAM,KAAK;GACtD;GACA,IAAI,MAAM,WAAW,MAAM;IAC1B,KAAK,aAAa,MAAM,KAAK;IAC7B;GACD;GACA,MAAM,MAAM,MAAM,KAAK,MAAM,MAAM,KAAK;GACxC,IAAI,KAAK,WAAW,WAAW;GAC/B,KAAK,MAAM,MAAM,OAAO,GAAG;EAC5B;CACD;;CAGA,YAAY,MAAqB,MAAoB;EACpD,MAAM,QAAQ,KAAK,OAAO,QAAQ,MAAM,UAAU,MAAM,SAAS,IAAI,CAAC,EAAE,SAAS;EACjF,KAAK,QAAQ,MACZ,GAAGC,UAAAA,UAAU,kBAAkB,KAAK,SAAS,KAAK,SAAA,GAAQC,UAAAA,oBAAAA,CAAoB,KAAK,CAAC,IAAID,UAAAA,SACzF;CACD;;CAGA,aAAa,MAAqB,OAAwB;EACzD,MAAM,OAAO,iBACZ,KAAK,SACL,KAAK,SAAA,GACLC,UAAAA,oBAAAA,CAAoB,MAAM,SAAS,MAAM,IAAI,IAAA,GAC7CA,UAAAA,oBAAAA,CAAoB,YAAY,KAAK,OAAO,MAAM,KAAK,CAAC,CACzD;EACA,KAAK,QAAQ,MAAM,GAAG,OAAOD,UAAAA,SAAS;CACvC;;CAGA,MAAM,MAAqB,OAAmD;EAC7E,IAAI,MAAM,YAAY,YAAY,OAAO,KAAK,aAAa,MAAM,KAAK;EACtE,IAAI,MAAM,YAAY,WAAW,OAAO,KAAK,SAAS,MAAM,KAAK;EACjE,IAAI,MAAM,YAAY,UAAU,OAAO,KAAK,WAAW,MAAM,KAAK;EAClE,IAAI,MAAM,YAAY,UAAU,OAAO,KAAK,QAAQ,MAAM,KAAK;EAC/D,IAAI,MAAM,YAAY,YAAY,OAAO,KAAK,UAAU,MAAM,KAAK;EACnE,IAAI,MAAM,YAAY,QAAQ,OAAO,KAAK,MAAM,MAAM,KAAK;EAC3D,OAAO,KAAK,SAAS,MAAM,KAAK;CACjC;;;;;;;;CASA,MAAM,MAAqB,OAAkB,KAAmC;EAC/E,KAAK,MAAM,MAAM,IAAI;EACrB,MAAM,QAAQ,QAAQ,KAAA,IAAY,KAAA,KAAA,GAAYE,gBAAAA,WAAAA,CAAW,OAAO,GAAG;EACnE,KAAK,KAAK,MAAM,OAAA,GAAMC,gBAAAA,cAAAA,CAAc,KAAK,IAAI,QAAQ,KAAA,CAAS;EAC9D,IAAI,UAAU,KAAA,MAAA,GAAaA,gBAAAA,cAAAA,CAAc,GAAG,GAAG,KAAK,WAAW,MAAM,MAAM,eAAe;CAC3F;;CAGA,iBAAiB,MAAqB,QAAqD;EAC1F,MAAM,SAAsB,CAAC;EAC7B,KAAK,MAAM,SAAS,QAAQ;GAC3B,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK;GACpC,IAAI,UAAU,KAAA,GAAW;GACzB,IAAI,MAAM,WAAW,QAAQ,MAAM,WAAW,MAAM;GACpD,IAAI,KAAK,SAAS,IAAI,MAAM,IAAI,GAAG;GACnC,IAAI,OAAO,MAAM,SAAS,KAAK,SAAS,MAAM,IAAI,GAAG;GACrD,OAAO,KAAK,KAAK;EAClB;EACA,OAAO;CACR;;CAGA,QAAQ,MAAqB,QAAqC;EACjE,KAAK,MAAM,SAAS,QAAQ;GAC3B,MAAM,SAAA,GAAQF,UAAAA,oBAAAA,CAAoB,KAAK,MAAM,MAAM,KAAK,CAAC,EAAE,SAAS,MAAM,KAAK;GAC/E,MAAM,WAAA,GAAUA,UAAAA,oBAAAA,CAAoB,MAAM,OAAO;GACjD,MAAM,QAAA,GAAOG,UAAAA,gBAAAA,CAAgB,KAAK,SAAS,KAAK,QAAQ,GAAG,MAAM,IAAI,SAAS;GAC9E,KAAK,QAAQ,MAAM,GAAG,OAAOJ,UAAAA,SAAS;EACvC;CACD;;CAKA,SAAS,MAAqB,OAAmC;EAChE,MAAM,SAAA,GAAQK,UAAAA,iBAAAA,CAAiB,YAAY,KAAK,GAAG,KAAK,SAAS,KAAK,MAAM;EAC5E,IAAI,gBAAgB,KAAK,MAAM,GAAG,OAAO,KAAK,OAAO,MAAM,OAAOC,UAAAA,WAAW;EAC7E,OAAO,KAAK,MAAM,MAAM,MAAM,SAAS,MAAM,OAAO;CACrD;;CAGA,aAAa,MAAqB,OAAuC;EACxE,MAAM,SAAA,GAAQC,UAAAA,oBAAAA,CAAoB,OAAO,KAAK,SAAS,KAAK,MAAM;EAClE,IAAI,gBAAgB,KAAK,MAAM,GAAG,OAAO,KAAK,OAAO,MAAM,OAAOC,UAAAA,cAAc;EAChF,OAAO,KAAK,QAAQ,OAAA,GAAMC,UAAAA,mBAAAA,CAAmB,KAAK,SAAS,KAAK,QAAQ,MAAM,OAAO,CAAC;CACvF;;;;;;CAOA,MAAM,SAAS,MAAqB,OAA0C;EAC7E,MAAM,SAAA,GAAQC,UAAAA,mBAAAA,CAAmB,OAAO,KAAK,SAAS,KAAK,MAAM;EACjE,IAAI,gBAAgB,KAAK,MAAM,GAAG,OAAO,KAAK,OAAO,MAAM,OAAOC,UAAAA,aAAa;EAC/E,MAAM,UAAA,GAASC,UAAAA,mBAAAA,CACd,KAAK,SACL,KAAK,QACL,MAAM,SACN,qBACD;EACA,MAAM,OAAO,MAAM,KAAK,QAAQ,MAAM,MAAM;EAC5C,MAAM,SAAS,KAAK,KAAK,CAAC,CAAC,YAAY;EACvC,IAAI,OAAO,WAAW,GAAG,OAAO,MAAM;EACtC,IAAI,WAAW,OAAO,WAAW,OAAO,OAAO;EAC/C,IAAI,WAAW,OAAO,WAAW,MAAM,OAAO;EAC9C,OAAO,KAAK,KAAK;CAClB;;CAGA,WAAW,MAAqB,OAAqC;EACpE,MAAM,SAAA,GAAQC,UAAAA,kBAAAA,CAAkB,OAAO,KAAK,SAAS,KAAK,MAAM;EAChE,IAAI,gBAAgB,KAAK,MAAM,GAAG,OAAO,KAAK,OAAO,MAAM,OAAOC,UAAAA,YAAY;EAC9E,OAAO,KAAK,OAAO,MAAM,MAAM,SAAS,MAAM,OAAO;CACtD;;;;;;;CAQA,MAAM,QAAQ,MAAqB,OAAqD;EACvF,MAAM,UAAU,cAAc,MAAM,OAAO;EAC3C,KAAK,kBAAkB,MAAM,OAAO;EACpC,IAAI,MAAM,SAAS,MAAM;GACxB,IAAI,QAAQ,SAAS,GAAG;IACvB,MAAM,UAAU,QAAQ,KAAK,YAAY;KACxC,GAAG;KACH,QAAA,GAAOb,UAAAA,oBAAAA,CAAoB,OAAO,KAAK;IACxC,EAAE;IACF,KAAK,QAAQ,MAAM,GAAG,qBAAqB,KAAK,SAAS,KAAK,QAAQ,OAAO,IAAID,UAAAA,SAAS;GAC3F;GACA,OAAO,KAAK,SAAS,MAAM,KAAK;EACjC;EACA,IAAI,QAAQ,WAAW,GAAG,OAAO,KAAA;EACjC,MAAM,SAAA,GAAQe,UAAAA,kBAAAA,CAAkB;GAAE,GAAG;GAAO;EAAQ,GAAG,KAAK,SAAS,KAAK,MAAM;EAChF,IAAI,gBAAgB,KAAK,MAAM,GAAG,OAAO,KAAK,OAAO,MAAM,OAAOC,UAAAA,YAAY;EAC9E,KAAK,WAAW,MAAM,SAAS,OAAO;EACtC,MAAM,OAAO,MAAM,KAAK,QAAQ,MAAM,KAAK,YAAY,GAAG,qBAAqB,EAAE,CAAC;EAClF,OAAO,QAAQ,OAAO,SAAS,KAAK,KAAK,GAAG,EAAE,IAAI,EAAE,EAAE;CACvD;;;;;;CAOA,MAAM,UAAU,MAAqB,OAA2C;EAC/E,MAAM,UAAU,cAAc,MAAM,OAAO;EAC3C,KAAK,kBAAkB,MAAM,OAAO;EACpC,MAAM,SAAA,GAAQC,UAAAA,oBAAAA,CAAoB;GAAE,GAAG;GAAO;EAAQ,GAAG,KAAK,SAAS,KAAK,MAAM;EAClF,IAAI,gBAAgB,KAAK,MAAM,GAAG,OAAO,KAAK,OAAO,MAAM,OAAOC,UAAAA,cAAc;EAChF,KAAK,WAAW,MAAM,SAAS,OAAO;EAEtC,QAAO,MADY,KAAK,QAAQ,MAAM,KAAK,YAAY,GAAG,uBAAuB,EAAE,CAAC,EAAA,CAElF,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,QAAQ,OAAO,SAAS,KAAK,KAAK,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,CACnE,QAAQ,UAA2B,UAAU,KAAA,CAAS;CACzD;;;;;;CAOA,MAAM,MAAM,MAAqB,OAAmD;EACnF,IAAI,MAAM,aAAa,MAAM,KAAK,QAAQ,MAAM,GAAG,KAAK,YAAY,SAAS,IAAIlB,UAAAA,SAAS;EAC1F,MAAM,QAAkB,CAAC;EACzB,SAAS;GACR,MAAM,QAAQ,MAAM,KAAK,SAAS,MAAM,KAAK;GAC7C,IAAI,MAAM,WAAW,GAAG;GACxB,MAAM,KAAK,KAAK;GAChB,IAAI,MAAM,aAAa,QAAQ,KAAK,QAAQ;EAC7C;EACA,OAAO,MAAM,SAAS,IAAI,QAAQ,KAAA;CACnC;;CAGA,kBAAkB,SAAuC;EACxD,MAAM,UAAU,eAAe,OAAO;EACtC,IAAI,QAAQ,WAAW,GAAG;EAC1B,KAAK,QAAQ,MAAM,GAAG,sBAAsB,KAAK,SAAS,KAAK,QAAQ,OAAO,IAAIA,UAAAA,SAAS;CAC5F;;CAGA,WAAW,SAAiB,SAAuC;EAClE,MAAM,UAAA,GAASS,UAAAA,mBAAAA,CAAmB,KAAK,SAAS,KAAK,QAAQ,OAAO;EACpE,MAAM,OAAO,mBAAmB,KAAK,SAAS,KAAK,QAAQ,OAAO;EAClE,KAAK,QAAQ,MAAM,GAAG,SAAST,UAAAA,UAAU,OAAOA,UAAAA,SAAS;CAC1D;;CAGA,YAAY,MAAsB;EACjC,OAAO,KAAK,QAAQ,OAAO,KAAK,OAAO,MAAM,MAAM,IAAI;CACxD;;;;;;;CAUA,UAAU,OAAe,SAAqD;EAC7E,KAAK,UAAU,IAAI,OAAO,OAAO;EACjC,KAAK,OAAO,aAAa,IAAI;EAC7B,KAAK,OAAO,SAAS;EACrB,KAAK,OAAO,GAAG,QAAQ,OAAO;CAC/B;CAEA,UAAU,OAAqB;EAC9B,MAAM,UAAU,KAAK,UAAU,IAAI,KAAK;EACxC,IAAI,YAAY,KAAA,GAAW;EAC3B,KAAK,UAAU,OAAO,KAAK;EAC3B,KAAK,OAAO,IAAI,QAAQ,OAAO;EAC/B,KAAK,OAAO,aAAa,KAAK;EAC9B,KAAK,OAAO,QAAQ;CACrB;;;;;;;;;;CAWA,OACC,MACA,SACA,QACa;EACb,OAAO,IAAI,SAAY,SAAS,WAAW;GAE1C,KAAK,QAAQ,MAAM,WAAW;GAC9B,IAAI;GACJ,IAAI;IACH,YAAY,OAAO,UAAA,GAASmB,UAAAA,SAAAA,CAAS,EAAE,CAAC,CAAC,CAAC;IAC1C,KAAK,QAAQ,MAAM,SAAS;GAC7B,SAAS,OAAO;IAEf,KAAK,QAAQ,MAAM,GAAG,cAAcnB,UAAAA,SAAS;IAC7C,OAAO,KAAK;IACZ;GACD;GACA,MAAM,QAAQ,CAAC;GACf,MAAM,UAAU,KAAK,eACpB,OACA,SACA,UAAU,SAAS,GACnB,QACA,SACA,MACD;GACA,KAAK,UAAU,OAAO,OAAO;GAG7B,KAAU,OAAO,OAAO,UAAmB,KAAK,WAAW,OAAO,QAAQ,KAAK,CAAC;EACjF,CAAC;CACF;CAEA,eACC,OACA,SACA,cACA,QACA,SACA,QACuC;EACvC,IAAI,QAAQ;EACZ,IAAI,QAAQ;EACZ,QAAQ,UAAU;GACjB,IAAI;IACH,MAAM,OAAO,OAAO,QAAA,GAAOmB,UAAAA,SAAAA,CAAS,KAAK,CAAC;IAC1C,QAAQ,KAAK;IACb,IAAI,KAAK,WAAW,UAAU;KAC7B,KAAK,QAAQ,KAAK,MAAM,KAAK;KAC7B,QAAQ,UAAU,KAAK,IAAI;KAC3B;IACD;IAEA,KAAK,QAAQ,KAAK,MAAM,KAAK;IAC7B,KAAK,UAAU,KAAK;IACpB,KAAK,QAAQ,MAAM,GAAG,cAAcnB,UAAAA,SAAS;IAC7C,IAAI,KAAK,WAAW,UAAU,OAAO,IAAIoB,UAAAA,cAAc,UAAU,kBAAkB,CAAC;SAC/E,IAAI,KAAK,WAAW,YAAY,KAAK,UAAU,KAAA,GAAW,QAAQ,KAAK,KAAK;SAC5E,OAAO,IAAIA,UAAAA,cAAc,UAAU,0BAA0B,CAAC;GACpE,SAAS,OAAO;IAGf,KAAK,UAAU,KAAK;IACpB,KAAK,QAAQ,MAAM,GAAG,cAAcpB,UAAAA,SAAS;IAC7C,OAAO,KAAK;GACb;EACD;CACD;;CAGA,WAAW,OAAe,QAAoC,OAAsB;EACnF,IAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;EAChC,KAAK,UAAU,KAAK;EACpB,KAAK,QAAQ,MAAM,GAAG,cAAcA,UAAAA,SAAS;EAC7C,OAAO,KAAK;CACb;;CAGA,QAAQ,MAAc,eAA6B;EAClD,KAAK,QAAQ,MAAM,GAAG,aAAa,aAAa,IAAI,MAAM;CAC3D;;CAKA,MAAM,MAAM,MAAqB,SAAiB,MAA+B;EAChF,MAAM,SAAS,MAAM,KAAK,QAAQ,OAAA,GAAMS,UAAAA,mBAAAA,CAAmB,KAAK,SAAS,KAAK,QAAQ,OAAO,CAAC;EAC9F,OAAO,OAAO,SAAS,IAAI,SAAS;CACrC;;;;;;CAOA,MAAM,OAAO,MAAqB,SAAiB,MAA+B;EACjF,MAAM,UAAA,GAASG,UAAAA,mBAAAA,CAAmB,KAAK,SAAS,KAAK,QAAQ,SAAS,oBAAoB;EAC1F,KAAK,QAAQ,MAAM,GAAG,SAASZ,UAAAA,SAAS;EACxC,MAAM,YAAsB,CAAC;EAC7B,SAAS;GACR,MAAM,OAAO,MAAM,KAAK,MAAM,IAAI;GAClC,IAAI,SAAS,KAAA,GAAW;GACxB,UAAU,KAAK,IAAI;EACpB;EACA,MAAM,OAAO,UAAU,KAAK,IAAI;EAChC,OAAO,KAAK,SAAS,IAAI,OAAO;CACjC;;CAGA,MAAM,QAAQ,MAAqB,QAAiC;EACnE,KAAK,QAAQ,MAAM,GAAG,OAAO,EAAE;EAE/B,OAAO,MADY,KAAK,MAAM,IAAI,KACnB;CAChB;;;;;;;CAQA,MAAM,MAAkD;EACvD,MAAM,WAAW,KAAK,OAAO,MAAM;EACnC,IAAI,aAAa,KAAA,GAAW,OAAO,QAAQ,QAAQ,QAAQ;EAC3D,IAAI,KAAK,QAAQ,OAAO,QAAQ,QAAQ,KAAA,CAAS;EACjD,KAAK,aAAa;EAClB,OAAO,IAAI,SAA6B,SAAS,WAAW;GAC3D,KAAK,SAAS;GACd,KAAU,OAAO,OAAO,UAAmB;IAC1C,IAAI,KAAK,WAAW,SAAS;IAC7B,KAAK,SAAS,KAAA;IACd,OAAO,KAAK;GACb,CAAC;EACF,CAAC;CACF;;;;;;;CAQA,eAAqB;EACpB,IAAI,KAAK,eAAe,KAAA,GAAW;EACnC,MAAM,MAAA,GAAKqB,cAAAA,gBAAAA,CAAgB,KAAK,cAAc,CAAC;EAC/C,GAAG,GAAG,SAAS,SAAS,KAAK,QAAQ,IAAI,CAAC;EAC1C,GAAG,GAAG,eAAe,KAAK,QAAQ,CAAC;EACnC,KAAK,aAAa;CACnB;;CAGA,QAAQ,MAAoB;EAC3B,MAAM,QAAQ,KAAK;EACnB,IAAI,UAAU,KAAA,GAAW;GACxB,KAAK,OAAO,KAAK,IAAI;GACrB;EACD;EACA,KAAK,SAAS,KAAA;EACd,MAAM,IAAI;CACX;;CAGA,UAAgB;EACf,KAAK,SAAS;EACd,MAAM,QAAQ,KAAK;EACnB,IAAI,UAAU,KAAA,GAAW;EACzB,KAAK,SAAS,KAAA;EACd,MAAM,KAAA,CAAS;CAChB;;CAGA,SAAe;EACd,MAAM,QAAQ,KAAK;EACnB,KAAK,SAAS,KAAA;EACd,IAAI,UAAU,KAAA,GAAW,MAAM,KAAA,CAAS;EACxC,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,KAAA,GAAW;EACtB,KAAK,aAAa,KAAA;EAGlB,GAAG,mBAAmB,OAAO;EAC7B,GAAG,MAAM;CACV;;;;;;;;;CAUA,gBAAqE;EAEpE,MAAM,QAAQ,KAAK;EACnB,IAAI,CAAC,WAAW,KAAK,GACpB,MAAM,IAAID,UAAAA,cAAc,UAAU,oDAAoD;EACvF,OAAO;GAAE;GAAO,UAAU;EAAM;CACjC;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/hBA,SAAgB,eAAe,SAA8C;CAC5E,OAAO,IAAI,SAAS,OAAO;AAC5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orkestrel/terminal",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "An interactive terminal-prompt toolkit — a headless prompt state-machine broker, an SSE client bridge, and a raw-mode terminal driver. Part of the @orkestrel line.",
5
5
  "keywords": [
6
6
  "cli",
@@ -83,24 +83,24 @@
83
83
  "test:setup": "vitest run --config vite.config.ts --no-cache --reporter=dot --project setup"
84
84
  },
85
85
  "dependencies": {
86
- "@orkestrel/console": "^0.0.14",
87
- "@orkestrel/contract": "^0.0.17",
88
- "@orkestrel/database": "^0.0.15",
89
- "@orkestrel/emitter": "^0.0.10",
90
- "@orkestrel/form": "^0.0.7",
91
- "@orkestrel/sse": "^0.0.8"
86
+ "@orkestrel/console": "^0.0.15",
87
+ "@orkestrel/contract": "^0.0.19",
88
+ "@orkestrel/database": "^0.0.17",
89
+ "@orkestrel/emitter": "^0.0.11",
90
+ "@orkestrel/form": "^0.0.8",
91
+ "@orkestrel/sse": "^0.0.9"
92
92
  },
93
93
  "devDependencies": {
94
- "@microsoft/api-extractor": "^7.59.1",
95
- "@orkestrel/guide": "^0.0.19",
96
- "@orkestrel/probe": "^0.0.15",
97
- "@orkestrel/scaffold": "^0.0.70",
98
- "@orkestrel/test": "^0.0.15",
99
- "@types/node": "^26.5.1",
100
- "oxfmt": "^0.68.0",
101
- "oxlint": "^1.83.0",
94
+ "@microsoft/api-extractor": "^7.59.3",
95
+ "@orkestrel/guide": "^0.0.24",
96
+ "@orkestrel/probe": "^0.0.19",
97
+ "@orkestrel/scaffold": "^0.0.90",
98
+ "@orkestrel/test": "^0.0.24",
99
+ "@types/node": "^26.6.4",
100
+ "oxfmt": "^0.71.0",
101
+ "oxlint": "^1.86.0",
102
102
  "typescript": "^6.0.3",
103
- "vite": "^8.3.0",
103
+ "vite": "^8.3.2",
104
104
  "vitest": "^4.1.11"
105
105
  },
106
106
  "engines": {