@rytejs/core 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -166,7 +166,11 @@ var WorkflowExecutor = class {
166
166
  };
167
167
  return;
168
168
  }
169
- const dispatchResult = await router.dispatch(restoreResult.workflow, ctx.command);
169
+ const dispatchResult = await router.dispatch(
170
+ restoreResult.workflow,
171
+ ctx.command.type,
172
+ ctx.command.payload
173
+ );
170
174
  ctx.result = dispatchResult;
171
175
  if (dispatchResult.ok) {
172
176
  ctx.snapshot = definition.serialize(dispatchResult.workflow);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/executor/index.ts","../../src/compose.ts","../../src/store/errors.ts","../../src/executor/executor.ts"],"sourcesContent":["export { WorkflowExecutor } from \"./executor.js\";\nexport type {\n\tExecutionResult,\n\tExecutorContext,\n\tExecutorError,\n\tExecutorMiddleware,\n} from \"./types.js\";\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","export class ConcurrencyConflictError extends Error {\n\treadonly name = \"ConcurrencyConflictError\";\n\n\tconstructor(\n\t\treadonly workflowId: string,\n\t\treadonly expectedVersion: number,\n\t\treadonly actualVersion: number,\n\t) {\n\t\tsuper(\n\t\t\t`Concurrency conflict for workflow \"${workflowId}\": expected version ${expectedVersion}, actual ${actualVersion}`,\n\t\t);\n\t}\n}\n","import { compose } from \"../compose.js\";\nimport type { WorkflowRouter } from \"../router.js\";\nimport type { WorkflowSnapshot } from \"../snapshot.js\";\nimport { ConcurrencyConflictError } from \"../store/errors.js\";\nimport type { StoreAdapter } from \"../store/types.js\";\nimport type { WorkflowConfig } from \"../types.js\";\nimport type { ExecutionResult, ExecutorContext, ExecutorMiddleware } from \"./types.js\";\n\nexport class WorkflowExecutor<TConfig extends WorkflowConfig> {\n\tprivate readonly middleware: ExecutorMiddleware[] = [];\n\n\tconstructor(\n\t\tpublic readonly router: WorkflowRouter<TConfig>,\n\t\tprivate readonly store: StoreAdapter,\n\t) {}\n\n\tuse(middleware: ExecutorMiddleware): this {\n\t\tthis.middleware.push(middleware);\n\t\treturn this;\n\t}\n\n\tasync execute(\n\t\tid: string,\n\t\tcommand: { type: string; payload: unknown },\n\t\toptions?: { expectedVersion?: number },\n\t): Promise<ExecutionResult> {\n\t\t// 1. Load\n\t\tconst stored = await this.store.load(id);\n\t\tif (!stored) {\n\t\t\treturn { ok: false, error: { category: \"not_found\", id } };\n\t\t}\n\n\t\t// 2. Optimistic version check\n\t\tif (options?.expectedVersion !== undefined && options.expectedVersion !== stored.version) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\tid,\n\t\t\t\t\texpectedVersion: options.expectedVersion,\n\t\t\t\t\tactualVersion: stored.version,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 3. Build context\n\t\tconst ctx: ExecutorContext = {\n\t\t\tid,\n\t\t\tcommand,\n\t\t\tstored,\n\t\t\tresult: null,\n\t\t\tsnapshot: null,\n\t\t\tevents: [],\n\t\t};\n\n\t\t// 4. Run pipeline\n\t\ttry {\n\t\t\tconst chain = [...this.middleware, this.dispatchHandler()];\n\t\t\tawait compose(chain)(ctx);\n\t\t} catch (err) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\terror: err,\n\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 5. Save if dispatch succeeded\n\t\tif (ctx.snapshot) {\n\t\t\tconst newVersion = stored.version + 1;\n\t\t\tconst savedSnapshot = { ...ctx.snapshot, version: newVersion };\n\n\t\t\ttry {\n\t\t\t\tawait this.store.save({\n\t\t\t\t\tid,\n\t\t\t\t\tsnapshot: ctx.snapshot,\n\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\tevents: ctx.events,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof ConcurrencyConflictError) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\t\t\tactualVersion: err.actualVersion,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\t\terror: err,\n\t\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsnapshot: savedSnapshot,\n\t\t\t\tversion: newVersion,\n\t\t\t\tevents: ctx.events,\n\t\t\t};\n\t\t}\n\n\t\t// 6. Dispatch failed — return the error\n\t\tif (ctx.result && !ctx.result.ok) {\n\t\t\treturn { ok: false, error: ctx.result.error };\n\t\t}\n\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: {\n\t\t\t\tcategory: \"unexpected\",\n\t\t\t\terror: new Error(\"Pipeline completed without setting snapshot or error\"),\n\t\t\t\tmessage: \"Pipeline completed without setting snapshot or error\",\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate dispatchHandler(): ExecutorMiddleware {\n\t\tconst definition = this.router.definition;\n\t\tconst router = this.router;\n\n\t\treturn async (ctx, _next) => {\n\t\t\tconst restoreResult = definition.deserialize(ctx.stored.snapshot);\n\t\t\tif (!restoreResult.ok) {\n\t\t\t\tctx.result = {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"restore\" as const,\n\t\t\t\t\t\tid: ctx.id,\n\t\t\t\t\t\tissues: restoreResult.error.issues,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// as never: type erasure — executor holds WorkflowConfig base type,\n\t\t\t// but dispatch validates commands against Zod schemas at runtime\n\t\t\tconst dispatchResult = await router.dispatch(restoreResult.workflow, ctx.command as never);\n\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — DispatchResult<TConfig> assigned to DispatchResult<WorkflowConfig>\n\t\t\tctx.result = dispatchResult as any;\n\n\t\t\tif (dispatchResult.ok) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — TConfig narrows WorkflowSnapshot but ctx.snapshot is unparameterized\n\t\t\t\tctx.snapshot = definition.serialize(dispatchResult.workflow) as any as WorkflowSnapshot;\n\t\t\t\tctx.events = (dispatchResult.events as Array<{ type: string; data: unknown }>).map((e) => ({\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tdata: e.data,\n\t\t\t\t}));\n\t\t\t}\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/executor/index.ts","../../src/compose.ts","../../src/store/errors.ts","../../src/executor/executor.ts"],"sourcesContent":["export { WorkflowExecutor } from \"./executor.js\";\nexport type {\n\tExecutionResult,\n\tExecutorContext,\n\tExecutorError,\n\tExecutorMiddleware,\n} from \"./types.js\";\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","export class ConcurrencyConflictError extends Error {\n\treadonly name = \"ConcurrencyConflictError\";\n\n\tconstructor(\n\t\treadonly workflowId: string,\n\t\treadonly expectedVersion: number,\n\t\treadonly actualVersion: number,\n\t) {\n\t\tsuper(\n\t\t\t`Concurrency conflict for workflow \"${workflowId}\": expected version ${expectedVersion}, actual ${actualVersion}`,\n\t\t);\n\t}\n}\n","import { compose } from \"../compose.js\";\nimport type { WorkflowRouter } from \"../router.js\";\nimport type { WorkflowSnapshot } from \"../snapshot.js\";\nimport { ConcurrencyConflictError } from \"../store/errors.js\";\nimport type { StoreAdapter } from \"../store/types.js\";\nimport type { WorkflowConfig } from \"../types.js\";\nimport type { ExecutionResult, ExecutorContext, ExecutorMiddleware } from \"./types.js\";\n\nexport class WorkflowExecutor<TConfig extends WorkflowConfig> {\n\tprivate readonly middleware: ExecutorMiddleware[] = [];\n\n\tconstructor(\n\t\tpublic readonly router: WorkflowRouter<TConfig>,\n\t\tprivate readonly store: StoreAdapter,\n\t) {}\n\n\tuse(middleware: ExecutorMiddleware): this {\n\t\tthis.middleware.push(middleware);\n\t\treturn this;\n\t}\n\n\tasync execute(\n\t\tid: string,\n\t\tcommand: { type: string; payload: unknown },\n\t\toptions?: { expectedVersion?: number },\n\t): Promise<ExecutionResult> {\n\t\t// 1. Load\n\t\tconst stored = await this.store.load(id);\n\t\tif (!stored) {\n\t\t\treturn { ok: false, error: { category: \"not_found\", id } };\n\t\t}\n\n\t\t// 2. Optimistic version check\n\t\tif (options?.expectedVersion !== undefined && options.expectedVersion !== stored.version) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\tid,\n\t\t\t\t\texpectedVersion: options.expectedVersion,\n\t\t\t\t\tactualVersion: stored.version,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 3. Build context\n\t\tconst ctx: ExecutorContext = {\n\t\t\tid,\n\t\t\tcommand,\n\t\t\tstored,\n\t\t\tresult: null,\n\t\t\tsnapshot: null,\n\t\t\tevents: [],\n\t\t};\n\n\t\t// 4. Run pipeline\n\t\ttry {\n\t\t\tconst chain = [...this.middleware, this.dispatchHandler()];\n\t\t\tawait compose(chain)(ctx);\n\t\t} catch (err) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\terror: err,\n\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 5. Save if dispatch succeeded\n\t\tif (ctx.snapshot) {\n\t\t\tconst newVersion = stored.version + 1;\n\t\t\tconst savedSnapshot = { ...ctx.snapshot, version: newVersion };\n\n\t\t\ttry {\n\t\t\t\tawait this.store.save({\n\t\t\t\t\tid,\n\t\t\t\t\tsnapshot: ctx.snapshot,\n\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\tevents: ctx.events,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof ConcurrencyConflictError) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\t\t\tactualVersion: err.actualVersion,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\t\terror: err,\n\t\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsnapshot: savedSnapshot,\n\t\t\t\tversion: newVersion,\n\t\t\t\tevents: ctx.events,\n\t\t\t};\n\t\t}\n\n\t\t// 6. Dispatch failed — return the error\n\t\tif (ctx.result && !ctx.result.ok) {\n\t\t\treturn { ok: false, error: ctx.result.error };\n\t\t}\n\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: {\n\t\t\t\tcategory: \"unexpected\",\n\t\t\t\terror: new Error(\"Pipeline completed without setting snapshot or error\"),\n\t\t\t\tmessage: \"Pipeline completed without setting snapshot or error\",\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate dispatchHandler(): ExecutorMiddleware {\n\t\tconst definition = this.router.definition;\n\t\tconst router = this.router;\n\n\t\treturn async (ctx, _next) => {\n\t\t\tconst restoreResult = definition.deserialize(ctx.stored.snapshot);\n\t\t\tif (!restoreResult.ok) {\n\t\t\t\tctx.result = {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"restore\" as const,\n\t\t\t\t\t\tid: ctx.id,\n\t\t\t\t\t\tissues: restoreResult.error.issues,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// as never: type erasure — executor holds WorkflowConfig base type,\n\t\t\t// but dispatch validates commands against Zod schemas at runtime\n\t\t\tconst dispatchResult = await router.dispatch(\n\t\t\t\trestoreResult.workflow,\n\t\t\t\tctx.command.type as never,\n\t\t\t\tctx.command.payload as never,\n\t\t\t);\n\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — DispatchResult<TConfig> assigned to DispatchResult<WorkflowConfig>\n\t\t\tctx.result = dispatchResult as any;\n\n\t\t\tif (dispatchResult.ok) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — TConfig narrows WorkflowSnapshot but ctx.snapshot is unparameterized\n\t\t\t\tctx.snapshot = definition.serialize(dispatchResult.workflow) as any as WorkflowSnapshot;\n\t\t\t\tctx.events = (dispatchResult.events as Array<{ type: string; data: unknown }>).map((e) => ({\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tdata: e.data,\n\t\t\t\t}));\n\t\t\t}\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,4 @@
1
- import { d as WorkflowRouter } from '../plugin-CQUbAdxj.cjs';
1
+ import { d as WorkflowRouter } from '../plugin-CUhe4_1Z.cjs';
2
2
  import { S as StoredWorkflow, a as StoreAdapter } from '../types-BuFyzP66.cjs';
3
3
  import { a as WorkflowSnapshot, P as PipelineError, W as WorkflowConfig, D as DispatchResult } from '../snapshot-6pxxtOjB.cjs';
4
4
  import 'zod';
@@ -1,4 +1,4 @@
1
- import { d as WorkflowRouter } from '../plugin-CIVHyBaZ.js';
1
+ import { d as WorkflowRouter } from '../plugin-BVTjxA13.js';
2
2
  import { S as StoredWorkflow, a as StoreAdapter } from '../types-D41DR3xh.js';
3
3
  import { a as WorkflowSnapshot, P as PipelineError, W as WorkflowConfig, D as DispatchResult } from '../snapshot-6pxxtOjB.js';
4
4
  import 'zod';
@@ -119,7 +119,11 @@ var WorkflowExecutor = class {
119
119
  };
120
120
  return;
121
121
  }
122
- const dispatchResult = await router.dispatch(restoreResult.workflow, ctx.command);
122
+ const dispatchResult = await router.dispatch(
123
+ restoreResult.workflow,
124
+ ctx.command.type,
125
+ ctx.command.payload
126
+ );
123
127
  ctx.result = dispatchResult;
124
128
  if (dispatchResult.ok) {
125
129
  ctx.snapshot = definition.serialize(dispatchResult.workflow);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/executor/executor.ts"],"sourcesContent":["import { compose } from \"../compose.js\";\nimport type { WorkflowRouter } from \"../router.js\";\nimport type { WorkflowSnapshot } from \"../snapshot.js\";\nimport { ConcurrencyConflictError } from \"../store/errors.js\";\nimport type { StoreAdapter } from \"../store/types.js\";\nimport type { WorkflowConfig } from \"../types.js\";\nimport type { ExecutionResult, ExecutorContext, ExecutorMiddleware } from \"./types.js\";\n\nexport class WorkflowExecutor<TConfig extends WorkflowConfig> {\n\tprivate readonly middleware: ExecutorMiddleware[] = [];\n\n\tconstructor(\n\t\tpublic readonly router: WorkflowRouter<TConfig>,\n\t\tprivate readonly store: StoreAdapter,\n\t) {}\n\n\tuse(middleware: ExecutorMiddleware): this {\n\t\tthis.middleware.push(middleware);\n\t\treturn this;\n\t}\n\n\tasync execute(\n\t\tid: string,\n\t\tcommand: { type: string; payload: unknown },\n\t\toptions?: { expectedVersion?: number },\n\t): Promise<ExecutionResult> {\n\t\t// 1. Load\n\t\tconst stored = await this.store.load(id);\n\t\tif (!stored) {\n\t\t\treturn { ok: false, error: { category: \"not_found\", id } };\n\t\t}\n\n\t\t// 2. Optimistic version check\n\t\tif (options?.expectedVersion !== undefined && options.expectedVersion !== stored.version) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\tid,\n\t\t\t\t\texpectedVersion: options.expectedVersion,\n\t\t\t\t\tactualVersion: stored.version,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 3. Build context\n\t\tconst ctx: ExecutorContext = {\n\t\t\tid,\n\t\t\tcommand,\n\t\t\tstored,\n\t\t\tresult: null,\n\t\t\tsnapshot: null,\n\t\t\tevents: [],\n\t\t};\n\n\t\t// 4. Run pipeline\n\t\ttry {\n\t\t\tconst chain = [...this.middleware, this.dispatchHandler()];\n\t\t\tawait compose(chain)(ctx);\n\t\t} catch (err) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\terror: err,\n\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 5. Save if dispatch succeeded\n\t\tif (ctx.snapshot) {\n\t\t\tconst newVersion = stored.version + 1;\n\t\t\tconst savedSnapshot = { ...ctx.snapshot, version: newVersion };\n\n\t\t\ttry {\n\t\t\t\tawait this.store.save({\n\t\t\t\t\tid,\n\t\t\t\t\tsnapshot: ctx.snapshot,\n\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\tevents: ctx.events,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof ConcurrencyConflictError) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\t\t\tactualVersion: err.actualVersion,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\t\terror: err,\n\t\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsnapshot: savedSnapshot,\n\t\t\t\tversion: newVersion,\n\t\t\t\tevents: ctx.events,\n\t\t\t};\n\t\t}\n\n\t\t// 6. Dispatch failed — return the error\n\t\tif (ctx.result && !ctx.result.ok) {\n\t\t\treturn { ok: false, error: ctx.result.error };\n\t\t}\n\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: {\n\t\t\t\tcategory: \"unexpected\",\n\t\t\t\terror: new Error(\"Pipeline completed without setting snapshot or error\"),\n\t\t\t\tmessage: \"Pipeline completed without setting snapshot or error\",\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate dispatchHandler(): ExecutorMiddleware {\n\t\tconst definition = this.router.definition;\n\t\tconst router = this.router;\n\n\t\treturn async (ctx, _next) => {\n\t\t\tconst restoreResult = definition.deserialize(ctx.stored.snapshot);\n\t\t\tif (!restoreResult.ok) {\n\t\t\t\tctx.result = {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"restore\" as const,\n\t\t\t\t\t\tid: ctx.id,\n\t\t\t\t\t\tissues: restoreResult.error.issues,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// as never: type erasure — executor holds WorkflowConfig base type,\n\t\t\t// but dispatch validates commands against Zod schemas at runtime\n\t\t\tconst dispatchResult = await router.dispatch(restoreResult.workflow, ctx.command as never);\n\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — DispatchResult<TConfig> assigned to DispatchResult<WorkflowConfig>\n\t\t\tctx.result = dispatchResult as any;\n\n\t\t\tif (dispatchResult.ok) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — TConfig narrows WorkflowSnapshot but ctx.snapshot is unparameterized\n\t\t\t\tctx.snapshot = definition.serialize(dispatchResult.workflow) as any as WorkflowSnapshot;\n\t\t\t\tctx.events = (dispatchResult.events as Array<{ type: string; data: unknown }>).map((e) => ({\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tdata: e.data,\n\t\t\t\t}));\n\t\t\t}\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;AAQO,IAAM,mBAAN,MAAuD;AAAA,EAG7D,YACiB,QACC,OAChB;AAFe;AACC;AAAA,EACf;AAAA,EALc,aAAmC,CAAC;AAAA,EAOrD,IAAI,YAAsC;AACzC,SAAK,WAAW,KAAK,UAAU;AAC/B,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,QACL,IACA,SACA,SAC2B;AAE3B,UAAM,SAAS,MAAM,KAAK,MAAM,KAAK,EAAE;AACvC,QAAI,CAAC,QAAQ;AACZ,aAAO,EAAE,IAAI,OAAO,OAAO,EAAE,UAAU,aAAa,GAAG,EAAE;AAAA,IAC1D;AAGA,QAAI,SAAS,oBAAoB,UAAa,QAAQ,oBAAoB,OAAO,SAAS;AACzF,aAAO;AAAA,QACN,IAAI;AAAA,QACJ,OAAO;AAAA,UACN,UAAU;AAAA,UACV;AAAA,UACA,iBAAiB,QAAQ;AAAA,UACzB,eAAe,OAAO;AAAA,QACvB;AAAA,MACD;AAAA,IACD;AAGA,UAAM,MAAuB;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,QAAQ,CAAC;AAAA,IACV;AAGA,QAAI;AACH,YAAM,QAAQ,CAAC,GAAG,KAAK,YAAY,KAAK,gBAAgB,CAAC;AACzD,YAAM,QAAQ,KAAK,EAAE,GAAG;AAAA,IACzB,SAAS,KAAK;AACb,aAAO;AAAA,QACN,IAAI;AAAA,QACJ,OAAO;AAAA,UACN,UAAU;AAAA,UACV,OAAO;AAAA,UACP,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACzD;AAAA,MACD;AAAA,IACD;AAGA,QAAI,IAAI,UAAU;AACjB,YAAM,aAAa,OAAO,UAAU;AACpC,YAAM,gBAAgB,EAAE,GAAG,IAAI,UAAU,SAAS,WAAW;AAE7D,UAAI;AACH,cAAM,KAAK,MAAM,KAAK;AAAA,UACrB;AAAA,UACA,UAAU,IAAI;AAAA,UACd,iBAAiB,OAAO;AAAA,UACxB,QAAQ,IAAI;AAAA,QACb,CAAC;AAAA,MACF,SAAS,KAAK;AACb,YAAI,eAAe,0BAA0B;AAC5C,iBAAO;AAAA,YACN,IAAI;AAAA,YACJ,OAAO;AAAA,cACN,UAAU;AAAA,cACV;AAAA,cACA,iBAAiB,OAAO;AAAA,cACxB,eAAe,IAAI;AAAA,YACpB;AAAA,UACD;AAAA,QACD;AACA,eAAO;AAAA,UACN,IAAI;AAAA,UACJ,OAAO;AAAA,YACN,UAAU;AAAA,YACV,OAAO;AAAA,YACP,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACzD;AAAA,QACD;AAAA,MACD;AAEA,aAAO;AAAA,QACN,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,QAAQ,IAAI;AAAA,MACb;AAAA,IACD;AAGA,QAAI,IAAI,UAAU,CAAC,IAAI,OAAO,IAAI;AACjC,aAAO,EAAE,IAAI,OAAO,OAAO,IAAI,OAAO,MAAM;AAAA,IAC7C;AAEA,WAAO;AAAA,MACN,IAAI;AAAA,MACJ,OAAO;AAAA,QACN,UAAU;AAAA,QACV,OAAO,IAAI,MAAM,sDAAsD;AAAA,QACvE,SAAS;AAAA,MACV;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,kBAAsC;AAC7C,UAAM,aAAa,KAAK,OAAO;AAC/B,UAAM,SAAS,KAAK;AAEpB,WAAO,OAAO,KAAK,UAAU;AAC5B,YAAM,gBAAgB,WAAW,YAAY,IAAI,OAAO,QAAQ;AAChE,UAAI,CAAC,cAAc,IAAI;AACtB,YAAI,SAAS;AAAA,UACZ,IAAI;AAAA,UACJ,OAAO;AAAA,YACN,UAAU;AAAA,YACV,IAAI,IAAI;AAAA,YACR,QAAQ,cAAc,MAAM;AAAA,UAC7B;AAAA,QACD;AACA;AAAA,MACD;AAIA,YAAM,iBAAiB,MAAM,OAAO,SAAS,cAAc,UAAU,IAAI,OAAgB;AAGzF,UAAI,SAAS;AAEb,UAAI,eAAe,IAAI;AAEtB,YAAI,WAAW,WAAW,UAAU,eAAe,QAAQ;AAC3D,YAAI,SAAU,eAAe,OAAkD,IAAI,CAAC,OAAO;AAAA,UAC1F,MAAM,EAAE;AAAA,UACR,MAAM,EAAE;AAAA,QACT,EAAE;AAAA,MACH;AAAA,IACD;AAAA,EACD;AACD;","names":[]}
1
+ {"version":3,"sources":["../../src/executor/executor.ts"],"sourcesContent":["import { compose } from \"../compose.js\";\nimport type { WorkflowRouter } from \"../router.js\";\nimport type { WorkflowSnapshot } from \"../snapshot.js\";\nimport { ConcurrencyConflictError } from \"../store/errors.js\";\nimport type { StoreAdapter } from \"../store/types.js\";\nimport type { WorkflowConfig } from \"../types.js\";\nimport type { ExecutionResult, ExecutorContext, ExecutorMiddleware } from \"./types.js\";\n\nexport class WorkflowExecutor<TConfig extends WorkflowConfig> {\n\tprivate readonly middleware: ExecutorMiddleware[] = [];\n\n\tconstructor(\n\t\tpublic readonly router: WorkflowRouter<TConfig>,\n\t\tprivate readonly store: StoreAdapter,\n\t) {}\n\n\tuse(middleware: ExecutorMiddleware): this {\n\t\tthis.middleware.push(middleware);\n\t\treturn this;\n\t}\n\n\tasync execute(\n\t\tid: string,\n\t\tcommand: { type: string; payload: unknown },\n\t\toptions?: { expectedVersion?: number },\n\t): Promise<ExecutionResult> {\n\t\t// 1. Load\n\t\tconst stored = await this.store.load(id);\n\t\tif (!stored) {\n\t\t\treturn { ok: false, error: { category: \"not_found\", id } };\n\t\t}\n\n\t\t// 2. Optimistic version check\n\t\tif (options?.expectedVersion !== undefined && options.expectedVersion !== stored.version) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\tid,\n\t\t\t\t\texpectedVersion: options.expectedVersion,\n\t\t\t\t\tactualVersion: stored.version,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 3. Build context\n\t\tconst ctx: ExecutorContext = {\n\t\t\tid,\n\t\t\tcommand,\n\t\t\tstored,\n\t\t\tresult: null,\n\t\t\tsnapshot: null,\n\t\t\tevents: [],\n\t\t};\n\n\t\t// 4. Run pipeline\n\t\ttry {\n\t\t\tconst chain = [...this.middleware, this.dispatchHandler()];\n\t\t\tawait compose(chain)(ctx);\n\t\t} catch (err) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\terror: {\n\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\terror: err,\n\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// 5. Save if dispatch succeeded\n\t\tif (ctx.snapshot) {\n\t\t\tconst newVersion = stored.version + 1;\n\t\t\tconst savedSnapshot = { ...ctx.snapshot, version: newVersion };\n\n\t\t\ttry {\n\t\t\t\tawait this.store.save({\n\t\t\t\t\tid,\n\t\t\t\t\tsnapshot: ctx.snapshot,\n\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\tevents: ctx.events,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof ConcurrencyConflictError) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcategory: \"conflict\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\texpectedVersion: stored.version,\n\t\t\t\t\t\t\tactualVersion: err.actualVersion,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"unexpected\",\n\t\t\t\t\t\terror: err,\n\t\t\t\t\t\tmessage: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsnapshot: savedSnapshot,\n\t\t\t\tversion: newVersion,\n\t\t\t\tevents: ctx.events,\n\t\t\t};\n\t\t}\n\n\t\t// 6. Dispatch failed — return the error\n\t\tif (ctx.result && !ctx.result.ok) {\n\t\t\treturn { ok: false, error: ctx.result.error };\n\t\t}\n\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: {\n\t\t\t\tcategory: \"unexpected\",\n\t\t\t\terror: new Error(\"Pipeline completed without setting snapshot or error\"),\n\t\t\t\tmessage: \"Pipeline completed without setting snapshot or error\",\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate dispatchHandler(): ExecutorMiddleware {\n\t\tconst definition = this.router.definition;\n\t\tconst router = this.router;\n\n\t\treturn async (ctx, _next) => {\n\t\t\tconst restoreResult = definition.deserialize(ctx.stored.snapshot);\n\t\t\tif (!restoreResult.ok) {\n\t\t\t\tctx.result = {\n\t\t\t\t\tok: false as const,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcategory: \"restore\" as const,\n\t\t\t\t\t\tid: ctx.id,\n\t\t\t\t\t\tissues: restoreResult.error.issues,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// as never: type erasure — executor holds WorkflowConfig base type,\n\t\t\t// but dispatch validates commands against Zod schemas at runtime\n\t\t\tconst dispatchResult = await router.dispatch(\n\t\t\t\trestoreResult.workflow,\n\t\t\t\tctx.command.type as never,\n\t\t\t\tctx.command.payload as never,\n\t\t\t);\n\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — DispatchResult<TConfig> assigned to DispatchResult<WorkflowConfig>\n\t\t\tctx.result = dispatchResult as any;\n\n\t\t\tif (dispatchResult.ok) {\n\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: type erasure — TConfig narrows WorkflowSnapshot but ctx.snapshot is unparameterized\n\t\t\t\tctx.snapshot = definition.serialize(dispatchResult.workflow) as any as WorkflowSnapshot;\n\t\t\t\tctx.events = (dispatchResult.events as Array<{ type: string; data: unknown }>).map((e) => ({\n\t\t\t\t\ttype: e.type,\n\t\t\t\t\tdata: e.data,\n\t\t\t\t}));\n\t\t\t}\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -506,6 +506,12 @@ function createContext(definition, originalWorkflow, command, deps, options) {
506
506
  command,
507
507
  workflow: originalWorkflow,
508
508
  deps: options?.wrapDeps !== false && deps != null && typeof deps === "object" ? wrapDeps(deps) : deps,
509
+ get payload() {
510
+ return command.payload;
511
+ },
512
+ get state() {
513
+ return mutableState;
514
+ },
509
515
  get data() {
510
516
  return { ...mutableData };
511
517
  },
@@ -520,7 +526,14 @@ function createContext(definition, originalWorkflow, command, deps, options) {
520
526
  },
521
527
  transition(target, data) {
522
528
  if (!definition.hasState(target)) {
523
- throw new Error(`Unknown state: ${target}`);
529
+ throw new ValidationError("transition", [
530
+ {
531
+ code: "custom",
532
+ message: `Unknown state: ${target}`,
533
+ input: target,
534
+ path: ["state"]
535
+ }
536
+ ]);
524
537
  }
525
538
  const schema = definition.getStateSchema(target);
526
539
  const result = schema.safeParse(data);
@@ -530,26 +543,30 @@ function createContext(definition, originalWorkflow, command, deps, options) {
530
543
  mutableState = target;
531
544
  mutableData = result.data;
532
545
  },
533
- emit(event) {
534
- const schema = definition.getEventSchema(event.type);
535
- const result = schema.safeParse(event.data);
546
+ emit(type, data) {
547
+ const schema = definition.getEventSchema(type);
548
+ const result = schema.safeParse(data);
536
549
  if (!result.success) {
537
550
  throw new ValidationError("event", result.error.issues);
538
551
  }
539
- accumulatedEvents.push({ type: event.type, data: result.data });
552
+ accumulatedEvents.push({ type, data: result.data });
540
553
  },
541
554
  get events() {
542
555
  return [...accumulatedEvents];
543
556
  },
544
- error(err) {
545
- const schema = definition.getErrorSchema(err.code);
546
- const result = schema.safeParse(err.data);
557
+ error(code, data) {
558
+ const schema = definition.getErrorSchema(code);
559
+ const result = schema.safeParse(data);
547
560
  if (!result.success) {
548
- throw new Error(
549
- `Invalid error data for '${err.code}': ${result.error.issues.map((i) => i.message).join(", ")}`
550
- );
561
+ throw new ValidationError("state", result.error.issues);
551
562
  }
552
- throw new DomainErrorSignal(err.code, result.data);
563
+ throw new DomainErrorSignal(code, result.data);
564
+ },
565
+ match(matchers, fallback) {
566
+ const matcher = matchers[originalWorkflow.state];
567
+ if (matcher) return matcher(ctx.data, originalWorkflow);
568
+ if (fallback) return fallback();
569
+ throw new Error(`No matcher for state: ${originalWorkflow.state}`);
553
570
  },
554
571
  set(key, value) {
555
572
  middlewareState.set(key.id, value);
@@ -766,7 +783,8 @@ var WorkflowRouter = class _WorkflowRouter {
766
783
  * @param command - The command with its type and payload
767
784
  * @returns A {@link DispatchResult} indicating success or failure with the updated workflow and events
768
785
  */
769
- async dispatch(workflow, command) {
786
+ async dispatch(workflow, type, payload) {
787
+ const command = { type, payload };
770
788
  await this.hookRegistry.emit("dispatch:start", this.onHookError, workflow, command);
771
789
  let result;
772
790
  try {