@pogodisco/zephyr 1.3.5 → 1.3.6
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/registry.js +0 -40
- package/dist/utils.js +0 -41
- package/dist/workflow-composer.d.ts +46 -27
- package/dist/workflow-composer.js +418 -16
- package/dist/workflow-executor.js +9 -631
- package/dist/workflow-module.d.ts +1 -5
- package/dist/workflow-module.js +1 -5
- package/package.json +1 -1
|
@@ -1,634 +1,12 @@
|
|
|
1
|
-
// import { composeMiddleware } from "../middleware/index.js";
|
|
2
|
-
// import { ActionRegistry } from "../registry/types.js";
|
|
3
|
-
// import { WorkflowDef } from "./main.js";
|
|
4
|
-
// import { ExecutionFrame, WorkflowMiddleware } from "./types.js";
|
|
5
|
-
//
|
|
6
|
-
// export async function executeWorkflow<Reg extends ActionRegistry, I, R>(
|
|
7
|
-
// workflow: WorkflowDef<Reg, I, R>,
|
|
8
|
-
// registry: Reg,
|
|
9
|
-
// input: I,
|
|
10
|
-
// middleware: WorkflowMiddleware<Reg>[] = [],
|
|
11
|
-
// ): Promise<{ results: R; extras: Record<string, any> }> {
|
|
12
|
-
// const results: Record<string, any> = {};
|
|
13
|
-
// const extras: Record<string, any> = {};
|
|
14
|
-
// extras.frames = {} as Record<string, ExecutionFrame>; // <-- store execution frames per step
|
|
15
|
-
//
|
|
16
|
-
// const stepById = new Map(workflow.steps.map((s) => [s.id, s]));
|
|
17
|
-
// const remainingDeps = new Map<string, number>();
|
|
18
|
-
// const dependents = new Map<string, string[]>();
|
|
19
|
-
// const ready: string[] = [];
|
|
20
|
-
//
|
|
21
|
-
// // Build dependency graph
|
|
22
|
-
// for (const step of workflow.steps) {
|
|
23
|
-
// remainingDeps.set(step.id, step.dependsOn.length);
|
|
24
|
-
// if (step.dependsOn.length === 0) ready.push(step.id);
|
|
25
|
-
//
|
|
26
|
-
// for (const dep of step.dependsOn) {
|
|
27
|
-
// if (!dependents.has(dep)) dependents.set(dep, []);
|
|
28
|
-
// dependents.get(dep)!.push(step.id);
|
|
29
|
-
// }
|
|
30
|
-
// }
|
|
31
|
-
//
|
|
32
|
-
// let completed = 0;
|
|
33
|
-
//
|
|
34
|
-
// // Scheduler loop
|
|
35
|
-
// while (ready.length > 0) {
|
|
36
|
-
// const batch = ready.splice(0);
|
|
37
|
-
//
|
|
38
|
-
// await Promise.all(
|
|
39
|
-
// batch.map(async (stepId) => {
|
|
40
|
-
// const step = stepById.get(stepId)!;
|
|
41
|
-
//
|
|
42
|
-
// const frame: ExecutionFrame = {
|
|
43
|
-
// stepId,
|
|
44
|
-
// attempts: 0,
|
|
45
|
-
// start: Date.now(),
|
|
46
|
-
// };
|
|
47
|
-
// extras.frames[stepId] = frame;
|
|
48
|
-
//
|
|
49
|
-
// const ctx = {
|
|
50
|
-
// stepId,
|
|
51
|
-
// input,
|
|
52
|
-
// results,
|
|
53
|
-
// registry,
|
|
54
|
-
// extras,
|
|
55
|
-
// frame,
|
|
56
|
-
// };
|
|
57
|
-
//
|
|
58
|
-
// const core = async () => {
|
|
59
|
-
// frame.attempts++;
|
|
60
|
-
// frame.input = step.resolve({ input, results });
|
|
61
|
-
//
|
|
62
|
-
// try {
|
|
63
|
-
// const action = registry[step.action];
|
|
64
|
-
//
|
|
65
|
-
// const result = await action(frame.input);
|
|
66
|
-
// frame.output = result;
|
|
67
|
-
// frame.end = Date.now();
|
|
68
|
-
//
|
|
69
|
-
// results[step.id] = result;
|
|
70
|
-
// return result;
|
|
71
|
-
// } catch (err) {
|
|
72
|
-
// frame.error = err;
|
|
73
|
-
// frame.end = Date.now();
|
|
74
|
-
// throw err;
|
|
75
|
-
// }
|
|
76
|
-
// };
|
|
77
|
-
//
|
|
78
|
-
// const composed = composeMiddleware(middleware, ctx, core);
|
|
79
|
-
// await composed();
|
|
80
|
-
//
|
|
81
|
-
// // Activate dependents
|
|
82
|
-
// for (const childId of dependents.get(stepId) ?? []) {
|
|
83
|
-
// const remaining = remainingDeps.get(childId)! - 1;
|
|
84
|
-
// remainingDeps.set(childId, remaining);
|
|
85
|
-
// if (remaining === 0) ready.push(childId);
|
|
86
|
-
// }
|
|
87
|
-
//
|
|
88
|
-
// completed++;
|
|
89
|
-
// }),
|
|
90
|
-
// );
|
|
91
|
-
// }
|
|
92
|
-
//
|
|
93
|
-
// // Deadlock detection
|
|
94
|
-
// if (completed !== workflow.steps.length) {
|
|
95
|
-
// throw new Error("Workflow execution failed (cycle or missing dependency)");
|
|
96
|
-
// }
|
|
97
|
-
//
|
|
98
|
-
// return { results: results as R, extras };
|
|
99
|
-
// }
|
|
100
|
-
// import { composeMiddleware } from "./middleware.js";
|
|
101
|
-
// import { ActionRegistry, ExecutionFrame, WorkflowMiddleware } from "./types.js";
|
|
102
|
-
// import { StepDef, WorkflowDef } from "./workflow-composer.js";
|
|
103
|
-
//
|
|
104
|
-
// export async function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>(
|
|
105
|
-
// workflow: WorkflowDef<Reg, I, R, any, O>,
|
|
106
|
-
// registry: Reg,
|
|
107
|
-
// input: I,
|
|
108
|
-
// middleware: WorkflowMiddleware<Reg>[] = [],
|
|
109
|
-
// ): Promise<{ results: R; output: O; extras: Record<string, any> }> {
|
|
110
|
-
// const results: Record<string, any> = {};
|
|
111
|
-
// const extras: Record<string, any> = {};
|
|
112
|
-
// extras.frames = {} as Record<string, ExecutionFrame>;
|
|
113
|
-
//
|
|
114
|
-
// // --- strongly type steps ---
|
|
115
|
-
//
|
|
116
|
-
// const stepById = new Map<string, StepDef<Reg, any, any>>(
|
|
117
|
-
// workflow.steps.map((s: StepDef<Reg, any, any>) => [s.id, s]),
|
|
118
|
-
// );
|
|
119
|
-
// const remainingDeps = new Map<string, number>();
|
|
120
|
-
// const dependents = new Map<string, string[]>();
|
|
121
|
-
// const ready: string[] = [];
|
|
122
|
-
//
|
|
123
|
-
// // Build dependency graph
|
|
124
|
-
// for (const step of workflow.steps) {
|
|
125
|
-
// remainingDeps.set(step.id, step.dependsOn.length);
|
|
126
|
-
// if (step.dependsOn.length === 0) ready.push(step.id);
|
|
127
|
-
//
|
|
128
|
-
// for (const dep of step.dependsOn) {
|
|
129
|
-
// if (!dependents.has(dep)) dependents.set(dep, []);
|
|
130
|
-
// dependents.get(dep)!.push(step.id);
|
|
131
|
-
// }
|
|
132
|
-
// }
|
|
133
|
-
//
|
|
134
|
-
// let completed = 0;
|
|
135
|
-
//
|
|
136
|
-
// while (ready.length > 0) {
|
|
137
|
-
// const batch = ready.splice(0);
|
|
138
|
-
//
|
|
139
|
-
// await Promise.all(
|
|
140
|
-
// batch.map(async (stepId) => {
|
|
141
|
-
// const step = stepById.get(stepId)!;
|
|
142
|
-
//
|
|
143
|
-
// const frame: ExecutionFrame = {
|
|
144
|
-
// stepId,
|
|
145
|
-
// attempts: 0,
|
|
146
|
-
// start: Date.now(),
|
|
147
|
-
// };
|
|
148
|
-
// extras.frames[stepId] = frame;
|
|
149
|
-
//
|
|
150
|
-
// const ctx = { stepId, input, results, registry, extras, frame };
|
|
151
|
-
//
|
|
152
|
-
// const core = async () => {
|
|
153
|
-
// frame.attempts++;
|
|
154
|
-
// frame.input = step.resolve?.({ input, results }) ?? undefined;
|
|
155
|
-
//
|
|
156
|
-
// try {
|
|
157
|
-
// const action = registry[step.action];
|
|
158
|
-
// const result =
|
|
159
|
-
// frame.input === undefined
|
|
160
|
-
// ? await (action as () => Promise<any>)()
|
|
161
|
-
// : await action(frame.input);
|
|
162
|
-
// // const result = await action(frame.input);
|
|
163
|
-
// frame.output = result;
|
|
164
|
-
// frame.end = Date.now();
|
|
165
|
-
//
|
|
166
|
-
// results[step.id] = result;
|
|
167
|
-
// return result;
|
|
168
|
-
// } catch (err) {
|
|
169
|
-
// frame.error = err;
|
|
170
|
-
// frame.end = Date.now();
|
|
171
|
-
// throw err;
|
|
172
|
-
// }
|
|
173
|
-
// };
|
|
174
|
-
//
|
|
175
|
-
// const composed = composeMiddleware(middleware, ctx, core);
|
|
176
|
-
// await composed();
|
|
177
|
-
//
|
|
178
|
-
// for (const childId of dependents.get(stepId) ?? []) {
|
|
179
|
-
// const remaining = remainingDeps.get(childId)! - 1;
|
|
180
|
-
// remainingDeps.set(childId, remaining);
|
|
181
|
-
// if (remaining === 0) ready.push(childId);
|
|
182
|
-
// }
|
|
183
|
-
//
|
|
184
|
-
// completed++;
|
|
185
|
-
// }),
|
|
186
|
-
// );
|
|
187
|
-
// }
|
|
188
|
-
//
|
|
189
|
-
// if (completed !== workflow.steps.length) {
|
|
190
|
-
// throw new Error("Workflow execution failed (cycle or missing dependency)");
|
|
191
|
-
// }
|
|
192
|
-
//
|
|
193
|
-
// // Resolve output
|
|
194
|
-
// const output: O = workflow.outputResolver
|
|
195
|
-
// ? workflow.outputResolver({ input, results: results as R })
|
|
196
|
-
// : (results as unknown as O);
|
|
197
|
-
//
|
|
198
|
-
// return { results: results as R, output, extras };
|
|
199
|
-
// }
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
///// with any fn shape
|
|
203
|
-
// import { composeObserver } from "./observer.js";
|
|
204
|
-
// import { ActionRegistry, ExecutionFrame, WorkflowObserver } from "./types.js";
|
|
205
|
-
// import { StepDef, WorkflowDef } from "./workflow-composer.js";
|
|
206
|
-
//
|
|
207
|
-
// export async function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>(
|
|
208
|
-
// workflow: WorkflowDef<Reg, I, R, any, O>,
|
|
209
|
-
// registry: Reg,
|
|
210
|
-
// input: I,
|
|
211
|
-
// observers: WorkflowObserver<Reg>[] = [],
|
|
212
|
-
// ): Promise<{ results: R; output: O; extras: Record<string, any> }> {
|
|
213
|
-
// const results: Record<string, any> = {};
|
|
214
|
-
// const extras: Record<string, any> = {};
|
|
215
|
-
// extras.frames = {} as Record<string, ExecutionFrame>;
|
|
216
|
-
//
|
|
217
|
-
// const stepById = new Map<string, StepDef<Reg, any, any>>(
|
|
218
|
-
// workflow.steps.map((s: StepDef<Reg, any, any>) => [s.id, s]),
|
|
219
|
-
// );
|
|
220
|
-
// const remainingDeps = new Map<string, number>();
|
|
221
|
-
// const dependents = new Map<string, string[]>();
|
|
222
|
-
// const ready: string[] = [];
|
|
223
|
-
//
|
|
224
|
-
// // Build dependency graph
|
|
225
|
-
// for (const step of workflow.steps) {
|
|
226
|
-
// remainingDeps.set(step.id, step.dependsOn.length);
|
|
227
|
-
// if (step.dependsOn.length === 0) ready.push(step.id);
|
|
228
|
-
//
|
|
229
|
-
// for (const dep of step.dependsOn) {
|
|
230
|
-
// if (!dependents.has(dep)) dependents.set(dep, []);
|
|
231
|
-
// dependents.get(dep)!.push(step.id);
|
|
232
|
-
// }
|
|
233
|
-
// }
|
|
234
|
-
//
|
|
235
|
-
// let completed = 0;
|
|
236
|
-
//
|
|
237
|
-
// while (ready.length > 0) {
|
|
238
|
-
// const batch = ready.splice(0);
|
|
239
|
-
//
|
|
240
|
-
// await Promise.all(
|
|
241
|
-
// batch.map(async (stepId) => {
|
|
242
|
-
// const step = stepById.get(stepId)!;
|
|
243
|
-
//
|
|
244
|
-
// const frame: ExecutionFrame = {
|
|
245
|
-
// stepId,
|
|
246
|
-
// attempts: 0,
|
|
247
|
-
// start: Date.now(),
|
|
248
|
-
// };
|
|
249
|
-
// extras.frames[stepId] = frame;
|
|
250
|
-
// const context = (workflow as any).__context;
|
|
251
|
-
// const ctx = {
|
|
252
|
-
// stepId,
|
|
253
|
-
// input,
|
|
254
|
-
// results,
|
|
255
|
-
// context,
|
|
256
|
-
// registry,
|
|
257
|
-
// extras,
|
|
258
|
-
// frame,
|
|
259
|
-
// };
|
|
260
|
-
//
|
|
261
|
-
// const core = async () => {
|
|
262
|
-
// frame.attempts++;
|
|
263
|
-
//
|
|
264
|
-
// const stepCtx = { input, results, context };
|
|
265
|
-
//
|
|
266
|
-
// if (step.when && !step.when(stepCtx)) {
|
|
267
|
-
// frame.output = undefined;
|
|
268
|
-
// frame.end = Date.now();
|
|
269
|
-
// frame.skipped = true;
|
|
270
|
-
// results[step.id] = undefined;
|
|
271
|
-
// return undefined;
|
|
272
|
-
// }
|
|
273
|
-
//
|
|
274
|
-
// // 👇 Get the resolved arguments (should be a tuple or undefined)
|
|
275
|
-
// // const resolvedArgs = step.resolve?.({ input, results, context });
|
|
276
|
-
// const resolvedArgs = step.resolve?.(stepCtx);
|
|
277
|
-
// frame.input = resolvedArgs;
|
|
278
|
-
//
|
|
279
|
-
// try {
|
|
280
|
-
// const action = registry[step.action];
|
|
281
|
-
//
|
|
282
|
-
// let result;
|
|
283
|
-
// if (resolvedArgs === undefined) {
|
|
284
|
-
// // No arguments - call with nothing
|
|
285
|
-
// result = await (action as () => Promise<any>)();
|
|
286
|
-
// } else if (Array.isArray(resolvedArgs)) {
|
|
287
|
-
// // Tuple arguments - spread as positional params
|
|
288
|
-
// result = await action(...resolvedArgs);
|
|
289
|
-
// } else {
|
|
290
|
-
// // Single object argument (backward compatibility)
|
|
291
|
-
// result = await action(resolvedArgs);
|
|
292
|
-
// }
|
|
293
|
-
//
|
|
294
|
-
// frame.output = result;
|
|
295
|
-
// frame.end = Date.now();
|
|
296
|
-
//
|
|
297
|
-
// results[step.id] = result;
|
|
298
|
-
// return result;
|
|
299
|
-
// } catch (err) {
|
|
300
|
-
// frame.error = err;
|
|
301
|
-
// frame.end = Date.now();
|
|
302
|
-
// throw err;
|
|
303
|
-
// }
|
|
304
|
-
// };
|
|
305
|
-
//
|
|
306
|
-
// const composed = composeObserver(observers, ctx, core);
|
|
307
|
-
// await composed();
|
|
308
|
-
//
|
|
309
|
-
// for (const childId of dependents.get(stepId) ?? []) {
|
|
310
|
-
// const remaining = remainingDeps.get(childId)! - 1;
|
|
311
|
-
// remainingDeps.set(childId, remaining);
|
|
312
|
-
// if (remaining === 0) ready.push(childId);
|
|
313
|
-
// }
|
|
314
|
-
//
|
|
315
|
-
// completed++;
|
|
316
|
-
// }),
|
|
317
|
-
// );
|
|
318
|
-
// }
|
|
319
|
-
//
|
|
320
|
-
// if (completed !== workflow.steps.length) {
|
|
321
|
-
// throw new Error("Workflow execution failed (cycle or missing dependency)");
|
|
322
|
-
// }
|
|
323
|
-
//
|
|
324
|
-
// // Resolve output
|
|
325
|
-
// const output: O = workflow.outputResolver
|
|
326
|
-
// ? workflow.outputResolver({ input, results: results as R })
|
|
327
|
-
// : (results as unknown as O);
|
|
328
|
-
//
|
|
329
|
-
// return { results: results as R, output, extras };
|
|
330
|
-
// }
|
|
331
|
-
/////////////////////// with for each check
|
|
332
|
-
// import { composeObserver } from "./observer.js";
|
|
333
|
-
// import { ActionRegistry, ExecutionFrame, WorkflowObserver } from "./types.js";
|
|
334
|
-
// import { StepDef, WorkflowDef } from "./workflow-composer.js";
|
|
335
|
-
//
|
|
336
|
-
// export async function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>(
|
|
337
|
-
// workflow: WorkflowDef<Reg, I, R, any, O>,
|
|
338
|
-
// registry: Reg,
|
|
339
|
-
// input: I,
|
|
340
|
-
// observers: WorkflowObserver<Reg>[] = [],
|
|
341
|
-
// ): Promise<{ results: R; output: O; extras: Record<string, any> }> {
|
|
342
|
-
// const results: Record<string, any> = {};
|
|
343
|
-
// const extras: Record<string, any> = {};
|
|
344
|
-
// extras.frames = {} as Record<string, ExecutionFrame>;
|
|
345
|
-
//
|
|
346
|
-
// const stepById = new Map<string, StepDef<Reg, any, any>>(
|
|
347
|
-
// workflow.steps.map((s: StepDef<Reg, any, any>) => [s.id, s]),
|
|
348
|
-
// );
|
|
349
|
-
// const remainingDeps = new Map<string, number>();
|
|
350
|
-
// const dependents = new Map<string, string[]>();
|
|
351
|
-
// const ready: string[] = [];
|
|
352
|
-
//
|
|
353
|
-
// // Build dependency graph
|
|
354
|
-
// for (const step of workflow.steps) {
|
|
355
|
-
// remainingDeps.set(step.id, step.dependsOn.length);
|
|
356
|
-
// if (step.dependsOn.length === 0) ready.push(step.id);
|
|
357
|
-
//
|
|
358
|
-
// for (const dep of step.dependsOn) {
|
|
359
|
-
// if (!dependents.has(dep)) dependents.set(dep, []);
|
|
360
|
-
// dependents.get(dep)!.push(step.id);
|
|
361
|
-
// }
|
|
362
|
-
// }
|
|
363
|
-
//
|
|
364
|
-
// let completed = 0;
|
|
365
|
-
//
|
|
366
|
-
// // Main execution loop
|
|
367
|
-
// while (ready.length > 0) {
|
|
368
|
-
// const batch = ready.splice(0);
|
|
369
|
-
//
|
|
370
|
-
// await Promise.all(
|
|
371
|
-
// batch.map(async (stepId) => {
|
|
372
|
-
// const step = stepById.get(stepId)!;
|
|
373
|
-
//
|
|
374
|
-
// const frame: ExecutionFrame = {
|
|
375
|
-
// stepId,
|
|
376
|
-
// attempts: 0,
|
|
377
|
-
// start: Date.now(),
|
|
378
|
-
// };
|
|
379
|
-
// extras.frames[stepId] = frame;
|
|
380
|
-
// const context = (workflow as any).__context;
|
|
381
|
-
// const ctx = {
|
|
382
|
-
// stepId,
|
|
383
|
-
// input,
|
|
384
|
-
// results,
|
|
385
|
-
// context,
|
|
386
|
-
// registry,
|
|
387
|
-
// extras,
|
|
388
|
-
// frame,
|
|
389
|
-
// };
|
|
390
|
-
//
|
|
391
|
-
// const core = async () => {
|
|
392
|
-
// frame.attempts++;
|
|
393
|
-
//
|
|
394
|
-
// const stepCtx = { input, results, context };
|
|
395
|
-
//
|
|
396
|
-
// // Conditional skip
|
|
397
|
-
// if (step.when && !step.when(stepCtx)) {
|
|
398
|
-
// frame.output = undefined;
|
|
399
|
-
// frame.end = Date.now();
|
|
400
|
-
// frame.skipped = true;
|
|
401
|
-
// results[step.id] = undefined;
|
|
402
|
-
// return undefined;
|
|
403
|
-
// }
|
|
404
|
-
//
|
|
405
|
-
// const resolvedArgs = step.resolve?.(stepCtx);
|
|
406
|
-
// frame.input = resolvedArgs;
|
|
407
|
-
//
|
|
408
|
-
// try {
|
|
409
|
-
// const action = registry[step.action];
|
|
410
|
-
// let result;
|
|
411
|
-
//
|
|
412
|
-
// if (resolvedArgs === undefined) {
|
|
413
|
-
// result = await (action as () => Promise<any>)();
|
|
414
|
-
// } else if (Array.isArray(resolvedArgs)) {
|
|
415
|
-
// result = await action(...resolvedArgs);
|
|
416
|
-
// } else {
|
|
417
|
-
// result = await action(resolvedArgs);
|
|
418
|
-
// }
|
|
419
|
-
//
|
|
420
|
-
// frame.output = result;
|
|
421
|
-
// frame.end = Date.now();
|
|
422
|
-
//
|
|
423
|
-
// results[step.id] = result;
|
|
424
|
-
// return result;
|
|
425
|
-
// } catch (err) {
|
|
426
|
-
// frame.error = err;
|
|
427
|
-
// frame.end = Date.now();
|
|
428
|
-
// throw err;
|
|
429
|
-
// }
|
|
430
|
-
// };
|
|
431
|
-
//
|
|
432
|
-
// const composed = composeObserver(observers, ctx, core);
|
|
433
|
-
// await composed();
|
|
434
|
-
//
|
|
435
|
-
// // Notify dependents
|
|
436
|
-
// for (const childId of dependents.get(stepId) ?? []) {
|
|
437
|
-
// const remaining = remainingDeps.get(childId)! - 1;
|
|
438
|
-
// remainingDeps.set(childId, remaining);
|
|
439
|
-
// if (remaining === 0) ready.push(childId);
|
|
440
|
-
// }
|
|
441
|
-
//
|
|
442
|
-
// completed++;
|
|
443
|
-
// }),
|
|
444
|
-
// );
|
|
445
|
-
// }
|
|
446
|
-
//
|
|
447
|
-
// if (completed !== workflow.steps.length) {
|
|
448
|
-
// throw new Error("Workflow execution failed (cycle or missing dependency)");
|
|
449
|
-
// }
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
// // Resolve workflow output
|
|
453
|
-
// const output: O = workflow.outputResolver
|
|
454
|
-
// ? workflow.outputResolver({ input, results: results as R })
|
|
455
|
-
// : (results as unknown as O);
|
|
456
|
-
//
|
|
457
|
-
// return { results: results as R, output, extras };
|
|
458
|
-
// }
|
|
459
|
-
// import { composeObserver } from "./observer.js";
|
|
460
|
-
// import { ActionRegistry, ExecutionFrame, WorkflowObserver } from "./types.js";
|
|
461
|
-
// import { StepDef, WorkflowDef } from "./workflow-composer.js";
|
|
462
|
-
//
|
|
463
|
-
// export async function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>(
|
|
464
|
-
// workflow: WorkflowDef<Reg, I, R, any, O>,
|
|
465
|
-
// registry: Reg,
|
|
466
|
-
// input: I,
|
|
467
|
-
// observers: WorkflowObserver<Reg>[] = [],
|
|
468
|
-
// ): Promise<{ results: R; output: O; extras: Record<string, any> }> {
|
|
469
|
-
// const results: Record<string, any> = {};
|
|
470
|
-
// const extras: Record<string, any> = {};
|
|
471
|
-
// extras.frames = {} as Record<string, ExecutionFrame>;
|
|
472
|
-
//
|
|
473
|
-
// const stepById = new Map<string, StepDef<Reg, any, any>>(
|
|
474
|
-
// workflow.steps.map((s: StepDef<Reg, any, any>) => [s.id, s]),
|
|
475
|
-
// );
|
|
476
|
-
// const remainingDeps = new Map<string, number>();
|
|
477
|
-
// const dependents = new Map<string, string[]>();
|
|
478
|
-
// const ready: string[] = [];
|
|
479
|
-
//
|
|
480
|
-
// // Build dependency graph
|
|
481
|
-
// for (const step of workflow.steps) {
|
|
482
|
-
// remainingDeps.set(step.id, step.dependsOn.length);
|
|
483
|
-
// if (step.dependsOn.length === 0) ready.push(step.id);
|
|
484
|
-
//
|
|
485
|
-
// for (const dep of step.dependsOn) {
|
|
486
|
-
// if (!dependents.has(dep)) dependents.set(dep, []);
|
|
487
|
-
// dependents.get(dep)!.push(step.id);
|
|
488
|
-
// }
|
|
489
|
-
// }
|
|
490
|
-
//
|
|
491
|
-
// let completed = 0;
|
|
492
|
-
//
|
|
493
|
-
// // Main execution loop
|
|
494
|
-
// while (ready.length > 0) {
|
|
495
|
-
// const batch = ready.splice(0);
|
|
496
|
-
//
|
|
497
|
-
// await Promise.all(
|
|
498
|
-
// batch.map(async (stepId) => {
|
|
499
|
-
// const step = stepById.get(stepId)!;
|
|
500
|
-
//
|
|
501
|
-
// const frame: ExecutionFrame = {
|
|
502
|
-
// stepId,
|
|
503
|
-
// attempts: 0,
|
|
504
|
-
// start: Date.now(),
|
|
505
|
-
// };
|
|
506
|
-
// extras.frames[stepId] = frame;
|
|
507
|
-
// const context = (workflow as any).__context;
|
|
508
|
-
// const ctx = {
|
|
509
|
-
// stepId,
|
|
510
|
-
// input,
|
|
511
|
-
// results,
|
|
512
|
-
// context,
|
|
513
|
-
// registry,
|
|
514
|
-
// extras,
|
|
515
|
-
// frame,
|
|
516
|
-
// };
|
|
517
|
-
//
|
|
518
|
-
// const core = async () => {
|
|
519
|
-
// frame.attempts++;
|
|
520
|
-
//
|
|
521
|
-
// const stepCtx = { input, results, context };
|
|
522
|
-
//
|
|
523
|
-
// // Conditional skip
|
|
524
|
-
// if (step.when && !step.when(stepCtx)) {
|
|
525
|
-
// frame.output = undefined;
|
|
526
|
-
// frame.end = Date.now();
|
|
527
|
-
// frame.skipped = true;
|
|
528
|
-
// results[step.id] = undefined;
|
|
529
|
-
// return undefined;
|
|
530
|
-
// }
|
|
531
|
-
//
|
|
532
|
-
// const resolvedArgs = step.resolve?.(stepCtx);
|
|
533
|
-
// frame.input = resolvedArgs;
|
|
534
|
-
//
|
|
535
|
-
// try {
|
|
536
|
-
// const action = registry[step.action];
|
|
537
|
-
// let result;
|
|
538
|
-
//
|
|
539
|
-
// // Handle loop flag
|
|
540
|
-
//
|
|
541
|
-
// // DEBUG: Log what's happening
|
|
542
|
-
// if (step.loop) {
|
|
543
|
-
// if (!Array.isArray(resolvedArgs)) {
|
|
544
|
-
// throw new Error(
|
|
545
|
-
// `Step "${step.id}" with loop=true must return an array`,
|
|
546
|
-
// );
|
|
547
|
-
// }
|
|
548
|
-
//
|
|
549
|
-
// result = await Promise.all(
|
|
550
|
-
// resolvedArgs.map(async (item) => {
|
|
551
|
-
// if (Array.isArray(item)) {
|
|
552
|
-
// return await action(...item);
|
|
553
|
-
// } else {
|
|
554
|
-
// return await action(item);
|
|
555
|
-
// }
|
|
556
|
-
// }),
|
|
557
|
-
// );
|
|
558
|
-
// // if (
|
|
559
|
-
// // resolvedArgs &&
|
|
560
|
-
// // resolvedArgs[0] &&
|
|
561
|
-
// // Array.isArray(resolvedArgs[0])
|
|
562
|
-
// // ) {
|
|
563
|
-
// // const items = resolvedArgs[0];
|
|
564
|
-
// //
|
|
565
|
-
// // result = await Promise.all(
|
|
566
|
-
// // items.map(async (item, idx) => {
|
|
567
|
-
// // return await action(item);
|
|
568
|
-
// // }),
|
|
569
|
-
// // );
|
|
570
|
-
// // } else {
|
|
571
|
-
// // if (resolvedArgs === undefined) {
|
|
572
|
-
// // result = await (action as () => Promise<any>)();
|
|
573
|
-
// // } else if (Array.isArray(resolvedArgs)) {
|
|
574
|
-
// // result = await action(...resolvedArgs);
|
|
575
|
-
// // } else {
|
|
576
|
-
// // result = await action(resolvedArgs);
|
|
577
|
-
// // }
|
|
578
|
-
// // }
|
|
579
|
-
// } else {
|
|
580
|
-
// // Normal execution
|
|
581
|
-
// if (resolvedArgs === undefined) {
|
|
582
|
-
// result = await (action as () => Promise<any>)();
|
|
583
|
-
// } else if (Array.isArray(resolvedArgs)) {
|
|
584
|
-
// result = await action(...resolvedArgs);
|
|
585
|
-
// } else {
|
|
586
|
-
// result = await action(resolvedArgs);
|
|
587
|
-
// }
|
|
588
|
-
// }
|
|
589
|
-
//
|
|
590
|
-
// frame.output = result;
|
|
591
|
-
// frame.end = Date.now();
|
|
592
|
-
//
|
|
593
|
-
// results[step.id] = result;
|
|
594
|
-
// return result;
|
|
595
|
-
// } catch (err) {
|
|
596
|
-
// frame.error = err;
|
|
597
|
-
// frame.end = Date.now();
|
|
598
|
-
// throw err;
|
|
599
|
-
// }
|
|
600
|
-
// };
|
|
601
|
-
//
|
|
602
|
-
// const composed = composeObserver(observers, ctx, core);
|
|
603
|
-
// await composed();
|
|
604
|
-
//
|
|
605
|
-
// // Notify dependents
|
|
606
|
-
// for (const childId of dependents.get(stepId) ?? []) {
|
|
607
|
-
// const remaining = remainingDeps.get(childId)! - 1;
|
|
608
|
-
// remainingDeps.set(childId, remaining);
|
|
609
|
-
// if (remaining === 0) ready.push(childId);
|
|
610
|
-
// }
|
|
611
|
-
//
|
|
612
|
-
// completed++;
|
|
613
|
-
// }),
|
|
614
|
-
// );
|
|
615
|
-
// }
|
|
616
|
-
//
|
|
617
|
-
// if (completed !== workflow.steps.length) {
|
|
618
|
-
// throw new Error("Workflow execution failed (cycle or missing dependency)");
|
|
619
|
-
// }
|
|
620
|
-
//
|
|
621
|
-
// // Resolve workflow output
|
|
622
|
-
// const output: O = workflow.outputResolver
|
|
623
|
-
// ? workflow.outputResolver({ input, results: results as R })
|
|
624
|
-
// : (results as unknown as O);
|
|
625
|
-
//
|
|
626
|
-
// return { results: results as R, output, extras };
|
|
627
|
-
// }
|
|
628
|
-
// ///////////////////////////////////////////////////////////////
|
|
629
|
-
//
|
|
630
|
-
//
|
|
631
1
|
import { composeObserver } from "./observer.js";
|
|
2
|
+
function createCallHelpers() {
|
|
3
|
+
return {
|
|
4
|
+
args: (...args) => ({ kind: "positional", args }),
|
|
5
|
+
obj: (arg) => ({ kind: "object", args: arg }),
|
|
6
|
+
none: () => ({ kind: "none" }),
|
|
7
|
+
loop: (items) => ({ kind: "loop", items }),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
632
10
|
export async function executeWorkflow(workflow, registry, input, observers = []) {
|
|
633
11
|
const results = {};
|
|
634
12
|
const extras = {};
|
|
@@ -695,7 +73,7 @@ export async function executeWorkflow(workflow, registry, input, observers = [])
|
|
|
695
73
|
};
|
|
696
74
|
const core = async () => {
|
|
697
75
|
frame.attempts++;
|
|
698
|
-
const stepCtx = { input, results, context };
|
|
76
|
+
const stepCtx = { input, results, context, ...createCallHelpers() };
|
|
699
77
|
// Conditional skip
|
|
700
78
|
if (step.when && !step.when(stepCtx)) {
|
|
701
79
|
frame.output = undefined;
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { ActionRegistry } from "./types.js";
|
|
2
|
-
import { createWorkflow,
|
|
2
|
+
import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
|
|
3
3
|
type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
|
|
4
4
|
type ModuleShape = Record<string, AnyWorkflow>;
|
|
5
5
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
6
6
|
type UnionToIntersectionOrEmpty<U> = [U] extends [never] ? {} : UnionToIntersection<U>;
|
|
7
7
|
export type ModuleContext<Reg extends ActionRegistry, Context extends Record<string, any>, Deps extends ModuleShape> = {
|
|
8
8
|
wf: ReturnType<typeof createWorkflow<Reg, Context>>;
|
|
9
|
-
args: typeof WorkflowBuilder.args;
|
|
10
|
-
obj: typeof WorkflowBuilder.obj;
|
|
11
|
-
loop: typeof WorkflowBuilder.loop;
|
|
12
|
-
none: typeof WorkflowBuilder.none;
|
|
13
9
|
deps: Deps;
|
|
14
10
|
context: Context;
|
|
15
11
|
tools: <T>(factory: (ctx: {
|
package/dist/workflow-module.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createWorkflow,
|
|
1
|
+
import { createWorkflow, } from "./workflow-composer.js";
|
|
2
2
|
export function createModule({ registry, context, use, define, }) {
|
|
3
3
|
const wf = createWorkflow(registry, context);
|
|
4
4
|
const deps = (use ? Object.assign({}, ...use) : {});
|
|
@@ -6,10 +6,6 @@ export function createModule({ registry, context, use, define, }) {
|
|
|
6
6
|
wf,
|
|
7
7
|
deps,
|
|
8
8
|
context,
|
|
9
|
-
args: WorkflowBuilder.args,
|
|
10
|
-
obj: WorkflowBuilder.obj,
|
|
11
|
-
loop: WorkflowBuilder.loop,
|
|
12
|
-
none: WorkflowBuilder.none,
|
|
13
9
|
tools: (factory) => factory({ wf, deps, context }),
|
|
14
10
|
};
|
|
15
11
|
const own = define(moduleCtx);
|