@daltonr/pathwrite-core 0.6.3 → 0.8.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/LICENSE +1 -1
- package/README.md +57 -5
- package/dist/index.d.ts +189 -16
- package/dist/index.js +198 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +381 -66
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -21,6 +21,30 @@ onEnter: (ctx) => {
|
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
### ✅ Track Unsaved Changes with `isDirty`
|
|
25
|
+
```typescript
|
|
26
|
+
// In your UI component
|
|
27
|
+
const snapshot = engine.snapshot();
|
|
28
|
+
if (snapshot.isDirty) {
|
|
29
|
+
// Show "unsaved changes" warning, disable Save button until changes made, etc.
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Revert changes
|
|
33
|
+
<button onClick={() => engine.resetStep()}>Undo Changes</button>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### ✅ Compute Time on Step with `stepEnteredAt`
|
|
37
|
+
```typescript
|
|
38
|
+
// Analytics or timeout warnings
|
|
39
|
+
const snapshot = engine.snapshot();
|
|
40
|
+
const durationMs = Date.now() - snapshot.stepEnteredAt;
|
|
41
|
+
const durationSec = Math.floor(durationMs / 1000);
|
|
42
|
+
|
|
43
|
+
if (durationSec > 300) { // 5 minutes
|
|
44
|
+
console.warn("User has been on this step for 5+ minutes");
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
24
48
|
### ✅ Correlate Sub-Paths with `meta`
|
|
25
49
|
```typescript
|
|
26
50
|
// Starting sub-path
|
|
@@ -53,18 +77,26 @@ const path: PathDefinition<CourseData> = {
|
|
|
53
77
|
onLeave: (ctx) => ({ courseName: ctx.data.courseName.trim() })
|
|
54
78
|
},
|
|
55
79
|
{ id: "review" }
|
|
56
|
-
]
|
|
80
|
+
],
|
|
81
|
+
onComplete: (data) => {
|
|
82
|
+
// Called when the path completes
|
|
83
|
+
console.log("Course created:", data.courseName);
|
|
84
|
+
},
|
|
85
|
+
onCancel: (data) => {
|
|
86
|
+
// Called when the path is cancelled
|
|
87
|
+
console.log("Course creation cancelled");
|
|
88
|
+
}
|
|
57
89
|
};
|
|
58
90
|
```
|
|
59
91
|
|
|
60
92
|
| Type | Description |
|
|
61
93
|
|------|-------------|
|
|
62
|
-
| `PathDefinition<TData>` | A path's ID, title,
|
|
94
|
+
| `PathDefinition<TData>` | A path's ID, title, ordered list of step definitions, and optional `onComplete` / `onCancel` callbacks. |
|
|
63
95
|
| `PathStep<TData>` | A single step: guards, lifecycle hooks. |
|
|
64
96
|
| `PathStepContext<TData>` | Passed to every hook and guard. `data` is a **readonly snapshot copy** — return a patch to update state. |
|
|
65
97
|
| `PathSnapshot<TData>` | Point-in-time read of the engine: step ID, index, count, flags, and a copy of data. |
|
|
66
98
|
| `PathEvent` | Union of `stateChanged` (includes `cause`), `completed`, `cancelled`, and `resumed`. |
|
|
67
|
-
| `StateChangeCause` | Identifies the method that triggered a `stateChanged` event: `"start"` \| `"next"` \| `"previous"` \| `"goToStep"` \| `"goToStepChecked"` \| `"setData"` \| `"cancel"` \| `"restart"`. |
|
|
99
|
+
| `StateChangeCause` | Identifies the method that triggered a `stateChanged` event: `"start"` \| `"next"` \| `"previous"` \| `"goToStep"` \| `"goToStepChecked"` \| `"setData"` \| `"resetStep"` \| `"cancel"` \| `"restart"`. |
|
|
68
100
|
| `PathObserver` | `(event: PathEvent, engine: PathEngine) => void` — a function registered at construction time that receives every event for the engine's lifetime. |
|
|
69
101
|
| `PathEngineOptions` | `{ observers?: PathObserver[] }` — options accepted by the `PathEngine` constructor and `PathEngine.fromState()`. |
|
|
70
102
|
| `ObserverStrategy` | Union type for the five built-in trigger strategies: `"onEveryChange" \| "onNext" \| "onSubPathComplete" \| "onComplete" \| "manual"`. Import and use in your own observer factories. |
|
|
@@ -83,10 +115,15 @@ engine.next();
|
|
|
83
115
|
engine.previous();
|
|
84
116
|
engine.cancel();
|
|
85
117
|
engine.setData(key, value); // update a single data value; emits stateChanged
|
|
86
|
-
engine.
|
|
118
|
+
engine.resetStep(); // revert current step data to what it was on entry; emits stateChanged
|
|
87
119
|
engine.goToStepChecked(stepId); // jump to step by ID; checks canMoveNext / canMovePrevious first
|
|
88
120
|
engine.snapshot(); // returns PathSnapshot | null
|
|
89
121
|
|
|
122
|
+
// Key PathSnapshot fields
|
|
123
|
+
snapshot.isDirty // true if any data changed since entering this step (resets on navigation or resetStep)
|
|
124
|
+
snapshot.hasAttemptedNext // true after user clicks Next at least once on this step
|
|
125
|
+
snapshot.stepEnteredAt // Date.now() timestamp when step was entered (for analytics, timeout warnings)
|
|
126
|
+
|
|
90
127
|
// Serialization API (for persistence)
|
|
91
128
|
const state = engine.exportState(); // returns SerializedPathState | null
|
|
92
129
|
const restoredEngine = PathEngine.fromState(state, pathDefinitions, { observers: [...] });
|
|
@@ -449,10 +486,20 @@ function ReviewStep() {
|
|
|
449
486
|
### What the Shell Renders During Sub-Paths
|
|
450
487
|
|
|
451
488
|
When a sub-path is active:
|
|
452
|
-
-
|
|
489
|
+
- The **root progress bar** stays visible (compact, muted) above the sub-path's progress bar so users always see their place in the main flow
|
|
490
|
+
- The main progress bar shows the sub-path's steps
|
|
453
491
|
- Back button on sub-path's first step cancels the sub-path
|
|
454
492
|
- Completing the sub-path returns to parent (parent step re-renders)
|
|
455
493
|
|
|
494
|
+
The snapshot includes `rootProgress` (type `RootProgress`) when `nestingLevel > 0`:
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
if (snapshot.rootProgress) {
|
|
498
|
+
// { pathId, stepIndex, stepCount, progress, steps }
|
|
499
|
+
console.log(`Main flow: step ${snapshot.rootProgress.stepIndex + 1} of ${snapshot.rootProgress.stepCount}`);
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
456
503
|
### Nesting Levels
|
|
457
504
|
|
|
458
505
|
Sub-paths can themselves start sub-paths (unlimited nesting). Use `snapshot.nestingLevel` to determine depth:
|
|
@@ -519,3 +566,8 @@ Throws if:
|
|
|
519
566
|
|
|
520
567
|
The restored engine is fully functional — you can continue navigation, modify data, complete or cancel paths normally.
|
|
521
568
|
```
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
© 2026 Devjoy Ltd. MIT License.
|
|
573
|
+
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type PathData = Record<string, unknown>;
|
|
2
2
|
/**
|
|
3
|
-
* The return type of a `
|
|
3
|
+
* The return type of a `fieldErrors` hook. Each key is a field ID; the value
|
|
4
4
|
* is an error string, or `undefined` / omitted to indicate no error for that field.
|
|
5
5
|
*
|
|
6
6
|
* Use `"_"` as a key for form-level errors that don't belong to a specific field:
|
|
@@ -16,12 +16,16 @@ export interface SerializedPathState {
|
|
|
16
16
|
data: PathData;
|
|
17
17
|
visitedStepIds: string[];
|
|
18
18
|
subPathMeta?: Record<string, unknown>;
|
|
19
|
+
stepEntryData?: PathData;
|
|
20
|
+
stepEnteredAt?: number;
|
|
19
21
|
pathStack: Array<{
|
|
20
22
|
pathId: string;
|
|
21
23
|
currentStepIndex: number;
|
|
22
24
|
data: PathData;
|
|
23
25
|
visitedStepIds: string[];
|
|
24
26
|
subPathMeta?: Record<string, unknown>;
|
|
27
|
+
stepEntryData?: PathData;
|
|
28
|
+
stepEnteredAt?: number;
|
|
25
29
|
}>;
|
|
26
30
|
_isNavigating: boolean;
|
|
27
31
|
}
|
|
@@ -51,6 +55,45 @@ export interface PathStepContext<TData extends PathData = PathData> {
|
|
|
51
55
|
*/
|
|
52
56
|
readonly isFirstEntry: boolean;
|
|
53
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* A conditional step selection placed in a path's `steps` array in place of a
|
|
60
|
+
* single `PathStep`. When the engine reaches a `StepChoice` it calls `select`
|
|
61
|
+
* to decide which of the bundled `steps` to activate. The chosen step is then
|
|
62
|
+
* treated exactly like any other step — its hooks, guards, and validation all
|
|
63
|
+
* apply normally.
|
|
64
|
+
*
|
|
65
|
+
* `StepChoice` has its own `id` (used for progress tracking and `goToStep`)
|
|
66
|
+
* while `formId` on the snapshot exposes which inner step was selected, so the
|
|
67
|
+
* UI can render the right component.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* {
|
|
72
|
+
* id: "contact-details",
|
|
73
|
+
* select: ({ data }) => data.accountType === "company" ? "company" : "individual",
|
|
74
|
+
* steps: [
|
|
75
|
+
* {
|
|
76
|
+
* id: "individual",
|
|
77
|
+
* fieldErrors: ({ data }) => ({ name: !data.name ? "Required." : undefined }),
|
|
78
|
+
* },
|
|
79
|
+
* {
|
|
80
|
+
* id: "company",
|
|
81
|
+
* fieldErrors: ({ data }) => ({ companyName: !data.companyName ? "Required." : undefined }),
|
|
82
|
+
* },
|
|
83
|
+
* ],
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export interface StepChoice<TData extends PathData = PathData> {
|
|
88
|
+
id: string;
|
|
89
|
+
title?: string;
|
|
90
|
+
meta?: Record<string, unknown>;
|
|
91
|
+
/** Called on step entry. Return the `id` of the step to activate. Throws if the returned id is not found in `steps`. */
|
|
92
|
+
select: (ctx: PathStepContext<TData>) => string;
|
|
93
|
+
steps: PathStep<TData>[];
|
|
94
|
+
/** When `true`, the engine skips this choice slot entirely (same semantics as `PathStep.shouldSkip`). */
|
|
95
|
+
shouldSkip?: (ctx: PathStepContext<TData>) => boolean | Promise<boolean>;
|
|
96
|
+
}
|
|
54
97
|
export interface PathStep<TData extends PathData = PathData> {
|
|
55
98
|
id: string;
|
|
56
99
|
title?: string;
|
|
@@ -64,7 +107,7 @@ export interface PathStep<TData extends PathData = PathData> {
|
|
|
64
107
|
* by field name) so consumers do not need to duplicate validation logic in
|
|
65
108
|
* the template. Return `undefined` for a field to indicate no error.
|
|
66
109
|
*
|
|
67
|
-
* When `
|
|
110
|
+
* When `fieldErrors` is provided and `canMoveNext` is **not**, the engine
|
|
68
111
|
* automatically derives `canMoveNext` as `true` when all values are `undefined`
|
|
69
112
|
* (i.e. no messages), eliminating the need to express the same logic twice.
|
|
70
113
|
*
|
|
@@ -72,13 +115,29 @@ export interface PathStep<TData extends PathData = PathData> {
|
|
|
72
115
|
*
|
|
73
116
|
* @example
|
|
74
117
|
* ```typescript
|
|
75
|
-
*
|
|
118
|
+
* fieldErrors: ({ data }) => ({
|
|
76
119
|
* name: !data.name?.trim() ? "Required." : undefined,
|
|
77
120
|
* email: !isValidEmail(data.email) ? "Invalid email address." : undefined,
|
|
78
121
|
* })
|
|
79
122
|
* ```
|
|
80
123
|
*/
|
|
81
|
-
|
|
124
|
+
fieldErrors?: (ctx: PathStepContext<TData>) => FieldErrors;
|
|
125
|
+
/**
|
|
126
|
+
* Returns a map of field ID → warning message for non-blocking advisories.
|
|
127
|
+
* Same shape as `fieldErrors`, but warnings never affect `canMoveNext` —
|
|
128
|
+
* they are purely informational. Shells render them in amber/yellow instead
|
|
129
|
+
* of red.
|
|
130
|
+
*
|
|
131
|
+
* Evaluated synchronously on every snapshot; async functions default to `{}`.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* fieldWarnings: ({ data }) => ({
|
|
136
|
+
* email: looksLikeTypo(data.email) ? "Did you mean gmail.com?" : undefined,
|
|
137
|
+
* })
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
fieldWarnings?: (ctx: PathStepContext<TData>) => FieldErrors;
|
|
82
141
|
onEnter?: (ctx: PathStepContext<TData>) => Partial<TData> | void | Promise<Partial<TData> | void>;
|
|
83
142
|
onLeave?: (ctx: PathStepContext<TData>) => Partial<TData> | void | Promise<Partial<TData> | void>;
|
|
84
143
|
/**
|
|
@@ -101,7 +160,20 @@ export interface PathStep<TData extends PathData = PathData> {
|
|
|
101
160
|
export interface PathDefinition<TData extends PathData = PathData> {
|
|
102
161
|
id: string;
|
|
103
162
|
title?: string;
|
|
104
|
-
steps: PathStep<TData>[];
|
|
163
|
+
steps: (PathStep<TData> | StepChoice<TData>)[];
|
|
164
|
+
/**
|
|
165
|
+
* Optional callback invoked when this path completes (i.e. the user
|
|
166
|
+
* reaches the end of the last step). Receives the final path data.
|
|
167
|
+
* Only called for top-level paths — sub-path completion is handled by
|
|
168
|
+
* the parent step's `onSubPathComplete` hook.
|
|
169
|
+
*/
|
|
170
|
+
onComplete?: (data: TData) => void | Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Optional callback invoked when this path is cancelled. Receives the
|
|
173
|
+
* path data at the time of cancellation. Only called for top-level paths —
|
|
174
|
+
* sub-path cancellation is handled by the parent step's `onSubPathCancel` hook.
|
|
175
|
+
*/
|
|
176
|
+
onCancel?: (data: TData) => void | Promise<void>;
|
|
105
177
|
}
|
|
106
178
|
export type StepStatus = "completed" | "current" | "upcoming";
|
|
107
179
|
export interface StepSummary {
|
|
@@ -110,11 +182,42 @@ export interface StepSummary {
|
|
|
110
182
|
meta?: Record<string, unknown>;
|
|
111
183
|
status: StepStatus;
|
|
112
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Controls how the shell renders progress bars when a sub-path is active.
|
|
187
|
+
*
|
|
188
|
+
* | Value | Behaviour |
|
|
189
|
+
* |----------------|------------------------------------------------------------------------|
|
|
190
|
+
* | `"merged"` | Root and sub-path bars in one card (default) |
|
|
191
|
+
* | `"split"` | Root and sub-path bars as separate cards |
|
|
192
|
+
* | `"rootOnly"` | Only the root bar — sub-path bar hidden |
|
|
193
|
+
* | `"activeOnly"` | Only the active (sub-path) bar — root bar hidden (pre-v0.7 behaviour) |
|
|
194
|
+
*/
|
|
195
|
+
export type ProgressLayout = "merged" | "split" | "rootOnly" | "activeOnly";
|
|
196
|
+
/**
|
|
197
|
+
* Summary of the root (top-level) path's progress. Present on `PathSnapshot`
|
|
198
|
+
* only when `nestingLevel > 0` — i.e. a sub-path is active.
|
|
199
|
+
*
|
|
200
|
+
* Shells use this to keep the top-level progress bar visible while navigating
|
|
201
|
+
* a sub-path, so users never lose sight of where they are in the main flow.
|
|
202
|
+
*/
|
|
203
|
+
export interface RootProgress {
|
|
204
|
+
pathId: string;
|
|
205
|
+
stepIndex: number;
|
|
206
|
+
stepCount: number;
|
|
207
|
+
progress: number;
|
|
208
|
+
steps: StepSummary[];
|
|
209
|
+
}
|
|
113
210
|
export interface PathSnapshot<TData extends PathData = PathData> {
|
|
114
211
|
pathId: string;
|
|
115
212
|
stepId: string;
|
|
116
213
|
stepTitle?: string;
|
|
117
214
|
stepMeta?: Record<string, unknown>;
|
|
215
|
+
/**
|
|
216
|
+
* The `id` of the selected inner `PathStep` when the current position in
|
|
217
|
+
* the path is a `StepChoice`. `undefined` for ordinary steps.
|
|
218
|
+
* Use this to decide which form component to render.
|
|
219
|
+
*/
|
|
220
|
+
formId?: string;
|
|
118
221
|
stepIndex: number;
|
|
119
222
|
stepCount: number;
|
|
120
223
|
progress: number;
|
|
@@ -122,9 +225,15 @@ export interface PathSnapshot<TData extends PathData = PathData> {
|
|
|
122
225
|
isFirstStep: boolean;
|
|
123
226
|
isLastStep: boolean;
|
|
124
227
|
nestingLevel: number;
|
|
228
|
+
/**
|
|
229
|
+
* Progress summary of the root (top-level) path. Only present when
|
|
230
|
+
* `nestingLevel > 0`. Shells use this to render a persistent top-level
|
|
231
|
+
* progress bar above the sub-path's own progress bar.
|
|
232
|
+
*/
|
|
233
|
+
rootProgress?: RootProgress;
|
|
125
234
|
/** True while an async guard or hook is executing. Use to disable navigation controls. */
|
|
126
235
|
isNavigating: boolean;
|
|
127
|
-
/** Whether the current step's `canMoveNext` guard allows advancing. Async guards default to `true`. Auto-derived as `true` when `
|
|
236
|
+
/** Whether the current step's `canMoveNext` guard allows advancing. Async guards default to `true`. Auto-derived as `true` when `fieldErrors` is defined and returns no messages, and `canMoveNext` is not explicitly defined. */
|
|
128
237
|
canMoveNext: boolean;
|
|
129
238
|
/** Whether the current step's `canMovePrevious` guard allows going back. Async guards default to `true`. */
|
|
130
239
|
canMovePrevious: boolean;
|
|
@@ -137,28 +246,64 @@ export interface PathSnapshot<TData extends PathData = PathData> {
|
|
|
137
246
|
* render and only appear after the user has attempted to proceed:
|
|
138
247
|
*
|
|
139
248
|
* ```svelte
|
|
140
|
-
* {#if snapshot.hasAttemptedNext && snapshot.
|
|
141
|
-
* <span class="error">{snapshot.
|
|
249
|
+
* {#if snapshot.hasAttemptedNext && snapshot.fieldErrors.email}
|
|
250
|
+
* <span class="error">{snapshot.fieldErrors.email}</span>
|
|
142
251
|
* {/if}
|
|
143
252
|
* ```
|
|
144
253
|
*
|
|
145
|
-
* The shell itself uses this flag to gate its own automatic `
|
|
254
|
+
* The shell itself uses this flag to gate its own automatic `fieldErrors`
|
|
146
255
|
* summary rendering — errors are never shown before the first Next attempt.
|
|
147
256
|
*/
|
|
148
257
|
hasAttemptedNext: boolean;
|
|
258
|
+
/**
|
|
259
|
+
* True if any data has changed since entering this step. Automatically computed
|
|
260
|
+
* by comparing the current data to the snapshot taken on step entry. Resets to
|
|
261
|
+
* `false` when navigating to a new step or calling `resetStep()`.
|
|
262
|
+
*
|
|
263
|
+
* Useful for "unsaved changes" warnings, disabling Save buttons until changes
|
|
264
|
+
* are made, or styling forms to indicate modifications.
|
|
265
|
+
*
|
|
266
|
+
* ```typescript
|
|
267
|
+
* {#if snapshot.isDirty}
|
|
268
|
+
* <span class="warning">You have unsaved changes</span>
|
|
269
|
+
* {/if}
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
isDirty: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Timestamp (from `Date.now()`) captured when the current step was entered.
|
|
275
|
+
* Useful for analytics, timeout warnings, or computing how long a user has
|
|
276
|
+
* been on a step.
|
|
277
|
+
*
|
|
278
|
+
* ```typescript
|
|
279
|
+
* const durationMs = Date.now() - snapshot.stepEnteredAt;
|
|
280
|
+
* const durationSec = Math.floor(durationMs / 1000);
|
|
281
|
+
* ```
|
|
282
|
+
*
|
|
283
|
+
* Resets to a new timestamp each time the step is entered (including when
|
|
284
|
+
* navigating back to a previously visited step).
|
|
285
|
+
*/
|
|
286
|
+
stepEnteredAt: number;
|
|
149
287
|
/**
|
|
150
288
|
* Field-keyed validation messages for the current step. Empty object when there are none.
|
|
151
|
-
* Use in step templates to render inline per-field errors: `snapshot.
|
|
289
|
+
* Use in step templates to render inline per-field errors: `snapshot.fieldErrors['email']`.
|
|
152
290
|
* The shell also renders these automatically in a labeled summary box.
|
|
153
291
|
* Use `"_"` as a key for form-level (non-field-specific) errors.
|
|
154
292
|
*/
|
|
155
|
-
|
|
293
|
+
fieldErrors: Record<string, string>;
|
|
294
|
+
/**
|
|
295
|
+
* Field-keyed warning messages for the current step. Empty object when there are none.
|
|
296
|
+
* Same shape as `fieldErrors` but purely informational — warnings never block navigation.
|
|
297
|
+
* Shells render these in amber/yellow instead of red.
|
|
298
|
+
* Use `"_"` as a key for form-level (non-field-specific) warnings.
|
|
299
|
+
*/
|
|
300
|
+
fieldWarnings: Record<string, string>;
|
|
156
301
|
data: TData;
|
|
157
302
|
}
|
|
158
303
|
/**
|
|
159
304
|
* Identifies the public method that triggered a `stateChanged` event.
|
|
160
305
|
*/
|
|
161
|
-
export type StateChangeCause = "start" | "next" | "previous" | "goToStep" | "goToStepChecked" | "setData" | "cancel" | "restart";
|
|
306
|
+
export type StateChangeCause = "start" | "next" | "previous" | "goToStep" | "goToStepChecked" | "setData" | "resetStep" | "cancel" | "restart";
|
|
162
307
|
export type PathEvent = {
|
|
163
308
|
type: "stateChanged";
|
|
164
309
|
cause: StateChangeCause;
|
|
@@ -286,6 +431,13 @@ export declare class PathEngine {
|
|
|
286
431
|
* API consistency. */
|
|
287
432
|
cancel(): Promise<void>;
|
|
288
433
|
setData(key: string, value: unknown): Promise<void>;
|
|
434
|
+
/**
|
|
435
|
+
* Resets the current step's data to what it was when the step was entered.
|
|
436
|
+
* Useful for "Clear" or "Reset" buttons that undo changes within a step.
|
|
437
|
+
* Emits a `stateChanged` event with cause `"resetStep"`.
|
|
438
|
+
* Throws if no path is active.
|
|
439
|
+
*/
|
|
440
|
+
resetStep(): Promise<void>;
|
|
289
441
|
/** Jumps directly to the step with the given ID. Does not check guards or shouldSkip. */
|
|
290
442
|
goToStep(stepId: string): Promise<void>;
|
|
291
443
|
/**
|
|
@@ -323,7 +475,20 @@ export declare class PathEngine {
|
|
|
323
475
|
private assertPathHasSteps;
|
|
324
476
|
private emit;
|
|
325
477
|
private emitStateChanged;
|
|
326
|
-
|
|
478
|
+
/** Returns the raw item at the current index — either a PathStep or a StepChoice. */
|
|
479
|
+
private getCurrentItem;
|
|
480
|
+
/**
|
|
481
|
+
* Calls `StepChoice.select` and caches the chosen inner step in
|
|
482
|
+
* `active.resolvedChoiceStep`. Clears the cache when the current item is a
|
|
483
|
+
* plain `PathStep`. Throws if `select` returns an id not present in `steps`.
|
|
484
|
+
*/
|
|
485
|
+
private cacheResolvedChoiceStep;
|
|
486
|
+
/**
|
|
487
|
+
* Returns the effective `PathStep` for the current position. When the
|
|
488
|
+
* current item is a `StepChoice`, returns the cached resolved inner step.
|
|
489
|
+
* When it is a plain `PathStep`, returns it directly.
|
|
490
|
+
*/
|
|
491
|
+
private getEffectiveStep;
|
|
327
492
|
private applyPatch;
|
|
328
493
|
private skipSteps;
|
|
329
494
|
private enterCurrentStep;
|
|
@@ -348,18 +513,26 @@ export declare class PathEngine {
|
|
|
348
513
|
/**
|
|
349
514
|
* Evaluates `canMoveNext` synchronously for inclusion in the snapshot.
|
|
350
515
|
* When `canMoveNext` is defined, delegates to `evaluateGuardSync`.
|
|
351
|
-
* When absent but `
|
|
516
|
+
* When absent but `fieldErrors` is defined, auto-derives: `true` iff no messages.
|
|
352
517
|
* When neither is defined, returns `true`.
|
|
353
518
|
*/
|
|
354
519
|
private evaluateCanMoveNextSync;
|
|
355
520
|
/**
|
|
356
|
-
* Evaluates a
|
|
521
|
+
* Evaluates a fieldErrors function synchronously for inclusion in the snapshot.
|
|
357
522
|
* If the hook is absent, returns `{}`.
|
|
358
523
|
* If the hook returns a `Promise`, returns `{}` (async hooks are not supported in snapshots).
|
|
359
524
|
* `undefined` values are stripped from the result — only fields with a defined message are included.
|
|
360
525
|
*
|
|
361
|
-
* **Note:** Like guards, `
|
|
526
|
+
* **Note:** Like guards, `fieldErrors` is evaluated before `onEnter` runs on first
|
|
362
527
|
* entry. Write it defensively so it does not throw when fields are absent.
|
|
363
528
|
*/
|
|
364
529
|
private evaluateFieldMessagesSync;
|
|
530
|
+
/**
|
|
531
|
+
* Compares the current step data to the snapshot taken when the step was entered.
|
|
532
|
+
* Returns `true` if any data value has changed.
|
|
533
|
+
*
|
|
534
|
+
* Performs a shallow comparison — only top-level keys are checked. Nested objects
|
|
535
|
+
* are compared by reference, not by deep equality.
|
|
536
|
+
*/
|
|
537
|
+
private computeIsDirty;
|
|
365
538
|
}
|