@rytejs/core 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-YTJGSTKG.js +19 -0
- package/dist/chunk-YTJGSTKG.js.map +1 -0
- package/dist/chunk-ZFRIE42B.js +17 -0
- package/dist/chunk-ZFRIE42B.js.map +1 -0
- package/dist/executor/index.cjs +185 -0
- package/dist/executor/index.cjs.map +1 -0
- package/dist/executor/index.d.cts +69 -0
- package/dist/executor/index.d.ts +69 -0
- package/dist/executor/index.js +137 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/index.cjs +39 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -451
- package/dist/index.d.ts +7 -451
- package/dist/index.js +42 -21
- package/dist/index.js.map +1 -1
- package/dist/plugin-DHN3Pk52.d.ts +339 -0
- package/dist/plugin-DHS8yUmS.d.cts +339 -0
- package/dist/reactor/index.cjs +69 -0
- package/dist/reactor/index.cjs.map +1 -0
- package/dist/reactor/index.d.cts +31 -0
- package/dist/reactor/index.d.ts +31 -0
- package/dist/reactor/index.js +41 -0
- package/dist/reactor/index.js.map +1 -0
- package/dist/snapshot-D5iZubCz.d.cts +165 -0
- package/dist/snapshot-D5iZubCz.d.ts +165 -0
- package/dist/store/index.cjs +68 -0
- package/dist/store/index.cjs.map +1 -0
- package/dist/store/index.d.cts +16 -0
- package/dist/store/index.d.ts +16 -0
- package/dist/store/index.js +31 -0
- package/dist/store/index.js.map +1 -0
- package/dist/types-BtMTMoOZ.d.cts +21 -0
- package/dist/types-C0nlrs5c.d.ts +21 -0
- package/package.json +23 -6
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/compose.ts
|
|
2
|
+
function compose(middleware) {
|
|
3
|
+
return async (ctx) => {
|
|
4
|
+
let index = -1;
|
|
5
|
+
async function dispatch(i) {
|
|
6
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
7
|
+
index = i;
|
|
8
|
+
const fn = middleware[i];
|
|
9
|
+
if (!fn) return;
|
|
10
|
+
await fn(ctx, () => dispatch(i + 1));
|
|
11
|
+
}
|
|
12
|
+
await dispatch(0);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
compose
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=chunk-YTJGSTKG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compose.ts"],"sourcesContent":["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"],"mappings":";AAGO,SAAS,QAAc,YAA8D;AAC3F,SAAO,OAAO,QAAc;AAC3B,QAAI,QAAQ;AACZ,mBAAe,SAAS,GAA0B;AACjD,UAAI,KAAK,MAAO,OAAM,IAAI,MAAM,8BAA8B;AAC9D,cAAQ;AACR,YAAM,KAAK,WAAW,CAAC;AACvB,UAAI,CAAC,GAAI;AACT,YAAM,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC,CAAC;AAAA,IACpC;AACA,UAAM,SAAS,CAAC;AAAA,EACjB;AACD;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/store/errors.ts
|
|
2
|
+
var ConcurrencyConflictError = class extends Error {
|
|
3
|
+
constructor(workflowId, expectedVersion, actualVersion) {
|
|
4
|
+
super(
|
|
5
|
+
`Concurrency conflict for workflow "${workflowId}": expected version ${expectedVersion}, actual ${actualVersion}`
|
|
6
|
+
);
|
|
7
|
+
this.workflowId = workflowId;
|
|
8
|
+
this.expectedVersion = expectedVersion;
|
|
9
|
+
this.actualVersion = actualVersion;
|
|
10
|
+
}
|
|
11
|
+
name = "ConcurrencyConflictError";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
ConcurrencyConflictError
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=chunk-ZFRIE42B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/store/errors.ts"],"sourcesContent":["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"],"mappings":";AAAO,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAGnD,YACU,YACA,iBACA,eACR;AACD;AAAA,MACC,sCAAsC,UAAU,uBAAuB,eAAe,YAAY,aAAa;AAAA,IAChH;AANS;AACA;AACA;AAAA,EAKV;AAAA,EAVS,OAAO;AAWjB;","names":[]}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/executor/index.ts
|
|
21
|
+
var executor_exports = {};
|
|
22
|
+
__export(executor_exports, {
|
|
23
|
+
WorkflowExecutor: () => WorkflowExecutor
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(executor_exports);
|
|
26
|
+
|
|
27
|
+
// src/compose.ts
|
|
28
|
+
function compose(middleware) {
|
|
29
|
+
return async (ctx) => {
|
|
30
|
+
let index = -1;
|
|
31
|
+
async function dispatch(i) {
|
|
32
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
33
|
+
index = i;
|
|
34
|
+
const fn = middleware[i];
|
|
35
|
+
if (!fn) return;
|
|
36
|
+
await fn(ctx, () => dispatch(i + 1));
|
|
37
|
+
}
|
|
38
|
+
await dispatch(0);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/store/errors.ts
|
|
43
|
+
var ConcurrencyConflictError = class extends Error {
|
|
44
|
+
constructor(workflowId, expectedVersion, actualVersion) {
|
|
45
|
+
super(
|
|
46
|
+
`Concurrency conflict for workflow "${workflowId}": expected version ${expectedVersion}, actual ${actualVersion}`
|
|
47
|
+
);
|
|
48
|
+
this.workflowId = workflowId;
|
|
49
|
+
this.expectedVersion = expectedVersion;
|
|
50
|
+
this.actualVersion = actualVersion;
|
|
51
|
+
}
|
|
52
|
+
name = "ConcurrencyConflictError";
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/executor/executor.ts
|
|
56
|
+
var WorkflowExecutor = class {
|
|
57
|
+
constructor(router, store) {
|
|
58
|
+
this.router = router;
|
|
59
|
+
this.store = store;
|
|
60
|
+
}
|
|
61
|
+
middleware = [];
|
|
62
|
+
use(middleware) {
|
|
63
|
+
this.middleware.push(middleware);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
async execute(id, command, options) {
|
|
67
|
+
const stored = await this.store.load(id);
|
|
68
|
+
if (!stored) {
|
|
69
|
+
return { ok: false, error: { category: "not_found", id } };
|
|
70
|
+
}
|
|
71
|
+
if (options?.expectedVersion !== void 0 && options.expectedVersion !== stored.version) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: {
|
|
75
|
+
category: "conflict",
|
|
76
|
+
id,
|
|
77
|
+
expectedVersion: options.expectedVersion,
|
|
78
|
+
actualVersion: stored.version
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const ctx = {
|
|
83
|
+
id,
|
|
84
|
+
command,
|
|
85
|
+
stored,
|
|
86
|
+
result: null,
|
|
87
|
+
snapshot: null,
|
|
88
|
+
events: []
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
const chain = [...this.middleware, this.dispatchHandler()];
|
|
92
|
+
await compose(chain)(ctx);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
error: {
|
|
97
|
+
category: "unexpected",
|
|
98
|
+
error: err,
|
|
99
|
+
message: err instanceof Error ? err.message : String(err)
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (ctx.snapshot) {
|
|
104
|
+
const newVersion = stored.version + 1;
|
|
105
|
+
const savedSnapshot = { ...ctx.snapshot, version: newVersion };
|
|
106
|
+
try {
|
|
107
|
+
await this.store.save({
|
|
108
|
+
id,
|
|
109
|
+
snapshot: ctx.snapshot,
|
|
110
|
+
expectedVersion: stored.version,
|
|
111
|
+
events: ctx.events
|
|
112
|
+
});
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err instanceof ConcurrencyConflictError) {
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
error: {
|
|
118
|
+
category: "conflict",
|
|
119
|
+
id,
|
|
120
|
+
expectedVersion: stored.version,
|
|
121
|
+
actualVersion: err.actualVersion
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
error: {
|
|
128
|
+
category: "unexpected",
|
|
129
|
+
error: err,
|
|
130
|
+
message: err instanceof Error ? err.message : String(err)
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
snapshot: savedSnapshot,
|
|
137
|
+
version: newVersion,
|
|
138
|
+
events: ctx.events
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (ctx.result && !ctx.result.ok) {
|
|
142
|
+
return { ok: false, error: ctx.result.error };
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
error: {
|
|
147
|
+
category: "unexpected",
|
|
148
|
+
error: new Error("Pipeline completed without setting snapshot or error"),
|
|
149
|
+
message: "Pipeline completed without setting snapshot or error"
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
dispatchHandler() {
|
|
154
|
+
const definition = this.router.definition;
|
|
155
|
+
const router = this.router;
|
|
156
|
+
return async (ctx, _next) => {
|
|
157
|
+
const restoreResult = definition.deserialize(ctx.stored.snapshot);
|
|
158
|
+
if (!restoreResult.ok) {
|
|
159
|
+
ctx.result = {
|
|
160
|
+
ok: false,
|
|
161
|
+
error: {
|
|
162
|
+
category: "restore",
|
|
163
|
+
id: ctx.id,
|
|
164
|
+
issues: restoreResult.error.issues
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const dispatchResult = await router.dispatch(restoreResult.workflow, ctx.command);
|
|
170
|
+
ctx.result = dispatchResult;
|
|
171
|
+
if (dispatchResult.ok) {
|
|
172
|
+
ctx.snapshot = definition.serialize(dispatchResult.workflow);
|
|
173
|
+
ctx.events = dispatchResult.events.map((e) => ({
|
|
174
|
+
type: e.type,
|
|
175
|
+
data: e.data
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
182
|
+
0 && (module.exports = {
|
|
183
|
+
WorkflowExecutor
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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":[]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { c as WorkflowRouter } from '../plugin-DHS8yUmS.cjs';
|
|
2
|
+
import { S as StoredWorkflow, a as StoreAdapter } from '../types-BtMTMoOZ.cjs';
|
|
3
|
+
import { a as WorkflowSnapshot, P as PipelineError, W as WorkflowConfig, D as DispatchResult } from '../snapshot-D5iZubCz.cjs';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
interface ExecutorContext {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly command: {
|
|
9
|
+
type: string;
|
|
10
|
+
payload: unknown;
|
|
11
|
+
};
|
|
12
|
+
readonly stored: StoredWorkflow;
|
|
13
|
+
result: DispatchResult<WorkflowConfig> | {
|
|
14
|
+
ok: false;
|
|
15
|
+
error: ExecutorError;
|
|
16
|
+
} | null;
|
|
17
|
+
snapshot: WorkflowSnapshot | null;
|
|
18
|
+
events: Array<{
|
|
19
|
+
type: string;
|
|
20
|
+
data: unknown;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
type ExecutorMiddleware = (ctx: ExecutorContext, next: () => Promise<void>) => Promise<void>;
|
|
24
|
+
type ExecutorError = {
|
|
25
|
+
category: "not_found";
|
|
26
|
+
id: string;
|
|
27
|
+
} | {
|
|
28
|
+
category: "conflict";
|
|
29
|
+
id: string;
|
|
30
|
+
expectedVersion: number;
|
|
31
|
+
actualVersion: number;
|
|
32
|
+
} | {
|
|
33
|
+
category: "restore";
|
|
34
|
+
id: string;
|
|
35
|
+
issues: unknown[];
|
|
36
|
+
} | {
|
|
37
|
+
category: "unexpected";
|
|
38
|
+
error: unknown;
|
|
39
|
+
message: string;
|
|
40
|
+
};
|
|
41
|
+
type ExecutionResult = {
|
|
42
|
+
ok: true;
|
|
43
|
+
snapshot: WorkflowSnapshot;
|
|
44
|
+
version: number;
|
|
45
|
+
events: Array<{
|
|
46
|
+
type: string;
|
|
47
|
+
data: unknown;
|
|
48
|
+
}>;
|
|
49
|
+
} | {
|
|
50
|
+
ok: false;
|
|
51
|
+
error: PipelineError<WorkflowConfig> | ExecutorError;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
declare class WorkflowExecutor<TConfig extends WorkflowConfig> {
|
|
55
|
+
readonly router: WorkflowRouter<TConfig>;
|
|
56
|
+
private readonly store;
|
|
57
|
+
private readonly middleware;
|
|
58
|
+
constructor(router: WorkflowRouter<TConfig>, store: StoreAdapter);
|
|
59
|
+
use(middleware: ExecutorMiddleware): this;
|
|
60
|
+
execute(id: string, command: {
|
|
61
|
+
type: string;
|
|
62
|
+
payload: unknown;
|
|
63
|
+
}, options?: {
|
|
64
|
+
expectedVersion?: number;
|
|
65
|
+
}): Promise<ExecutionResult>;
|
|
66
|
+
private dispatchHandler;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { type ExecutionResult, type ExecutorContext, type ExecutorError, type ExecutorMiddleware, WorkflowExecutor };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { c as WorkflowRouter } from '../plugin-DHN3Pk52.js';
|
|
2
|
+
import { S as StoredWorkflow, a as StoreAdapter } from '../types-C0nlrs5c.js';
|
|
3
|
+
import { a as WorkflowSnapshot, P as PipelineError, W as WorkflowConfig, D as DispatchResult } from '../snapshot-D5iZubCz.js';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
interface ExecutorContext {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly command: {
|
|
9
|
+
type: string;
|
|
10
|
+
payload: unknown;
|
|
11
|
+
};
|
|
12
|
+
readonly stored: StoredWorkflow;
|
|
13
|
+
result: DispatchResult<WorkflowConfig> | {
|
|
14
|
+
ok: false;
|
|
15
|
+
error: ExecutorError;
|
|
16
|
+
} | null;
|
|
17
|
+
snapshot: WorkflowSnapshot | null;
|
|
18
|
+
events: Array<{
|
|
19
|
+
type: string;
|
|
20
|
+
data: unknown;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
type ExecutorMiddleware = (ctx: ExecutorContext, next: () => Promise<void>) => Promise<void>;
|
|
24
|
+
type ExecutorError = {
|
|
25
|
+
category: "not_found";
|
|
26
|
+
id: string;
|
|
27
|
+
} | {
|
|
28
|
+
category: "conflict";
|
|
29
|
+
id: string;
|
|
30
|
+
expectedVersion: number;
|
|
31
|
+
actualVersion: number;
|
|
32
|
+
} | {
|
|
33
|
+
category: "restore";
|
|
34
|
+
id: string;
|
|
35
|
+
issues: unknown[];
|
|
36
|
+
} | {
|
|
37
|
+
category: "unexpected";
|
|
38
|
+
error: unknown;
|
|
39
|
+
message: string;
|
|
40
|
+
};
|
|
41
|
+
type ExecutionResult = {
|
|
42
|
+
ok: true;
|
|
43
|
+
snapshot: WorkflowSnapshot;
|
|
44
|
+
version: number;
|
|
45
|
+
events: Array<{
|
|
46
|
+
type: string;
|
|
47
|
+
data: unknown;
|
|
48
|
+
}>;
|
|
49
|
+
} | {
|
|
50
|
+
ok: false;
|
|
51
|
+
error: PipelineError<WorkflowConfig> | ExecutorError;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
declare class WorkflowExecutor<TConfig extends WorkflowConfig> {
|
|
55
|
+
readonly router: WorkflowRouter<TConfig>;
|
|
56
|
+
private readonly store;
|
|
57
|
+
private readonly middleware;
|
|
58
|
+
constructor(router: WorkflowRouter<TConfig>, store: StoreAdapter);
|
|
59
|
+
use(middleware: ExecutorMiddleware): this;
|
|
60
|
+
execute(id: string, command: {
|
|
61
|
+
type: string;
|
|
62
|
+
payload: unknown;
|
|
63
|
+
}, options?: {
|
|
64
|
+
expectedVersion?: number;
|
|
65
|
+
}): Promise<ExecutionResult>;
|
|
66
|
+
private dispatchHandler;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { type ExecutionResult, type ExecutorContext, type ExecutorError, type ExecutorMiddleware, WorkflowExecutor };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compose
|
|
3
|
+
} from "../chunk-YTJGSTKG.js";
|
|
4
|
+
import {
|
|
5
|
+
ConcurrencyConflictError
|
|
6
|
+
} from "../chunk-ZFRIE42B.js";
|
|
7
|
+
|
|
8
|
+
// src/executor/executor.ts
|
|
9
|
+
var WorkflowExecutor = class {
|
|
10
|
+
constructor(router, store) {
|
|
11
|
+
this.router = router;
|
|
12
|
+
this.store = store;
|
|
13
|
+
}
|
|
14
|
+
middleware = [];
|
|
15
|
+
use(middleware) {
|
|
16
|
+
this.middleware.push(middleware);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
async execute(id, command, options) {
|
|
20
|
+
const stored = await this.store.load(id);
|
|
21
|
+
if (!stored) {
|
|
22
|
+
return { ok: false, error: { category: "not_found", id } };
|
|
23
|
+
}
|
|
24
|
+
if (options?.expectedVersion !== void 0 && options.expectedVersion !== stored.version) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
error: {
|
|
28
|
+
category: "conflict",
|
|
29
|
+
id,
|
|
30
|
+
expectedVersion: options.expectedVersion,
|
|
31
|
+
actualVersion: stored.version
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const ctx = {
|
|
36
|
+
id,
|
|
37
|
+
command,
|
|
38
|
+
stored,
|
|
39
|
+
result: null,
|
|
40
|
+
snapshot: null,
|
|
41
|
+
events: []
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
const chain = [...this.middleware, this.dispatchHandler()];
|
|
45
|
+
await compose(chain)(ctx);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: {
|
|
50
|
+
category: "unexpected",
|
|
51
|
+
error: err,
|
|
52
|
+
message: err instanceof Error ? err.message : String(err)
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (ctx.snapshot) {
|
|
57
|
+
const newVersion = stored.version + 1;
|
|
58
|
+
const savedSnapshot = { ...ctx.snapshot, version: newVersion };
|
|
59
|
+
try {
|
|
60
|
+
await this.store.save({
|
|
61
|
+
id,
|
|
62
|
+
snapshot: ctx.snapshot,
|
|
63
|
+
expectedVersion: stored.version,
|
|
64
|
+
events: ctx.events
|
|
65
|
+
});
|
|
66
|
+
} catch (err) {
|
|
67
|
+
if (err instanceof ConcurrencyConflictError) {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
error: {
|
|
71
|
+
category: "conflict",
|
|
72
|
+
id,
|
|
73
|
+
expectedVersion: stored.version,
|
|
74
|
+
actualVersion: err.actualVersion
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
error: {
|
|
81
|
+
category: "unexpected",
|
|
82
|
+
error: err,
|
|
83
|
+
message: err instanceof Error ? err.message : String(err)
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
snapshot: savedSnapshot,
|
|
90
|
+
version: newVersion,
|
|
91
|
+
events: ctx.events
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (ctx.result && !ctx.result.ok) {
|
|
95
|
+
return { ok: false, error: ctx.result.error };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
error: {
|
|
100
|
+
category: "unexpected",
|
|
101
|
+
error: new Error("Pipeline completed without setting snapshot or error"),
|
|
102
|
+
message: "Pipeline completed without setting snapshot or error"
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
dispatchHandler() {
|
|
107
|
+
const definition = this.router.definition;
|
|
108
|
+
const router = this.router;
|
|
109
|
+
return async (ctx, _next) => {
|
|
110
|
+
const restoreResult = definition.deserialize(ctx.stored.snapshot);
|
|
111
|
+
if (!restoreResult.ok) {
|
|
112
|
+
ctx.result = {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: {
|
|
115
|
+
category: "restore",
|
|
116
|
+
id: ctx.id,
|
|
117
|
+
issues: restoreResult.error.issues
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const dispatchResult = await router.dispatch(restoreResult.workflow, ctx.command);
|
|
123
|
+
ctx.result = dispatchResult;
|
|
124
|
+
if (dispatchResult.ok) {
|
|
125
|
+
ctx.snapshot = definition.serialize(dispatchResult.workflow);
|
|
126
|
+
ctx.events = dispatchResult.events.map((e) => ({
|
|
127
|
+
type: e.type,
|
|
128
|
+
data: e.data
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
export {
|
|
135
|
+
WorkflowExecutor
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
ValidationError: () => ValidationError,
|
|
26
26
|
WorkflowRouter: () => WorkflowRouter,
|
|
27
27
|
createKey: () => createKey,
|
|
28
|
+
defineGenericPlugin: () => defineGenericPlugin,
|
|
28
29
|
defineMigrations: () => defineMigrations,
|
|
29
30
|
definePlugin: () => definePlugin,
|
|
30
31
|
defineWorkflow: () => defineWorkflow,
|
|
@@ -108,7 +109,13 @@ function defineWorkflow(name, config) {
|
|
|
108
109
|
hasState(stateName) {
|
|
109
110
|
return stateName in config.states;
|
|
110
111
|
},
|
|
111
|
-
|
|
112
|
+
hasCommand(commandName) {
|
|
113
|
+
return commandName in config.commands;
|
|
114
|
+
},
|
|
115
|
+
hasEvent(eventName) {
|
|
116
|
+
return eventName in config.events;
|
|
117
|
+
},
|
|
118
|
+
serialize(workflow) {
|
|
112
119
|
return {
|
|
113
120
|
id: workflow.id,
|
|
114
121
|
definitionName: name,
|
|
@@ -116,10 +123,11 @@ function defineWorkflow(name, config) {
|
|
|
116
123
|
data: workflow.data,
|
|
117
124
|
createdAt: workflow.createdAt.toISOString(),
|
|
118
125
|
updatedAt: workflow.updatedAt.toISOString(),
|
|
119
|
-
modelVersion: config.modelVersion ?? 1
|
|
126
|
+
modelVersion: config.modelVersion ?? 1,
|
|
127
|
+
version: workflow.version ?? 1
|
|
120
128
|
};
|
|
121
129
|
},
|
|
122
|
-
|
|
130
|
+
deserialize(snap) {
|
|
123
131
|
const stateSchema = config.states[snap.state];
|
|
124
132
|
if (!stateSchema) {
|
|
125
133
|
return {
|
|
@@ -283,6 +291,9 @@ function definePlugin(fn) {
|
|
|
283
291
|
Object.defineProperty(plugin, PLUGIN_SYMBOL, { value: true, writable: false });
|
|
284
292
|
return plugin;
|
|
285
293
|
}
|
|
294
|
+
function defineGenericPlugin(fn) {
|
|
295
|
+
return definePlugin(fn);
|
|
296
|
+
}
|
|
286
297
|
function isPlugin(value) {
|
|
287
298
|
return typeof value === "function" && PLUGIN_SYMBOL in value;
|
|
288
299
|
}
|
|
@@ -456,6 +467,8 @@ function createContext(definition, originalWorkflow, command, deps, options) {
|
|
|
456
467
|
var HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
457
468
|
"dispatch:start",
|
|
458
469
|
"dispatch:end",
|
|
470
|
+
"pipeline:start",
|
|
471
|
+
"pipeline:end",
|
|
459
472
|
"transition",
|
|
460
473
|
"error",
|
|
461
474
|
"event"
|
|
@@ -640,6 +653,25 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
640
653
|
* @returns A {@link DispatchResult} indicating success or failure with the updated workflow and events
|
|
641
654
|
*/
|
|
642
655
|
async dispatch(workflow, command) {
|
|
656
|
+
await this.hookRegistry.emit("dispatch:start", this.onHookError, workflow, command);
|
|
657
|
+
let result;
|
|
658
|
+
try {
|
|
659
|
+
result = await this.executePipeline(workflow, command);
|
|
660
|
+
} catch (err) {
|
|
661
|
+
result = {
|
|
662
|
+
ok: false,
|
|
663
|
+
error: {
|
|
664
|
+
category: "unexpected",
|
|
665
|
+
error: err,
|
|
666
|
+
message: err instanceof Error ? err.message : String(err)
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
} finally {
|
|
670
|
+
await this.hookRegistry.emit("dispatch:end", this.onHookError, workflow, command, result);
|
|
671
|
+
}
|
|
672
|
+
return result;
|
|
673
|
+
}
|
|
674
|
+
async executePipeline(workflow, command) {
|
|
643
675
|
if (!this.definition.hasState(workflow.state)) {
|
|
644
676
|
return {
|
|
645
677
|
ok: false,
|
|
@@ -711,7 +743,7 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
711
743
|
this.deps,
|
|
712
744
|
{ wrapDeps: this.wrapDeps }
|
|
713
745
|
);
|
|
714
|
-
await this.hookRegistry.emit("
|
|
746
|
+
await this.hookRegistry.emit("pipeline:start", this.onHookError, ctx);
|
|
715
747
|
try {
|
|
716
748
|
const composed = compose(chain);
|
|
717
749
|
await composed(ctx);
|
|
@@ -734,7 +766,7 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
734
766
|
await this.hookRegistry.emit("event", this.onHookError, event, result.workflow);
|
|
735
767
|
}
|
|
736
768
|
}
|
|
737
|
-
await this.hookRegistry.emit("
|
|
769
|
+
await this.hookRegistry.emit("pipeline:end", this.onHookError, ctx, result);
|
|
738
770
|
return result;
|
|
739
771
|
} catch (err) {
|
|
740
772
|
let result;
|
|
@@ -778,7 +810,7 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
778
810
|
};
|
|
779
811
|
}
|
|
780
812
|
await this.hookRegistry.emit("error", this.onHookError, result.error, ctx);
|
|
781
|
-
await this.hookRegistry.emit("
|
|
813
|
+
await this.hookRegistry.emit("pipeline:end", this.onHookError, ctx, result);
|
|
782
814
|
return result;
|
|
783
815
|
}
|
|
784
816
|
}
|
|
@@ -790,6 +822,7 @@ var WorkflowRouter = class _WorkflowRouter {
|
|
|
790
822
|
ValidationError,
|
|
791
823
|
WorkflowRouter,
|
|
792
824
|
createKey,
|
|
825
|
+
defineGenericPlugin,
|
|
793
826
|
defineMigrations,
|
|
794
827
|
definePlugin,
|
|
795
828
|
defineWorkflow,
|