@daltonr/pathwrite-core 0.1.4 → 0.2.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 CHANGED
@@ -43,6 +43,8 @@ engine.next();
43
43
  engine.previous();
44
44
  engine.cancel();
45
45
  engine.setData(key, value); // update a single data value; emits stateChanged
46
+ engine.goToStep(stepId); // jump to step by ID; bypasses guards and shouldSkip
47
+ engine.goToStepChecked(stepId); // jump to step by ID; checks canMoveNext / canMovePrevious first
46
48
  engine.snapshot(); // returns PathSnapshot | null
47
49
 
48
50
  const unsubscribe = engine.subscribe((event) => { ... });
@@ -60,6 +62,7 @@ All hooks are optional. Hooks that want to update data **return a partial patch*
60
62
  | `onSubPathComplete` | On the parent step when a sub-path finishes | ✅ |
61
63
  | `canMoveNext` | Before advancing — return `false` to block | — |
62
64
  | `canMovePrevious` | Before going back — return `false` to block | — |
65
+ | `validationMessages` | On every snapshot — return `string[]` explaining why the step is not yet valid | — |
63
66
 
64
67
  ### Snapshot guard booleans
65
68
 
package/dist/index.d.ts CHANGED
@@ -11,6 +11,13 @@ export interface PathStep<TData extends PathData = PathData> {
11
11
  shouldSkip?: (ctx: PathStepContext<TData>) => boolean | Promise<boolean>;
12
12
  canMoveNext?: (ctx: PathStepContext<TData>) => boolean | Promise<boolean>;
13
13
  canMovePrevious?: (ctx: PathStepContext<TData>) => boolean | Promise<boolean>;
14
+ /**
15
+ * Returns a list of human-readable messages explaining why the step is not
16
+ * yet valid. The shell displays these messages below the step content so
17
+ * consumers do not need to duplicate guard logic in the template.
18
+ * Evaluated synchronously on every snapshot; async functions default to `[]`.
19
+ */
20
+ validationMessages?: (ctx: PathStepContext<TData>) => string[] | Promise<string[]>;
14
21
  onEnter?: (ctx: PathStepContext<TData>) => Partial<TData> | void | Promise<Partial<TData> | void>;
15
22
  onLeave?: (ctx: PathStepContext<TData>) => Partial<TData> | void | Promise<Partial<TData> | void>;
16
23
  onSubPathComplete?: (subPathId: string, subPathData: PathData, ctx: PathStepContext<TData>) => Partial<TData> | void | Promise<Partial<TData> | void>;
@@ -45,6 +52,8 @@ export interface PathSnapshot<TData extends PathData = PathData> {
45
52
  canMoveNext: boolean;
46
53
  /** Whether the current step's `canMovePrevious` guard allows going back. Async guards default to `true`. */
47
54
  canMovePrevious: boolean;
55
+ /** Messages from the current step's `validationMessages` hook. Empty array when there are none. */
56
+ validationMessages: string[];
48
57
  data: TData;
49
58
  }
50
59
  export type PathEvent = {
@@ -80,11 +89,23 @@ export declare class PathEngine {
80
89
  setData(key: string, value: unknown): Promise<void>;
81
90
  /** Jumps directly to the step with the given ID. Does not check guards or shouldSkip. */
82
91
  goToStep(stepId: string): Promise<void>;
92
+ /**
93
+ * Jumps directly to the step with the given ID, but first checks the
94
+ * direction-appropriate guard on the current step:
95
+ * - Going forward → checks `canMoveNext`
96
+ * - Going backward → checks `canMovePrevious`
97
+ *
98
+ * If the guard blocks, navigation does not occur and `stateChanged` is still
99
+ * emitted (so the UI can react). `shouldSkip` is not evaluated.
100
+ * Throws if the target step ID does not exist.
101
+ */
102
+ goToStepChecked(stepId: string): Promise<void>;
83
103
  snapshot(): PathSnapshot | null;
84
104
  private _startAsync;
85
105
  private _nextAsync;
86
106
  private _previousAsync;
87
107
  private _goToStepAsync;
108
+ private _goToStepCheckedAsync;
88
109
  private finishActivePath;
89
110
  private requireActivePath;
90
111
  private assertPathHasSteps;
@@ -103,4 +124,10 @@ export declare class PathEngine {
103
124
  * If the guard returns a `Promise`, returns `true` (optimistic default).
104
125
  */
105
126
  private evaluateGuardSync;
127
+ /**
128
+ * Evaluates a validationMessages function synchronously for inclusion in the snapshot.
129
+ * If the hook is absent, returns `[]`.
130
+ * If the hook returns a `Promise`, returns `[]` (async hooks are not supported in snapshots).
131
+ */
132
+ private evaluateValidationMessagesSync;
106
133
  }
package/dist/index.js CHANGED
@@ -58,6 +58,26 @@ export class PathEngine {
58
58
  }
59
59
  return this._goToStepAsync(active, targetIndex);
60
60
  }
61
+ /**
62
+ * Jumps directly to the step with the given ID, but first checks the
63
+ * direction-appropriate guard on the current step:
64
+ * - Going forward → checks `canMoveNext`
65
+ * - Going backward → checks `canMovePrevious`
66
+ *
67
+ * If the guard blocks, navigation does not occur and `stateChanged` is still
68
+ * emitted (so the UI can react). `shouldSkip` is not evaluated.
69
+ * Throws if the target step ID does not exist.
70
+ */
71
+ goToStepChecked(stepId) {
72
+ const active = this.requireActivePath();
73
+ const targetIndex = active.definition.steps.findIndex((s) => s.id === stepId);
74
+ if (targetIndex === -1) {
75
+ return Promise.reject(new Error(`Step "${stepId}" not found in path "${active.definition.id}".`));
76
+ }
77
+ if (targetIndex === active.currentStepIndex)
78
+ return Promise.resolve();
79
+ return this._goToStepCheckedAsync(active, targetIndex);
80
+ }
61
81
  snapshot() {
62
82
  if (this.activePath === null) {
63
83
  return null;
@@ -89,6 +109,7 @@ export class PathEngine {
89
109
  isNavigating: this._isNavigating,
90
110
  canMoveNext: this.evaluateGuardSync(step.canMoveNext, active),
91
111
  canMovePrevious: this.evaluateGuardSync(step.canMovePrevious, active),
112
+ validationMessages: this.evaluateValidationMessagesSync(step.validationMessages, active),
92
113
  data: { ...active.data }
93
114
  };
94
115
  }
@@ -155,6 +176,11 @@ export class PathEngine {
155
176
  async _previousAsync(active) {
156
177
  if (this._isNavigating)
157
178
  return;
179
+ // No-op when already on the first step of a top-level path.
180
+ // Sub-paths still cancel/pop back to the parent when previous() is called
181
+ // on their first step (the currentStepIndex < 0 branch below handles that).
182
+ if (active.currentStepIndex === 0 && this.pathStack.length === 0)
183
+ return;
158
184
  this._isNavigating = true;
159
185
  this.emitStateChanged();
160
186
  try {
@@ -198,6 +224,31 @@ export class PathEngine {
198
224
  throw err;
199
225
  }
200
226
  }
227
+ async _goToStepCheckedAsync(active, targetIndex) {
228
+ if (this._isNavigating)
229
+ return;
230
+ this._isNavigating = true;
231
+ this.emitStateChanged();
232
+ try {
233
+ const currentStep = this.getCurrentStep(active);
234
+ const goingForward = targetIndex > active.currentStepIndex;
235
+ const allowed = goingForward
236
+ ? await this.canMoveNext(active, currentStep)
237
+ : await this.canMovePrevious(active, currentStep);
238
+ if (allowed) {
239
+ this.applyPatch(await this.leaveCurrentStep(active, currentStep));
240
+ active.currentStepIndex = targetIndex;
241
+ this.applyPatch(await this.enterCurrentStep());
242
+ }
243
+ this._isNavigating = false;
244
+ this.emitStateChanged();
245
+ }
246
+ catch (err) {
247
+ this._isNavigating = false;
248
+ this.emitStateChanged();
249
+ throw err;
250
+ }
251
+ }
201
252
  async finishActivePath() {
202
253
  const finished = this.requireActivePath();
203
254
  const finishedPathId = finished.definition.id;
@@ -342,5 +393,24 @@ export class PathEngine {
342
393
  // Async guard — default to true (optimistic); the engine will enforce the real result on navigation.
343
394
  return true;
344
395
  }
396
+ /**
397
+ * Evaluates a validationMessages function synchronously for inclusion in the snapshot.
398
+ * If the hook is absent, returns `[]`.
399
+ * If the hook returns a `Promise`, returns `[]` (async hooks are not supported in snapshots).
400
+ */
401
+ evaluateValidationMessagesSync(fn, active) {
402
+ if (!fn)
403
+ return [];
404
+ const ctx = {
405
+ pathId: active.definition.id,
406
+ stepId: this.getCurrentStep(active).id,
407
+ data: { ...active.data }
408
+ };
409
+ const result = fn(ctx);
410
+ if (Array.isArray(result))
411
+ return result;
412
+ // Async hook — default to empty; consumers should keep validationMessages synchronous.
413
+ return [];
414
+ }
345
415
  }
346
416
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daltonr/pathwrite-core",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Headless path engine — deterministic state machine with stack-based sub-path orchestration. Zero dependencies.",
@@ -29,6 +29,7 @@
29
29
  "types": "dist/index.d.ts",
30
30
  "files": [
31
31
  "dist",
32
+ "src",
32
33
  "README.md",
33
34
  "LICENSE"
34
35
  ],
package/src/index.ts ADDED
@@ -0,0 +1,565 @@
1
+ export type PathData = Record<string, unknown>;
2
+
3
+ export interface PathStepContext<TData extends PathData = PathData> {
4
+ readonly pathId: string;
5
+ readonly stepId: string;
6
+ readonly data: Readonly<TData>;
7
+ }
8
+
9
+ export interface PathStep<TData extends PathData = PathData> {
10
+ id: string;
11
+ title?: string;
12
+ meta?: Record<string, unknown>;
13
+ shouldSkip?: (ctx: PathStepContext<TData>) => boolean | Promise<boolean>;
14
+ canMoveNext?: (ctx: PathStepContext<TData>) => boolean | Promise<boolean>;
15
+ canMovePrevious?: (ctx: PathStepContext<TData>) => boolean | Promise<boolean>;
16
+ /**
17
+ * Returns a list of human-readable messages explaining why the step is not
18
+ * yet valid. The shell displays these messages below the step content so
19
+ * consumers do not need to duplicate guard logic in the template.
20
+ * Evaluated synchronously on every snapshot; async functions default to `[]`.
21
+ */
22
+ validationMessages?: (ctx: PathStepContext<TData>) => string[] | Promise<string[]>;
23
+ onEnter?: (ctx: PathStepContext<TData>) => Partial<TData> | void | Promise<Partial<TData> | void>;
24
+ onLeave?: (ctx: PathStepContext<TData>) => Partial<TData> | void | Promise<Partial<TData> | void>;
25
+ onSubPathComplete?: (
26
+ subPathId: string,
27
+ subPathData: PathData,
28
+ ctx: PathStepContext<TData>
29
+ ) => Partial<TData> | void | Promise<Partial<TData> | void>;
30
+ }
31
+
32
+ export interface PathDefinition<TData extends PathData = PathData> {
33
+ id: string;
34
+ title?: string;
35
+ steps: PathStep<TData>[];
36
+ }
37
+
38
+ export type StepStatus = "completed" | "current" | "upcoming";
39
+
40
+ export interface StepSummary {
41
+ id: string;
42
+ title?: string;
43
+ meta?: Record<string, unknown>;
44
+ status: StepStatus;
45
+ }
46
+
47
+ export interface PathSnapshot<TData extends PathData = PathData> {
48
+ pathId: string;
49
+ stepId: string;
50
+ stepTitle?: string;
51
+ stepMeta?: Record<string, unknown>;
52
+ stepIndex: number;
53
+ stepCount: number;
54
+ progress: number;
55
+ steps: StepSummary[];
56
+ isFirstStep: boolean;
57
+ isLastStep: boolean;
58
+ nestingLevel: number;
59
+ /** True while an async guard or hook is executing. Use to disable navigation controls. */
60
+ isNavigating: boolean;
61
+ /** Whether the current step's `canMoveNext` guard allows advancing. Async guards default to `true`. */
62
+ canMoveNext: boolean;
63
+ /** Whether the current step's `canMovePrevious` guard allows going back. Async guards default to `true`. */
64
+ canMovePrevious: boolean;
65
+ /** Messages from the current step's `validationMessages` hook. Empty array when there are none. */
66
+ validationMessages: string[];
67
+ data: TData;
68
+ }
69
+
70
+ export type PathEvent =
71
+ | { type: "stateChanged"; snapshot: PathSnapshot }
72
+ | { type: "completed"; pathId: string; data: PathData }
73
+ | { type: "cancelled"; pathId: string; data: PathData }
74
+ | {
75
+ type: "resumed";
76
+ resumedPathId: string;
77
+ fromSubPathId: string;
78
+ snapshot: PathSnapshot;
79
+ };
80
+
81
+ interface ActivePath {
82
+ definition: PathDefinition;
83
+ currentStepIndex: number;
84
+ data: PathData;
85
+ }
86
+
87
+ export class PathEngine {
88
+ private activePath: ActivePath | null = null;
89
+ private readonly pathStack: ActivePath[] = [];
90
+ private readonly listeners = new Set<(event: PathEvent) => void>();
91
+ private _isNavigating = false;
92
+
93
+ public subscribe(listener: (event: PathEvent) => void): () => void {
94
+ this.listeners.add(listener);
95
+ return () => this.listeners.delete(listener);
96
+ }
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // Public API
100
+ // ---------------------------------------------------------------------------
101
+
102
+ public start(path: PathDefinition, initialData: PathData = {}): Promise<void> {
103
+ this.assertPathHasSteps(path);
104
+ return this._startAsync(path, initialData);
105
+ }
106
+
107
+ /** Starts a sub-path on top of the currently active path. Throws if no path is running. */
108
+ public startSubPath(path: PathDefinition, initialData: PathData = {}): Promise<void> {
109
+ this.requireActivePath();
110
+ return this.start(path, initialData);
111
+ }
112
+
113
+ public next(): Promise<void> {
114
+ const active = this.requireActivePath();
115
+ return this._nextAsync(active);
116
+ }
117
+
118
+ public previous(): Promise<void> {
119
+ const active = this.requireActivePath();
120
+ return this._previousAsync(active);
121
+ }
122
+
123
+ /** Cancel is synchronous (no hooks). Returns a resolved Promise for API consistency. */
124
+ public cancel(): Promise<void> {
125
+ const active = this.requireActivePath();
126
+ if (this._isNavigating) return Promise.resolve();
127
+
128
+ const cancelledPathId = active.definition.id;
129
+ const cancelledData = { ...active.data };
130
+
131
+ if (this.pathStack.length > 0) {
132
+ this.activePath = this.pathStack.pop() ?? null;
133
+ this.emitStateChanged();
134
+ return Promise.resolve();
135
+ }
136
+
137
+ this.activePath = null;
138
+ this.emit({ type: "cancelled", pathId: cancelledPathId, data: cancelledData });
139
+ return Promise.resolve();
140
+ }
141
+
142
+ public setData(key: string, value: unknown): Promise<void> {
143
+ const active = this.requireActivePath();
144
+ active.data[key] = value;
145
+ this.emitStateChanged();
146
+ return Promise.resolve();
147
+ }
148
+
149
+ /** Jumps directly to the step with the given ID. Does not check guards or shouldSkip. */
150
+ public goToStep(stepId: string): Promise<void> {
151
+ const active = this.requireActivePath();
152
+ const targetIndex = active.definition.steps.findIndex((s) => s.id === stepId);
153
+ if (targetIndex === -1) {
154
+ throw new Error(`Step "${stepId}" not found in path "${active.definition.id}".`);
155
+ }
156
+ return this._goToStepAsync(active, targetIndex);
157
+ }
158
+
159
+ /**
160
+ * Jumps directly to the step with the given ID, but first checks the
161
+ * direction-appropriate guard on the current step:
162
+ * - Going forward → checks `canMoveNext`
163
+ * - Going backward → checks `canMovePrevious`
164
+ *
165
+ * If the guard blocks, navigation does not occur and `stateChanged` is still
166
+ * emitted (so the UI can react). `shouldSkip` is not evaluated.
167
+ * Throws if the target step ID does not exist.
168
+ */
169
+ public goToStepChecked(stepId: string): Promise<void> {
170
+ const active = this.requireActivePath();
171
+ const targetIndex = active.definition.steps.findIndex((s) => s.id === stepId);
172
+ if (targetIndex === -1) {
173
+ return Promise.reject(new Error(`Step "${stepId}" not found in path "${active.definition.id}".`));
174
+ }
175
+ if (targetIndex === active.currentStepIndex) return Promise.resolve();
176
+ return this._goToStepCheckedAsync(active, targetIndex);
177
+ }
178
+
179
+ public snapshot(): PathSnapshot | null {
180
+ if (this.activePath === null) {
181
+ return null;
182
+ }
183
+
184
+ const active = this.activePath;
185
+ const step = this.getCurrentStep(active);
186
+ const { steps } = active.definition;
187
+ const stepCount = steps.length;
188
+
189
+ return {
190
+ pathId: active.definition.id,
191
+ stepId: step.id,
192
+ stepTitle: step.title,
193
+ stepMeta: step.meta,
194
+ stepIndex: active.currentStepIndex,
195
+ stepCount,
196
+ progress: stepCount <= 1 ? 1 : active.currentStepIndex / (stepCount - 1),
197
+ steps: steps.map((s, i) => ({
198
+ id: s.id,
199
+ title: s.title,
200
+ meta: s.meta,
201
+ status: i < active.currentStepIndex ? "completed" as const
202
+ : i === active.currentStepIndex ? "current" as const
203
+ : "upcoming" as const
204
+ })),
205
+ isFirstStep: active.currentStepIndex === 0,
206
+ isLastStep:
207
+ active.currentStepIndex === stepCount - 1 &&
208
+ this.pathStack.length === 0,
209
+ nestingLevel: this.pathStack.length,
210
+ isNavigating: this._isNavigating,
211
+ canMoveNext: this.evaluateGuardSync(step.canMoveNext, active),
212
+ canMovePrevious: this.evaluateGuardSync(step.canMovePrevious, active),
213
+ validationMessages: this.evaluateValidationMessagesSync(step.validationMessages, active),
214
+ data: { ...active.data }
215
+ };
216
+ }
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Private async helpers
220
+ // ---------------------------------------------------------------------------
221
+
222
+ private async _startAsync(path: PathDefinition, initialData: PathData): Promise<void> {
223
+ if (this._isNavigating) return;
224
+
225
+ if (this.activePath !== null) {
226
+ this.pathStack.push(this.activePath);
227
+ }
228
+
229
+ this.activePath = {
230
+ definition: path,
231
+ currentStepIndex: 0,
232
+ data: { ...initialData }
233
+ };
234
+
235
+ this._isNavigating = true;
236
+
237
+ await this.skipSteps(1);
238
+
239
+ if (this.activePath.currentStepIndex >= path.steps.length) {
240
+ this._isNavigating = false;
241
+ await this.finishActivePath();
242
+ return;
243
+ }
244
+
245
+ this.emitStateChanged();
246
+
247
+ try {
248
+ this.applyPatch(await this.enterCurrentStep());
249
+ this._isNavigating = false;
250
+ this.emitStateChanged();
251
+ } catch (err) {
252
+ this._isNavigating = false;
253
+ this.emitStateChanged();
254
+ throw err;
255
+ }
256
+ }
257
+
258
+ private async _nextAsync(active: ActivePath): Promise<void> {
259
+ if (this._isNavigating) return;
260
+
261
+ this._isNavigating = true;
262
+ this.emitStateChanged();
263
+
264
+ try {
265
+ const step = this.getCurrentStep(active);
266
+
267
+ if (await this.canMoveNext(active, step)) {
268
+ this.applyPatch(await this.leaveCurrentStep(active, step));
269
+ active.currentStepIndex += 1;
270
+ await this.skipSteps(1);
271
+
272
+ if (active.currentStepIndex >= active.definition.steps.length) {
273
+ this._isNavigating = false;
274
+ await this.finishActivePath();
275
+ return;
276
+ }
277
+
278
+ this.applyPatch(await this.enterCurrentStep());
279
+ }
280
+
281
+ this._isNavigating = false;
282
+ this.emitStateChanged();
283
+ } catch (err) {
284
+ this._isNavigating = false;
285
+ this.emitStateChanged();
286
+ throw err;
287
+ }
288
+ }
289
+
290
+ private async _previousAsync(active: ActivePath): Promise<void> {
291
+ if (this._isNavigating) return;
292
+
293
+ // No-op when already on the first step of a top-level path.
294
+ // Sub-paths still cancel/pop back to the parent when previous() is called
295
+ // on their first step (the currentStepIndex < 0 branch below handles that).
296
+ if (active.currentStepIndex === 0 && this.pathStack.length === 0) return;
297
+
298
+ this._isNavigating = true;
299
+ this.emitStateChanged();
300
+
301
+ try {
302
+ const step = this.getCurrentStep(active);
303
+
304
+ if (await this.canMovePrevious(active, step)) {
305
+ this.applyPatch(await this.leaveCurrentStep(active, step));
306
+ active.currentStepIndex -= 1;
307
+ await this.skipSteps(-1);
308
+
309
+ if (active.currentStepIndex < 0) {
310
+ this._isNavigating = false;
311
+ await this.cancel();
312
+ return;
313
+ }
314
+
315
+ this.applyPatch(await this.enterCurrentStep());
316
+ }
317
+
318
+ this._isNavigating = false;
319
+ this.emitStateChanged();
320
+ } catch (err) {
321
+ this._isNavigating = false;
322
+ this.emitStateChanged();
323
+ throw err;
324
+ }
325
+ }
326
+
327
+ private async _goToStepAsync(active: ActivePath, targetIndex: number): Promise<void> {
328
+ if (this._isNavigating) return;
329
+
330
+ this._isNavigating = true;
331
+ this.emitStateChanged();
332
+
333
+ try {
334
+ const currentStep = this.getCurrentStep(active);
335
+ this.applyPatch(await this.leaveCurrentStep(active, currentStep));
336
+
337
+ active.currentStepIndex = targetIndex;
338
+
339
+ this.applyPatch(await this.enterCurrentStep());
340
+ this._isNavigating = false;
341
+ this.emitStateChanged();
342
+ } catch (err) {
343
+ this._isNavigating = false;
344
+ this.emitStateChanged();
345
+ throw err;
346
+ }
347
+ }
348
+
349
+ private async _goToStepCheckedAsync(active: ActivePath, targetIndex: number): Promise<void> {
350
+ if (this._isNavigating) return;
351
+
352
+ this._isNavigating = true;
353
+ this.emitStateChanged();
354
+
355
+ try {
356
+ const currentStep = this.getCurrentStep(active);
357
+ const goingForward = targetIndex > active.currentStepIndex;
358
+ const allowed = goingForward
359
+ ? await this.canMoveNext(active, currentStep)
360
+ : await this.canMovePrevious(active, currentStep);
361
+
362
+ if (allowed) {
363
+ this.applyPatch(await this.leaveCurrentStep(active, currentStep));
364
+ active.currentStepIndex = targetIndex;
365
+ this.applyPatch(await this.enterCurrentStep());
366
+ }
367
+
368
+ this._isNavigating = false;
369
+ this.emitStateChanged();
370
+ } catch (err) {
371
+ this._isNavigating = false;
372
+ this.emitStateChanged();
373
+ throw err;
374
+ }
375
+ }
376
+
377
+ private async finishActivePath(): Promise<void> {
378
+ const finished = this.requireActivePath();
379
+ const finishedPathId = finished.definition.id;
380
+ const finishedData = { ...finished.data };
381
+
382
+ if (this.pathStack.length > 0) {
383
+ this.activePath = this.pathStack.pop()!;
384
+ const parent = this.activePath;
385
+ const parentStep = this.getCurrentStep(parent);
386
+
387
+ if (parentStep.onSubPathComplete) {
388
+ const ctx: PathStepContext = {
389
+ pathId: parent.definition.id,
390
+ stepId: parentStep.id,
391
+ data: { ...parent.data }
392
+ };
393
+ this.applyPatch(
394
+ await parentStep.onSubPathComplete(finishedPathId, finishedData, ctx)
395
+ );
396
+ }
397
+
398
+ this.emit({
399
+ type: "resumed",
400
+ resumedPathId: parent.definition.id,
401
+ fromSubPathId: finishedPathId,
402
+ snapshot: this.snapshot()!
403
+ });
404
+ } else {
405
+ this.activePath = null;
406
+ this.emit({ type: "completed", pathId: finishedPathId, data: finishedData });
407
+ }
408
+ }
409
+
410
+ // ---------------------------------------------------------------------------
411
+ // Private helpers
412
+ // ---------------------------------------------------------------------------
413
+
414
+ private requireActivePath(): ActivePath {
415
+ if (this.activePath === null) {
416
+ throw new Error("No active path.");
417
+ }
418
+ return this.activePath;
419
+ }
420
+
421
+ private assertPathHasSteps(path: PathDefinition): void {
422
+ if (!path.steps || path.steps.length === 0) {
423
+ throw new Error(`Path "${path.id}" must have at least one step.`);
424
+ }
425
+ }
426
+
427
+ private emit(event: PathEvent): void {
428
+ for (const listener of this.listeners) {
429
+ listener(event);
430
+ }
431
+ }
432
+
433
+ private emitStateChanged(): void {
434
+ this.emit({ type: "stateChanged", snapshot: this.snapshot()! });
435
+ }
436
+
437
+ private getCurrentStep(active: ActivePath): PathStep {
438
+ return active.definition.steps[active.currentStepIndex];
439
+ }
440
+
441
+ private applyPatch(patch: Partial<PathData> | void | null | undefined): void {
442
+ if (patch && typeof patch === "object") {
443
+ const active = this.activePath;
444
+ if (active) {
445
+ Object.assign(active.data, patch);
446
+ }
447
+ }
448
+ }
449
+
450
+ private async skipSteps(direction: 1 | -1): Promise<void> {
451
+ const active = this.activePath;
452
+ if (!active) return;
453
+
454
+ while (
455
+ active.currentStepIndex >= 0 &&
456
+ active.currentStepIndex < active.definition.steps.length
457
+ ) {
458
+ const step = active.definition.steps[active.currentStepIndex];
459
+ if (!step.shouldSkip) break;
460
+ const ctx: PathStepContext = {
461
+ pathId: active.definition.id,
462
+ stepId: step.id,
463
+ data: { ...active.data }
464
+ };
465
+ const skip = await step.shouldSkip(ctx);
466
+ if (!skip) break;
467
+ active.currentStepIndex += direction;
468
+ }
469
+ }
470
+
471
+ private async enterCurrentStep(): Promise<Partial<PathData> | void> {
472
+ const active = this.activePath;
473
+ if (!active) return;
474
+ const step = this.getCurrentStep(active);
475
+ if (!step.onEnter) return;
476
+ const ctx: PathStepContext = {
477
+ pathId: active.definition.id,
478
+ stepId: step.id,
479
+ data: { ...active.data }
480
+ };
481
+ return step.onEnter(ctx);
482
+ }
483
+
484
+ private async leaveCurrentStep(
485
+ active: ActivePath,
486
+ step: PathStep
487
+ ): Promise<Partial<PathData> | void> {
488
+ if (!step.onLeave) return;
489
+ const ctx: PathStepContext = {
490
+ pathId: active.definition.id,
491
+ stepId: step.id,
492
+ data: { ...active.data }
493
+ };
494
+ return step.onLeave(ctx);
495
+ }
496
+
497
+ private async canMoveNext(
498
+ active: ActivePath,
499
+ step: PathStep
500
+ ): Promise<boolean> {
501
+ if (!step.canMoveNext) return true;
502
+ const ctx: PathStepContext = {
503
+ pathId: active.definition.id,
504
+ stepId: step.id,
505
+ data: { ...active.data }
506
+ };
507
+ return step.canMoveNext(ctx);
508
+ }
509
+
510
+ private async canMovePrevious(
511
+ active: ActivePath,
512
+ step: PathStep
513
+ ): Promise<boolean> {
514
+ if (!step.canMovePrevious) return true;
515
+ const ctx: PathStepContext = {
516
+ pathId: active.definition.id,
517
+ stepId: step.id,
518
+ data: { ...active.data }
519
+ };
520
+ return step.canMovePrevious(ctx);
521
+ }
522
+
523
+ /**
524
+ * Evaluates a guard function synchronously for inclusion in the snapshot.
525
+ * If the guard is absent, returns `true`.
526
+ * If the guard returns a `Promise`, returns `true` (optimistic default).
527
+ */
528
+ private evaluateGuardSync(
529
+ guard: ((ctx: PathStepContext) => boolean | Promise<boolean>) | undefined,
530
+ active: ActivePath
531
+ ): boolean {
532
+ if (!guard) return true;
533
+ const ctx: PathStepContext = {
534
+ pathId: active.definition.id,
535
+ stepId: this.getCurrentStep(active).id,
536
+ data: { ...active.data }
537
+ };
538
+ const result = guard(ctx);
539
+ if (typeof result === "boolean") return result;
540
+ // Async guard — default to true (optimistic); the engine will enforce the real result on navigation.
541
+ return true;
542
+ }
543
+
544
+ /**
545
+ * Evaluates a validationMessages function synchronously for inclusion in the snapshot.
546
+ * If the hook is absent, returns `[]`.
547
+ * If the hook returns a `Promise`, returns `[]` (async hooks are not supported in snapshots).
548
+ */
549
+ private evaluateValidationMessagesSync(
550
+ fn: ((ctx: PathStepContext) => string[] | Promise<string[]>) | undefined,
551
+ active: ActivePath
552
+ ): string[] {
553
+ if (!fn) return [];
554
+ const ctx: PathStepContext = {
555
+ pathId: active.definition.id,
556
+ stepId: this.getCurrentStep(active).id,
557
+ data: { ...active.data }
558
+ };
559
+ const result = fn(ctx);
560
+ if (Array.isArray(result)) return result;
561
+ // Async hook — default to empty; consumers should keep validationMessages synchronous.
562
+ return [];
563
+ }
564
+ }
565
+