@wizzard-packages/core 0.4.1 → 0.5.0-canary-20260908083257

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.
Files changed (51) hide show
  1. package/dist/{flow-Bmv_KegP.d.cts → expr-C9w_odUt.d.cts} +7 -81
  2. package/dist/{flow-Bmv_KegP.d.ts → expr-C9w_odUt.d.ts} +7 -81
  3. package/dist/flow-B6E6c5Gc.d.ts +90 -0
  4. package/dist/flow-kC0pucDF.d.cts +90 -0
  5. package/dist/navigate-DJkRL8XS.d.ts +199 -0
  6. package/dist/navigate-DUUWXjAr.d.cts +199 -0
  7. package/dist/state-BFDnj4LP.d.cts +39 -0
  8. package/dist/state-BFDnj4LP.d.ts +39 -0
  9. package/dist/v1/expr-builder.cjs +32 -0
  10. package/dist/v1/expr-builder.cjs.map +1 -0
  11. package/dist/v1/expr-builder.d.cts +63 -0
  12. package/dist/v1/expr-builder.d.ts +63 -0
  13. package/dist/v1/expr-builder.js +18 -0
  14. package/dist/v1/expr-builder.js.map +1 -0
  15. package/dist/v1/graph.cjs +101 -0
  16. package/dist/v1/graph.cjs.map +1 -0
  17. package/dist/v1/graph.d.cts +71 -0
  18. package/dist/v1/graph.d.ts +71 -0
  19. package/dist/v1/graph.js +99 -0
  20. package/dist/v1/graph.js.map +1 -0
  21. package/dist/v1/groups.cjs +395 -0
  22. package/dist/v1/groups.cjs.map +1 -0
  23. package/dist/v1/groups.d.cts +90 -0
  24. package/dist/v1/groups.d.ts +90 -0
  25. package/dist/v1/groups.js +389 -0
  26. package/dist/v1/groups.js.map +1 -0
  27. package/dist/v1/index.cjs +265 -79
  28. package/dist/v1/index.cjs.map +1 -1
  29. package/dist/v1/index.d.cts +102 -131
  30. package/dist/v1/index.d.ts +102 -131
  31. package/dist/v1/index.js +264 -80
  32. package/dist/v1/index.js.map +1 -1
  33. package/dist/v1/session.cjs +139 -0
  34. package/dist/v1/session.cjs.map +1 -0
  35. package/dist/v1/session.d.cts +78 -0
  36. package/dist/v1/session.d.ts +78 -0
  37. package/dist/v1/session.js +134 -0
  38. package/dist/v1/session.js.map +1 -0
  39. package/dist/v1/snapshot.cjs +191 -0
  40. package/dist/v1/snapshot.cjs.map +1 -0
  41. package/dist/v1/snapshot.d.cts +106 -0
  42. package/dist/v1/snapshot.d.ts +106 -0
  43. package/dist/v1/snapshot.js +188 -0
  44. package/dist/v1/snapshot.js.map +1 -0
  45. package/dist/v1/validate-flow.cjs +37 -0
  46. package/dist/v1/validate-flow.cjs.map +1 -1
  47. package/dist/v1/validate-flow.d.cts +2 -1
  48. package/dist/v1/validate-flow.d.ts +2 -1
  49. package/dist/v1/validate-flow.js +37 -0
  50. package/dist/v1/validate-flow.js.map +1 -1
  51. package/package.json +51 -1
@@ -45,10 +45,15 @@ type Expr = null | boolean | number | string | readonly Expr[] | {
45
45
  interface Scope {
46
46
  data: Record<string, unknown>;
47
47
  ctx: Record<string, unknown>;
48
- /** Present inside a `repeat` group. */
48
+ /**
49
+ * Present inside a `repeat` group. `key` is the item's identity under
50
+ * `keyBy` - the one part a flow can use to address its own item's data,
51
+ * because `index` moves when the list is reordered.
52
+ */
49
53
  loop?: {
50
54
  index: number;
51
55
  item: unknown;
56
+ key: string;
52
57
  };
53
58
  }
54
59
  type Resolver = (args: Json | undefined, scope: Scope) => unknown;
@@ -84,83 +89,4 @@ type AsyncRegistry = Readonly<Record<string, AsyncResolver>>;
84
89
  /** Evaluates to a boolean, awaiting `$ref`. An absent expression is `true`. */
85
90
  declare function testAsync(e: Expr | undefined, scope: Scope, registry?: AsyncRegistry): Promise<boolean>;
86
91
 
87
- /**
88
- * The flow definition. Pure JSON — no functions, no class instances, nothing
89
- * that `JSON.stringify` would drop. The same object can be written by hand,
90
- * served by a backend, or generated; the engine cannot tell which.
91
- *
92
- * Everything that cannot be serialized is named instead: `view` names a
93
- * component, `$ref` names a function, both looked up in a registry the host
94
- * supplies.
95
- */
96
- interface FlowDefinition {
97
- id: string;
98
- /** Bumped by the producer when the shape changes; used to reject stale patches. */
99
- version?: number;
100
- /** Default linear order. A step missing from `order` is reachable only via `on.next`. */
101
- order?: readonly string[];
102
- /** Keyed by id, not an array, so a patch can address one step without index maths. */
103
- steps: Readonly<Record<string, StepDef>>;
104
- policy?: NavigationPolicy;
105
- validate?: {
106
- on?: 'change' | 'blur' | 'next' | 'manual';
107
- debounceMs?: number;
108
- };
109
- }
110
- type NavigationPolicy = 'sequential' | 'visited' | 'free';
111
- type Target = string | {
112
- to: string;
113
- when?: Expr;
114
- };
115
- interface StepBase {
116
- label?: string;
117
- /** Reachability. A step whose `when` is false is skipped, not shown disabled. */
118
- when?: Expr;
119
- guards?: {
120
- enter?: Expr;
121
- exit?: Expr;
122
- };
123
- on?: {
124
- next?: Target | readonly Target[];
125
- back?: Target | 'auto';
126
- };
127
- /** Key of this step's slice of `data`. Defaults to the step id. */
128
- slice?: string;
129
- /** Field schema for the host to render. The engine never reads it. */
130
- ui?: Json;
131
- /** Loaded on enter, with an abort signal. */
132
- load?: {
133
- $ref: string;
134
- args?: Json;
135
- };
136
- /** The body of this step arrives later, from the host. */
137
- deferred?: boolean;
138
- }
139
- interface AtomStep extends StepBase {
140
- view?: string;
141
- validate?: {
142
- $ref: string;
143
- args?: Json;
144
- };
145
- }
146
- /**
147
- * A sub-flow. With `repeat` it is also the loop construct — the same machinery,
148
- * because a loop is a sub-flow entered once per item. Iteration state lives in
149
- * the runtime frame, never in the definition, so the flow stays static and
150
- * diffable.
151
- */
152
- interface GroupStep extends StepBase {
153
- flow: string | FlowDefinition;
154
- repeat?: {
155
- over: Expr;
156
- keyBy?: string;
157
- };
158
- /** Parent expressions piped into the child's `ctx`. */
159
- input?: Readonly<Record<string, Expr>>;
160
- }
161
- type StepDef = AtomStep | GroupStep;
162
- declare const isGroup: (s: StepDef) => s is GroupStep;
163
- /** Sentinel target meaning the flow is finished. */
164
- declare const END: "@end";
165
-
166
- export { type AtomStep as A, END as E, type FlowDefinition as F, type GroupStep as G, type Json as J, type NavigationPolicy as N, type Registry as R, type StepDef as S, type Target as T, type AsyncRegistry as a, type Scope as b, type Expr as c, type Resolver as d, ExprError as e, evaluate as f, evaluateAsync as g, type AsyncResolver as h, isSync as i, testAsync as j, isGroup as k, test as t };
92
+ export { type AsyncRegistry as A, type Expr as E, type Json as J, type Registry as R, type Scope as S, type Resolver as a, ExprError as b, evaluateAsync as c, type AsyncResolver as d, evaluate as e, testAsync as f, isSync as i, test as t };
@@ -45,10 +45,15 @@ type Expr = null | boolean | number | string | readonly Expr[] | {
45
45
  interface Scope {
46
46
  data: Record<string, unknown>;
47
47
  ctx: Record<string, unknown>;
48
- /** Present inside a `repeat` group. */
48
+ /**
49
+ * Present inside a `repeat` group. `key` is the item's identity under
50
+ * `keyBy` - the one part a flow can use to address its own item's data,
51
+ * because `index` moves when the list is reordered.
52
+ */
49
53
  loop?: {
50
54
  index: number;
51
55
  item: unknown;
56
+ key: string;
52
57
  };
53
58
  }
54
59
  type Resolver = (args: Json | undefined, scope: Scope) => unknown;
@@ -84,83 +89,4 @@ type AsyncRegistry = Readonly<Record<string, AsyncResolver>>;
84
89
  /** Evaluates to a boolean, awaiting `$ref`. An absent expression is `true`. */
85
90
  declare function testAsync(e: Expr | undefined, scope: Scope, registry?: AsyncRegistry): Promise<boolean>;
86
91
 
87
- /**
88
- * The flow definition. Pure JSON — no functions, no class instances, nothing
89
- * that `JSON.stringify` would drop. The same object can be written by hand,
90
- * served by a backend, or generated; the engine cannot tell which.
91
- *
92
- * Everything that cannot be serialized is named instead: `view` names a
93
- * component, `$ref` names a function, both looked up in a registry the host
94
- * supplies.
95
- */
96
- interface FlowDefinition {
97
- id: string;
98
- /** Bumped by the producer when the shape changes; used to reject stale patches. */
99
- version?: number;
100
- /** Default linear order. A step missing from `order` is reachable only via `on.next`. */
101
- order?: readonly string[];
102
- /** Keyed by id, not an array, so a patch can address one step without index maths. */
103
- steps: Readonly<Record<string, StepDef>>;
104
- policy?: NavigationPolicy;
105
- validate?: {
106
- on?: 'change' | 'blur' | 'next' | 'manual';
107
- debounceMs?: number;
108
- };
109
- }
110
- type NavigationPolicy = 'sequential' | 'visited' | 'free';
111
- type Target = string | {
112
- to: string;
113
- when?: Expr;
114
- };
115
- interface StepBase {
116
- label?: string;
117
- /** Reachability. A step whose `when` is false is skipped, not shown disabled. */
118
- when?: Expr;
119
- guards?: {
120
- enter?: Expr;
121
- exit?: Expr;
122
- };
123
- on?: {
124
- next?: Target | readonly Target[];
125
- back?: Target | 'auto';
126
- };
127
- /** Key of this step's slice of `data`. Defaults to the step id. */
128
- slice?: string;
129
- /** Field schema for the host to render. The engine never reads it. */
130
- ui?: Json;
131
- /** Loaded on enter, with an abort signal. */
132
- load?: {
133
- $ref: string;
134
- args?: Json;
135
- };
136
- /** The body of this step arrives later, from the host. */
137
- deferred?: boolean;
138
- }
139
- interface AtomStep extends StepBase {
140
- view?: string;
141
- validate?: {
142
- $ref: string;
143
- args?: Json;
144
- };
145
- }
146
- /**
147
- * A sub-flow. With `repeat` it is also the loop construct — the same machinery,
148
- * because a loop is a sub-flow entered once per item. Iteration state lives in
149
- * the runtime frame, never in the definition, so the flow stays static and
150
- * diffable.
151
- */
152
- interface GroupStep extends StepBase {
153
- flow: string | FlowDefinition;
154
- repeat?: {
155
- over: Expr;
156
- keyBy?: string;
157
- };
158
- /** Parent expressions piped into the child's `ctx`. */
159
- input?: Readonly<Record<string, Expr>>;
160
- }
161
- type StepDef = AtomStep | GroupStep;
162
- declare const isGroup: (s: StepDef) => s is GroupStep;
163
- /** Sentinel target meaning the flow is finished. */
164
- declare const END: "@end";
165
-
166
- export { type AtomStep as A, END as E, type FlowDefinition as F, type GroupStep as G, type Json as J, type NavigationPolicy as N, type Registry as R, type StepDef as S, type Target as T, type AsyncRegistry as a, type Scope as b, type Expr as c, type Resolver as d, ExprError as e, evaluate as f, evaluateAsync as g, type AsyncResolver as h, isSync as i, testAsync as j, isGroup as k, test as t };
92
+ export { type AsyncRegistry as A, type Expr as E, type Json as J, type Registry as R, type Scope as S, type Resolver as a, ExprError as b, evaluateAsync as c, type AsyncResolver as d, evaluate as e, testAsync as f, isSync as i, test as t };
@@ -0,0 +1,90 @@
1
+ import { E as Expr, J as Json } from './expr-C9w_odUt.js';
2
+
3
+ /**
4
+ * The flow definition. Pure JSON — no functions, no class instances, nothing
5
+ * that `JSON.stringify` would drop. The same object can be written by hand,
6
+ * served by a backend, or generated; the engine cannot tell which.
7
+ *
8
+ * Everything that cannot be serialized is named instead: `view` names a
9
+ * component, `$ref` names a function, both looked up in a registry the host
10
+ * supplies.
11
+ */
12
+ interface FlowDefinition {
13
+ id: string;
14
+ /** Bumped by the producer when the shape changes; used to reject stale patches. */
15
+ version?: number;
16
+ /** Default linear order. A step missing from `order` is reachable only via `on.next`. */
17
+ order?: readonly string[];
18
+ /** Keyed by id, not an array, so a patch can address one step without index maths. */
19
+ steps: Readonly<Record<string, StepDef>>;
20
+ policy?: NavigationPolicy;
21
+ validate?: {
22
+ on?: 'change' | 'blur' | 'next' | 'manual';
23
+ debounceMs?: number;
24
+ };
25
+ }
26
+ type NavigationPolicy = 'sequential' | 'visited' | 'free';
27
+ type Target = string | {
28
+ to: string;
29
+ when?: Expr;
30
+ };
31
+ interface StepBase {
32
+ label?: string;
33
+ /** Reachability. A step whose `when` is false is skipped, not shown disabled. */
34
+ when?: Expr;
35
+ guards?: {
36
+ enter?: Expr;
37
+ exit?: Expr;
38
+ };
39
+ on?: {
40
+ next?: Target | readonly Target[];
41
+ back?: Target | 'auto';
42
+ };
43
+ /** Key of this step's slice of `data`. Defaults to the step id. */
44
+ slice?: string;
45
+ /** Field schema for the host to render. The engine never reads it. */
46
+ ui?: Json;
47
+ /** Loaded on enter, with an abort signal. */
48
+ load?: {
49
+ $ref: string;
50
+ args?: Json;
51
+ };
52
+ /** The body of this step arrives later, from the host. */
53
+ deferred?: boolean;
54
+ /**
55
+ * What to forget when this step is left, forwards or backwards. Data is
56
+ * kept by default: a person who goes back and returns expects their answers
57
+ * to still be there. `true` drops the step's whole slice (`slice`, or the
58
+ * step id); a list drops those `data` paths, in the same path language as
59
+ * `set()`. Nothing is cleared on completion — that data is the submission.
60
+ */
61
+ clearOnLeave?: readonly string[] | true;
62
+ }
63
+ interface AtomStep extends StepBase {
64
+ view?: string;
65
+ validate?: {
66
+ $ref: string;
67
+ args?: Json;
68
+ };
69
+ }
70
+ /**
71
+ * A sub-flow. With `repeat` it is also the loop construct — the same machinery,
72
+ * because a loop is a sub-flow entered once per item. Iteration state lives in
73
+ * the runtime frame, never in the definition, so the flow stays static and
74
+ * diffable.
75
+ */
76
+ interface GroupStep extends StepBase {
77
+ flow: string | FlowDefinition;
78
+ repeat?: {
79
+ over: Expr;
80
+ keyBy?: string;
81
+ };
82
+ /** Parent expressions piped into the child's `ctx`. */
83
+ input?: Readonly<Record<string, Expr>>;
84
+ }
85
+ type StepDef = AtomStep | GroupStep;
86
+ declare const isGroup: (s: StepDef) => s is GroupStep;
87
+ /** Sentinel target meaning the flow is finished. */
88
+ declare const END: "@end";
89
+
90
+ export { type AtomStep as A, END as E, type FlowDefinition as F, type GroupStep as G, type NavigationPolicy as N, type StepDef as S, type Target as T, isGroup as i };
@@ -0,0 +1,90 @@
1
+ import { E as Expr, J as Json } from './expr-C9w_odUt.cjs';
2
+
3
+ /**
4
+ * The flow definition. Pure JSON — no functions, no class instances, nothing
5
+ * that `JSON.stringify` would drop. The same object can be written by hand,
6
+ * served by a backend, or generated; the engine cannot tell which.
7
+ *
8
+ * Everything that cannot be serialized is named instead: `view` names a
9
+ * component, `$ref` names a function, both looked up in a registry the host
10
+ * supplies.
11
+ */
12
+ interface FlowDefinition {
13
+ id: string;
14
+ /** Bumped by the producer when the shape changes; used to reject stale patches. */
15
+ version?: number;
16
+ /** Default linear order. A step missing from `order` is reachable only via `on.next`. */
17
+ order?: readonly string[];
18
+ /** Keyed by id, not an array, so a patch can address one step without index maths. */
19
+ steps: Readonly<Record<string, StepDef>>;
20
+ policy?: NavigationPolicy;
21
+ validate?: {
22
+ on?: 'change' | 'blur' | 'next' | 'manual';
23
+ debounceMs?: number;
24
+ };
25
+ }
26
+ type NavigationPolicy = 'sequential' | 'visited' | 'free';
27
+ type Target = string | {
28
+ to: string;
29
+ when?: Expr;
30
+ };
31
+ interface StepBase {
32
+ label?: string;
33
+ /** Reachability. A step whose `when` is false is skipped, not shown disabled. */
34
+ when?: Expr;
35
+ guards?: {
36
+ enter?: Expr;
37
+ exit?: Expr;
38
+ };
39
+ on?: {
40
+ next?: Target | readonly Target[];
41
+ back?: Target | 'auto';
42
+ };
43
+ /** Key of this step's slice of `data`. Defaults to the step id. */
44
+ slice?: string;
45
+ /** Field schema for the host to render. The engine never reads it. */
46
+ ui?: Json;
47
+ /** Loaded on enter, with an abort signal. */
48
+ load?: {
49
+ $ref: string;
50
+ args?: Json;
51
+ };
52
+ /** The body of this step arrives later, from the host. */
53
+ deferred?: boolean;
54
+ /**
55
+ * What to forget when this step is left, forwards or backwards. Data is
56
+ * kept by default: a person who goes back and returns expects their answers
57
+ * to still be there. `true` drops the step's whole slice (`slice`, or the
58
+ * step id); a list drops those `data` paths, in the same path language as
59
+ * `set()`. Nothing is cleared on completion — that data is the submission.
60
+ */
61
+ clearOnLeave?: readonly string[] | true;
62
+ }
63
+ interface AtomStep extends StepBase {
64
+ view?: string;
65
+ validate?: {
66
+ $ref: string;
67
+ args?: Json;
68
+ };
69
+ }
70
+ /**
71
+ * A sub-flow. With `repeat` it is also the loop construct — the same machinery,
72
+ * because a loop is a sub-flow entered once per item. Iteration state lives in
73
+ * the runtime frame, never in the definition, so the flow stays static and
74
+ * diffable.
75
+ */
76
+ interface GroupStep extends StepBase {
77
+ flow: string | FlowDefinition;
78
+ repeat?: {
79
+ over: Expr;
80
+ keyBy?: string;
81
+ };
82
+ /** Parent expressions piped into the child's `ctx`. */
83
+ input?: Readonly<Record<string, Expr>>;
84
+ }
85
+ type StepDef = AtomStep | GroupStep;
86
+ declare const isGroup: (s: StepDef) => s is GroupStep;
87
+ /** Sentinel target meaning the flow is finished. */
88
+ declare const END: "@end";
89
+
90
+ export { type AtomStep as A, END as E, type FlowDefinition as F, type GroupStep as G, type NavigationPolicy as N, type StepDef as S, type Target as T, isGroup as i };
@@ -0,0 +1,199 @@
1
+ import { R as Registry, S as Scope, A as AsyncRegistry } from './expr-C9w_odUt.js';
2
+ import { F as FlowDefinition, E as END, S as StepDef } from './flow-B6E6c5Gc.js';
3
+ import { W as WizardState, F as Frame } from './state-BFDnj4LP.js';
4
+
5
+ /**
6
+ * The navigation pipeline.
7
+ *
8
+ * Every way of moving — next, back, jump — runs through this one function, so
9
+ * there is one place where validation, guards, loading and policy are ordered,
10
+ * and one place that can get the ordering wrong. The 0.x engine implemented
11
+ * this three times, in the React context, the React store and the Vue binding,
12
+ * and the three copies disagreed about the default for validation and about
13
+ * whether middleware ran at all.
14
+ *
15
+ * Two writes happen, and only two. The first is the epoch bump in phase 0,
16
+ * which is the lock: a concurrent call has to see it to know it superseded us.
17
+ * The second is the single commit in phase 9. Nothing is written in between, so
18
+ * a navigation that loses the race, throws, or is aborted leaves no trace.
19
+ *
20
+ * Sub-flows and repeat groups push frames onto the stack, and this pass walks a
21
+ * single level on its own. The traversal that walks the rest lives behind
22
+ * `@wizzard-packages/core/groups` and is installed as `ctx.groups`: phase 0.5
23
+ * asks it which flow owns the current frame and what the scope there is, phase
24
+ * 4 asks it for the whole move, phase 8 asks it again when the data moved under
25
+ * an await, and phase 9 commits the stack it returned. With nothing installed
26
+ * every one of those falls back to the flat expression it replaced.
27
+ */
28
+ type NavReason = 'invalid' | 'blocked' | 'no-target' | 'superseded' | 'aborted' | 'not-reachable';
29
+ type NavResult = {
30
+ ok: true;
31
+ from: string | null;
32
+ to: string | typeof END;
33
+ } | {
34
+ ok: false;
35
+ reason: NavReason;
36
+ /** Which plugin or guard refused. Replaces the goToStepResult probe of 0.x. */
37
+ by?: string;
38
+ errors?: Readonly<Record<string, string>>;
39
+ };
40
+ type NavIntent = {
41
+ type: 'next';
42
+ } | {
43
+ type: 'back';
44
+ } | {
45
+ type: 'go';
46
+ to: string;
47
+ force?: boolean;
48
+ };
49
+ /** What a plugin may answer from beforeNavigate. */
50
+ type NavDecision = void | false | {
51
+ block: string;
52
+ } | {
53
+ redirect: string;
54
+ };
55
+ /**
56
+ * One navigation attempt as `onAttempt` reports it: once when it starts and
57
+ * once when it ends, with the result, or errors, with what was thrown.
58
+ *
59
+ * `id` counts attempts on one wizard, so a plugin can pair an end with its
60
+ * start when two overlap - a superseded attempt ends after the one that
61
+ * superseded it began. `source` is `'start'` for the engine's own first move,
62
+ * so a log can tell it from a call the application made. `rev` is the state's
63
+ * revision at the moment of the event.
64
+ */
65
+ type AttemptPhase = {
66
+ phase: 'start';
67
+ } | {
68
+ phase: 'end';
69
+ result: NavResult;
70
+ } | {
71
+ phase: 'error';
72
+ error: unknown;
73
+ };
74
+ type Attempt = {
75
+ id: number;
76
+ intent: NavIntent;
77
+ source: 'call' | 'start';
78
+ rev: number;
79
+ } & AttemptPhase;
80
+ interface Hooks {
81
+ name: string;
82
+ /**
83
+ * Runs once, when the engine is built, before anything reads it. A plugin
84
+ * that restores a saved session does it here. The returned function, if any,
85
+ * runs on `destroy`.
86
+ *
87
+ * A commit made from inside `init` does not come back as `onCommit`: the
88
+ * plugin already knows what it just wrote, and re-entering is how a restore
89
+ * turns into a loop.
90
+ */
91
+ init?: (host: PluginHost) => void | (() => void);
92
+ /**
93
+ * Every committed state, from any path - navigation, `set`, `patch`,
94
+ * `setCtx`, `reset`, `patchFlow`. A plugin that persists writes here.
95
+ *
96
+ * Not a veto and not a place to be slow: it runs inside the write, so
97
+ * throwing disables the plugin rather than failing the write.
98
+ */
99
+ onCommit?: (state: WizardState, previous: WizardState) => void;
100
+ beforeNavigate?: (e: {
101
+ from: string | null;
102
+ to: string | typeof END | null;
103
+ state: WizardState;
104
+ }) => NavDecision | Promise<NavDecision>;
105
+ afterNavigate?: (e: {
106
+ from: string | null;
107
+ to: string | typeof END;
108
+ state: WizardState;
109
+ }) => void;
110
+ /**
111
+ * Every attempt to move, whether or not it commits. A refused `next()` never
112
+ * writes, so `onCommit` cannot report it; this can, with the reason. Fired
113
+ * by the store around the whole pipeline, never from inside it, and never
114
+ * for `cancel()` - the aborted attempt ends here with its `aborted` result.
115
+ *
116
+ * Observation only, like `onCommit`: throwing disables the plugin.
117
+ */
118
+ onAttempt?: (attempt: Attempt) => void;
119
+ /** Supplies the body of a deferred step, typically over the network. */
120
+ loadStep?: (stepId: string, signal: AbortSignal) => Promise<StepDef | undefined>;
121
+ }
122
+ /** What a plugin is handed at `init`. Deliberately small: read, and write once. */
123
+ interface PluginHost {
124
+ getState: () => WizardState;
125
+ getFlow: () => FlowDefinition;
126
+ /** Replaces state through the one commit path, exactly as the engine does. */
127
+ commit: (patch: Partial<WizardState>) => void;
128
+ }
129
+ interface NavHost {
130
+ read: () => WizardState;
131
+ /** The single setter owned by the store. Notifies listeners exactly once. */
132
+ write: (next: WizardState) => void;
133
+ }
134
+ /**
135
+ * Sub-flow definitions by the key a `GroupStep.flow` string names them by. The
136
+ * same registry `checkSession` and `buildGraph` take: the expression `Registry`
137
+ * holds resolver functions, not definitions, so a group referenced by id has
138
+ * nothing else that could resolve it.
139
+ */
140
+ type SubFlows = Readonly<Record<string, FlowDefinition>>;
141
+ /**
142
+ * Group and repeat traversal, installed rather than imported.
143
+ *
144
+ * The implementation is `@wizzard-packages/core/groups`, its own entry with its
145
+ * own budget, so a flat flow carries none of it. The type lives here because a
146
+ * type costs a bundle nothing and this entry has to be able to accept one.
147
+ */
148
+ interface Traversal {
149
+ /** Phase 0.5: the flow that owns the top frame, and the scope at that frame. */
150
+ here: (root: FlowDefinition, state: WizardState, registry?: Registry, subFlows?: SubFlows) => {
151
+ flow: FlowDefinition;
152
+ scope: Scope;
153
+ };
154
+ /**
155
+ * Phase 4: the whole move, pure. `null` means no target.
156
+ *
157
+ * `flow` is the flow that owns the *new* top frame, which is not always the
158
+ * one `here` named: a move that enters or leaves a group changes which flow
159
+ * phases 5-7 have to look the target up in.
160
+ */
161
+ step: (root: FlowDefinition, state: WizardState, intent: NavIntent, registry?: Registry, subFlows?: SubFlows) => {
162
+ stack: readonly Frame[];
163
+ to: string | typeof END;
164
+ flow: FlowDefinition;
165
+ scope: Scope;
166
+ } | {
167
+ ok: false;
168
+ reason: 'invalid' | 'not-reachable';
169
+ by: string;
170
+ errors?: Readonly<Record<string, string>>;
171
+ } | null;
172
+ }
173
+ interface NavContext {
174
+ flow: FlowDefinition;
175
+ registry?: AsyncRegistry;
176
+ hooks?: readonly Hooks[];
177
+ /** Returns field errors, or null when the step is valid. */
178
+ validate?: (stepId: string, state: WizardState) => Promise<Readonly<Record<string, string>> | null>;
179
+ /**
180
+ * Runs the load of a step before entering it.
181
+ *
182
+ * Both the reference and the scope arrive from the pipeline rather than being
183
+ * looked back up. A step inside a sub-flow has no id in the root flow, and
184
+ * the scope the resolver needs is the one *after* the move: on entry to a
185
+ * repeat, `loop` does not exist in the committed state at all, and on an
186
+ * advance it still names the item being left.
187
+ */
188
+ load?: (stepId: string, load: unknown, scope: Scope, signal: AbortSignal) => Promise<void>;
189
+ /** Group and repeat traversal. Absent means flat flows only. */
190
+ groups?: Traversal;
191
+ /** Sub-flow definitions a string `GroupStep.flow` names. */
192
+ subFlows?: SubFlows;
193
+ signal?: AbortSignal;
194
+ }
195
+ declare function runNav(ctx: NavContext, host: NavHost, intent: NavIntent, opts?: {
196
+ validate?: boolean;
197
+ }): Promise<NavResult>;
198
+
199
+ export { type AttemptPhase as A, type Hooks as H, type NavIntent as N, type PluginHost as P, type SubFlows as S, type Traversal as T, type NavResult as a, type NavReason as b, type NavDecision as c, type Attempt as d, type NavHost as e, type NavContext as f, runNav as r };