@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.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
- /** Thrown internally when Zod validation fails during dispatch. */
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
- /** Thrown internally when a handler calls ctx.error(). Caught by the router. */
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
- /** The result of defineWorkflow() — holds schemas and creates workflow instances. */
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
- /** Creates a unique typed key for storing/retrieving values in context. */
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
- readonly migrations: ReadonlyMap<number, MigrationFn>;
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
- onStep?: (fromVersion: number, toVersion: number, snapshot: WorkflowSnapshot) => void;
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, MigrationFn>): MigrationPipeline<TConfig>;
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>]): this;
217
- use(middleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>): this;
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
- /** Adds global middleware, merges another router, or applies a plugin. */
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
- /** Registers handlers for one or more states. */
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
- /** Registers a lifecycle hook callback. */
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
- /** Registers a wildcard handler that matches any state. */
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
- /** Dispatches a command to the appropriate handler and returns the result. */
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 router.use(). */
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
- /** Brands a function as a Ryte plugin for use with router.use(). */
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
- /** Checks whether a value is a branded Ryte plugin. */
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]) => [Number(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 fn = pipeline.migrations.get(version);
203
- if (!fn) {
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
- /** Adds global middleware, merges another router, or applies a plugin. */
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
- /** Registers handlers for one or more states. */
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
- /** Dispatches a command to the appropriate handler and returns the result. */
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
- throw err;
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);