@modular-react/journeys 0.1.0 → 1.0.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.
- package/LICENSE +21 -21
- package/README.md +2367 -1669
- package/dist/index.d.ts +686 -28
- package/dist/index.js +0 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime-BUVl0_Ad.js +1422 -0
- package/dist/runtime-BUVl0_Ad.js.map +1 -0
- package/dist/testing.d.ts +155 -6
- package/dist/testing.js +62 -30
- package/dist/testing.js.map +1 -1
- package/package.json +7 -5
- package/dist/runtime-DyU_PmaC.js +0 -599
- package/dist/runtime-DyU_PmaC.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { AbandonCtx } from '@modular-react/core';
|
|
2
|
+
import { CatalogMeta } from '@modular-react/core';
|
|
3
|
+
import { ChildOutcome } from '@modular-react/core';
|
|
2
4
|
import { ComponentType } from 'react';
|
|
3
5
|
import { EntryInputOf } from '@modular-react/core';
|
|
4
6
|
import { EntryNamesOf } from '@modular-react/core';
|
|
@@ -7,6 +9,8 @@ import { ExitCtx } from '@modular-react/core';
|
|
|
7
9
|
import { ExitNamesOf } from '@modular-react/core';
|
|
8
10
|
import { ExitOutputOf } from '@modular-react/core';
|
|
9
11
|
import { InstanceId } from '@modular-react/core';
|
|
12
|
+
import { InvokeSpec } from '@modular-react/core';
|
|
13
|
+
import { isJourneySystemAbort } from '@modular-react/core';
|
|
10
14
|
import { JourneyDefinitionSummary } from '@modular-react/core';
|
|
11
15
|
import { JourneyHandleRef } from '@modular-react/core';
|
|
12
16
|
import { JourneyInstance } from '@modular-react/core';
|
|
@@ -14,13 +18,20 @@ import { JourneyPersistence } from '@modular-react/core';
|
|
|
14
18
|
import { JourneyRuntime } from '@modular-react/core';
|
|
15
19
|
import { JourneyStatus } from '@modular-react/core';
|
|
16
20
|
import { JourneyStep } from '@modular-react/core';
|
|
21
|
+
import { JourneySystemAbortReason } from '@modular-react/core';
|
|
22
|
+
import { JourneySystemAbortReasonCode } from '@modular-react/core';
|
|
17
23
|
import { MaybePromise } from '@modular-react/core';
|
|
18
24
|
import { ModuleDescriptor } from '@modular-react/core';
|
|
19
25
|
import { ModuleExitEvent } from '@modular-react/react';
|
|
20
26
|
import { ModuleTypeMap } from '@modular-react/core';
|
|
21
27
|
import { NavigationItemBase } from '@modular-react/core';
|
|
28
|
+
import { ParentLink } from '@modular-react/core';
|
|
29
|
+
import { PendingInvoke } from '@modular-react/core';
|
|
22
30
|
import { ReactNode } from 'react';
|
|
23
31
|
import { RegistryPlugin } from '@modular-react/core';
|
|
32
|
+
import { ResumeBounceCounter } from '@modular-react/core';
|
|
33
|
+
import { ResumeHandler } from '@modular-react/core';
|
|
34
|
+
import { ResumeMap } from '@modular-react/core';
|
|
24
35
|
import { SerializedJourney } from '@modular-react/core';
|
|
25
36
|
import { StepSpec } from '@modular-react/core';
|
|
26
37
|
import { TerminalCtx } from '@modular-react/core';
|
|
@@ -31,13 +42,44 @@ import { TransitionResult } from '@modular-react/core';
|
|
|
31
42
|
|
|
32
43
|
export { AbandonCtx }
|
|
33
44
|
|
|
34
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* A transition handler with declared `targets` metadata attached. Functionally
|
|
47
|
+
* identical to the bare handler the journey runtime expects — the call
|
|
48
|
+
* signature is preserved verbatim, so the value drops directly into a
|
|
49
|
+
* `transitions[mod][entry][exit]` slot. The host's preloader walks
|
|
50
|
+
* `Object.values(perEntry)` and reads each handler's `.targets` to schedule
|
|
51
|
+
* speculative imports.
|
|
52
|
+
*/
|
|
53
|
+
export declare type AnnotatedTransitionHandler<THandler extends (ctx: any) => any, TTargets extends readonly (StepObjectRef | TerminalSentinel)[]> = THandler & {
|
|
54
|
+
readonly targets: TTargets;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Erased shape used by the registry — `any` on every generic lets the
|
|
35
58
|
* registry store definitions from different journeys side-by-side.
|
|
36
59
|
* Tightening to `unknown` breaks variance: `initialState: (input: TInput)
|
|
37
60
|
* => TState` for a specific journey is not assignable to
|
|
38
61
|
* `(input: unknown) => unknown` because function parameters are
|
|
39
|
-
* contravariant, so the registry would reject any concrete definition.
|
|
40
|
-
|
|
62
|
+
* contravariant, so the registry would reject any concrete definition.
|
|
63
|
+
*
|
|
64
|
+
* TModules is also `any` (rather than `ModuleTypeMap`) so the structural
|
|
65
|
+
* variance check on `ResumeMap`/`TransitionMap` does not strictly require
|
|
66
|
+
* the wide form to carry every specific module key — `any` short-circuits
|
|
67
|
+
* the property-by-property check and admits any concrete TModules. */
|
|
68
|
+
export declare type AnyJourneyDefinition = JourneyDefinition<any, any, any, any, any>;
|
|
69
|
+
|
|
70
|
+
export { ChildOutcome }
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Convenience wrapper for ordering two version strings. Returns -1 if `a`
|
|
74
|
+
* sorts before `b`, 1 if after, 0 if equal. Throws {@link SemverParseError}
|
|
75
|
+
* if either input is not a strict `MAJOR.MINOR.PATCH`.
|
|
76
|
+
*
|
|
77
|
+
* Useful for persisted-data compatibility checks: "is the version recorded
|
|
78
|
+
* on this saved blob older than the cutoff at which we changed the on-disk
|
|
79
|
+
* shape?". For matching against a range (`^1.0.0`, `>=1.5 <2`, …) use
|
|
80
|
+
* {@link satisfies} instead.
|
|
81
|
+
*/
|
|
82
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
41
83
|
|
|
42
84
|
/**
|
|
43
85
|
* Create a journey runtime bound to a set of registered journeys. The
|
|
@@ -117,34 +159,43 @@ export declare function createWebStoragePersistence<TInput, TState>(options: Web
|
|
|
117
159
|
* transitions, and the journey's private state.
|
|
118
160
|
*
|
|
119
161
|
* **Why the empty parens?** TypeScript can't partially infer generics: if
|
|
120
|
-
* `defineJourney` took `<TModules, TState, TInput>` in a single
|
|
121
|
-
* either have to spell all
|
|
122
|
-
* from `initialState`'s parameter — or spell none, losing the
|
|
123
|
-
* narrow `TModules` / `TState`. The two-call shape splits the
|
|
124
|
-
* so `TModules` + `TState`
|
|
125
|
-
* inferred from the definition object
|
|
162
|
+
* `defineJourney` took `<TModules, TState, TInput, TOutput>` in a single
|
|
163
|
+
* call, you'd either have to spell all four — losing the ability to infer
|
|
164
|
+
* `TInput` from `initialState`'s parameter — or spell none, losing the
|
|
165
|
+
* ability to narrow `TModules` / `TState`. The two-call shape splits the
|
|
166
|
+
* generics so `TModules` + `TState` (+ optional `TOutput`) are explicit
|
|
167
|
+
* (first call) while `TInput` is inferred from the definition object
|
|
168
|
+
* (second call).
|
|
126
169
|
*
|
|
127
170
|
* ```ts
|
|
128
|
-
* defineJourney<OnboardingModules, OnboardingState>()({
|
|
171
|
+
* defineJourney<OnboardingModules, OnboardingState>()({ ... });
|
|
172
|
+
* defineJourney<OnboardingModules, OnboardingState, { token: string }>()({
|
|
129
173
|
* id: "customer-onboarding",
|
|
130
174
|
* version: "1.0.0",
|
|
131
175
|
* initialState: (input: { customerId: string }) => ({ ... }),
|
|
132
|
-
* // TInput is inferred as { customerId: string } here
|
|
133
176
|
* start: (state) => ({ module: "profile", entry: "review", input: { customerId: state.customerId } }),
|
|
134
|
-
* transitions: {
|
|
177
|
+
* transitions: {
|
|
178
|
+
* billing: {
|
|
179
|
+
* collect: {
|
|
180
|
+
* done: ({ output }) => ({ complete: { token: output.token } }),
|
|
181
|
+
* // ^ checked against { token: string }
|
|
182
|
+
* },
|
|
183
|
+
* },
|
|
184
|
+
* },
|
|
135
185
|
* });
|
|
136
186
|
* ```
|
|
137
187
|
*
|
|
138
188
|
* Zero runtime cost — the definition is returned unchanged.
|
|
139
189
|
*/
|
|
140
|
-
export declare const defineJourney: <TModules extends ModuleTypeMap, TState
|
|
190
|
+
export declare const defineJourney: <TModules extends ModuleTypeMap, TState, TOutput = unknown, TMeta extends { [K in keyof TMeta]: unknown; } = Record<string, unknown>>() => <TInput = void>(definition: JourneyDefinition<TModules, TState, TInput, TOutput, CatalogMeta & TMeta>) => JourneyDefinition<TModules, TState, TInput, TOutput, any>;
|
|
141
191
|
|
|
142
192
|
/**
|
|
143
193
|
* Build a handle from a journey definition. Runtime identity is just
|
|
144
194
|
* `{ id: def.id }`; the returned object is typed so callers get
|
|
145
|
-
* `input`-checking through the `start` overload.
|
|
195
|
+
* `input`-checking through the `start` overload and `outcome.payload`
|
|
196
|
+
* narrowing through a parent journey's resume handler.
|
|
146
197
|
*/
|
|
147
|
-
export declare function defineJourneyHandle<TModules extends ModuleTypeMap, TState, TInput>(def: JourneyDefinition<TModules, TState, TInput>): JourneyHandle<string, TInput>;
|
|
198
|
+
export declare function defineJourneyHandle<TModules extends ModuleTypeMap, TState, TInput, TOutput>(def: JourneyDefinition<TModules, TState, TInput, TOutput>): JourneyHandle<string, TInput, TOutput>;
|
|
148
199
|
|
|
149
200
|
/**
|
|
150
201
|
* Identity helper that ties a persistence adapter's `keyFor` input to a
|
|
@@ -175,6 +226,75 @@ export declare function defineJourneyHandle<TModules extends ModuleTypeMap, TSta
|
|
|
175
226
|
*/
|
|
176
227
|
export declare function defineJourneyPersistence<TInput, TState>(adapter: JourneyPersistence<TState, TInput>): JourneyPersistence<TState, TInput>;
|
|
177
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Wrap a transition handler with a static declaration of every outcome it may
|
|
231
|
+
* take. Two effects:
|
|
232
|
+
*
|
|
233
|
+
* 1. **Runtime — preload precision.** `<JourneyOutlet>`'s default
|
|
234
|
+
* `preload="precise"` mode reads `targets` and warms exactly those
|
|
235
|
+
* entries' chunks during idle time, so navigating Next finds the
|
|
236
|
+
* chunk already cached. Bare-function handlers contribute nothing
|
|
237
|
+
* to precise mode (they fall back to `preload="aggressive"` if set).
|
|
238
|
+
* Sentinel targets (`"complete"`, `"abort"`, `"invoke"`) carry no
|
|
239
|
+
* chunk to preload — they are skipped.
|
|
240
|
+
*
|
|
241
|
+
* 2. **Type-level — the handler's return is constrained to the declared
|
|
242
|
+
* arms.** Declaring `targets: [{ module: "plan", entry: "choose" }]`
|
|
243
|
+
* means the handler may only return `{ next: ... }`; declaring
|
|
244
|
+
* `targets: ["abort"]` means only `{ abort: ... }`; mixing both
|
|
245
|
+
* allows either. Returning an undeclared arm is a compile error.
|
|
246
|
+
*
|
|
247
|
+
* **`targets` is mandatory.** A wrapped handler must enumerate every
|
|
248
|
+
* outcome it may take. If you don't want a declaration, use a bare
|
|
249
|
+
* function — the runtime invocation path is identical, and bare handlers
|
|
250
|
+
* sit out of precise-mode preload.
|
|
251
|
+
*
|
|
252
|
+
* Two call shapes:
|
|
253
|
+
*
|
|
254
|
+
* ```ts
|
|
255
|
+
* // Curried (recommended): bind the journey's generics once, get full
|
|
256
|
+
* // contextual typing on every wrapped handler. Naming convention mirrors
|
|
257
|
+
* // `selectModule` (a descriptive verb for the binder, not an
|
|
258
|
+
* // abbreviation — `tx` reads as "transaction" in most codebases).
|
|
259
|
+
* const transition = defineTransition<OnboardingModules, OnboardingState>();
|
|
260
|
+
*
|
|
261
|
+
* profileComplete: transition({
|
|
262
|
+
* targets: [{ module: "plan", entry: "choose" }],
|
|
263
|
+
* handle: ({ output, state }) => ({
|
|
264
|
+
* state: { ...state, hint: output.hint },
|
|
265
|
+
* next: { module: "plan", entry: "choose", input: ... },
|
|
266
|
+
* }),
|
|
267
|
+
* }),
|
|
268
|
+
*
|
|
269
|
+
* // Mix step refs with sentinels for handlers that branch between
|
|
270
|
+
* // next and a terminal arm:
|
|
271
|
+
* checkout: transition({
|
|
272
|
+
* targets: [{ module: "plan", entry: "choose" }, "abort"],
|
|
273
|
+
* handle: ({ output }) =>
|
|
274
|
+
* output.kind === "ok"
|
|
275
|
+
* ? { next: { module: "plan", entry: "choose", input: ... } }
|
|
276
|
+
* : { abort: { reason: "user-cancelled" } },
|
|
277
|
+
* }),
|
|
278
|
+
*
|
|
279
|
+
* // Bare: zero-config, no contextual narrowing. Targets accept any
|
|
280
|
+
* // `{ module: string; entry: string } | "complete" | "abort" | "invoke"`;
|
|
281
|
+
* // useful for one-off handlers or for journeys whose return literals
|
|
282
|
+
* // are already typed by an outer annotation.
|
|
283
|
+
* cancelled: defineTransition({
|
|
284
|
+
* targets: ["abort"],
|
|
285
|
+
* handle: () => ({ abort: { reason: "user-cancelled" } }),
|
|
286
|
+
* }),
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
export declare function defineTransition<TModules extends ModuleTypeMap, TState = unknown, TOutput = unknown>(): TypedTransitionBinder<TModules, TState, TOutput>;
|
|
290
|
+
|
|
291
|
+
export declare function defineTransition<THandler extends (ctx: any) => any, const TTargets extends readonly (StepObjectRef | TerminalSentinel)[]>(spec: DefineTransitionSpec<THandler, TTargets>): AnnotatedTransitionHandler<THandler, TTargets>;
|
|
292
|
+
|
|
293
|
+
declare interface DefineTransitionSpec<THandler extends (ctx: any) => any, TTargets extends readonly (StepObjectRef | TerminalSentinel)[]> {
|
|
294
|
+
readonly targets: TTargets;
|
|
295
|
+
readonly handle: THandler;
|
|
296
|
+
}
|
|
297
|
+
|
|
178
298
|
export { EntryInputOf }
|
|
179
299
|
|
|
180
300
|
export { EntryNamesOf }
|
|
@@ -189,6 +309,54 @@ export { ExitOutputOf }
|
|
|
189
309
|
|
|
190
310
|
export { InstanceId }
|
|
191
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Typed builder for the `{ invoke }` arm of `TransitionResult`. The union
|
|
314
|
+
* arm itself declares `InvokeSpec<unknown, unknown>` — bare object literals
|
|
315
|
+
* like `{ invoke: { handle, input, resume } }` therefore won't catch a
|
|
316
|
+
* mismatch between `input` and the handle's `TInput`. Going through this
|
|
317
|
+
* helper threads both `TInput` and `TOutput` from the handle into `input`
|
|
318
|
+
* (which must be assignable) and the returned spec, so authors get
|
|
319
|
+
* end-to-end checking at the call site:
|
|
320
|
+
*
|
|
321
|
+
* ```ts
|
|
322
|
+
* confirmAge: ({ state }) =>
|
|
323
|
+
* invoke({
|
|
324
|
+
* handle: verifyIdentityHandle, // TInput = { customerId: string }
|
|
325
|
+
* input: { customerId: state.id }, // checked against TInput
|
|
326
|
+
* resume: "afterAgeVerified",
|
|
327
|
+
* }),
|
|
328
|
+
* ```
|
|
329
|
+
*
|
|
330
|
+
* The runtime treats the result identically to a hand-built literal — the
|
|
331
|
+
* only purpose is the compile-time check.
|
|
332
|
+
*/
|
|
333
|
+
export declare function invoke<TInput, TOutput>(spec: {
|
|
334
|
+
readonly handle: JourneyHandleRef<string, TInput, TOutput>;
|
|
335
|
+
readonly input: TInput;
|
|
336
|
+
readonly resume: string;
|
|
337
|
+
}): {
|
|
338
|
+
readonly invoke: InvokeSpec<TInput, TOutput>;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
export { InvokeSpec }
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Narrow a value to the annotated form. Used by the auto-preloader to read
|
|
345
|
+
* `targets` from a handler without trusting structural lookups on `unknown`.
|
|
346
|
+
* Each target must be either a `{ module, entry }` string-pair or one of
|
|
347
|
+
* the recognized sentinel strings.
|
|
348
|
+
*/
|
|
349
|
+
export declare function isAnnotatedTransition(value: unknown): value is AnnotatedTransitionHandler<(ctx: any) => any, readonly (StepObjectRef | TerminalSentinel)[]>;
|
|
350
|
+
|
|
351
|
+
export { isJourneySystemAbort }
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Narrow a value to one of the recognized {@link TerminalSentinel} strings.
|
|
355
|
+
* Exposed for hosts that introspect a wrapped handler's targets and want to
|
|
356
|
+
* separate step refs from terminal arms without a string-equality dance.
|
|
357
|
+
*/
|
|
358
|
+
export declare function isTerminalSentinel(value: unknown): value is TerminalSentinel;
|
|
359
|
+
|
|
192
360
|
/**
|
|
193
361
|
* Default shape the journeys plugin emits for each `nav`-carrying journey.
|
|
194
362
|
* When {@link JourneysPluginOptions.buildNavItem} is provided, the plugin
|
|
@@ -218,16 +386,102 @@ export declare interface JourneyDefaultNavItem extends NavigationItemBase {
|
|
|
218
386
|
};
|
|
219
387
|
}
|
|
220
388
|
|
|
221
|
-
export declare interface JourneyDefinition<TModules extends ModuleTypeMap, TState, TInput = void
|
|
389
|
+
export declare interface JourneyDefinition<TModules extends ModuleTypeMap, TState, TInput = void, TOutput = unknown, TMeta extends {
|
|
390
|
+
[K in keyof TMeta]: unknown;
|
|
391
|
+
} = Record<string, unknown>> {
|
|
222
392
|
readonly id: string;
|
|
223
393
|
readonly version: string;
|
|
224
|
-
readonly meta?: Readonly<
|
|
394
|
+
readonly meta?: Readonly<CatalogMeta & TMeta>;
|
|
225
395
|
readonly initialState: (input: TInput) => TState;
|
|
226
396
|
readonly start: (state: TState, input: TInput) => StepSpec<TModules>;
|
|
227
|
-
readonly transitions: TransitionMap<TModules, TState>;
|
|
397
|
+
readonly transitions: TransitionMap<TModules, TState, TOutput>;
|
|
398
|
+
/**
|
|
399
|
+
* Resume handlers fired when a child journey `invoke`d from a parent
|
|
400
|
+
* step terminates. Keyed by `[moduleId][entryName][resumeName]` — the
|
|
401
|
+
* runtime looks up `resumes[currentMod][currentEntry][invokeSpec.resume]`
|
|
402
|
+
* at child terminal time and applies the result as the parent's next
|
|
403
|
+
* transition. Optional — journeys that never invoke can omit it.
|
|
404
|
+
*/
|
|
405
|
+
readonly resumes?: ResumeMap<TModules, TState, TOutput>;
|
|
406
|
+
/**
|
|
407
|
+
* Closed set of journey handles this journey may invoke from any of its
|
|
408
|
+
* transitions (or from a resume that returns `{ invoke }`). Strongly
|
|
409
|
+
* recommended for any journey that uses `invoke`:
|
|
410
|
+
*
|
|
411
|
+
* 1. **Static cycle detection.** When every journey in a registration
|
|
412
|
+
* declares `invokes`, the registry runs a graph-level cycle check
|
|
413
|
+
* at validation time and rejects the registration with a path like
|
|
414
|
+
* `cycle detected: A → B → A` — far easier to diagnose than the
|
|
415
|
+
* runtime `invoke-cycle` abort.
|
|
416
|
+
* 2. **Runtime arrival check.** At invoke time the runtime verifies
|
|
417
|
+
* that the dispatched handle id appears in `invokes`; an unexpected
|
|
418
|
+
* handle aborts the parent with reason `invoke-undeclared-child`,
|
|
419
|
+
* catching dynamic dispatch bugs (a transition that branches on
|
|
420
|
+
* `output` and lands on a handle the author never intended).
|
|
421
|
+
*
|
|
422
|
+
* Omit only when the call set is genuinely dynamic (e.g. a host that
|
|
423
|
+
* receives child handles from a slot contribution at runtime). The
|
|
424
|
+
* runtime cycle / depth / bounce guards still apply in that case;
|
|
425
|
+
* they just no longer have a static graph to cross-check against.
|
|
426
|
+
*
|
|
427
|
+
* Self-loops (a journey listing its own handle) are reported as a
|
|
428
|
+
* cycle — by construction they would also blow the call-stack guard
|
|
429
|
+
* at runtime.
|
|
430
|
+
*/
|
|
431
|
+
readonly invokes?: ReadonlyArray<JourneyHandleRef<string, any, any>>;
|
|
432
|
+
/**
|
|
433
|
+
* Expected module version ranges, keyed by the `id` of a module declared
|
|
434
|
+
* in `TModules`. The keys are constrained to that map so a typo in a
|
|
435
|
+
* module id is a compile error rather than a registration-time issue
|
|
436
|
+
* ("requires module 'profil' but it is not registered"). The journeys
|
|
437
|
+
* plugin checks each entry at registry resolve time against the
|
|
438
|
+
* actually-registered module's `version` field; any incompatibility
|
|
439
|
+
* fails assembly with a {@link JourneyValidationError} listing every
|
|
440
|
+
* mismatch at once.
|
|
441
|
+
*
|
|
442
|
+
* **Why declare this even though the journey already references modules
|
|
443
|
+
* by id in `transitions`?** A journey that mixes in a module from another
|
|
444
|
+
* team is implicitly coupled to that module's exit-name and input-shape
|
|
445
|
+
* contract. The id-and-shape match holds today, but a backwards-
|
|
446
|
+
* incompatible bump on the other side ("we renamed the `success` exit
|
|
447
|
+
* to `done`") would otherwise only surface at runtime when the journey
|
|
448
|
+
* actually navigates to that step. Adding a compat declaration moves
|
|
449
|
+
* that failure to startup so an incompatible deployment refuses to come
|
|
450
|
+
* up at all, instead of breaking a single user mid-flow.
|
|
451
|
+
*
|
|
452
|
+
* The range syntax is an npm-style subset: caret, tilde, x-range,
|
|
453
|
+
* comparators, AND, OR, and hyphen — see the README's
|
|
454
|
+
* "Pattern - module compatibility (`moduleCompat`)" section for the
|
|
455
|
+
* exhaustive list. Pre-release tags and build metadata are not
|
|
456
|
+
* supported. Module ids in this map are typed against `TModules` so
|
|
457
|
+
* a typo is a compile error; an entry naming a module that genuinely
|
|
458
|
+
* isn't registered (e.g. via the erased `AnyJourneyDefinition` path)
|
|
459
|
+
* is reported with a dedicated "module not registered" issue.
|
|
460
|
+
*
|
|
461
|
+
* Optional. A journey that omits this field opts out of compatibility
|
|
462
|
+
* enforcement entirely; the existing structural validators
|
|
463
|
+
* (`transitions` referencing real modules / entries / exits) still run.
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* ```ts
|
|
467
|
+
* defineJourney<OnboardingModules, OnboardingState>()({
|
|
468
|
+
* id: "onboarding",
|
|
469
|
+
* version: "1.0.0",
|
|
470
|
+
* moduleCompat: {
|
|
471
|
+
* profile: "^1.0.0",
|
|
472
|
+
* billing: "^2.0.0 || ^3.0.0",
|
|
473
|
+
* plan: ">=1.5.0 <2.0.0",
|
|
474
|
+
* },
|
|
475
|
+
* // ...
|
|
476
|
+
* });
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
readonly moduleCompat?: {
|
|
480
|
+
readonly [K in keyof TModules & string]?: string;
|
|
481
|
+
};
|
|
228
482
|
readonly onTransition?: (ev: TransitionEvent_2<TModules, TState>) => void;
|
|
229
|
-
readonly onAbandon?: (ctx: AbandonCtx<TModules, TState>) => TransitionResult<TModules, TState>;
|
|
230
|
-
readonly onComplete?: (ctx: TerminalCtx<TState>, result:
|
|
483
|
+
readonly onAbandon?: (ctx: AbandonCtx<TModules, TState>) => TransitionResult<TModules, TState, TOutput>;
|
|
484
|
+
readonly onComplete?: (ctx: TerminalCtx<TState>, result: TOutput) => void;
|
|
231
485
|
readonly onAbort?: (ctx: TerminalCtx<TState>, reason: unknown) => void;
|
|
232
486
|
readonly onHydrate?: (blob: SerializedJourney<TState>) => SerializedJourney<TState>;
|
|
233
487
|
}
|
|
@@ -236,14 +490,16 @@ export { JourneyDefinitionSummary }
|
|
|
236
490
|
|
|
237
491
|
/**
|
|
238
492
|
* Lightweight token a journey exports so modules and shells can open it
|
|
239
|
-
* with a typed `input`
|
|
493
|
+
* with a typed `input` (and a typed `outcome.payload` when invoked from a
|
|
494
|
+
* parent journey) without pulling in the journey's runtime code.
|
|
240
495
|
* Structurally identical to `JourneyHandleRef` in `@modular-react/core` —
|
|
241
496
|
* re-exported here so authors have a single canonical name to import.
|
|
242
497
|
*
|
|
243
|
-
* The `__input`
|
|
244
|
-
*
|
|
498
|
+
* The `__input` and `__output` fields are phantom: they never hold values
|
|
499
|
+
* at runtime, they only carry types for the `start(handle, input)`
|
|
500
|
+
* overload and a parent journey's resume handler signature.
|
|
245
501
|
*/
|
|
246
|
-
export declare type JourneyHandle<TId extends string = string, TInput = unknown> = JourneyHandleRef<TId, TInput>;
|
|
502
|
+
export declare type JourneyHandle<TId extends string = string, TInput = unknown, TOutput = unknown> = JourneyHandleRef<TId, TInput, TOutput>;
|
|
247
503
|
|
|
248
504
|
export declare class JourneyHydrationError extends Error {
|
|
249
505
|
constructor(message: string, options?: ErrorOptions);
|
|
@@ -337,6 +593,19 @@ export declare interface JourneyOutletProps {
|
|
|
337
593
|
readonly onStepError?: (err: unknown, ctx: {
|
|
338
594
|
step: JourneyStep;
|
|
339
595
|
}) => JourneyStepErrorPolicy;
|
|
596
|
+
/**
|
|
597
|
+
* When `false`, the outlet renders the instance you handed it directly,
|
|
598
|
+
* even if it has a child journey in flight. Set this when you compose
|
|
599
|
+
* two outlets to render parent and child side-by-side or in a modal —
|
|
600
|
+
* the parent outlet stays on the parent's step, and a sibling outlet
|
|
601
|
+
* keyed off `instance.activeChildId` renders the child.
|
|
602
|
+
*
|
|
603
|
+
* When `true` (the default), the outlet walks the active call chain
|
|
604
|
+
* from the supplied `instanceId` down to the leaf and renders the leaf,
|
|
605
|
+
* matching the subroutine intuition: a child takes over the same
|
|
606
|
+
* outlet for the duration of its run.
|
|
607
|
+
*/
|
|
608
|
+
readonly leafOnly?: boolean;
|
|
340
609
|
/**
|
|
341
610
|
* Cap on `retry` responses before the outlet falls back to `abort`. The
|
|
342
611
|
* counter increments on every retry from `onStepError` and is never reset,
|
|
@@ -355,6 +624,36 @@ export declare interface JourneyOutletProps {
|
|
|
355
624
|
* through their own reporting.
|
|
356
625
|
*/
|
|
357
626
|
readonly errorComponent?: ComponentType<JourneyOutletErrorProps>;
|
|
627
|
+
/**
|
|
628
|
+
* Speculatively prefetch the chunks for entries reachable from the
|
|
629
|
+
* current step during idle time after mount, so the next click finds
|
|
630
|
+
* its bundle hot.
|
|
631
|
+
*
|
|
632
|
+
* `"precise"` (default, alias `true`) — read declared `targets` from
|
|
633
|
+
* `defineTransition({ targets, handle })`-annotated handlers on the
|
|
634
|
+
* current step's transitions. Preload exactly those entries.
|
|
635
|
+
* Bare-function handlers contribute nothing (this is the precise
|
|
636
|
+
* mode's whole point — no guessing).
|
|
637
|
+
*
|
|
638
|
+
* `"aggressive"` — preload every entry that appears as a transition
|
|
639
|
+
* source OR as a declared `target` of any annotated handler in the
|
|
640
|
+
* journey's `transitions` map. The destination-side pass catches
|
|
641
|
+
* terminal-only steps that have no outbound transitions of their
|
|
642
|
+
* own (e.g. a freshly-added receipt screen reachable from `next:`
|
|
643
|
+
* but not yet wired with its own exits). A step reachable only
|
|
644
|
+
* via `definition.start` AND with no outbound transitions of its
|
|
645
|
+
* own is the one remaining static gap — but such a step can only
|
|
646
|
+
* be the current step on first mount (no exits → no advance), and
|
|
647
|
+
* the skip-current logic already excludes it. Useful when handlers
|
|
648
|
+
* are not annotated and the journey is small enough that warming
|
|
649
|
+
* all candidates is cheap.
|
|
650
|
+
*
|
|
651
|
+
* `false` — opt out entirely.
|
|
652
|
+
*
|
|
653
|
+
* Has no effect for eager (`component:`) entries — their import is
|
|
654
|
+
* already resolved. Effects only fire in the browser; SSR is a no-op.
|
|
655
|
+
*/
|
|
656
|
+
readonly preload?: boolean | "precise" | "aggressive";
|
|
358
657
|
}
|
|
359
658
|
|
|
360
659
|
export { JourneyPersistence }
|
|
@@ -434,12 +733,16 @@ export declare interface JourneyRegisterOptions<TState = unknown, TInput = unkno
|
|
|
434
733
|
*/
|
|
435
734
|
onHydrate?: (blob: SerializedJourney<TState>) => SerializedJourney<TState>;
|
|
436
735
|
/**
|
|
437
|
-
* Fires when a step component throws
|
|
438
|
-
* an
|
|
439
|
-
* aborts / retries according to the outlet's
|
|
736
|
+
* Fires when a step component throws, a transition handler throws,
|
|
737
|
+
* or an invoke / resume / abandon hook throws. Observation-only — the
|
|
738
|
+
* runtime still aborts / retries according to the outlet's
|
|
739
|
+
* `onStepError` policy. The `phase` discriminator lets telemetry
|
|
740
|
+
* distinguish a component throw (`"step"`) from an invoke/resume
|
|
741
|
+
* control-plane failure or a custom `onAbandon` crash.
|
|
440
742
|
*/
|
|
441
743
|
onError?: (err: unknown, ctx: {
|
|
442
744
|
step: JourneyStep | null;
|
|
745
|
+
phase: "step" | "invoke" | "resume" | "abandon";
|
|
443
746
|
}) => void;
|
|
444
747
|
/**
|
|
445
748
|
* Optional. Without it, journeys live in memory only — every
|
|
@@ -480,6 +783,58 @@ export declare interface JourneyRegisterOptions<TState = unknown, TInput = unkno
|
|
|
480
783
|
* default item into the app's narrowed type.
|
|
481
784
|
*/
|
|
482
785
|
nav?: JourneyNavContribution<TInput>;
|
|
786
|
+
/**
|
|
787
|
+
* Cap on the depth of an in-flight invoke chain that includes this
|
|
788
|
+
* journey. The depth is the number of *active* journey instances in
|
|
789
|
+
* the chain — a root parent on its own is depth 1, a parent with one
|
|
790
|
+
* in-flight child is depth 2, etc. When an invoke would push depth
|
|
791
|
+
* beyond `maxCallStackDepth`, the parent aborts with reason
|
|
792
|
+
* `invoke-stack-overflow` (the child is never started) and the
|
|
793
|
+
* registration's `onError` fires with `phase: "invoke"`.
|
|
794
|
+
*
|
|
795
|
+
* The runtime resolves the effective limit as the **minimum** of every
|
|
796
|
+
* non-undefined `maxCallStackDepth` across the active chain (ancestors
|
|
797
|
+
* + the new parent + the would-be child's own setting). The most
|
|
798
|
+
* restrictive journey wins, so a cautious utility journey can lower
|
|
799
|
+
* the cap for any flow that includes it without coordinating with the
|
|
800
|
+
* other journeys.
|
|
801
|
+
*
|
|
802
|
+
* Default: `16`. Set lower for journeys whose call graphs are known
|
|
803
|
+
* to be shallow; raise it (carefully) only for genuinely deep
|
|
804
|
+
* compositions. Setting it to `1` blocks `invoke` from this journey
|
|
805
|
+
* outright.
|
|
806
|
+
*
|
|
807
|
+
* `0`, negative, or non-finite values are treated as "no opinion" and
|
|
808
|
+
* fall through to the next journey's setting (or the library default
|
|
809
|
+
* if no journey in the chain expresses an opinion). This matches the
|
|
810
|
+
* `maxHistory` convention so a misconfigured `0` cannot accidentally
|
|
811
|
+
* disable the guard.
|
|
812
|
+
*/
|
|
813
|
+
maxCallStackDepth?: number;
|
|
814
|
+
/**
|
|
815
|
+
* Cap on consecutive resume "bounces" at the *same* parent step. A
|
|
816
|
+
* bounce is a resume that returns `{ invoke }` (re-invoking a child
|
|
817
|
+
* instead of advancing the parent's step). The counter increments on
|
|
818
|
+
* every resume that returns `{ invoke }` and resets to zero whenever
|
|
819
|
+
* the parent's step actually advances (`{ next | complete | abort }`
|
|
820
|
+
* from any source). When the counter would exceed
|
|
821
|
+
* `maxResumeBouncesPerStep`, the parent aborts with reason
|
|
822
|
+
* `resume-bounce-limit`; the child whose terminal triggered the
|
|
823
|
+
* over-the-limit bounce is *not* re-invoked.
|
|
824
|
+
*
|
|
825
|
+
* The counter is persisted on the parent's blob (see
|
|
826
|
+
* `SerializedJourney.resumeBouncesAtStep`) so a reload-bounce sequence
|
|
827
|
+
* cannot reset the budget by round-tripping through storage.
|
|
828
|
+
*
|
|
829
|
+
* Default: `8`. Raise it for flows that legitimately retry a sub-flow
|
|
830
|
+
* many times in a row; lower it for paranoia. The check uses the
|
|
831
|
+
* parent's setting only — children do not influence their parent's
|
|
832
|
+
* bounce budget.
|
|
833
|
+
*
|
|
834
|
+
* `0`, negative, or non-finite values fall through to the library
|
|
835
|
+
* default (matches `maxCallStackDepth` and `maxHistory`).
|
|
836
|
+
*/
|
|
837
|
+
maxResumeBouncesPerStep?: number;
|
|
483
838
|
}
|
|
484
839
|
|
|
485
840
|
export { JourneyRuntime }
|
|
@@ -530,7 +885,7 @@ export declare interface JourneysPluginExtension {
|
|
|
530
885
|
* `options.nav.buildInput` is typed against the journey's input — pass a
|
|
531
886
|
* typed definition and both are checked end-to-end.
|
|
532
887
|
*/
|
|
533
|
-
registerJourney<TModules extends ModuleTypeMap, TState, TInput>(definition: JourneyDefinition<TModules, TState, TInput>, options?: JourneyRegisterOptions<TState, TInput>): void;
|
|
888
|
+
registerJourney<TModules extends ModuleTypeMap, TState, TInput, TOutput = unknown>(definition: JourneyDefinition<TModules, TState, TInput, TOutput>, options?: JourneyRegisterOptions<TState, TInput>): void;
|
|
534
889
|
}
|
|
535
890
|
|
|
536
891
|
export declare interface JourneysPluginOptions<TNavItem extends NavigationItemBase = JourneyDefaultNavItem> {
|
|
@@ -569,6 +924,10 @@ export { JourneyStep }
|
|
|
569
924
|
|
|
570
925
|
export declare type JourneyStepErrorPolicy = "abort" | "retry" | "ignore";
|
|
571
926
|
|
|
927
|
+
export { JourneySystemAbortReason }
|
|
928
|
+
|
|
929
|
+
export { JourneySystemAbortReasonCode }
|
|
930
|
+
|
|
572
931
|
/**
|
|
573
932
|
* Aggregated error thrown when one or more registered journeys reference
|
|
574
933
|
* module ids, entry names, or exit names that do not exist (or that
|
|
@@ -662,16 +1021,242 @@ export declare interface ModuleTabProps<TInput = unknown> {
|
|
|
662
1021
|
|
|
663
1022
|
export { ModuleTypeMap }
|
|
664
1023
|
|
|
1024
|
+
/**
|
|
1025
|
+
* Narrow the handler return type to only the arms whose targets are declared.
|
|
1026
|
+
* - `{ module, entry }` in targets → `next:` arm allowed (with `input` typed
|
|
1027
|
+
* against the chosen entry). Multiple refs collapse into one `next:` key
|
|
1028
|
+
* whose value is the union of step specs.
|
|
1029
|
+
* - `"complete"` in targets → `complete:` arm allowed.
|
|
1030
|
+
* - `"abort"` in targets → `abort:` arm allowed.
|
|
1031
|
+
* - `"invoke"` in targets → `invoke:` arm allowed.
|
|
1032
|
+
*
|
|
1033
|
+
* Declaring an arm in targets but never returning it is fine (over-declaring
|
|
1034
|
+
* is conservative for preload). Returning an arm that wasn't declared is a
|
|
1035
|
+
* compile error — the wrapped handler can't drift past the declaration.
|
|
1036
|
+
*/
|
|
1037
|
+
declare type NarrowedTransitionResult<TModules extends ModuleTypeMap, TState, TOutput, TTargets extends readonly StepRef<TModules>[]> = (Extract<TTargets[number], StepObjectRef> extends never ? never : {
|
|
1038
|
+
readonly next: StepSpecFromRef<TModules, Extract<TTargets[number], StepObjectRef>>;
|
|
1039
|
+
readonly state?: TState;
|
|
1040
|
+
}) | (Extract<TTargets[number], "complete"> extends never ? never : {
|
|
1041
|
+
readonly complete: TOutput;
|
|
1042
|
+
readonly state?: TState;
|
|
1043
|
+
}) | (Extract<TTargets[number], "abort"> extends never ? never : {
|
|
1044
|
+
readonly abort: unknown;
|
|
1045
|
+
readonly state?: TState;
|
|
1046
|
+
}) | (Extract<TTargets[number], "invoke"> extends never ? never : {
|
|
1047
|
+
readonly invoke: InvokeSpec<unknown, unknown>;
|
|
1048
|
+
readonly state?: TState;
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
export { ParentLink }
|
|
1052
|
+
|
|
1053
|
+
export { PendingInvoke }
|
|
1054
|
+
|
|
665
1055
|
/** Internal registration record — definition + options kept together. */
|
|
666
1056
|
export declare interface RegisteredJourney<TState = unknown, TInput = unknown> {
|
|
667
1057
|
readonly definition: AnyJourneyDefinition;
|
|
668
1058
|
readonly options: JourneyRegisterOptions<TState, TInput> | undefined;
|
|
669
1059
|
}
|
|
670
1060
|
|
|
1061
|
+
export { ResumeBounceCounter }
|
|
1062
|
+
|
|
1063
|
+
export { ResumeHandler }
|
|
1064
|
+
|
|
1065
|
+
export { ResumeMap }
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Convenience wrapper that parses both inputs and runs satisfaction. Use
|
|
1069
|
+
* this for one-shot checks; for hot loops, parse once and pass the cached
|
|
1070
|
+
* {@link ParsedRange} to {@link satisfiesParsed}.
|
|
1071
|
+
*/
|
|
1072
|
+
export declare function satisfies(version: string, range: string): boolean;
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* Curried helper for state-driven module dispatch in a transition handler.
|
|
1076
|
+
*
|
|
1077
|
+
* `selectModule<TModules>()` binds the journey's module map; the inner call
|
|
1078
|
+
* takes a discriminator (`key`) whose value names the next module and a
|
|
1079
|
+
* cases object that supplies the entry + input for each branch. Returns a
|
|
1080
|
+
* `StepSpec` ready to drop into `{ next }`.
|
|
1081
|
+
*
|
|
1082
|
+
* **Why curry on `TModules`?** Same partial-inference reason as
|
|
1083
|
+
* `defineJourney`: TypeScript can't infer `TKey` while we're also forcing
|
|
1084
|
+
* `TModules` to be specified — splitting the calls lets us spell the module
|
|
1085
|
+
* map once and let the discriminator's union flow naturally into `TKey`.
|
|
1086
|
+
*
|
|
1087
|
+
* **Exhaustive by design.** The cases object is `Record<TKey, …>`, so when
|
|
1088
|
+
* `key` is a union literal (`"github" | "strapi" | "contentful"`),
|
|
1089
|
+
* forgetting a branch is a compile error. When you want a default-everything
|
|
1090
|
+
* fallback instead of branch-by-branch coverage, use
|
|
1091
|
+
* {@link selectModuleOrDefault}.
|
|
1092
|
+
*
|
|
1093
|
+
* **What you get:**
|
|
1094
|
+
* - **Per-branch input checking** — each case's `entry` and `input` are
|
|
1095
|
+
* typed against the module the branch dispatches to. You can't paste a
|
|
1096
|
+
* `strapi`-shaped input under the `github` key.
|
|
1097
|
+
* - **One state-spread instead of N** — call sites no longer repeat
|
|
1098
|
+
* `{ state, next: { … } }` per branch.
|
|
1099
|
+
*
|
|
1100
|
+
* **Limit:** the discriminator key must equal the target module id. When
|
|
1101
|
+
* the discriminator differs from the id (e.g. `tier: "free" | "paid"`
|
|
1102
|
+
* dispatching to module ids `trial-onboarding` / `billing-onboarding`),
|
|
1103
|
+
* fall back to a `switch` returning `next` per branch — that case isn't
|
|
1104
|
+
* common enough yet to justify a second helper.
|
|
1105
|
+
*
|
|
1106
|
+
* @example
|
|
1107
|
+
* ```ts
|
|
1108
|
+
* import { selectModule } from "@modular-react/journeys";
|
|
1109
|
+
*
|
|
1110
|
+
* const select = selectModule<IntegrationModules>();
|
|
1111
|
+
*
|
|
1112
|
+
* chosen: ({ output, state }) => ({
|
|
1113
|
+
* state: { ...state, selected: output.kind },
|
|
1114
|
+
* next: select(output.kind, {
|
|
1115
|
+
* github: { entry: "configure", input: { workspaceId: state.workspaceId } },
|
|
1116
|
+
* strapi: { entry: "configure", input: { workspaceId: state.workspaceId } },
|
|
1117
|
+
* contentful: { entry: "configure", input: { workspaceId: state.workspaceId } },
|
|
1118
|
+
* }),
|
|
1119
|
+
* }),
|
|
1120
|
+
* ```
|
|
1121
|
+
*
|
|
1122
|
+
* Zero runtime cost beyond an object lookup.
|
|
1123
|
+
*/
|
|
1124
|
+
export declare const selectModule: <TModules extends ModuleTypeMap>() => <TKey extends keyof TModules & string>(key: TKey, cases: SelectModuleCases<TModules, TKey>) => StepSpec<TModules>;
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Cases map for the exhaustive form `selectModule(key, cases)` — one case
|
|
1128
|
+
* per discriminator value. Each case's `entry` is narrowed against that
|
|
1129
|
+
* module's `entryPoints`; `input` is checked against that entry. Missing a
|
|
1130
|
+
* key is a compile error, so a journey gains exhaustiveness for free when
|
|
1131
|
+
* the discriminator is a union literal.
|
|
1132
|
+
*/
|
|
1133
|
+
export declare type SelectModuleCases<TModules extends ModuleTypeMap, TKey extends keyof TModules & string> = {
|
|
1134
|
+
readonly [M in TKey]: StepCaseFor<TModules, M>;
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Cases map for the fallback form `selectModuleOrDefault(key, cases,
|
|
1139
|
+
* fallback)`. Every case is optional and any discriminator value not
|
|
1140
|
+
* present in `cases` falls through to the explicit fallback `StepSpec`.
|
|
1141
|
+
* `TKey` is intentionally widened to `string` so callers can pass a
|
|
1142
|
+
* discriminator that includes values outside the module map (the fallback
|
|
1143
|
+
* handles them).
|
|
1144
|
+
*
|
|
1145
|
+
* Keys are constrained to `Extract<TKey, keyof TModules>` so a typo on a
|
|
1146
|
+
* module id still errors — the looseness is only on TKey itself, not on
|
|
1147
|
+
* which module ids the cases object accepts.
|
|
1148
|
+
*/
|
|
1149
|
+
export declare type SelectModuleCasesPartial<TModules extends ModuleTypeMap, TKey extends string> = {
|
|
1150
|
+
readonly [M in Extract<TKey, keyof TModules & string>]?: StepCaseFor<TModules, M>;
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* Sibling of {@link selectModule} that allows partial cases plus an
|
|
1155
|
+
* explicit fallback `StepSpec`. Use when most discriminator values funnel
|
|
1156
|
+
* into a generic module and only a few warrant their own specific
|
|
1157
|
+
* dispatch.
|
|
1158
|
+
*
|
|
1159
|
+
* Kept as a separate function (rather than a third argument on
|
|
1160
|
+
* `selectModule`) so that the *exhaustive* call site is visually
|
|
1161
|
+
* distinct from the *fallback-allowed* one — losing exhaustiveness in
|
|
1162
|
+
* `selectModule` by accidentally adding a third argument later would
|
|
1163
|
+
* silently disable the missing-branch compile error.
|
|
1164
|
+
*
|
|
1165
|
+
* The cases object is `Partial<Record<TKey, …>>`; any discriminator value
|
|
1166
|
+
* not present uses `fallback`. The fallback is a full `StepSpec`
|
|
1167
|
+
* (carrying its own `module`) since it isn't keyed by the discriminator.
|
|
1168
|
+
*
|
|
1169
|
+
* @example
|
|
1170
|
+
* ```ts
|
|
1171
|
+
* import { selectModuleOrDefault } from "@modular-react/journeys";
|
|
1172
|
+
*
|
|
1173
|
+
* const select = selectModuleOrDefault<IntegrationModules>();
|
|
1174
|
+
*
|
|
1175
|
+
* chosen: ({ output, state }) => ({
|
|
1176
|
+
* state: { ...state, selected: output.kind },
|
|
1177
|
+
* next: select(
|
|
1178
|
+
* output.kind,
|
|
1179
|
+
* {
|
|
1180
|
+
* github: { entry: "configure", input: { workspaceId: state.workspaceId, repo: output.repo } },
|
|
1181
|
+
* },
|
|
1182
|
+
* { module: "generic", entry: "configure", input: { workspaceId: state.workspaceId, kind: output.kind } },
|
|
1183
|
+
* ),
|
|
1184
|
+
* }),
|
|
1185
|
+
* ```
|
|
1186
|
+
*/
|
|
1187
|
+
export declare const selectModuleOrDefault: <TModules extends ModuleTypeMap>() => <TKey extends string>(key: TKey, cases: SelectModuleCasesPartial<TModules, TKey>, fallback: StepSpec<TModules>) => StepSpec<TModules>;
|
|
1188
|
+
|
|
1189
|
+
export declare class SemverParseError extends Error {
|
|
1190
|
+
constructor(message: string);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
671
1193
|
export { SerializedJourney }
|
|
672
1194
|
|
|
1195
|
+
/**
|
|
1196
|
+
* One case in a `selectModule` map: an entry name on module `M` plus the
|
|
1197
|
+
* matching input shape. The mapped object union is collapsed via the
|
|
1198
|
+
* indexed-access trick at the end so the resulting type is a discriminated
|
|
1199
|
+
* union over the module's entries (the same shape `StepSpec` uses).
|
|
1200
|
+
*/
|
|
1201
|
+
declare type StepCaseFor<TModules extends ModuleTypeMap, M extends keyof TModules & string> = {
|
|
1202
|
+
[E in EntryNamesOf<TModules[M]> & string]: {
|
|
1203
|
+
readonly entry: E;
|
|
1204
|
+
readonly input: EntryInputOf<TModules[M], E>;
|
|
1205
|
+
};
|
|
1206
|
+
}[EntryNamesOf<TModules[M]> & string];
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* Type predicate that splits a `StepRef` into its step-ref vs sentinel arms.
|
|
1210
|
+
* Object refs land in the `next:` arm; sentinels gate the terminal arms.
|
|
1211
|
+
*/
|
|
1212
|
+
declare type StepObjectRef = {
|
|
1213
|
+
readonly module: string;
|
|
1214
|
+
readonly entry: string;
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* Reference to one possible outcome of a transition handler. Used by
|
|
1219
|
+
* {@link defineTransition} to declare every branch the handler may take;
|
|
1220
|
+
* the host's auto-preloader reads the `{ module, entry }` entries to warm
|
|
1221
|
+
* chunks for next-step candidates from the current step, and the catalog
|
|
1222
|
+
* harvester reads the sentinels to derive `aborts` / `completes` flags
|
|
1223
|
+
* without an AST walk over the handler body.
|
|
1224
|
+
*
|
|
1225
|
+
* The `{ module, entry }` shape mirrors the `next:` field handlers return —
|
|
1226
|
+
* a step ref is just `StepSpec` without the runtime-computed `input` —
|
|
1227
|
+
* so authors don't flip between two notations for the same idea.
|
|
1228
|
+
*
|
|
1229
|
+
* When `TModules` is bound (via the curried `defineTransition<TModules>()`
|
|
1230
|
+
* binder), `module` narrows to `keyof TModules` and `entry` narrows to that
|
|
1231
|
+
* module's `entryPoints` keys.
|
|
1232
|
+
*/
|
|
1233
|
+
export declare type StepRef<TModules extends ModuleTypeMap> = {
|
|
1234
|
+
[M in keyof TModules & string]: {
|
|
1235
|
+
[E in EntryNamesOf<TModules[M]> & string]: {
|
|
1236
|
+
readonly module: M;
|
|
1237
|
+
readonly entry: E;
|
|
1238
|
+
};
|
|
1239
|
+
}[EntryNamesOf<TModules[M]> & string];
|
|
1240
|
+
}[keyof TModules & string] | TerminalSentinel;
|
|
1241
|
+
|
|
673
1242
|
export { StepSpec }
|
|
674
1243
|
|
|
1244
|
+
/**
|
|
1245
|
+
* Build the `next.{ module, entry, input }` shape for one declared step ref.
|
|
1246
|
+
* Distributes over a union of refs so multiple targets produce a union of
|
|
1247
|
+
* step specs under a single `next:` key (rather than separate `{ next: A }`
|
|
1248
|
+
* vs `{ next: B }` arms — the latter would reject conditional handler
|
|
1249
|
+
* returns like `next: cond ? planRef : billingRef`).
|
|
1250
|
+
*/
|
|
1251
|
+
declare type StepSpecFromRef<TModules extends ModuleTypeMap, TRef> = TRef extends {
|
|
1252
|
+
readonly module: infer M;
|
|
1253
|
+
readonly entry: infer E;
|
|
1254
|
+
} ? M extends keyof TModules & string ? E extends EntryNamesOf<TModules[M]> & string ? {
|
|
1255
|
+
readonly module: M;
|
|
1256
|
+
readonly entry: E;
|
|
1257
|
+
readonly input: EntryInputOf<TModules[M], E>;
|
|
1258
|
+
} : never : never : never;
|
|
1259
|
+
|
|
675
1260
|
/**
|
|
676
1261
|
* Narrowed variant of {@link JourneyPersistence} whose methods are
|
|
677
1262
|
* guaranteed synchronous — `load` returns `SerializedJourney<TState> | null`
|
|
@@ -698,12 +1283,42 @@ export { TerminalCtx }
|
|
|
698
1283
|
|
|
699
1284
|
export { TerminalOutcome }
|
|
700
1285
|
|
|
1286
|
+
/**
|
|
1287
|
+
* Sentinel value declaring a non-`next` outcome on a wrapped transition
|
|
1288
|
+
* handler. Mixed into the `targets:` array alongside `{ module, entry }`
|
|
1289
|
+
* step refs so a single declaration captures every branch the handler
|
|
1290
|
+
* may take.
|
|
1291
|
+
*
|
|
1292
|
+
* - `"complete"` — handler may return `{ complete: ... }` (terminates).
|
|
1293
|
+
* - `"abort"` — handler may return `{ abort: ... }` (terminates with abort).
|
|
1294
|
+
* - `"invoke"` — handler may return `{ invoke: { handle, input, resume } }`
|
|
1295
|
+
* (suspends the parent, runs a child journey). The specific handle is
|
|
1296
|
+
* not type-narrowed here — the journey definition's `invokes` field
|
|
1297
|
+
* remains the closed-set declaration the runtime cycle / undeclared-
|
|
1298
|
+
* child guards check against.
|
|
1299
|
+
*/
|
|
1300
|
+
export declare type TerminalSentinel = "complete" | "abort" | "invoke";
|
|
1301
|
+
|
|
701
1302
|
export { TransitionEvent_2 as TransitionEvent }
|
|
702
1303
|
|
|
703
1304
|
export { TransitionMap }
|
|
704
1305
|
|
|
705
1306
|
export { TransitionResult }
|
|
706
1307
|
|
|
1308
|
+
/**
|
|
1309
|
+
* Curried binder used by {@link defineTransition} to thread the journey's
|
|
1310
|
+
* `TModules` / `TState` / `TOutput` into the handler's contextual return
|
|
1311
|
+
* type — `next.module` / `next.entry` and the choice of arms (`next` vs
|
|
1312
|
+
* `complete` vs `abort` vs `invoke`) check against the bound generics
|
|
1313
|
+
* instead of widening to plain `string` / accepting any arm.
|
|
1314
|
+
*/
|
|
1315
|
+
declare interface TypedTransitionBinder<TModules extends ModuleTypeMap, TState, TOutput> {
|
|
1316
|
+
<const TTargets extends readonly StepRef<TModules>[], TEntryInput = unknown, TExitOutput = unknown>(spec: {
|
|
1317
|
+
readonly targets: TTargets;
|
|
1318
|
+
readonly handle: (ctx: ExitCtx<TState, TExitOutput, TEntryInput>) => NarrowedTransitionResult<TModules, TState, TOutput, TTargets>;
|
|
1319
|
+
}): AnnotatedTransitionHandler<(ctx: ExitCtx<TState, TExitOutput, TEntryInput>) => TransitionResult<TModules, TState, TOutput>, TTargets>;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
707
1322
|
/**
|
|
708
1323
|
* Thrown when `runtime.start()` / `runtime.hydrate()` is called with a
|
|
709
1324
|
* journey id that is not registered. Distinct class so shells can
|
|
@@ -715,6 +1330,19 @@ export declare class UnknownJourneyError extends Error {
|
|
|
715
1330
|
constructor(journeyId: string, registered: readonly string[]);
|
|
716
1331
|
}
|
|
717
1332
|
|
|
1333
|
+
/**
|
|
1334
|
+
* Returns the call stack for an outlet's instance — root at index 0, the
|
|
1335
|
+
* active leaf at the end, intermediate parents in between. Useful for
|
|
1336
|
+
* shells that render layered presentations (e.g. parent visible
|
|
1337
|
+
* underneath, child in a modal): mount the parent outlet with
|
|
1338
|
+
* `leafOnly={false}` and the child outlet against `chain[chain.length - 1]`.
|
|
1339
|
+
*
|
|
1340
|
+
* Subscribes to every instance in the chain so the array re-resolves
|
|
1341
|
+
* when the chain shifts. Length is at least 1 (the root) for any
|
|
1342
|
+
* registered instance.
|
|
1343
|
+
*/
|
|
1344
|
+
export declare function useJourneyCallStack(runtime: JourneyRuntime, rootId: InstanceId): readonly InstanceId[];
|
|
1345
|
+
|
|
718
1346
|
/** Read the current provider value, or `null` when none is mounted. */
|
|
719
1347
|
export declare function useJourneyContext(): JourneyProviderValue | null;
|
|
720
1348
|
|
|
@@ -727,6 +1355,36 @@ export declare function validateJourneyContracts(journeys: readonly RegisteredJo
|
|
|
727
1355
|
*/
|
|
728
1356
|
export declare function validateJourneyDefinition(def: AnyJourneyDefinition): readonly string[];
|
|
729
1357
|
|
|
1358
|
+
/**
|
|
1359
|
+
* Verify the directed graph of journey-to-journey invocations, derived
|
|
1360
|
+
* from each registered journey's `invokes` declaration, contains no
|
|
1361
|
+
* cycles. A cycle in the static graph would, at runtime, manifest as
|
|
1362
|
+
* either an infinite chain (depth-limited by `maxCallStackDepth`) or a
|
|
1363
|
+
* same-id-on-stack abort — both are recoverable but late. The graph
|
|
1364
|
+
* check turns the same mistake into a registration-time error citing
|
|
1365
|
+
* the cycle path.
|
|
1366
|
+
*
|
|
1367
|
+
* Run automatically as part of {@link validateJourneyContracts}. Exposed
|
|
1368
|
+
* separately so shells that compose registrations (e.g. plugin chaining)
|
|
1369
|
+
* can run the graph check on a partial slice without invoking the full
|
|
1370
|
+
* contracts validator. Throws {@link JourneyValidationError} when one or
|
|
1371
|
+
* more cycles are detected.
|
|
1372
|
+
*
|
|
1373
|
+
* **What's checked.** The graph only spans journeys whose `invokes`
|
|
1374
|
+
* field is declared as an array. Edges to journey ids that are not
|
|
1375
|
+
* present in `journeys` are ignored — those will fail at runtime with
|
|
1376
|
+
* `invoke-unknown-journey`, not as cycle reports. Self-loops (a journey
|
|
1377
|
+
* that lists its own handle) are reported as a one-cycle.
|
|
1378
|
+
*
|
|
1379
|
+
* **What's NOT checked.** Journeys that omit `invokes` contribute no
|
|
1380
|
+
* edges; a cycle that runs through such a journey will not be caught
|
|
1381
|
+
* statically, and the runtime guards (`invoke-cycle`,
|
|
1382
|
+
* `invoke-stack-overflow`) remain the safety net. Authors who want
|
|
1383
|
+
* full static coverage should declare `invokes` on every journey that
|
|
1384
|
+
* uses `invoke()`.
|
|
1385
|
+
*/
|
|
1386
|
+
export declare function validateJourneyGraph(journeys: readonly RegisteredJourney[]): void;
|
|
1387
|
+
|
|
730
1388
|
export declare interface WebStoragePersistenceOptions<TInput> {
|
|
731
1389
|
/**
|
|
732
1390
|
* Compute the persistence key from the journey id and starting input.
|