@pogodisco/zephyr 1.3.4 → 1.3.5
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/observer.d.ts +4 -0
- package/dist/observer.js +69 -0
- package/dist/session.d.ts +3 -3
- package/dist/session.js +4 -4
- package/dist/types.d.ts +1 -1
- package/dist/types.js +0 -46
- package/dist/workflow-composer.d.ts +37 -23
- package/dist/workflow-composer.js +44 -566
- package/dist/workflow-executor.d.ts +3 -3
- package/dist/workflow-executor.js +463 -21
- package/dist/workflow-module.d.ts +16 -7
- package/dist/workflow-module.js +12 -38
- package/package.json +1 -1
|
@@ -200,16 +200,446 @@
|
|
|
200
200
|
//
|
|
201
201
|
//
|
|
202
202
|
///// with any fn shape
|
|
203
|
-
import {
|
|
204
|
-
|
|
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
|
+
import { composeObserver } from "./observer.js";
|
|
632
|
+
export async function executeWorkflow(workflow, registry, input, observers = []) {
|
|
205
633
|
const results = {};
|
|
206
634
|
const extras = {};
|
|
207
635
|
extras.frames = {};
|
|
636
|
+
// -----------------------------
|
|
637
|
+
// Fully typed step map
|
|
638
|
+
// -----------------------------
|
|
208
639
|
const stepById = new Map(workflow.steps.map((s) => [s.id, s]));
|
|
209
640
|
const remainingDeps = new Map();
|
|
210
641
|
const dependents = new Map();
|
|
211
642
|
const ready = [];
|
|
212
|
-
// Build dependency graph
|
|
213
643
|
for (const step of workflow.steps) {
|
|
214
644
|
remainingDeps.set(step.id, step.dependsOn.length);
|
|
215
645
|
if (step.dependsOn.length === 0)
|
|
@@ -221,6 +651,28 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
|
|
|
221
651
|
}
|
|
222
652
|
}
|
|
223
653
|
let completed = 0;
|
|
654
|
+
// -----------------------------
|
|
655
|
+
// Normalized runner
|
|
656
|
+
// -----------------------------
|
|
657
|
+
const runAction = async (input, action) => {
|
|
658
|
+
if (!input)
|
|
659
|
+
return await action();
|
|
660
|
+
switch (input.kind) {
|
|
661
|
+
case "none":
|
|
662
|
+
return await action();
|
|
663
|
+
case "positional":
|
|
664
|
+
return await action(...input.args);
|
|
665
|
+
case "object":
|
|
666
|
+
return await action(input.args);
|
|
667
|
+
case "loop":
|
|
668
|
+
return await Promise.all(input.items.map((item) => runAction(item, action)));
|
|
669
|
+
default:
|
|
670
|
+
throw new Error(`Unknown ResolvedStepInput kind: ${input.kind}`);
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
// -----------------------------
|
|
674
|
+
// Execution loop
|
|
675
|
+
// -----------------------------
|
|
224
676
|
while (ready.length > 0) {
|
|
225
677
|
const batch = ready.splice(0);
|
|
226
678
|
await Promise.all(batch.map(async (stepId) => {
|
|
@@ -244,6 +696,7 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
|
|
|
244
696
|
const core = async () => {
|
|
245
697
|
frame.attempts++;
|
|
246
698
|
const stepCtx = { input, results, context };
|
|
699
|
+
// Conditional skip
|
|
247
700
|
if (step.when && !step.when(stepCtx)) {
|
|
248
701
|
frame.output = undefined;
|
|
249
702
|
frame.end = Date.now();
|
|
@@ -251,25 +704,11 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
|
|
|
251
704
|
results[step.id] = undefined;
|
|
252
705
|
return undefined;
|
|
253
706
|
}
|
|
254
|
-
// 👇 Get the resolved arguments (should be a tuple or undefined)
|
|
255
|
-
// const resolvedArgs = step.resolve?.({ input, results, context });
|
|
256
707
|
const resolvedArgs = step.resolve?.(stepCtx);
|
|
257
708
|
frame.input = resolvedArgs;
|
|
258
709
|
try {
|
|
259
710
|
const action = registry[step.action];
|
|
260
|
-
|
|
261
|
-
if (resolvedArgs === undefined) {
|
|
262
|
-
// No arguments - call with nothing
|
|
263
|
-
result = await action();
|
|
264
|
-
}
|
|
265
|
-
else if (Array.isArray(resolvedArgs)) {
|
|
266
|
-
// Tuple arguments - spread as positional params
|
|
267
|
-
result = await action(...resolvedArgs);
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
// Single object argument (backward compatibility)
|
|
271
|
-
result = await action(resolvedArgs);
|
|
272
|
-
}
|
|
711
|
+
const result = await runAction(resolvedArgs, action);
|
|
273
712
|
frame.output = result;
|
|
274
713
|
frame.end = Date.now();
|
|
275
714
|
results[step.id] = result;
|
|
@@ -281,7 +720,7 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
|
|
|
281
720
|
throw err;
|
|
282
721
|
}
|
|
283
722
|
};
|
|
284
|
-
const composed =
|
|
723
|
+
const composed = composeObserver(observers, ctx, core);
|
|
285
724
|
await composed();
|
|
286
725
|
for (const childId of dependents.get(stepId) ?? []) {
|
|
287
726
|
const remaining = remainingDeps.get(childId) - 1;
|
|
@@ -295,9 +734,12 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
|
|
|
295
734
|
if (completed !== workflow.steps.length) {
|
|
296
735
|
throw new Error("Workflow execution failed (cycle or missing dependency)");
|
|
297
736
|
}
|
|
298
|
-
// Resolve output
|
|
299
737
|
const output = workflow.outputResolver
|
|
300
|
-
? workflow.outputResolver({
|
|
738
|
+
? workflow.outputResolver({
|
|
739
|
+
input,
|
|
740
|
+
results: results,
|
|
741
|
+
context: workflow.__context,
|
|
742
|
+
})
|
|
301
743
|
: results;
|
|
302
744
|
return { results: results, output, extras };
|
|
303
745
|
}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import { ActionRegistry } from "./types.js";
|
|
2
|
-
import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
|
|
3
|
-
export declare function workflowModule<Reg extends ActionRegistry, Context extends Record<string, any>, Flows extends Record<string, any>>(registry: Reg, context: Context, factory: (wf: ReturnType<typeof createWorkflow<Reg, Context>>) => Flows): {
|
|
4
|
-
wf: <Input = unknown>(name: string) => import("./workflow-composer.js").WorkflowBuilder<Reg, Input, Context, [], {}, undefined>;
|
|
5
|
-
} & Flows & {
|
|
6
|
-
extend<Extra extends Record<string, any>>(overrides: Extra): Flows & Extra;
|
|
7
|
-
};
|
|
2
|
+
import { createWorkflow, WorkflowBuilder, WorkflowDef } from "./workflow-composer.js";
|
|
8
3
|
type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
|
|
9
4
|
type ModuleShape = Record<string, AnyWorkflow>;
|
|
10
5
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
11
6
|
type UnionToIntersectionOrEmpty<U> = [U] extends [never] ? {} : UnionToIntersection<U>;
|
|
7
|
+
export type ModuleContext<Reg extends ActionRegistry, Context extends Record<string, any>, Deps extends ModuleShape> = {
|
|
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
|
+
deps: Deps;
|
|
14
|
+
context: Context;
|
|
15
|
+
tools: <T>(factory: (ctx: {
|
|
16
|
+
wf: ModuleContext<Reg, Context, Deps>["wf"];
|
|
17
|
+
deps: Deps;
|
|
18
|
+
context: Context;
|
|
19
|
+
}) => T) => T;
|
|
20
|
+
};
|
|
12
21
|
export declare function createModule<Reg extends ActionRegistry, Context extends Record<string, any>, Use extends ModuleShape[] = [], Deps extends ModuleShape = UnionToIntersectionOrEmpty<Use[number]> & ModuleShape, Own extends ModuleShape = {}>({ registry, context, use, define, }: {
|
|
13
22
|
registry: Reg;
|
|
14
23
|
context: Context;
|
|
15
24
|
use?: [...Use];
|
|
16
|
-
define: (
|
|
25
|
+
define: (ctx: ModuleContext<Reg, Context, Deps>) => Own;
|
|
17
26
|
}): Deps & Own;
|
|
18
27
|
export {};
|
package/dist/workflow-module.js
CHANGED
|
@@ -1,44 +1,18 @@
|
|
|
1
|
-
import { createWorkflow } from "./workflow-composer.js";
|
|
2
|
-
// export function workflowModule<
|
|
3
|
-
// Reg extends ActionRegistry,
|
|
4
|
-
// Context extends Record<string, any>,
|
|
5
|
-
// Flows extends Record<string, any>,
|
|
6
|
-
// >(
|
|
7
|
-
// registry: Reg,
|
|
8
|
-
// context: Context,
|
|
9
|
-
// factory: (wf: ReturnType<typeof createWorkflow<Reg, Context>>) => Flows,
|
|
10
|
-
// ) {
|
|
11
|
-
// const wf = createWorkflow(registry, context);
|
|
12
|
-
// const flows = factory(wf);
|
|
13
|
-
//
|
|
14
|
-
// return {
|
|
15
|
-
// ...flows,
|
|
16
|
-
// extend(overrides: Partial<Flows>): Flows {
|
|
17
|
-
// return {
|
|
18
|
-
// ...flows,
|
|
19
|
-
// ...overrides,
|
|
20
|
-
// };
|
|
21
|
-
// },
|
|
22
|
-
// };
|
|
23
|
-
// }
|
|
24
|
-
export function workflowModule(registry, context, factory) {
|
|
25
|
-
const wf = createWorkflow(registry, context);
|
|
26
|
-
const flows = factory(wf);
|
|
27
|
-
return {
|
|
28
|
-
wf,
|
|
29
|
-
...flows,
|
|
30
|
-
extend(overrides) {
|
|
31
|
-
return {
|
|
32
|
-
...flows,
|
|
33
|
-
...overrides,
|
|
34
|
-
};
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
1
|
+
import { createWorkflow, WorkflowBuilder, } from "./workflow-composer.js";
|
|
38
2
|
export function createModule({ registry, context, use, define, }) {
|
|
39
3
|
const wf = createWorkflow(registry, context);
|
|
40
4
|
const deps = (use ? Object.assign({}, ...use) : {});
|
|
41
|
-
const
|
|
5
|
+
const moduleCtx = {
|
|
6
|
+
wf,
|
|
7
|
+
deps,
|
|
8
|
+
context,
|
|
9
|
+
args: WorkflowBuilder.args,
|
|
10
|
+
obj: WorkflowBuilder.obj,
|
|
11
|
+
loop: WorkflowBuilder.loop,
|
|
12
|
+
none: WorkflowBuilder.none,
|
|
13
|
+
tools: (factory) => factory({ wf, deps, context }),
|
|
14
|
+
};
|
|
15
|
+
const own = define(moduleCtx);
|
|
42
16
|
return {
|
|
43
17
|
...deps,
|
|
44
18
|
...own,
|