@pogodisco/zephyr 1.4.1 → 1.4.2
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 +0 -2
- package/dist/index.js +0 -2
- package/dist/observer.js +8 -7
- package/dist/types.d.ts +3 -0
- package/dist/utils.d.ts +1 -10
- package/dist/utils.js +5 -34
- package/dist/workflow-composer.d.ts +1 -0
- package/dist/workflow-composer.js +5 -451
- package/dist/workflow-executor.d.ts +10 -4
- package/dist/workflow-executor.js +29 -246
- package/dist/workflow-module.d.ts +19 -14
- package/dist/workflow-module.js +29 -153
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,6 @@ export * from "./registry.js";
|
|
|
3
3
|
export * from "./workflow-executor.js";
|
|
4
4
|
export * from "./workflow-composer.js";
|
|
5
5
|
export * from "./workflow-module.js";
|
|
6
|
-
export * from "./utils.js";
|
|
7
6
|
export * from "./types.js";
|
|
8
|
-
export * from "./utils.js";
|
|
9
7
|
export * from "./session.js";
|
|
10
8
|
export { useMetrics, useLog } from "./observer.js";
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,6 @@ export * from "./registry.js";
|
|
|
3
3
|
export * from "./workflow-executor.js";
|
|
4
4
|
export * from "./workflow-composer.js";
|
|
5
5
|
export * from "./workflow-module.js";
|
|
6
|
-
export * from "./utils.js";
|
|
7
6
|
export * from "./types.js";
|
|
8
|
-
export * from "./utils.js";
|
|
9
7
|
export * from "./session.js";
|
|
10
8
|
export { useMetrics, useLog } from "./observer.js";
|
package/dist/observer.js
CHANGED
|
@@ -15,16 +15,17 @@ export function composeObserver(middleware, ctx, core) {
|
|
|
15
15
|
export function useLog() {
|
|
16
16
|
return async ({ frame, stepId }, next) => {
|
|
17
17
|
eventStream.emit({
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
stepId: frame.stepId,
|
|
19
|
+
state: "start",
|
|
20
|
+
start: frame.start,
|
|
20
21
|
timestamp: frame.start,
|
|
21
22
|
input: frame.input,
|
|
22
23
|
});
|
|
23
24
|
try {
|
|
24
25
|
const res = await next();
|
|
25
26
|
eventStream.emit({
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
id: frame.stepId,
|
|
28
|
+
state: frame.skipped ? "skipped" : "success",
|
|
28
29
|
output: frame.output,
|
|
29
30
|
duration: frame.end - frame.start,
|
|
30
31
|
attempts: frame.attempts,
|
|
@@ -34,11 +35,11 @@ export function useLog() {
|
|
|
34
35
|
}
|
|
35
36
|
catch (err) {
|
|
36
37
|
eventStream.emit({
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
...frame,
|
|
39
|
+
state: "fail",
|
|
39
40
|
error: frame.error,
|
|
40
41
|
timestamp: Date.now(),
|
|
41
|
-
attempts: frame.attempts,
|
|
42
|
+
// attempts: frame.attempts,
|
|
42
43
|
});
|
|
43
44
|
throw err;
|
|
44
45
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -26,3 +26,6 @@ export type WorkflowObserver<Reg extends ActionRegistry = any> = {
|
|
|
26
26
|
frame: ExecutionFrame;
|
|
27
27
|
}, next: () => Promise<any>): Promise<any>;
|
|
28
28
|
};
|
|
29
|
+
export interface Executor {
|
|
30
|
+
run: (wfId: string, input: Record<string, any>, context: Record<string, any>, obververs?: WorkflowObserver[]) => Promise<any>;
|
|
31
|
+
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* For generic actions - preserves the generic parameter
|
|
4
|
-
*/
|
|
5
|
-
export declare function genericAction<F extends AnyFn>(fn: F): <T>() => ((...args: Parameters<F>) => ReturnType<F>);
|
|
6
|
-
/**
|
|
7
|
-
* For fixed actions
|
|
8
|
-
*/
|
|
9
|
-
export declare function fixedAction<F extends AnyFn>(fn: F): () => ((...args: Parameters<F>) => ReturnType<F>);
|
|
10
|
-
export {};
|
|
1
|
+
export declare function generateWorkflowId(name: string): string;
|
package/dist/utils.js
CHANGED
|
@@ -1,35 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return () => {
|
|
7
|
-
return fn;
|
|
8
|
-
};
|
|
1
|
+
export function generateWorkflowId(name) {
|
|
2
|
+
// Short random + timestamp for practically unique ID
|
|
3
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
4
|
+
const time = Date.now().toString(36);
|
|
5
|
+
return `${name}-${time}-${random}`;
|
|
9
6
|
}
|
|
10
|
-
/**
|
|
11
|
-
* For fixed actions
|
|
12
|
-
*/
|
|
13
|
-
export function fixedAction(fn) {
|
|
14
|
-
return () => {
|
|
15
|
-
return fn;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
// export function createRuntime<Reg extends ActionRegistry, Context>(
|
|
19
|
-
// registry: Reg,
|
|
20
|
-
// context: Context,
|
|
21
|
-
// ) {
|
|
22
|
-
// return {
|
|
23
|
-
// run: async <W extends WorkflowDef<Reg, any, any, any, any>>(
|
|
24
|
-
// workflow: W,
|
|
25
|
-
// input: W extends WorkflowDef<Reg, infer I, any, any, any> ? I : never,
|
|
26
|
-
// observers?: WorkflowObserver[],
|
|
27
|
-
// ): Promise<{
|
|
28
|
-
// output: W extends WorkflowDef<Reg, any, any, any, infer O> ? O : never;
|
|
29
|
-
// results: W extends WorkflowDef<Reg, any, infer R, any, any> ? R : never;
|
|
30
|
-
// extras: Record<string, any>;
|
|
31
|
-
// }> => {
|
|
32
|
-
// return executeWorkflow(workflow, registry, input, context, observers);
|
|
33
|
-
// },
|
|
34
|
-
// };
|
|
35
|
-
// }
|
|
@@ -51,6 +51,7 @@ export type StepDef<Reg extends ActionRegistry, ID extends string = string, Acti
|
|
|
51
51
|
};
|
|
52
52
|
export type WorkflowDef<Reg extends ActionRegistry, Input, Results, Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[], Output = undefined> = {
|
|
53
53
|
name: string;
|
|
54
|
+
_id: string;
|
|
54
55
|
steps: Steps;
|
|
55
56
|
entrySteps: StepDef<Reg>[];
|
|
56
57
|
endSteps: StepDef<Reg>[];
|
|
@@ -1,354 +1,3 @@
|
|
|
1
|
-
// import {
|
|
2
|
-
// ActionParams,
|
|
3
|
-
// ActionRegistry,
|
|
4
|
-
// ActionReturn,
|
|
5
|
-
// Simplify,
|
|
6
|
-
// } from "./types.js";
|
|
7
|
-
//
|
|
8
|
-
// /* ------------------------------------------------ */
|
|
9
|
-
// /* STEP INPUT NORMALIZATION TYPES */
|
|
10
|
-
// /* ------------------------------------------------ */
|
|
11
|
-
//
|
|
12
|
-
// type NormalizedCall =
|
|
13
|
-
// | { kind: "none" }
|
|
14
|
-
// | { kind: "positional"; args: any[] }
|
|
15
|
-
// | { kind: "object"; args: any };
|
|
16
|
-
//
|
|
17
|
-
// export type ResolvedStepInput =
|
|
18
|
-
// | NormalizedCall
|
|
19
|
-
// | { kind: "loop"; items: NormalizedCall[] };
|
|
20
|
-
//
|
|
21
|
-
// type CallHelpers<Reg extends ActionRegistry, ActionName extends keyof Reg> = {
|
|
22
|
-
// args: (...args: ActionParams<Reg, ActionName>) => {
|
|
23
|
-
// kind: "positional";
|
|
24
|
-
// args: ActionParams<Reg, ActionName>;
|
|
25
|
-
// };
|
|
26
|
-
//
|
|
27
|
-
// obj: ActionParams<Reg, ActionName> extends [infer A]
|
|
28
|
-
// ? (arg: A) => { kind: "object"; args: A }
|
|
29
|
-
// : never;
|
|
30
|
-
//
|
|
31
|
-
// none: () => { kind: "none" };
|
|
32
|
-
// loop: (
|
|
33
|
-
// items:
|
|
34
|
-
// | { kind: "positional"; args: ActionParams<Reg, ActionName> }[]
|
|
35
|
-
// | { kind: "object"; args: ActionParams<Reg, ActionName>[0] }[],
|
|
36
|
-
// ) => {
|
|
37
|
-
// kind: "loop";
|
|
38
|
-
// items: typeof items;
|
|
39
|
-
// };
|
|
40
|
-
// };
|
|
41
|
-
//
|
|
42
|
-
// /* ------------------------------------------------ */
|
|
43
|
-
// /* STEP RESULT TYPES */
|
|
44
|
-
// /* ------------------------------------------------ */
|
|
45
|
-
//
|
|
46
|
-
// type SubflowResult<SubResults, SubOutput> = SubOutput extends undefined
|
|
47
|
-
// ? SubResults
|
|
48
|
-
// : SubOutput;
|
|
49
|
-
//
|
|
50
|
-
// type WorkflowInput<T> =
|
|
51
|
-
// T extends WorkflowDef<any, infer I, any, any, any> ? I : never;
|
|
52
|
-
//
|
|
53
|
-
// type WorkflowResults<T> =
|
|
54
|
-
// T extends WorkflowDef<any, any, infer R, any, any> ? R : never;
|
|
55
|
-
//
|
|
56
|
-
// type WorkflowOutput<T> =
|
|
57
|
-
// T extends WorkflowDef<any, any, any, any, infer O> ? O : never;
|
|
58
|
-
//
|
|
59
|
-
// export type StepResultFromResolve<
|
|
60
|
-
// Reg extends ActionRegistry,
|
|
61
|
-
// ActionName extends keyof Reg,
|
|
62
|
-
// R extends ResolvedStepInput,
|
|
63
|
-
// > = R extends { kind: "loop" }
|
|
64
|
-
// ? ActionReturn<Reg, ActionName>[]
|
|
65
|
-
// : ActionReturn<Reg, ActionName>;
|
|
66
|
-
//
|
|
67
|
-
// /* ------------------------------------------------ */
|
|
68
|
-
// /* STEP DEFINITION */
|
|
69
|
-
// /* ------------------------------------------------ */
|
|
70
|
-
//
|
|
71
|
-
// export type StepDef<
|
|
72
|
-
// Reg extends ActionRegistry,
|
|
73
|
-
// ID extends string = string,
|
|
74
|
-
// ActionName extends keyof Reg = any,
|
|
75
|
-
// > = {
|
|
76
|
-
// id: ID;
|
|
77
|
-
// action: ActionName;
|
|
78
|
-
// dependsOn: string[];
|
|
79
|
-
// resolve: (ctx: any) => ResolvedStepInput;
|
|
80
|
-
// when?: (ctx: any) => boolean;
|
|
81
|
-
// options?: StepOptions<any, any, any>;
|
|
82
|
-
// };
|
|
83
|
-
//
|
|
84
|
-
// /* ------------------------------------------------ */
|
|
85
|
-
// /* WORKFLOW DEFINITION */
|
|
86
|
-
// /* ------------------------------------------------ */
|
|
87
|
-
//
|
|
88
|
-
// export type WorkflowDef<
|
|
89
|
-
// Reg extends ActionRegistry,
|
|
90
|
-
// Input,
|
|
91
|
-
// Results,
|
|
92
|
-
// Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[],
|
|
93
|
-
// Output = undefined,
|
|
94
|
-
// > = {
|
|
95
|
-
// name: string;
|
|
96
|
-
// steps: Steps;
|
|
97
|
-
// entrySteps: StepDef<Reg>[];
|
|
98
|
-
// endSteps: StepDef<Reg>[];
|
|
99
|
-
// input: Input;
|
|
100
|
-
// results: Results;
|
|
101
|
-
// outputResolver?: (ctx: any) => Output;
|
|
102
|
-
// // __context?: any;
|
|
103
|
-
// };
|
|
104
|
-
// type StepRuntimeCtx<I, R, C> = {
|
|
105
|
-
// input: I;
|
|
106
|
-
// results: R;
|
|
107
|
-
// context: C;
|
|
108
|
-
// };
|
|
109
|
-
// type StepOptions<Input, Results, Context> = {
|
|
110
|
-
// retry?: number;
|
|
111
|
-
// retryDelay?: number | ((attempt: number) => number);
|
|
112
|
-
// timeout?: number;
|
|
113
|
-
//
|
|
114
|
-
// continueOnError?: boolean;
|
|
115
|
-
//
|
|
116
|
-
// onError?: (err: unknown, ctx: StepRuntimeCtx<Input, Results, Context>) => any;
|
|
117
|
-
//
|
|
118
|
-
// // optional later:
|
|
119
|
-
// label?: string;
|
|
120
|
-
// meta?: Record<string, any>;
|
|
121
|
-
// };
|
|
122
|
-
//
|
|
123
|
-
// /* ------------------------------------------------ */
|
|
124
|
-
// /* HELPER TYPES */
|
|
125
|
-
// /* ------------------------------------------------ */
|
|
126
|
-
//
|
|
127
|
-
// type MergeBranchResults<
|
|
128
|
-
// Branches extends readonly any[],
|
|
129
|
-
// Acc,
|
|
130
|
-
// > = Branches extends readonly [infer Head, ...infer Tail]
|
|
131
|
-
// ? MergeBranchResults<
|
|
132
|
-
// Tail,
|
|
133
|
-
// Acc & (Head extends WorkflowBuilder<any, any, any, any, infer R> ? R : {})
|
|
134
|
-
// >
|
|
135
|
-
// : Acc;
|
|
136
|
-
//
|
|
137
|
-
// type MergeBranchSteps<
|
|
138
|
-
// Branches extends readonly any[],
|
|
139
|
-
// Acc extends any[],
|
|
140
|
-
// > = Branches extends readonly [infer Head, ...infer Tail]
|
|
141
|
-
// ? MergeBranchSteps<
|
|
142
|
-
// Tail,
|
|
143
|
-
// [
|
|
144
|
-
// ...Acc,
|
|
145
|
-
// ...(Head extends WorkflowBuilder<any, any, any, infer S, any> ? S : []),
|
|
146
|
-
// ]
|
|
147
|
-
// >
|
|
148
|
-
// : Acc;
|
|
149
|
-
//
|
|
150
|
-
// // type UnionToIntersection<U> = (U extends any ? (x: U) => any : never) extends (
|
|
151
|
-
// // x: infer I,
|
|
152
|
-
// // ) => any
|
|
153
|
-
// // ? I
|
|
154
|
-
// // : never;
|
|
155
|
-
// //
|
|
156
|
-
// // type ExtractBranchResults<Branches extends readonly any[]> =
|
|
157
|
-
// // Branches[number] extends WorkflowBuilder<any, any, any, any, infer R>
|
|
158
|
-
// // ? R
|
|
159
|
-
// // : never;
|
|
160
|
-
// //
|
|
161
|
-
// // type MergeBranchResults<Branches extends readonly any[], Acc> = Simplify<
|
|
162
|
-
// // Acc & Partial<ExtractBranchResults<Branches>>
|
|
163
|
-
// // >;
|
|
164
|
-
//
|
|
165
|
-
// /* ------------------------------------------------ */
|
|
166
|
-
// /* FLUENT WORKFLOW BUILDER */
|
|
167
|
-
// /* ------------------------------------------------ */
|
|
168
|
-
//
|
|
169
|
-
// export class WorkflowBuilder<
|
|
170
|
-
// Reg extends ActionRegistry,
|
|
171
|
-
// Input = unknown,
|
|
172
|
-
// Context = unknown,
|
|
173
|
-
// Steps extends StepDef<Reg, any, any>[] = [],
|
|
174
|
-
// Results = {},
|
|
175
|
-
// Output = undefined,
|
|
176
|
-
// > {
|
|
177
|
-
// private steps: StepDef<Reg, any, any>[] = [];
|
|
178
|
-
// private frontier: string[] = [];
|
|
179
|
-
// private pendingWhen?: (ctx: {
|
|
180
|
-
// input: Input;
|
|
181
|
-
// results: Results;
|
|
182
|
-
// context: Context;
|
|
183
|
-
// }) => boolean;
|
|
184
|
-
// private outputResolver?: (ctx: any) => any;
|
|
185
|
-
// private clearPendingWhen() {
|
|
186
|
-
// this.pendingWhen = undefined;
|
|
187
|
-
// }
|
|
188
|
-
//
|
|
189
|
-
// constructor(private name: string) {}
|
|
190
|
-
//
|
|
191
|
-
// step<
|
|
192
|
-
// ID extends string,
|
|
193
|
-
// ActionName extends keyof Reg & string,
|
|
194
|
-
// ResolveOut extends ResolvedStepInput = ResolvedStepInput,
|
|
195
|
-
// >(
|
|
196
|
-
// id: ID,
|
|
197
|
-
// action: ActionName,
|
|
198
|
-
// resolve?: (
|
|
199
|
-
// ctx: {
|
|
200
|
-
// input: Input;
|
|
201
|
-
// results: Results;
|
|
202
|
-
// context: Context;
|
|
203
|
-
// } & CallHelpers<Reg, ActionName>,
|
|
204
|
-
// ) => ResolveOut,
|
|
205
|
-
// dependsOn?: string[],
|
|
206
|
-
// options?: StepOptions<Input, Results, Context>,
|
|
207
|
-
// ): WorkflowBuilder<
|
|
208
|
-
// Reg,
|
|
209
|
-
// Input,
|
|
210
|
-
// Context,
|
|
211
|
-
// [...Steps, StepDef<Reg, ID, ActionName>],
|
|
212
|
-
// Simplify<
|
|
213
|
-
// Results & {
|
|
214
|
-
// [K in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut>;
|
|
215
|
-
// }
|
|
216
|
-
// >
|
|
217
|
-
// > {
|
|
218
|
-
// const deps = dependsOn ?? [...this.frontier];
|
|
219
|
-
//
|
|
220
|
-
// this.steps.push({
|
|
221
|
-
// id,
|
|
222
|
-
// action,
|
|
223
|
-
// resolve: resolve ?? (() => ({ kind: "none" })),
|
|
224
|
-
// dependsOn: deps,
|
|
225
|
-
// when: this.pendingWhen,
|
|
226
|
-
// options,
|
|
227
|
-
// });
|
|
228
|
-
//
|
|
229
|
-
// this.frontier = [id];
|
|
230
|
-
//
|
|
231
|
-
// return this as any;
|
|
232
|
-
// }
|
|
233
|
-
//
|
|
234
|
-
// /* ------------------------------------------------ */
|
|
235
|
-
// /* Sequential shortcut */
|
|
236
|
-
// /* ------------------------------------------------ */
|
|
237
|
-
// seq<
|
|
238
|
-
// ID extends string = string,
|
|
239
|
-
// ActionName extends keyof Reg & string = any,
|
|
240
|
-
// ResolveOut extends ResolvedStepInput = ResolvedStepInput,
|
|
241
|
-
// >(
|
|
242
|
-
// id: ID,
|
|
243
|
-
// action: ActionName,
|
|
244
|
-
// resolve?: (
|
|
245
|
-
// ctx: {
|
|
246
|
-
// input: Input;
|
|
247
|
-
// results: Results;
|
|
248
|
-
// context: Context;
|
|
249
|
-
// } & CallHelpers<Reg, ActionName>,
|
|
250
|
-
// ) => ResolveOut,
|
|
251
|
-
// options?: StepOptions<Input, Results, Context>,
|
|
252
|
-
// ) {
|
|
253
|
-
// return this.step<ID, ActionName, ResolveOut>(
|
|
254
|
-
// id,
|
|
255
|
-
// action,
|
|
256
|
-
// resolve,
|
|
257
|
-
// undefined,
|
|
258
|
-
// options,
|
|
259
|
-
// );
|
|
260
|
-
// }
|
|
261
|
-
//
|
|
262
|
-
// /* ------------------------------------------------ */
|
|
263
|
-
// /* Override the result of the last step */
|
|
264
|
-
// /* ------------------------------------------------ */
|
|
265
|
-
// as<NewType>(): WorkflowBuilder<
|
|
266
|
-
// Reg,
|
|
267
|
-
// Input,
|
|
268
|
-
// Context,
|
|
269
|
-
// Steps,
|
|
270
|
-
// // Override the result of the last step only
|
|
271
|
-
// Steps extends [...infer Rest, infer Last]
|
|
272
|
-
// ? Last extends StepDef<Reg, infer ID, any>
|
|
273
|
-
// ? Simplify<Omit<Results, ID> & { [K in ID]: NewType }>
|
|
274
|
-
// : Results
|
|
275
|
-
// : Results,
|
|
276
|
-
// Output
|
|
277
|
-
// > {
|
|
278
|
-
// return this as any;
|
|
279
|
-
// }
|
|
280
|
-
//
|
|
281
|
-
// parallel<
|
|
282
|
-
// Branches extends readonly WorkflowBuilder<Reg, Input, Context, any, any>[],
|
|
283
|
-
// >(
|
|
284
|
-
// ...branches: {
|
|
285
|
-
// [K in keyof Branches]: (
|
|
286
|
-
// builder: WorkflowBuilder<Reg, Input, Context, [], Results>,
|
|
287
|
-
// ) => Branches[K];
|
|
288
|
-
// }
|
|
289
|
-
// ): WorkflowBuilder<
|
|
290
|
-
// Reg,
|
|
291
|
-
// Input,
|
|
292
|
-
// Context,
|
|
293
|
-
// MergeBranchSteps<Branches, Steps>,
|
|
294
|
-
// // [
|
|
295
|
-
// // ...Steps,
|
|
296
|
-
// // ...(Branches[number] extends WorkflowBuilder<Reg, any, any, infer S, any>
|
|
297
|
-
// // ? S
|
|
298
|
-
// // : never),
|
|
299
|
-
// // ],
|
|
300
|
-
// Simplify<MergeBranchResults<Branches, Results>>
|
|
301
|
-
// > {
|
|
302
|
-
// const parentFrontier = [...this.frontier];
|
|
303
|
-
// const branchEnds: string[] = [];
|
|
304
|
-
//
|
|
305
|
-
// branches.forEach((branch) => {
|
|
306
|
-
// const b = new WorkflowBuilder<Reg, Input, Context, [], Results>(
|
|
307
|
-
// this.name,
|
|
308
|
-
// );
|
|
309
|
-
//
|
|
310
|
-
// b.frontier = parentFrontier;
|
|
311
|
-
// branch(b);
|
|
312
|
-
// branchEnds.push(...b.frontier);
|
|
313
|
-
// this.steps.push(...(b as any).steps);
|
|
314
|
-
// });
|
|
315
|
-
//
|
|
316
|
-
// this.frontier = branchEnds;
|
|
317
|
-
// this.clearPendingWhen();
|
|
318
|
-
// return this as any;
|
|
319
|
-
// }
|
|
320
|
-
//
|
|
321
|
-
// /* ------------------------------------------------ */
|
|
322
|
-
// /* Join helper */
|
|
323
|
-
// /* ------------------------------------------------ */
|
|
324
|
-
// join<
|
|
325
|
-
// ID extends string = string,
|
|
326
|
-
// ActionName extends keyof Reg & string = any,
|
|
327
|
-
// ResolveOut extends ResolvedStepInput = ResolvedStepInput,
|
|
328
|
-
// >(
|
|
329
|
-
// id: ID,
|
|
330
|
-
// action: ActionName,
|
|
331
|
-
// resolve?: (
|
|
332
|
-
// ctx: {
|
|
333
|
-
// input: Input;
|
|
334
|
-
// results: Results;
|
|
335
|
-
// context: Context;
|
|
336
|
-
// } & CallHelpers<Reg, ActionName>,
|
|
337
|
-
// ) => ResolveOut,
|
|
338
|
-
// options?: StepOptions<Input, Results, Context>,
|
|
339
|
-
// ) {
|
|
340
|
-
// const result = this.step<ID, ActionName, ResolveOut>(
|
|
341
|
-
// id,
|
|
342
|
-
// action,
|
|
343
|
-
// resolve,
|
|
344
|
-
// [...this.frontier],
|
|
345
|
-
// options,
|
|
346
|
-
// );
|
|
347
|
-
//
|
|
348
|
-
// this.clearPendingWhen();
|
|
349
|
-
// return result;
|
|
350
|
-
// }
|
|
351
|
-
//
|
|
352
1
|
// /* ------------------------------------------------ */
|
|
353
2
|
// /* Subflow */
|
|
354
3
|
// /* ------------------------------------------------ */
|
|
@@ -419,90 +68,7 @@
|
|
|
419
68
|
// /* Conditional */
|
|
420
69
|
// /* ------------------------------------------------ */
|
|
421
70
|
//
|
|
422
|
-
|
|
423
|
-
// predicate: (ctx: {
|
|
424
|
-
// input: Input;
|
|
425
|
-
// results: Results;
|
|
426
|
-
// context: Context;
|
|
427
|
-
// }) => boolean,
|
|
428
|
-
// ): WorkflowBuilder<Reg, Input, Context, Steps, Results, Output> {
|
|
429
|
-
// this.pendingWhen = predicate;
|
|
430
|
-
// return this;
|
|
431
|
-
// }
|
|
432
|
-
//
|
|
433
|
-
// endWhen(): this {
|
|
434
|
-
// this.clearPendingWhen();
|
|
435
|
-
//
|
|
436
|
-
// return this;
|
|
437
|
-
// }
|
|
438
|
-
//
|
|
439
|
-
// /* ------------------------------------------------ */
|
|
440
|
-
// /* Workflow output */
|
|
441
|
-
// /* ------------------------------------------------ */
|
|
442
|
-
// output<Output>(
|
|
443
|
-
// fn: (ctx: { input: Input; results: Results; context: Context }) => Output,
|
|
444
|
-
// ): WorkflowDef<Reg, Input, Results, Steps, Output> {
|
|
445
|
-
// this.outputResolver = fn;
|
|
446
|
-
// return this.build() as WorkflowDef<Reg, Input, Results, Steps, Output>;
|
|
447
|
-
// }
|
|
448
|
-
//
|
|
449
|
-
// build(): WorkflowDef<Reg, Input, Results, Steps> {
|
|
450
|
-
// this.validateDependencies();
|
|
451
|
-
//
|
|
452
|
-
// return {
|
|
453
|
-
// name: this.name,
|
|
454
|
-
// steps: this.steps as Steps,
|
|
455
|
-
// entrySteps: this.steps.filter((s) => s.dependsOn.length === 0),
|
|
456
|
-
// endSteps: this.getEndSteps(),
|
|
457
|
-
// input: {} as Input,
|
|
458
|
-
// results: {} as Results,
|
|
459
|
-
// outputResolver: this.outputResolver,
|
|
460
|
-
// };
|
|
461
|
-
// }
|
|
462
|
-
//
|
|
463
|
-
// private validateDependencies() {
|
|
464
|
-
// const stepIds = new Set(this.steps.map((s) => s.id));
|
|
465
|
-
// for (const step of this.steps) {
|
|
466
|
-
// for (const dep of step.dependsOn) {
|
|
467
|
-
// if (!stepIds.has(dep))
|
|
468
|
-
// throw new Error(`Step ${step.id} depends on unknown step ${dep}`);
|
|
469
|
-
// }
|
|
470
|
-
// }
|
|
471
|
-
// }
|
|
472
|
-
//
|
|
473
|
-
// private getEndSteps() {
|
|
474
|
-
// const hasDependents = new Set<string>();
|
|
475
|
-
// for (const step of this.steps) {
|
|
476
|
-
// for (const dep of step.dependsOn) hasDependents.add(dep);
|
|
477
|
-
// }
|
|
478
|
-
// return this.steps.filter((s) => !hasDependents.has(s.id));
|
|
479
|
-
// }
|
|
480
|
-
// }
|
|
481
|
-
//
|
|
482
|
-
// /* ------------------------------------------------ */
|
|
483
|
-
// /* WORKFLOW CREATOR */
|
|
484
|
-
// /* ------------------------------------------------ */
|
|
485
|
-
// // export function createWorkflow<
|
|
486
|
-
// // Reg extends ActionRegistry,
|
|
487
|
-
// // Context extends Record<string, any> = {},
|
|
488
|
-
// // >(registry: Reg, context?: Context) {
|
|
489
|
-
// // return function workflow<Input = unknown>(name: string) {
|
|
490
|
-
// // return new WorkflowBuilder<Reg, Input, Context>(name);
|
|
491
|
-
// // };
|
|
492
|
-
// // }
|
|
493
|
-
//
|
|
494
|
-
// export function createWorkflow<
|
|
495
|
-
// Reg extends ActionRegistry,
|
|
496
|
-
// Context extends Record<string, any> = {},
|
|
497
|
-
// >() {
|
|
498
|
-
// return function workflow<Input = unknown>(name: string) {
|
|
499
|
-
// return new WorkflowBuilder<Reg, Input, Context>(name);
|
|
500
|
-
// };
|
|
501
|
-
// }
|
|
502
|
-
//
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
//
|
|
71
|
+
import { generateWorkflowId } from "./utils.js";
|
|
506
72
|
/* ------------------------------------------------ */
|
|
507
73
|
/* FLUENT WORKFLOW BUILDER */
|
|
508
74
|
/* ------------------------------------------------ */
|
|
@@ -546,12 +112,12 @@ export class WorkflowBuilder {
|
|
|
546
112
|
branches.forEach((branch) => {
|
|
547
113
|
const b = new WorkflowBuilder(this.name);
|
|
548
114
|
b.frontier = parentFrontier;
|
|
115
|
+
b.pendingWhen = this.pendingWhen;
|
|
549
116
|
branch(b);
|
|
550
117
|
branchEnds.push(...b.frontier);
|
|
551
118
|
this.steps.push(...b.steps);
|
|
552
119
|
});
|
|
553
120
|
this.frontier = branchEnds;
|
|
554
|
-
this.clearPendingWhen();
|
|
555
121
|
return this;
|
|
556
122
|
}
|
|
557
123
|
/* ------------------------------------------------ */
|
|
@@ -567,10 +133,8 @@ export class WorkflowBuilder {
|
|
|
567
133
|
id: prefix,
|
|
568
134
|
action: "__subflow__",
|
|
569
135
|
dependsOn: [...this.frontier],
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
args: resolveInput(ctx),
|
|
573
|
-
}),
|
|
136
|
+
when: this.pendingWhen,
|
|
137
|
+
resolve: (ctx) => resolveInput(ctx),
|
|
574
138
|
options,
|
|
575
139
|
__subflowId: workflowKey, // 👈 STRING ONLY
|
|
576
140
|
});
|
|
@@ -598,6 +162,7 @@ export class WorkflowBuilder {
|
|
|
598
162
|
build() {
|
|
599
163
|
this.validateDependencies();
|
|
600
164
|
return {
|
|
165
|
+
_id: generateWorkflowId(this.name),
|
|
601
166
|
name: this.name,
|
|
602
167
|
steps: this.steps,
|
|
603
168
|
entrySteps: this.steps.filter((s) => s.dependsOn.length === 0),
|
|
@@ -625,17 +190,6 @@ export class WorkflowBuilder {
|
|
|
625
190
|
return this.steps.filter((s) => !hasDependents.has(s.id));
|
|
626
191
|
}
|
|
627
192
|
}
|
|
628
|
-
/* ------------------------------------------------ */
|
|
629
|
-
/* WORKFLOW CREATOR */
|
|
630
|
-
/* ------------------------------------------------ */
|
|
631
|
-
// export function createWorkflow<
|
|
632
|
-
// Reg extends ActionRegistry,
|
|
633
|
-
// Context extends Record<string, any> = {},
|
|
634
|
-
// >(registry: Reg, context?: Context) {
|
|
635
|
-
// return function workflow<Input = unknown>(name: string) {
|
|
636
|
-
// return new WorkflowBuilder<Reg, Input, Context>(name);
|
|
637
|
-
// };
|
|
638
|
-
// }
|
|
639
193
|
export function createWorkflow() {
|
|
640
194
|
return function workflow(name) {
|
|
641
195
|
return new WorkflowBuilder(name);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ActionRegistry, WorkflowObserver } from "./types.js";
|
|
1
|
+
import { ActionRegistry, Executor, WorkflowObserver } from "./types.js";
|
|
2
2
|
import { WorkflowDef } from "./workflow-composer.js";
|
|
3
|
-
import { WorkflowInput, WorkflowOutput } from "./workflow-module.js";
|
|
4
3
|
export declare function createCallHelpers(): {
|
|
5
4
|
args: (...args: any[]) => {
|
|
6
5
|
kind: string;
|
|
@@ -18,7 +17,14 @@ export declare function createCallHelpers(): {
|
|
|
18
17
|
items: any[];
|
|
19
18
|
};
|
|
20
19
|
};
|
|
21
|
-
export declare function executeWorkflow<Reg extends ActionRegistry,
|
|
22
|
-
|
|
20
|
+
export declare function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>({ workflow, actionRegistry, input, context, depsExecutors, observers, }: {
|
|
21
|
+
workflow: WorkflowDef<Reg, I, any, any, O>;
|
|
22
|
+
actionRegistry: Reg;
|
|
23
|
+
input: I;
|
|
24
|
+
context: any;
|
|
25
|
+
depsExecutors: Record<string, Executor>;
|
|
26
|
+
observers: WorkflowObserver<Reg>[];
|
|
27
|
+
}): Promise<{
|
|
28
|
+
output: O;
|
|
23
29
|
extras: Record<string, any>;
|
|
24
30
|
}>;
|
|
@@ -1,234 +1,3 @@
|
|
|
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
|
-
// }
|
|
232
1
|
import { composeObserver } from "./observer.js";
|
|
233
2
|
export function createCallHelpers() {
|
|
234
3
|
return {
|
|
@@ -270,11 +39,7 @@ async function runWithRetry(actionFn, stepOptions) {
|
|
|
270
39
|
}
|
|
271
40
|
throw lastError;
|
|
272
41
|
}
|
|
273
|
-
export async function executeWorkflow(
|
|
274
|
-
const workflow = workflowRegistry[workflowId];
|
|
275
|
-
if (!workflow) {
|
|
276
|
-
throw new Error(`Workflow not found: ${String(workflowId)}`);
|
|
277
|
-
}
|
|
42
|
+
export async function executeWorkflow({ workflow, actionRegistry, input, context, depsExecutors, observers = [], }) {
|
|
278
43
|
const results = {};
|
|
279
44
|
const extras = {};
|
|
280
45
|
extras.frames = {};
|
|
@@ -340,33 +105,51 @@ export async function executeWorkflow(workflowId, workflowRegistry, actionRegist
|
|
|
340
105
|
extras,
|
|
341
106
|
frame,
|
|
342
107
|
};
|
|
108
|
+
const stepCtx = {
|
|
109
|
+
input,
|
|
110
|
+
results,
|
|
111
|
+
context,
|
|
112
|
+
...createCallHelpers(),
|
|
113
|
+
};
|
|
114
|
+
const resolvedArgs = step.resolve?.(stepCtx);
|
|
115
|
+
frame.input = resolvedArgs;
|
|
343
116
|
const core = async () => {
|
|
344
117
|
frame.attempts++;
|
|
345
|
-
|
|
346
|
-
//
|
|
118
|
+
// -----------------------------
|
|
119
|
+
// ✅ SINGLE when evaluation
|
|
120
|
+
// -----------------------------
|
|
347
121
|
if (step.when && !step.when(stepCtx)) {
|
|
122
|
+
frame.input = undefined; // 👈 important for observer consistency
|
|
348
123
|
frame.output = undefined;
|
|
349
124
|
frame.end = Date.now();
|
|
350
125
|
frame.skipped = true;
|
|
351
126
|
results[step.id] = undefined;
|
|
352
127
|
return undefined;
|
|
353
128
|
}
|
|
354
|
-
const resolvedArgs = step.resolve?.(stepCtx);
|
|
355
|
-
frame.input = resolvedArgs;
|
|
356
129
|
// -----------------------------
|
|
357
130
|
// Subflow handling
|
|
358
131
|
// -----------------------------
|
|
359
132
|
if (step.__subflowId) {
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
const subExecution = await
|
|
363
|
-
//
|
|
133
|
+
const [modId, subWfId] = step.__subflowId.split(".");
|
|
134
|
+
const exec = depsExecutors[modId];
|
|
135
|
+
const subExecution = await exec.run(subWfId, resolvedArgs, context, observers);
|
|
136
|
+
// const subExecution = await executeWorkflow(
|
|
137
|
+
// step.__subflowId,
|
|
138
|
+
// workflowRegistry,
|
|
139
|
+
// actionRegistry,
|
|
140
|
+
// resolvedArgs as any,
|
|
141
|
+
// context,
|
|
142
|
+
// observers,
|
|
143
|
+
// );
|
|
364
144
|
frame.output = subExecution.output;
|
|
145
|
+
frame.end = Date.now();
|
|
365
146
|
results[step.id] = subExecution.output;
|
|
366
|
-
// Merge extras
|
|
367
147
|
Object.assign(extras, subExecution.extras);
|
|
368
148
|
return subExecution.output;
|
|
369
149
|
}
|
|
150
|
+
// -----------------------------
|
|
151
|
+
// Normal action
|
|
152
|
+
// -----------------------------
|
|
370
153
|
const actionFn = async () => {
|
|
371
154
|
const action = actionRegistry[step.action];
|
|
372
155
|
return await runAction(resolvedArgs, action);
|
|
@@ -411,7 +194,7 @@ export async function executeWorkflow(workflowId, workflowRegistry, actionRegist
|
|
|
411
194
|
? workflow.outputResolver({
|
|
412
195
|
input,
|
|
413
196
|
results,
|
|
414
|
-
context
|
|
197
|
+
context,
|
|
415
198
|
})
|
|
416
199
|
: results;
|
|
417
200
|
return {
|
|
@@ -1,42 +1,47 @@
|
|
|
1
|
-
import { ActionRegistry, WorkflowObserver } from "./types.js";
|
|
1
|
+
import { ActionRegistry, Executor, Simplify, WorkflowObserver } from "./types.js";
|
|
2
2
|
import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
|
|
3
3
|
type UnionToIntersection<U> = (U extends any ? (x: U) => any : never) extends (x: infer I) => any ? I : never;
|
|
4
|
-
type
|
|
5
|
-
|
|
4
|
+
type EnsureWorkflowShape<T> = {
|
|
5
|
+
[K in keyof T]: T[K] extends WorkflowDef<any, any, any, any, any> ? T[K] : never;
|
|
6
|
+
};
|
|
7
|
+
type DepWorkflows<Deps extends ModuleMap> = keyof Deps extends never ? {} : Simplify<EnsureWorkflowShape<UnionToIntersection<{
|
|
6
8
|
[D in keyof Deps & string]: {
|
|
7
9
|
[K in keyof Deps[D]["workflows"] & string as `${D}.${K}`]: Deps[D]["workflows"][K];
|
|
8
10
|
};
|
|
9
|
-
}[keyof Deps & string]
|
|
11
|
+
}[keyof Deps & string]>>>;
|
|
10
12
|
type WorkflowRegistry<Own extends ModuleShape, Deps extends ModuleMap> = Own & DepWorkflows<Deps>;
|
|
11
13
|
type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
|
|
12
14
|
type ModuleShape = Record<string, AnyWorkflow>;
|
|
13
15
|
type ModuleMap = Record<string, Module<any, any, any, any>>;
|
|
14
|
-
type
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
type FinalContext<Context extends Record<string, any>, Deps extends ModuleMap> = Context & ContextFromDepsRecursive<Deps>;
|
|
17
|
+
type ContextFromDepsRecursive<Deps extends ModuleMap> = [keyof Deps] extends [
|
|
18
|
+
never
|
|
19
|
+
] ? {} : UnionToIntersection<{
|
|
20
|
+
[K in keyof Deps]: Deps[K] extends Module<any, infer Ctx, any, infer SubDeps> ? Ctx & ContextFromDepsRecursive<SubDeps> : never;
|
|
21
|
+
}[keyof Deps]>;
|
|
18
22
|
export type WorkflowInput<W> = W extends WorkflowDef<any, infer I, any, any, any> ? I : never;
|
|
19
23
|
export type WorkflowResults<W> = W extends WorkflowDef<any, any, infer R, any, any> ? R : never;
|
|
20
24
|
export type WorkflowOutput<W> = W extends WorkflowDef<any, any, any, any, infer O> ? O : never;
|
|
21
25
|
type Module<Reg extends ActionRegistry, Context extends Record<string, any>, Own extends ModuleShape, Deps extends ModuleMap> = {
|
|
22
26
|
workflows: Own;
|
|
27
|
+
__getExecutor: () => Executor;
|
|
23
28
|
createRuntime: (config: {
|
|
24
|
-
|
|
25
|
-
context: FinalContext<Reg, Context, Deps>;
|
|
29
|
+
context: FinalContext<Context, Deps>;
|
|
26
30
|
}) => {
|
|
27
31
|
run: <K extends keyof WorkflowRegistry<Own, Deps>>(workflow: K, input: WorkflowInput<WorkflowRegistry<Own, Deps>[K]>, observers?: WorkflowObserver<Reg>[]) => Promise<{
|
|
28
32
|
output: WorkflowOutput<WorkflowRegistry<Own, Deps>[K]>;
|
|
29
33
|
extras: Record<string, any>;
|
|
30
34
|
}>;
|
|
31
|
-
getContext: () => FinalContext<
|
|
35
|
+
getContext: () => FinalContext<Context, Deps>;
|
|
32
36
|
};
|
|
33
37
|
};
|
|
34
|
-
|
|
38
|
+
type ModuleContext<Reg extends ActionRegistry, WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, Context extends Record<string, any>> = {
|
|
35
39
|
wf: ReturnType<typeof createWorkflow<Reg, WFReg, Context>>;
|
|
36
40
|
context: Context;
|
|
37
41
|
};
|
|
38
|
-
export declare function createModuleFactory<
|
|
42
|
+
export declare function createModuleFactory<Context extends Record<string, any>>(): <Reg extends ActionRegistry = Record<string, any>, Use extends ModuleMap = {}, Own extends ModuleShape = {}>(config: {
|
|
43
|
+
actionRegistry: Reg;
|
|
39
44
|
use?: Use;
|
|
40
|
-
define: (ctx: ModuleContext<
|
|
45
|
+
define: (ctx: ModuleContext<typeof config.actionRegistry, DepWorkflows<Use>, Context>) => Own;
|
|
41
46
|
}) => Module<Reg, Context, Own, Use>;
|
|
42
47
|
export {};
|
package/dist/workflow-module.js
CHANGED
|
@@ -1,174 +1,50 @@
|
|
|
1
|
-
// import { ActionRegistry, Simplify, WorkflowObserver } from "./types.js";
|
|
2
|
-
// import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
|
|
3
|
-
// import { executeWorkflow } from "./workflow-executor.js";
|
|
4
|
-
//
|
|
5
|
-
// type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
|
|
6
|
-
//
|
|
7
|
-
// type WorkflowFromDeps<Deps extends Record<string, any>> = {
|
|
8
|
-
// [K in keyof Deps]: Deps[K] extends Module<any, any, infer Own, infer SubDeps>
|
|
9
|
-
// ? Own[keyof Own] | WorkflowFromDeps<SubDeps>
|
|
10
|
-
// : never;
|
|
11
|
-
// }[keyof Deps];
|
|
12
|
-
// type ModuleShape = Record<string, AnyWorkflow>;
|
|
13
|
-
//
|
|
14
|
-
// type ContextFromDeps<Deps> = [keyof Deps] extends [never]
|
|
15
|
-
// ? {} // 👈 THIS is the fix
|
|
16
|
-
// : {
|
|
17
|
-
// [K in keyof Deps]: Deps[K] extends Module<any, infer Ctx, any, any>
|
|
18
|
-
// ? Ctx
|
|
19
|
-
// : never;
|
|
20
|
-
// }[keyof Deps];
|
|
21
|
-
//
|
|
22
|
-
// type FinalContext<
|
|
23
|
-
// Reg extends ActionRegistry,
|
|
24
|
-
// Context extends Record<string, any>,
|
|
25
|
-
// Deps extends ModuleMap<Reg>,
|
|
26
|
-
// > = Context & ContextFromDeps<Deps>;
|
|
27
|
-
//
|
|
28
|
-
// type ModuleMap<Reg extends ActionRegistry> = Record<
|
|
29
|
-
// string,
|
|
30
|
-
// Module<Reg, any, any, any>
|
|
31
|
-
// >;
|
|
32
|
-
//
|
|
33
|
-
// // type Module<Own extends ModuleShape = {}, Deps extends ModuleMap = {}> = Own & {
|
|
34
|
-
// // deps: Deps;
|
|
35
|
-
// // };
|
|
36
|
-
// export type WorkflowInput<W> =
|
|
37
|
-
// W extends WorkflowDef<any, infer I, any, any, any> ? I : never;
|
|
38
|
-
//
|
|
39
|
-
// export type WorkflowResults<W> =
|
|
40
|
-
// W extends WorkflowDef<any, any, infer R, any, any> ? R : never;
|
|
41
|
-
//
|
|
42
|
-
// export type WorkflowOutput<W> =
|
|
43
|
-
// W extends WorkflowDef<any, any, any, any, infer O> ? O : never;
|
|
44
|
-
// type Module<
|
|
45
|
-
// Reg extends ActionRegistry,
|
|
46
|
-
// Context extends Record<string, any>,
|
|
47
|
-
// Own extends ModuleShape = {},
|
|
48
|
-
// Deps extends ModuleMap<Reg> = {},
|
|
49
|
-
// > = {
|
|
50
|
-
// own: Own;
|
|
51
|
-
// deps: Deps;
|
|
52
|
-
// createRuntime: (config: {
|
|
53
|
-
// registry: Reg;
|
|
54
|
-
// context: FinalContext<Reg, Context, Deps>;
|
|
55
|
-
// }) => {
|
|
56
|
-
// run: <W extends Own[keyof Own] | WorkflowFromDeps<Deps>>(
|
|
57
|
-
// workflow: W,
|
|
58
|
-
// input: WorkflowInput<W>,
|
|
59
|
-
// obververs?: WorkflowObserver<Reg>[],
|
|
60
|
-
// ) => Promise<{
|
|
61
|
-
// results: WorkflowResults<W>;
|
|
62
|
-
// output: WorkflowOutput<W>;
|
|
63
|
-
// extras: Record<string, any>;
|
|
64
|
-
// }>;
|
|
65
|
-
// getContext: () => FinalContext<Reg, Context, Deps>;
|
|
66
|
-
// };
|
|
67
|
-
// };
|
|
68
|
-
//
|
|
69
|
-
// export type ModuleContext<
|
|
70
|
-
// Reg extends ActionRegistry,
|
|
71
|
-
// Context extends Record<string, any>,
|
|
72
|
-
// Deps extends ModuleMap<Reg>,
|
|
73
|
-
// > = {
|
|
74
|
-
// wf: ReturnType<typeof createWorkflow<Reg, Context>>;
|
|
75
|
-
//
|
|
76
|
-
// deps: Deps;
|
|
77
|
-
// context: Context;
|
|
78
|
-
//
|
|
79
|
-
// tools: <T>(
|
|
80
|
-
// factory: (ctx: {
|
|
81
|
-
// wf: ModuleContext<Reg, Context, Deps>["wf"];
|
|
82
|
-
// deps: Deps;
|
|
83
|
-
// context: Context;
|
|
84
|
-
// }) => T,
|
|
85
|
-
// ) => T;
|
|
86
|
-
// };
|
|
87
|
-
//
|
|
88
|
-
// function createModule<
|
|
89
|
-
// Reg extends ActionRegistry,
|
|
90
|
-
// Context extends Record<string, any>,
|
|
91
|
-
// Use extends ModuleMap<Reg>,
|
|
92
|
-
// Own extends ModuleShape,
|
|
93
|
-
// >(config: {
|
|
94
|
-
// use?: Use;
|
|
95
|
-
// define: (ctx: ModuleContext<Reg, Context, Use>) => Own;
|
|
96
|
-
// }): Module<Reg, Context, Own, Use> {
|
|
97
|
-
// const wf = createWorkflow<Reg, Context>();
|
|
98
|
-
//
|
|
99
|
-
// const deps = (config.use ?? {}) as Use;
|
|
100
|
-
//
|
|
101
|
-
// const moduleCtx: ModuleContext<Reg, Context, Use> = {
|
|
102
|
-
// wf,
|
|
103
|
-
// deps,
|
|
104
|
-
// context: {} as Context,
|
|
105
|
-
//
|
|
106
|
-
// tools: (factory) =>
|
|
107
|
-
// factory({
|
|
108
|
-
// wf,
|
|
109
|
-
// deps,
|
|
110
|
-
// context: {} as Context,
|
|
111
|
-
// }),
|
|
112
|
-
// };
|
|
113
|
-
//
|
|
114
|
-
// const own = config.define(moduleCtx);
|
|
115
|
-
//
|
|
116
|
-
// return {
|
|
117
|
-
// own,
|
|
118
|
-
// deps,
|
|
119
|
-
// createRuntime({ registry, context }) {
|
|
120
|
-
// const runtimeCtx = { ...context } as FinalContext<Reg, Context, Use>;
|
|
121
|
-
// return {
|
|
122
|
-
// run: async (workflow, input, observers = []) => {
|
|
123
|
-
// return executeWorkflow(workflow, registry, input, context, observers);
|
|
124
|
-
// },
|
|
125
|
-
// getContext: () => ({ ...runtimeCtx }),
|
|
126
|
-
// };
|
|
127
|
-
// },
|
|
128
|
-
// };
|
|
129
|
-
// }
|
|
130
|
-
//
|
|
131
|
-
// export function createModuleFactory<
|
|
132
|
-
// Reg extends ActionRegistry,
|
|
133
|
-
// Context extends Record<string, any>,
|
|
134
|
-
// >() {
|
|
135
|
-
// return function <
|
|
136
|
-
// Use extends ModuleMap<Reg> = {},
|
|
137
|
-
// Own extends ModuleShape = {},
|
|
138
|
-
// >(config: {
|
|
139
|
-
// use?: Use;
|
|
140
|
-
// define: (ctx: ModuleContext<Reg, Context, Use>) => Own;
|
|
141
|
-
// }): Module<Reg, Context, Own, Use> {
|
|
142
|
-
// return createModule<Reg, Context, Use, Own>(config);
|
|
143
|
-
// };
|
|
144
|
-
// }
|
|
145
|
-
//
|
|
146
1
|
import { createWorkflow } from "./workflow-composer.js";
|
|
147
2
|
import { executeWorkflow } from "./workflow-executor.js";
|
|
148
|
-
/* ------------------------------------------------ */
|
|
149
|
-
/* CREATE MODULE (FIXED) */
|
|
150
|
-
/* ------------------------------------------------ */
|
|
151
3
|
function createModule(config) {
|
|
152
4
|
const deps = (config.use ?? {});
|
|
153
|
-
// ✅ CRITICAL: wf only sees DEP workflows
|
|
154
5
|
const wf = createWorkflow();
|
|
155
6
|
const own = config.define({
|
|
156
7
|
wf,
|
|
157
8
|
context: {},
|
|
158
9
|
});
|
|
159
|
-
function
|
|
10
|
+
function buildWorkflowMap() {
|
|
160
11
|
const depWFs = Object.fromEntries(Object.entries(deps).flatMap(([name, mod]) => Object.entries(mod.workflows).map(([k, wf]) => [`${name}.${k}`, wf])));
|
|
161
12
|
return { ...own, ...depWFs };
|
|
162
13
|
}
|
|
14
|
+
const workflowMap = buildWorkflowMap();
|
|
15
|
+
const depsExecutors = Object.fromEntries(Object.entries(deps).map(([name, mod]) => [name, mod.__getExecutor()]));
|
|
16
|
+
const executor = {
|
|
17
|
+
run(wfId, input, context, observers = []) {
|
|
18
|
+
const workflow = workflowMap[wfId];
|
|
19
|
+
if (!workflow) {
|
|
20
|
+
throw new Error(`Workflow not found: ${String(wfId)}`);
|
|
21
|
+
}
|
|
22
|
+
return executeWorkflow({
|
|
23
|
+
workflow,
|
|
24
|
+
actionRegistry: config.actionRegistry,
|
|
25
|
+
depsExecutors,
|
|
26
|
+
input,
|
|
27
|
+
context,
|
|
28
|
+
observers,
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
};
|
|
163
32
|
return {
|
|
164
33
|
workflows: own,
|
|
165
|
-
|
|
166
|
-
|
|
34
|
+
__getExecutor: () => executor,
|
|
35
|
+
createRuntime({ context }) {
|
|
36
|
+
let runtimeActions = config.actionRegistry;
|
|
167
37
|
return {
|
|
168
38
|
run: async (workflowId, input, observers = []) => {
|
|
169
|
-
return
|
|
39
|
+
return executor.run(workflowId, input, context, observers);
|
|
170
40
|
},
|
|
171
41
|
getContext: () => ({ ...context }),
|
|
42
|
+
setActionRegistry(reg) {
|
|
43
|
+
runtimeActions = reg;
|
|
44
|
+
// ⚠️ optional: if you REALLY want override, you'd need:
|
|
45
|
+
// executor.actions = reg
|
|
46
|
+
// but better keep actions immutable
|
|
47
|
+
},
|
|
172
48
|
};
|
|
173
49
|
},
|
|
174
50
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pogodisco/zephyr",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
"prepublishOnly": "npm run build",
|
|
34
34
|
"build": "tsc -p tsconfig.build.json",
|
|
35
35
|
"dev": "tsc -w -p tsconfig.json",
|
|
36
|
+
"test": "vitest",
|
|
37
|
+
"test:run": "vitest run",
|
|
36
38
|
"test:types": "tsd"
|
|
37
39
|
},
|
|
38
40
|
"tsd": {
|
|
@@ -45,6 +47,7 @@
|
|
|
45
47
|
"@types/node": "^25.2.3",
|
|
46
48
|
"chalk": "^5.6.2",
|
|
47
49
|
"pretty-format": "^30.2.0",
|
|
48
|
-
"tsd": "^0.33.0"
|
|
50
|
+
"tsd": "^0.33.0",
|
|
51
|
+
"vitest": "^4.1.2"
|
|
49
52
|
}
|
|
50
53
|
}
|