@rytejs/core 0.1.0 → 0.2.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/README.md +100 -0
- package/dist/index.cjs +46 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +46 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# @rytejs/core
|
|
2
|
+
|
|
3
|
+
Type-safe workflow engine with Zod validation and middleware pipelines.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/helico-tech/rytejs/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/@rytejs/core)
|
|
7
|
+
|
|
8
|
+
## Why Ryte?
|
|
9
|
+
|
|
10
|
+
- **Fully typed from definition to dispatch** -- define your states, commands, events, and errors with Zod schemas. TypeScript infers everything automatically. State names, command payloads, event data, error codes -- all with full autocompletion, no manual type annotations.
|
|
11
|
+
- **Checking `workflow.state` narrows `workflow.data`** -- TypeScript knows exactly which data shape each state has. Discriminated unions, not type casts.
|
|
12
|
+
- **`ctx.error()` is type-checked** -- you can only raise error codes that exist in your definition, with the correct data shape. Domain failures are part of the contract.
|
|
13
|
+
- **Koa-style middleware** -- global, state-scoped, and inline middleware with the onion model. Add auth, logging, or validation without touching handlers.
|
|
14
|
+
- **Fluent builder API** -- chain `.state()`, `.on()`, `.use()` calls. Every method returns `this`.
|
|
15
|
+
- **Composable routers** -- split handlers across files and compose them with `.use()`. Routers are routers.
|
|
16
|
+
- **Zero platform lock-in** -- pure logic with no runtime dependencies beyond Zod. Works on Node.js, Bun, and Deno.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @rytejs/core zod
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Example
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { z } from "zod";
|
|
28
|
+
import { defineWorkflow, WorkflowRouter } from "@rytejs/core";
|
|
29
|
+
|
|
30
|
+
const taskWorkflow = defineWorkflow("task", {
|
|
31
|
+
states: {
|
|
32
|
+
Todo: z.object({ title: z.string(), assignee: z.string().optional() }),
|
|
33
|
+
Done: z.object({ title: z.string(), completedAt: z.coerce.date() }),
|
|
34
|
+
},
|
|
35
|
+
commands: {
|
|
36
|
+
Complete: z.object({}),
|
|
37
|
+
},
|
|
38
|
+
events: {
|
|
39
|
+
TaskCompleted: z.object({ taskId: z.string() }),
|
|
40
|
+
},
|
|
41
|
+
errors: {
|
|
42
|
+
NotAssigned: z.object({ title: z.string() }),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const router = new WorkflowRouter(taskWorkflow)
|
|
47
|
+
.state("Todo", (state) => {
|
|
48
|
+
state.on("Complete", (ctx) => {
|
|
49
|
+
if (!ctx.data.assignee) {
|
|
50
|
+
ctx.error({ code: "NotAssigned", data: { title: ctx.data.title } });
|
|
51
|
+
}
|
|
52
|
+
ctx.transition("Done", {
|
|
53
|
+
title: ctx.data.title,
|
|
54
|
+
completedAt: new Date(),
|
|
55
|
+
});
|
|
56
|
+
ctx.emit({ type: "TaskCompleted", data: { taskId: ctx.workflow.id } });
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const task = taskWorkflow.createWorkflow("task-1", {
|
|
61
|
+
initialState: "Todo",
|
|
62
|
+
data: { title: "Read the docs", assignee: "alice" },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const result = await router.dispatch(task, {
|
|
66
|
+
type: "Complete",
|
|
67
|
+
payload: {},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (result.ok) {
|
|
71
|
+
console.log(result.workflow.state); // "Done"
|
|
72
|
+
console.log(result.events[0]?.type); // "TaskCompleted"
|
|
73
|
+
} else if (result.error.category === "domain") {
|
|
74
|
+
console.log(result.error.code); // "NotAssigned"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Type Safety Highlights
|
|
79
|
+
|
|
80
|
+
Every part of the API is fully typed with zero manual annotations:
|
|
81
|
+
|
|
82
|
+
- **State names** -- `router.state("Todo", ...)` only accepts states from your definition
|
|
83
|
+
- **Command names** -- `state.on("Complete", ...)` only accepts commands from your definition
|
|
84
|
+
- **Payload types** -- `ctx.command.payload` is typed based on the command's Zod schema
|
|
85
|
+
- **State data** -- `ctx.data` is typed based on the current state's Zod schema
|
|
86
|
+
- **Transitions** -- `ctx.transition("Done", data)` validates that `data` matches the target state's schema
|
|
87
|
+
- **Events** -- `ctx.emit({ type, data })` validates both type and data against event schemas
|
|
88
|
+
- **Errors** -- `ctx.error({ code, data })` only accepts error codes from your definition with matching data
|
|
89
|
+
- **Discriminated unions** -- `if (workflow.state === "Todo") { workflow.data.title }` narrows automatically
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
- [Getting Started](https://helico-tech.github.io/rytejs/guide/getting-started)
|
|
94
|
+
- [Defining Workflows](https://helico-tech.github.io/rytejs/guide/defining-workflows)
|
|
95
|
+
- [Routing Commands](https://helico-tech.github.io/rytejs/guide/routing-commands)
|
|
96
|
+
- [API Reference](https://helico-tech.github.io/rytejs/api/)
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -220,20 +220,62 @@ var StateBuilder = class {
|
|
|
220
220
|
return this;
|
|
221
221
|
}
|
|
222
222
|
};
|
|
223
|
-
var WorkflowRouter = class {
|
|
223
|
+
var WorkflowRouter = class _WorkflowRouter {
|
|
224
224
|
constructor(definition, deps = {}) {
|
|
225
225
|
this.definition = definition;
|
|
226
226
|
this.deps = deps;
|
|
227
227
|
}
|
|
228
228
|
globalMiddleware = [];
|
|
229
|
+
// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types
|
|
229
230
|
singleStateBuilders = /* @__PURE__ */ new Map();
|
|
231
|
+
// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types
|
|
230
232
|
multiStateBuilders = /* @__PURE__ */ new Map();
|
|
231
233
|
wildcardHandlers = /* @__PURE__ */ new Map();
|
|
232
|
-
/** Adds global middleware
|
|
233
|
-
use(
|
|
234
|
-
|
|
234
|
+
/** Adds global middleware or merges another router's handlers. */
|
|
235
|
+
use(middlewareOrRouter) {
|
|
236
|
+
if (middlewareOrRouter instanceof _WorkflowRouter) {
|
|
237
|
+
this.merge(middlewareOrRouter);
|
|
238
|
+
} else {
|
|
239
|
+
this.globalMiddleware.push(middlewareOrRouter);
|
|
240
|
+
}
|
|
235
241
|
return this;
|
|
236
242
|
}
|
|
243
|
+
merge(child) {
|
|
244
|
+
if (child.definition !== this.definition) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Cannot merge router for '${child.definition.name}' into router for '${this.definition.name}': definition mismatch`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
this.globalMiddleware.push(...child.globalMiddleware);
|
|
250
|
+
this.mergeStateBuilders(this.singleStateBuilders, child.singleStateBuilders);
|
|
251
|
+
this.mergeStateBuilders(this.multiStateBuilders, child.multiStateBuilders);
|
|
252
|
+
for (const [command, entry] of child.wildcardHandlers) {
|
|
253
|
+
if (!this.wildcardHandlers.has(command)) {
|
|
254
|
+
this.wildcardHandlers.set(command, {
|
|
255
|
+
inlineMiddleware: [...entry.inlineMiddleware],
|
|
256
|
+
handler: entry.handler
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
mergeStateBuilders(target, source) {
|
|
262
|
+
for (const [stateName, childBuilder] of source) {
|
|
263
|
+
let parentBuilder = target.get(stateName);
|
|
264
|
+
if (!parentBuilder) {
|
|
265
|
+
parentBuilder = new StateBuilder();
|
|
266
|
+
target.set(stateName, parentBuilder);
|
|
267
|
+
}
|
|
268
|
+
for (const [command, entry] of childBuilder.handlers) {
|
|
269
|
+
if (!parentBuilder.handlers.has(command)) {
|
|
270
|
+
parentBuilder.handlers.set(command, {
|
|
271
|
+
inlineMiddleware: [...entry.inlineMiddleware],
|
|
272
|
+
handler: entry.handler
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
parentBuilder.middleware.push(...childBuilder.middleware);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
237
279
|
/** Registers handlers for one or more states. */
|
|
238
280
|
state(name, setup) {
|
|
239
281
|
const names = Array.isArray(name) ? name : [name];
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/definition.ts","../src/key.ts","../src/compose.ts","../src/types.ts","../src/context.ts","../src/router.ts"],"sourcesContent":["export type { Context } from \"./context.js\";\nexport type { WorkflowDefinition } from \"./definition.js\";\nexport { defineWorkflow } from \"./definition.js\";\nexport type { Handler } from \"./handler.js\";\nexport type { ContextKey } from \"./key.js\";\nexport { createKey } from \"./key.js\";\nexport type { Middleware } from \"./middleware.js\";\nexport { WorkflowRouter } from \"./router.js\";\nexport type {\n\tCommandNames,\n\tCommandPayload,\n\tDispatchResult,\n\tErrorCodes,\n\tErrorData,\n\tEventData,\n\tEventNames,\n\tPipelineError,\n\tStateData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n\tWorkflowOf,\n} from \"./types.js\";\nexport { DomainErrorSignal, ValidationError } from \"./types.js\";\n","import type { ZodType, z } from \"zod\";\nimport type { StateNames, WorkflowConfig, WorkflowOf } from \"./types.js\";\n\n/** The result of defineWorkflow() — holds schemas and creates workflow instances. */\nexport interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {\n\treadonly config: TConfig;\n\treadonly name: string;\n\tcreateWorkflow<S extends StateNames<TConfig>>(\n\t\tid: string,\n\t\tconfig: { initialState: S; data: z.infer<TConfig[\"states\"][S]> },\n\t): WorkflowOf<TConfig, S>;\n\tgetStateSchema(stateName: string): ZodType;\n\tgetCommandSchema(commandName: string): ZodType;\n\tgetEventSchema(eventName: string): ZodType;\n\tgetErrorSchema(errorCode: string): ZodType;\n\thasState(stateName: string): boolean;\n}\n\n/**\n * Creates a workflow definition from a name and Zod schema configuration.\n */\nexport function defineWorkflow<const TConfig extends WorkflowConfig>(\n\tname: string,\n\tconfig: TConfig,\n): WorkflowDefinition<TConfig> {\n\treturn {\n\t\tconfig,\n\t\tname,\n\n\t\tcreateWorkflow(id, wfConfig) {\n\t\t\tconst schema = config.states[wfConfig.initialState as string];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${wfConfig.initialState as string}`);\n\t\t\tconst result = schema.safeParse(wfConfig.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid initial data for state '${wfConfig.initialState as string}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst now = new Date();\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tdefinitionName: name,\n\t\t\t\tstate: wfConfig.initialState,\n\t\t\t\tdata: result.data,\n\t\t\t\tcreatedAt: now,\n\t\t\t\tupdatedAt: now,\n\t\t\t} as WorkflowOf<TConfig, typeof wfConfig.initialState>;\n\t\t},\n\n\t\tgetStateSchema(stateName: string): ZodType {\n\t\t\tconst schema = config.states[stateName];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${stateName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetCommandSchema(commandName: string): ZodType {\n\t\t\tconst schema = config.commands[commandName];\n\t\t\tif (!schema) throw new Error(`Unknown command: ${commandName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetEventSchema(eventName: string): ZodType {\n\t\t\tconst schema = config.events[eventName];\n\t\t\tif (!schema) throw new Error(`Unknown event: ${eventName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetErrorSchema(errorCode: string): ZodType {\n\t\t\tconst schema = config.errors[errorCode];\n\t\t\tif (!schema) throw new Error(`Unknown error: ${errorCode}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\thasState(stateName: string): boolean {\n\t\t\treturn stateName in config.states;\n\t\t},\n\t};\n}\n","/** A phantom-typed key for type-safe middleware state storage. */\nexport interface ContextKey<T> {\n\treadonly _phantom: T;\n\treadonly id: symbol;\n}\n\n/** Creates a unique typed key for storing/retrieving values in context. */\nexport function createKey<T>(name: string): ContextKey<T> {\n\treturn { id: Symbol(name) } as ContextKey<T>;\n}\n","type Middleware<TCtx> = (ctx: TCtx, next: () => Promise<void>) => Promise<void>;\n\n/** Composes an array of middleware into a single function (Koa-style onion model). */\nexport function compose<TCtx>(middleware: Middleware<TCtx>[]): (ctx: TCtx) => Promise<void> {\n\treturn async (ctx: TCtx) => {\n\t\tlet index = -1;\n\t\tasync function dispatch(i: number): Promise<void> {\n\t\t\tif (i <= index) throw new Error(\"next() called multiple times\");\n\t\t\tindex = i;\n\t\t\tconst fn = middleware[i];\n\t\t\tif (!fn) return;\n\t\t\tawait fn(ctx, () => dispatch(i + 1));\n\t\t}\n\t\tawait dispatch(0);\n\t};\n}\n","import type { ZodType, z } from \"zod\";\n\nexport interface WorkflowConfig {\n\tstates: Record<string, ZodType>;\n\tcommands: Record<string, ZodType>;\n\tevents: Record<string, ZodType>;\n\terrors: Record<string, ZodType>;\n}\n\nexport type StateNames<T extends WorkflowConfig> = keyof T[\"states\"] & string;\nexport type CommandNames<T extends WorkflowConfig> = keyof T[\"commands\"] & string;\nexport type EventNames<T extends WorkflowConfig> = keyof T[\"events\"] & string;\nexport type ErrorCodes<T extends WorkflowConfig> = keyof T[\"errors\"] & string;\n\nexport type StateData<\n\tT extends WorkflowConfig,\n\tS extends StateNames<T>,\n> = T[\"states\"][S] extends ZodType ? z.infer<T[\"states\"][S]> : never;\n\nexport type CommandPayload<\n\tT extends WorkflowConfig,\n\tC extends CommandNames<T>,\n> = T[\"commands\"][C] extends ZodType ? z.infer<T[\"commands\"][C]> : never;\n\nexport type EventData<\n\tT extends WorkflowConfig,\n\tE extends EventNames<T>,\n> = T[\"events\"][E] extends ZodType ? z.infer<T[\"events\"][E]> : never;\n\nexport type ErrorData<\n\tT extends WorkflowConfig,\n\tC extends ErrorCodes<T>,\n> = T[\"errors\"][C] extends ZodType ? z.infer<T[\"errors\"][C]> : never;\n\n/** Workflow narrowed to a specific known state. */\nexport interface WorkflowOf<TConfig extends WorkflowConfig, S extends StateNames<TConfig>> {\n\treadonly id: string;\n\treadonly definitionName: string;\n\treadonly state: S;\n\treadonly data: StateData<TConfig, S>;\n\treadonly createdAt: Date;\n\treadonly updatedAt: Date;\n}\n\n/** Discriminated union of all possible workflow states — checking .state narrows .data. */\nexport type Workflow<TConfig extends WorkflowConfig = WorkflowConfig> = {\n\t[S in StateNames<TConfig>]: WorkflowOf<TConfig, S>;\n}[StateNames<TConfig>];\n\nexport type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tcategory: \"validation\";\n\t\t\tsource: \"command\" | \"state\" | \"event\" | \"transition\";\n\t\t\tissues: z.core.$ZodIssue[];\n\t\t\tmessage: string;\n\t }\n\t| {\n\t\t\tcategory: \"domain\";\n\t\t\tcode: ErrorCodes<TConfig>;\n\t\t\tdata: ErrorData<TConfig, ErrorCodes<TConfig>>;\n\t }\n\t| {\n\t\t\tcategory: \"router\";\n\t\t\tcode: \"NO_HANDLER\" | \"UNKNOWN_STATE\";\n\t\t\tmessage: string;\n\t };\n\nexport type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tok: true;\n\t\t\tworkflow: Workflow<TConfig>;\n\t\t\tevents: Array<{ type: EventNames<TConfig>; data: unknown }>;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\terror: PipelineError<TConfig>;\n\t };\n\n/** Thrown internally when Zod validation fails during dispatch. */\nexport class ValidationError extends Error {\n\tconstructor(\n\t\tpublic readonly source: \"command\" | \"state\" | \"event\" | \"transition\",\n\t\tpublic readonly issues: z.core.$ZodIssue[],\n\t) {\n\t\tsuper(`Validation failed (${source}): ${issues.map((i) => i.message).join(\", \")}`);\n\t\tthis.name = \"ValidationError\";\n\t}\n}\n\n/** Thrown internally when a handler calls ctx.error(). Caught by the router. */\nexport class DomainErrorSignal extends Error {\n\tconstructor(\n\t\tpublic readonly code: string,\n\t\tpublic readonly data: unknown,\n\t) {\n\t\tsuper(`Domain error: ${code}`);\n\t\tthis.name = \"DomainErrorSignal\";\n\t}\n}\n","import type { WorkflowDefinition } from \"./definition.js\";\nimport type { ContextKey } from \"./key.js\";\nimport type {\n\tCommandNames,\n\tCommandPayload,\n\tErrorCodes,\n\tErrorData,\n\tEventData,\n\tEventNames,\n\tStateData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n\tWorkflowOf,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\n/** Mutable context flowing through the middleware pipeline during dispatch. */\nexport interface Context<\n\tTConfig extends WorkflowConfig,\n\tTDeps,\n\tTState extends StateNames<TConfig> = StateNames<TConfig>,\n\tTCommand extends CommandNames<TConfig> = CommandNames<TConfig>,\n> {\n\treadonly command: {\n\t\treadonly type: TCommand;\n\t\treadonly payload: CommandPayload<TConfig, TCommand>;\n\t};\n\treadonly workflow: WorkflowOf<TConfig, TState>;\n\treadonly deps: TDeps;\n\n\treadonly data: StateData<TConfig, TState>;\n\tupdate(data: Partial<StateData<TConfig, TState>>): void;\n\n\ttransition<Target extends StateNames<TConfig>>(\n\t\ttarget: Target,\n\t\tdata: StateData<TConfig, Target>,\n\t): void;\n\n\temit<E extends EventNames<TConfig>>(event: { type: E; data: EventData<TConfig, E> }): void;\n\treadonly events: ReadonlyArray<{ type: EventNames<TConfig>; data: unknown }>;\n\n\terror<C extends ErrorCodes<TConfig>>(err: { code: C; data: ErrorData<TConfig, C> }): never;\n\n\tset<T>(key: ContextKey<T>, value: T): void;\n\tget<T>(key: ContextKey<T>): T;\n\tgetOrNull<T>(key: ContextKey<T>): T | undefined;\n\n\t/** @internal — not part of the handler API */\n\tgetWorkflowSnapshot(): Workflow<TConfig>;\n}\n\ninterface DomainEvent {\n\ttype: string;\n\tdata: unknown;\n}\n\n/** @internal Creates a context for dispatch. Not part of public API. */\nexport function createContext<TConfig extends WorkflowConfig, TDeps>(\n\tdefinition: WorkflowDefinition<TConfig>,\n\toriginalWorkflow: Workflow<TConfig>,\n\tcommand: { type: string; payload: unknown },\n\tdeps: TDeps,\n): Context<TConfig, TDeps> {\n\tlet mutableState = originalWorkflow.state;\n\tlet mutableData: Record<string, unknown> = {\n\t\t...(originalWorkflow.data as Record<string, unknown>),\n\t};\n\n\tconst accumulatedEvents: DomainEvent[] = [];\n\tconst middlewareState = new Map<symbol, unknown>();\n\n\tconst ctx = {\n\t\tcommand,\n\t\tworkflow: originalWorkflow,\n\t\tdeps,\n\n\t\tget data() {\n\t\t\treturn { ...mutableData } as StateData<TConfig, StateNames<TConfig>>;\n\t\t},\n\n\t\tupdate(data: Record<string, unknown>) {\n\t\t\tconst merged = { ...mutableData, ...data };\n\t\t\tconst schema = definition.getStateSchema(mutableState);\n\t\t\tconst result = schema.safeParse(merged);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"state\", result.error.issues);\n\t\t\t}\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\ttransition(target: string, data: unknown) {\n\t\t\tif (!definition.hasState(target)) {\n\t\t\t\tthrow new Error(`Unknown state: ${target}`);\n\t\t\t}\n\t\t\tconst schema = definition.getStateSchema(target);\n\t\t\tconst result = schema.safeParse(data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"transition\", result.error.issues);\n\t\t\t}\n\t\t\tmutableState = target;\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\temit(event: { type: string; data: unknown }) {\n\t\t\tconst schema = definition.getEventSchema(event.type);\n\t\t\tconst result = schema.safeParse(event.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"event\", result.error.issues);\n\t\t\t}\n\t\t\taccumulatedEvents.push({ type: event.type, data: result.data });\n\t\t},\n\n\t\tget events() {\n\t\t\treturn [...accumulatedEvents];\n\t\t},\n\n\t\terror(err: { code: string; data: unknown }) {\n\t\t\tconst schema = definition.getErrorSchema(err.code);\n\t\t\tconst result = schema.safeParse(err.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid error data for '${err.code}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new DomainErrorSignal(err.code, result.data);\n\t\t},\n\n\t\tset<T>(key: ContextKey<T>, value: T) {\n\t\t\tmiddlewareState.set(key.id, value);\n\t\t},\n\n\t\tget<T>(key: ContextKey<T>): T {\n\t\t\tif (!middlewareState.has(key.id)) {\n\t\t\t\tthrow new Error(`Context key not set: ${key.id.toString()}`);\n\t\t\t}\n\t\t\treturn middlewareState.get(key.id) as T;\n\t\t},\n\n\t\tgetOrNull<T>(key: ContextKey<T>): T | undefined {\n\t\t\treturn middlewareState.get(key.id) as T | undefined;\n\t\t},\n\n\t\tgetWorkflowSnapshot(): Workflow<TConfig> {\n\t\t\treturn {\n\t\t\t\tid: originalWorkflow.id,\n\t\t\t\tdefinitionName: originalWorkflow.definitionName,\n\t\t\t\tstate: mutableState,\n\t\t\t\tdata: { ...mutableData },\n\t\t\t\tcreatedAt: originalWorkflow.createdAt,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t} as Workflow<TConfig>;\n\t\t},\n\t};\n\n\treturn ctx as unknown as Context<TConfig, TDeps>;\n}\n","import { compose } from \"./compose.js\";\nimport { type Context, createContext } from \"./context.js\";\nimport type { WorkflowDefinition } from \"./definition.js\";\nimport type {\n\tCommandNames,\n\tDispatchResult,\n\tErrorCodes,\n\tErrorData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\ntype AnyMiddleware = (ctx: any, next: () => Promise<void>) => Promise<void>;\ntype AnyHandler = (ctx: any) => void | Promise<void>;\n\ntype HandlerEntry = { inlineMiddleware: AnyMiddleware[]; handler: AnyMiddleware };\n\nclass StateBuilder<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>> {\n\t/** @internal */ readonly middleware: AnyMiddleware[] = [];\n\t/** @internal */ readonly handlers = new Map<string, HandlerEntry>();\n\n\ton<C extends CommandNames<TConfig>>(\n\t\tcommand: C,\n\t\t...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.handlers.set(command as string, { inlineMiddleware, handler: wrappedHandler });\n\t\treturn this;\n\t}\n\n\tuse(\n\t\tmiddleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>,\n\t): this {\n\t\tthis.middleware.push(middleware as AnyMiddleware);\n\t\treturn this;\n\t}\n}\n\n/**\n * Routes commands to handlers based on workflow state.\n *\n * Supports global middleware, state-scoped middleware, inline middleware,\n * wildcard handlers, and multi-state handlers.\n */\nexport class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {\n\tprivate globalMiddleware: AnyMiddleware[] = [];\n\tprivate singleStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\tprivate multiStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\tprivate wildcardHandlers = new Map<string, HandlerEntry>();\n\n\tconstructor(\n\t\tprivate readonly definition: WorkflowDefinition<TConfig>,\n\t\tprivate readonly deps: TDeps = {} as TDeps,\n\t) {}\n\n\t/** Adds global middleware that wraps all dispatches. */\n\tuse(\n\t\tmiddleware: (ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>,\n\t): this {\n\t\tthis.globalMiddleware.push(middleware as AnyMiddleware);\n\t\treturn this;\n\t}\n\n\t/** Registers handlers for one or more states. */\n\tstate<P extends StateNames<TConfig> | readonly StateNames<TConfig>[]>(\n\t\tname: P,\n\t\tsetup: (\n\t\t\tstate: StateBuilder<\n\t\t\t\tTConfig,\n\t\t\t\tTDeps,\n\t\t\t\tP extends readonly (infer S)[] ? S & StateNames<TConfig> : P & StateNames<TConfig>\n\t\t\t>,\n\t\t) => void,\n\t): this {\n\t\tconst names = Array.isArray(name) ? name : [name];\n\t\tconst isMulti = Array.isArray(name);\n\t\tconst routerMap = isMulti ? this.multiStateBuilders : this.singleStateBuilders;\n\n\t\tfor (const n of names as string[]) {\n\t\t\tlet router = routerMap.get(n);\n\t\t\tif (!router) {\n\t\t\t\trouter = new StateBuilder<TConfig, TDeps, any>();\n\t\t\t\trouterMap.set(n, router);\n\t\t\t}\n\t\t\tsetup(router as any);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Registers a wildcard handler that matches any state. */\n\ton<C extends CommandNames<TConfig>>(\n\t\t_state: \"*\",\n\t\tcommand: C,\n\t\t...fns: [\n\t\t\t...AnyMiddleware[],\n\t\t\t(ctx: Context<TConfig, TDeps, StateNames<TConfig>, C>) => void | Promise<void>,\n\t\t]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.wildcardHandlers.set(command as string, {\n\t\t\tinlineMiddleware,\n\t\t\thandler: wrappedHandler,\n\t\t});\n\t\treturn this;\n\t}\n\n\t/** Dispatches a command to the appropriate handler and returns the result. */\n\tasync dispatch(\n\t\tworkflow: Workflow<TConfig>,\n\t\tcommand: { type: CommandNames<TConfig>; payload: unknown },\n\t): Promise<DispatchResult<TConfig>> {\n\t\tif (!this.definition.hasState(workflow.state)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"UNKNOWN_STATE\",\n\t\t\t\t\tmessage: `Unknown state: ${workflow.state}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst commandSchema = this.definition.getCommandSchema(command.type);\n\t\tconst payloadResult = commandSchema.safeParse(command.payload);\n\t\tif (!payloadResult.success) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"validation\",\n\t\t\t\t\tsource: \"command\",\n\t\t\t\t\tissues: payloadResult.error.issues,\n\t\t\t\t\tmessage: `Invalid command payload: ${payloadResult.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst validatedCommand = { type: command.type, payload: payloadResult.data };\n\n\t\tconst stateName = workflow.state;\n\t\tconst singleRouter = this.singleStateBuilders.get(stateName);\n\t\tconst multiRouter = this.multiStateBuilders.get(stateName);\n\t\tconst singleHandler = singleRouter?.handlers.get(command.type);\n\t\tconst multiHandler = multiRouter?.handlers.get(command.type);\n\t\tconst wildcardHandler = this.wildcardHandlers.get(command.type);\n\n\t\tlet routeEntry: HandlerEntry | undefined;\n\t\tlet matchedRouter: StateBuilder<TConfig, TDeps, any> | undefined;\n\n\t\tif (singleHandler) {\n\t\t\trouteEntry = singleHandler;\n\t\t\tmatchedRouter = singleRouter;\n\t\t} else if (multiHandler) {\n\t\t\trouteEntry = multiHandler;\n\t\t\tmatchedRouter = multiRouter;\n\t\t} else if (wildcardHandler) {\n\t\t\trouteEntry = wildcardHandler;\n\t\t\tmatchedRouter = undefined;\n\t\t}\n\n\t\tif (!routeEntry) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"NO_HANDLER\",\n\t\t\t\t\tmessage: `No handler for command '${command.type}' in state '${stateName}'`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst stateMiddleware: AnyMiddleware[] = [];\n\t\tif (matchedRouter) {\n\t\t\tif (singleRouter) stateMiddleware.push(...singleRouter.middleware);\n\t\t\tif (multiRouter && multiRouter !== singleRouter)\n\t\t\t\tstateMiddleware.push(...multiRouter.middleware);\n\t\t}\n\n\t\tconst chain: AnyMiddleware[] = [\n\t\t\t...this.globalMiddleware,\n\t\t\t...stateMiddleware,\n\t\t\t...routeEntry.inlineMiddleware,\n\t\t\trouteEntry.handler,\n\t\t];\n\n\t\tconst ctx = createContext<TConfig, TDeps>(\n\t\t\tthis.definition,\n\t\t\tworkflow,\n\t\t\tvalidatedCommand,\n\t\t\tthis.deps,\n\t\t);\n\n\t\ttry {\n\t\t\tconst composed = compose(chain);\n\t\t\tawait composed(ctx);\n\t\t\treturn {\n\t\t\t\tok: true as const,\n\t\t\t\tworkflow: ctx.getWorkflowSnapshot(),\n\t\t\t\tevents: [...ctx.events],\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof DomainErrorSignal) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"domain\" as const,\n\t\t\t\t\t\tcode: err.code as ErrorCodes<TConfig>,\n\t\t\t\t\t\tdata: err.data as ErrorData<TConfig, ErrorCodes<TConfig>>,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof ValidationError) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"validation\" as const,\n\t\t\t\t\t\tsource: err.source,\n\t\t\t\t\t\tissues: err.issues,\n\t\t\t\t\t\tmessage: err.message,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/definition.ts","../src/key.ts","../src/compose.ts","../src/types.ts","../src/context.ts","../src/router.ts"],"sourcesContent":["export type { Context } from \"./context.js\";\nexport type { WorkflowDefinition } from \"./definition.js\";\nexport { defineWorkflow } from \"./definition.js\";\nexport type { Handler } from \"./handler.js\";\nexport type { ContextKey } from \"./key.js\";\nexport { createKey } from \"./key.js\";\nexport type { Middleware } from \"./middleware.js\";\nexport { WorkflowRouter } from \"./router.js\";\nexport type {\n\tCommandNames,\n\tCommandPayload,\n\tDispatchResult,\n\tErrorCodes,\n\tErrorData,\n\tEventData,\n\tEventNames,\n\tPipelineError,\n\tStateData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n\tWorkflowOf,\n} from \"./types.js\";\nexport { DomainErrorSignal, ValidationError } from \"./types.js\";\n","import type { ZodType, z } from \"zod\";\nimport type { StateNames, WorkflowConfig, WorkflowOf } from \"./types.js\";\n\n/** The result of defineWorkflow() — holds schemas and creates workflow instances. */\nexport interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {\n\treadonly config: TConfig;\n\treadonly name: string;\n\tcreateWorkflow<S extends StateNames<TConfig>>(\n\t\tid: string,\n\t\tconfig: { initialState: S; data: z.infer<TConfig[\"states\"][S]> },\n\t): WorkflowOf<TConfig, S>;\n\tgetStateSchema(stateName: string): ZodType;\n\tgetCommandSchema(commandName: string): ZodType;\n\tgetEventSchema(eventName: string): ZodType;\n\tgetErrorSchema(errorCode: string): ZodType;\n\thasState(stateName: string): boolean;\n}\n\n/**\n * Creates a workflow definition from a name and Zod schema configuration.\n */\nexport function defineWorkflow<const TConfig extends WorkflowConfig>(\n\tname: string,\n\tconfig: TConfig,\n): WorkflowDefinition<TConfig> {\n\treturn {\n\t\tconfig,\n\t\tname,\n\n\t\tcreateWorkflow(id, wfConfig) {\n\t\t\tconst schema = config.states[wfConfig.initialState as string];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${wfConfig.initialState as string}`);\n\t\t\tconst result = schema.safeParse(wfConfig.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid initial data for state '${wfConfig.initialState as string}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst now = new Date();\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tdefinitionName: name,\n\t\t\t\tstate: wfConfig.initialState,\n\t\t\t\tdata: result.data,\n\t\t\t\tcreatedAt: now,\n\t\t\t\tupdatedAt: now,\n\t\t\t} as WorkflowOf<TConfig, typeof wfConfig.initialState>;\n\t\t},\n\n\t\tgetStateSchema(stateName: string): ZodType {\n\t\t\tconst schema = config.states[stateName];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${stateName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetCommandSchema(commandName: string): ZodType {\n\t\t\tconst schema = config.commands[commandName];\n\t\t\tif (!schema) throw new Error(`Unknown command: ${commandName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetEventSchema(eventName: string): ZodType {\n\t\t\tconst schema = config.events[eventName];\n\t\t\tif (!schema) throw new Error(`Unknown event: ${eventName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetErrorSchema(errorCode: string): ZodType {\n\t\t\tconst schema = config.errors[errorCode];\n\t\t\tif (!schema) throw new Error(`Unknown error: ${errorCode}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\thasState(stateName: string): boolean {\n\t\t\treturn stateName in config.states;\n\t\t},\n\t};\n}\n","/** A phantom-typed key for type-safe middleware state storage. */\nexport interface ContextKey<T> {\n\treadonly _phantom: T;\n\treadonly id: symbol;\n}\n\n/** Creates a unique typed key for storing/retrieving values in context. */\nexport function createKey<T>(name: string): ContextKey<T> {\n\treturn { id: Symbol(name) } as ContextKey<T>;\n}\n","type Middleware<TCtx> = (ctx: TCtx, next: () => Promise<void>) => Promise<void>;\n\n/** Composes an array of middleware into a single function (Koa-style onion model). */\nexport function compose<TCtx>(middleware: Middleware<TCtx>[]): (ctx: TCtx) => Promise<void> {\n\treturn async (ctx: TCtx) => {\n\t\tlet index = -1;\n\t\tasync function dispatch(i: number): Promise<void> {\n\t\t\tif (i <= index) throw new Error(\"next() called multiple times\");\n\t\t\tindex = i;\n\t\t\tconst fn = middleware[i];\n\t\t\tif (!fn) return;\n\t\t\tawait fn(ctx, () => dispatch(i + 1));\n\t\t}\n\t\tawait dispatch(0);\n\t};\n}\n","import type { ZodType, z } from \"zod\";\n\nexport interface WorkflowConfig {\n\tstates: Record<string, ZodType>;\n\tcommands: Record<string, ZodType>;\n\tevents: Record<string, ZodType>;\n\terrors: Record<string, ZodType>;\n}\n\nexport type StateNames<T extends WorkflowConfig> = keyof T[\"states\"] & string;\nexport type CommandNames<T extends WorkflowConfig> = keyof T[\"commands\"] & string;\nexport type EventNames<T extends WorkflowConfig> = keyof T[\"events\"] & string;\nexport type ErrorCodes<T extends WorkflowConfig> = keyof T[\"errors\"] & string;\n\nexport type StateData<\n\tT extends WorkflowConfig,\n\tS extends StateNames<T>,\n> = T[\"states\"][S] extends ZodType ? z.infer<T[\"states\"][S]> : never;\n\nexport type CommandPayload<\n\tT extends WorkflowConfig,\n\tC extends CommandNames<T>,\n> = T[\"commands\"][C] extends ZodType ? z.infer<T[\"commands\"][C]> : never;\n\nexport type EventData<\n\tT extends WorkflowConfig,\n\tE extends EventNames<T>,\n> = T[\"events\"][E] extends ZodType ? z.infer<T[\"events\"][E]> : never;\n\nexport type ErrorData<\n\tT extends WorkflowConfig,\n\tC extends ErrorCodes<T>,\n> = T[\"errors\"][C] extends ZodType ? z.infer<T[\"errors\"][C]> : never;\n\n/** Workflow narrowed to a specific known state. */\nexport interface WorkflowOf<TConfig extends WorkflowConfig, S extends StateNames<TConfig>> {\n\treadonly id: string;\n\treadonly definitionName: string;\n\treadonly state: S;\n\treadonly data: StateData<TConfig, S>;\n\treadonly createdAt: Date;\n\treadonly updatedAt: Date;\n}\n\n/** Discriminated union of all possible workflow states — checking .state narrows .data. */\nexport type Workflow<TConfig extends WorkflowConfig = WorkflowConfig> = {\n\t[S in StateNames<TConfig>]: WorkflowOf<TConfig, S>;\n}[StateNames<TConfig>];\n\nexport type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tcategory: \"validation\";\n\t\t\tsource: \"command\" | \"state\" | \"event\" | \"transition\";\n\t\t\tissues: z.core.$ZodIssue[];\n\t\t\tmessage: string;\n\t }\n\t| {\n\t\t\tcategory: \"domain\";\n\t\t\tcode: ErrorCodes<TConfig>;\n\t\t\tdata: ErrorData<TConfig, ErrorCodes<TConfig>>;\n\t }\n\t| {\n\t\t\tcategory: \"router\";\n\t\t\tcode: \"NO_HANDLER\" | \"UNKNOWN_STATE\";\n\t\t\tmessage: string;\n\t };\n\nexport type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tok: true;\n\t\t\tworkflow: Workflow<TConfig>;\n\t\t\tevents: Array<{ type: EventNames<TConfig>; data: unknown }>;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\terror: PipelineError<TConfig>;\n\t };\n\n/** Thrown internally when Zod validation fails during dispatch. */\nexport class ValidationError extends Error {\n\tconstructor(\n\t\tpublic readonly source: \"command\" | \"state\" | \"event\" | \"transition\",\n\t\tpublic readonly issues: z.core.$ZodIssue[],\n\t) {\n\t\tsuper(`Validation failed (${source}): ${issues.map((i) => i.message).join(\", \")}`);\n\t\tthis.name = \"ValidationError\";\n\t}\n}\n\n/** Thrown internally when a handler calls ctx.error(). Caught by the router. */\nexport class DomainErrorSignal extends Error {\n\tconstructor(\n\t\tpublic readonly code: string,\n\t\tpublic readonly data: unknown,\n\t) {\n\t\tsuper(`Domain error: ${code}`);\n\t\tthis.name = \"DomainErrorSignal\";\n\t}\n}\n","import type { WorkflowDefinition } from \"./definition.js\";\nimport type { ContextKey } from \"./key.js\";\nimport type {\n\tCommandNames,\n\tCommandPayload,\n\tErrorCodes,\n\tErrorData,\n\tEventData,\n\tEventNames,\n\tStateData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n\tWorkflowOf,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\n/** Mutable context flowing through the middleware pipeline during dispatch. */\nexport interface Context<\n\tTConfig extends WorkflowConfig,\n\tTDeps,\n\tTState extends StateNames<TConfig> = StateNames<TConfig>,\n\tTCommand extends CommandNames<TConfig> = CommandNames<TConfig>,\n> {\n\treadonly command: {\n\t\treadonly type: TCommand;\n\t\treadonly payload: CommandPayload<TConfig, TCommand>;\n\t};\n\treadonly workflow: WorkflowOf<TConfig, TState>;\n\treadonly deps: TDeps;\n\n\treadonly data: StateData<TConfig, TState>;\n\tupdate(data: Partial<StateData<TConfig, TState>>): void;\n\n\ttransition<Target extends StateNames<TConfig>>(\n\t\ttarget: Target,\n\t\tdata: StateData<TConfig, Target>,\n\t): void;\n\n\temit<E extends EventNames<TConfig>>(event: { type: E; data: EventData<TConfig, E> }): void;\n\treadonly events: ReadonlyArray<{ type: EventNames<TConfig>; data: unknown }>;\n\n\terror<C extends ErrorCodes<TConfig>>(err: { code: C; data: ErrorData<TConfig, C> }): never;\n\n\tset<T>(key: ContextKey<T>, value: T): void;\n\tget<T>(key: ContextKey<T>): T;\n\tgetOrNull<T>(key: ContextKey<T>): T | undefined;\n\n\t/** @internal — not part of the handler API */\n\tgetWorkflowSnapshot(): Workflow<TConfig>;\n}\n\ninterface DomainEvent {\n\ttype: string;\n\tdata: unknown;\n}\n\n/** @internal Creates a context for dispatch. Not part of public API. */\nexport function createContext<TConfig extends WorkflowConfig, TDeps>(\n\tdefinition: WorkflowDefinition<TConfig>,\n\toriginalWorkflow: Workflow<TConfig>,\n\tcommand: { type: string; payload: unknown },\n\tdeps: TDeps,\n): Context<TConfig, TDeps> {\n\tlet mutableState = originalWorkflow.state;\n\tlet mutableData: Record<string, unknown> = {\n\t\t...(originalWorkflow.data as Record<string, unknown>),\n\t};\n\n\tconst accumulatedEvents: DomainEvent[] = [];\n\tconst middlewareState = new Map<symbol, unknown>();\n\n\tconst ctx = {\n\t\tcommand,\n\t\tworkflow: originalWorkflow,\n\t\tdeps,\n\n\t\tget data() {\n\t\t\treturn { ...mutableData } as StateData<TConfig, StateNames<TConfig>>;\n\t\t},\n\n\t\tupdate(data: Record<string, unknown>) {\n\t\t\tconst merged = { ...mutableData, ...data };\n\t\t\tconst schema = definition.getStateSchema(mutableState);\n\t\t\tconst result = schema.safeParse(merged);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"state\", result.error.issues);\n\t\t\t}\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\ttransition(target: string, data: unknown) {\n\t\t\tif (!definition.hasState(target)) {\n\t\t\t\tthrow new Error(`Unknown state: ${target}`);\n\t\t\t}\n\t\t\tconst schema = definition.getStateSchema(target);\n\t\t\tconst result = schema.safeParse(data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"transition\", result.error.issues);\n\t\t\t}\n\t\t\tmutableState = target;\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\temit(event: { type: string; data: unknown }) {\n\t\t\tconst schema = definition.getEventSchema(event.type);\n\t\t\tconst result = schema.safeParse(event.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"event\", result.error.issues);\n\t\t\t}\n\t\t\taccumulatedEvents.push({ type: event.type, data: result.data });\n\t\t},\n\n\t\tget events() {\n\t\t\treturn [...accumulatedEvents];\n\t\t},\n\n\t\terror(err: { code: string; data: unknown }) {\n\t\t\tconst schema = definition.getErrorSchema(err.code);\n\t\t\tconst result = schema.safeParse(err.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid error data for '${err.code}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new DomainErrorSignal(err.code, result.data);\n\t\t},\n\n\t\tset<T>(key: ContextKey<T>, value: T) {\n\t\t\tmiddlewareState.set(key.id, value);\n\t\t},\n\n\t\tget<T>(key: ContextKey<T>): T {\n\t\t\tif (!middlewareState.has(key.id)) {\n\t\t\t\tthrow new Error(`Context key not set: ${key.id.toString()}`);\n\t\t\t}\n\t\t\treturn middlewareState.get(key.id) as T;\n\t\t},\n\n\t\tgetOrNull<T>(key: ContextKey<T>): T | undefined {\n\t\t\treturn middlewareState.get(key.id) as T | undefined;\n\t\t},\n\n\t\tgetWorkflowSnapshot(): Workflow<TConfig> {\n\t\t\treturn {\n\t\t\t\tid: originalWorkflow.id,\n\t\t\t\tdefinitionName: originalWorkflow.definitionName,\n\t\t\t\tstate: mutableState,\n\t\t\t\tdata: { ...mutableData },\n\t\t\t\tcreatedAt: originalWorkflow.createdAt,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t} as Workflow<TConfig>;\n\t\t},\n\t};\n\n\treturn ctx as unknown as Context<TConfig, TDeps>;\n}\n","import { compose } from \"./compose.js\";\nimport { type Context, createContext } from \"./context.js\";\nimport type { WorkflowDefinition } from \"./definition.js\";\nimport type {\n\tCommandNames,\n\tDispatchResult,\n\tErrorCodes,\n\tErrorData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\n// biome-ignore lint/suspicious/noExplicitAny: internal type erasure for heterogeneous middleware storage\ntype AnyMiddleware = (ctx: any, next: () => Promise<void>) => Promise<void>;\n// biome-ignore lint/suspicious/noExplicitAny: internal type erasure for heterogeneous handler storage\ntype AnyHandler = (ctx: any) => void | Promise<void>;\n\ntype HandlerEntry = { inlineMiddleware: AnyMiddleware[]; handler: AnyMiddleware };\n\nclass StateBuilder<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>> {\n\t/** @internal */ readonly middleware: AnyMiddleware[] = [];\n\t/** @internal */ readonly handlers = new Map<string, HandlerEntry>();\n\n\ton<C extends CommandNames<TConfig>>(\n\t\tcommand: C,\n\t\t...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.handlers.set(command as string, { inlineMiddleware, handler: wrappedHandler });\n\t\treturn this;\n\t}\n\n\tuse(\n\t\tmiddleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>,\n\t): this {\n\t\tthis.middleware.push(middleware as AnyMiddleware);\n\t\treturn this;\n\t}\n}\n\n/**\n * Routes commands to handlers based on workflow state.\n *\n * Supports global middleware, state-scoped middleware, inline middleware,\n * wildcard handlers, and multi-state handlers.\n */\n// biome-ignore lint/complexity/noBannedTypes: {} is correct here — TDeps defaults to \"no deps\", inferred away when deps are provided\nexport class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {\n\tprivate globalMiddleware: AnyMiddleware[] = [];\n\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\tprivate singleStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\tprivate multiStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\tprivate wildcardHandlers = new Map<string, HandlerEntry>();\n\n\tconstructor(\n\t\tprivate readonly definition: WorkflowDefinition<TConfig>,\n\t\tprivate readonly deps: TDeps = {} as TDeps,\n\t) {}\n\n\t/** Adds global middleware or merges another router's handlers. */\n\tuse(\n\t\tmiddlewareOrRouter:\n\t\t\t| ((ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>)\n\t\t\t| WorkflowRouter<TConfig, TDeps>,\n\t): this {\n\t\tif (middlewareOrRouter instanceof WorkflowRouter) {\n\t\t\tthis.merge(middlewareOrRouter);\n\t\t} else {\n\t\t\tthis.globalMiddleware.push(middlewareOrRouter as AnyMiddleware);\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate merge(child: WorkflowRouter<TConfig, TDeps>): void {\n\t\tif (child.definition !== this.definition) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot merge router for '${child.definition.name}' into router for '${this.definition.name}': definition mismatch`,\n\t\t\t);\n\t\t}\n\n\t\tthis.globalMiddleware.push(...child.globalMiddleware);\n\t\tthis.mergeStateBuilders(this.singleStateBuilders, child.singleStateBuilders);\n\t\tthis.mergeStateBuilders(this.multiStateBuilders, child.multiStateBuilders);\n\n\t\tfor (const [command, entry] of child.wildcardHandlers) {\n\t\t\tif (!this.wildcardHandlers.has(command)) {\n\t\t\t\tthis.wildcardHandlers.set(command, {\n\t\t\t\t\tinlineMiddleware: [...entry.inlineMiddleware],\n\t\t\t\t\thandler: entry.handler,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate mergeStateBuilders(\n\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\t\ttarget: Map<string, StateBuilder<TConfig, TDeps, any>>,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\t\tsource: Map<string, StateBuilder<TConfig, TDeps, any>>,\n\t): void {\n\t\tfor (const [stateName, childBuilder] of source) {\n\t\t\tlet parentBuilder = target.get(stateName);\n\t\t\tif (!parentBuilder) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — state name is dynamic at runtime\n\t\t\t\tparentBuilder = new StateBuilder<TConfig, TDeps, any>();\n\t\t\t\ttarget.set(stateName, parentBuilder);\n\t\t\t}\n\t\t\tfor (const [command, entry] of childBuilder.handlers) {\n\t\t\t\tif (!parentBuilder.handlers.has(command)) {\n\t\t\t\t\tparentBuilder.handlers.set(command, {\n\t\t\t\t\t\tinlineMiddleware: [...entry.inlineMiddleware],\n\t\t\t\t\t\thandler: entry.handler,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tparentBuilder.middleware.push(...childBuilder.middleware);\n\t\t}\n\t}\n\n\t/** Registers handlers for one or more states. */\n\tstate<P extends StateNames<TConfig> | readonly StateNames<TConfig>[]>(\n\t\tname: P,\n\t\tsetup: (\n\t\t\tstate: StateBuilder<\n\t\t\t\tTConfig,\n\t\t\t\tTDeps,\n\t\t\t\tP extends readonly (infer S)[] ? S & StateNames<TConfig> : P & StateNames<TConfig>\n\t\t\t>,\n\t\t) => void,\n\t): this {\n\t\tconst names = Array.isArray(name) ? name : [name];\n\t\tconst isMulti = Array.isArray(name);\n\t\tconst routerMap = isMulti ? this.multiStateBuilders : this.singleStateBuilders;\n\n\t\tfor (const n of names as string[]) {\n\t\t\tlet router = routerMap.get(n);\n\t\t\tif (!router) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — state name is dynamic at runtime\n\t\t\t\trouter = new StateBuilder<TConfig, TDeps, any>();\n\t\t\t\trouterMap.set(n, router);\n\t\t\t}\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — setup callback expects a specific state type\n\t\t\tsetup(router as any);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Registers a wildcard handler that matches any state. */\n\ton<C extends CommandNames<TConfig>>(\n\t\t_state: \"*\",\n\t\tcommand: C,\n\t\t...fns: [\n\t\t\t...AnyMiddleware[],\n\t\t\t(ctx: Context<TConfig, TDeps, StateNames<TConfig>, C>) => void | Promise<void>,\n\t\t]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.wildcardHandlers.set(command as string, {\n\t\t\tinlineMiddleware,\n\t\t\thandler: wrappedHandler,\n\t\t});\n\t\treturn this;\n\t}\n\n\t/** Dispatches a command to the appropriate handler and returns the result. */\n\tasync dispatch(\n\t\tworkflow: Workflow<TConfig>,\n\t\tcommand: { type: CommandNames<TConfig>; payload: unknown },\n\t): Promise<DispatchResult<TConfig>> {\n\t\tif (!this.definition.hasState(workflow.state)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"UNKNOWN_STATE\",\n\t\t\t\t\tmessage: `Unknown state: ${workflow.state}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst commandSchema = this.definition.getCommandSchema(command.type);\n\t\tconst payloadResult = commandSchema.safeParse(command.payload);\n\t\tif (!payloadResult.success) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"validation\",\n\t\t\t\t\tsource: \"command\",\n\t\t\t\t\tissues: payloadResult.error.issues,\n\t\t\t\t\tmessage: `Invalid command payload: ${payloadResult.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst validatedCommand = { type: command.type, payload: payloadResult.data };\n\n\t\tconst stateName = workflow.state;\n\t\tconst singleRouter = this.singleStateBuilders.get(stateName);\n\t\tconst multiRouter = this.multiStateBuilders.get(stateName);\n\t\tconst singleHandler = singleRouter?.handlers.get(command.type);\n\t\tconst multiHandler = multiRouter?.handlers.get(command.type);\n\t\tconst wildcardHandler = this.wildcardHandlers.get(command.type);\n\n\t\tlet routeEntry: HandlerEntry | undefined;\n\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — matched router's state type is dynamic\n\t\tlet matchedRouter: StateBuilder<TConfig, TDeps, any> | undefined;\n\n\t\tif (singleHandler) {\n\t\t\trouteEntry = singleHandler;\n\t\t\tmatchedRouter = singleRouter;\n\t\t} else if (multiHandler) {\n\t\t\trouteEntry = multiHandler;\n\t\t\tmatchedRouter = multiRouter;\n\t\t} else if (wildcardHandler) {\n\t\t\trouteEntry = wildcardHandler;\n\t\t\tmatchedRouter = undefined;\n\t\t}\n\n\t\tif (!routeEntry) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"NO_HANDLER\",\n\t\t\t\t\tmessage: `No handler for command '${command.type}' in state '${stateName}'`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst stateMiddleware: AnyMiddleware[] = [];\n\t\tif (matchedRouter) {\n\t\t\tif (singleRouter) stateMiddleware.push(...singleRouter.middleware);\n\t\t\tif (multiRouter && multiRouter !== singleRouter)\n\t\t\t\tstateMiddleware.push(...multiRouter.middleware);\n\t\t}\n\n\t\tconst chain: AnyMiddleware[] = [\n\t\t\t...this.globalMiddleware,\n\t\t\t...stateMiddleware,\n\t\t\t...routeEntry.inlineMiddleware,\n\t\t\trouteEntry.handler,\n\t\t];\n\n\t\tconst ctx = createContext<TConfig, TDeps>(\n\t\t\tthis.definition,\n\t\t\tworkflow,\n\t\t\tvalidatedCommand,\n\t\t\tthis.deps,\n\t\t);\n\n\t\ttry {\n\t\t\tconst composed = compose(chain);\n\t\t\tawait composed(ctx);\n\t\t\treturn {\n\t\t\t\tok: true as const,\n\t\t\t\tworkflow: ctx.getWorkflowSnapshot(),\n\t\t\t\tevents: [...ctx.events],\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof DomainErrorSignal) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"domain\" as const,\n\t\t\t\t\t\tcode: err.code as ErrorCodes<TConfig>,\n\t\t\t\t\t\tdata: err.data as ErrorData<TConfig, ErrorCodes<TConfig>>,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof ValidationError) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"validation\" as const,\n\t\t\t\t\t\tsource: err.source,\n\t\t\t\t\t\tissues: err.issues,\n\t\t\t\t\t\tmessage: err.message,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -159,8 +159,10 @@ declare class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {
|
|
|
159
159
|
private multiStateBuilders;
|
|
160
160
|
private wildcardHandlers;
|
|
161
161
|
constructor(definition: WorkflowDefinition<TConfig>, deps?: TDeps);
|
|
162
|
-
/** Adds global middleware
|
|
163
|
-
use(
|
|
162
|
+
/** Adds global middleware or merges another router's handlers. */
|
|
163
|
+
use(middlewareOrRouter: ((ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>) | WorkflowRouter<TConfig, TDeps>): this;
|
|
164
|
+
private merge;
|
|
165
|
+
private mergeStateBuilders;
|
|
164
166
|
/** Registers handlers for one or more states. */
|
|
165
167
|
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;
|
|
166
168
|
/** Registers a wildcard handler that matches any state. */
|
package/dist/index.d.ts
CHANGED
|
@@ -159,8 +159,10 @@ declare class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {
|
|
|
159
159
|
private multiStateBuilders;
|
|
160
160
|
private wildcardHandlers;
|
|
161
161
|
constructor(definition: WorkflowDefinition<TConfig>, deps?: TDeps);
|
|
162
|
-
/** Adds global middleware
|
|
163
|
-
use(
|
|
162
|
+
/** Adds global middleware or merges another router's handlers. */
|
|
163
|
+
use(middlewareOrRouter: ((ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>) | WorkflowRouter<TConfig, TDeps>): this;
|
|
164
|
+
private merge;
|
|
165
|
+
private mergeStateBuilders;
|
|
164
166
|
/** Registers handlers for one or more states. */
|
|
165
167
|
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;
|
|
166
168
|
/** Registers a wildcard handler that matches any state. */
|
package/dist/index.js
CHANGED
|
@@ -190,20 +190,62 @@ var StateBuilder = class {
|
|
|
190
190
|
return this;
|
|
191
191
|
}
|
|
192
192
|
};
|
|
193
|
-
var WorkflowRouter = class {
|
|
193
|
+
var WorkflowRouter = class _WorkflowRouter {
|
|
194
194
|
constructor(definition, deps = {}) {
|
|
195
195
|
this.definition = definition;
|
|
196
196
|
this.deps = deps;
|
|
197
197
|
}
|
|
198
198
|
globalMiddleware = [];
|
|
199
|
+
// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types
|
|
199
200
|
singleStateBuilders = /* @__PURE__ */ new Map();
|
|
201
|
+
// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types
|
|
200
202
|
multiStateBuilders = /* @__PURE__ */ new Map();
|
|
201
203
|
wildcardHandlers = /* @__PURE__ */ new Map();
|
|
202
|
-
/** Adds global middleware
|
|
203
|
-
use(
|
|
204
|
-
|
|
204
|
+
/** Adds global middleware or merges another router's handlers. */
|
|
205
|
+
use(middlewareOrRouter) {
|
|
206
|
+
if (middlewareOrRouter instanceof _WorkflowRouter) {
|
|
207
|
+
this.merge(middlewareOrRouter);
|
|
208
|
+
} else {
|
|
209
|
+
this.globalMiddleware.push(middlewareOrRouter);
|
|
210
|
+
}
|
|
205
211
|
return this;
|
|
206
212
|
}
|
|
213
|
+
merge(child) {
|
|
214
|
+
if (child.definition !== this.definition) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Cannot merge router for '${child.definition.name}' into router for '${this.definition.name}': definition mismatch`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
this.globalMiddleware.push(...child.globalMiddleware);
|
|
220
|
+
this.mergeStateBuilders(this.singleStateBuilders, child.singleStateBuilders);
|
|
221
|
+
this.mergeStateBuilders(this.multiStateBuilders, child.multiStateBuilders);
|
|
222
|
+
for (const [command, entry] of child.wildcardHandlers) {
|
|
223
|
+
if (!this.wildcardHandlers.has(command)) {
|
|
224
|
+
this.wildcardHandlers.set(command, {
|
|
225
|
+
inlineMiddleware: [...entry.inlineMiddleware],
|
|
226
|
+
handler: entry.handler
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
mergeStateBuilders(target, source) {
|
|
232
|
+
for (const [stateName, childBuilder] of source) {
|
|
233
|
+
let parentBuilder = target.get(stateName);
|
|
234
|
+
if (!parentBuilder) {
|
|
235
|
+
parentBuilder = new StateBuilder();
|
|
236
|
+
target.set(stateName, parentBuilder);
|
|
237
|
+
}
|
|
238
|
+
for (const [command, entry] of childBuilder.handlers) {
|
|
239
|
+
if (!parentBuilder.handlers.has(command)) {
|
|
240
|
+
parentBuilder.handlers.set(command, {
|
|
241
|
+
inlineMiddleware: [...entry.inlineMiddleware],
|
|
242
|
+
handler: entry.handler
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
parentBuilder.middleware.push(...childBuilder.middleware);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
207
249
|
/** Registers handlers for one or more states. */
|
|
208
250
|
state(name, setup) {
|
|
209
251
|
const names = Array.isArray(name) ? name : [name];
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/definition.ts","../src/key.ts","../src/compose.ts","../src/types.ts","../src/context.ts","../src/router.ts"],"sourcesContent":["import type { ZodType, z } from \"zod\";\nimport type { StateNames, WorkflowConfig, WorkflowOf } from \"./types.js\";\n\n/** The result of defineWorkflow() — holds schemas and creates workflow instances. */\nexport interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {\n\treadonly config: TConfig;\n\treadonly name: string;\n\tcreateWorkflow<S extends StateNames<TConfig>>(\n\t\tid: string,\n\t\tconfig: { initialState: S; data: z.infer<TConfig[\"states\"][S]> },\n\t): WorkflowOf<TConfig, S>;\n\tgetStateSchema(stateName: string): ZodType;\n\tgetCommandSchema(commandName: string): ZodType;\n\tgetEventSchema(eventName: string): ZodType;\n\tgetErrorSchema(errorCode: string): ZodType;\n\thasState(stateName: string): boolean;\n}\n\n/**\n * Creates a workflow definition from a name and Zod schema configuration.\n */\nexport function defineWorkflow<const TConfig extends WorkflowConfig>(\n\tname: string,\n\tconfig: TConfig,\n): WorkflowDefinition<TConfig> {\n\treturn {\n\t\tconfig,\n\t\tname,\n\n\t\tcreateWorkflow(id, wfConfig) {\n\t\t\tconst schema = config.states[wfConfig.initialState as string];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${wfConfig.initialState as string}`);\n\t\t\tconst result = schema.safeParse(wfConfig.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid initial data for state '${wfConfig.initialState as string}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst now = new Date();\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tdefinitionName: name,\n\t\t\t\tstate: wfConfig.initialState,\n\t\t\t\tdata: result.data,\n\t\t\t\tcreatedAt: now,\n\t\t\t\tupdatedAt: now,\n\t\t\t} as WorkflowOf<TConfig, typeof wfConfig.initialState>;\n\t\t},\n\n\t\tgetStateSchema(stateName: string): ZodType {\n\t\t\tconst schema = config.states[stateName];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${stateName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetCommandSchema(commandName: string): ZodType {\n\t\t\tconst schema = config.commands[commandName];\n\t\t\tif (!schema) throw new Error(`Unknown command: ${commandName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetEventSchema(eventName: string): ZodType {\n\t\t\tconst schema = config.events[eventName];\n\t\t\tif (!schema) throw new Error(`Unknown event: ${eventName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetErrorSchema(errorCode: string): ZodType {\n\t\t\tconst schema = config.errors[errorCode];\n\t\t\tif (!schema) throw new Error(`Unknown error: ${errorCode}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\thasState(stateName: string): boolean {\n\t\t\treturn stateName in config.states;\n\t\t},\n\t};\n}\n","/** A phantom-typed key for type-safe middleware state storage. */\nexport interface ContextKey<T> {\n\treadonly _phantom: T;\n\treadonly id: symbol;\n}\n\n/** Creates a unique typed key for storing/retrieving values in context. */\nexport function createKey<T>(name: string): ContextKey<T> {\n\treturn { id: Symbol(name) } as ContextKey<T>;\n}\n","type Middleware<TCtx> = (ctx: TCtx, next: () => Promise<void>) => Promise<void>;\n\n/** Composes an array of middleware into a single function (Koa-style onion model). */\nexport function compose<TCtx>(middleware: Middleware<TCtx>[]): (ctx: TCtx) => Promise<void> {\n\treturn async (ctx: TCtx) => {\n\t\tlet index = -1;\n\t\tasync function dispatch(i: number): Promise<void> {\n\t\t\tif (i <= index) throw new Error(\"next() called multiple times\");\n\t\t\tindex = i;\n\t\t\tconst fn = middleware[i];\n\t\t\tif (!fn) return;\n\t\t\tawait fn(ctx, () => dispatch(i + 1));\n\t\t}\n\t\tawait dispatch(0);\n\t};\n}\n","import type { ZodType, z } from \"zod\";\n\nexport interface WorkflowConfig {\n\tstates: Record<string, ZodType>;\n\tcommands: Record<string, ZodType>;\n\tevents: Record<string, ZodType>;\n\terrors: Record<string, ZodType>;\n}\n\nexport type StateNames<T extends WorkflowConfig> = keyof T[\"states\"] & string;\nexport type CommandNames<T extends WorkflowConfig> = keyof T[\"commands\"] & string;\nexport type EventNames<T extends WorkflowConfig> = keyof T[\"events\"] & string;\nexport type ErrorCodes<T extends WorkflowConfig> = keyof T[\"errors\"] & string;\n\nexport type StateData<\n\tT extends WorkflowConfig,\n\tS extends StateNames<T>,\n> = T[\"states\"][S] extends ZodType ? z.infer<T[\"states\"][S]> : never;\n\nexport type CommandPayload<\n\tT extends WorkflowConfig,\n\tC extends CommandNames<T>,\n> = T[\"commands\"][C] extends ZodType ? z.infer<T[\"commands\"][C]> : never;\n\nexport type EventData<\n\tT extends WorkflowConfig,\n\tE extends EventNames<T>,\n> = T[\"events\"][E] extends ZodType ? z.infer<T[\"events\"][E]> : never;\n\nexport type ErrorData<\n\tT extends WorkflowConfig,\n\tC extends ErrorCodes<T>,\n> = T[\"errors\"][C] extends ZodType ? z.infer<T[\"errors\"][C]> : never;\n\n/** Workflow narrowed to a specific known state. */\nexport interface WorkflowOf<TConfig extends WorkflowConfig, S extends StateNames<TConfig>> {\n\treadonly id: string;\n\treadonly definitionName: string;\n\treadonly state: S;\n\treadonly data: StateData<TConfig, S>;\n\treadonly createdAt: Date;\n\treadonly updatedAt: Date;\n}\n\n/** Discriminated union of all possible workflow states — checking .state narrows .data. */\nexport type Workflow<TConfig extends WorkflowConfig = WorkflowConfig> = {\n\t[S in StateNames<TConfig>]: WorkflowOf<TConfig, S>;\n}[StateNames<TConfig>];\n\nexport type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tcategory: \"validation\";\n\t\t\tsource: \"command\" | \"state\" | \"event\" | \"transition\";\n\t\t\tissues: z.core.$ZodIssue[];\n\t\t\tmessage: string;\n\t }\n\t| {\n\t\t\tcategory: \"domain\";\n\t\t\tcode: ErrorCodes<TConfig>;\n\t\t\tdata: ErrorData<TConfig, ErrorCodes<TConfig>>;\n\t }\n\t| {\n\t\t\tcategory: \"router\";\n\t\t\tcode: \"NO_HANDLER\" | \"UNKNOWN_STATE\";\n\t\t\tmessage: string;\n\t };\n\nexport type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tok: true;\n\t\t\tworkflow: Workflow<TConfig>;\n\t\t\tevents: Array<{ type: EventNames<TConfig>; data: unknown }>;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\terror: PipelineError<TConfig>;\n\t };\n\n/** Thrown internally when Zod validation fails during dispatch. */\nexport class ValidationError extends Error {\n\tconstructor(\n\t\tpublic readonly source: \"command\" | \"state\" | \"event\" | \"transition\",\n\t\tpublic readonly issues: z.core.$ZodIssue[],\n\t) {\n\t\tsuper(`Validation failed (${source}): ${issues.map((i) => i.message).join(\", \")}`);\n\t\tthis.name = \"ValidationError\";\n\t}\n}\n\n/** Thrown internally when a handler calls ctx.error(). Caught by the router. */\nexport class DomainErrorSignal extends Error {\n\tconstructor(\n\t\tpublic readonly code: string,\n\t\tpublic readonly data: unknown,\n\t) {\n\t\tsuper(`Domain error: ${code}`);\n\t\tthis.name = \"DomainErrorSignal\";\n\t}\n}\n","import type { WorkflowDefinition } from \"./definition.js\";\nimport type { ContextKey } from \"./key.js\";\nimport type {\n\tCommandNames,\n\tCommandPayload,\n\tErrorCodes,\n\tErrorData,\n\tEventData,\n\tEventNames,\n\tStateData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n\tWorkflowOf,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\n/** Mutable context flowing through the middleware pipeline during dispatch. */\nexport interface Context<\n\tTConfig extends WorkflowConfig,\n\tTDeps,\n\tTState extends StateNames<TConfig> = StateNames<TConfig>,\n\tTCommand extends CommandNames<TConfig> = CommandNames<TConfig>,\n> {\n\treadonly command: {\n\t\treadonly type: TCommand;\n\t\treadonly payload: CommandPayload<TConfig, TCommand>;\n\t};\n\treadonly workflow: WorkflowOf<TConfig, TState>;\n\treadonly deps: TDeps;\n\n\treadonly data: StateData<TConfig, TState>;\n\tupdate(data: Partial<StateData<TConfig, TState>>): void;\n\n\ttransition<Target extends StateNames<TConfig>>(\n\t\ttarget: Target,\n\t\tdata: StateData<TConfig, Target>,\n\t): void;\n\n\temit<E extends EventNames<TConfig>>(event: { type: E; data: EventData<TConfig, E> }): void;\n\treadonly events: ReadonlyArray<{ type: EventNames<TConfig>; data: unknown }>;\n\n\terror<C extends ErrorCodes<TConfig>>(err: { code: C; data: ErrorData<TConfig, C> }): never;\n\n\tset<T>(key: ContextKey<T>, value: T): void;\n\tget<T>(key: ContextKey<T>): T;\n\tgetOrNull<T>(key: ContextKey<T>): T | undefined;\n\n\t/** @internal — not part of the handler API */\n\tgetWorkflowSnapshot(): Workflow<TConfig>;\n}\n\ninterface DomainEvent {\n\ttype: string;\n\tdata: unknown;\n}\n\n/** @internal Creates a context for dispatch. Not part of public API. */\nexport function createContext<TConfig extends WorkflowConfig, TDeps>(\n\tdefinition: WorkflowDefinition<TConfig>,\n\toriginalWorkflow: Workflow<TConfig>,\n\tcommand: { type: string; payload: unknown },\n\tdeps: TDeps,\n): Context<TConfig, TDeps> {\n\tlet mutableState = originalWorkflow.state;\n\tlet mutableData: Record<string, unknown> = {\n\t\t...(originalWorkflow.data as Record<string, unknown>),\n\t};\n\n\tconst accumulatedEvents: DomainEvent[] = [];\n\tconst middlewareState = new Map<symbol, unknown>();\n\n\tconst ctx = {\n\t\tcommand,\n\t\tworkflow: originalWorkflow,\n\t\tdeps,\n\n\t\tget data() {\n\t\t\treturn { ...mutableData } as StateData<TConfig, StateNames<TConfig>>;\n\t\t},\n\n\t\tupdate(data: Record<string, unknown>) {\n\t\t\tconst merged = { ...mutableData, ...data };\n\t\t\tconst schema = definition.getStateSchema(mutableState);\n\t\t\tconst result = schema.safeParse(merged);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"state\", result.error.issues);\n\t\t\t}\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\ttransition(target: string, data: unknown) {\n\t\t\tif (!definition.hasState(target)) {\n\t\t\t\tthrow new Error(`Unknown state: ${target}`);\n\t\t\t}\n\t\t\tconst schema = definition.getStateSchema(target);\n\t\t\tconst result = schema.safeParse(data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"transition\", result.error.issues);\n\t\t\t}\n\t\t\tmutableState = target;\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\temit(event: { type: string; data: unknown }) {\n\t\t\tconst schema = definition.getEventSchema(event.type);\n\t\t\tconst result = schema.safeParse(event.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"event\", result.error.issues);\n\t\t\t}\n\t\t\taccumulatedEvents.push({ type: event.type, data: result.data });\n\t\t},\n\n\t\tget events() {\n\t\t\treturn [...accumulatedEvents];\n\t\t},\n\n\t\terror(err: { code: string; data: unknown }) {\n\t\t\tconst schema = definition.getErrorSchema(err.code);\n\t\t\tconst result = schema.safeParse(err.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid error data for '${err.code}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new DomainErrorSignal(err.code, result.data);\n\t\t},\n\n\t\tset<T>(key: ContextKey<T>, value: T) {\n\t\t\tmiddlewareState.set(key.id, value);\n\t\t},\n\n\t\tget<T>(key: ContextKey<T>): T {\n\t\t\tif (!middlewareState.has(key.id)) {\n\t\t\t\tthrow new Error(`Context key not set: ${key.id.toString()}`);\n\t\t\t}\n\t\t\treturn middlewareState.get(key.id) as T;\n\t\t},\n\n\t\tgetOrNull<T>(key: ContextKey<T>): T | undefined {\n\t\t\treturn middlewareState.get(key.id) as T | undefined;\n\t\t},\n\n\t\tgetWorkflowSnapshot(): Workflow<TConfig> {\n\t\t\treturn {\n\t\t\t\tid: originalWorkflow.id,\n\t\t\t\tdefinitionName: originalWorkflow.definitionName,\n\t\t\t\tstate: mutableState,\n\t\t\t\tdata: { ...mutableData },\n\t\t\t\tcreatedAt: originalWorkflow.createdAt,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t} as Workflow<TConfig>;\n\t\t},\n\t};\n\n\treturn ctx as unknown as Context<TConfig, TDeps>;\n}\n","import { compose } from \"./compose.js\";\nimport { type Context, createContext } from \"./context.js\";\nimport type { WorkflowDefinition } from \"./definition.js\";\nimport type {\n\tCommandNames,\n\tDispatchResult,\n\tErrorCodes,\n\tErrorData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\ntype AnyMiddleware = (ctx: any, next: () => Promise<void>) => Promise<void>;\ntype AnyHandler = (ctx: any) => void | Promise<void>;\n\ntype HandlerEntry = { inlineMiddleware: AnyMiddleware[]; handler: AnyMiddleware };\n\nclass StateBuilder<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>> {\n\t/** @internal */ readonly middleware: AnyMiddleware[] = [];\n\t/** @internal */ readonly handlers = new Map<string, HandlerEntry>();\n\n\ton<C extends CommandNames<TConfig>>(\n\t\tcommand: C,\n\t\t...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.handlers.set(command as string, { inlineMiddleware, handler: wrappedHandler });\n\t\treturn this;\n\t}\n\n\tuse(\n\t\tmiddleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>,\n\t): this {\n\t\tthis.middleware.push(middleware as AnyMiddleware);\n\t\treturn this;\n\t}\n}\n\n/**\n * Routes commands to handlers based on workflow state.\n *\n * Supports global middleware, state-scoped middleware, inline middleware,\n * wildcard handlers, and multi-state handlers.\n */\nexport class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {\n\tprivate globalMiddleware: AnyMiddleware[] = [];\n\tprivate singleStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\tprivate multiStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\tprivate wildcardHandlers = new Map<string, HandlerEntry>();\n\n\tconstructor(\n\t\tprivate readonly definition: WorkflowDefinition<TConfig>,\n\t\tprivate readonly deps: TDeps = {} as TDeps,\n\t) {}\n\n\t/** Adds global middleware that wraps all dispatches. */\n\tuse(\n\t\tmiddleware: (ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>,\n\t): this {\n\t\tthis.globalMiddleware.push(middleware as AnyMiddleware);\n\t\treturn this;\n\t}\n\n\t/** Registers handlers for one or more states. */\n\tstate<P extends StateNames<TConfig> | readonly StateNames<TConfig>[]>(\n\t\tname: P,\n\t\tsetup: (\n\t\t\tstate: StateBuilder<\n\t\t\t\tTConfig,\n\t\t\t\tTDeps,\n\t\t\t\tP extends readonly (infer S)[] ? S & StateNames<TConfig> : P & StateNames<TConfig>\n\t\t\t>,\n\t\t) => void,\n\t): this {\n\t\tconst names = Array.isArray(name) ? name : [name];\n\t\tconst isMulti = Array.isArray(name);\n\t\tconst routerMap = isMulti ? this.multiStateBuilders : this.singleStateBuilders;\n\n\t\tfor (const n of names as string[]) {\n\t\t\tlet router = routerMap.get(n);\n\t\t\tif (!router) {\n\t\t\t\trouter = new StateBuilder<TConfig, TDeps, any>();\n\t\t\t\trouterMap.set(n, router);\n\t\t\t}\n\t\t\tsetup(router as any);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Registers a wildcard handler that matches any state. */\n\ton<C extends CommandNames<TConfig>>(\n\t\t_state: \"*\",\n\t\tcommand: C,\n\t\t...fns: [\n\t\t\t...AnyMiddleware[],\n\t\t\t(ctx: Context<TConfig, TDeps, StateNames<TConfig>, C>) => void | Promise<void>,\n\t\t]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.wildcardHandlers.set(command as string, {\n\t\t\tinlineMiddleware,\n\t\t\thandler: wrappedHandler,\n\t\t});\n\t\treturn this;\n\t}\n\n\t/** Dispatches a command to the appropriate handler and returns the result. */\n\tasync dispatch(\n\t\tworkflow: Workflow<TConfig>,\n\t\tcommand: { type: CommandNames<TConfig>; payload: unknown },\n\t): Promise<DispatchResult<TConfig>> {\n\t\tif (!this.definition.hasState(workflow.state)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"UNKNOWN_STATE\",\n\t\t\t\t\tmessage: `Unknown state: ${workflow.state}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst commandSchema = this.definition.getCommandSchema(command.type);\n\t\tconst payloadResult = commandSchema.safeParse(command.payload);\n\t\tif (!payloadResult.success) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"validation\",\n\t\t\t\t\tsource: \"command\",\n\t\t\t\t\tissues: payloadResult.error.issues,\n\t\t\t\t\tmessage: `Invalid command payload: ${payloadResult.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst validatedCommand = { type: command.type, payload: payloadResult.data };\n\n\t\tconst stateName = workflow.state;\n\t\tconst singleRouter = this.singleStateBuilders.get(stateName);\n\t\tconst multiRouter = this.multiStateBuilders.get(stateName);\n\t\tconst singleHandler = singleRouter?.handlers.get(command.type);\n\t\tconst multiHandler = multiRouter?.handlers.get(command.type);\n\t\tconst wildcardHandler = this.wildcardHandlers.get(command.type);\n\n\t\tlet routeEntry: HandlerEntry | undefined;\n\t\tlet matchedRouter: StateBuilder<TConfig, TDeps, any> | undefined;\n\n\t\tif (singleHandler) {\n\t\t\trouteEntry = singleHandler;\n\t\t\tmatchedRouter = singleRouter;\n\t\t} else if (multiHandler) {\n\t\t\trouteEntry = multiHandler;\n\t\t\tmatchedRouter = multiRouter;\n\t\t} else if (wildcardHandler) {\n\t\t\trouteEntry = wildcardHandler;\n\t\t\tmatchedRouter = undefined;\n\t\t}\n\n\t\tif (!routeEntry) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"NO_HANDLER\",\n\t\t\t\t\tmessage: `No handler for command '${command.type}' in state '${stateName}'`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst stateMiddleware: AnyMiddleware[] = [];\n\t\tif (matchedRouter) {\n\t\t\tif (singleRouter) stateMiddleware.push(...singleRouter.middleware);\n\t\t\tif (multiRouter && multiRouter !== singleRouter)\n\t\t\t\tstateMiddleware.push(...multiRouter.middleware);\n\t\t}\n\n\t\tconst chain: AnyMiddleware[] = [\n\t\t\t...this.globalMiddleware,\n\t\t\t...stateMiddleware,\n\t\t\t...routeEntry.inlineMiddleware,\n\t\t\trouteEntry.handler,\n\t\t];\n\n\t\tconst ctx = createContext<TConfig, TDeps>(\n\t\t\tthis.definition,\n\t\t\tworkflow,\n\t\t\tvalidatedCommand,\n\t\t\tthis.deps,\n\t\t);\n\n\t\ttry {\n\t\t\tconst composed = compose(chain);\n\t\t\tawait composed(ctx);\n\t\t\treturn {\n\t\t\t\tok: true as const,\n\t\t\t\tworkflow: ctx.getWorkflowSnapshot(),\n\t\t\t\tevents: [...ctx.events],\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof DomainErrorSignal) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"domain\" as const,\n\t\t\t\t\t\tcode: err.code as ErrorCodes<TConfig>,\n\t\t\t\t\t\tdata: err.data as ErrorData<TConfig, ErrorCodes<TConfig>>,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof ValidationError) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"validation\" as const,\n\t\t\t\t\t\tsource: err.source,\n\t\t\t\t\t\tissues: err.issues,\n\t\t\t\t\t\tmessage: err.message,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/definition.ts","../src/key.ts","../src/compose.ts","../src/types.ts","../src/context.ts","../src/router.ts"],"sourcesContent":["import type { ZodType, z } from \"zod\";\nimport type { StateNames, WorkflowConfig, WorkflowOf } from \"./types.js\";\n\n/** The result of defineWorkflow() — holds schemas and creates workflow instances. */\nexport interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {\n\treadonly config: TConfig;\n\treadonly name: string;\n\tcreateWorkflow<S extends StateNames<TConfig>>(\n\t\tid: string,\n\t\tconfig: { initialState: S; data: z.infer<TConfig[\"states\"][S]> },\n\t): WorkflowOf<TConfig, S>;\n\tgetStateSchema(stateName: string): ZodType;\n\tgetCommandSchema(commandName: string): ZodType;\n\tgetEventSchema(eventName: string): ZodType;\n\tgetErrorSchema(errorCode: string): ZodType;\n\thasState(stateName: string): boolean;\n}\n\n/**\n * Creates a workflow definition from a name and Zod schema configuration.\n */\nexport function defineWorkflow<const TConfig extends WorkflowConfig>(\n\tname: string,\n\tconfig: TConfig,\n): WorkflowDefinition<TConfig> {\n\treturn {\n\t\tconfig,\n\t\tname,\n\n\t\tcreateWorkflow(id, wfConfig) {\n\t\t\tconst schema = config.states[wfConfig.initialState as string];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${wfConfig.initialState as string}`);\n\t\t\tconst result = schema.safeParse(wfConfig.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid initial data for state '${wfConfig.initialState as string}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst now = new Date();\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tdefinitionName: name,\n\t\t\t\tstate: wfConfig.initialState,\n\t\t\t\tdata: result.data,\n\t\t\t\tcreatedAt: now,\n\t\t\t\tupdatedAt: now,\n\t\t\t} as WorkflowOf<TConfig, typeof wfConfig.initialState>;\n\t\t},\n\n\t\tgetStateSchema(stateName: string): ZodType {\n\t\t\tconst schema = config.states[stateName];\n\t\t\tif (!schema) throw new Error(`Unknown state: ${stateName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetCommandSchema(commandName: string): ZodType {\n\t\t\tconst schema = config.commands[commandName];\n\t\t\tif (!schema) throw new Error(`Unknown command: ${commandName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetEventSchema(eventName: string): ZodType {\n\t\t\tconst schema = config.events[eventName];\n\t\t\tif (!schema) throw new Error(`Unknown event: ${eventName}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\tgetErrorSchema(errorCode: string): ZodType {\n\t\t\tconst schema = config.errors[errorCode];\n\t\t\tif (!schema) throw new Error(`Unknown error: ${errorCode}`);\n\t\t\treturn schema;\n\t\t},\n\n\t\thasState(stateName: string): boolean {\n\t\t\treturn stateName in config.states;\n\t\t},\n\t};\n}\n","/** A phantom-typed key for type-safe middleware state storage. */\nexport interface ContextKey<T> {\n\treadonly _phantom: T;\n\treadonly id: symbol;\n}\n\n/** Creates a unique typed key for storing/retrieving values in context. */\nexport function createKey<T>(name: string): ContextKey<T> {\n\treturn { id: Symbol(name) } as ContextKey<T>;\n}\n","type Middleware<TCtx> = (ctx: TCtx, next: () => Promise<void>) => Promise<void>;\n\n/** Composes an array of middleware into a single function (Koa-style onion model). */\nexport function compose<TCtx>(middleware: Middleware<TCtx>[]): (ctx: TCtx) => Promise<void> {\n\treturn async (ctx: TCtx) => {\n\t\tlet index = -1;\n\t\tasync function dispatch(i: number): Promise<void> {\n\t\t\tif (i <= index) throw new Error(\"next() called multiple times\");\n\t\t\tindex = i;\n\t\t\tconst fn = middleware[i];\n\t\t\tif (!fn) return;\n\t\t\tawait fn(ctx, () => dispatch(i + 1));\n\t\t}\n\t\tawait dispatch(0);\n\t};\n}\n","import type { ZodType, z } from \"zod\";\n\nexport interface WorkflowConfig {\n\tstates: Record<string, ZodType>;\n\tcommands: Record<string, ZodType>;\n\tevents: Record<string, ZodType>;\n\terrors: Record<string, ZodType>;\n}\n\nexport type StateNames<T extends WorkflowConfig> = keyof T[\"states\"] & string;\nexport type CommandNames<T extends WorkflowConfig> = keyof T[\"commands\"] & string;\nexport type EventNames<T extends WorkflowConfig> = keyof T[\"events\"] & string;\nexport type ErrorCodes<T extends WorkflowConfig> = keyof T[\"errors\"] & string;\n\nexport type StateData<\n\tT extends WorkflowConfig,\n\tS extends StateNames<T>,\n> = T[\"states\"][S] extends ZodType ? z.infer<T[\"states\"][S]> : never;\n\nexport type CommandPayload<\n\tT extends WorkflowConfig,\n\tC extends CommandNames<T>,\n> = T[\"commands\"][C] extends ZodType ? z.infer<T[\"commands\"][C]> : never;\n\nexport type EventData<\n\tT extends WorkflowConfig,\n\tE extends EventNames<T>,\n> = T[\"events\"][E] extends ZodType ? z.infer<T[\"events\"][E]> : never;\n\nexport type ErrorData<\n\tT extends WorkflowConfig,\n\tC extends ErrorCodes<T>,\n> = T[\"errors\"][C] extends ZodType ? z.infer<T[\"errors\"][C]> : never;\n\n/** Workflow narrowed to a specific known state. */\nexport interface WorkflowOf<TConfig extends WorkflowConfig, S extends StateNames<TConfig>> {\n\treadonly id: string;\n\treadonly definitionName: string;\n\treadonly state: S;\n\treadonly data: StateData<TConfig, S>;\n\treadonly createdAt: Date;\n\treadonly updatedAt: Date;\n}\n\n/** Discriminated union of all possible workflow states — checking .state narrows .data. */\nexport type Workflow<TConfig extends WorkflowConfig = WorkflowConfig> = {\n\t[S in StateNames<TConfig>]: WorkflowOf<TConfig, S>;\n}[StateNames<TConfig>];\n\nexport type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tcategory: \"validation\";\n\t\t\tsource: \"command\" | \"state\" | \"event\" | \"transition\";\n\t\t\tissues: z.core.$ZodIssue[];\n\t\t\tmessage: string;\n\t }\n\t| {\n\t\t\tcategory: \"domain\";\n\t\t\tcode: ErrorCodes<TConfig>;\n\t\t\tdata: ErrorData<TConfig, ErrorCodes<TConfig>>;\n\t }\n\t| {\n\t\t\tcategory: \"router\";\n\t\t\tcode: \"NO_HANDLER\" | \"UNKNOWN_STATE\";\n\t\t\tmessage: string;\n\t };\n\nexport type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> =\n\t| {\n\t\t\tok: true;\n\t\t\tworkflow: Workflow<TConfig>;\n\t\t\tevents: Array<{ type: EventNames<TConfig>; data: unknown }>;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\terror: PipelineError<TConfig>;\n\t };\n\n/** Thrown internally when Zod validation fails during dispatch. */\nexport class ValidationError extends Error {\n\tconstructor(\n\t\tpublic readonly source: \"command\" | \"state\" | \"event\" | \"transition\",\n\t\tpublic readonly issues: z.core.$ZodIssue[],\n\t) {\n\t\tsuper(`Validation failed (${source}): ${issues.map((i) => i.message).join(\", \")}`);\n\t\tthis.name = \"ValidationError\";\n\t}\n}\n\n/** Thrown internally when a handler calls ctx.error(). Caught by the router. */\nexport class DomainErrorSignal extends Error {\n\tconstructor(\n\t\tpublic readonly code: string,\n\t\tpublic readonly data: unknown,\n\t) {\n\t\tsuper(`Domain error: ${code}`);\n\t\tthis.name = \"DomainErrorSignal\";\n\t}\n}\n","import type { WorkflowDefinition } from \"./definition.js\";\nimport type { ContextKey } from \"./key.js\";\nimport type {\n\tCommandNames,\n\tCommandPayload,\n\tErrorCodes,\n\tErrorData,\n\tEventData,\n\tEventNames,\n\tStateData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n\tWorkflowOf,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\n/** Mutable context flowing through the middleware pipeline during dispatch. */\nexport interface Context<\n\tTConfig extends WorkflowConfig,\n\tTDeps,\n\tTState extends StateNames<TConfig> = StateNames<TConfig>,\n\tTCommand extends CommandNames<TConfig> = CommandNames<TConfig>,\n> {\n\treadonly command: {\n\t\treadonly type: TCommand;\n\t\treadonly payload: CommandPayload<TConfig, TCommand>;\n\t};\n\treadonly workflow: WorkflowOf<TConfig, TState>;\n\treadonly deps: TDeps;\n\n\treadonly data: StateData<TConfig, TState>;\n\tupdate(data: Partial<StateData<TConfig, TState>>): void;\n\n\ttransition<Target extends StateNames<TConfig>>(\n\t\ttarget: Target,\n\t\tdata: StateData<TConfig, Target>,\n\t): void;\n\n\temit<E extends EventNames<TConfig>>(event: { type: E; data: EventData<TConfig, E> }): void;\n\treadonly events: ReadonlyArray<{ type: EventNames<TConfig>; data: unknown }>;\n\n\terror<C extends ErrorCodes<TConfig>>(err: { code: C; data: ErrorData<TConfig, C> }): never;\n\n\tset<T>(key: ContextKey<T>, value: T): void;\n\tget<T>(key: ContextKey<T>): T;\n\tgetOrNull<T>(key: ContextKey<T>): T | undefined;\n\n\t/** @internal — not part of the handler API */\n\tgetWorkflowSnapshot(): Workflow<TConfig>;\n}\n\ninterface DomainEvent {\n\ttype: string;\n\tdata: unknown;\n}\n\n/** @internal Creates a context for dispatch. Not part of public API. */\nexport function createContext<TConfig extends WorkflowConfig, TDeps>(\n\tdefinition: WorkflowDefinition<TConfig>,\n\toriginalWorkflow: Workflow<TConfig>,\n\tcommand: { type: string; payload: unknown },\n\tdeps: TDeps,\n): Context<TConfig, TDeps> {\n\tlet mutableState = originalWorkflow.state;\n\tlet mutableData: Record<string, unknown> = {\n\t\t...(originalWorkflow.data as Record<string, unknown>),\n\t};\n\n\tconst accumulatedEvents: DomainEvent[] = [];\n\tconst middlewareState = new Map<symbol, unknown>();\n\n\tconst ctx = {\n\t\tcommand,\n\t\tworkflow: originalWorkflow,\n\t\tdeps,\n\n\t\tget data() {\n\t\t\treturn { ...mutableData } as StateData<TConfig, StateNames<TConfig>>;\n\t\t},\n\n\t\tupdate(data: Record<string, unknown>) {\n\t\t\tconst merged = { ...mutableData, ...data };\n\t\t\tconst schema = definition.getStateSchema(mutableState);\n\t\t\tconst result = schema.safeParse(merged);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"state\", result.error.issues);\n\t\t\t}\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\ttransition(target: string, data: unknown) {\n\t\t\tif (!definition.hasState(target)) {\n\t\t\t\tthrow new Error(`Unknown state: ${target}`);\n\t\t\t}\n\t\t\tconst schema = definition.getStateSchema(target);\n\t\t\tconst result = schema.safeParse(data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"transition\", result.error.issues);\n\t\t\t}\n\t\t\tmutableState = target;\n\t\t\tmutableData = result.data as Record<string, unknown>;\n\t\t},\n\n\t\temit(event: { type: string; data: unknown }) {\n\t\t\tconst schema = definition.getEventSchema(event.type);\n\t\t\tconst result = schema.safeParse(event.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new ValidationError(\"event\", result.error.issues);\n\t\t\t}\n\t\t\taccumulatedEvents.push({ type: event.type, data: result.data });\n\t\t},\n\n\t\tget events() {\n\t\t\treturn [...accumulatedEvents];\n\t\t},\n\n\t\terror(err: { code: string; data: unknown }) {\n\t\t\tconst schema = definition.getErrorSchema(err.code);\n\t\t\tconst result = schema.safeParse(err.data);\n\t\t\tif (!result.success) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid error data for '${err.code}': ${result.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new DomainErrorSignal(err.code, result.data);\n\t\t},\n\n\t\tset<T>(key: ContextKey<T>, value: T) {\n\t\t\tmiddlewareState.set(key.id, value);\n\t\t},\n\n\t\tget<T>(key: ContextKey<T>): T {\n\t\t\tif (!middlewareState.has(key.id)) {\n\t\t\t\tthrow new Error(`Context key not set: ${key.id.toString()}`);\n\t\t\t}\n\t\t\treturn middlewareState.get(key.id) as T;\n\t\t},\n\n\t\tgetOrNull<T>(key: ContextKey<T>): T | undefined {\n\t\t\treturn middlewareState.get(key.id) as T | undefined;\n\t\t},\n\n\t\tgetWorkflowSnapshot(): Workflow<TConfig> {\n\t\t\treturn {\n\t\t\t\tid: originalWorkflow.id,\n\t\t\t\tdefinitionName: originalWorkflow.definitionName,\n\t\t\t\tstate: mutableState,\n\t\t\t\tdata: { ...mutableData },\n\t\t\t\tcreatedAt: originalWorkflow.createdAt,\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t} as Workflow<TConfig>;\n\t\t},\n\t};\n\n\treturn ctx as unknown as Context<TConfig, TDeps>;\n}\n","import { compose } from \"./compose.js\";\nimport { type Context, createContext } from \"./context.js\";\nimport type { WorkflowDefinition } from \"./definition.js\";\nimport type {\n\tCommandNames,\n\tDispatchResult,\n\tErrorCodes,\n\tErrorData,\n\tStateNames,\n\tWorkflow,\n\tWorkflowConfig,\n} from \"./types.js\";\nimport { DomainErrorSignal, ValidationError } from \"./types.js\";\n\n// biome-ignore lint/suspicious/noExplicitAny: internal type erasure for heterogeneous middleware storage\ntype AnyMiddleware = (ctx: any, next: () => Promise<void>) => Promise<void>;\n// biome-ignore lint/suspicious/noExplicitAny: internal type erasure for heterogeneous handler storage\ntype AnyHandler = (ctx: any) => void | Promise<void>;\n\ntype HandlerEntry = { inlineMiddleware: AnyMiddleware[]; handler: AnyMiddleware };\n\nclass StateBuilder<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>> {\n\t/** @internal */ readonly middleware: AnyMiddleware[] = [];\n\t/** @internal */ readonly handlers = new Map<string, HandlerEntry>();\n\n\ton<C extends CommandNames<TConfig>>(\n\t\tcommand: C,\n\t\t...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.handlers.set(command as string, { inlineMiddleware, handler: wrappedHandler });\n\t\treturn this;\n\t}\n\n\tuse(\n\t\tmiddleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>,\n\t): this {\n\t\tthis.middleware.push(middleware as AnyMiddleware);\n\t\treturn this;\n\t}\n}\n\n/**\n * Routes commands to handlers based on workflow state.\n *\n * Supports global middleware, state-scoped middleware, inline middleware,\n * wildcard handlers, and multi-state handlers.\n */\n// biome-ignore lint/complexity/noBannedTypes: {} is correct here — TDeps defaults to \"no deps\", inferred away when deps are provided\nexport class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {\n\tprivate globalMiddleware: AnyMiddleware[] = [];\n\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\tprivate singleStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\tprivate multiStateBuilders = new Map<string, StateBuilder<TConfig, TDeps, any>>();\n\tprivate wildcardHandlers = new Map<string, HandlerEntry>();\n\n\tconstructor(\n\t\tprivate readonly definition: WorkflowDefinition<TConfig>,\n\t\tprivate readonly deps: TDeps = {} as TDeps,\n\t) {}\n\n\t/** Adds global middleware or merges another router's handlers. */\n\tuse(\n\t\tmiddlewareOrRouter:\n\t\t\t| ((ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>)\n\t\t\t| WorkflowRouter<TConfig, TDeps>,\n\t): this {\n\t\tif (middlewareOrRouter instanceof WorkflowRouter) {\n\t\t\tthis.merge(middlewareOrRouter);\n\t\t} else {\n\t\t\tthis.globalMiddleware.push(middlewareOrRouter as AnyMiddleware);\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate merge(child: WorkflowRouter<TConfig, TDeps>): void {\n\t\tif (child.definition !== this.definition) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot merge router for '${child.definition.name}' into router for '${this.definition.name}': definition mismatch`,\n\t\t\t);\n\t\t}\n\n\t\tthis.globalMiddleware.push(...child.globalMiddleware);\n\t\tthis.mergeStateBuilders(this.singleStateBuilders, child.singleStateBuilders);\n\t\tthis.mergeStateBuilders(this.multiStateBuilders, child.multiStateBuilders);\n\n\t\tfor (const [command, entry] of child.wildcardHandlers) {\n\t\t\tif (!this.wildcardHandlers.has(command)) {\n\t\t\t\tthis.wildcardHandlers.set(command, {\n\t\t\t\t\tinlineMiddleware: [...entry.inlineMiddleware],\n\t\t\t\t\thandler: entry.handler,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate mergeStateBuilders(\n\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\t\ttarget: Map<string, StateBuilder<TConfig, TDeps, any>>,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — builders store handlers for different state types\n\t\tsource: Map<string, StateBuilder<TConfig, TDeps, any>>,\n\t): void {\n\t\tfor (const [stateName, childBuilder] of source) {\n\t\t\tlet parentBuilder = target.get(stateName);\n\t\t\tif (!parentBuilder) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — state name is dynamic at runtime\n\t\t\t\tparentBuilder = new StateBuilder<TConfig, TDeps, any>();\n\t\t\t\ttarget.set(stateName, parentBuilder);\n\t\t\t}\n\t\t\tfor (const [command, entry] of childBuilder.handlers) {\n\t\t\t\tif (!parentBuilder.handlers.has(command)) {\n\t\t\t\t\tparentBuilder.handlers.set(command, {\n\t\t\t\t\t\tinlineMiddleware: [...entry.inlineMiddleware],\n\t\t\t\t\t\thandler: entry.handler,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tparentBuilder.middleware.push(...childBuilder.middleware);\n\t\t}\n\t}\n\n\t/** Registers handlers for one or more states. */\n\tstate<P extends StateNames<TConfig> | readonly StateNames<TConfig>[]>(\n\t\tname: P,\n\t\tsetup: (\n\t\t\tstate: StateBuilder<\n\t\t\t\tTConfig,\n\t\t\t\tTDeps,\n\t\t\t\tP extends readonly (infer S)[] ? S & StateNames<TConfig> : P & StateNames<TConfig>\n\t\t\t>,\n\t\t) => void,\n\t): this {\n\t\tconst names = Array.isArray(name) ? name : [name];\n\t\tconst isMulti = Array.isArray(name);\n\t\tconst routerMap = isMulti ? this.multiStateBuilders : this.singleStateBuilders;\n\n\t\tfor (const n of names as string[]) {\n\t\t\tlet router = routerMap.get(n);\n\t\t\tif (!router) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — state name is dynamic at runtime\n\t\t\t\trouter = new StateBuilder<TConfig, TDeps, any>();\n\t\t\t\trouterMap.set(n, router);\n\t\t\t}\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — setup callback expects a specific state type\n\t\t\tsetup(router as any);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Registers a wildcard handler that matches any state. */\n\ton<C extends CommandNames<TConfig>>(\n\t\t_state: \"*\",\n\t\tcommand: C,\n\t\t...fns: [\n\t\t\t...AnyMiddleware[],\n\t\t\t(ctx: Context<TConfig, TDeps, StateNames<TConfig>, C>) => void | Promise<void>,\n\t\t]\n\t): this {\n\t\tif (fns.length === 0) throw new Error(\"on() requires at least a handler\");\n\t\tconst handler = fns.pop() as AnyHandler;\n\t\tconst inlineMiddleware = fns as AnyMiddleware[];\n\t\tconst wrappedHandler: AnyMiddleware = async (ctx, _next) => {\n\t\t\tawait handler(ctx);\n\t\t};\n\t\tthis.wildcardHandlers.set(command as string, {\n\t\t\tinlineMiddleware,\n\t\t\thandler: wrappedHandler,\n\t\t});\n\t\treturn this;\n\t}\n\n\t/** Dispatches a command to the appropriate handler and returns the result. */\n\tasync dispatch(\n\t\tworkflow: Workflow<TConfig>,\n\t\tcommand: { type: CommandNames<TConfig>; payload: unknown },\n\t): Promise<DispatchResult<TConfig>> {\n\t\tif (!this.definition.hasState(workflow.state)) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"UNKNOWN_STATE\",\n\t\t\t\t\tmessage: `Unknown state: ${workflow.state}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst commandSchema = this.definition.getCommandSchema(command.type);\n\t\tconst payloadResult = commandSchema.safeParse(command.payload);\n\t\tif (!payloadResult.success) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"validation\",\n\t\t\t\t\tsource: \"command\",\n\t\t\t\t\tissues: payloadResult.error.issues,\n\t\t\t\t\tmessage: `Invalid command payload: ${payloadResult.error.issues.map((i) => i.message).join(\", \")}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst validatedCommand = { type: command.type, payload: payloadResult.data };\n\n\t\tconst stateName = workflow.state;\n\t\tconst singleRouter = this.singleStateBuilders.get(stateName);\n\t\tconst multiRouter = this.multiStateBuilders.get(stateName);\n\t\tconst singleHandler = singleRouter?.handlers.get(command.type);\n\t\tconst multiHandler = multiRouter?.handlers.get(command.type);\n\t\tconst wildcardHandler = this.wildcardHandlers.get(command.type);\n\n\t\tlet routeEntry: HandlerEntry | undefined;\n\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — matched router's state type is dynamic\n\t\tlet matchedRouter: StateBuilder<TConfig, TDeps, any> | undefined;\n\n\t\tif (singleHandler) {\n\t\t\trouteEntry = singleHandler;\n\t\t\tmatchedRouter = singleRouter;\n\t\t} else if (multiHandler) {\n\t\t\trouteEntry = multiHandler;\n\t\t\tmatchedRouter = multiRouter;\n\t\t} else if (wildcardHandler) {\n\t\t\trouteEntry = wildcardHandler;\n\t\t\tmatchedRouter = undefined;\n\t\t}\n\n\t\tif (!routeEntry) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"router\",\n\t\t\t\t\tcode: \"NO_HANDLER\",\n\t\t\t\t\tmessage: `No handler for command '${command.type}' in state '${stateName}'`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst stateMiddleware: AnyMiddleware[] = [];\n\t\tif (matchedRouter) {\n\t\t\tif (singleRouter) stateMiddleware.push(...singleRouter.middleware);\n\t\t\tif (multiRouter && multiRouter !== singleRouter)\n\t\t\t\tstateMiddleware.push(...multiRouter.middleware);\n\t\t}\n\n\t\tconst chain: AnyMiddleware[] = [\n\t\t\t...this.globalMiddleware,\n\t\t\t...stateMiddleware,\n\t\t\t...routeEntry.inlineMiddleware,\n\t\t\trouteEntry.handler,\n\t\t];\n\n\t\tconst ctx = createContext<TConfig, TDeps>(\n\t\t\tthis.definition,\n\t\t\tworkflow,\n\t\t\tvalidatedCommand,\n\t\t\tthis.deps,\n\t\t);\n\n\t\ttry {\n\t\t\tconst composed = compose(chain);\n\t\t\tawait composed(ctx);\n\t\t\treturn {\n\t\t\t\tok: true as const,\n\t\t\t\tworkflow: ctx.getWorkflowSnapshot(),\n\t\t\t\tevents: [...ctx.events],\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof DomainErrorSignal) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"domain\" as const,\n\t\t\t\t\t\tcode: err.code as ErrorCodes<TConfig>,\n\t\t\t\t\t\tdata: err.data as ErrorData<TConfig, ErrorCodes<TConfig>>,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof ValidationError) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"validation\" as const,\n\t\t\t\t\t\tsource: err.source,\n\t\t\t\t\t\tissues: err.issues,\n\t\t\t\t\t\tmessage: err.message,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|