@rytejs/core 0.1.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/LICENSE +21 -0
- package/dist/index.cjs +380 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +178 -0
- package/dist/index.d.ts +178 -0
- package/dist/index.js +349 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryte Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
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/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DomainErrorSignal: () => DomainErrorSignal,
|
|
24
|
+
ValidationError: () => ValidationError,
|
|
25
|
+
WorkflowRouter: () => WorkflowRouter,
|
|
26
|
+
createKey: () => createKey,
|
|
27
|
+
defineWorkflow: () => defineWorkflow
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/definition.ts
|
|
32
|
+
function defineWorkflow(name, config) {
|
|
33
|
+
return {
|
|
34
|
+
config,
|
|
35
|
+
name,
|
|
36
|
+
createWorkflow(id, wfConfig) {
|
|
37
|
+
const schema = config.states[wfConfig.initialState];
|
|
38
|
+
if (!schema) throw new Error(`Unknown state: ${wfConfig.initialState}`);
|
|
39
|
+
const result = schema.safeParse(wfConfig.data);
|
|
40
|
+
if (!result.success) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Invalid initial data for state '${wfConfig.initialState}': ${result.error.issues.map((i) => i.message).join(", ")}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const now = /* @__PURE__ */ new Date();
|
|
46
|
+
return {
|
|
47
|
+
id,
|
|
48
|
+
definitionName: name,
|
|
49
|
+
state: wfConfig.initialState,
|
|
50
|
+
data: result.data,
|
|
51
|
+
createdAt: now,
|
|
52
|
+
updatedAt: now
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
getStateSchema(stateName) {
|
|
56
|
+
const schema = config.states[stateName];
|
|
57
|
+
if (!schema) throw new Error(`Unknown state: ${stateName}`);
|
|
58
|
+
return schema;
|
|
59
|
+
},
|
|
60
|
+
getCommandSchema(commandName) {
|
|
61
|
+
const schema = config.commands[commandName];
|
|
62
|
+
if (!schema) throw new Error(`Unknown command: ${commandName}`);
|
|
63
|
+
return schema;
|
|
64
|
+
},
|
|
65
|
+
getEventSchema(eventName) {
|
|
66
|
+
const schema = config.events[eventName];
|
|
67
|
+
if (!schema) throw new Error(`Unknown event: ${eventName}`);
|
|
68
|
+
return schema;
|
|
69
|
+
},
|
|
70
|
+
getErrorSchema(errorCode) {
|
|
71
|
+
const schema = config.errors[errorCode];
|
|
72
|
+
if (!schema) throw new Error(`Unknown error: ${errorCode}`);
|
|
73
|
+
return schema;
|
|
74
|
+
},
|
|
75
|
+
hasState(stateName) {
|
|
76
|
+
return stateName in config.states;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/key.ts
|
|
82
|
+
function createKey(name) {
|
|
83
|
+
return { id: Symbol(name) };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/compose.ts
|
|
87
|
+
function compose(middleware) {
|
|
88
|
+
return async (ctx) => {
|
|
89
|
+
let index = -1;
|
|
90
|
+
async function dispatch(i) {
|
|
91
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
92
|
+
index = i;
|
|
93
|
+
const fn = middleware[i];
|
|
94
|
+
if (!fn) return;
|
|
95
|
+
await fn(ctx, () => dispatch(i + 1));
|
|
96
|
+
}
|
|
97
|
+
await dispatch(0);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/types.ts
|
|
102
|
+
var ValidationError = class extends Error {
|
|
103
|
+
constructor(source, issues) {
|
|
104
|
+
super(`Validation failed (${source}): ${issues.map((i) => i.message).join(", ")}`);
|
|
105
|
+
this.source = source;
|
|
106
|
+
this.issues = issues;
|
|
107
|
+
this.name = "ValidationError";
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var DomainErrorSignal = class extends Error {
|
|
111
|
+
constructor(code, data) {
|
|
112
|
+
super(`Domain error: ${code}`);
|
|
113
|
+
this.code = code;
|
|
114
|
+
this.data = data;
|
|
115
|
+
this.name = "DomainErrorSignal";
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// src/context.ts
|
|
120
|
+
function createContext(definition, originalWorkflow, command, deps) {
|
|
121
|
+
let mutableState = originalWorkflow.state;
|
|
122
|
+
let mutableData = {
|
|
123
|
+
...originalWorkflow.data
|
|
124
|
+
};
|
|
125
|
+
const accumulatedEvents = [];
|
|
126
|
+
const middlewareState = /* @__PURE__ */ new Map();
|
|
127
|
+
const ctx = {
|
|
128
|
+
command,
|
|
129
|
+
workflow: originalWorkflow,
|
|
130
|
+
deps,
|
|
131
|
+
get data() {
|
|
132
|
+
return { ...mutableData };
|
|
133
|
+
},
|
|
134
|
+
update(data) {
|
|
135
|
+
const merged = { ...mutableData, ...data };
|
|
136
|
+
const schema = definition.getStateSchema(mutableState);
|
|
137
|
+
const result = schema.safeParse(merged);
|
|
138
|
+
if (!result.success) {
|
|
139
|
+
throw new ValidationError("state", result.error.issues);
|
|
140
|
+
}
|
|
141
|
+
mutableData = result.data;
|
|
142
|
+
},
|
|
143
|
+
transition(target, data) {
|
|
144
|
+
if (!definition.hasState(target)) {
|
|
145
|
+
throw new Error(`Unknown state: ${target}`);
|
|
146
|
+
}
|
|
147
|
+
const schema = definition.getStateSchema(target);
|
|
148
|
+
const result = schema.safeParse(data);
|
|
149
|
+
if (!result.success) {
|
|
150
|
+
throw new ValidationError("transition", result.error.issues);
|
|
151
|
+
}
|
|
152
|
+
mutableState = target;
|
|
153
|
+
mutableData = result.data;
|
|
154
|
+
},
|
|
155
|
+
emit(event) {
|
|
156
|
+
const schema = definition.getEventSchema(event.type);
|
|
157
|
+
const result = schema.safeParse(event.data);
|
|
158
|
+
if (!result.success) {
|
|
159
|
+
throw new ValidationError("event", result.error.issues);
|
|
160
|
+
}
|
|
161
|
+
accumulatedEvents.push({ type: event.type, data: result.data });
|
|
162
|
+
},
|
|
163
|
+
get events() {
|
|
164
|
+
return [...accumulatedEvents];
|
|
165
|
+
},
|
|
166
|
+
error(err) {
|
|
167
|
+
const schema = definition.getErrorSchema(err.code);
|
|
168
|
+
const result = schema.safeParse(err.data);
|
|
169
|
+
if (!result.success) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Invalid error data for '${err.code}': ${result.error.issues.map((i) => i.message).join(", ")}`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
throw new DomainErrorSignal(err.code, result.data);
|
|
175
|
+
},
|
|
176
|
+
set(key, value) {
|
|
177
|
+
middlewareState.set(key.id, value);
|
|
178
|
+
},
|
|
179
|
+
get(key) {
|
|
180
|
+
if (!middlewareState.has(key.id)) {
|
|
181
|
+
throw new Error(`Context key not set: ${key.id.toString()}`);
|
|
182
|
+
}
|
|
183
|
+
return middlewareState.get(key.id);
|
|
184
|
+
},
|
|
185
|
+
getOrNull(key) {
|
|
186
|
+
return middlewareState.get(key.id);
|
|
187
|
+
},
|
|
188
|
+
getWorkflowSnapshot() {
|
|
189
|
+
return {
|
|
190
|
+
id: originalWorkflow.id,
|
|
191
|
+
definitionName: originalWorkflow.definitionName,
|
|
192
|
+
state: mutableState,
|
|
193
|
+
data: { ...mutableData },
|
|
194
|
+
createdAt: originalWorkflow.createdAt,
|
|
195
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
return ctx;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/router.ts
|
|
203
|
+
var StateBuilder = class {
|
|
204
|
+
/** @internal */
|
|
205
|
+
middleware = [];
|
|
206
|
+
/** @internal */
|
|
207
|
+
handlers = /* @__PURE__ */ new Map();
|
|
208
|
+
on(command, ...fns) {
|
|
209
|
+
if (fns.length === 0) throw new Error("on() requires at least a handler");
|
|
210
|
+
const handler = fns.pop();
|
|
211
|
+
const inlineMiddleware = fns;
|
|
212
|
+
const wrappedHandler = async (ctx, _next) => {
|
|
213
|
+
await handler(ctx);
|
|
214
|
+
};
|
|
215
|
+
this.handlers.set(command, { inlineMiddleware, handler: wrappedHandler });
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
use(middleware) {
|
|
219
|
+
this.middleware.push(middleware);
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
var WorkflowRouter = class {
|
|
224
|
+
constructor(definition, deps = {}) {
|
|
225
|
+
this.definition = definition;
|
|
226
|
+
this.deps = deps;
|
|
227
|
+
}
|
|
228
|
+
globalMiddleware = [];
|
|
229
|
+
singleStateBuilders = /* @__PURE__ */ new Map();
|
|
230
|
+
multiStateBuilders = /* @__PURE__ */ new Map();
|
|
231
|
+
wildcardHandlers = /* @__PURE__ */ new Map();
|
|
232
|
+
/** Adds global middleware that wraps all dispatches. */
|
|
233
|
+
use(middleware) {
|
|
234
|
+
this.globalMiddleware.push(middleware);
|
|
235
|
+
return this;
|
|
236
|
+
}
|
|
237
|
+
/** Registers handlers for one or more states. */
|
|
238
|
+
state(name, setup) {
|
|
239
|
+
const names = Array.isArray(name) ? name : [name];
|
|
240
|
+
const isMulti = Array.isArray(name);
|
|
241
|
+
const routerMap = isMulti ? this.multiStateBuilders : this.singleStateBuilders;
|
|
242
|
+
for (const n of names) {
|
|
243
|
+
let router = routerMap.get(n);
|
|
244
|
+
if (!router) {
|
|
245
|
+
router = new StateBuilder();
|
|
246
|
+
routerMap.set(n, router);
|
|
247
|
+
}
|
|
248
|
+
setup(router);
|
|
249
|
+
}
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
/** Registers a wildcard handler that matches any state. */
|
|
253
|
+
on(_state, command, ...fns) {
|
|
254
|
+
if (fns.length === 0) throw new Error("on() requires at least a handler");
|
|
255
|
+
const handler = fns.pop();
|
|
256
|
+
const inlineMiddleware = fns;
|
|
257
|
+
const wrappedHandler = async (ctx, _next) => {
|
|
258
|
+
await handler(ctx);
|
|
259
|
+
};
|
|
260
|
+
this.wildcardHandlers.set(command, {
|
|
261
|
+
inlineMiddleware,
|
|
262
|
+
handler: wrappedHandler
|
|
263
|
+
});
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
/** Dispatches a command to the appropriate handler and returns the result. */
|
|
267
|
+
async dispatch(workflow, command) {
|
|
268
|
+
if (!this.definition.hasState(workflow.state)) {
|
|
269
|
+
return {
|
|
270
|
+
ok: false,
|
|
271
|
+
error: {
|
|
272
|
+
category: "router",
|
|
273
|
+
code: "UNKNOWN_STATE",
|
|
274
|
+
message: `Unknown state: ${workflow.state}`
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const commandSchema = this.definition.getCommandSchema(command.type);
|
|
279
|
+
const payloadResult = commandSchema.safeParse(command.payload);
|
|
280
|
+
if (!payloadResult.success) {
|
|
281
|
+
return {
|
|
282
|
+
ok: false,
|
|
283
|
+
error: {
|
|
284
|
+
category: "validation",
|
|
285
|
+
source: "command",
|
|
286
|
+
issues: payloadResult.error.issues,
|
|
287
|
+
message: `Invalid command payload: ${payloadResult.error.issues.map((i) => i.message).join(", ")}`
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const validatedCommand = { type: command.type, payload: payloadResult.data };
|
|
292
|
+
const stateName = workflow.state;
|
|
293
|
+
const singleRouter = this.singleStateBuilders.get(stateName);
|
|
294
|
+
const multiRouter = this.multiStateBuilders.get(stateName);
|
|
295
|
+
const singleHandler = singleRouter?.handlers.get(command.type);
|
|
296
|
+
const multiHandler = multiRouter?.handlers.get(command.type);
|
|
297
|
+
const wildcardHandler = this.wildcardHandlers.get(command.type);
|
|
298
|
+
let routeEntry;
|
|
299
|
+
let matchedRouter;
|
|
300
|
+
if (singleHandler) {
|
|
301
|
+
routeEntry = singleHandler;
|
|
302
|
+
matchedRouter = singleRouter;
|
|
303
|
+
} else if (multiHandler) {
|
|
304
|
+
routeEntry = multiHandler;
|
|
305
|
+
matchedRouter = multiRouter;
|
|
306
|
+
} else if (wildcardHandler) {
|
|
307
|
+
routeEntry = wildcardHandler;
|
|
308
|
+
matchedRouter = void 0;
|
|
309
|
+
}
|
|
310
|
+
if (!routeEntry) {
|
|
311
|
+
return {
|
|
312
|
+
ok: false,
|
|
313
|
+
error: {
|
|
314
|
+
category: "router",
|
|
315
|
+
code: "NO_HANDLER",
|
|
316
|
+
message: `No handler for command '${command.type}' in state '${stateName}'`
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const stateMiddleware = [];
|
|
321
|
+
if (matchedRouter) {
|
|
322
|
+
if (singleRouter) stateMiddleware.push(...singleRouter.middleware);
|
|
323
|
+
if (multiRouter && multiRouter !== singleRouter)
|
|
324
|
+
stateMiddleware.push(...multiRouter.middleware);
|
|
325
|
+
}
|
|
326
|
+
const chain = [
|
|
327
|
+
...this.globalMiddleware,
|
|
328
|
+
...stateMiddleware,
|
|
329
|
+
...routeEntry.inlineMiddleware,
|
|
330
|
+
routeEntry.handler
|
|
331
|
+
];
|
|
332
|
+
const ctx = createContext(
|
|
333
|
+
this.definition,
|
|
334
|
+
workflow,
|
|
335
|
+
validatedCommand,
|
|
336
|
+
this.deps
|
|
337
|
+
);
|
|
338
|
+
try {
|
|
339
|
+
const composed = compose(chain);
|
|
340
|
+
await composed(ctx);
|
|
341
|
+
return {
|
|
342
|
+
ok: true,
|
|
343
|
+
workflow: ctx.getWorkflowSnapshot(),
|
|
344
|
+
events: [...ctx.events]
|
|
345
|
+
};
|
|
346
|
+
} catch (err) {
|
|
347
|
+
if (err instanceof DomainErrorSignal) {
|
|
348
|
+
return {
|
|
349
|
+
ok: false,
|
|
350
|
+
error: {
|
|
351
|
+
category: "domain",
|
|
352
|
+
code: err.code,
|
|
353
|
+
data: err.data
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (err instanceof ValidationError) {
|
|
358
|
+
return {
|
|
359
|
+
ok: false,
|
|
360
|
+
error: {
|
|
361
|
+
category: "validation",
|
|
362
|
+
source: err.source,
|
|
363
|
+
issues: err.issues,
|
|
364
|
+
message: err.message
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
throw err;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
373
|
+
0 && (module.exports = {
|
|
374
|
+
DomainErrorSignal,
|
|
375
|
+
ValidationError,
|
|
376
|
+
WorkflowRouter,
|
|
377
|
+
createKey,
|
|
378
|
+
defineWorkflow
|
|
379
|
+
});
|
|
380
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { ZodType, z } from 'zod';
|
|
2
|
+
|
|
3
|
+
interface WorkflowConfig {
|
|
4
|
+
states: Record<string, ZodType>;
|
|
5
|
+
commands: Record<string, ZodType>;
|
|
6
|
+
events: Record<string, ZodType>;
|
|
7
|
+
errors: Record<string, ZodType>;
|
|
8
|
+
}
|
|
9
|
+
type StateNames<T extends WorkflowConfig> = keyof T["states"] & string;
|
|
10
|
+
type CommandNames<T extends WorkflowConfig> = keyof T["commands"] & string;
|
|
11
|
+
type EventNames<T extends WorkflowConfig> = keyof T["events"] & string;
|
|
12
|
+
type ErrorCodes<T extends WorkflowConfig> = keyof T["errors"] & string;
|
|
13
|
+
type StateData<T extends WorkflowConfig, S extends StateNames<T>> = T["states"][S] extends ZodType ? z.infer<T["states"][S]> : never;
|
|
14
|
+
type CommandPayload<T extends WorkflowConfig, C extends CommandNames<T>> = T["commands"][C] extends ZodType ? z.infer<T["commands"][C]> : never;
|
|
15
|
+
type EventData<T extends WorkflowConfig, E extends EventNames<T>> = T["events"][E] extends ZodType ? z.infer<T["events"][E]> : never;
|
|
16
|
+
type ErrorData<T extends WorkflowConfig, C extends ErrorCodes<T>> = T["errors"][C] extends ZodType ? z.infer<T["errors"][C]> : never;
|
|
17
|
+
/** Workflow narrowed to a specific known state. */
|
|
18
|
+
interface WorkflowOf<TConfig extends WorkflowConfig, S extends StateNames<TConfig>> {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly definitionName: string;
|
|
21
|
+
readonly state: S;
|
|
22
|
+
readonly data: StateData<TConfig, S>;
|
|
23
|
+
readonly createdAt: Date;
|
|
24
|
+
readonly updatedAt: Date;
|
|
25
|
+
}
|
|
26
|
+
/** Discriminated union of all possible workflow states — checking .state narrows .data. */
|
|
27
|
+
type Workflow<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
28
|
+
[S in StateNames<TConfig>]: WorkflowOf<TConfig, S>;
|
|
29
|
+
}[StateNames<TConfig>];
|
|
30
|
+
type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
31
|
+
category: "validation";
|
|
32
|
+
source: "command" | "state" | "event" | "transition";
|
|
33
|
+
issues: z.core.$ZodIssue[];
|
|
34
|
+
message: string;
|
|
35
|
+
} | {
|
|
36
|
+
category: "domain";
|
|
37
|
+
code: ErrorCodes<TConfig>;
|
|
38
|
+
data: ErrorData<TConfig, ErrorCodes<TConfig>>;
|
|
39
|
+
} | {
|
|
40
|
+
category: "router";
|
|
41
|
+
code: "NO_HANDLER" | "UNKNOWN_STATE";
|
|
42
|
+
message: string;
|
|
43
|
+
};
|
|
44
|
+
type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
45
|
+
ok: true;
|
|
46
|
+
workflow: Workflow<TConfig>;
|
|
47
|
+
events: Array<{
|
|
48
|
+
type: EventNames<TConfig>;
|
|
49
|
+
data: unknown;
|
|
50
|
+
}>;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
error: PipelineError<TConfig>;
|
|
54
|
+
};
|
|
55
|
+
/** Thrown internally when Zod validation fails during dispatch. */
|
|
56
|
+
declare class ValidationError extends Error {
|
|
57
|
+
readonly source: "command" | "state" | "event" | "transition";
|
|
58
|
+
readonly issues: z.core.$ZodIssue[];
|
|
59
|
+
constructor(source: "command" | "state" | "event" | "transition", issues: z.core.$ZodIssue[]);
|
|
60
|
+
}
|
|
61
|
+
/** Thrown internally when a handler calls ctx.error(). Caught by the router. */
|
|
62
|
+
declare class DomainErrorSignal extends Error {
|
|
63
|
+
readonly code: string;
|
|
64
|
+
readonly data: unknown;
|
|
65
|
+
constructor(code: string, data: unknown);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The result of defineWorkflow() — holds schemas and creates workflow instances. */
|
|
69
|
+
interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {
|
|
70
|
+
readonly config: TConfig;
|
|
71
|
+
readonly name: string;
|
|
72
|
+
createWorkflow<S extends StateNames<TConfig>>(id: string, config: {
|
|
73
|
+
initialState: S;
|
|
74
|
+
data: z.infer<TConfig["states"][S]>;
|
|
75
|
+
}): WorkflowOf<TConfig, S>;
|
|
76
|
+
getStateSchema(stateName: string): ZodType;
|
|
77
|
+
getCommandSchema(commandName: string): ZodType;
|
|
78
|
+
getEventSchema(eventName: string): ZodType;
|
|
79
|
+
getErrorSchema(errorCode: string): ZodType;
|
|
80
|
+
hasState(stateName: string): boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Creates a workflow definition from a name and Zod schema configuration.
|
|
84
|
+
*/
|
|
85
|
+
declare function defineWorkflow<const TConfig extends WorkflowConfig>(name: string, config: TConfig): WorkflowDefinition<TConfig>;
|
|
86
|
+
|
|
87
|
+
/** A phantom-typed key for type-safe middleware state storage. */
|
|
88
|
+
interface ContextKey<T> {
|
|
89
|
+
readonly _phantom: T;
|
|
90
|
+
readonly id: symbol;
|
|
91
|
+
}
|
|
92
|
+
/** Creates a unique typed key for storing/retrieving values in context. */
|
|
93
|
+
declare function createKey<T>(name: string): ContextKey<T>;
|
|
94
|
+
|
|
95
|
+
/** Mutable context flowing through the middleware pipeline during dispatch. */
|
|
96
|
+
interface Context<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig> = StateNames<TConfig>, TCommand extends CommandNames<TConfig> = CommandNames<TConfig>> {
|
|
97
|
+
readonly command: {
|
|
98
|
+
readonly type: TCommand;
|
|
99
|
+
readonly payload: CommandPayload<TConfig, TCommand>;
|
|
100
|
+
};
|
|
101
|
+
readonly workflow: WorkflowOf<TConfig, TState>;
|
|
102
|
+
readonly deps: TDeps;
|
|
103
|
+
readonly data: StateData<TConfig, TState>;
|
|
104
|
+
update(data: Partial<StateData<TConfig, TState>>): void;
|
|
105
|
+
transition<Target extends StateNames<TConfig>>(target: Target, data: StateData<TConfig, Target>): void;
|
|
106
|
+
emit<E extends EventNames<TConfig>>(event: {
|
|
107
|
+
type: E;
|
|
108
|
+
data: EventData<TConfig, E>;
|
|
109
|
+
}): void;
|
|
110
|
+
readonly events: ReadonlyArray<{
|
|
111
|
+
type: EventNames<TConfig>;
|
|
112
|
+
data: unknown;
|
|
113
|
+
}>;
|
|
114
|
+
error<C extends ErrorCodes<TConfig>>(err: {
|
|
115
|
+
code: C;
|
|
116
|
+
data: ErrorData<TConfig, C>;
|
|
117
|
+
}): never;
|
|
118
|
+
set<T>(key: ContextKey<T>, value: T): void;
|
|
119
|
+
get<T>(key: ContextKey<T>): T;
|
|
120
|
+
getOrNull<T>(key: ContextKey<T>): T | undefined;
|
|
121
|
+
/** @internal — not part of the handler API */
|
|
122
|
+
getWorkflowSnapshot(): Workflow<TConfig>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Terminal handler function — receives fully typed context with state and command narrowing. */
|
|
126
|
+
type Handler<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>, TCommand extends CommandNames<TConfig>> = (ctx: Context<TConfig, TDeps, TState, TCommand>) => void | Promise<void>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Koa-style middleware function with full context narrowing via defaults.
|
|
130
|
+
*
|
|
131
|
+
* - Global middleware: `Middleware<TConfig, TDeps>` — union of all states/commands
|
|
132
|
+
* - State-scoped: `Middleware<TConfig, TDeps, "draft">` — narrowed to state
|
|
133
|
+
* - Inline: `Middleware<TConfig, TDeps, "draft", "publish">` — fully narrowed
|
|
134
|
+
*/
|
|
135
|
+
type Middleware<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig> = StateNames<TConfig>, TCommand extends CommandNames<TConfig> = CommandNames<TConfig>> = (ctx: Context<TConfig, TDeps, TState, TCommand>, next: () => Promise<void>) => Promise<void>;
|
|
136
|
+
|
|
137
|
+
type AnyMiddleware = (ctx: any, next: () => Promise<void>) => Promise<void>;
|
|
138
|
+
type HandlerEntry = {
|
|
139
|
+
inlineMiddleware: AnyMiddleware[];
|
|
140
|
+
handler: AnyMiddleware;
|
|
141
|
+
};
|
|
142
|
+
declare class StateBuilder<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>> {
|
|
143
|
+
/** @internal */ readonly middleware: AnyMiddleware[];
|
|
144
|
+
/** @internal */ readonly handlers: Map<string, HandlerEntry>;
|
|
145
|
+
on<C extends CommandNames<TConfig>>(command: C, ...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>]): this;
|
|
146
|
+
use(middleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>): this;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Routes commands to handlers based on workflow state.
|
|
150
|
+
*
|
|
151
|
+
* Supports global middleware, state-scoped middleware, inline middleware,
|
|
152
|
+
* wildcard handlers, and multi-state handlers.
|
|
153
|
+
*/
|
|
154
|
+
declare class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {
|
|
155
|
+
private readonly definition;
|
|
156
|
+
private readonly deps;
|
|
157
|
+
private globalMiddleware;
|
|
158
|
+
private singleStateBuilders;
|
|
159
|
+
private multiStateBuilders;
|
|
160
|
+
private wildcardHandlers;
|
|
161
|
+
constructor(definition: WorkflowDefinition<TConfig>, deps?: TDeps);
|
|
162
|
+
/** Adds global middleware that wraps all dispatches. */
|
|
163
|
+
use(middleware: (ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>): this;
|
|
164
|
+
/** Registers handlers for one or more states. */
|
|
165
|
+
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
|
+
/** Registers a wildcard handler that matches any state. */
|
|
167
|
+
on<C extends CommandNames<TConfig>>(_state: "*", command: C, ...fns: [
|
|
168
|
+
...AnyMiddleware[],
|
|
169
|
+
(ctx: Context<TConfig, TDeps, StateNames<TConfig>, C>) => void | Promise<void>
|
|
170
|
+
]): this;
|
|
171
|
+
/** Dispatches a command to the appropriate handler and returns the result. */
|
|
172
|
+
dispatch(workflow: Workflow<TConfig>, command: {
|
|
173
|
+
type: CommandNames<TConfig>;
|
|
174
|
+
payload: unknown;
|
|
175
|
+
}): Promise<DispatchResult<TConfig>>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export { type CommandNames, type CommandPayload, type Context, type ContextKey, type DispatchResult, DomainErrorSignal, type ErrorCodes, type ErrorData, type EventData, type EventNames, type Handler, type Middleware, type PipelineError, type StateData, type StateNames, ValidationError, type Workflow, type WorkflowConfig, type WorkflowDefinition, type WorkflowOf, WorkflowRouter, createKey, defineWorkflow };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { ZodType, z } from 'zod';
|
|
2
|
+
|
|
3
|
+
interface WorkflowConfig {
|
|
4
|
+
states: Record<string, ZodType>;
|
|
5
|
+
commands: Record<string, ZodType>;
|
|
6
|
+
events: Record<string, ZodType>;
|
|
7
|
+
errors: Record<string, ZodType>;
|
|
8
|
+
}
|
|
9
|
+
type StateNames<T extends WorkflowConfig> = keyof T["states"] & string;
|
|
10
|
+
type CommandNames<T extends WorkflowConfig> = keyof T["commands"] & string;
|
|
11
|
+
type EventNames<T extends WorkflowConfig> = keyof T["events"] & string;
|
|
12
|
+
type ErrorCodes<T extends WorkflowConfig> = keyof T["errors"] & string;
|
|
13
|
+
type StateData<T extends WorkflowConfig, S extends StateNames<T>> = T["states"][S] extends ZodType ? z.infer<T["states"][S]> : never;
|
|
14
|
+
type CommandPayload<T extends WorkflowConfig, C extends CommandNames<T>> = T["commands"][C] extends ZodType ? z.infer<T["commands"][C]> : never;
|
|
15
|
+
type EventData<T extends WorkflowConfig, E extends EventNames<T>> = T["events"][E] extends ZodType ? z.infer<T["events"][E]> : never;
|
|
16
|
+
type ErrorData<T extends WorkflowConfig, C extends ErrorCodes<T>> = T["errors"][C] extends ZodType ? z.infer<T["errors"][C]> : never;
|
|
17
|
+
/** Workflow narrowed to a specific known state. */
|
|
18
|
+
interface WorkflowOf<TConfig extends WorkflowConfig, S extends StateNames<TConfig>> {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly definitionName: string;
|
|
21
|
+
readonly state: S;
|
|
22
|
+
readonly data: StateData<TConfig, S>;
|
|
23
|
+
readonly createdAt: Date;
|
|
24
|
+
readonly updatedAt: Date;
|
|
25
|
+
}
|
|
26
|
+
/** Discriminated union of all possible workflow states — checking .state narrows .data. */
|
|
27
|
+
type Workflow<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
28
|
+
[S in StateNames<TConfig>]: WorkflowOf<TConfig, S>;
|
|
29
|
+
}[StateNames<TConfig>];
|
|
30
|
+
type PipelineError<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
31
|
+
category: "validation";
|
|
32
|
+
source: "command" | "state" | "event" | "transition";
|
|
33
|
+
issues: z.core.$ZodIssue[];
|
|
34
|
+
message: string;
|
|
35
|
+
} | {
|
|
36
|
+
category: "domain";
|
|
37
|
+
code: ErrorCodes<TConfig>;
|
|
38
|
+
data: ErrorData<TConfig, ErrorCodes<TConfig>>;
|
|
39
|
+
} | {
|
|
40
|
+
category: "router";
|
|
41
|
+
code: "NO_HANDLER" | "UNKNOWN_STATE";
|
|
42
|
+
message: string;
|
|
43
|
+
};
|
|
44
|
+
type DispatchResult<TConfig extends WorkflowConfig = WorkflowConfig> = {
|
|
45
|
+
ok: true;
|
|
46
|
+
workflow: Workflow<TConfig>;
|
|
47
|
+
events: Array<{
|
|
48
|
+
type: EventNames<TConfig>;
|
|
49
|
+
data: unknown;
|
|
50
|
+
}>;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
error: PipelineError<TConfig>;
|
|
54
|
+
};
|
|
55
|
+
/** Thrown internally when Zod validation fails during dispatch. */
|
|
56
|
+
declare class ValidationError extends Error {
|
|
57
|
+
readonly source: "command" | "state" | "event" | "transition";
|
|
58
|
+
readonly issues: z.core.$ZodIssue[];
|
|
59
|
+
constructor(source: "command" | "state" | "event" | "transition", issues: z.core.$ZodIssue[]);
|
|
60
|
+
}
|
|
61
|
+
/** Thrown internally when a handler calls ctx.error(). Caught by the router. */
|
|
62
|
+
declare class DomainErrorSignal extends Error {
|
|
63
|
+
readonly code: string;
|
|
64
|
+
readonly data: unknown;
|
|
65
|
+
constructor(code: string, data: unknown);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The result of defineWorkflow() — holds schemas and creates workflow instances. */
|
|
69
|
+
interface WorkflowDefinition<TConfig extends WorkflowConfig = WorkflowConfig> {
|
|
70
|
+
readonly config: TConfig;
|
|
71
|
+
readonly name: string;
|
|
72
|
+
createWorkflow<S extends StateNames<TConfig>>(id: string, config: {
|
|
73
|
+
initialState: S;
|
|
74
|
+
data: z.infer<TConfig["states"][S]>;
|
|
75
|
+
}): WorkflowOf<TConfig, S>;
|
|
76
|
+
getStateSchema(stateName: string): ZodType;
|
|
77
|
+
getCommandSchema(commandName: string): ZodType;
|
|
78
|
+
getEventSchema(eventName: string): ZodType;
|
|
79
|
+
getErrorSchema(errorCode: string): ZodType;
|
|
80
|
+
hasState(stateName: string): boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Creates a workflow definition from a name and Zod schema configuration.
|
|
84
|
+
*/
|
|
85
|
+
declare function defineWorkflow<const TConfig extends WorkflowConfig>(name: string, config: TConfig): WorkflowDefinition<TConfig>;
|
|
86
|
+
|
|
87
|
+
/** A phantom-typed key for type-safe middleware state storage. */
|
|
88
|
+
interface ContextKey<T> {
|
|
89
|
+
readonly _phantom: T;
|
|
90
|
+
readonly id: symbol;
|
|
91
|
+
}
|
|
92
|
+
/** Creates a unique typed key for storing/retrieving values in context. */
|
|
93
|
+
declare function createKey<T>(name: string): ContextKey<T>;
|
|
94
|
+
|
|
95
|
+
/** Mutable context flowing through the middleware pipeline during dispatch. */
|
|
96
|
+
interface Context<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig> = StateNames<TConfig>, TCommand extends CommandNames<TConfig> = CommandNames<TConfig>> {
|
|
97
|
+
readonly command: {
|
|
98
|
+
readonly type: TCommand;
|
|
99
|
+
readonly payload: CommandPayload<TConfig, TCommand>;
|
|
100
|
+
};
|
|
101
|
+
readonly workflow: WorkflowOf<TConfig, TState>;
|
|
102
|
+
readonly deps: TDeps;
|
|
103
|
+
readonly data: StateData<TConfig, TState>;
|
|
104
|
+
update(data: Partial<StateData<TConfig, TState>>): void;
|
|
105
|
+
transition<Target extends StateNames<TConfig>>(target: Target, data: StateData<TConfig, Target>): void;
|
|
106
|
+
emit<E extends EventNames<TConfig>>(event: {
|
|
107
|
+
type: E;
|
|
108
|
+
data: EventData<TConfig, E>;
|
|
109
|
+
}): void;
|
|
110
|
+
readonly events: ReadonlyArray<{
|
|
111
|
+
type: EventNames<TConfig>;
|
|
112
|
+
data: unknown;
|
|
113
|
+
}>;
|
|
114
|
+
error<C extends ErrorCodes<TConfig>>(err: {
|
|
115
|
+
code: C;
|
|
116
|
+
data: ErrorData<TConfig, C>;
|
|
117
|
+
}): never;
|
|
118
|
+
set<T>(key: ContextKey<T>, value: T): void;
|
|
119
|
+
get<T>(key: ContextKey<T>): T;
|
|
120
|
+
getOrNull<T>(key: ContextKey<T>): T | undefined;
|
|
121
|
+
/** @internal — not part of the handler API */
|
|
122
|
+
getWorkflowSnapshot(): Workflow<TConfig>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Terminal handler function — receives fully typed context with state and command narrowing. */
|
|
126
|
+
type Handler<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>, TCommand extends CommandNames<TConfig>> = (ctx: Context<TConfig, TDeps, TState, TCommand>) => void | Promise<void>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Koa-style middleware function with full context narrowing via defaults.
|
|
130
|
+
*
|
|
131
|
+
* - Global middleware: `Middleware<TConfig, TDeps>` — union of all states/commands
|
|
132
|
+
* - State-scoped: `Middleware<TConfig, TDeps, "draft">` — narrowed to state
|
|
133
|
+
* - Inline: `Middleware<TConfig, TDeps, "draft", "publish">` — fully narrowed
|
|
134
|
+
*/
|
|
135
|
+
type Middleware<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig> = StateNames<TConfig>, TCommand extends CommandNames<TConfig> = CommandNames<TConfig>> = (ctx: Context<TConfig, TDeps, TState, TCommand>, next: () => Promise<void>) => Promise<void>;
|
|
136
|
+
|
|
137
|
+
type AnyMiddleware = (ctx: any, next: () => Promise<void>) => Promise<void>;
|
|
138
|
+
type HandlerEntry = {
|
|
139
|
+
inlineMiddleware: AnyMiddleware[];
|
|
140
|
+
handler: AnyMiddleware;
|
|
141
|
+
};
|
|
142
|
+
declare class StateBuilder<TConfig extends WorkflowConfig, TDeps, TState extends StateNames<TConfig>> {
|
|
143
|
+
/** @internal */ readonly middleware: AnyMiddleware[];
|
|
144
|
+
/** @internal */ readonly handlers: Map<string, HandlerEntry>;
|
|
145
|
+
on<C extends CommandNames<TConfig>>(command: C, ...fns: [...AnyMiddleware[], (ctx: Context<TConfig, TDeps, TState, C>) => void | Promise<void>]): this;
|
|
146
|
+
use(middleware: (ctx: Context<TConfig, TDeps, TState>, next: () => Promise<void>) => Promise<void>): this;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Routes commands to handlers based on workflow state.
|
|
150
|
+
*
|
|
151
|
+
* Supports global middleware, state-scoped middleware, inline middleware,
|
|
152
|
+
* wildcard handlers, and multi-state handlers.
|
|
153
|
+
*/
|
|
154
|
+
declare class WorkflowRouter<TConfig extends WorkflowConfig, TDeps = {}> {
|
|
155
|
+
private readonly definition;
|
|
156
|
+
private readonly deps;
|
|
157
|
+
private globalMiddleware;
|
|
158
|
+
private singleStateBuilders;
|
|
159
|
+
private multiStateBuilders;
|
|
160
|
+
private wildcardHandlers;
|
|
161
|
+
constructor(definition: WorkflowDefinition<TConfig>, deps?: TDeps);
|
|
162
|
+
/** Adds global middleware that wraps all dispatches. */
|
|
163
|
+
use(middleware: (ctx: Context<TConfig, TDeps>, next: () => Promise<void>) => Promise<void>): this;
|
|
164
|
+
/** Registers handlers for one or more states. */
|
|
165
|
+
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
|
+
/** Registers a wildcard handler that matches any state. */
|
|
167
|
+
on<C extends CommandNames<TConfig>>(_state: "*", command: C, ...fns: [
|
|
168
|
+
...AnyMiddleware[],
|
|
169
|
+
(ctx: Context<TConfig, TDeps, StateNames<TConfig>, C>) => void | Promise<void>
|
|
170
|
+
]): this;
|
|
171
|
+
/** Dispatches a command to the appropriate handler and returns the result. */
|
|
172
|
+
dispatch(workflow: Workflow<TConfig>, command: {
|
|
173
|
+
type: CommandNames<TConfig>;
|
|
174
|
+
payload: unknown;
|
|
175
|
+
}): Promise<DispatchResult<TConfig>>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export { type CommandNames, type CommandPayload, type Context, type ContextKey, type DispatchResult, DomainErrorSignal, type ErrorCodes, type ErrorData, type EventData, type EventNames, type Handler, type Middleware, type PipelineError, type StateData, type StateNames, ValidationError, type Workflow, type WorkflowConfig, type WorkflowDefinition, type WorkflowOf, WorkflowRouter, createKey, defineWorkflow };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// src/definition.ts
|
|
2
|
+
function defineWorkflow(name, config) {
|
|
3
|
+
return {
|
|
4
|
+
config,
|
|
5
|
+
name,
|
|
6
|
+
createWorkflow(id, wfConfig) {
|
|
7
|
+
const schema = config.states[wfConfig.initialState];
|
|
8
|
+
if (!schema) throw new Error(`Unknown state: ${wfConfig.initialState}`);
|
|
9
|
+
const result = schema.safeParse(wfConfig.data);
|
|
10
|
+
if (!result.success) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`Invalid initial data for state '${wfConfig.initialState}': ${result.error.issues.map((i) => i.message).join(", ")}`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
const now = /* @__PURE__ */ new Date();
|
|
16
|
+
return {
|
|
17
|
+
id,
|
|
18
|
+
definitionName: name,
|
|
19
|
+
state: wfConfig.initialState,
|
|
20
|
+
data: result.data,
|
|
21
|
+
createdAt: now,
|
|
22
|
+
updatedAt: now
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
getStateSchema(stateName) {
|
|
26
|
+
const schema = config.states[stateName];
|
|
27
|
+
if (!schema) throw new Error(`Unknown state: ${stateName}`);
|
|
28
|
+
return schema;
|
|
29
|
+
},
|
|
30
|
+
getCommandSchema(commandName) {
|
|
31
|
+
const schema = config.commands[commandName];
|
|
32
|
+
if (!schema) throw new Error(`Unknown command: ${commandName}`);
|
|
33
|
+
return schema;
|
|
34
|
+
},
|
|
35
|
+
getEventSchema(eventName) {
|
|
36
|
+
const schema = config.events[eventName];
|
|
37
|
+
if (!schema) throw new Error(`Unknown event: ${eventName}`);
|
|
38
|
+
return schema;
|
|
39
|
+
},
|
|
40
|
+
getErrorSchema(errorCode) {
|
|
41
|
+
const schema = config.errors[errorCode];
|
|
42
|
+
if (!schema) throw new Error(`Unknown error: ${errorCode}`);
|
|
43
|
+
return schema;
|
|
44
|
+
},
|
|
45
|
+
hasState(stateName) {
|
|
46
|
+
return stateName in config.states;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/key.ts
|
|
52
|
+
function createKey(name) {
|
|
53
|
+
return { id: Symbol(name) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/compose.ts
|
|
57
|
+
function compose(middleware) {
|
|
58
|
+
return async (ctx) => {
|
|
59
|
+
let index = -1;
|
|
60
|
+
async function dispatch(i) {
|
|
61
|
+
if (i <= index) throw new Error("next() called multiple times");
|
|
62
|
+
index = i;
|
|
63
|
+
const fn = middleware[i];
|
|
64
|
+
if (!fn) return;
|
|
65
|
+
await fn(ctx, () => dispatch(i + 1));
|
|
66
|
+
}
|
|
67
|
+
await dispatch(0);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/types.ts
|
|
72
|
+
var ValidationError = class extends Error {
|
|
73
|
+
constructor(source, issues) {
|
|
74
|
+
super(`Validation failed (${source}): ${issues.map((i) => i.message).join(", ")}`);
|
|
75
|
+
this.source = source;
|
|
76
|
+
this.issues = issues;
|
|
77
|
+
this.name = "ValidationError";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var DomainErrorSignal = class extends Error {
|
|
81
|
+
constructor(code, data) {
|
|
82
|
+
super(`Domain error: ${code}`);
|
|
83
|
+
this.code = code;
|
|
84
|
+
this.data = data;
|
|
85
|
+
this.name = "DomainErrorSignal";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// src/context.ts
|
|
90
|
+
function createContext(definition, originalWorkflow, command, deps) {
|
|
91
|
+
let mutableState = originalWorkflow.state;
|
|
92
|
+
let mutableData = {
|
|
93
|
+
...originalWorkflow.data
|
|
94
|
+
};
|
|
95
|
+
const accumulatedEvents = [];
|
|
96
|
+
const middlewareState = /* @__PURE__ */ new Map();
|
|
97
|
+
const ctx = {
|
|
98
|
+
command,
|
|
99
|
+
workflow: originalWorkflow,
|
|
100
|
+
deps,
|
|
101
|
+
get data() {
|
|
102
|
+
return { ...mutableData };
|
|
103
|
+
},
|
|
104
|
+
update(data) {
|
|
105
|
+
const merged = { ...mutableData, ...data };
|
|
106
|
+
const schema = definition.getStateSchema(mutableState);
|
|
107
|
+
const result = schema.safeParse(merged);
|
|
108
|
+
if (!result.success) {
|
|
109
|
+
throw new ValidationError("state", result.error.issues);
|
|
110
|
+
}
|
|
111
|
+
mutableData = result.data;
|
|
112
|
+
},
|
|
113
|
+
transition(target, data) {
|
|
114
|
+
if (!definition.hasState(target)) {
|
|
115
|
+
throw new Error(`Unknown state: ${target}`);
|
|
116
|
+
}
|
|
117
|
+
const schema = definition.getStateSchema(target);
|
|
118
|
+
const result = schema.safeParse(data);
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
throw new ValidationError("transition", result.error.issues);
|
|
121
|
+
}
|
|
122
|
+
mutableState = target;
|
|
123
|
+
mutableData = result.data;
|
|
124
|
+
},
|
|
125
|
+
emit(event) {
|
|
126
|
+
const schema = definition.getEventSchema(event.type);
|
|
127
|
+
const result = schema.safeParse(event.data);
|
|
128
|
+
if (!result.success) {
|
|
129
|
+
throw new ValidationError("event", result.error.issues);
|
|
130
|
+
}
|
|
131
|
+
accumulatedEvents.push({ type: event.type, data: result.data });
|
|
132
|
+
},
|
|
133
|
+
get events() {
|
|
134
|
+
return [...accumulatedEvents];
|
|
135
|
+
},
|
|
136
|
+
error(err) {
|
|
137
|
+
const schema = definition.getErrorSchema(err.code);
|
|
138
|
+
const result = schema.safeParse(err.data);
|
|
139
|
+
if (!result.success) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Invalid error data for '${err.code}': ${result.error.issues.map((i) => i.message).join(", ")}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
throw new DomainErrorSignal(err.code, result.data);
|
|
145
|
+
},
|
|
146
|
+
set(key, value) {
|
|
147
|
+
middlewareState.set(key.id, value);
|
|
148
|
+
},
|
|
149
|
+
get(key) {
|
|
150
|
+
if (!middlewareState.has(key.id)) {
|
|
151
|
+
throw new Error(`Context key not set: ${key.id.toString()}`);
|
|
152
|
+
}
|
|
153
|
+
return middlewareState.get(key.id);
|
|
154
|
+
},
|
|
155
|
+
getOrNull(key) {
|
|
156
|
+
return middlewareState.get(key.id);
|
|
157
|
+
},
|
|
158
|
+
getWorkflowSnapshot() {
|
|
159
|
+
return {
|
|
160
|
+
id: originalWorkflow.id,
|
|
161
|
+
definitionName: originalWorkflow.definitionName,
|
|
162
|
+
state: mutableState,
|
|
163
|
+
data: { ...mutableData },
|
|
164
|
+
createdAt: originalWorkflow.createdAt,
|
|
165
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
return ctx;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/router.ts
|
|
173
|
+
var StateBuilder = class {
|
|
174
|
+
/** @internal */
|
|
175
|
+
middleware = [];
|
|
176
|
+
/** @internal */
|
|
177
|
+
handlers = /* @__PURE__ */ new Map();
|
|
178
|
+
on(command, ...fns) {
|
|
179
|
+
if (fns.length === 0) throw new Error("on() requires at least a handler");
|
|
180
|
+
const handler = fns.pop();
|
|
181
|
+
const inlineMiddleware = fns;
|
|
182
|
+
const wrappedHandler = async (ctx, _next) => {
|
|
183
|
+
await handler(ctx);
|
|
184
|
+
};
|
|
185
|
+
this.handlers.set(command, { inlineMiddleware, handler: wrappedHandler });
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
use(middleware) {
|
|
189
|
+
this.middleware.push(middleware);
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var WorkflowRouter = class {
|
|
194
|
+
constructor(definition, deps = {}) {
|
|
195
|
+
this.definition = definition;
|
|
196
|
+
this.deps = deps;
|
|
197
|
+
}
|
|
198
|
+
globalMiddleware = [];
|
|
199
|
+
singleStateBuilders = /* @__PURE__ */ new Map();
|
|
200
|
+
multiStateBuilders = /* @__PURE__ */ new Map();
|
|
201
|
+
wildcardHandlers = /* @__PURE__ */ new Map();
|
|
202
|
+
/** Adds global middleware that wraps all dispatches. */
|
|
203
|
+
use(middleware) {
|
|
204
|
+
this.globalMiddleware.push(middleware);
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
/** Registers handlers for one or more states. */
|
|
208
|
+
state(name, setup) {
|
|
209
|
+
const names = Array.isArray(name) ? name : [name];
|
|
210
|
+
const isMulti = Array.isArray(name);
|
|
211
|
+
const routerMap = isMulti ? this.multiStateBuilders : this.singleStateBuilders;
|
|
212
|
+
for (const n of names) {
|
|
213
|
+
let router = routerMap.get(n);
|
|
214
|
+
if (!router) {
|
|
215
|
+
router = new StateBuilder();
|
|
216
|
+
routerMap.set(n, router);
|
|
217
|
+
}
|
|
218
|
+
setup(router);
|
|
219
|
+
}
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
/** Registers a wildcard handler that matches any state. */
|
|
223
|
+
on(_state, command, ...fns) {
|
|
224
|
+
if (fns.length === 0) throw new Error("on() requires at least a handler");
|
|
225
|
+
const handler = fns.pop();
|
|
226
|
+
const inlineMiddleware = fns;
|
|
227
|
+
const wrappedHandler = async (ctx, _next) => {
|
|
228
|
+
await handler(ctx);
|
|
229
|
+
};
|
|
230
|
+
this.wildcardHandlers.set(command, {
|
|
231
|
+
inlineMiddleware,
|
|
232
|
+
handler: wrappedHandler
|
|
233
|
+
});
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
/** Dispatches a command to the appropriate handler and returns the result. */
|
|
237
|
+
async dispatch(workflow, command) {
|
|
238
|
+
if (!this.definition.hasState(workflow.state)) {
|
|
239
|
+
return {
|
|
240
|
+
ok: false,
|
|
241
|
+
error: {
|
|
242
|
+
category: "router",
|
|
243
|
+
code: "UNKNOWN_STATE",
|
|
244
|
+
message: `Unknown state: ${workflow.state}`
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const commandSchema = this.definition.getCommandSchema(command.type);
|
|
249
|
+
const payloadResult = commandSchema.safeParse(command.payload);
|
|
250
|
+
if (!payloadResult.success) {
|
|
251
|
+
return {
|
|
252
|
+
ok: false,
|
|
253
|
+
error: {
|
|
254
|
+
category: "validation",
|
|
255
|
+
source: "command",
|
|
256
|
+
issues: payloadResult.error.issues,
|
|
257
|
+
message: `Invalid command payload: ${payloadResult.error.issues.map((i) => i.message).join(", ")}`
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const validatedCommand = { type: command.type, payload: payloadResult.data };
|
|
262
|
+
const stateName = workflow.state;
|
|
263
|
+
const singleRouter = this.singleStateBuilders.get(stateName);
|
|
264
|
+
const multiRouter = this.multiStateBuilders.get(stateName);
|
|
265
|
+
const singleHandler = singleRouter?.handlers.get(command.type);
|
|
266
|
+
const multiHandler = multiRouter?.handlers.get(command.type);
|
|
267
|
+
const wildcardHandler = this.wildcardHandlers.get(command.type);
|
|
268
|
+
let routeEntry;
|
|
269
|
+
let matchedRouter;
|
|
270
|
+
if (singleHandler) {
|
|
271
|
+
routeEntry = singleHandler;
|
|
272
|
+
matchedRouter = singleRouter;
|
|
273
|
+
} else if (multiHandler) {
|
|
274
|
+
routeEntry = multiHandler;
|
|
275
|
+
matchedRouter = multiRouter;
|
|
276
|
+
} else if (wildcardHandler) {
|
|
277
|
+
routeEntry = wildcardHandler;
|
|
278
|
+
matchedRouter = void 0;
|
|
279
|
+
}
|
|
280
|
+
if (!routeEntry) {
|
|
281
|
+
return {
|
|
282
|
+
ok: false,
|
|
283
|
+
error: {
|
|
284
|
+
category: "router",
|
|
285
|
+
code: "NO_HANDLER",
|
|
286
|
+
message: `No handler for command '${command.type}' in state '${stateName}'`
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const stateMiddleware = [];
|
|
291
|
+
if (matchedRouter) {
|
|
292
|
+
if (singleRouter) stateMiddleware.push(...singleRouter.middleware);
|
|
293
|
+
if (multiRouter && multiRouter !== singleRouter)
|
|
294
|
+
stateMiddleware.push(...multiRouter.middleware);
|
|
295
|
+
}
|
|
296
|
+
const chain = [
|
|
297
|
+
...this.globalMiddleware,
|
|
298
|
+
...stateMiddleware,
|
|
299
|
+
...routeEntry.inlineMiddleware,
|
|
300
|
+
routeEntry.handler
|
|
301
|
+
];
|
|
302
|
+
const ctx = createContext(
|
|
303
|
+
this.definition,
|
|
304
|
+
workflow,
|
|
305
|
+
validatedCommand,
|
|
306
|
+
this.deps
|
|
307
|
+
);
|
|
308
|
+
try {
|
|
309
|
+
const composed = compose(chain);
|
|
310
|
+
await composed(ctx);
|
|
311
|
+
return {
|
|
312
|
+
ok: true,
|
|
313
|
+
workflow: ctx.getWorkflowSnapshot(),
|
|
314
|
+
events: [...ctx.events]
|
|
315
|
+
};
|
|
316
|
+
} catch (err) {
|
|
317
|
+
if (err instanceof DomainErrorSignal) {
|
|
318
|
+
return {
|
|
319
|
+
ok: false,
|
|
320
|
+
error: {
|
|
321
|
+
category: "domain",
|
|
322
|
+
code: err.code,
|
|
323
|
+
data: err.data
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (err instanceof ValidationError) {
|
|
328
|
+
return {
|
|
329
|
+
ok: false,
|
|
330
|
+
error: {
|
|
331
|
+
category: "validation",
|
|
332
|
+
source: err.source,
|
|
333
|
+
issues: err.issues,
|
|
334
|
+
message: err.message
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
throw err;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
export {
|
|
343
|
+
DomainErrorSignal,
|
|
344
|
+
ValidationError,
|
|
345
|
+
WorkflowRouter,
|
|
346
|
+
createKey,
|
|
347
|
+
defineWorkflow
|
|
348
|
+
};
|
|
349
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rytejs/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Type-safe workflow engine with Zod validation and middleware pipelines",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.cjs"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.cjs",
|
|
15
|
+
"module": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/helico-tech/rytejs",
|
|
24
|
+
"directory": "packages/core"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://helico-tech.github.io/rytejs",
|
|
27
|
+
"bugs": "https://github.com/helico-tech/rytejs/issues",
|
|
28
|
+
"keywords": [
|
|
29
|
+
"workflow",
|
|
30
|
+
"state-machine",
|
|
31
|
+
"typescript",
|
|
32
|
+
"zod",
|
|
33
|
+
"middleware"
|
|
34
|
+
],
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"zod": "^4.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.7.0",
|
|
41
|
+
"vitest": "^3.0.0",
|
|
42
|
+
"zod": "^4.0.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"test:coverage": "vitest run --coverage",
|
|
52
|
+
"typecheck": "tsc --noEmit"
|
|
53
|
+
}
|
|
54
|
+
}
|