@jarenjs/flow 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # @jarenjs/flow
2
+
3
+ Executable workflow documents, in two formats. The **jaren-fsm format**
4
+ is a finite state machine as one JSON value — declared states, an
5
+ initial state, and a document-ordered transition table whose guards and
6
+ effect props are [Jaren JSON Query](../json/docs/QUERY-FORMAT.md)
7
+ documents — compiled, once, into a **pure step function**. The
8
+ **jaren-dag format** is an acyclic dataflow whose nodes are the suite's
9
+ own engines — query documents, JSLT stylesheets, registered async
10
+ tasks — wired by edges that carry data and compiled into a
11
+ run-to-completion executor.
12
+
13
+ It is the executable half of a round trip the suite already ships: a
14
+ `stateDiagram-v2` parsed by [`@jarenjs/mermaid`](../../components/mermaid)
15
+ projects (via a JSLT stylesheet) into exactly this shape, and this
16
+ engine runs it. The format is a strict superset of that projection
17
+ contract — every projected document compiles unchanged.
18
+
19
+ The grammar is published as JSON Schema in
20
+ [`schemas/jaren-fsm.schema.json`](schemas/jaren-fsm.schema.json) (with a
21
+ mechanically derived draft-07 twin for providers pinned to older
22
+ drafts) — hand it to a constrained decoder and a language model cannot
23
+ emit a machine with an unknown member or a malformed guard. The
24
+ normative contract is [docs/FLOW-FORMAT.md](docs/FLOW-FORMAT.md). Zero
25
+ dependencies outside the suite; no `eval`, CSP-safe; the only runtime
26
+ import is `@jarenjs/json`.
27
+
28
+ ## The format in one glance
29
+
30
+ ```json
31
+ {
32
+ "$fsm": "0.1",
33
+ "initial": "idle",
34
+ "states": [
35
+ "idle",
36
+ { "id": "loading", "entry": [{ "run": "fetch", "with": { "url": "$.context.url" } }] },
37
+ { "id": "done", "final": true }
38
+ ],
39
+ "transitions": [
40
+ { "from": "idle", "event": "start", "to": "loading" },
41
+ { "from": "loading", "event": "ok", "guard": "$.payload.fresh", "to": "done" },
42
+ { "from": "loading", "event": "fail", "to": "idle" }
43
+ ]
44
+ }
45
+ ```
46
+
47
+ Guards are asserted by effective boolean value against one scope —
48
+ `{ state, event, payload, context }` — and **effects are data**: a fired
49
+ transition returns resolved `{ run, with? }` descriptors (exit →
50
+ transition → entry); the engine never executes them. Which registry runs
51
+ them is the host's business, the same boundary discipline as
52
+ `@jarenjs/app` effects.
53
+
54
+ ## Usage
55
+
56
+ ```javascript
57
+ import { compileFsm, createFsmSession } from '@jarenjs/flow';
58
+
59
+ const fsm = compileFsm(doc); // throws FlowCompileError (JF0xxx) on a bad document
60
+ fsm.initial; // 'idle'
61
+ fsm.states; // ['idle', 'loading', 'done'] (frozen)
62
+ fsm.events('loading'); // ['ok', 'fail'] (frozen)
63
+
64
+ // The pure core: state in, result out. Nothing is held, nothing runs.
65
+ const r = fsm.step('loading', 'ok', { payload: { fresh: true }, context: { url: '/api' } });
66
+ r; // { changed: true, state: 'done', effects: [], final: true, errors: [] }
67
+
68
+ // The thin mutable convenience over it:
69
+ const s = createFsmSession(fsm); // starts at doc initial
70
+ s.can('start'); // true — a dry run (step is pure, so can() IS step())
71
+ s.send('start'); // advances; returns the same result shape
72
+ s.state; // 'loading'
73
+ s.done; // final-state flag
74
+ ```
75
+
76
+ `step` is **total over machine input**: an event no transition answers
77
+ is ignored (`changed: false`, no effects) — the conventional FSM
78
+ reading. Only caller mistakes throw: an undeclared state id or a
79
+ non-string event (`FlowRuntimeError`, JF2001/JF2002).
80
+
81
+ ## Fail closed, report precisely
82
+
83
+ A guard that throws at evaluation reads **false**; an effect whose
84
+ `with` throws is **omitted**. Neither aborts the step — each appends a
85
+ plain-data record `{ code, docPath, message }` to the result's `errors`
86
+ array, where `docPath` is a JSON Pointer to exactly the query that
87
+ failed (`/transitions/2/guard`). That is the suite's standard
88
+ machine-repairable error shape: a host surfaces the records through its
89
+ own error channel, and a repair loop knows precisely what to fix.
90
+
91
+ Compile-time failures throw `FlowCompileError` with the same `code` +
92
+ `docPath` discipline (JF0001–JF0009, table in
93
+ [FLOW-FORMAT.md §5](docs/FLOW-FORMAT.md)); a guard that cannot compile
94
+ is a compile error, never a runtime surprise.
95
+
96
+ ## Hosting a machine in @jarenjs/app
97
+
98
+ `fsmToApp` turns the same document into **generated standard app
99
+ documents** — a state slice plus one plain-JSON action per named event,
100
+ compiled by the app like any hand-written action, with no flow code at
101
+ runtime:
102
+
103
+ ```javascript
104
+ import { fsmToApp, fsmStateSchema } from '@jarenjs/flow';
105
+ import { createApp } from '@jarenjs/app';
106
+
107
+ const { slice, actions, events } = fsmToApp(doc); // { pointer: '/fsm', namespace: 'fsm/' }
108
+ const app = createApp({
109
+ state: { fsm: slice },
110
+ view,
111
+ actions: { ...actions, ...hostActions },
112
+ }, { node, effects, validateState }); // fsmStateSchema(doc) guards the slice
113
+ ```
114
+
115
+ Control state lives at `<pointer>/current`; the host's app state **is**
116
+ the machine's `context`; guards and effect props evaluate
117
+ pre-transition in both worlds, and target states are baked as literals.
118
+ Selection semantics are provably the headless engine's — the test
119
+ suite drives both through the same scripts and asserts state and
120
+ effects agree. The convention, the scope mapping table, multi-machine
121
+ layout and the honestly-stated divergences (a throwing guard fails the
122
+ hosted transaction instead of reading false) live in
123
+ [docs/APP-INTEGRATION.md](docs/APP-INTEGRATION.md).
124
+
125
+ ## The dataflow half — jaren-dag
126
+
127
+ "Connect different parts together" as one schema-validated JSON value:
128
+ nodes from a closed vocabulary (`input`, `output`, `const`, `query`,
129
+ `jslt`, `task`) wired by edges, run to completion for one input. The
130
+ suite's engines compose as **data** — a query filters, a stylesheet
131
+ projects, and the result can be a vnode tree without this package ever
132
+ importing a rendering line:
133
+
134
+ ```javascript
135
+ import { compileDag } from '@jarenjs/flow';
136
+
137
+ const dag = compileDag({
138
+ $dag: '0.1',
139
+ nodes: {
140
+ rows: { kind: 'input' },
141
+ adults: { kind: 'query',
142
+ query: { $for: { r: '$[*]' }, $where: { $ge: ['$r.age', 18] }, $return: '$r' } },
143
+ view: { kind: 'jslt',
144
+ stylesheet: [{ match: '$', body: ['ul', {},
145
+ [{ $for: { p: '$[*]' }, $return: ['li', {}, '$p.name'] }]] }] },
146
+ out: { kind: 'output' },
147
+ },
148
+ edges: [
149
+ { from: 'rows', to: 'adults' },
150
+ { from: 'adults', to: 'view' },
151
+ { from: 'view', to: 'out' },
152
+ ],
153
+ }, { tasks: {} });
154
+
155
+ await dag.run(people); // ['ul', {}, [['li', {}, 'ada'], …]] — a vnode, as JSON
156
+ ```
157
+
158
+ Cycles, port rules and the exactly-one-output rule are **compile-time**
159
+ rejections (JF0xxx with `docPath`); a `task` node's handler is resolved
160
+ at compile too, and called as `handler({ with, input }, signal)` with
161
+ the run's shared `AbortSignal`. Independent branches run concurrently,
162
+ but determinism is *same input → same output values* — results are
163
+ keyed and port objects assemble in edge document order, so completion
164
+ timing can never change a value. Failure is fail-closed: the first
165
+ failing node aborts the shared signal and rejects the whole run
166
+ (`JF2006` with `nodeId`, `docPath`, `cause`; a caller abort is
167
+ `JF2007`) — no retries, no partial results. Per-node observability is
168
+ the `onNode` record stream (`{ id, status, ms }`), and a compiled dag
169
+ hosts in an app as **one effect** through `createTaskEffect` — the
170
+ recipe is in [docs/APP-INTEGRATION.md](docs/APP-INTEGRATION.md), the
171
+ contract in [docs/FLOW-FORMAT.md](docs/FLOW-FORMAT.md) §6–§7.
172
+
173
+ ## One scope, one honest quirk
174
+
175
+ `$.state`, `$.event`, `$.payload`, `$.context` — control state lives in
176
+ the machine, data state lives in the host, passed per call. A guard
177
+ written as a plain string that does not start with `$` is a *literal*
178
+ (query semantics), and a non-empty literal is EBV-**true**: a display
179
+ guard carried over from a diagram label (`count > 3`) is vacuously true
180
+ by design, because a picture's annotation must not change execution.
181
+ Real conditions are `$`-paths (`"$.payload.fresh"`) or operator
182
+ documents (`{ "$gt": ["$.context.count", 3] }`).
183
+
184
+ ## Performance (measured)
185
+
186
+ `npm run benchmark:flow` (run it yourself — the FSM head-to-head needs
187
+ the `xstate` benchmark devDependency, and the memory row needs
188
+ `node --expose-gc`, which the script passes). The same logical machine is
189
+ built with `@jarenjs/flow` and XState v5 and asserted to agree before
190
+ timing:
191
+
192
+ - **Transitions** — the pure `step` runs several times faster than an
193
+ XState actor's `send` (≈<!--bm:flow.fsmBand-->5.6–8.1<!--/bm-->× across 5/50/500-state machines); the
194
+ `createFsmSession` wrapper is on the page too.
195
+ - **Compile** — `compileFsm` beats `createMachine` + `createActor`
196
+ ≈1.5–2.6×. Not like-for-like: XState builds a scheduling actor, so the
197
+ row is each engine's description→drivable cost.
198
+ - **The wedge** — a conformance fact, not a timing: a jaren-fsm document
199
+ is JSON *including its guards*, so it survives `JSON.stringify` →
200
+ `JSON.parse` and still compiles and still fires its guard. XState's
201
+ guards are functions JSON drops, so the round-tripped machine throws
202
+ "Guard not implemented" at the guarded transition. Serialize, store,
203
+ diff, ship, replay — that is the whole reason to speak JSON all the way
204
+ down.
205
+ - **Where we lose** — a compiled Jaren machine holds **more** memory than
206
+ the XState actor (every guard compiles to its own query closure); the
207
+ benchmark publishes the KiB-per-machine loss beside the wins.
208
+ - **The dag tax** — no npm library executes schema-validated JSON
209
+ dataflow, so the honest rival is the same pipeline hand-written in
210
+ JavaScript. A `compileDag` run costs ~8–20× the hand-written baseline —
211
+ the published price of dataflow as one serializable,
212
+ constrained-decodable JSON value, sitting beside what it buys.
213
+
214
+ The full tables, the wedge as a conformance row, and the fairness notes
215
+ are on the [benchmarks page](https://jklarenbeek.github.io/jarenjs/#/benchmarks?suite=flow).
216
+
217
+ ## Authoring with a model
218
+
219
+ A jaren-fsm or jaren-dag document is JSON published as a schema, so a
220
+ constrained decoder can author one — and because every compile error
221
+ carries a `code` and a `docPath`, a schema-valid but semantically broken
222
+ machine (a transition to an undeclared state, say) repairs in a bounded
223
+ loop rather than flailing. `@jarenjs/ai`'s
224
+ [*Authoring engine documents*](../ai/README.md#authoring-engine-documents-validate-and-compile)
225
+ section shows the `composeChecks(schema, compileGate)` recipe, and
226
+ [*A model as a dataflow node*](../ai/README.md#a-model-as-a-dataflow-node)
227
+ runs a model as an ordinary dag `task`. Neither package imports the
228
+ other — the composition is data.
229
+
230
+ ## Development
231
+
232
+ Unit tests live in `test/flow/` at the repository root
233
+ (`npm run test:flow`). See the repository [README](../../README.md) for
234
+ the full suite documentation.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @file The @jarenjs/app adapter: turn a jaren-fsm document into the
3
+ * standard app documents that host it (docs/APP-INTEGRATION.md). This
4
+ * is a GENERATOR, not a runtime — it emits pure JSON action documents
5
+ * the app compiles like any hand-written action, and nothing from this
6
+ * package runs afterwards. Selection semantics are the headless
7
+ * engine's, reproduced structurally: one conditional chain per named
8
+ * event, transitions in document order, `$and`'s first-false stop
9
+ * playing the role of the step function's guard gate.
10
+ *
11
+ * The scope mapping (APP-INTEGRATION.md §scope): a guard or `with`
12
+ * query authored against FLOW-FORMAT §3's `{ state, event, payload,
13
+ * context }` is rewritten to read the reserved variable `__fsm`, bound
14
+ * once per action to `{ state: <pointer>.current, event: <literal>,
15
+ * payload: $payload, context: $ }` — all pre-transition, exactly like
16
+ * the headless step scope.
17
+ */
18
+ /**
19
+ * Generate the standard app documents that host a jaren-fsm machine:
20
+ * a state slice, one action document per distinct named event, and the
21
+ * event vocabulary. The output is pure JSON with the machine's target
22
+ * states, event names and effect origins baked as literals; guards and
23
+ * effect props run through the app's own query engine at dispatch
24
+ * time. See docs/APP-INTEGRATION.md for the convention this implements.
25
+ *
26
+ * @param {any} fsmDoc - a jaren-fsm document (FLOW-FORMAT §2)
27
+ * @param {{ pointer?: string, namespace?: string }} [options] -
28
+ * `pointer` (default `/fsm`) is where the slice lives in app state,
29
+ * as a chain of identifier-safe segments; `namespace` (default
30
+ * `fsm/`) prefixes the generated action names.
31
+ * @returns {{ slice: { current: string|null }, actions: Record<string, any>, events: string[] }}
32
+ * @throws {import('./errors.js').FlowCompileError} on a bad machine
33
+ * document (the same JF0xxx codes as `compileFsm`)
34
+ * @throws {TypeError} on malformed options or a document binding the
35
+ * reserved `__fsm` variable name
36
+ */
37
+ export declare function fsmToApp(fsmDoc: any, options?: {
38
+ pointer?: string;
39
+ namespace?: string;
40
+ }): {
41
+ slice: {
42
+ current: string | null;
43
+ };
44
+ actions: Record<string, any>;
45
+ events: string[];
46
+ };
47
+ /**
48
+ * The state slice's JSON Schema: `current` as an enum of the machine's
49
+ * declared state ids — compose it into a `validateState` schema at the
50
+ * slice pointer so no hand-written action can corrupt the control
51
+ * state (APP-INTEGRATION.md §fail-closed).
52
+ * @param {any} fsmDoc - a jaren-fsm document
53
+ * @returns {{ type: 'object', required: string[], properties: { current: { description: string, enum: string[] } } }}
54
+ * @throws {import('./errors.js').FlowCompileError} on a bad machine document
55
+ */
56
+ export declare function fsmStateSchema(fsmDoc: any): {
57
+ type: 'object';
58
+ required: string[];
59
+ properties: {
60
+ current: {
61
+ description: string;
62
+ enum: string[];
63
+ };
64
+ };
65
+ };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @file The jaren-dag engine: compile an acyclic dataflow document
3
+ * (docs/FLOW-FORMAT.md §6) once — every embedded query, stylesheet,
4
+ * `with` and `select` becomes a closure, the task registry is resolved,
5
+ * the wiring rules are proven — and run it many times. A run resolves
6
+ * nodes as their inputs arrive (independent branches concurrently),
7
+ * delivers values by reference, and fails closed: the first failing
8
+ * node aborts the shared signal and rejects the whole run (§7.3) — no
9
+ * retries, no partial results.
10
+ *
11
+ * Determinism is same input → same output VALUES, never same timing:
12
+ * results are keyed per node and port objects assemble in edge
13
+ * document order, so completion order cannot change a value (§7.2).
14
+ */
15
+ export type DagNodeRecord = {
16
+ id: string;
17
+ status: 'ok' | 'error' | 'aborted' | 'restored';
18
+ ms: number;
19
+ };
20
+ export type DagCheckpointStore = {
21
+ load: (runId: string) => any;
22
+ save: (runId: string, nodeId: string, value: any) => any;
23
+ complete: (runId: string, result: any) => any;
24
+ };
25
+ export type CompiledDag = {
26
+ /**
27
+ * - Declared node ids, document order.
28
+ */
29
+ nodes: readonly string[];
30
+ /**
31
+ * - The output node's id.
32
+ */
33
+ output: string;
34
+ /**
35
+ * -
36
+ * Execute the graph for one input (`undefined` reads as `null`).
37
+ */
38
+ run: (input?: any, opts?: {
39
+ signal?: AbortSignal;
40
+ onNode?: (record: DagNodeRecord) => void;
41
+ runId?: string;
42
+ }) => Promise<any>;
43
+ };
44
+ /**
45
+ * A settlement record handed to `onNode` (§7.4). `restored` fires at
46
+ * the start of a RESUMED run for every node whose checkpointed value
47
+ * was seeded instead of evaluated (§7.6).
48
+ * @typedef {{ id: string, status: 'ok'|'error'|'aborted'|'restored', ms: number }} DagNodeRecord
49
+ */
50
+ /**
51
+ * The opt-in checkpoint store (§7.6): `load` answers a prior run's
52
+ * recorded values (or null), `save` records one declared node's
53
+ * value, `complete` records the run's result. Any member may return a
54
+ * promise; a throwing store fails the run (JF2009), never silently.
55
+ * @typedef {Object} DagCheckpointStore
56
+ * @property {(runId: string) => any} load
57
+ * @property {(runId: string, nodeId: string, value: any) => any} save
58
+ * @property {(runId: string, result: any) => any} complete
59
+ */
60
+ /**
61
+ * A compiled jaren-dag graph.
62
+ * @typedef {Object} CompiledDag
63
+ * @property {readonly string[]} nodes - Declared node ids, document order.
64
+ * @property {string} output - The output node's id.
65
+ * @property {(input?: any, opts?: { signal?: AbortSignal, onNode?: (record: DagNodeRecord) => void, runId?: string }) => Promise<any>} run -
66
+ * Execute the graph for one input (`undefined` reads as `null`).
67
+ */
68
+ /**
69
+ * Compile a jaren-dag document (docs/FLOW-FORMAT.md §6–§7) against a
70
+ * task registry. Everything is decided here: structural validation,
71
+ * the wiring rules, acyclicity, embedded-document compilation and
72
+ * registry resolution — `run` only executes closures.
73
+ *
74
+ * @param {any} doc - the jaren-dag document
75
+ * @param {{ tasks?: Record<string, (props: { with: any, input: any }, signal: AbortSignal) => any>,
76
+ * checkpoint?: DagCheckpointStore }} [options]
77
+ * @returns {CompiledDag}
78
+ * @throws {FlowCompileError} when the document violates the format (JF0xxx)
79
+ * @throws {TypeError} when the options are malformed (a registry that is
80
+ * not an object, a registered handler that is not a function, or a
81
+ * checkpoint store missing one of load/save/complete)
82
+ */
83
+ export declare function compileDag(doc: any, options?: {
84
+ tasks?: Record<string, (props: {
85
+ with: any;
86
+ input: any;
87
+ }, signal: AbortSignal) => any>;
88
+ checkpoint?: DagCheckpointStore;
89
+ }): CompiledDag;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * @file Error types for @jarenjs/flow, built on `@jarenjs/core`'s coded
3
+ * contract: every failure carries a stable `code` (JF0xxx compile,
4
+ * JF2xxx runtime), a bare `reason`, a composed `message`, and — where
5
+ * one exists — the `docPath` of the offending member of the flow
6
+ * document. The feedback shape a repair loop needs. The normative
7
+ * table lives in docs/FLOW-FORMAT.md §5, proven in sync with
8
+ * `FLOW_CODES` below by a test.
9
+ */
10
+ import { CodedError } from '@jarenjs/core/errors';
11
+ /**
12
+ * The runtime code table (the `CSV_CODES` shape): one entry per code
13
+ * this package can raise, proven in sync with FLOW-FORMAT.md §5's
14
+ * normative table by a test — the "must stay in sync by hand" note this
15
+ * file used to carry is now a checked fact.
16
+ */
17
+ export declare const FLOW_CODES: Readonly<{
18
+ JF0001: "the document is not an object, or $fsm is not 0.1";
19
+ JF0002: "states is not an array, or a state entry is malformed";
20
+ JF0003: "two state entries share one id";
21
+ JF0004: "initial is neither null nor a declared state id";
22
+ JF0005: "transitions is not an array, or an entry is malformed";
23
+ JF0006: "a transition from/to names no declared state";
24
+ JF0007: "a guard failed to compile as a query document";
25
+ JF0008: "an effects list or effect descriptor is malformed";
26
+ JF0009: "an effect with failed to compile as a query document";
27
+ JF0010: "the dag document is not an object, or $dag is not 0.1";
28
+ JF0011: "nodes is not an object, or a node declaration is malformed";
29
+ JF0012: "edges is not an array, or an edge entry is malformed";
30
+ JF0013: "an edge from/to names no declared node";
31
+ JF0014: "an embedded document failed to compile";
32
+ JF0015: "the wiring rules are violated";
33
+ JF0016: "the graph has a cycle";
34
+ JF0017: "the document does not declare exactly one output node";
35
+ JF0018: "a task node names a handler the registry does not provide";
36
+ JF2001: "a state id the machine does not declare";
37
+ JF2002: "step was called with a non-string event";
38
+ JF2003: "a guard threw while evaluating";
39
+ JF2004: "an effect with threw while evaluating";
40
+ JF2005: "a session was created with no start state";
41
+ JF2006: "a dag node failed while evaluating; the run rejects";
42
+ JF2007: "the caller signal aborted the run";
43
+ JF2008: "a declared checkpoint value is not JSON-serializable";
44
+ JF2009: "the checkpoint store failed";
45
+ }>;
46
+ /**
47
+ * A defect in the flow document itself, raised while `compileFsm`
48
+ * compiles it. Codes:
49
+ *
50
+ * - `JF0001` — the document is not an object, or `$fsm` is present and
51
+ * not `'0.1'`
52
+ * - `JF0002` — `states` is not an array, or a state entry is neither a
53
+ * string nor an object with a string `id` (or carries a non-boolean
54
+ * `final`)
55
+ * - `JF0003` — two state entries share one id
56
+ * - `JF0004` — `initial` is neither null nor the id of a declared state
57
+ * - `JF0005` — `transitions` is not an array, or a transition entry is
58
+ * malformed (not an object; `from`/`to` not strings; `event` neither
59
+ * a string nor null)
60
+ * - `JF0006` — a transition's `from` or `to` names no declared state
61
+ * - `JF0007` — a guard failed to compile as a query document (see
62
+ * `cause`)
63
+ * - `JF0008` — an effects list is not an array, or an effect
64
+ * descriptor is not an object with a non-empty string `run`
65
+ * - `JF0009` — an effect's `with` failed to compile as a query
66
+ * document (see `cause`)
67
+ *
68
+ * Dag documents (`compileDag`):
69
+ *
70
+ * - `JF0010` — the dag document is not an object, or `$dag` is not
71
+ * `'0.1'` (the key is required — the dag format has no legacy
72
+ * contract to stay compatible with)
73
+ * - `JF0011` — `nodes` is not an object, or a node declaration is
74
+ * malformed (not an object; unknown `kind`; a kind-specific member
75
+ * missing or mistyped: `const` needs `value`, `query` needs
76
+ * `query`, `jslt` needs `stylesheet`, `task` needs a non-empty
77
+ * string `run`)
78
+ * - `JF0012` — `edges` is not an array, or an edge entry is malformed
79
+ * (not an object; `from`/`to` not strings; `port` present but not a
80
+ * non-empty string)
81
+ * - `JF0013` — an edge's `from` or `to` names no declared node
82
+ * - `JF0014` — an embedded document failed to compile (a node's
83
+ * `query`/`stylesheet`/`with` or an edge's `select`; see `cause`)
84
+ * - `JF0015` — the wiring rules are violated: an edge enters an
85
+ * `input`/`const` node or leaves the `output` node; fan-in without
86
+ * complete unique ports (a ported inbound set must be all-ported
87
+ * and duplicate-free); or a consuming node (`query`/`jslt`/`task`/
88
+ * `output`) has no inbound edge
89
+ * - `JF0016` — the graph has a cycle (the message lists the member
90
+ * ids; `docPath` points at the first edge inside it)
91
+ * - `JF0017` — the document does not declare exactly one `output`
92
+ * node
93
+ * - `JF0018` — a `task` node names a handler the compile-time
94
+ * registry does not provide
95
+ */
96
+ export declare class FlowCompileError extends CodedError {
97
+ /**
98
+ * @param {string} code
99
+ * @param {string} reason - The bare reason; `message` is composed per
100
+ * the coded contract.
101
+ * @param {string} [docPath] - JSON Pointer into the flow document;
102
+ * `''` is the document root, `undefined` means no location.
103
+ * @param {Error} [cause]
104
+ */
105
+ constructor(code: string, reason: string, docPath?: string, cause?: Error);
106
+ }
107
+ /**
108
+ * A failure while a compiled machine is being driven. Only caller
109
+ * mistakes throw; document-level evaluation failures never do — they
110
+ * fail closed and are recorded as plain data on the step result
111
+ * (FLOW-FORMAT §5.2). Codes:
112
+ *
113
+ * - `JF2001` — `step`, `events` or `final` was called with a state id
114
+ * the machine does not declare (thrown; a caller bug, not machine
115
+ * input)
116
+ * - `JF2002` — `step` was called with a non-string event (thrown)
117
+ * - `JF2003` — a guard threw while evaluating (recorded on the step
118
+ * result; the guard reads false and selection continues)
119
+ * - `JF2004` — an effect's `with` threw while evaluating (recorded on
120
+ * the step result; the effect is omitted)
121
+ * - `JF2005` — a session was created with no start state (`initial`
122
+ * is null and none was given) (thrown)
123
+ *
124
+ * Dag runs (`compileDag(...).run`) have no recorded-error channel —
125
+ * a failure rejects the run promise (fail closed, no partial results):
126
+ *
127
+ * - `JF2006` — a node failed while evaluating; the run rejects, the
128
+ * shared signal aborts in-flight siblings, and the error carries
129
+ * the failing node's id as an own `nodeId` property beside
130
+ * `docPath` and `cause`
131
+ * - `JF2007` — the caller's `signal` aborted the run (`cause` is the
132
+ * abort reason when one was given)
133
+ */
134
+ export declare class FlowRuntimeError extends CodedError {
135
+ /**
136
+ * @param {string} code
137
+ * @param {string} reason - The bare reason; `message` is composed per
138
+ * the coded contract.
139
+ * @param {string} [docPath] - JSON Pointer into the flow document;
140
+ * `''` is the document root, `undefined` means no location.
141
+ * @param {Error} [cause]
142
+ */
143
+ constructor(code: string, reason: string, docPath?: string, cause?: Error);
144
+ }
145
+ /**
146
+ * Normalize a thrown value to an `Error`. A dag run rejects with whatever
147
+ * a registered task threw, and a task is host code free to throw a
148
+ * non-Error, so the rejection contract needs one: a bare value becomes an
149
+ * `Error` naming the type it arrived as. In the fsm engine the only throw
150
+ * sources are the query engine's own error classes, so there it is
151
+ * belt-and-braces rather than hostile-input hardening.
152
+ * @param {unknown} v
153
+ * @returns {Error}
154
+ */
155
+ export declare function asError(v: unknown): Error;