@beehexa/hexasync-template-worker-flow 2608.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { type RoutableStep } from './routes.js';
2
+ /** What a synthesised node is, so a renderer picks a shape without inspecting the label. */
3
+ export type JunctionKind = 'if' | 'expression' | 'unknown';
4
+ /** A node the document does not declare, produced because a route needs somewhere to branch. */
5
+ export interface Junction {
6
+ /** Relative path from the owning step, e.g. `['next', 'if']`. The caller prefixes the step's address. */
7
+ readonly path: readonly string[];
8
+ readonly kind: JunctionKind;
9
+ /** The text a renderer draws in it: the condition, the expression, or the unresolved target. */
10
+ readonly label: string;
11
+ /** The step key that produced it. */
12
+ readonly owner: string;
13
+ }
14
+ /** An edge in the route graph. Ends are either a step key or a junction path, tagged so the caller can address it. */
15
+ export interface GraphEdge {
16
+ readonly from: EdgeEnd;
17
+ readonly to: EdgeEnd;
18
+ readonly label?: string;
19
+ }
20
+ export type EdgeEnd = {
21
+ readonly kind: 'step';
22
+ readonly key: string;
23
+ } | {
24
+ readonly kind: 'junction';
25
+ readonly owner: string;
26
+ readonly path: readonly string[];
27
+ };
28
+ export interface RouteGraph {
29
+ readonly junctions: readonly Junction[];
30
+ readonly edges: readonly GraphEdge[];
31
+ }
32
+ /**
33
+ * Every junction and every edge for the steps of ONE stage.
34
+ *
35
+ * Iterative over an explicit work list, never recursive — `then`/`else` nest without bound and a document is
36
+ * untrusted input. The work list carries the PATH, which is what makes the addressing positional-free.
37
+ */
38
+ export declare function routeGraph(steps: readonly RoutableStep[]): RouteGraph;
39
+ /** The address of a junction, given its owning step's address. AD-33 rule 4 / rule 5. */
40
+ export declare function junctionAddress(ownerAddress: string, path: readonly string[]): string;
41
+ //# sourceMappingURL=routeGraph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeGraph.d.ts","sourceRoot":"","sources":["../src/routeGraph.ts"],"names":[],"mappings":"AAqCA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAErB,4FAA4F;AAC5F,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,YAAY,GAAG,SAAS,CAAC;AAE3D,gGAAgG;AAChG,MAAM,WAAW,QAAQ;IACvB,yGAAyG;IACzG,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,gGAAgG;IAChG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,sHAAsH;AACtH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,OAAO,GACf;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC/C;IACE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC,CAAC;AAEN,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;CACtC;AAID;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,UAAU,CAsMrE;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,MAAM,CAOR"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The route GRAPH — edges that pass THROUGH the nodes a branch synthesises.
3
+ *
4
+ * ### Why this exists alongside `routeEdges`, which is flat
5
+ *
6
+ * ⛔ The first version of this package had `routeEdges` only, and `flow.ts` synthesised a decision node beside it
7
+ * without connecting the two. The Story 4.1 review measured what that produced: a diamond with **zero** edges
8
+ * touching it, while the YES/NO arms left the step — so the diamond was unreachable and undrawable, and the step
9
+ * carried arms it does not have. Worse, it synthesised for the ~22 corpus steps with an object-valued `next` and
10
+ * produced **no** node for the **3,480** with an expression-valued one, which is exactly where both shipped
11
+ * surfaces DO synthesise (`_EXPR_<n>` in `flowChart.ts:145-155`, in the golden baseline for fixture 01).
12
+ *
13
+ * `routeEdges` is kept because it answers a genuinely different question — *"what routes where"*, flat, which is
14
+ * what `flowEdges` answers and what `routeParity.spec.ts` compares. This answers *"how is it drawn"*, and
15
+ * `routeGraph.spec.ts` asserts the two agree about reachability so they cannot drift.
16
+ *
17
+ * ### The three synthesised kinds, from the shipped baseline
18
+ *
19
+ * | kind | when | drawn as | label |
20
+ * | --- | --- | --- | --- |
21
+ * | `if` | an object route `{ if, then, else }` | diamond | the `if` text, or `?` |
22
+ * | `expression` | a Scriban route naming ≥1 real step | diamond | the expression |
23
+ * | `unknown` | a route naming no step at all | stadium | the route text |
24
+ *
25
+ * ### Addressed by PATH, per AD-33 rule 4
26
+ *
27
+ * A structural node is *"named by the keys that reach it"*. So the junction for a step's object `next` is
28
+ * `<step>:next:if` — AD-33's own example — a nested arm is `<step>:next:then:if`, an `IF` branching through `data`
29
+ * is `<step>:data:if`, and an expression is `<step>:next:expression`. No counter anywhere, so inserting a step above
30
+ * renumbers nothing. The path also disambiguates naturally: two dead ends leaving one step reach it by different
31
+ * keys (`:next:unknown` and `:onError:unknown`), so neither needs an index.
32
+ */
33
+ import { keySegment, entrySegment, nodeAddress, } from '@beehexa/hexasync-template-model';
34
+ import { looksLikeExpression, quotedKeys, } from './routes.js';
35
+ const step_ = (key) => ({ kind: 'step', key });
36
+ /**
37
+ * Every junction and every edge for the steps of ONE stage.
38
+ *
39
+ * Iterative over an explicit work list, never recursive — `then`/`else` nest without bound and a document is
40
+ * untrusted input. The work list carries the PATH, which is what makes the addressing positional-free.
41
+ */
42
+ export function routeGraph(steps) {
43
+ const known = new Set(steps.map((s) => String(s.key)));
44
+ const junctions = [];
45
+ const edges = [];
46
+ /** Junctions already created, so two routes reaching the same path share one node. */
47
+ const seen = new Set();
48
+ const junction = (owner, path, kind, label) => {
49
+ const id = `${owner} ${path.join(' ')}`;
50
+ if (!seen.has(id)) {
51
+ seen.add(id);
52
+ junctions.push({ owner, path, kind, label });
53
+ }
54
+ return { kind: 'junction', owner, path };
55
+ };
56
+ for (const step of steps) {
57
+ const owner = String(step.key);
58
+ const type = String(step.stepType ?? '').toUpperCase();
59
+ const isBranchObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
60
+ /**
61
+ * Branch objects already entered on THIS step's walk — the cycle guard.
62
+ *
63
+ * ⛔ Without it a cyclic branch does not merely loop, it **kills the process**. Found by the Story 4.2 review and
64
+ * confirmed here: `const a = {}; a.then = a; routeGraph([{ key: 'A', next: a }])` exits **134**, `FATAL ERROR:
65
+ * heap out of memory`, in a few seconds. The `path` grows by two segments per iteration, `seen` is keyed by that
66
+ * path so it never repeats, and both `junctions` and the work list grow without bound.
67
+ *
68
+ * This module's own docblock argued *"iterative, never recursive — a document is untrusted input"*. Iteration was
69
+ * the right call and it removed the only thing that had been bounding this: the reference implementation recurses
70
+ * and throws a **catchable** `RangeError`. An uncatchable V8 fatal is strictly worse, and this package is bundled
71
+ * into the VS Code extension host, so the blast radius is the editor process.
72
+ *
73
+ * REACHABLE FROM YAML with the parser the CLI already uses — a recursive anchor is all it takes:
74
+ *
75
+ * ```yaml
76
+ * next: &loop
77
+ * if: "x"
78
+ * then: *loop
79
+ * ```
80
+ *
81
+ * Identity, not path: the same object reached twice is the cycle, however long the path to it.
82
+ */
83
+ const entered = new WeakSet();
84
+ /** Work list of routes still to place: where the arrow leaves, the value, the path, the label. */
85
+ const pending = [];
86
+ // ── Which route(s) this step declares, and under which key ──
87
+ //
88
+ // An IF whose `data` is an object but declares NEITHER `then` NOR `else` falls back to `next`. Without the
89
+ // fallback a partly-written IF loses its only edge and its target is orphaned in the diagram — `data:
90
+ // { expression: '…' }` with `next: 'DONE'` drew `CHECK → <diamond>` and nothing else, leaving `DONE` floating.
91
+ //
92
+ // This mirrors `routes.ts`, the flat twin, which has always had the fallback and says why in the same words. The
93
+ // two disagreed until Story 4.7's review measured it; this module's own docblock claimed the case "routed
94
+ // correctly through its `next`", which was aspiration rather than behaviour. `type` comes from `displayType`
95
+ // first, and every UI-served step carries one, so the divergence was reachable from a real surface.
96
+ const branching = (type === 'IF' || type === 'SWITCH') && isBranchObject(step.data);
97
+ const ifWithoutBranch = type === 'IF' &&
98
+ branching &&
99
+ step.data.then == null &&
100
+ step.data.else == null;
101
+ if (branching && !ifWithoutBranch) {
102
+ pending.push({ from: step_(owner), value: step.data, path: ['data'] });
103
+ }
104
+ else {
105
+ pending.push({ from: step_(owner), value: step.next, path: ['next'] });
106
+ }
107
+ if (step.onError !== null && step.onError !== undefined) {
108
+ pending.push({
109
+ from: step_(owner),
110
+ value: step.onError,
111
+ path: ['onError'],
112
+ label: 'onError',
113
+ });
114
+ }
115
+ while (pending.length > 0) {
116
+ const { from, value, path, label } = pending.pop();
117
+ if (value === null || value === undefined || value === '')
118
+ continue;
119
+ const labelled = label !== undefined ? { label } : {};
120
+ // ── An object route: a diamond, and its arms leave the DIAMOND, not the step ──
121
+ if (isBranchObject(value)) {
122
+ const branch = value;
123
+ if (entered.has(branch)) {
124
+ /**
125
+ * A cycle. Reported as a dead end labelled for what it is, rather than dropped: a route that loops back on
126
+ * itself is a real document defect, and a diagram that simply stops there tells the reader nothing.
127
+ */
128
+ const point = junction(owner, [...path, 'cycle'], 'unknown', 'cycle: this branch refers to itself');
129
+ edges.push({ from, to: point, ...labelled });
130
+ continue;
131
+ }
132
+ entered.add(branch);
133
+ const isSwitch = type === 'SWITCH' && path.length === 1 && path[0] === 'data';
134
+ if (isSwitch) {
135
+ // A SWITCH's arms are its data KEYS. The junction is the switch point itself.
136
+ const point = junction(owner, [...path, 'switch'], 'if', 'SWITCH');
137
+ edges.push({ from, to: point, ...labelled });
138
+ for (const arm of Object.keys(branch)) {
139
+ pending.push({
140
+ from: point,
141
+ value: arm,
142
+ path: [...path, arm],
143
+ label: arm,
144
+ });
145
+ }
146
+ continue;
147
+ }
148
+ const condition = branch.if === null || branch.if === undefined
149
+ ? '?'
150
+ : String(branch.if);
151
+ const point = junction(owner, [...path, 'if'], 'if', condition);
152
+ edges.push({ from, to: point, ...labelled });
153
+ // `else` first so `then` is placed first — a reader expects YES before NO.
154
+ pending.push({
155
+ from: point,
156
+ value: branch.else,
157
+ path: [...path, 'else'],
158
+ label: 'NO',
159
+ });
160
+ pending.push({
161
+ from: point,
162
+ value: branch.then,
163
+ path: [...path, 'then'],
164
+ label: 'YES',
165
+ });
166
+ continue;
167
+ }
168
+ const text = String(value);
169
+ // ── A direct step key: a plain edge, no synthesis ──
170
+ if (known.has(text)) {
171
+ edges.push({ from, to: step_(text), ...labelled });
172
+ continue;
173
+ }
174
+ // ── An expression naming real steps: a diamond, then one arrow per target ──
175
+ if (looksLikeExpression(text)) {
176
+ const targets = quotedKeys(text).filter((k) => known.has(k));
177
+ if (targets.length > 0) {
178
+ const point = junction(owner, [...path, 'expression'], 'expression', text);
179
+ edges.push({ from, to: point, ...labelled });
180
+ // Unlabelled, as the baseline draws them: which target it takes is a run-time fact.
181
+ for (const target of targets) {
182
+ edges.push({ from: point, to: step_(target) });
183
+ }
184
+ continue;
185
+ }
186
+ }
187
+ // ── Naming nothing: a terminal, kept so the reader sees the dead end ──
188
+ const point = junction(owner, [...path, 'unknown'], 'unknown', text);
189
+ edges.push({ from, to: point, ...labelled });
190
+ }
191
+ /**
192
+ * The exception BACK-REFERENCE, which is reversed: an edge INTO this step from the one that threw. No junction —
193
+ * it is a direct edge between two authored steps.
194
+ */
195
+ if (step.fromStep && known.has(step.fromStep)) {
196
+ edges.push({
197
+ from: step_(step.fromStep),
198
+ to: step_(owner),
199
+ label: 'Exception',
200
+ });
201
+ }
202
+ }
203
+ return { junctions, edges };
204
+ }
205
+ /** The address of a junction, given its owning step's address. AD-33 rule 4 / rule 5. */
206
+ export function junctionAddress(ownerAddress, path) {
207
+ return nodeAddress([
208
+ keySegment(ownerAddress),
209
+ // A path element is a document key where it is one (`next`, `then`, `if`) and a dictionary tail where it is a
210
+ // SWITCH arm — `entrySegment` normalises both and refuses a `!`-prefixed one.
211
+ ...path.map((element) => entrySegment(element) ?? keySegment(element)),
212
+ ]);
213
+ }
214
+ //# sourceMappingURL=routeGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeGraph.js","sourceRoot":"","sources":["../src/routeGraph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EACL,UAAU,EACV,YAAY,EACZ,WAAW,GACZ,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,mBAAmB,EACnB,UAAU,GAEX,MAAM,aAAa,CAAC;AAoCrB,MAAM,KAAK,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAA8B;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,sFAAsF;IACtF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,QAAQ,GAAG,CACf,KAAa,EACb,IAAuB,EACvB,IAAkB,EAClB,KAAa,EACJ,EAAE;QACX,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC3C,CAAC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACvD,MAAM,cAAc,GAAG,CAAC,CAAU,EAAE,EAAE,CACpC,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAE3D;;;;;;;;;;;;;;;;;;;;;;WAsBG;QACH,MAAM,OAAO,GAAG,IAAI,OAAO,EAAU,CAAC;QAEtC,kGAAkG;QAClG,MAAM,OAAO,GAKP,EAAE,CAAC;QAET,+DAA+D;QAC/D,EAAE;QACF,2GAA2G;QAC3G,sGAAsG;QACtG,+GAA+G;QAC/G,EAAE;QACF,iHAAiH;QACjH,0GAA0G;QAC1G,6GAA6G;QAC7G,oGAAoG;QACpG,MAAM,SAAS,GACb,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,MAAM,eAAe,GACnB,IAAI,KAAK,IAAI;YACb,SAAS;YACR,IAAI,CAAC,IAAgC,CAAC,IAAI,IAAI,IAAI;YAClD,IAAI,CAAC,IAAgC,CAAC,IAAI,IAAI,IAAI,CAAC;QACtD,IAAI,SAAS,IAAI,CAAC,eAAe,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC;gBAClB,KAAK,EAAE,IAAI,CAAC,OAAO;gBACnB,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,KAAK,EAAE,SAAS;aACjB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,GAAG,EAAG,CAAC;YACpD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;gBAAE,SAAS;YACpE,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAEtD,iFAAiF;YACjF,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,KAAgC,CAAC;gBAChD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;oBACxB;;;uBAGG;oBACH,MAAM,KAAK,GAAG,QAAQ,CACpB,KAAK,EACL,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,EAClB,SAAS,EACT,qCAAqC,CACtC,CAAC;oBACF,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;oBAC7C,SAAS;gBACX,CAAC;gBACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACpB,MAAM,QAAQ,GACZ,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;gBAC/D,IAAI,QAAQ,EAAE,CAAC;oBACb,8EAA8E;oBAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;oBACnE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;oBAC7C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;wBACtC,OAAO,CAAC,IAAI,CAAC;4BACX,IAAI,EAAE,KAAK;4BACX,KAAK,EAAE,GAAG;4BACV,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,CAAC;4BACpB,KAAK,EAAE,GAAG;yBACX,CAAC,CAAC;oBACL,CAAC;oBACD,SAAS;gBACX,CAAC;gBACD,MAAM,SAAS,GACb,MAAM,CAAC,EAAE,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,KAAK,SAAS;oBAC3C,CAAC,CAAC,GAAG;oBACL,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACxB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBAChE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;gBAC7C,2EAA2E;gBAC3E,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE,MAAM,CAAC,IAAI;oBAClB,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC;oBACvB,KAAK,EAAE,IAAI;iBACZ,CAAC,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE,MAAM,CAAC,IAAI;oBAClB,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC;oBACvB,KAAK,EAAE,KAAK;iBACb,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAE3B,sDAAsD;YACtD,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;gBACnD,SAAS;YACX,CAAC;YAED,8EAA8E;YAC9E,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,MAAM,KAAK,GAAG,QAAQ,CACpB,KAAK,EACL,CAAC,GAAG,IAAI,EAAE,YAAY,CAAC,EACvB,YAAY,EACZ,IAAI,CACL,CAAC;oBACF,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;oBAC7C,oFAAoF;oBACpF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;wBAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBACjD,CAAC;oBACD,SAAS;gBACX,CAAC;YACH,CAAC;YAED,yEAAyE;YACzE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YACrE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;QAC/C,CAAC;QAED;;;WAGG;QACH,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAC1B,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC;gBAChB,KAAK,EAAE,WAAW;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,eAAe,CAC7B,YAAoB,EACpB,IAAuB;IAEvB,OAAO,WAAW,CAAC;QACjB,UAAU,CAAC,YAAY,CAAC;QACxB,8GAA8G;QAC9G,8EAA8E;QAC9E,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;KACvE,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Where a step goes next — Story 4.1 AC-2, all five kinds, in the MODEL rather than in a renderer.
3
+ *
4
+ * The semantics are the runtime's: `core.api/hexasync.steps/Execution/NextStepBuilder.cs` and `IfStepExecutor`.
5
+ * This package does not invent them; NFR-1 says the editor invents no semantics the runtime lacks.
6
+ *
7
+ * | kind | where it is declared | who drew it before |
8
+ * | --- | --- | --- |
9
+ * | by step TYPE | `IF` / `SWITCH` read their `data` | CLI only — dashboard reads only `step.next` |
10
+ * | by step DATA | `{ if, then, else }` on `next` or `data` | CLI |
11
+ * | an EXPRESSION | a Scriban `next` naming keys in quotes | CLI |
12
+ * | an ERROR route | `onError` | CLI |
13
+ * | an exception BACK-REFERENCE | `fromStep` → this step | **nobody** — see below |
14
+ *
15
+ * ### The back-reference is why routing has to live here
16
+ *
17
+ * `fromStep` produces an edge *into* the exception step from the step that threw. It exists **only in the CLI's
18
+ * drawing path** (`flowChart.ts:199-201` emits `fromStep --|"Exception"|-> this`) and **not** in the shared
19
+ * `flowEdges`. The VS Code extension imports `flowEdges`, so it has never drawn that edge at all — a whole route
20
+ * kind missing from one surface because the renderer, not the model, owned it. Moving routing into the model is what
21
+ * makes AC-2's last clause true everywhere, and it is the concrete argument for this package existing.
22
+ *
23
+ * ### An unresolvable route is REPORTED, not dropped
24
+ *
25
+ * A route naming no step in the stage yields an edge with `raw` and no `to`. A reader needs to see the dead end:
26
+ * silently dropping it draws a flow that terminates where the document does not say it terminates, which is the
27
+ * more expensive error. Same for an expression whose quoted keys match nothing.
28
+ */
29
+ /** A step as routing needs to see it. Deliberately structural — this package reads no schema and no file. */
30
+ export interface RoutableStep {
31
+ readonly key: string;
32
+ /** Raw `next`: a step key, a `{ if, then, else }` branch, a Scriban expression, or absent. */
33
+ readonly next?: unknown;
34
+ /** The step's declared type, e.g. `IF`, `SWITCH`. Compared case-insensitively. */
35
+ readonly stepType?: string;
36
+ /** For `IF`/`SWITCH`, the runtime passes `data` to the route builder. */
37
+ readonly data?: unknown;
38
+ /** A recovery target. */
39
+ readonly onError?: unknown;
40
+ /** An Exception step's back-reference: the step whose failure reaches this one. */
41
+ readonly fromStep?: string;
42
+ }
43
+ /** One arrow between two steps. */
44
+ export interface RouteEdge {
45
+ /** The step the arrow leaves. */
46
+ readonly from: string;
47
+ /** The step it reaches, when that step exists in this stage. */
48
+ readonly to?: string;
49
+ /** `YES`, `NO`, a SWITCH branch name, `onError`, `Exception`. */
50
+ readonly label?: string;
51
+ /**
52
+ * The route text when it does not name a known step — an expression, or a dead end.
53
+ *
54
+ * Carried alongside `to` for an expression that DOES resolve, because which branch it takes at run time is a fact
55
+ * a static view cannot know, and showing the expression is how the reader learns that.
56
+ */
57
+ readonly raw?: string;
58
+ }
59
+ /** Every `"QUOTED_KEY"` in an expression. */
60
+ export declare function quotedKeys(expression: string): readonly string[];
61
+ /** Is this route text an expression rather than a step key? */
62
+ export declare function looksLikeExpression(text: string): boolean;
63
+ /**
64
+ * Every edge the steps of ONE stage declare.
65
+ *
66
+ * Iterative, never recursive: `then`/`else` nest without bound and a document is untrusted input, so a deep branch
67
+ * must not be able to exhaust the stack. (The same rule the puller step standards state: *no recursion — stack or
68
+ * loop*.)
69
+ */
70
+ export declare function routeEdges(steps: readonly RoutableStep[]): readonly RouteEdge[];
71
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,6GAA6G;AAC7G,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,yBAAyB;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,mCAAmC;AACnC,MAAM,WAAW,SAAS;IACxB,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAEhE;AAED,+DAA+D;AAC/D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,YAAY,EAAE,GAC7B,SAAS,SAAS,EAAE,CAmHtB"}
package/dist/routes.js ADDED
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Where a step goes next — Story 4.1 AC-2, all five kinds, in the MODEL rather than in a renderer.
3
+ *
4
+ * The semantics are the runtime's: `core.api/hexasync.steps/Execution/NextStepBuilder.cs` and `IfStepExecutor`.
5
+ * This package does not invent them; NFR-1 says the editor invents no semantics the runtime lacks.
6
+ *
7
+ * | kind | where it is declared | who drew it before |
8
+ * | --- | --- | --- |
9
+ * | by step TYPE | `IF` / `SWITCH` read their `data` | CLI only — dashboard reads only `step.next` |
10
+ * | by step DATA | `{ if, then, else }` on `next` or `data` | CLI |
11
+ * | an EXPRESSION | a Scriban `next` naming keys in quotes | CLI |
12
+ * | an ERROR route | `onError` | CLI |
13
+ * | an exception BACK-REFERENCE | `fromStep` → this step | **nobody** — see below |
14
+ *
15
+ * ### The back-reference is why routing has to live here
16
+ *
17
+ * `fromStep` produces an edge *into* the exception step from the step that threw. It exists **only in the CLI's
18
+ * drawing path** (`flowChart.ts:199-201` emits `fromStep --|"Exception"|-> this`) and **not** in the shared
19
+ * `flowEdges`. The VS Code extension imports `flowEdges`, so it has never drawn that edge at all — a whole route
20
+ * kind missing from one surface because the renderer, not the model, owned it. Moving routing into the model is what
21
+ * makes AC-2's last clause true everywhere, and it is the concrete argument for this package existing.
22
+ *
23
+ * ### An unresolvable route is REPORTED, not dropped
24
+ *
25
+ * A route naming no step in the stage yields an edge with `raw` and no `to`. A reader needs to see the dead end:
26
+ * silently dropping it draws a flow that terminates where the document does not say it terminates, which is the
27
+ * more expensive error. Same for an expression whose quoted keys match nothing.
28
+ */
29
+ /** Every `"QUOTED_KEY"` in an expression. */
30
+ export function quotedKeys(expression) {
31
+ return [...expression.matchAll(/"([A-Za-z0-9_]+)"/g)].map((m) => m[1]);
32
+ }
33
+ /** Is this route text an expression rather than a step key? */
34
+ export function looksLikeExpression(text) {
35
+ return (text.includes('{{') || text.includes('}}') || /"[A-Za-z0-9_]+"/.test(text));
36
+ }
37
+ /**
38
+ * Every edge the steps of ONE stage declare.
39
+ *
40
+ * Iterative, never recursive: `then`/`else` nest without bound and a document is untrusted input, so a deep branch
41
+ * must not be able to exhaust the stack. (The same rule the puller step standards state: *no recursion — stack or
42
+ * loop*.)
43
+ */
44
+ export function routeEdges(steps) {
45
+ // `String`, because the exported function is callable with a non-string `key` (Story 4.1 review, LOW-3): the raw
46
+ // value leaked into `RouteEdge.from`, typed `string`, and a route to step `5` was reported as a dead end because
47
+ // `known` held the number while the route text was `'5'`. `workerFlow` filters these out; a direct caller cannot.
48
+ const known = new Set(steps.map((s) => String(s.key)));
49
+ const edges = [];
50
+ const walk = (from, value, label) => {
51
+ /**
52
+ * Branch objects already entered on THIS step's walk — the cycle guard.
53
+ *
54
+ * ⛔ Added at the Epic 4 close (2026-08-12). `routeGraph.ts` and the frontend twin both carry this guard, with the
55
+ * reason recorded: a cyclic branch does not loop, it **kills the process** with an uncatchable V8 fatal, and this
56
+ * code is bundled into the VS Code extension host. This walker never received it, and it is the one the
57
+ * extension's fallback SVG still consumes on every preview — so the guarded packages closed the hole on the new
58
+ * path and left it open on the shipped one.
59
+ *
60
+ * Measured: `const a = {}; a.then = a;` walked through here under `--max-old-space-size=200` gives
61
+ * `FATAL ERROR: … heap out of memory`, process aborted. A `try/catch` around the preview does not help.
62
+ *
63
+ * REACHABLE FROM YAML with the parser the CLI already uses — a recursive anchor is all it takes:
64
+ *
65
+ * ```yaml
66
+ * next: &loop
67
+ * if: "x"
68
+ * then: *loop
69
+ * ```
70
+ *
71
+ * Identity, not path: the same object reached twice is the cycle, however long the path to it.
72
+ */
73
+ const entered = new WeakSet();
74
+ const stack = [
75
+ { value, ...(label !== undefined ? { label } : {}) },
76
+ ];
77
+ while (stack.length > 0) {
78
+ const { value: node, label: current } = stack.pop();
79
+ if (node === null || node === undefined || node === '')
80
+ continue;
81
+ const labelled = current !== undefined ? { label: current } : {};
82
+ if (typeof node === 'object' && !Array.isArray(node)) {
83
+ if (entered.has(node))
84
+ continue;
85
+ entered.add(node);
86
+ const branch = node;
87
+ // `else` pushed first so `then` is walked first — a reader expects YES before NO.
88
+ stack.push({ value: branch.else, label: 'NO' });
89
+ stack.push({ value: branch.then, label: 'YES' });
90
+ continue;
91
+ }
92
+ const text = String(node);
93
+ if (known.has(text)) {
94
+ edges.push({ from, to: text, ...labelled });
95
+ continue;
96
+ }
97
+ if (looksLikeExpression(text)) {
98
+ const targets = quotedKeys(text).filter((k) => known.has(k));
99
+ for (const target of targets) {
100
+ edges.push({ from, to: target, raw: text, ...labelled });
101
+ }
102
+ // Resolving to nothing is still a route, and the expression is the only thing that can be shown about it.
103
+ if (targets.length === 0)
104
+ edges.push({ from, raw: text, ...labelled });
105
+ continue;
106
+ }
107
+ // Names no step in this stage. Kept so the reader sees the dead end.
108
+ edges.push({ from, raw: text, ...labelled });
109
+ }
110
+ };
111
+ for (const rawStep of steps) {
112
+ const step = { ...rawStep, key: String(rawStep.key) };
113
+ const type = String(step.stepType ?? '').toUpperCase();
114
+ const data = step.data && typeof step.data === 'object' && !Array.isArray(step.data)
115
+ ? step.data
116
+ : undefined;
117
+ if (type === 'IF' && data) {
118
+ // `IfStepExecutor` passes `StepData` to the builder, so an IF routes from its `data`.
119
+ if (data.then !== null && data.then !== undefined) {
120
+ walk(step.key, data.then, 'YES');
121
+ }
122
+ if (data.else !== null && data.else !== undefined) {
123
+ walk(step.key, data.else, 'NO');
124
+ }
125
+ // An IF declaring neither branch falls back to `next` — otherwise a partly-written IF loses its only edge.
126
+ if ((data.then === null || data.then === undefined) &&
127
+ (data.else === null || data.else === undefined)) {
128
+ walk(step.key, step.next);
129
+ }
130
+ }
131
+ else if (type === 'SWITCH' && data) {
132
+ // A SWITCH's branches are its `data` KEYS, each labelled with itself.
133
+ for (const branch of Object.keys(data))
134
+ walk(step.key, branch, branch);
135
+ }
136
+ else {
137
+ walk(step.key, step.next);
138
+ }
139
+ if (step.onError !== null && step.onError !== undefined) {
140
+ walk(step.key, step.onError, 'onError');
141
+ }
142
+ /**
143
+ * The exception BACK-REFERENCE — an edge INTO this step, from the one that threw.
144
+ *
145
+ * Reversed relative to every other kind, which is why a renderer that owned routing kept forgetting it. Only
146
+ * drawn when `fromStep` names a step in this stage: an exception handler pointing outside its own stage is a
147
+ * document error, and inventing a node for it would draw a step the stage does not contain.
148
+ */
149
+ if (step.fromStep && known.has(step.fromStep)) {
150
+ edges.push({ from: step.fromStep, to: step.key, label: 'Exception' });
151
+ }
152
+ }
153
+ return edges;
154
+ }
155
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * A worker component's stages, and the arrows between the ones it actually has — Story 4.1 AC-1.
3
+ *
4
+ * ### Three shapes, one vocabulary
5
+ *
6
+ * A pusher, a **legacy** puller (`beforePullSteps`/`pullSteps`/`afterPullSteps`) and a **steps-based** puller
7
+ * (`steps`/`onErrorSteps`/`finalSteps`). Two vocabularies existed before this and they disagreed three ways; each
8
+ * disagreement is settled below by evidence rather than preference.
9
+ *
10
+ * **1. PRESENCE decides the shape, not a flag.** The CLI's `provenance.ts` picks between two stage lists using a
11
+ * `newKind` boolean. AC-1 says stage edges come from *"the stages actually present"*, and a boolean is a second
12
+ * source of truth for something the document already states by having the key. The extension's structure — one list
13
+ * per collection, presence deciding — is adopted. It also lets both puller kinds be listed together, which only
14
+ * works because the new kind's main stage is named `steps` outright and so cannot collide with the legacy three.
15
+ *
16
+ * **2. ⛔ A pusher DOES have `finalSteps` — this decision was WRONG and is reversed.** The original reasoning is kept
17
+ * below because the way it failed is instructive:
18
+ *
19
+ * > *"`assets/pushers/pusher.json` declares exactly `beforePushSteps`, `pushSteps`, `afterPushSteps`. The extension's
20
+ * > `STAGE_ORDER` lists a fourth, so it carries a stage that can never populate — a third vocabulary disagreeing with
21
+ * > the one authority. Dropped. Measured, so the drop costs nothing: 0 authored corpus pushers declare `finalSteps`."*
22
+ *
23
+ * The schema was treated as the authority, and the schema was itself wrong. **`PushStepAccessor` reads
24
+ * `$.finalSteps[*]` → `StepType.Final`** — the RUNTIME runs the stage. So the "third vocabulary" was the only one
25
+ * telling the truth, and the corpus count of 0 measured what authors could express through tooling that denied the
26
+ * stage, not what the platform supports. `WORKER_STAGES.pushers` declares it (Story 4.5 review, H2), and by Story 4.7
27
+ * the same omission had been found in **five** independent places, dashboard's `pusherPhases()` last.
28
+ *
29
+ * The transferable rule: **when a schema and the runtime disagree, the runtime is the authority.** A schema is a claim
30
+ * about the runtime and can be stale; `PushStepAccessor` is the thing that actually runs.
31
+ *
32
+ * **3. `label` and `phase` are carried.** A renderer needs a human label, and `phase` is what tells an error stage
33
+ * from a sequence stage without string-matching the key. The CLI has neither and its report is poorer for it.
34
+ *
35
+ * ### ⚠️ The steps-based puller is exercised by NOTHING real
36
+ *
37
+ * Measured over the authored corpus (`output.yaml` excluded), counting array-valued stage keys on every `pullers[]`
38
+ * and `pushers[]` entry:
39
+ *
40
+ * | stage | authored occurrences |
41
+ * | --- | --- |
42
+ * | `pullSteps` / `beforePullSteps` / `afterPullSteps` | 970 / 715 / 509 |
43
+ * | `pushSteps` / `beforePushSteps` / `afterPushSteps` | 925 / 756 / 767 |
44
+ * | `steps` / `onErrorSteps` / `finalSteps` | **0 / 0 / 0** |
45
+ *
46
+ * Story 4.1 says `onErrorSteps` and `finalSteps` appear in zero corpus files. That is true and it understates the
47
+ * position: **the whole steps-based kind has no authored instance anywhere**, so the shape is supported by the
48
+ * tooling and exercised by nothing. Fixture `03` is the only exercise it gets, which makes it the fixture most
49
+ * likely to drift and the one to treat as load-bearing rather than as an afterthought.
50
+ */
51
+ /** What a stage is FOR, so a renderer never has to string-match a key to find out. */
52
+ export type StagePhase = 'sequence' | 'error' | 'final';
53
+ /** One stage a component may declare. */
54
+ export interface StageDefinition {
55
+ /** The document key: `pullSteps`, `onErrorSteps`. */
56
+ readonly key: string;
57
+ /** For a human: `Before pull`. */
58
+ readonly label: string;
59
+ readonly phase: StagePhase;
60
+ }
61
+ /** The collections this package models. A frontend workflow is Story 4.2's, deliberately separate (AD-21). */
62
+ export type WorkerCollection = 'pullers' | 'pushers';
63
+ /**
64
+ * Every stage each collection may declare, in run order.
65
+ *
66
+ * ⛔ These keys mirror `assets/pullers/puller.json` and `assets/pushers/pusher.json`, which are the authority.
67
+ *
68
+ * ⚠️ **Mirrored, not DERIVED, and Task 2 asked for derived** (Story 4.1 review, M5). Deriving would mean reading a
69
+ * schema file, which AD-3 forbids this package from doing and NFR-4 forbids in the extension's host — so the schemas
70
+ * are the authority for a *test*, not for the runtime value. `stageSchemaParity.spec.ts` asserts this table against
71
+ * them in both directions, which is arguably stronger than derivation (it catches a stage the schema drops as well as
72
+ * one the table invents); it is nonetheless a deviation from the task as written, and it is skippable when the
73
+ * sibling repo is absent — which is why that spec also hardcodes the pusher expectation so something always runs.
74
+ */
75
+ export declare const WORKER_STAGES: Readonly<Record<WorkerCollection, readonly StageDefinition[]>>;
76
+ /**
77
+ * Which kind of puller a component is — reported, never used to CHOOSE the stage list.
78
+ *
79
+ * Worth having because a consumer may want to say so, and worth computing from presence: `legacy` and `steps` are
80
+ * observations about the document, and `unknown` is the honest answer for a puller declaring no stage at all rather
81
+ * than a guess at which kind it meant to be.
82
+ */
83
+ export type PullerKind = 'legacy' | 'steps' | 'unknown';
84
+ export declare function pullerKindOf(component: Record<string, unknown>): PullerKind;
85
+ /** The stages a component actually declares, in run order. */
86
+ export declare function stagesPresent(collection: WorkerCollection, component: Record<string, unknown>): readonly StageDefinition[];
87
+ /** One arrow between two stages. */
88
+ export interface StageEdge {
89
+ readonly from: string;
90
+ readonly to: string;
91
+ /** Absent for a plain sequence arrow; a renderer draws the label only where there is one. */
92
+ readonly label?: string;
93
+ }
94
+ /**
95
+ * The arrows between the stages a component HAS.
96
+ *
97
+ * Three rules, and the second and third are why this is not a `zip` of the ordered list:
98
+ *
99
+ * 1. **Sequence stages chain in order.** Computed from what is present, so a puller declaring only `pullSteps` has
100
+ * **no stage edges at all** — which fixture 02's baseline records verbatim as *"stage edges: none"*. An arrow
101
+ * into a stage that is not drawn points at nothing.
102
+ * 2. **An `error` stage is reachable from EVERY sequence stage**, labelled, because that is what an error stage
103
+ * means: any of them can fail into it. Drawing it as one more link in the chain would say the opposite — that it
104
+ * runs after the last sequence stage, on success.
105
+ * 3. **A `final` stage runs always**, from the last sequence stage and from the error stage, labelled `always`. It
106
+ * is the one stage a component reaches whichever way the run ends.
107
+ *
108
+ * The labels are the extension's, which is the only implementation that has stage *semantics* rather than only
109
+ * geometry.
110
+ */
111
+ export declare function stageEdges(collection: WorkerCollection, component: Record<string, unknown>): readonly StageEdge[];
112
+ //# sourceMappingURL=stages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stages.d.ts","sourceRoot":"","sources":["../src/stages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,sFAAsF;AACtF,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,8GAA8G;AAC9G,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAClC,MAAM,CAAC,gBAAgB,EAAE,SAAS,eAAe,EAAE,CAAC,CAmCrD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AA8BxD,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAa3E;AAED,8DAA8D;AAC9D,wBAAgB,aAAa,CAC3B,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,SAAS,eAAe,EAAE,CAI5B;AAED,oCAAoC;AACpC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,SAAS,SAAS,EAAE,CAqBtB"}