@rxova/journey-core 1.0.0-rc.3 → 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/README.md +185 -189
- package/dist/connectors/immer/immer.cjs +2 -0
- package/dist/connectors/immer/immer.cjs.map +7 -0
- package/dist/connectors/immer/immer.d.cts +11 -0
- package/dist/connectors/immer/immer.d.ts +11 -0
- package/dist/connectors/immer/immer.js +2 -0
- package/dist/connectors/immer/immer.js.map +7 -0
- package/dist/connectors/immer/immer.types.d.cts +9 -0
- package/dist/connectors/immer/immer.types.d.ts +9 -0
- package/dist/core/errors.d.cts +57 -0
- package/dist/core/errors.d.ts +57 -0
- package/dist/core/helpers.d.cts +40 -0
- package/dist/core/helpers.d.ts +40 -0
- package/dist/core/machine.d.cts +9 -0
- package/dist/core/machine.d.ts +9 -0
- package/dist/core/runtime.d.cts +115 -0
- package/dist/core/runtime.d.ts +115 -0
- package/dist/core/runtime.types.d.cts +112 -0
- package/dist/core/runtime.types.d.ts +112 -0
- package/dist/core/store.d.cts +31 -0
- package/dist/core/store.d.ts +31 -0
- package/dist/core/types.d.cts +481 -0
- package/dist/core/types.d.ts +481 -0
- package/dist/diagnostics/diagnostics.helpers.d.cts +17 -0
- package/dist/diagnostics/diagnostics.helpers.d.ts +17 -0
- package/dist/diagnostics/diagnostics.types.d.cts +22 -0
- package/dist/diagnostics/diagnostics.types.d.ts +22 -0
- package/dist/graph/bag.types.d.cts +91 -0
- package/dist/graph/bag.types.d.ts +91 -0
- package/dist/graph/graph.d.cts +63 -0
- package/dist/graph/graph.d.ts +63 -0
- package/dist/graph/graph.types.d.cts +212 -0
- package/dist/graph/graph.types.d.ts +212 -0
- package/dist/graph/work.d.cts +81 -0
- package/dist/graph/work.d.ts +81 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.cts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +1 -1
- package/dist/index.js.map +4 -4
- package/dist/linear/linear.d.cts +33 -0
- package/dist/linear/linear.d.ts +33 -0
- package/dist/linear/linear.types.d.cts +28 -0
- package/dist/linear/linear.types.d.ts +28 -0
- package/dist/plugins/analytics/analytics.d.cts +8 -0
- package/dist/plugins/analytics/analytics.d.ts +8 -0
- package/dist/plugins/analytics/analytics.helpers.d.cts +2 -0
- package/dist/plugins/analytics/analytics.helpers.d.ts +2 -0
- package/dist/plugins/analytics/analytics.types.d.cts +27 -0
- package/dist/plugins/analytics/analytics.types.d.ts +27 -0
- package/dist/plugins/execution-paths/execution-paths.d.cts +7 -4
- package/dist/plugins/execution-paths/execution-paths.d.ts +7 -4
- package/dist/plugins/execution-paths/execution-paths.types.d.cts +21 -0
- package/dist/plugins/execution-paths/execution-paths.types.d.ts +21 -0
- package/dist/plugins/index.cjs +2 -0
- package/dist/plugins/index.cjs.map +7 -0
- package/dist/plugins/index.d.cts +19 -0
- package/dist/plugins/index.d.ts +19 -0
- package/dist/plugins/index.js +2 -0
- package/dist/plugins/index.js.map +7 -0
- package/dist/plugins/persistence/persistence.d.cts +16 -0
- package/dist/plugins/persistence/persistence.d.ts +16 -0
- package/dist/plugins/persistence/persistence.helpers.d.cts +34 -0
- package/dist/plugins/persistence/persistence.helpers.d.ts +34 -0
- package/dist/plugins/persistence/persistence.types.d.cts +63 -0
- package/dist/plugins/persistence/persistence.types.d.ts +63 -0
- package/dist/plugins/replay/replay.d.cts +7 -0
- package/dist/plugins/replay/replay.d.ts +7 -0
- package/dist/plugins/replay/replay.helpers.d.cts +22 -0
- package/dist/plugins/replay/replay.helpers.d.ts +22 -0
- package/dist/plugins/replay/replay.types.d.cts +28 -0
- package/dist/plugins/replay/replay.types.d.ts +28 -0
- package/package.json +61 -80
- package/dist/journey-builder/index.d.cts +0 -5
- package/dist/journey-builder/index.d.ts +0 -5
- package/dist/journey-builder/types.d.cts +0 -126
- package/dist/journey-builder/types.d.ts +0 -126
- package/dist/journey-machine/async-state.d.cts +0 -12
- package/dist/journey-machine/async-state.d.ts +0 -12
- package/dist/journey-machine/computed.d.cts +0 -2
- package/dist/journey-machine/computed.d.ts +0 -2
- package/dist/journey-machine/controls.d.cts +0 -17
- package/dist/journey-machine/controls.d.ts +0 -17
- package/dist/journey-machine/helpers.d.cts +0 -52
- package/dist/journey-machine/helpers.d.ts +0 -52
- package/dist/journey-machine/index.d.cts +0 -3
- package/dist/journey-machine/index.d.ts +0 -3
- package/dist/journey-machine/navigation.d.cts +0 -32
- package/dist/journey-machine/navigation.d.ts +0 -32
- package/dist/journey-machine/plugin-controller.d.cts +0 -15
- package/dist/journey-machine/plugin-controller.d.ts +0 -15
- package/dist/journey-machine/resolve-journey-definition.d.cts +0 -2
- package/dist/journey-machine/resolve-journey-definition.d.ts +0 -2
- package/dist/journey-machine/runtime.d.cts +0 -33
- package/dist/journey-machine/runtime.d.ts +0 -33
- package/dist/journey-machine/send.d.cts +0 -9
- package/dist/journey-machine/send.d.ts +0 -9
- package/dist/plugins/analytics/index.cjs +0 -2
- package/dist/plugins/analytics/index.cjs.map +0 -7
- package/dist/plugins/analytics/index.d.cts +0 -25
- package/dist/plugins/analytics/index.d.ts +0 -25
- package/dist/plugins/analytics/index.js +0 -2
- package/dist/plugins/analytics/index.js.map +0 -7
- package/dist/plugins/autosave/index.cjs +0 -2
- package/dist/plugins/autosave/index.cjs.map +0 -7
- package/dist/plugins/autosave/index.d.cts +0 -17
- package/dist/plugins/autosave/index.d.ts +0 -17
- package/dist/plugins/autosave/index.js +0 -2
- package/dist/plugins/autosave/index.js.map +0 -7
- package/dist/plugins/diagnostics/diagnostics.d.cts +0 -3
- package/dist/plugins/diagnostics/diagnostics.d.ts +0 -3
- package/dist/plugins/diagnostics/index.cjs +0 -2
- package/dist/plugins/diagnostics/index.cjs.map +0 -7
- package/dist/plugins/diagnostics/index.d.cts +0 -15
- package/dist/plugins/diagnostics/index.d.ts +0 -15
- package/dist/plugins/diagnostics/index.js +0 -2
- package/dist/plugins/diagnostics/index.js.map +0 -7
- package/dist/plugins/execution-paths/index.cjs +0 -2
- package/dist/plugins/execution-paths/index.cjs.map +0 -7
- package/dist/plugins/execution-paths/index.d.cts +0 -18
- package/dist/plugins/execution-paths/index.d.ts +0 -18
- package/dist/plugins/execution-paths/index.js +0 -2
- package/dist/plugins/execution-paths/index.js.map +0 -7
- package/dist/plugins/persistence/controller.d.cts +0 -17
- package/dist/plugins/persistence/controller.d.ts +0 -17
- package/dist/plugins/persistence/index.cjs +0 -2
- package/dist/plugins/persistence/index.cjs.map +0 -7
- package/dist/plugins/persistence/index.d.cts +0 -12
- package/dist/plugins/persistence/index.d.ts +0 -12
- package/dist/plugins/persistence/index.js +0 -2
- package/dist/plugins/persistence/index.js.map +0 -7
- package/dist/plugins/replay/index.cjs +0 -2
- package/dist/plugins/replay/index.cjs.map +0 -7
- package/dist/plugins/replay/index.d.cts +0 -27
- package/dist/plugins/replay/index.d.ts +0 -27
- package/dist/plugins/replay/index.js +0 -2
- package/dist/plugins/replay/index.js.map +0 -7
- package/dist/types/analytics.types.d.cts +0 -33
- package/dist/types/analytics.types.d.ts +0 -33
- package/dist/types/autosave.types.d.cts +0 -23
- package/dist/types/autosave.types.d.ts +0 -23
- package/dist/types/diagnostics.types.d.cts +0 -38
- package/dist/types/diagnostics.types.d.ts +0 -38
- package/dist/types/index.d.cts +0 -9
- package/dist/types/index.d.ts +0 -9
- package/dist/types/journey.types.d.cts +0 -191
- package/dist/types/journey.types.d.ts +0 -191
- package/dist/types/machine.types.d.cts +0 -104
- package/dist/types/machine.types.d.ts +0 -104
- package/dist/types/observation.types.d.cts +0 -83
- package/dist/types/observation.types.d.ts +0 -83
- package/dist/types/persistence.types.d.cts +0 -34
- package/dist/types/persistence.types.d.ts +0 -34
- package/dist/types/replay.types.d.cts +0 -35
- package/dist/types/replay.types.d.ts +0 -35
- package/dist/types/transitions.types.d.cts +0 -122
- package/dist/types/transitions.types.d.ts +0 -122
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { GraphOnEntry, GraphStepConfig } from "./graph.types.js";
|
|
2
|
+
import type { JourneyEventObject } from "../core/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* One declaration point for a journey's types.
|
|
5
|
+
*
|
|
6
|
+
* Two things need it. Steps authored in separate files have to name the shape
|
|
7
|
+
* they satisfy, and `withTypes` needs somewhere to pin types that the
|
|
8
|
+
* definition cannot infer on its own. Everything except `context`, `stepId`
|
|
9
|
+
* and `events` is optional and falls back.
|
|
10
|
+
*/
|
|
11
|
+
export type Bag = {
|
|
12
|
+
context: unknown;
|
|
13
|
+
stepId: string;
|
|
14
|
+
events: JourneyEventObject;
|
|
15
|
+
meta?: unknown;
|
|
16
|
+
handlers?: unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Result type per event name, for events declaring `run`.
|
|
19
|
+
*
|
|
20
|
+
* Load-bearing rather than a convenience: a declared `run` sits at a property
|
|
21
|
+
* position, which is not an inference site, so `commit`'s `result` is
|
|
22
|
+
* `unknown` unless it is pinned here. One result type per event name — not
|
|
23
|
+
* per (step, event) pair.
|
|
24
|
+
*/
|
|
25
|
+
results?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* `meta`, `handlers` and `results` are optional on the bag, so these must infer
|
|
29
|
+
* from an optional property. Matching `{ meta: infer T }` — a *required*
|
|
30
|
+
* property — silently skipped anyone who mirrored the constraint and wrote
|
|
31
|
+
* `meta?: MyMeta`: they got the fallback instead of their own type, and the
|
|
32
|
+
* eventual error pointed nowhere near the bag declaration. `NonNullable` strips
|
|
33
|
+
* the `undefined` that optionality adds.
|
|
34
|
+
*/
|
|
35
|
+
export type MetaOf<TBag extends Bag> = TBag extends {
|
|
36
|
+
meta?: infer TMeta;
|
|
37
|
+
} ? [TMeta] extends [undefined] ? Record<string, unknown> : NonNullable<TMeta> : Record<string, unknown>;
|
|
38
|
+
export type HandlersOf<TBag extends Bag> = TBag extends {
|
|
39
|
+
handlers?: infer THandlers;
|
|
40
|
+
} ? [THandlers] extends [undefined] ? Record<string, never> : NonNullable<THandlers> : Record<string, never>;
|
|
41
|
+
export type ResultsOf<TBag extends Bag> = TBag extends {
|
|
42
|
+
results?: infer TResults;
|
|
43
|
+
} ? [TResults] extends [undefined] ? Record<string, never> : NonNullable<TResults> : Record<string, never>;
|
|
44
|
+
/**
|
|
45
|
+
* The declared result for one event, or `unknown` when the bag pins none.
|
|
46
|
+
*
|
|
47
|
+
* The empty-default check comes first and is load-bearing: `ResultsOf` falls
|
|
48
|
+
* back to `Record<string, never>`, whose `keyof` is `string`, so without it
|
|
49
|
+
* every event name "matches" and resolves to `never` — which rejects every
|
|
50
|
+
* `run` rather than accepting any.
|
|
51
|
+
*/
|
|
52
|
+
export type ResultOf<TBag extends Bag, TType extends string> = [ResultsOf<TBag>] extends [
|
|
53
|
+
Record<string, never>
|
|
54
|
+
] ? unknown : TType extends keyof ResultsOf<TBag> ? ResultsOf<TBag>[TType] : unknown;
|
|
55
|
+
/**
|
|
56
|
+
* One step's config for a pinned bag — what a step authored in its own file
|
|
57
|
+
* annotates itself with.
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* export const loginStep: GraphStep<AuthBag> = {
|
|
61
|
+
* metadata: { label: "Login" },
|
|
62
|
+
* on: { submit: "twofa" }
|
|
63
|
+
* };
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export type GraphStep<TBag extends Bag> = Omit<GraphStepConfig<TBag["context"], TBag["stepId"], TBag["events"], MetaOf<TBag>, HandlersOf<TBag>>, "on"> & {
|
|
67
|
+
readonly on?: {
|
|
68
|
+
readonly [TType in TBag["events"]["type"]]?: GraphOnEntry<TBag["context"], TBag["stepId"], TBag["events"], HandlersOf<TBag>, MetaOf<TBag>, ResultOf<TBag, TType>, Extract<TBag["events"], {
|
|
69
|
+
type: TType;
|
|
70
|
+
}>>;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* A whole graph definition for a pinned bag, for the case `withTypes` cannot
|
|
75
|
+
* express: a standalone definition passed to the factory more than once, with
|
|
76
|
+
* different options each time.
|
|
77
|
+
*
|
|
78
|
+
* ```ts
|
|
79
|
+
* export const definition = {
|
|
80
|
+
* initial: "login",
|
|
81
|
+
* context: initialContext,
|
|
82
|
+
* steps: { login: { on: { submit: "twofa" } }, twofa: {} }
|
|
83
|
+
* } satisfies GraphDefinition<AuthBag>;
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export type GraphDefinition<TBag extends Bag> = {
|
|
87
|
+
readonly initial: TBag["stepId"];
|
|
88
|
+
readonly context: TBag["context"];
|
|
89
|
+
readonly handlers?: HandlersOf<TBag>;
|
|
90
|
+
readonly steps: Readonly<Record<TBag["stepId"], GraphStep<TBag>>>;
|
|
91
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { RuntimeStep, RuntimeTransition } from "../core/runtime.types.js";
|
|
2
|
+
import type { GraphJourneyMachine, GraphJourneyOptions, GraphStepConfig, LooseGraphDefinition } from "./graph.types.js";
|
|
3
|
+
import type { AnySendWork } from "../core/runtime.types.js";
|
|
4
|
+
import type { Bag, GraphDefinition, HandlersOf, MetaOf } from "./bag.types.js";
|
|
5
|
+
import type { AnyJourneyPlugin, CompletePayloadOf, JourneyEventObject, JourneyTerminationPayloads, TerminatePayloadOf } from "../core/types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Flattens each step's colocated `on` into the runtime's flat transition list,
|
|
8
|
+
* in declaration order, and validates every target.
|
|
9
|
+
*
|
|
10
|
+
* Order is per step, then per event, then per candidate. That differs from the
|
|
11
|
+
* old central map's global per-event order, but only for the same event
|
|
12
|
+
* declared from two steps — and candidates are filtered by `from` before order
|
|
13
|
+
* is consulted, so the selected edge is unchanged. `first-enabled-in-declared-
|
|
14
|
+
* order` is asserted directly in graph.test.ts.
|
|
15
|
+
*
|
|
16
|
+
* `from` cannot dangle here: it is the key of the step that declares the entry.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeGraphDefinition(definition: LooseGraphDefinition): {
|
|
19
|
+
stepIds: string[];
|
|
20
|
+
steps: Record<string, RuntimeStep>;
|
|
21
|
+
transitions: RuntimeTransition[];
|
|
22
|
+
eventWork: Record<string, AnySendWork>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Creates a graph journey runtime from a pure-data definition.
|
|
26
|
+
*
|
|
27
|
+
* `send(event, payload?)` is the primary verb; `goToStepById` is
|
|
28
|
+
* transition-gated sugar (fires only if an enabled transition targets that
|
|
29
|
+
* id). Timeline moves bypass transition gating — retracing a walked path is
|
|
30
|
+
* always legal. Step `onLeave` effects still run after those moves commit.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createGraphJourney<TContext, TStepId extends string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, const TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads>(definition: {
|
|
33
|
+
readonly steps: Readonly<Record<TStepId, GraphStepConfig<NoInfer<TContext>, NoInfer<TStepId>, NoInfer<TEvents>, TMeta, NoInfer<THandlers>>>>;
|
|
34
|
+
readonly initial: NoInfer<TStepId>;
|
|
35
|
+
readonly context: TContext;
|
|
36
|
+
readonly handlers?: THandlers;
|
|
37
|
+
/** @internal Builder-produced; opaque by design (see GraphJourneyDefinition). */
|
|
38
|
+
readonly eventWork?: Readonly<Record<string, unknown>>;
|
|
39
|
+
readonly $events?: TEvents;
|
|
40
|
+
readonly $payloads?: TTerminationPayloads;
|
|
41
|
+
}, options?: GraphJourneyOptions<NoInfer<THandlers>, TPlugins, NoInfer<TStepId>>): GraphJourneyMachine<TContext, TStepId, TEvents, TMeta, TPlugins, CompletePayloadOf<TTerminationPayloads>, TerminatePayloadOf<TTerminationPayloads>>;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a graph journey factory with its types pinned up front instead of
|
|
44
|
+
* inferred.
|
|
45
|
+
*
|
|
46
|
+
* Reach for it when the definition cannot supply a type on its own —
|
|
47
|
+
* `handlers`, a `meta` shape, or a `run` result, none of which sit at an
|
|
48
|
+
* inference site — or when the same bag is shared by steps authored in
|
|
49
|
+
* separate files. The definition is then *checked* against the bag rather than
|
|
50
|
+
* read for types.
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* const login = withGraphTypes<AuthBag>()({
|
|
54
|
+
* initial: "login",
|
|
55
|
+
* context: initialContext,
|
|
56
|
+
* steps: { login: { on: { submit: "twofa" } }, twofa: {} }
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* Plain `createGraphJourney(definition)` stays the default: step ids infer from
|
|
61
|
+
* the `steps` keys and event names from the `on` keys.
|
|
62
|
+
*/
|
|
63
|
+
export declare const withGraphTypes: <TBag extends Bag>() => <const TPlugins extends readonly AnyJourneyPlugin[] = readonly []>(definition: GraphDefinition<TBag>, options?: GraphJourneyOptions<HandlersOf<TBag>, TPlugins, TBag["stepId"]>) => GraphJourneyMachine<TBag["context"], TBag["stepId"], TBag["events"], MetaOf<TBag>, TPlugins>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { RuntimeStep, RuntimeTransition } from "../core/runtime.types.js";
|
|
2
|
+
import type { GraphJourneyMachine, GraphJourneyOptions, GraphStepConfig, LooseGraphDefinition } from "./graph.types.js";
|
|
3
|
+
import type { AnySendWork } from "../core/runtime.types.js";
|
|
4
|
+
import type { Bag, GraphDefinition, HandlersOf, MetaOf } from "./bag.types.js";
|
|
5
|
+
import type { AnyJourneyPlugin, CompletePayloadOf, JourneyEventObject, JourneyTerminationPayloads, TerminatePayloadOf } from "../core/types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Flattens each step's colocated `on` into the runtime's flat transition list,
|
|
8
|
+
* in declaration order, and validates every target.
|
|
9
|
+
*
|
|
10
|
+
* Order is per step, then per event, then per candidate. That differs from the
|
|
11
|
+
* old central map's global per-event order, but only for the same event
|
|
12
|
+
* declared from two steps — and candidates are filtered by `from` before order
|
|
13
|
+
* is consulted, so the selected edge is unchanged. `first-enabled-in-declared-
|
|
14
|
+
* order` is asserted directly in graph.test.ts.
|
|
15
|
+
*
|
|
16
|
+
* `from` cannot dangle here: it is the key of the step that declares the entry.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeGraphDefinition(definition: LooseGraphDefinition): {
|
|
19
|
+
stepIds: string[];
|
|
20
|
+
steps: Record<string, RuntimeStep>;
|
|
21
|
+
transitions: RuntimeTransition[];
|
|
22
|
+
eventWork: Record<string, AnySendWork>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Creates a graph journey runtime from a pure-data definition.
|
|
26
|
+
*
|
|
27
|
+
* `send(event, payload?)` is the primary verb; `goToStepById` is
|
|
28
|
+
* transition-gated sugar (fires only if an enabled transition targets that
|
|
29
|
+
* id). Timeline moves bypass transition gating — retracing a walked path is
|
|
30
|
+
* always legal. Step `onLeave` effects still run after those moves commit.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createGraphJourney<TContext, TStepId extends string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, const TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads>(definition: {
|
|
33
|
+
readonly steps: Readonly<Record<TStepId, GraphStepConfig<NoInfer<TContext>, NoInfer<TStepId>, NoInfer<TEvents>, TMeta, NoInfer<THandlers>>>>;
|
|
34
|
+
readonly initial: NoInfer<TStepId>;
|
|
35
|
+
readonly context: TContext;
|
|
36
|
+
readonly handlers?: THandlers;
|
|
37
|
+
/** @internal Builder-produced; opaque by design (see GraphJourneyDefinition). */
|
|
38
|
+
readonly eventWork?: Readonly<Record<string, unknown>>;
|
|
39
|
+
readonly $events?: TEvents;
|
|
40
|
+
readonly $payloads?: TTerminationPayloads;
|
|
41
|
+
}, options?: GraphJourneyOptions<NoInfer<THandlers>, TPlugins, NoInfer<TStepId>>): GraphJourneyMachine<TContext, TStepId, TEvents, TMeta, TPlugins, CompletePayloadOf<TTerminationPayloads>, TerminatePayloadOf<TTerminationPayloads>>;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a graph journey factory with its types pinned up front instead of
|
|
44
|
+
* inferred.
|
|
45
|
+
*
|
|
46
|
+
* Reach for it when the definition cannot supply a type on its own —
|
|
47
|
+
* `handlers`, a `meta` shape, or a `run` result, none of which sit at an
|
|
48
|
+
* inference site — or when the same bag is shared by steps authored in
|
|
49
|
+
* separate files. The definition is then *checked* against the bag rather than
|
|
50
|
+
* read for types.
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* const login = withGraphTypes<AuthBag>()({
|
|
54
|
+
* initial: "login",
|
|
55
|
+
* context: initialContext,
|
|
56
|
+
* steps: { login: { on: { submit: "twofa" } }, twofa: {} }
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* Plain `createGraphJourney(definition)` stays the default: step ids infer from
|
|
61
|
+
* the `steps` keys and event names from the `on` keys.
|
|
62
|
+
*/
|
|
63
|
+
export declare const withGraphTypes: <TBag extends Bag>() => <const TPlugins extends readonly AnyJourneyPlugin[] = readonly []>(definition: GraphDefinition<TBag>, options?: GraphJourneyOptions<HandlersOf<TBag>, TPlugins, TBag["stepId"]>) => GraphJourneyMachine<TBag["context"], TBag["stepId"], TBag["events"], MetaOf<TBag>, TPlugins>;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type { AnySendWork, RuntimeStep, RuntimeTransition } from "../core/runtime.types.js";
|
|
2
|
+
import type { AnyJourneyPlugin, ContextUpdater, GraphSnapshot, JourneyEventObject, JourneyEventPayload, JourneyMachineBase, JourneyRuntimeOptions, JourneyTerminationPayloads, NavigationResult, OnEnterHook, OnLeaveHook, PluginApis, StepHookArgs } from "../core/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Guards are sync and pure: they are evaluated during snapshot derivation to
|
|
5
|
+
* compute `availableEvents`/`availableSteps`, so they receive no event payload
|
|
6
|
+
* and cannot be async. Caller-driven async validation belongs in navigation work.
|
|
7
|
+
*/
|
|
8
|
+
export type TransitionGuard<TContext, THandlers> = (args: {
|
|
9
|
+
readonly context: TContext;
|
|
10
|
+
readonly handlers: THandlers;
|
|
11
|
+
}) => boolean;
|
|
12
|
+
export type GraphHookArgs<TContext, TStepId extends string, TEvents extends JourneyEventObject, TMeta = Record<string, unknown>> = StepHookArgs<TContext, TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>>;
|
|
13
|
+
/** One colocated candidate; `from` is the step that declares it. */
|
|
14
|
+
export type GraphTransition<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, TTrigger extends JourneyEventObject = TEvents> = {
|
|
15
|
+
readonly to: TStepId;
|
|
16
|
+
readonly when?: TransitionGuard<TContext, THandlers>;
|
|
17
|
+
/**
|
|
18
|
+
* Names this edge. Several candidates on one event differ only by guard, so
|
|
19
|
+
* the name is what tells them apart in timeout messages, step-hook
|
|
20
|
+
* `transition` args, and the plugin structure view — a priority index says
|
|
21
|
+
* where the edge sits, not what it is.
|
|
22
|
+
*/
|
|
23
|
+
readonly label?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Budget for this edge's `onTransition`, overriding `defaultTimeoutMs`. One
|
|
26
|
+
* edge waiting on a slow third party is not a reason to widen the budget for
|
|
27
|
+
* every other edge in the graph.
|
|
28
|
+
*/
|
|
29
|
+
readonly timeoutMs?: number;
|
|
30
|
+
/** Async effect, post-commit, cannot cancel; a throw is handled like an `onEnter` throw. */
|
|
31
|
+
readonly onTransition?: (args: Omit<GraphHookArgs<TContext, TStepId, TEvents, TMeta>, "event"> & {
|
|
32
|
+
readonly event: TTrigger | null;
|
|
33
|
+
}) => void | Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* What one event maps to on a step. Three forms, discriminated at the top
|
|
37
|
+
* level so nothing has to sniff inside a candidate:
|
|
38
|
+
*
|
|
39
|
+
* - a step id — one unguarded candidate;
|
|
40
|
+
* - an array — ordered candidates, first enabled wins;
|
|
41
|
+
* - an object — async the machine owns, whose staged context the candidates
|
|
42
|
+
* are then evaluated against.
|
|
43
|
+
*
|
|
44
|
+
* A guarded lone candidate is `[{ to, when }]`: the extra brackets read
|
|
45
|
+
* honestly, since a guarded lone candidate means the event can fail.
|
|
46
|
+
*/
|
|
47
|
+
export type GraphOnEntry<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, TResult = unknown, TTrigger extends JourneyEventObject = TEvents> = TStepId | readonly GraphTransition<TContext, TStepId, TEvents, THandlers, TMeta, TTrigger>[] | {
|
|
48
|
+
readonly run: (args: SendWorkArgs<TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>, THandlers>) => TResult | Promise<TResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Declared as a method rather than a function-typed property on purpose.
|
|
51
|
+
* Method parameters are checked bivariantly, which is what lets a
|
|
52
|
+
* `commit` whose `result` is a concrete type sit in this slot when the
|
|
53
|
+
* slot's own `TResult` is the `unknown` fallback — the escape hatch the
|
|
54
|
+
* `work()` helper relies on to hand back an inferred config.
|
|
55
|
+
*/
|
|
56
|
+
commit?(args: SendWorkArgs<TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>, THandlers> & {
|
|
57
|
+
readonly result: TResult;
|
|
58
|
+
readonly updateContext: (updater: ContextUpdater<TContext>) => void;
|
|
59
|
+
}): void;
|
|
60
|
+
/** Names this work in timeout and error messages. */
|
|
61
|
+
readonly label?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Budget for `run`, overriding `defaultTimeoutMs`. This is the edge that
|
|
64
|
+
* usually needs its own: `run` is where a definition's third-party call
|
|
65
|
+
* lives.
|
|
66
|
+
*/
|
|
67
|
+
readonly timeoutMs?: number;
|
|
68
|
+
readonly candidates: readonly GraphTransition<TContext, TStepId, TEvents, THandlers, TMeta, TTrigger>[];
|
|
69
|
+
};
|
|
70
|
+
export type GraphStepConfig<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, TMeta = Record<string, unknown>, THandlers = unknown> = {
|
|
71
|
+
readonly metadata?: TMeta;
|
|
72
|
+
/** Outgoing transitions, keyed by event. */
|
|
73
|
+
readonly on?: {
|
|
74
|
+
readonly [TType in TEvents["type"]]?: GraphOnEntry<TContext, TStepId, TEvents, THandlers, TMeta, unknown, Extract<TEvents, {
|
|
75
|
+
type: TType;
|
|
76
|
+
}>>;
|
|
77
|
+
};
|
|
78
|
+
readonly onEnter?: OnEnterHook<TContext, TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>>;
|
|
79
|
+
readonly onLeave?: OnLeaveHook<TContext, TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>>;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Pure-data graph definition (also what the builder's `build()` produces).
|
|
83
|
+
* `$events` is a phantom carrier for the declared event union — never set at
|
|
84
|
+
* runtime.
|
|
85
|
+
*/
|
|
86
|
+
export type GraphJourneyDefinition<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads> = {
|
|
87
|
+
readonly steps: Readonly<Record<TStepId, GraphStepConfig<TContext, TStepId, TEvents, TMeta, THandlers>>>;
|
|
88
|
+
readonly initial: TStepId;
|
|
89
|
+
readonly context: TContext;
|
|
90
|
+
readonly handlers?: THandlers;
|
|
91
|
+
/**
|
|
92
|
+
* Work declared on an event, collected by `normalizeGraphDefinition`.
|
|
93
|
+
*
|
|
94
|
+
* @internal Its keys are a private encoding of the (origin step, event) pair
|
|
95
|
+
* and its values are internal work shapes. Typed as `unknown` on purpose so
|
|
96
|
+
* neither is nameable from the public surface: hand this field straight back
|
|
97
|
+
* to a factory, never construct or read it.
|
|
98
|
+
*/
|
|
99
|
+
readonly eventWork?: Readonly<Record<string, unknown>>;
|
|
100
|
+
/**
|
|
101
|
+
* Phantom type carrier — never read at runtime. Names the event union, which
|
|
102
|
+
* the definition's other fields cannot infer on their own.
|
|
103
|
+
*/
|
|
104
|
+
readonly $events?: TEvents;
|
|
105
|
+
/**
|
|
106
|
+
* Phantom type carrier — never read at runtime. Names the completion and
|
|
107
|
+
* termination payload types, so `controls.complete`/`terminate` and
|
|
108
|
+
* `snapshot.machine.outcome` are typed instead of `unknown`.
|
|
109
|
+
*/
|
|
110
|
+
readonly $payloads?: TTerminationPayloads;
|
|
111
|
+
};
|
|
112
|
+
export type GraphJourneyOptions<THandlers = unknown, TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TStepId extends string = string> = JourneyRuntimeOptions<TPlugins, TStepId> & {
|
|
113
|
+
/** Overrides the definition's handlers — one definition serves app and tests. */
|
|
114
|
+
handlers?: THandlers;
|
|
115
|
+
};
|
|
116
|
+
export type SendArgs<TEvents extends JourneyEventObject, TType extends TEvents["type"]> = JourneyEventPayload<TEvents, TType> extends undefined ? [] : [payload: JourneyEventPayload<TEvents, TType>];
|
|
117
|
+
/**
|
|
118
|
+
* Arguments for work attached to a `send`.
|
|
119
|
+
*
|
|
120
|
+
* There is no `to`: the work runs *before* routing, so no target has been
|
|
121
|
+
* chosen yet. `handlers` is threaded through because this is the async that a
|
|
122
|
+
* definition owns, and injected clients reach it no other way — step hooks
|
|
123
|
+
* deliberately do not receive handlers.
|
|
124
|
+
*/
|
|
125
|
+
export type SendWorkArgs<TStepId extends string, TEvents extends JourneyEventObject, TSnap, THandlers> = {
|
|
126
|
+
readonly snapshot: TSnap;
|
|
127
|
+
readonly from: TStepId;
|
|
128
|
+
readonly event: TEvents;
|
|
129
|
+
readonly handlers: THandlers;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Async attached to an event, run before the guards choose an edge.
|
|
133
|
+
*
|
|
134
|
+
* `run` is awaited while the machine holds its position. `commit` then stages
|
|
135
|
+
* context synchronously, and the guards are evaluated against that staged
|
|
136
|
+
* context — so the work supplies the facts routing is decided from without
|
|
137
|
+
* guards ever becoming async. If `run` throws, or no candidate is enabled once
|
|
138
|
+
* the context is staged, nothing commits and the machine stays put.
|
|
139
|
+
*/
|
|
140
|
+
export type SendWork<TContext, TStepId extends string, TEvents extends JourneyEventObject, TSnap, THandlers = unknown, TResult = void> = {
|
|
141
|
+
readonly run: (args: SendWorkArgs<TStepId, TEvents, TSnap, THandlers>) => TResult | Promise<TResult>;
|
|
142
|
+
readonly commit?: (args: SendWorkArgs<TStepId, TEvents, TSnap, THandlers> & {
|
|
143
|
+
readonly result: TResult;
|
|
144
|
+
readonly updateContext: (updater: ContextUpdater<TContext>) => void;
|
|
145
|
+
}) => void;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Declared events get exact payload tuples; undeclared events fall back to
|
|
149
|
+
* `(type, payload?)`.
|
|
150
|
+
*
|
|
151
|
+
* `send` takes no work: async that decides an event's outcome is declared on
|
|
152
|
+
* the step, which is the graph's one channel for it.
|
|
153
|
+
*/
|
|
154
|
+
export type SendVerb<TStepId extends string, TEvents extends JourneyEventObject> = JourneyEventObject extends TEvents ? (type: string, payload?: unknown) => Promise<NavigationResult<TStepId>> : <TType extends TEvents["type"]>(type: TType, ...payload: SendArgs<TEvents, TType>) => Promise<NavigationResult<TStepId>>;
|
|
155
|
+
export type GraphJourneyMachine<TContext, TStepId extends string, TEvents extends JourneyEventObject = JourneyEventObject, TMeta = Record<string, unknown>, TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TCompletePayload = unknown, TTerminatePayload = unknown> = JourneyMachineBase<TContext, TStepId, GraphSnapshot<TContext, TStepId, TMeta, TEvents, TCompletePayload, TTerminatePayload>, TCompletePayload, TTerminatePayload> & {
|
|
156
|
+
/**
|
|
157
|
+
* The graph's primary verb — its presence is itself the machine-type
|
|
158
|
+
* discriminant (linear has no events).
|
|
159
|
+
*/
|
|
160
|
+
send: SendVerb<TStepId, TEvents>;
|
|
161
|
+
readonly plugins: PluginApis<TPlugins>;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Internal, generics-erased view of one colocated `on` entry. The three forms
|
|
165
|
+
* are discriminated structurally at the top level — string, array, object —
|
|
166
|
+
* so normalization never has to sniff inside a candidate.
|
|
167
|
+
*/
|
|
168
|
+
export type LooseOnEntry = string | readonly LooseTransition[] | {
|
|
169
|
+
readonly run: AnySendWork["run"];
|
|
170
|
+
readonly commit?: AnySendWork["commit"];
|
|
171
|
+
readonly label?: string;
|
|
172
|
+
readonly timeoutMs?: number;
|
|
173
|
+
readonly candidates: readonly LooseTransition[];
|
|
174
|
+
};
|
|
175
|
+
/** A colocated candidate: `from` is the step that declares it. */
|
|
176
|
+
export type LooseTransition = {
|
|
177
|
+
readonly to: string;
|
|
178
|
+
readonly when?: NonNullable<RuntimeTransition["when"]>;
|
|
179
|
+
readonly onTransition?: NonNullable<RuntimeTransition["onTransition"]>;
|
|
180
|
+
readonly label?: string;
|
|
181
|
+
readonly timeoutMs?: number;
|
|
182
|
+
};
|
|
183
|
+
/** Internal, generics-erased view of a definition used by normalization. */
|
|
184
|
+
export type LooseGraphDefinition = {
|
|
185
|
+
readonly steps: Readonly<Record<string, GraphStepConfig & {
|
|
186
|
+
readonly on?: Readonly<Record<string, LooseOnEntry>>;
|
|
187
|
+
}>>;
|
|
188
|
+
readonly initial: string;
|
|
189
|
+
readonly context: unknown;
|
|
190
|
+
readonly handlers?: unknown;
|
|
191
|
+
};
|
|
192
|
+
export type MutableRuntimeStep = {
|
|
193
|
+
metadata: unknown;
|
|
194
|
+
onEnter?: NonNullable<RuntimeStep["onEnter"]>;
|
|
195
|
+
onLeave?: NonNullable<RuntimeStep["onLeave"]>;
|
|
196
|
+
};
|
|
197
|
+
export type MutableSendWork = {
|
|
198
|
+
run: AnySendWork["run"];
|
|
199
|
+
commit?: NonNullable<AnySendWork["commit"]>;
|
|
200
|
+
label?: string;
|
|
201
|
+
timeoutMs?: number;
|
|
202
|
+
};
|
|
203
|
+
export type MutableRuntimeTransition = {
|
|
204
|
+
event: string;
|
|
205
|
+
from: string;
|
|
206
|
+
to: string;
|
|
207
|
+
index: number;
|
|
208
|
+
label?: string;
|
|
209
|
+
timeoutMs?: number;
|
|
210
|
+
when?: NonNullable<RuntimeTransition["when"]>;
|
|
211
|
+
onTransition?: NonNullable<RuntimeTransition["onTransition"]>;
|
|
212
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type { AnySendWork, RuntimeStep, RuntimeTransition } from "../core/runtime.types.js";
|
|
2
|
+
import type { AnyJourneyPlugin, ContextUpdater, GraphSnapshot, JourneyEventObject, JourneyEventPayload, JourneyMachineBase, JourneyRuntimeOptions, JourneyTerminationPayloads, NavigationResult, OnEnterHook, OnLeaveHook, PluginApis, StepHookArgs } from "../core/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Guards are sync and pure: they are evaluated during snapshot derivation to
|
|
5
|
+
* compute `availableEvents`/`availableSteps`, so they receive no event payload
|
|
6
|
+
* and cannot be async. Caller-driven async validation belongs in navigation work.
|
|
7
|
+
*/
|
|
8
|
+
export type TransitionGuard<TContext, THandlers> = (args: {
|
|
9
|
+
readonly context: TContext;
|
|
10
|
+
readonly handlers: THandlers;
|
|
11
|
+
}) => boolean;
|
|
12
|
+
export type GraphHookArgs<TContext, TStepId extends string, TEvents extends JourneyEventObject, TMeta = Record<string, unknown>> = StepHookArgs<TContext, TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>>;
|
|
13
|
+
/** One colocated candidate; `from` is the step that declares it. */
|
|
14
|
+
export type GraphTransition<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, TTrigger extends JourneyEventObject = TEvents> = {
|
|
15
|
+
readonly to: TStepId;
|
|
16
|
+
readonly when?: TransitionGuard<TContext, THandlers>;
|
|
17
|
+
/**
|
|
18
|
+
* Names this edge. Several candidates on one event differ only by guard, so
|
|
19
|
+
* the name is what tells them apart in timeout messages, step-hook
|
|
20
|
+
* `transition` args, and the plugin structure view — a priority index says
|
|
21
|
+
* where the edge sits, not what it is.
|
|
22
|
+
*/
|
|
23
|
+
readonly label?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Budget for this edge's `onTransition`, overriding `defaultTimeoutMs`. One
|
|
26
|
+
* edge waiting on a slow third party is not a reason to widen the budget for
|
|
27
|
+
* every other edge in the graph.
|
|
28
|
+
*/
|
|
29
|
+
readonly timeoutMs?: number;
|
|
30
|
+
/** Async effect, post-commit, cannot cancel; a throw is handled like an `onEnter` throw. */
|
|
31
|
+
readonly onTransition?: (args: Omit<GraphHookArgs<TContext, TStepId, TEvents, TMeta>, "event"> & {
|
|
32
|
+
readonly event: TTrigger | null;
|
|
33
|
+
}) => void | Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* What one event maps to on a step. Three forms, discriminated at the top
|
|
37
|
+
* level so nothing has to sniff inside a candidate:
|
|
38
|
+
*
|
|
39
|
+
* - a step id — one unguarded candidate;
|
|
40
|
+
* - an array — ordered candidates, first enabled wins;
|
|
41
|
+
* - an object — async the machine owns, whose staged context the candidates
|
|
42
|
+
* are then evaluated against.
|
|
43
|
+
*
|
|
44
|
+
* A guarded lone candidate is `[{ to, when }]`: the extra brackets read
|
|
45
|
+
* honestly, since a guarded lone candidate means the event can fail.
|
|
46
|
+
*/
|
|
47
|
+
export type GraphOnEntry<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, TResult = unknown, TTrigger extends JourneyEventObject = TEvents> = TStepId | readonly GraphTransition<TContext, TStepId, TEvents, THandlers, TMeta, TTrigger>[] | {
|
|
48
|
+
readonly run: (args: SendWorkArgs<TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>, THandlers>) => TResult | Promise<TResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Declared as a method rather than a function-typed property on purpose.
|
|
51
|
+
* Method parameters are checked bivariantly, which is what lets a
|
|
52
|
+
* `commit` whose `result` is a concrete type sit in this slot when the
|
|
53
|
+
* slot's own `TResult` is the `unknown` fallback — the escape hatch the
|
|
54
|
+
* `work()` helper relies on to hand back an inferred config.
|
|
55
|
+
*/
|
|
56
|
+
commit?(args: SendWorkArgs<TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>, THandlers> & {
|
|
57
|
+
readonly result: TResult;
|
|
58
|
+
readonly updateContext: (updater: ContextUpdater<TContext>) => void;
|
|
59
|
+
}): void;
|
|
60
|
+
/** Names this work in timeout and error messages. */
|
|
61
|
+
readonly label?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Budget for `run`, overriding `defaultTimeoutMs`. This is the edge that
|
|
64
|
+
* usually needs its own: `run` is where a definition's third-party call
|
|
65
|
+
* lives.
|
|
66
|
+
*/
|
|
67
|
+
readonly timeoutMs?: number;
|
|
68
|
+
readonly candidates: readonly GraphTransition<TContext, TStepId, TEvents, THandlers, TMeta, TTrigger>[];
|
|
69
|
+
};
|
|
70
|
+
export type GraphStepConfig<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, TMeta = Record<string, unknown>, THandlers = unknown> = {
|
|
71
|
+
readonly metadata?: TMeta;
|
|
72
|
+
/** Outgoing transitions, keyed by event. */
|
|
73
|
+
readonly on?: {
|
|
74
|
+
readonly [TType in TEvents["type"]]?: GraphOnEntry<TContext, TStepId, TEvents, THandlers, TMeta, unknown, Extract<TEvents, {
|
|
75
|
+
type: TType;
|
|
76
|
+
}>>;
|
|
77
|
+
};
|
|
78
|
+
readonly onEnter?: OnEnterHook<TContext, TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>>;
|
|
79
|
+
readonly onLeave?: OnLeaveHook<TContext, TStepId, TEvents, GraphSnapshot<TContext, TStepId, TMeta, TEvents>>;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Pure-data graph definition (also what the builder's `build()` produces).
|
|
83
|
+
* `$events` is a phantom carrier for the declared event union — never set at
|
|
84
|
+
* runtime.
|
|
85
|
+
*/
|
|
86
|
+
export type GraphJourneyDefinition<TContext = unknown, TStepId extends string = string, TEvents extends JourneyEventObject = JourneyEventObject, THandlers = unknown, TMeta = Record<string, unknown>, TTerminationPayloads extends JourneyTerminationPayloads = JourneyTerminationPayloads> = {
|
|
87
|
+
readonly steps: Readonly<Record<TStepId, GraphStepConfig<TContext, TStepId, TEvents, TMeta, THandlers>>>;
|
|
88
|
+
readonly initial: TStepId;
|
|
89
|
+
readonly context: TContext;
|
|
90
|
+
readonly handlers?: THandlers;
|
|
91
|
+
/**
|
|
92
|
+
* Work declared on an event, collected by `normalizeGraphDefinition`.
|
|
93
|
+
*
|
|
94
|
+
* @internal Its keys are a private encoding of the (origin step, event) pair
|
|
95
|
+
* and its values are internal work shapes. Typed as `unknown` on purpose so
|
|
96
|
+
* neither is nameable from the public surface: hand this field straight back
|
|
97
|
+
* to a factory, never construct or read it.
|
|
98
|
+
*/
|
|
99
|
+
readonly eventWork?: Readonly<Record<string, unknown>>;
|
|
100
|
+
/**
|
|
101
|
+
* Phantom type carrier — never read at runtime. Names the event union, which
|
|
102
|
+
* the definition's other fields cannot infer on their own.
|
|
103
|
+
*/
|
|
104
|
+
readonly $events?: TEvents;
|
|
105
|
+
/**
|
|
106
|
+
* Phantom type carrier — never read at runtime. Names the completion and
|
|
107
|
+
* termination payload types, so `controls.complete`/`terminate` and
|
|
108
|
+
* `snapshot.machine.outcome` are typed instead of `unknown`.
|
|
109
|
+
*/
|
|
110
|
+
readonly $payloads?: TTerminationPayloads;
|
|
111
|
+
};
|
|
112
|
+
export type GraphJourneyOptions<THandlers = unknown, TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TStepId extends string = string> = JourneyRuntimeOptions<TPlugins, TStepId> & {
|
|
113
|
+
/** Overrides the definition's handlers — one definition serves app and tests. */
|
|
114
|
+
handlers?: THandlers;
|
|
115
|
+
};
|
|
116
|
+
export type SendArgs<TEvents extends JourneyEventObject, TType extends TEvents["type"]> = JourneyEventPayload<TEvents, TType> extends undefined ? [] : [payload: JourneyEventPayload<TEvents, TType>];
|
|
117
|
+
/**
|
|
118
|
+
* Arguments for work attached to a `send`.
|
|
119
|
+
*
|
|
120
|
+
* There is no `to`: the work runs *before* routing, so no target has been
|
|
121
|
+
* chosen yet. `handlers` is threaded through because this is the async that a
|
|
122
|
+
* definition owns, and injected clients reach it no other way — step hooks
|
|
123
|
+
* deliberately do not receive handlers.
|
|
124
|
+
*/
|
|
125
|
+
export type SendWorkArgs<TStepId extends string, TEvents extends JourneyEventObject, TSnap, THandlers> = {
|
|
126
|
+
readonly snapshot: TSnap;
|
|
127
|
+
readonly from: TStepId;
|
|
128
|
+
readonly event: TEvents;
|
|
129
|
+
readonly handlers: THandlers;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Async attached to an event, run before the guards choose an edge.
|
|
133
|
+
*
|
|
134
|
+
* `run` is awaited while the machine holds its position. `commit` then stages
|
|
135
|
+
* context synchronously, and the guards are evaluated against that staged
|
|
136
|
+
* context — so the work supplies the facts routing is decided from without
|
|
137
|
+
* guards ever becoming async. If `run` throws, or no candidate is enabled once
|
|
138
|
+
* the context is staged, nothing commits and the machine stays put.
|
|
139
|
+
*/
|
|
140
|
+
export type SendWork<TContext, TStepId extends string, TEvents extends JourneyEventObject, TSnap, THandlers = unknown, TResult = void> = {
|
|
141
|
+
readonly run: (args: SendWorkArgs<TStepId, TEvents, TSnap, THandlers>) => TResult | Promise<TResult>;
|
|
142
|
+
readonly commit?: (args: SendWorkArgs<TStepId, TEvents, TSnap, THandlers> & {
|
|
143
|
+
readonly result: TResult;
|
|
144
|
+
readonly updateContext: (updater: ContextUpdater<TContext>) => void;
|
|
145
|
+
}) => void;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Declared events get exact payload tuples; undeclared events fall back to
|
|
149
|
+
* `(type, payload?)`.
|
|
150
|
+
*
|
|
151
|
+
* `send` takes no work: async that decides an event's outcome is declared on
|
|
152
|
+
* the step, which is the graph's one channel for it.
|
|
153
|
+
*/
|
|
154
|
+
export type SendVerb<TStepId extends string, TEvents extends JourneyEventObject> = JourneyEventObject extends TEvents ? (type: string, payload?: unknown) => Promise<NavigationResult<TStepId>> : <TType extends TEvents["type"]>(type: TType, ...payload: SendArgs<TEvents, TType>) => Promise<NavigationResult<TStepId>>;
|
|
155
|
+
export type GraphJourneyMachine<TContext, TStepId extends string, TEvents extends JourneyEventObject = JourneyEventObject, TMeta = Record<string, unknown>, TPlugins extends readonly AnyJourneyPlugin[] = readonly [], TCompletePayload = unknown, TTerminatePayload = unknown> = JourneyMachineBase<TContext, TStepId, GraphSnapshot<TContext, TStepId, TMeta, TEvents, TCompletePayload, TTerminatePayload>, TCompletePayload, TTerminatePayload> & {
|
|
156
|
+
/**
|
|
157
|
+
* The graph's primary verb — its presence is itself the machine-type
|
|
158
|
+
* discriminant (linear has no events).
|
|
159
|
+
*/
|
|
160
|
+
send: SendVerb<TStepId, TEvents>;
|
|
161
|
+
readonly plugins: PluginApis<TPlugins>;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Internal, generics-erased view of one colocated `on` entry. The three forms
|
|
165
|
+
* are discriminated structurally at the top level — string, array, object —
|
|
166
|
+
* so normalization never has to sniff inside a candidate.
|
|
167
|
+
*/
|
|
168
|
+
export type LooseOnEntry = string | readonly LooseTransition[] | {
|
|
169
|
+
readonly run: AnySendWork["run"];
|
|
170
|
+
readonly commit?: AnySendWork["commit"];
|
|
171
|
+
readonly label?: string;
|
|
172
|
+
readonly timeoutMs?: number;
|
|
173
|
+
readonly candidates: readonly LooseTransition[];
|
|
174
|
+
};
|
|
175
|
+
/** A colocated candidate: `from` is the step that declares it. */
|
|
176
|
+
export type LooseTransition = {
|
|
177
|
+
readonly to: string;
|
|
178
|
+
readonly when?: NonNullable<RuntimeTransition["when"]>;
|
|
179
|
+
readonly onTransition?: NonNullable<RuntimeTransition["onTransition"]>;
|
|
180
|
+
readonly label?: string;
|
|
181
|
+
readonly timeoutMs?: number;
|
|
182
|
+
};
|
|
183
|
+
/** Internal, generics-erased view of a definition used by normalization. */
|
|
184
|
+
export type LooseGraphDefinition = {
|
|
185
|
+
readonly steps: Readonly<Record<string, GraphStepConfig & {
|
|
186
|
+
readonly on?: Readonly<Record<string, LooseOnEntry>>;
|
|
187
|
+
}>>;
|
|
188
|
+
readonly initial: string;
|
|
189
|
+
readonly context: unknown;
|
|
190
|
+
readonly handlers?: unknown;
|
|
191
|
+
};
|
|
192
|
+
export type MutableRuntimeStep = {
|
|
193
|
+
metadata: unknown;
|
|
194
|
+
onEnter?: NonNullable<RuntimeStep["onEnter"]>;
|
|
195
|
+
onLeave?: NonNullable<RuntimeStep["onLeave"]>;
|
|
196
|
+
};
|
|
197
|
+
export type MutableSendWork = {
|
|
198
|
+
run: AnySendWork["run"];
|
|
199
|
+
commit?: NonNullable<AnySendWork["commit"]>;
|
|
200
|
+
label?: string;
|
|
201
|
+
timeoutMs?: number;
|
|
202
|
+
};
|
|
203
|
+
export type MutableRuntimeTransition = {
|
|
204
|
+
event: string;
|
|
205
|
+
from: string;
|
|
206
|
+
to: string;
|
|
207
|
+
index: number;
|
|
208
|
+
label?: string;
|
|
209
|
+
timeoutMs?: number;
|
|
210
|
+
when?: NonNullable<RuntimeTransition["when"]>;
|
|
211
|
+
onTransition?: NonNullable<RuntimeTransition["onTransition"]>;
|
|
212
|
+
};
|