@rytejs/core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +45 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +240 -22
- package/dist/index.d.ts +240 -22
- package/dist/index.js +45 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,33 +1,52 @@
|
|
|
1
1
|
import { ZodType, z } from 'zod';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Shape of the configuration object passed to {@link defineWorkflow}.
|
|
5
|
+
*/
|
|
3
6
|
interface WorkflowConfig {
|
|
7
|
+
/** Optional version number for schema migrations. Defaults to 1. */
|
|
4
8
|
modelVersion?: number;
|
|
9
|
+
/** Record of state names to Zod schemas defining their data shape. */
|
|
5
10
|
states: Record<string, ZodType>;
|
|
11
|
+
/** Record of command names to Zod schemas defining their payload shape. */
|
|
6
12
|
commands: Record<string, ZodType>;
|
|
13
|
+
/** Record of event names to Zod schemas defining their data shape. */
|
|
7
14
|
events: Record<string, ZodType>;
|
|
15
|
+
/** Record of error codes to Zod schemas defining their data shape. */
|
|
8
16
|
errors: Record<string, ZodType>;
|
|
9
17
|
}
|
|
10
18
|
type StateNames<T extends WorkflowConfig> = keyof T["states"] & string;
|
|
11
19
|
type CommandNames<T extends WorkflowConfig> = keyof T["commands"] & string;
|
|
12
20
|
type EventNames<T extends WorkflowConfig> = keyof T["events"] & string;
|
|
13
21
|
type ErrorCodes<T extends WorkflowConfig> = keyof T["errors"] & string;
|
|
22
|
+
/** Infers the data type for a given state. */
|
|
14
23
|
type StateData<T extends WorkflowConfig, S extends StateNames<T>> = T["states"][S] extends ZodType ? z.infer<T["states"][S]> : never;
|
|
24
|
+
/** Infers the payload type for a given command. */
|
|
15
25
|
type CommandPayload<T extends WorkflowConfig, C extends CommandNames<T>> = T["commands"][C] extends ZodType ? z.infer<T["commands"][C]> : never;
|
|
26
|
+
/** Infers the data type for a given event. */
|
|
16
27
|
type EventData<T extends WorkflowConfig, E extends EventNames<T>> = T["events"][E] extends ZodType ? z.infer<T["events"][E]> : never;
|
|
28
|
+
/** Infers the data type for a given error code. */
|
|
17
29
|
type ErrorData<T extends WorkflowConfig, C extends ErrorCodes<T>> = T["errors"][C] extends ZodType ? z.infer<T["errors"][C]> : never;
|
|
18
30
|
/** Workflow narrowed to a specific known state. */
|
|
19
31
|
interface WorkflowOf<TConfig extends WorkflowConfig, S extends StateNames<TConfig>> {
|
|
32
|
+
/** Unique workflow instance identifier. */
|
|
20
33
|
readonly id: string;
|
|
34
|
+
/** Name of the workflow definition this instance belongs to. */
|
|
21
35
|
readonly definitionName: string;
|
|
36
|
+
/** Current state name. */
|
|
22
37
|
readonly state: S;
|
|
38
|
+
/** State data, typed according to the state's Zod schema. */
|
|
23
39
|
readonly data: StateData<TConfig, S>;
|
|
40
|
+
/** Timestamp of workflow creation. */
|
|
24
41
|
readonly createdAt: Date;
|
|
42
|
+
/** Timestamp of last state change. */
|
|
25
43
|
readonly updatedAt: Date;
|
|
26
44
|
}
|
|
27
45
|
/** Discriminated union of all possible workflow states — checking .state narrows .data. */
|
|
28
46
|
type Workflow<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
29
47
|
[S in StateNames<TConfig>]: WorkflowOf<TConfig, S>;
|
|
30
48
|
}[StateNames<TConfig>];
|
|
49
|
+
/** Discriminated union of all pipeline error types on `category`. */
|
|
31
50
|
type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
32
51
|
category: "validation";
|
|
33
52
|
source: "command" | "state" | "event" | "transition" | "restore";
|
|
@@ -41,7 +60,12 @@ type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
|
41
60
|
category: "router";
|
|
42
61
|
code: "NO_HANDLER" | "UNKNOWN_STATE";
|
|
43
62
|
message: string;
|
|
63
|
+
} | {
|
|
64
|
+
category: "unexpected";
|
|
65
|
+
error: unknown;
|
|
66
|
+
message: string;
|
|
44
67
|
};
|
|
68
|
+
/** Return type of {@link WorkflowRouter.dispatch}. Discriminated union on `ok`. */
|
|
45
69
|
type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
46
70
|
ok: true;
|
|
47
71
|
workflow: Workflow<TConfig>;
|
|
@@ -53,44 +77,115 @@ type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
|
53
77
|
ok: false;
|
|
54
78
|
error: PipelineError<TConfig>;
|
|
55
79
|
};
|
|
56
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Thrown internally when Zod validation fails during dispatch.
|
|
82
|
+
* Caught by the router and returned as a validation error in {@link DispatchResult}.
|
|
83
|
+
*
|
|
84
|
+
* @param source - Which validation stage failed
|
|
85
|
+
* @param issues - Array of Zod validation issues
|
|
86
|
+
*/
|
|
57
87
|
declare class ValidationError extends Error {
|
|
58
88
|
readonly source: "command" | "state" | "event" | "transition" | "restore";
|
|
59
89
|
readonly issues: z.core.$ZodIssue[];
|
|
60
90
|
constructor(source: "command" | "state" | "event" | "transition" | "restore", issues: z.core.$ZodIssue[]);
|
|
61
91
|
}
|
|
62
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Thrown internally when a handler calls `ctx.error()`.
|
|
94
|
+
* Caught by the router and returned as a domain error in {@link DispatchResult}.
|
|
95
|
+
*
|
|
96
|
+
* @param code - The error code string
|
|
97
|
+
* @param data - The error data payload
|
|
98
|
+
*/
|
|
63
99
|
declare class DomainErrorSignal extends Error {
|
|
64
100
|
readonly code: string;
|
|
65
101
|
readonly data: unknown;
|
|
66
102
|
constructor(code: string, data: unknown);
|
|
67
103
|
}
|
|
68
104
|
|
|
69
|
-
/** A plain, JSON-safe representation of a workflow's state. */
|
|
105
|
+
/** A plain, JSON-safe representation of a workflow's state for serialization and storage. */
|
|
70
106
|
interface WorkflowSnapshot<TConfig extends WorkflowConfig = WorkflowConfig> {
|
|
107
|
+
/** Unique workflow instance identifier. */
|
|
71
108
|
readonly id: string;
|
|
109
|
+
/** Name of the workflow definition. */
|
|
72
110
|
readonly definitionName: string;
|
|
111
|
+
/** Current state name. */
|
|
73
112
|
readonly state: StateNames<TConfig>;
|
|
113
|
+
/** State data (untyped — validated on {@link WorkflowDefinition.restore}). */
|
|
74
114
|
readonly data: unknown;
|
|
115
|
+
/** ISO 8601 timestamp of workflow creation. */
|
|
75
116
|
readonly createdAt: string;
|
|
117
|
+
/** ISO 8601 timestamp of last state change. */
|
|
76
118
|
readonly updatedAt: string;
|
|
119
|
+
/** Schema version number for migration support. */
|
|
77
120
|
readonly modelVersion: number;
|
|
78
121
|
}
|
|
79
122
|
|
|
80
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* The result of {@link defineWorkflow} — holds schemas and creates workflow instances.
|
|
125
|
+
*/
|
|
81
126
|
interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {
|
|
127
|
+
/** The raw Zod schema configuration. */
|
|
82
128
|
readonly config: TConfig;
|
|
129
|
+
/** The workflow definition name. */
|
|
83
130
|
readonly name: string;
|
|
131
|
+
/**
|
|
132
|
+
* Creates a new workflow instance in a given initial state.
|
|
133
|
+
*
|
|
134
|
+
* @param id - Unique identifier for this workflow instance
|
|
135
|
+
* @param config - Object containing `initialState` and the corresponding `data`
|
|
136
|
+
* @returns A {@link WorkflowOf} narrowed to the initial state
|
|
137
|
+
*/
|
|
84
138
|
createWorkflow<S extends StateNames<TConfig>>(id: string, config: {
|
|
85
139
|
initialState: S;
|
|
86
140
|
data: z.infer<TConfig["states"][S]>;
|
|
87
141
|
}): WorkflowOf<TConfig, S>;
|
|
142
|
+
/**
|
|
143
|
+
* Returns the Zod schema for a given state name.
|
|
144
|
+
*
|
|
145
|
+
* @param stateName - The state name to look up
|
|
146
|
+
* @throws If the state name is not found in the config
|
|
147
|
+
*/
|
|
88
148
|
getStateSchema(stateName: string): ZodType;
|
|
149
|
+
/**
|
|
150
|
+
* Returns the Zod schema for a given command name.
|
|
151
|
+
*
|
|
152
|
+
* @param commandName - The command name to look up
|
|
153
|
+
* @throws If the command name is not found in the config
|
|
154
|
+
*/
|
|
89
155
|
getCommandSchema(commandName: string): ZodType;
|
|
156
|
+
/**
|
|
157
|
+
* Returns the Zod schema for a given event name.
|
|
158
|
+
*
|
|
159
|
+
* @param eventName - The event name to look up
|
|
160
|
+
* @throws If the event name is not found in the config
|
|
161
|
+
*/
|
|
90
162
|
getEventSchema(eventName: string): ZodType;
|
|
163
|
+
/**
|
|
164
|
+
* Returns the Zod schema for a given error code.
|
|
165
|
+
*
|
|
166
|
+
* @param errorCode - The error code to look up
|
|
167
|
+
* @throws If the error code is not found in the config
|
|
168
|
+
*/
|
|
91
169
|
getErrorSchema(errorCode: string): ZodType;
|
|
170
|
+
/**
|
|
171
|
+
* Returns `true` if the given state name exists in the config.
|
|
172
|
+
*
|
|
173
|
+
* @param stateName - The state name to check
|
|
174
|
+
*/
|
|
92
175
|
hasState(stateName: string): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Serializes a workflow instance into a plain, JSON-safe snapshot.
|
|
178
|
+
*
|
|
179
|
+
* @param workflow - The workflow instance to serialize
|
|
180
|
+
* @returns A {@link WorkflowSnapshot} representing the current state
|
|
181
|
+
*/
|
|
93
182
|
snapshot(workflow: Workflow<TConfig>): WorkflowSnapshot<TConfig>;
|
|
183
|
+
/**
|
|
184
|
+
* Restores a workflow instance from a plain snapshot, validating the state data.
|
|
185
|
+
*
|
|
186
|
+
* @param snapshot - The snapshot to restore from
|
|
187
|
+
* @returns A result object: `{ ok: true, workflow }` or `{ ok: false, error }`
|
|
188
|
+
*/
|
|
94
189
|
restore(snapshot: WorkflowSnapshot<TConfig>): {
|
|
95
190
|
ok: true;
|
|
96
191
|
workflow: Workflow<TConfig>;
|
|
@@ -101,42 +196,88 @@ interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {
|
|
|
101
196
|
}
|
|
102
197
|
/**
|
|
103
198
|
* Creates a workflow definition from a name and Zod schema configuration.
|
|
199
|
+
*
|
|
200
|
+
* @param name - Unique name for this workflow type
|
|
201
|
+
* @param config - Object with `states`, `commands`, `events`, `errors` — each a record of Zod schemas
|
|
202
|
+
* @returns A {@link WorkflowDefinition} with methods for creating instances and accessing schemas
|
|
104
203
|
*/
|
|
105
204
|
declare function defineWorkflow<const TConfig extends WorkflowConfig>(name: string, config: TConfig): WorkflowDefinition<TConfig>;
|
|
106
205
|
|
|
107
|
-
/** A phantom-typed key for type-safe middleware state storage. */
|
|
206
|
+
/** A phantom-typed key for type-safe middleware state storage via {@link Context.set} and {@link Context.get}. */
|
|
108
207
|
interface ContextKey<T> {
|
|
208
|
+
/** @internal Phantom type brand — not used at runtime. */
|
|
109
209
|
readonly _phantom: T;
|
|
210
|
+
/** Internal symbol providing uniqueness. */
|
|
110
211
|
readonly id: symbol;
|
|
111
212
|
}
|
|
112
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* Creates a unique typed key for storing and retrieving values in context.
|
|
215
|
+
*
|
|
216
|
+
* @param name - Debug label (uniqueness comes from an internal `Symbol`)
|
|
217
|
+
* @returns A {@link ContextKey} for use with `ctx.set()`, `ctx.get()`, and `ctx.getOrNull()`
|
|
218
|
+
*/
|
|
113
219
|
declare function createKey<T>(name: string): ContextKey<T>;
|
|
114
220
|
|
|
115
221
|
/** Mutable context flowing through the middleware pipeline during dispatch. */
|
|
116
222
|
interface Context<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig> = StateNames<TConfig>, TCommand extends CommandNames<TConfig> = CommandNames<TConfig>> {
|
|
223
|
+
/** The command being dispatched, with type and validated payload. */
|
|
117
224
|
readonly command: {
|
|
118
225
|
readonly type: TCommand;
|
|
119
226
|
readonly payload: CommandPayload<TConfig, TCommand>;
|
|
120
227
|
};
|
|
228
|
+
/** The original workflow before any mutations. */
|
|
121
229
|
readonly workflow: WorkflowOf<TConfig, TState>;
|
|
230
|
+
/** Dependencies injected via the router constructor. */
|
|
122
231
|
readonly deps: TDeps;
|
|
232
|
+
/** Current state data (reflects mutations from {@link update}). */
|
|
123
233
|
readonly data: StateData<TConfig, TState>;
|
|
234
|
+
/**
|
|
235
|
+
* Merges partial data into the current state. Validates against the state's Zod schema.
|
|
236
|
+
* @param data - Partial state data to merge
|
|
237
|
+
*/
|
|
124
238
|
update(data: Partial<StateData<TConfig, TState>>): void;
|
|
239
|
+
/**
|
|
240
|
+
* Transitions the workflow to a new state with new data. Validates against the target state's Zod schema.
|
|
241
|
+
* @param target - Target state name
|
|
242
|
+
* @param data - Data for the target state
|
|
243
|
+
*/
|
|
125
244
|
transition<Target extends StateNames<TConfig>>(target: Target, data: StateData<TConfig, Target>): void;
|
|
245
|
+
/**
|
|
246
|
+
* Emits a domain event. Validates event data against the event's Zod schema.
|
|
247
|
+
* @param event - Event with type and data
|
|
248
|
+
*/
|
|
126
249
|
emit<E extends EventNames<TConfig>>(event: {
|
|
127
250
|
type: E;
|
|
128
251
|
data: EventData<TConfig, E>;
|
|
129
252
|
}): void;
|
|
253
|
+
/** Accumulated events emitted during this dispatch. */
|
|
130
254
|
readonly events: ReadonlyArray<{
|
|
131
255
|
type: EventNames<TConfig>;
|
|
132
256
|
data: unknown;
|
|
133
257
|
}>;
|
|
258
|
+
/**
|
|
259
|
+
* Signals a domain error. Validates error data and throws internally (caught by the router).
|
|
260
|
+
* @param err - Error with code and data
|
|
261
|
+
*/
|
|
134
262
|
error<C extends ErrorCodes<TConfig>>(err: {
|
|
135
263
|
code: C;
|
|
136
264
|
data: ErrorData<TConfig, C>;
|
|
137
265
|
}): never;
|
|
266
|
+
/**
|
|
267
|
+
* Stores a value in context-scoped middleware state.
|
|
268
|
+
* @param key - A {@link ContextKey} created via {@link createKey}
|
|
269
|
+
* @param value - The value to store
|
|
270
|
+
*/
|
|
138
271
|
set<T>(key: ContextKey<T>, value: T): void;
|
|
272
|
+
/**
|
|
273
|
+
* Retrieves a value from context-scoped middleware state. Throws if not set.
|
|
274
|
+
* @param key - A {@link ContextKey} created via {@link createKey}
|
|
275
|
+
*/
|
|
139
276
|
get<T>(key: ContextKey<T>): T;
|
|
277
|
+
/**
|
|
278
|
+
* Retrieves a value from context-scoped middleware state, or `undefined` if not set.
|
|
279
|
+
* @param key - A {@link ContextKey} created via {@link createKey}
|
|
280
|
+
*/
|
|
140
281
|
getOrNull<T>(key: ContextKey<T>): T | undefined;
|
|
141
282
|
/** @internal — not part of the handler API */
|
|
142
283
|
getWorkflowSnapshot(): Workflow<TConfig>;
|
|
@@ -159,13 +300,26 @@ type Middleware<TConfig extends WorkflowConfig, TDeps, TState extends StateNames
|
|
|
159
300
|
|
|
160
301
|
/** A function that transforms a snapshot's data from one version to the next. */
|
|
161
302
|
type MigrationFn = (snapshot: WorkflowSnapshot) => WorkflowSnapshot;
|
|
303
|
+
/** A migration entry — either a bare {@link MigrationFn} or an object with a description. */
|
|
304
|
+
type MigrationEntry = MigrationFn | {
|
|
305
|
+
description: string;
|
|
306
|
+
up: MigrationFn;
|
|
307
|
+
};
|
|
308
|
+
/** Internal normalized migration step with optional description. */
|
|
309
|
+
interface NormalizedMigration {
|
|
310
|
+
fn: MigrationFn;
|
|
311
|
+
description?: string;
|
|
312
|
+
}
|
|
162
313
|
/** A validated migration pipeline ready to transform snapshots. */
|
|
163
314
|
interface MigrationPipeline<TConfig extends WorkflowConfig = WorkflowConfig> {
|
|
315
|
+
/** The workflow definition this pipeline belongs to. */
|
|
164
316
|
readonly definition: WorkflowDefinition<TConfig>;
|
|
317
|
+
/** The target schema version to migrate snapshots to. */
|
|
165
318
|
readonly targetVersion: number;
|
|
166
|
-
|
|
319
|
+
/** Map of version number to normalized migration step. */
|
|
320
|
+
readonly migrations: ReadonlyMap<number, NormalizedMigration>;
|
|
167
321
|
}
|
|
168
|
-
/** Result of migrate
|
|
322
|
+
/** Result of {@link migrate}. */
|
|
169
323
|
type MigrateResult = {
|
|
170
324
|
ok: true;
|
|
171
325
|
snapshot: WorkflowSnapshot;
|
|
@@ -173,9 +327,20 @@ type MigrateResult = {
|
|
|
173
327
|
ok: false;
|
|
174
328
|
error: MigrationError;
|
|
175
329
|
};
|
|
176
|
-
/** Options for migrate
|
|
330
|
+
/** Options for {@link migrate}. */
|
|
177
331
|
interface MigrateOptions {
|
|
178
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Called after each successful migration step.
|
|
334
|
+
* @param fromVersion - The version before this step
|
|
335
|
+
* @param toVersion - The version after this step
|
|
336
|
+
* @param snapshot - The snapshot after this step
|
|
337
|
+
* @param description - Optional description from the migration entry
|
|
338
|
+
*/
|
|
339
|
+
onStep?: (fromVersion: number, toVersion: number, snapshot: WorkflowSnapshot, description?: string) => void;
|
|
340
|
+
/**
|
|
341
|
+
* Called when a migration step fails.
|
|
342
|
+
* @param error - The {@link MigrationError} describing the failure
|
|
343
|
+
*/
|
|
179
344
|
onError?: (error: MigrationError) => void;
|
|
180
345
|
}
|
|
181
346
|
/** Error thrown when a migration step fails. */
|
|
@@ -183,16 +348,31 @@ declare class MigrationError extends Error {
|
|
|
183
348
|
readonly fromVersion: number;
|
|
184
349
|
readonly toVersion: number;
|
|
185
350
|
readonly cause: unknown;
|
|
351
|
+
/**
|
|
352
|
+
* @param fromVersion - The schema version the migration started from
|
|
353
|
+
* @param toVersion - The schema version the migration was attempting to reach
|
|
354
|
+
* @param cause - The underlying error that caused the failure
|
|
355
|
+
*/
|
|
186
356
|
constructor(fromVersion: number, toVersion: number, cause: unknown);
|
|
187
357
|
}
|
|
188
358
|
/**
|
|
189
359
|
* Creates a validated migration pipeline from a definition and version-keyed transform functions.
|
|
190
360
|
* Each key is the target version — the function transforms from (key - 1) to key.
|
|
361
|
+
*
|
|
362
|
+
* @param definition - The workflow definition the migrations belong to
|
|
363
|
+
* @param migrationMap - A record mapping target version numbers to {@link MigrationEntry} values
|
|
364
|
+
* @returns A validated {@link MigrationPipeline} ready for use with {@link migrate}
|
|
365
|
+
* @throws If migration keys are not sequential from 2 to the definition's `modelVersion`, or if the highest key does not match `modelVersion`
|
|
191
366
|
*/
|
|
192
|
-
declare function defineMigrations<TConfig extends WorkflowConfig>(definition: WorkflowDefinition<TConfig>, migrationMap: Record<number,
|
|
367
|
+
declare function defineMigrations<TConfig extends WorkflowConfig>(definition: WorkflowDefinition<TConfig>, migrationMap: Record<number, MigrationEntry>): MigrationPipeline<TConfig>;
|
|
193
368
|
/**
|
|
194
369
|
* Runs the migration chain from the snapshot's modelVersion to the pipeline's targetVersion.
|
|
195
370
|
* Returns a Result. Auto-stamps modelVersion after each step.
|
|
371
|
+
*
|
|
372
|
+
* @param pipeline - The {@link MigrationPipeline} created by {@link defineMigrations}
|
|
373
|
+
* @param snapshot - The workflow snapshot to migrate
|
|
374
|
+
* @param options - Optional callbacks for step progress and error reporting
|
|
375
|
+
* @returns A {@link MigrateResult} with the migrated snapshot on success, or a {@link MigrationError} on failure
|
|
196
376
|
*/
|
|
197
377
|
declare function migrate<TConfig extends WorkflowConfig>(pipeline: MigrationPipeline<TConfig>, snapshot: WorkflowSnapshot, options?: MigrateOptions): MigrateResult;
|
|
198
378
|
|
|
@@ -207,14 +387,16 @@ type HandlerEntry = {
|
|
|
207
387
|
inlineMiddleware: AnyMiddleware[];
|
|
208
388
|
handler: AnyMiddleware;
|
|
209
389
|
};
|
|
390
|
+
/** Options for the {@link WorkflowRouter} constructor. */
|
|
210
391
|
interface RouterOptions {
|
|
392
|
+
/** Callback invoked when a lifecycle hook throws. Defaults to `console.error`. */
|
|
211
393
|
onHookError?: (error: unknown) => void;
|
|
212
394
|
}
|
|
213
395
|
declare class StateBuilder<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>> {
|
|
214
396
|
/** @internal */ readonly middleware: AnyMiddleware[];
|
|
215
397
|
/** @internal */ readonly handlers: Map<string, HandlerEntry>;
|
|
216
|
-
on<C extends CommandNames<TConfig>>(command: C, ...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>])
|
|
217
|
-
use(middleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>)
|
|
398
|
+
on: <C extends CommandNames<TConfig>>(command: C, ...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>]) => this;
|
|
399
|
+
use: (middleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>) => this;
|
|
218
400
|
}
|
|
219
401
|
/**
|
|
220
402
|
* Routes commands to handlers based on workflow state.
|
|
@@ -231,14 +413,30 @@ declare class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {
|
|
|
231
413
|
private wildcardHandlers;
|
|
232
414
|
private hookRegistry;
|
|
233
415
|
private readonly onHookError;
|
|
416
|
+
/**
|
|
417
|
+
* @param definition - The workflow definition describing states, commands, events, and errors
|
|
418
|
+
* @param deps - Dependencies injected into every handler context
|
|
419
|
+
* @param options - Router configuration options
|
|
420
|
+
*/
|
|
234
421
|
constructor(definition: WorkflowDefinition<TConfig>, deps?: TDeps, options?: RouterOptions);
|
|
235
|
-
/**
|
|
422
|
+
/**
|
|
423
|
+
* Adds global middleware, merges another router, or applies a plugin.
|
|
424
|
+
* @param arg - A middleware function, another {@link WorkflowRouter} to merge, or a {@link Plugin}
|
|
425
|
+
*/
|
|
236
426
|
use(arg: ((ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>) | WorkflowRouter<TConfig, TDeps> | Plugin<TConfig, TDeps>): this;
|
|
237
427
|
private merge;
|
|
238
428
|
private mergeStateBuilders;
|
|
239
|
-
/**
|
|
429
|
+
/**
|
|
430
|
+
* Registers handlers for one or more states.
|
|
431
|
+
* @param name - A state name or array of state names to register handlers for
|
|
432
|
+
* @param setup - Callback that receives a state builder to register commands and middleware
|
|
433
|
+
*/
|
|
240
434
|
state<P extends StateNames<TConfig> | readonly StateNames<TConfig>[]>(name: P, setup: (state: StateBuilder<TConfig, TDeps, P extends readonly (infer S)[] ? S & StateNames<TConfig> : P & StateNames<TConfig>>) => void): this;
|
|
241
|
-
/**
|
|
435
|
+
/**
|
|
436
|
+
* Registers a lifecycle hook callback.
|
|
437
|
+
* @param event - The lifecycle event name
|
|
438
|
+
* @param callback - The callback to invoke when the event fires
|
|
439
|
+
*/
|
|
242
440
|
on(event: "dispatch:start", callback: (ctx: ReadonlyContext<TConfig, TDeps>) => void | Promise<void>): this;
|
|
243
441
|
on(event: "dispatch:end", callback: (ctx: ReadonlyContext<TConfig, TDeps>, result: DispatchResult<TConfig>) => void | Promise<void>): this;
|
|
244
442
|
on(event: "transition", callback: (from: StateNames<TConfig>, to: StateNames<TConfig>, workflow: Workflow<TConfig>) => void | Promise<void>): this;
|
|
@@ -247,12 +445,22 @@ declare class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {
|
|
|
247
445
|
type: EventNames<TConfig>;
|
|
248
446
|
data: unknown;
|
|
249
447
|
}, workflow: Workflow<TConfig>) => void | Promise<void>): this;
|
|
250
|
-
/**
|
|
448
|
+
/**
|
|
449
|
+
* Registers a wildcard handler that matches any state.
|
|
450
|
+
* @param state - Must be `"*"` to match all states
|
|
451
|
+
* @param command - The command name to handle
|
|
452
|
+
* @param fns - Optional inline middleware followed by the terminal handler
|
|
453
|
+
*/
|
|
251
454
|
on<C extends CommandNames<TConfig>>(state: "*", command: C, ...fns: [
|
|
252
455
|
...AnyMiddleware[],
|
|
253
456
|
(ctx: Context<TConfig, TDeps, StateNames<TConfig>, C>) => void | Promise<void>
|
|
254
457
|
]): this;
|
|
255
|
-
/**
|
|
458
|
+
/**
|
|
459
|
+
* Dispatches a command to the appropriate handler and returns the result.
|
|
460
|
+
* @param workflow - The current workflow instance to dispatch against
|
|
461
|
+
* @param command - The command with its type and payload
|
|
462
|
+
* @returns A {@link DispatchResult} indicating success or failure with the updated workflow and events
|
|
463
|
+
*/
|
|
256
464
|
dispatch(workflow: Workflow<TConfig>, command: {
|
|
257
465
|
type: CommandNames<TConfig>;
|
|
258
466
|
payload: unknown;
|
|
@@ -260,13 +468,23 @@ declare class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {
|
|
|
260
468
|
}
|
|
261
469
|
|
|
262
470
|
declare const PLUGIN_SYMBOL: unique symbol;
|
|
263
|
-
/** A branded plugin function that can be passed to
|
|
471
|
+
/** A branded plugin function that can be passed to {@link WorkflowRouter.use}. */
|
|
264
472
|
type Plugin<TConfig extends WorkflowConfig, TDeps> = ((router: WorkflowRouter<TConfig, TDeps>) => void) & {
|
|
265
473
|
readonly [PLUGIN_SYMBOL]: true;
|
|
266
474
|
};
|
|
267
|
-
/**
|
|
475
|
+
/**
|
|
476
|
+
* Brands a function as a Ryte plugin for use with {@link WorkflowRouter.use}.
|
|
477
|
+
*
|
|
478
|
+
* @param fn - A function that configures a router (adds handlers, middleware, hooks)
|
|
479
|
+
* @returns A branded {@link Plugin} function
|
|
480
|
+
*/
|
|
268
481
|
declare function definePlugin<TConfig extends WorkflowConfig, TDeps>(fn: (router: WorkflowRouter<TConfig, TDeps>) => void): Plugin<TConfig, TDeps>;
|
|
269
|
-
/**
|
|
482
|
+
/**
|
|
483
|
+
* Checks whether a value is a branded Ryte plugin.
|
|
484
|
+
*
|
|
485
|
+
* @param value - The value to check
|
|
486
|
+
* @returns `true` if the value is a {@link Plugin}
|
|
487
|
+
*/
|
|
270
488
|
declare function isPlugin(value: unknown): value is Plugin<WorkflowConfig, unknown>;
|
|
271
489
|
|
|
272
|
-
export { type CommandNames, type CommandPayload, type Context, type ContextKey, type DispatchResult, DomainErrorSignal, type ErrorCodes, type ErrorData, type EventData, type EventNames, type Handler, type HookEvent, type Middleware, type MigrateOptions, type MigrateResult, MigrationError, type MigrationFn, type MigrationPipeline, type PipelineError, type Plugin, type ReadonlyContext, type RouterOptions, type StateData, type StateNames, ValidationError, type Workflow, type WorkflowConfig, type WorkflowDefinition, type WorkflowOf, WorkflowRouter, type WorkflowSnapshot, createKey, defineMigrations, definePlugin, defineWorkflow, isPlugin, migrate };
|
|
490
|
+
export { type CommandNames, type CommandPayload, type Context, type ContextKey, type DispatchResult, DomainErrorSignal, type ErrorCodes, type ErrorData, type EventData, type EventNames, type Handler, type HookEvent, type Middleware, type MigrateOptions, type MigrateResult, type MigrationEntry, MigrationError, type MigrationFn, type MigrationPipeline, type PipelineError, type Plugin, type ReadonlyContext, type RouterOptions, type StateData, type StateNames, ValidationError, type Workflow, type WorkflowConfig, type WorkflowDefinition, type WorkflowOf, WorkflowRouter, type WorkflowSnapshot, createKey, defineMigrations, definePlugin, defineWorkflow, isPlugin, migrate };
|
package/dist/index.js
CHANGED
|
@@ -118,6 +118,11 @@ function createKey(name) {
|
|
|
118
118
|
|
|
119
119
|
// src/migration.ts
|
|
120
120
|
var MigrationError = class extends Error {
|
|
121
|
+
/**
|
|
122
|
+
* @param fromVersion - The schema version the migration started from
|
|
123
|
+
* @param toVersion - The schema version the migration was attempting to reach
|
|
124
|
+
* @param cause - The underlying error that caused the failure
|
|
125
|
+
*/
|
|
121
126
|
constructor(fromVersion, toVersion, cause) {
|
|
122
127
|
super(
|
|
123
128
|
`Migration ${fromVersion} \u2192 ${toVersion} failed: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
@@ -130,7 +135,10 @@ var MigrationError = class extends Error {
|
|
|
130
135
|
};
|
|
131
136
|
function defineMigrations(definition, migrationMap) {
|
|
132
137
|
const targetVersion = definition.config.modelVersion ?? 1;
|
|
133
|
-
const entries = Object.entries(migrationMap).map(([k, v]) =>
|
|
138
|
+
const entries = Object.entries(migrationMap).map(([k, v]) => {
|
|
139
|
+
const normalized = typeof v === "function" ? { fn: v } : { fn: v.up, description: v.description };
|
|
140
|
+
return [Number(k), normalized];
|
|
141
|
+
});
|
|
134
142
|
for (const [version] of entries) {
|
|
135
143
|
if (version <= 1) {
|
|
136
144
|
throw new Error(`Migration keys must be > 1 (version 1 is the baseline). Got: ${version}`);
|
|
@@ -199,8 +207,8 @@ function migrate(pipeline, snapshot, options) {
|
|
|
199
207
|
}
|
|
200
208
|
let current = { ...snapshot };
|
|
201
209
|
for (let version = current.modelVersion + 1; version <= pipeline.targetVersion; version++) {
|
|
202
|
-
const
|
|
203
|
-
if (!
|
|
210
|
+
const migration = pipeline.migrations.get(version);
|
|
211
|
+
if (!migration) {
|
|
204
212
|
const error = new MigrationError(
|
|
205
213
|
version - 1,
|
|
206
214
|
version,
|
|
@@ -211,13 +219,13 @@ function migrate(pipeline, snapshot, options) {
|
|
|
211
219
|
}
|
|
212
220
|
const fromVersion = version - 1;
|
|
213
221
|
try {
|
|
214
|
-
current = { ...fn(current), modelVersion: version };
|
|
222
|
+
current = { ...migration.fn(current), modelVersion: version };
|
|
215
223
|
} catch (cause) {
|
|
216
224
|
const error = new MigrationError(fromVersion, version, cause);
|
|
217
225
|
options?.onError?.(error);
|
|
218
226
|
return { ok: false, error };
|
|
219
227
|
}
|
|
220
|
-
options?.onStep?.(fromVersion, version, current);
|
|
228
|
+
options?.onStep?.(fromVersion, version, current, migration.description);
|
|
221
229
|
}
|
|
222
230
|
return { ok: true, snapshot: current };
|
|
223
231
|
}
|
|
@@ -377,7 +385,7 @@ var StateBuilder = class {
|
|
|
377
385
|
middleware = [];
|
|
378
386
|
/** @internal */
|
|
379
387
|
handlers = /* @__PURE__ */ new Map();
|
|
380
|
-
on(command, ...fns) {
|
|
388
|
+
on = (command, ...fns) => {
|
|
381
389
|
if (fns.length === 0) throw new Error("on() requires at least a handler");
|
|
382
390
|
const handler = fns.pop();
|
|
383
391
|
const inlineMiddleware = fns;
|
|
@@ -386,13 +394,18 @@ var StateBuilder = class {
|
|
|
386
394
|
};
|
|
387
395
|
this.handlers.set(command, { inlineMiddleware, handler: wrappedHandler });
|
|
388
396
|
return this;
|
|
389
|
-
}
|
|
390
|
-
use(middleware) {
|
|
397
|
+
};
|
|
398
|
+
use = (middleware) => {
|
|
391
399
|
this.middleware.push(middleware);
|
|
392
400
|
return this;
|
|
393
|
-
}
|
|
401
|
+
};
|
|
394
402
|
};
|
|
395
403
|
var WorkflowRouter = class _WorkflowRouter {
|
|
404
|
+
/**
|
|
405
|
+
* @param definition - The workflow definition describing states, commands, events, and errors
|
|
406
|
+
* @param deps - Dependencies injected into every handler context
|
|
407
|
+
* @param options - Router configuration options
|
|
408
|
+
*/
|
|
396
409
|
constructor(definition, deps = {}, options = {}) {
|
|
397
410
|
this.definition = definition;
|
|
398
411
|
this.deps = deps;
|
|
@@ -406,7 +419,10 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
406
419
|
wildcardHandlers = /* @__PURE__ */ new Map();
|
|
407
420
|
hookRegistry = new HookRegistry();
|
|
408
421
|
onHookError;
|
|
409
|
-
/**
|
|
422
|
+
/**
|
|
423
|
+
* Adds global middleware, merges another router, or applies a plugin.
|
|
424
|
+
* @param arg - A middleware function, another {@link WorkflowRouter} to merge, or a {@link Plugin}
|
|
425
|
+
*/
|
|
410
426
|
use(arg) {
|
|
411
427
|
if (arg instanceof _WorkflowRouter) {
|
|
412
428
|
this.merge(arg);
|
|
@@ -454,7 +470,11 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
454
470
|
parentBuilder.middleware.push(...childBuilder.middleware);
|
|
455
471
|
}
|
|
456
472
|
}
|
|
457
|
-
/**
|
|
473
|
+
/**
|
|
474
|
+
* Registers handlers for one or more states.
|
|
475
|
+
* @param name - A state name or array of state names to register handlers for
|
|
476
|
+
* @param setup - Callback that receives a state builder to register commands and middleware
|
|
477
|
+
*/
|
|
458
478
|
state(name, setup) {
|
|
459
479
|
const names = Array.isArray(name) ? name : [name];
|
|
460
480
|
const isMulti = Array.isArray(name);
|
|
@@ -493,7 +513,12 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
493
513
|
}
|
|
494
514
|
throw new Error(`Unknown event or state: ${first}`);
|
|
495
515
|
}
|
|
496
|
-
/**
|
|
516
|
+
/**
|
|
517
|
+
* Dispatches a command to the appropriate handler and returns the result.
|
|
518
|
+
* @param workflow - The current workflow instance to dispatch against
|
|
519
|
+
* @param command - The command with its type and payload
|
|
520
|
+
* @returns A {@link DispatchResult} indicating success or failure with the updated workflow and events
|
|
521
|
+
*/
|
|
497
522
|
async dispatch(workflow, command) {
|
|
498
523
|
if (!this.definition.hasState(workflow.state)) {
|
|
499
524
|
return {
|
|
@@ -612,7 +637,14 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
612
637
|
}
|
|
613
638
|
};
|
|
614
639
|
} else {
|
|
615
|
-
|
|
640
|
+
result = {
|
|
641
|
+
ok: false,
|
|
642
|
+
error: {
|
|
643
|
+
category: "unexpected",
|
|
644
|
+
error: err,
|
|
645
|
+
message: err instanceof Error ? err.message : String(err)
|
|
646
|
+
}
|
|
647
|
+
};
|
|
616
648
|
}
|
|
617
649
|
await this.hookRegistry.emit("error", this.onHookError, result.error, ctx);
|
|
618
650
|
await this.hookRegistry.emit("dispatch:end", this.onHookError, ctx, result);
|