@pogodisco/zephyr 1.3.20 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types.d.ts +1 -1
- package/dist/workflow-composer.d.ts +19 -22
- package/dist/workflow-composer.js +12 -27
- package/dist/workflow-executor.d.ts +21 -4
- package/dist/workflow-executor.js +262 -6
- package/dist/workflow-module.d.ts +13 -32
- package/dist/workflow-module.js +14 -17
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export type WorkflowObserver<Reg extends ActionRegistry = any> = {
|
|
|
21
21
|
stepId: string;
|
|
22
22
|
input: any;
|
|
23
23
|
results: Record<string, any>;
|
|
24
|
-
|
|
24
|
+
actionRegistry: Reg;
|
|
25
25
|
extras: Record<string, any>;
|
|
26
26
|
frame: ExecutionFrame;
|
|
27
27
|
}, next: () => Promise<any>): Promise<any>;
|
|
@@ -35,10 +35,8 @@ type CallHelpers<Reg extends ActionRegistry, ActionName extends keyof Reg> = {
|
|
|
35
35
|
items: typeof items;
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
|
-
type SubflowResult<SubResults, SubOutput> = SubOutput extends undefined ? SubResults : SubOutput;
|
|
39
38
|
type WorkflowInput<T> = T extends WorkflowDef<any, infer I, any, any, any> ? I : never;
|
|
40
|
-
type
|
|
41
|
-
type WorkflowOutput<T> = T extends WorkflowDef<any, any, any, any, infer O> ? O : never;
|
|
39
|
+
type WorkflowOutput<T> = T extends WorkflowDef<any, any, any, any, infer O> ? unknown extends O ? undefined : O : undefined;
|
|
42
40
|
export type StepResultFromResolve<Reg extends ActionRegistry, ActionName extends keyof Reg, R extends ResolvedStepInput> = R extends {
|
|
43
41
|
kind: "loop";
|
|
44
42
|
} ? ActionReturn<Reg, ActionName>[] : ActionReturn<Reg, ActionName>;
|
|
@@ -49,6 +47,7 @@ export type StepDef<Reg extends ActionRegistry, ID extends string = string, Acti
|
|
|
49
47
|
resolve: (ctx: any) => ResolvedStepInput;
|
|
50
48
|
when?: (ctx: any) => boolean;
|
|
51
49
|
options?: StepOptions<any, any, any>;
|
|
50
|
+
__subflowId?: string;
|
|
52
51
|
};
|
|
53
52
|
export type WorkflowDef<Reg extends ActionRegistry, Input, Results, Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[], Output = undefined> = {
|
|
54
53
|
name: string;
|
|
@@ -73,12 +72,13 @@ type StepOptions<Input, Results, Context> = {
|
|
|
73
72
|
label?: string;
|
|
74
73
|
meta?: Record<string, any>;
|
|
75
74
|
};
|
|
76
|
-
type MergeBranchResults<Branches extends readonly any[], Acc> = Branches extends readonly [infer Head, ...infer Tail] ? MergeBranchResults<Tail, Acc & (Head extends WorkflowBuilder<any, any, any, any, infer R> ? R : {})> : Acc;
|
|
75
|
+
type MergeBranchResults<Branches extends readonly any[], Acc> = Branches extends readonly [infer Head, ...infer Tail] ? MergeBranchResults<Tail, Acc & (Head extends WorkflowBuilder<any, any, any, any, any, infer R> ? R : {})> : Acc;
|
|
77
76
|
type MergeBranchSteps<Branches extends readonly any[], Acc extends any[]> = Branches extends readonly [infer Head, ...infer Tail] ? MergeBranchSteps<Tail, [
|
|
78
77
|
...Acc,
|
|
79
|
-
...(Head extends WorkflowBuilder<any, any, any, infer S, any> ? S : [])
|
|
78
|
+
...(Head extends WorkflowBuilder<any, any, any, any, infer S, any> ? S : [])
|
|
80
79
|
]> : Acc;
|
|
81
|
-
export declare class WorkflowBuilder<Reg extends ActionRegistry,
|
|
80
|
+
export declare class WorkflowBuilder<Reg extends ActionRegistry, WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, //
|
|
81
|
+
Input = unknown, Context = unknown, Steps extends StepDef<Reg, any, any>[] = [], Results = {}, Output = undefined> {
|
|
82
82
|
private name;
|
|
83
83
|
private steps;
|
|
84
84
|
private frontier;
|
|
@@ -90,7 +90,7 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
|
|
|
90
90
|
input: Input;
|
|
91
91
|
results: Results;
|
|
92
92
|
context: Context;
|
|
93
|
-
} & CallHelpers<Reg, ActionName>) => ResolveOut, dependsOn?: string[], options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, Input, Context, [
|
|
93
|
+
} & CallHelpers<Reg, ActionName>) => ResolveOut, dependsOn?: string[], options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, WFReg, Input, Context, [
|
|
94
94
|
...Steps,
|
|
95
95
|
StepDef<Reg, ID, ActionName>
|
|
96
96
|
], Simplify<Results & {
|
|
@@ -100,34 +100,30 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
|
|
|
100
100
|
input: Input;
|
|
101
101
|
results: Results;
|
|
102
102
|
context: Context;
|
|
103
|
-
} & CallHelpers<Reg, ActionName>) => ResolveOut, options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K_1 in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut>; } extends infer T ? { [K in keyof T]: T[K]; } : never, undefined>;
|
|
104
|
-
as<NewType>(): WorkflowBuilder<Reg, Input, Context, Steps, Steps extends [...infer Rest, infer Last] ? Last extends StepDef<Reg, infer ID, any> ? Simplify<Omit<Results, ID> & {
|
|
103
|
+
} & CallHelpers<Reg, ActionName>) => ResolveOut, options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, WFReg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K_1 in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut>; } extends infer T ? { [K in keyof T]: T[K]; } : never, undefined>;
|
|
104
|
+
as<NewType>(): WorkflowBuilder<Reg, WFReg, Input, Context, Steps, Steps extends [...infer Rest, infer Last] ? Last extends StepDef<Reg, infer ID, any> ? Simplify<Omit<Results, ID> & {
|
|
105
105
|
[K in ID]: NewType;
|
|
106
106
|
}> : Results : Results, Output>;
|
|
107
|
-
parallel<Branches extends readonly WorkflowBuilder<Reg, Input, Context, any, any>[]>(...branches: {
|
|
108
|
-
[K in keyof Branches]: (builder: WorkflowBuilder<Reg, Input, Context, [], Results>) => Branches[K];
|
|
109
|
-
}): WorkflowBuilder<Reg, Input, Context, MergeBranchSteps<Branches, Steps>, Simplify<MergeBranchResults<Branches, Results>>>;
|
|
107
|
+
parallel<Branches extends readonly WorkflowBuilder<Reg, WFReg, Input, Context, any, any>[]>(...branches: {
|
|
108
|
+
[K in keyof Branches]: (builder: WorkflowBuilder<Reg, WFReg, Input, Context, [], Results>) => Branches[K];
|
|
109
|
+
}): WorkflowBuilder<Reg, WFReg, Input, Context, MergeBranchSteps<Branches, Steps>, Simplify<MergeBranchResults<Branches, Results>>>;
|
|
110
110
|
join<ID extends string = string, ActionName extends keyof Reg & string = any, ResolveOut extends ResolvedStepInput = ResolvedStepInput>(id: ID, action: ActionName, resolve?: (ctx: {
|
|
111
111
|
input: Input;
|
|
112
112
|
results: Results;
|
|
113
113
|
context: Context;
|
|
114
|
-
} & CallHelpers<Reg, ActionName>) => ResolveOut, options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K_1 in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut>; } extends infer T ? { [K in keyof T]: T[K]; } : never, undefined>;
|
|
115
|
-
subflow<Prefix extends string,
|
|
114
|
+
} & CallHelpers<Reg, ActionName>) => ResolveOut, options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, WFReg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K_1 in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut>; } extends infer T ? { [K in keyof T]: T[K]; } : never, undefined>;
|
|
115
|
+
subflow<Prefix extends string, K extends keyof WFReg & string>(prefix: Prefix, workflowKey: K, resolveInput: (ctx: {
|
|
116
116
|
input: Input;
|
|
117
117
|
results: Results;
|
|
118
118
|
context: Context;
|
|
119
|
-
}) => WorkflowInput<
|
|
120
|
-
|
|
121
|
-
results: Results;
|
|
122
|
-
context: Context;
|
|
123
|
-
}) => WorkflowInput<WF>, options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, Input, Context, Steps, Results & {
|
|
124
|
-
[K in Prefix]: SubflowResult<WorkflowResults<WF>, WorkflowOutput<WF>>;
|
|
119
|
+
}) => WorkflowInput<WFReg[K]>, options?: StepOptions<Input, Results, Context>): WorkflowBuilder<Reg, WFReg, Input, Context, Steps, Results & {
|
|
120
|
+
[P in Prefix]: WorkflowOutput<WFReg[K]>;
|
|
125
121
|
}>;
|
|
126
122
|
when(predicate: (ctx: {
|
|
127
123
|
input: Input;
|
|
128
124
|
results: Results;
|
|
129
125
|
context: Context;
|
|
130
|
-
}) => boolean): WorkflowBuilder<Reg, Input, Context, Steps, Results, Output>;
|
|
126
|
+
}) => boolean): WorkflowBuilder<Reg, WFReg, Input, Context, Steps, Results, Output>;
|
|
131
127
|
endWhen(): this;
|
|
132
128
|
output<Output>(fn: (ctx: {
|
|
133
129
|
input: Input;
|
|
@@ -138,5 +134,6 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
|
|
|
138
134
|
private validateDependencies;
|
|
139
135
|
private getEndSteps;
|
|
140
136
|
}
|
|
141
|
-
export declare function createWorkflow<Reg extends ActionRegistry,
|
|
137
|
+
export declare function createWorkflow<Reg extends ActionRegistry, WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, // 👈 NEW
|
|
138
|
+
Context extends Record<string, any> = {}>(): <Input = unknown>(name: string) => WorkflowBuilder<Reg, WFReg, Input, Context, [], {}, undefined>;
|
|
142
139
|
export {};
|
|
@@ -562,34 +562,19 @@ export class WorkflowBuilder {
|
|
|
562
562
|
this.clearPendingWhen();
|
|
563
563
|
return result;
|
|
564
564
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
id: idMap.get(step.id),
|
|
577
|
-
dependsOn: step.dependsOn.map((d) => idMap.get(d)),
|
|
578
|
-
resolve: (ctx) => {
|
|
579
|
-
const subInput = resolveInput ? resolveInput(ctx) : undefined;
|
|
580
|
-
return step.resolve({
|
|
581
|
-
input: subInput,
|
|
582
|
-
results: ctx.results,
|
|
583
|
-
context: ctx.context,
|
|
584
|
-
});
|
|
585
|
-
},
|
|
586
|
-
};
|
|
587
|
-
if (workflow.entrySteps.find((e) => e.id === step.id)) {
|
|
588
|
-
newStep.dependsOn = [...this.frontier];
|
|
589
|
-
}
|
|
590
|
-
this.steps.push(newStep);
|
|
565
|
+
subflow(prefix, workflowKey, resolveInput, options) {
|
|
566
|
+
this.steps.push({
|
|
567
|
+
id: prefix,
|
|
568
|
+
action: "__subflow__",
|
|
569
|
+
dependsOn: [...this.frontier],
|
|
570
|
+
resolve: (ctx) => ({
|
|
571
|
+
kind: "object",
|
|
572
|
+
args: resolveInput(ctx),
|
|
573
|
+
}),
|
|
574
|
+
options,
|
|
575
|
+
__subflowId: workflowKey, // 👈 STRING ONLY
|
|
591
576
|
});
|
|
592
|
-
this.frontier =
|
|
577
|
+
this.frontier = [prefix];
|
|
593
578
|
return this;
|
|
594
579
|
}
|
|
595
580
|
/* ------------------------------------------------ */
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { ActionRegistry, WorkflowObserver } from "./types.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { WorkflowDef } from "./workflow-composer.js";
|
|
3
|
+
import { WorkflowInput, WorkflowOutput } from "./workflow-module.js";
|
|
4
|
+
export declare function createCallHelpers(): {
|
|
5
|
+
args: (...args: any[]) => {
|
|
6
|
+
kind: string;
|
|
7
|
+
args: any[];
|
|
8
|
+
};
|
|
9
|
+
obj: (arg: any) => {
|
|
10
|
+
kind: string;
|
|
11
|
+
args: any;
|
|
12
|
+
};
|
|
13
|
+
none: () => {
|
|
14
|
+
kind: string;
|
|
15
|
+
};
|
|
16
|
+
loop: (items: any[]) => {
|
|
17
|
+
kind: string;
|
|
18
|
+
items: any[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare function executeWorkflow<Reg extends ActionRegistry, WR extends Record<string, WorkflowDef<any, any, any, any, any>>, K extends keyof WR>(workflowId: K, workflowRegistry: WR, actionRegistry: Reg, input: WorkflowInput<WR[K]>, context: any, observers?: WorkflowObserver<Reg>[]): Promise<{
|
|
22
|
+
output: WorkflowOutput<WR[K]>;
|
|
6
23
|
extras: Record<string, any>;
|
|
7
24
|
}>;
|
|
@@ -1,5 +1,236 @@
|
|
|
1
|
+
// import { composeObserver } from "./observer.js";
|
|
2
|
+
// import { ActionRegistry, ExecutionFrame, WorkflowObserver } from "./types.js";
|
|
3
|
+
// import {
|
|
4
|
+
// StepDef,
|
|
5
|
+
// WorkflowDef,
|
|
6
|
+
// ResolvedStepInput,
|
|
7
|
+
// } from "./workflow-composer.js";
|
|
8
|
+
//
|
|
9
|
+
// export function createCallHelpers() {
|
|
10
|
+
// return {
|
|
11
|
+
// args: (...args: any[]) => ({ kind: "positional", args }),
|
|
12
|
+
// obj: (arg: any) => ({ kind: "object", args: arg }),
|
|
13
|
+
// none: () => ({ kind: "none" }),
|
|
14
|
+
// loop: (items: any[]) => ({ kind: "loop", items }),
|
|
15
|
+
// };
|
|
16
|
+
// }
|
|
17
|
+
//
|
|
18
|
+
// // Helper to wrap action execution with timeout
|
|
19
|
+
// async function withTimeout<T>(promise: Promise<T>, ms?: number): Promise<T> {
|
|
20
|
+
// if (!ms) return promise;
|
|
21
|
+
// return Promise.race([
|
|
22
|
+
// promise,
|
|
23
|
+
// new Promise<T>((_, reject) =>
|
|
24
|
+
// setTimeout(() => reject(new Error("Timeout")), ms),
|
|
25
|
+
// ),
|
|
26
|
+
// ]);
|
|
27
|
+
// }
|
|
28
|
+
//
|
|
29
|
+
// // Helper to run action with retry support
|
|
30
|
+
// async function runWithRetry(
|
|
31
|
+
// actionFn: () => Promise<any>,
|
|
32
|
+
// stepOptions?: {
|
|
33
|
+
// retry?: number;
|
|
34
|
+
// retryDelay?: number | ((attempt: number) => number);
|
|
35
|
+
// },
|
|
36
|
+
// ): Promise<any> {
|
|
37
|
+
// const maxRetries = stepOptions?.retry ?? 0;
|
|
38
|
+
// let lastError: any;
|
|
39
|
+
//
|
|
40
|
+
// for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
41
|
+
// try {
|
|
42
|
+
// return await actionFn();
|
|
43
|
+
// } catch (err) {
|
|
44
|
+
// lastError = err;
|
|
45
|
+
//
|
|
46
|
+
// if (attempt === maxRetries) break;
|
|
47
|
+
//
|
|
48
|
+
// const delay = stepOptions?.retryDelay;
|
|
49
|
+
// if (typeof delay === "number") {
|
|
50
|
+
// await new Promise((r) => setTimeout(r, delay));
|
|
51
|
+
// } else if (typeof delay === "function") {
|
|
52
|
+
// await new Promise((r) => setTimeout(r, delay(attempt)));
|
|
53
|
+
// }
|
|
54
|
+
// }
|
|
55
|
+
// }
|
|
56
|
+
//
|
|
57
|
+
// throw lastError;
|
|
58
|
+
// }
|
|
59
|
+
//
|
|
60
|
+
// export async function executeWorkflow<
|
|
61
|
+
// Reg extends ActionRegistry,
|
|
62
|
+
// I,
|
|
63
|
+
// R,
|
|
64
|
+
// O = R,
|
|
65
|
+
// Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[],
|
|
66
|
+
// >(
|
|
67
|
+
// workflow: WorkflowDef<Reg, I, R, Steps, O>,
|
|
68
|
+
// registry: Reg,
|
|
69
|
+
// input: I,
|
|
70
|
+
// context: any,
|
|
71
|
+
// observers: WorkflowObserver<Reg>[] = [],
|
|
72
|
+
// ): Promise<{ results: R; output: O; extras: Record<string, any> }> {
|
|
73
|
+
// const results: Record<string, any> = {};
|
|
74
|
+
// const extras: Record<string, any> = {};
|
|
75
|
+
// extras.frames = {} as Record<string, ExecutionFrame>;
|
|
76
|
+
//
|
|
77
|
+
// // -----------------------------
|
|
78
|
+
// // Fully typed step map
|
|
79
|
+
// // -----------------------------
|
|
80
|
+
// const stepById: Map<string, StepDef<Reg, any, any>> = new Map(
|
|
81
|
+
// workflow.steps.map((s: StepDef<Reg, any, any>) => [s.id, s]),
|
|
82
|
+
// );
|
|
83
|
+
//
|
|
84
|
+
// const remainingDeps = new Map<string, number>();
|
|
85
|
+
// const dependents = new Map<string, string[]>();
|
|
86
|
+
// const ready: string[] = [];
|
|
87
|
+
//
|
|
88
|
+
// for (const step of workflow.steps) {
|
|
89
|
+
// remainingDeps.set(step.id, step.dependsOn.length);
|
|
90
|
+
// if (step.dependsOn.length === 0) ready.push(step.id);
|
|
91
|
+
//
|
|
92
|
+
// for (const dep of step.dependsOn) {
|
|
93
|
+
// if (!dependents.has(dep)) dependents.set(dep, []);
|
|
94
|
+
// dependents.get(dep)!.push(step.id);
|
|
95
|
+
// }
|
|
96
|
+
// }
|
|
97
|
+
//
|
|
98
|
+
// let completed = 0;
|
|
99
|
+
//
|
|
100
|
+
// // -----------------------------
|
|
101
|
+
// // Normalized runner
|
|
102
|
+
// // -----------------------------
|
|
103
|
+
// const runAction = async (
|
|
104
|
+
// input: ResolvedStepInput | undefined,
|
|
105
|
+
// action: any,
|
|
106
|
+
// ): Promise<any> => {
|
|
107
|
+
// if (!input) return await action();
|
|
108
|
+
//
|
|
109
|
+
// switch (input.kind) {
|
|
110
|
+
// case "none":
|
|
111
|
+
// return await action();
|
|
112
|
+
// case "positional":
|
|
113
|
+
// return await action(...input.args);
|
|
114
|
+
// case "object":
|
|
115
|
+
// return await action(input.args);
|
|
116
|
+
// case "loop":
|
|
117
|
+
// return await Promise.all(
|
|
118
|
+
// input.items.map((item) => runAction(item, action)),
|
|
119
|
+
// );
|
|
120
|
+
// default:
|
|
121
|
+
// throw new Error(
|
|
122
|
+
// `Unknown ResolvedStepInput kind: ${(input as any).kind}`,
|
|
123
|
+
// );
|
|
124
|
+
// }
|
|
125
|
+
// };
|
|
126
|
+
//
|
|
127
|
+
// // -----------------------------
|
|
128
|
+
// // Execution loop
|
|
129
|
+
// // -----------------------------
|
|
130
|
+
// while (ready.length > 0) {
|
|
131
|
+
// const batch = ready.splice(0);
|
|
132
|
+
//
|
|
133
|
+
// await Promise.all(
|
|
134
|
+
// batch.map(async (stepId) => {
|
|
135
|
+
// const step = stepById.get(stepId)!;
|
|
136
|
+
//
|
|
137
|
+
// const frame: ExecutionFrame = {
|
|
138
|
+
// stepId,
|
|
139
|
+
// attempts: 0,
|
|
140
|
+
// start: Date.now(),
|
|
141
|
+
// };
|
|
142
|
+
// extras.frames[stepId] = frame;
|
|
143
|
+
//
|
|
144
|
+
// const ctx = {
|
|
145
|
+
// stepId,
|
|
146
|
+
// input,
|
|
147
|
+
// results,
|
|
148
|
+
// context,
|
|
149
|
+
// registry,
|
|
150
|
+
// extras,
|
|
151
|
+
// frame,
|
|
152
|
+
// };
|
|
153
|
+
//
|
|
154
|
+
// const core = async () => {
|
|
155
|
+
// frame.attempts++;
|
|
156
|
+
// const stepCtx = { input, results, context, ...createCallHelpers() };
|
|
157
|
+
//
|
|
158
|
+
// // Conditional skip
|
|
159
|
+
// if (step.when && !step.when(stepCtx)) {
|
|
160
|
+
// frame.output = undefined;
|
|
161
|
+
// frame.end = Date.now();
|
|
162
|
+
// frame.skipped = true;
|
|
163
|
+
// results[step.id] = undefined;
|
|
164
|
+
// return undefined;
|
|
165
|
+
// }
|
|
166
|
+
//
|
|
167
|
+
// const resolvedArgs = step.resolve?.(stepCtx);
|
|
168
|
+
// frame.input = resolvedArgs;
|
|
169
|
+
//
|
|
170
|
+
// const actionFn = async () => {
|
|
171
|
+
// const action = registry[step.action];
|
|
172
|
+
// return await runAction(resolvedArgs, action);
|
|
173
|
+
// };
|
|
174
|
+
//
|
|
175
|
+
// try {
|
|
176
|
+
// const result = await withTimeout(
|
|
177
|
+
// runWithRetry(actionFn, step.options),
|
|
178
|
+
// step.options?.timeout,
|
|
179
|
+
// );
|
|
180
|
+
//
|
|
181
|
+
// frame.output = result;
|
|
182
|
+
// frame.end = Date.now();
|
|
183
|
+
// results[step.id] = result;
|
|
184
|
+
// return result;
|
|
185
|
+
// } catch (err) {
|
|
186
|
+
// frame.error = err;
|
|
187
|
+
// frame.end = Date.now();
|
|
188
|
+
//
|
|
189
|
+
// if (step.options?.onError) {
|
|
190
|
+
// const fallback = step.options.onError(err, ctx);
|
|
191
|
+
// results[step.id] = fallback;
|
|
192
|
+
// return fallback;
|
|
193
|
+
// }
|
|
194
|
+
//
|
|
195
|
+
// if (step.options?.continueOnError) {
|
|
196
|
+
// results[step.id] = undefined;
|
|
197
|
+
// return undefined;
|
|
198
|
+
// }
|
|
199
|
+
//
|
|
200
|
+
// throw err;
|
|
201
|
+
// }
|
|
202
|
+
// };
|
|
203
|
+
//
|
|
204
|
+
// const composed = composeObserver(observers, ctx, core);
|
|
205
|
+
// await composed();
|
|
206
|
+
//
|
|
207
|
+
// for (const childId of dependents.get(stepId) ?? []) {
|
|
208
|
+
// const remaining = remainingDeps.get(childId)! - 1;
|
|
209
|
+
// remainingDeps.set(childId, remaining);
|
|
210
|
+
// if (remaining === 0) ready.push(childId);
|
|
211
|
+
// }
|
|
212
|
+
//
|
|
213
|
+
// completed++;
|
|
214
|
+
// }),
|
|
215
|
+
// );
|
|
216
|
+
// }
|
|
217
|
+
//
|
|
218
|
+
// if (completed !== workflow.steps.length) {
|
|
219
|
+
// throw new Error("Workflow execution failed (cycle or missing dependency)");
|
|
220
|
+
// }
|
|
221
|
+
//
|
|
222
|
+
// const output: O = workflow.outputResolver
|
|
223
|
+
// ? workflow.outputResolver({
|
|
224
|
+
// input,
|
|
225
|
+
// results: results as R,
|
|
226
|
+
// context: (workflow as any).__context,
|
|
227
|
+
// })
|
|
228
|
+
// : (results as unknown as O);
|
|
229
|
+
//
|
|
230
|
+
// return { results: results as R, output, extras };
|
|
231
|
+
// }
|
|
1
232
|
import { composeObserver } from "./observer.js";
|
|
2
|
-
function createCallHelpers() {
|
|
233
|
+
export function createCallHelpers() {
|
|
3
234
|
return {
|
|
4
235
|
args: (...args) => ({ kind: "positional", args }),
|
|
5
236
|
obj: (arg) => ({ kind: "object", args: arg }),
|
|
@@ -39,13 +270,20 @@ async function runWithRetry(actionFn, stepOptions) {
|
|
|
39
270
|
}
|
|
40
271
|
throw lastError;
|
|
41
272
|
}
|
|
42
|
-
export async function executeWorkflow(
|
|
273
|
+
export async function executeWorkflow(workflowId, workflowRegistry, actionRegistry, input, context, observers = []) {
|
|
274
|
+
const workflow = workflowRegistry[workflowId];
|
|
275
|
+
if (!workflow) {
|
|
276
|
+
throw new Error(`Workflow not found: ${String(workflowId)}`);
|
|
277
|
+
}
|
|
43
278
|
const results = {};
|
|
44
279
|
const extras = {};
|
|
45
280
|
extras.frames = {};
|
|
46
281
|
// -----------------------------
|
|
47
282
|
// Fully typed step map
|
|
48
283
|
// -----------------------------
|
|
284
|
+
// const stepById: Map<string, StepDef<Reg, any, any>> = new Map(
|
|
285
|
+
// workflow.steps.map((s: StepDef<Reg, any, any>) => [s.id, s]),
|
|
286
|
+
// );
|
|
49
287
|
const stepById = new Map(workflow.steps.map((s) => [s.id, s]));
|
|
50
288
|
const remainingDeps = new Map();
|
|
51
289
|
const dependents = new Map();
|
|
@@ -98,7 +336,7 @@ export async function executeWorkflow(workflow, registry, input, context, observ
|
|
|
98
336
|
input,
|
|
99
337
|
results,
|
|
100
338
|
context,
|
|
101
|
-
|
|
339
|
+
actionRegistry,
|
|
102
340
|
extras,
|
|
103
341
|
frame,
|
|
104
342
|
};
|
|
@@ -115,8 +353,22 @@ export async function executeWorkflow(workflow, registry, input, context, observ
|
|
|
115
353
|
}
|
|
116
354
|
const resolvedArgs = step.resolve?.(stepCtx);
|
|
117
355
|
frame.input = resolvedArgs;
|
|
356
|
+
// -----------------------------
|
|
357
|
+
// Subflow handling
|
|
358
|
+
// -----------------------------
|
|
359
|
+
if (step.__subflowId) {
|
|
360
|
+
const subWorkflow = step.__subflow;
|
|
361
|
+
const subInput = resolvedArgs;
|
|
362
|
+
const subExecution = await executeWorkflow(step.__subflowId, workflowRegistry, actionRegistry, resolvedArgs, context, observers);
|
|
363
|
+
// Assign output to this step
|
|
364
|
+
frame.output = subExecution.output;
|
|
365
|
+
results[step.id] = subExecution.output;
|
|
366
|
+
// Merge extras
|
|
367
|
+
Object.assign(extras, subExecution.extras);
|
|
368
|
+
return subExecution.output;
|
|
369
|
+
}
|
|
118
370
|
const actionFn = async () => {
|
|
119
|
-
const action =
|
|
371
|
+
const action = actionRegistry[step.action];
|
|
120
372
|
return await runAction(resolvedArgs, action);
|
|
121
373
|
};
|
|
122
374
|
try {
|
|
@@ -158,9 +410,13 @@ export async function executeWorkflow(workflow, registry, input, context, observ
|
|
|
158
410
|
const output = workflow.outputResolver
|
|
159
411
|
? workflow.outputResolver({
|
|
160
412
|
input,
|
|
161
|
-
results
|
|
413
|
+
results,
|
|
162
414
|
context: workflow.__context,
|
|
163
415
|
})
|
|
164
416
|
: results;
|
|
165
|
-
return {
|
|
417
|
+
return {
|
|
418
|
+
// results: results as WorkflowResults<WR[K]>,
|
|
419
|
+
output: output,
|
|
420
|
+
extras,
|
|
421
|
+
};
|
|
166
422
|
}
|
|
@@ -1,59 +1,40 @@
|
|
|
1
1
|
import { ActionRegistry, WorkflowObserver } from "./types.js";
|
|
2
2
|
import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
7
|
-
type NamespacedDepWFs<DepName extends string, M extends Module<any, any, any, any>> = {
|
|
8
|
-
[K in keyof M["own"] & string as `${DepName}.${K}`]: M["own"][K];
|
|
9
|
-
};
|
|
10
|
-
type DepsWFs<Deps extends ModuleMap> = [keyof Deps] extends [never] ? {} : {
|
|
11
|
-
[DepName in keyof Deps & string]: NamespacedDepWFs<DepName, Deps[DepName]>;
|
|
3
|
+
type DepWorkflows<Deps extends ModuleMap> = keyof Deps extends never ? {} : {
|
|
4
|
+
[D in keyof Deps & string]: {
|
|
5
|
+
[K in keyof Deps[D]["workflows"] & string as `${D}.${K}`]: Deps[D]["workflows"][K];
|
|
6
|
+
};
|
|
12
7
|
}[keyof Deps & string];
|
|
13
|
-
type
|
|
14
|
-
[K in keyof Use]: Use[K] extends Module<any, infer Ctx, infer Own, any> ? ModulePublic<Ctx, Own> : never;
|
|
15
|
-
};
|
|
16
|
-
type ModulePublic<Context extends Record<string, any>, Own extends ModuleShape> = {
|
|
17
|
-
own: Own;
|
|
18
|
-
__ctx: Context;
|
|
19
|
-
};
|
|
8
|
+
type WorkflowRegistry<Own extends ModuleShape, Deps extends ModuleMap> = Own & DepWorkflows<Deps>;
|
|
20
9
|
type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
|
|
21
10
|
type ModuleShape = Record<string, AnyWorkflow>;
|
|
11
|
+
type ModuleMap = Record<string, Module<any, any, any, any>>;
|
|
22
12
|
type ContextFromDeps<Deps> = [keyof Deps] extends [never] ? {} : {
|
|
23
13
|
[K in keyof Deps]: Deps[K] extends Module<any, infer Ctx, any, any> ? Ctx : never;
|
|
24
14
|
}[keyof Deps];
|
|
25
15
|
type FinalContext<Reg extends ActionRegistry, Context extends Record<string, any>, Deps extends ModuleMap> = Context & ContextFromDeps<Deps>;
|
|
26
|
-
type ModuleMap = Record<string, Module<any, any, any, any>>;
|
|
27
16
|
export type WorkflowInput<W> = W extends WorkflowDef<any, infer I, any, any, any> ? I : never;
|
|
28
17
|
export type WorkflowResults<W> = W extends WorkflowDef<any, any, infer R, any, any> ? R : never;
|
|
29
18
|
export type WorkflowOutput<W> = W extends WorkflowDef<any, any, any, any, infer O> ? O : never;
|
|
30
|
-
type Module<Reg extends ActionRegistry, Context extends Record<string, any>, Own extends ModuleShape
|
|
31
|
-
|
|
32
|
-
deps: ModuleDepsPublic<Deps>;
|
|
19
|
+
type Module<Reg extends ActionRegistry, Context extends Record<string, any>, Own extends ModuleShape, Deps extends ModuleMap> = {
|
|
20
|
+
workflows: Own;
|
|
33
21
|
createRuntime: (config: {
|
|
34
22
|
registry: Reg;
|
|
35
23
|
context: FinalContext<Reg, Context, Deps>;
|
|
36
24
|
}) => {
|
|
37
|
-
run: <K extends keyof
|
|
38
|
-
|
|
39
|
-
output: WorkflowOutput<ModuleWFRegistry<Own, Deps>[K]>;
|
|
25
|
+
run: <K extends keyof WorkflowRegistry<Own, Deps>>(workflow: K, input: WorkflowInput<WorkflowRegistry<Own, Deps>[K]>, observers?: WorkflowObserver<Reg>[]) => Promise<{
|
|
26
|
+
output: WorkflowOutput<WorkflowRegistry<Own, Deps>[K]>;
|
|
40
27
|
extras: Record<string, any>;
|
|
41
28
|
}>;
|
|
42
29
|
getContext: () => FinalContext<Reg, Context, Deps>;
|
|
43
30
|
};
|
|
44
31
|
};
|
|
45
|
-
export type ModuleContext<Reg extends ActionRegistry,
|
|
46
|
-
wf: ReturnType<typeof createWorkflow<Reg, Context>>;
|
|
47
|
-
deps: Deps;
|
|
32
|
+
export type ModuleContext<Reg extends ActionRegistry, WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, Context extends Record<string, any>> = {
|
|
33
|
+
wf: ReturnType<typeof createWorkflow<Reg, WFReg, Context>>;
|
|
48
34
|
context: Context;
|
|
49
|
-
tools: <T>(factory: (ctx: {
|
|
50
|
-
wf: ModuleContext<Reg, Context, Deps>["wf"];
|
|
51
|
-
deps: Deps;
|
|
52
|
-
context: Context;
|
|
53
|
-
}) => T) => T;
|
|
54
35
|
};
|
|
55
36
|
export declare function createModuleFactory<Reg extends ActionRegistry, Context extends Record<string, any>>(): <Use extends ModuleMap = {}, Own extends ModuleShape = {}>(config: {
|
|
56
37
|
use?: Use;
|
|
57
|
-
define: (ctx: ModuleContext<Reg, Context
|
|
38
|
+
define: (ctx: ModuleContext<Reg, DepWorkflows<Use>, Context>) => Own;
|
|
58
39
|
}) => Module<Reg, Context, Own, Use>;
|
|
59
40
|
export {};
|
package/dist/workflow-module.js
CHANGED
|
@@ -145,40 +145,37 @@
|
|
|
145
145
|
//
|
|
146
146
|
import { createWorkflow } from "./workflow-composer.js";
|
|
147
147
|
import { executeWorkflow } from "./workflow-executor.js";
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
/* ------------------------------------------------ */
|
|
149
|
+
/* CREATE MODULE (FIXED) */
|
|
150
|
+
/* ------------------------------------------------ */
|
|
151
151
|
function createModule(config) {
|
|
152
152
|
const deps = (config.use ?? {});
|
|
153
|
+
// ✅ CRITICAL: wf only sees DEP workflows
|
|
154
|
+
const wf = createWorkflow();
|
|
153
155
|
const own = config.define({
|
|
154
|
-
wf
|
|
155
|
-
deps,
|
|
156
|
+
wf,
|
|
156
157
|
context: {},
|
|
157
|
-
tools: (factory) => factory({
|
|
158
|
-
wf: createWorkflow(),
|
|
159
|
-
deps,
|
|
160
|
-
context: {},
|
|
161
|
-
}),
|
|
162
158
|
});
|
|
163
|
-
function
|
|
164
|
-
const depWFs = Object.fromEntries(Object.entries(deps).flatMap(([
|
|
159
|
+
function buildRegistry() {
|
|
160
|
+
const depWFs = Object.fromEntries(Object.entries(deps).flatMap(([name, mod]) => Object.entries(mod.workflows).map(([k, wf]) => [`${name}.${k}`, wf])));
|
|
165
161
|
return { ...own, ...depWFs };
|
|
166
162
|
}
|
|
167
163
|
return {
|
|
168
|
-
own,
|
|
169
|
-
deps: toPublicDeps(deps),
|
|
164
|
+
workflows: own,
|
|
170
165
|
createRuntime({ registry, context }) {
|
|
171
|
-
const workflowRegistry =
|
|
166
|
+
const workflowRegistry = buildRegistry();
|
|
172
167
|
return {
|
|
173
168
|
run: async (workflowId, input, observers = []) => {
|
|
174
|
-
|
|
175
|
-
return executeWorkflow(wfObj, registry, input, context, observers);
|
|
169
|
+
return executeWorkflow(workflowId, workflowRegistry, registry, input, context, observers);
|
|
176
170
|
},
|
|
177
171
|
getContext: () => ({ ...context }),
|
|
178
172
|
};
|
|
179
173
|
},
|
|
180
174
|
};
|
|
181
175
|
}
|
|
176
|
+
/* ------------------------------------------------ */
|
|
177
|
+
/* FACTORY (FIXED) */
|
|
178
|
+
/* ------------------------------------------------ */
|
|
182
179
|
export function createModuleFactory() {
|
|
183
180
|
return function (config) {
|
|
184
181
|
return createModule(config);
|