@pogodisco/zephyr 1.3.4 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/registry.js +0 -40
- 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/utils.js +0 -41
- package/dist/workflow-composer.d.ts +56 -23
- package/dist/workflow-composer.js +230 -350
- package/dist/workflow-executor.d.ts +3 -3
- package/dist/workflow-executor.js +44 -224
- package/dist/workflow-module.d.ts +11 -6
- package/dist/workflow-module.js +8 -38
- package/package.json +1 -1
|
@@ -1,9 +1,37 @@
|
|
|
1
|
-
// import { ActionRegistry } from "
|
|
1
|
+
// import { ActionRegistry, ActionReturn } from "./types.js";
|
|
2
2
|
//
|
|
3
|
-
//
|
|
3
|
+
// /* ------------------------------------------------ */
|
|
4
|
+
// /* STEP INPUT NORMALIZATION TYPES */
|
|
5
|
+
// /* ------------------------------------------------ */
|
|
6
|
+
//
|
|
7
|
+
// export type NormalizedCall =
|
|
8
|
+
// | { kind: "none" }
|
|
9
|
+
// | { kind: "positional"; args: any[] }
|
|
10
|
+
// | { kind: "object"; args: any };
|
|
11
|
+
//
|
|
12
|
+
// export type ResolvedStepInput =
|
|
13
|
+
// | NormalizedCall
|
|
14
|
+
// | { kind: "loop"; items: NormalizedCall[] };
|
|
15
|
+
//
|
|
16
|
+
// /* ------------------------------------------------ */
|
|
17
|
+
// /* STEP RESULT TYPES */
|
|
18
|
+
// /* ------------------------------------------------ */
|
|
19
|
+
//
|
|
20
|
+
// type SubflowResult<SubResults, SubOutput> = SubOutput extends undefined
|
|
21
|
+
// ? SubResults
|
|
22
|
+
// : SubOutput;
|
|
23
|
+
//
|
|
24
|
+
// export type StepResult<
|
|
4
25
|
// Reg extends ActionRegistry,
|
|
5
26
|
// ActionName extends keyof Reg,
|
|
6
|
-
//
|
|
27
|
+
// Loop extends boolean | undefined = undefined,
|
|
28
|
+
// > = Loop extends true
|
|
29
|
+
// ? ActionReturn<Reg, ActionName>[]
|
|
30
|
+
// : ActionReturn<Reg, ActionName>;
|
|
31
|
+
//
|
|
32
|
+
// /* ------------------------------------------------ */
|
|
33
|
+
// /* STEP DEFINITION */
|
|
34
|
+
// /* ------------------------------------------------ */
|
|
7
35
|
//
|
|
8
36
|
// export type StepDef<
|
|
9
37
|
// Reg extends ActionRegistry,
|
|
@@ -13,15 +41,21 @@
|
|
|
13
41
|
// id: ID;
|
|
14
42
|
// action: ActionName;
|
|
15
43
|
// dependsOn: string[];
|
|
16
|
-
// resolve: (ctx: any) =>
|
|
44
|
+
// resolve: (ctx: any) => ResolvedStepInput;
|
|
17
45
|
// when?: (ctx: any) => boolean;
|
|
46
|
+
// loop?: boolean;
|
|
18
47
|
// };
|
|
19
48
|
//
|
|
49
|
+
// /* ------------------------------------------------ */
|
|
50
|
+
// /* WORKFLOW DEFINITION */
|
|
51
|
+
// /* ------------------------------------------------ */
|
|
52
|
+
//
|
|
20
53
|
// export type WorkflowDef<
|
|
21
54
|
// Reg extends ActionRegistry,
|
|
22
55
|
// Input,
|
|
23
56
|
// Results,
|
|
24
57
|
// Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[],
|
|
58
|
+
// Output = undefined,
|
|
25
59
|
// > = {
|
|
26
60
|
// name: string;
|
|
27
61
|
// steps: Steps;
|
|
@@ -29,53 +63,74 @@
|
|
|
29
63
|
// endSteps: StepDef<Reg>[];
|
|
30
64
|
// input: Input;
|
|
31
65
|
// results: Results;
|
|
66
|
+
// outputResolver?: (ctx: any) => Output;
|
|
67
|
+
// __context?: any;
|
|
32
68
|
// };
|
|
33
69
|
//
|
|
70
|
+
// /* ------------------------------------------------ */
|
|
71
|
+
// /* HELPER TYPES */
|
|
72
|
+
// /* ------------------------------------------------ */
|
|
73
|
+
//
|
|
34
74
|
// type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
35
75
|
// k: infer I,
|
|
36
76
|
// ) => void
|
|
37
77
|
// ? I
|
|
38
78
|
// : never;
|
|
39
79
|
//
|
|
80
|
+
// /* ------------------------------------------------ */
|
|
81
|
+
// /* FLUENT WORKFLOW BUILDER */
|
|
82
|
+
// /* ------------------------------------------------ */
|
|
83
|
+
//
|
|
40
84
|
// export class WorkflowBuilder<
|
|
41
85
|
// Reg extends ActionRegistry,
|
|
42
86
|
// Input = unknown,
|
|
87
|
+
// Context extends Record<string, any> = {},
|
|
43
88
|
// Steps extends StepDef<Reg, any, any>[] = [],
|
|
44
89
|
// Results = {},
|
|
90
|
+
// Output = undefined,
|
|
45
91
|
// > {
|
|
46
92
|
// private steps: StepDef<Reg, any, any>[] = [];
|
|
47
93
|
// private frontier: string[] = [];
|
|
94
|
+
// private outputResolver?: (ctx: any) => any;
|
|
48
95
|
//
|
|
49
96
|
// constructor(
|
|
50
97
|
// private name: string,
|
|
51
98
|
// private registry: Reg,
|
|
99
|
+
// private context: Context,
|
|
52
100
|
// ) {}
|
|
53
101
|
//
|
|
54
102
|
// /* ------------------------------------------------ */
|
|
55
|
-
// /* Base Step
|
|
103
|
+
// /* Base Step */
|
|
56
104
|
// /* ------------------------------------------------ */
|
|
57
|
-
//
|
|
58
|
-
//
|
|
105
|
+
// step<
|
|
106
|
+
// ID extends string,
|
|
107
|
+
// ActionName extends keyof Reg & string,
|
|
108
|
+
// Loop extends boolean | undefined = undefined,
|
|
109
|
+
// >(
|
|
59
110
|
// id: ID,
|
|
60
111
|
// action: ActionName,
|
|
61
|
-
// resolve
|
|
112
|
+
// resolve?: (ctx: {
|
|
62
113
|
// input: Input;
|
|
63
114
|
// results: Results;
|
|
64
|
-
//
|
|
115
|
+
// context: Context;
|
|
116
|
+
// }) => ResolvedStepInput,
|
|
65
117
|
// dependsOn?: string[],
|
|
118
|
+
// options?: { loop?: Loop },
|
|
66
119
|
// ): WorkflowBuilder<
|
|
67
120
|
// Reg,
|
|
68
121
|
// Input,
|
|
122
|
+
// Context,
|
|
69
123
|
// [...Steps, StepDef<Reg, ID, ActionName>],
|
|
70
|
-
// Results & { [K in ID]: StepResult<Reg, ActionName> }
|
|
124
|
+
// Results & { [K in ID]: StepResult<Reg, ActionName, Loop> }
|
|
71
125
|
// > {
|
|
72
126
|
// const deps = dependsOn ?? [...this.frontier];
|
|
73
127
|
//
|
|
74
128
|
// this.steps.push({
|
|
75
129
|
// id,
|
|
76
130
|
// action,
|
|
77
|
-
// resolve,
|
|
131
|
+
// resolve: resolve ?? (() => ({ kind: "none" })),
|
|
78
132
|
// dependsOn: deps,
|
|
133
|
+
// loop: options?.loop ?? false,
|
|
79
134
|
// });
|
|
80
135
|
//
|
|
81
136
|
// this.frontier = [id];
|
|
@@ -84,41 +139,42 @@
|
|
|
84
139
|
// }
|
|
85
140
|
//
|
|
86
141
|
// /* ------------------------------------------------ */
|
|
87
|
-
// /* Sequential shortcut
|
|
142
|
+
// /* Sequential shortcut */
|
|
88
143
|
// /* ------------------------------------------------ */
|
|
89
|
-
//
|
|
90
144
|
// seq<ID extends string, ActionName extends keyof Reg & string>(
|
|
91
145
|
// id: ID,
|
|
92
146
|
// action: ActionName,
|
|
93
|
-
// resolve
|
|
147
|
+
// resolve?: (ctx: {
|
|
94
148
|
// input: Input;
|
|
95
149
|
// results: Results;
|
|
96
|
-
//
|
|
150
|
+
// context: Context;
|
|
151
|
+
// }) => ResolvedStepInput,
|
|
152
|
+
// options?: { loop?: boolean },
|
|
97
153
|
// ) {
|
|
98
|
-
// return this.step(id, action, resolve);
|
|
154
|
+
// return this.step(id, action, resolve, undefined, options);
|
|
99
155
|
// }
|
|
100
156
|
//
|
|
101
157
|
// /* ------------------------------------------------ */
|
|
102
|
-
// /* Parallel branches
|
|
158
|
+
// /* Parallel branches */
|
|
103
159
|
// /* ------------------------------------------------ */
|
|
104
|
-
//
|
|
105
|
-
// parallel<Branches extends WorkflowBuilder<Reg, Input, any, any>[]>(
|
|
160
|
+
// parallel<Branches extends WorkflowBuilder<Reg, Input, Context, any, any>[]>(
|
|
106
161
|
// ...branches: {
|
|
107
162
|
// [K in keyof Branches]: (
|
|
108
|
-
// builder: WorkflowBuilder<Reg, Input, [], Results>,
|
|
163
|
+
// builder: WorkflowBuilder<Reg, Input, Context, [], Results>,
|
|
109
164
|
// ) => Branches[K];
|
|
110
165
|
// }
|
|
111
166
|
// ): WorkflowBuilder<
|
|
112
167
|
// Reg,
|
|
113
168
|
// Input,
|
|
169
|
+
// Context,
|
|
114
170
|
// [
|
|
115
171
|
// ...Steps,
|
|
116
|
-
// ...(Branches[number] extends WorkflowBuilder<Reg, any, infer S, any>
|
|
172
|
+
// ...(Branches[number] extends WorkflowBuilder<Reg, any, any, infer S, any>
|
|
117
173
|
// ? S
|
|
118
174
|
// : never),
|
|
119
175
|
// ],
|
|
120
176
|
// Results &
|
|
121
|
-
// (Branches[number] extends WorkflowBuilder<Reg, any, any, infer R>
|
|
177
|
+
// (Branches[number] extends WorkflowBuilder<Reg, any, any, any, infer R>
|
|
122
178
|
// ? UnionToIntersection<R>
|
|
123
179
|
// : {})
|
|
124
180
|
// > {
|
|
@@ -126,58 +182,62 @@
|
|
|
126
182
|
// const branchEnds: string[] = [];
|
|
127
183
|
//
|
|
128
184
|
// branches.forEach((branch) => {
|
|
129
|
-
// const b = new WorkflowBuilder<Reg, Input, [], Results>(
|
|
185
|
+
// const b = new WorkflowBuilder<Reg, Input, Context, [], Results>(
|
|
130
186
|
// this.name,
|
|
131
187
|
// this.registry,
|
|
188
|
+
// this.context,
|
|
132
189
|
// );
|
|
133
190
|
//
|
|
134
191
|
// b.frontier = parentFrontier;
|
|
135
|
-
//
|
|
136
192
|
// branch(b);
|
|
137
|
-
//
|
|
138
193
|
// branchEnds.push(...b.frontier);
|
|
139
|
-
//
|
|
140
194
|
// this.steps.push(...(b as any).steps);
|
|
141
195
|
// });
|
|
142
196
|
//
|
|
143
197
|
// this.frontier = branchEnds;
|
|
144
|
-
//
|
|
145
198
|
// return this as any;
|
|
146
199
|
// }
|
|
147
200
|
//
|
|
148
201
|
// /* ------------------------------------------------ */
|
|
149
|
-
// /* Join helper
|
|
202
|
+
// /* Join helper */
|
|
150
203
|
// /* ------------------------------------------------ */
|
|
151
|
-
//
|
|
152
204
|
// join<ID extends string, ActionName extends keyof Reg & string>(
|
|
153
205
|
// id: ID,
|
|
154
206
|
// action: ActionName,
|
|
155
|
-
// resolve
|
|
207
|
+
// resolve?: (ctx: {
|
|
156
208
|
// input: Input;
|
|
157
209
|
// results: Results;
|
|
158
|
-
//
|
|
210
|
+
// context: Context;
|
|
211
|
+
// }) => ResolvedStepInput,
|
|
212
|
+
// options?: { loop?: boolean },
|
|
159
213
|
// ) {
|
|
160
|
-
// return this.step(id, action, resolve, [...this.frontier]);
|
|
214
|
+
// return this.step(id, action, resolve, [...this.frontier], options);
|
|
161
215
|
// }
|
|
162
216
|
//
|
|
163
217
|
// /* ------------------------------------------------ */
|
|
164
|
-
// /* Subflow
|
|
218
|
+
// /* Subflow */
|
|
165
219
|
// /* ------------------------------------------------ */
|
|
166
|
-
//
|
|
167
220
|
// subflow<
|
|
168
221
|
// Prefix extends string,
|
|
169
222
|
// SubInput,
|
|
170
223
|
// SubResults,
|
|
171
224
|
// SubSteps extends StepDef<Reg, any, any>[],
|
|
225
|
+
// SubOutput,
|
|
172
226
|
// >(
|
|
173
227
|
// prefix: Prefix,
|
|
174
|
-
// workflow: WorkflowDef<Reg, SubInput, SubResults, SubSteps>,
|
|
175
|
-
// resolveInput
|
|
228
|
+
// workflow: WorkflowDef<Reg, SubInput, SubResults, SubSteps, SubOutput>,
|
|
229
|
+
// resolveInput?: (ctx: {
|
|
230
|
+
// input: Input;
|
|
231
|
+
// results: Results;
|
|
232
|
+
// context: Context;
|
|
233
|
+
// }) => SubInput,
|
|
234
|
+
// options?: { loop?: boolean },
|
|
176
235
|
// ): WorkflowBuilder<
|
|
177
236
|
// Reg,
|
|
178
237
|
// Input,
|
|
238
|
+
// Context,
|
|
179
239
|
// [...Steps, ...SubSteps],
|
|
180
|
-
// Results & { [K in Prefix]: SubResults }
|
|
240
|
+
// Results & { [K in Prefix]: SubflowResult<SubResults, SubOutput> }
|
|
181
241
|
// > {
|
|
182
242
|
// const idMap = new Map<string, string>();
|
|
183
243
|
//
|
|
@@ -186,21 +246,19 @@
|
|
|
186
246
|
// });
|
|
187
247
|
//
|
|
188
248
|
// workflow.steps.forEach((step) => {
|
|
189
|
-
// const newStep = {
|
|
249
|
+
// const newStep: StepDef<Reg, any, any> = {
|
|
190
250
|
// ...step,
|
|
191
|
-
//
|
|
192
251
|
// id: idMap.get(step.id)!,
|
|
193
|
-
//
|
|
194
252
|
// dependsOn: step.dependsOn.map((d) => idMap.get(d)!),
|
|
195
|
-
//
|
|
196
253
|
// resolve: (ctx: any) => {
|
|
197
|
-
// const subInput = resolveInput(ctx);
|
|
198
|
-
//
|
|
254
|
+
// const subInput = resolveInput ? resolveInput(ctx) : undefined;
|
|
199
255
|
// return step.resolve({
|
|
200
256
|
// input: subInput,
|
|
201
257
|
// results: ctx.results,
|
|
258
|
+
// context: ctx.context,
|
|
202
259
|
// });
|
|
203
260
|
// },
|
|
261
|
+
// ...(options ? { loop: options.loop ?? step.loop } : {}),
|
|
204
262
|
// };
|
|
205
263
|
//
|
|
206
264
|
// if (workflow.entrySteps.find((e) => e.id === step.id)) {
|
|
@@ -211,299 +269,35 @@
|
|
|
211
269
|
// });
|
|
212
270
|
//
|
|
213
271
|
// this.frontier = workflow.endSteps.map((e) => idMap.get(e.id)!);
|
|
214
|
-
//
|
|
215
272
|
// return this as any;
|
|
216
273
|
// }
|
|
217
274
|
//
|
|
218
275
|
// /* ------------------------------------------------ */
|
|
219
|
-
// /*
|
|
220
|
-
// /* ------------------------------------------------ */
|
|
221
|
-
//
|
|
222
|
-
// build(): WorkflowDef<Reg, Input, Results, Steps> {
|
|
223
|
-
// this.validateDependencies();
|
|
224
|
-
//
|
|
225
|
-
// return {
|
|
226
|
-
// name: this.name,
|
|
227
|
-
//
|
|
228
|
-
// steps: this.steps as Steps,
|
|
229
|
-
//
|
|
230
|
-
// entrySteps: this.steps.filter((s) => s.dependsOn.length === 0),
|
|
231
|
-
//
|
|
232
|
-
// endSteps: this.getEndSteps(),
|
|
233
|
-
//
|
|
234
|
-
// input: {} as Input,
|
|
235
|
-
//
|
|
236
|
-
// results: {} as Results,
|
|
237
|
-
// };
|
|
238
|
-
// }
|
|
239
|
-
//
|
|
240
|
-
// /* ------------------------------------------------ */
|
|
241
|
-
//
|
|
242
|
-
// private validateDependencies() {
|
|
243
|
-
// const stepIds = new Set(this.steps.map((s) => s.id));
|
|
244
|
-
//
|
|
245
|
-
// for (const step of this.steps) {
|
|
246
|
-
// for (const dep of step.dependsOn) {
|
|
247
|
-
// if (!stepIds.has(dep)) {
|
|
248
|
-
// throw new Error(`Step ${step.id} depends on unknown step ${dep}`);
|
|
249
|
-
// }
|
|
250
|
-
// }
|
|
251
|
-
// }
|
|
252
|
-
// }
|
|
253
|
-
//
|
|
254
|
-
// private getEndSteps() {
|
|
255
|
-
// const hasDependents = new Set<string>();
|
|
256
|
-
//
|
|
257
|
-
// for (const step of this.steps) {
|
|
258
|
-
// for (const dep of step.dependsOn) {
|
|
259
|
-
// hasDependents.add(dep);
|
|
260
|
-
// }
|
|
261
|
-
// }
|
|
262
|
-
//
|
|
263
|
-
// return this.steps.filter((s) => !hasDependents.has(s.id));
|
|
264
|
-
// }
|
|
265
|
-
// }
|
|
266
|
-
//
|
|
267
|
-
// // export function createWorkflow<Reg extends ActionRegistry, Input = unknown>(
|
|
268
|
-
// // registry: Reg,
|
|
269
|
-
// // ) {
|
|
270
|
-
// // return (name: string) => new WorkflowBuilder<Reg, Input>(name, registry);
|
|
271
|
-
// // }
|
|
272
|
-
//
|
|
273
|
-
// export function createWorkflow<Reg extends ActionRegistry>(registry: Reg) {
|
|
274
|
-
// return function workflow<Input = unknown>(name: string) {
|
|
275
|
-
// return new WorkflowBuilder<Reg, Input>(name, registry);
|
|
276
|
-
// };
|
|
277
|
-
// }
|
|
278
|
-
//
|
|
279
|
-
//////////////////////////////////////////////////////////////
|
|
280
|
-
// import { ActionRegistry } from "./types.js";
|
|
281
|
-
//
|
|
282
|
-
// type StepResult<
|
|
283
|
-
// Reg extends ActionRegistry,
|
|
284
|
-
// ActionName extends keyof Reg,
|
|
285
|
-
// > = Awaited<ReturnType<Reg[ActionName]>>;
|
|
286
|
-
//
|
|
287
|
-
// export type StepDef<
|
|
288
|
-
// Reg extends ActionRegistry,
|
|
289
|
-
// ID extends string = string,
|
|
290
|
-
// ActionName extends keyof Reg = any,
|
|
291
|
-
// > = {
|
|
292
|
-
// id: ID;
|
|
293
|
-
// action: ActionName;
|
|
294
|
-
// dependsOn: string[];
|
|
295
|
-
// resolve: (ctx: any) => Parameters<Reg[ActionName]>[0];
|
|
296
|
-
// when?: (ctx: any) => boolean;
|
|
297
|
-
// };
|
|
298
|
-
//
|
|
299
|
-
// export type WorkflowDef<
|
|
300
|
-
// Reg extends ActionRegistry,
|
|
301
|
-
// Input,
|
|
302
|
-
// Results,
|
|
303
|
-
// Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[],
|
|
304
|
-
// Output = undefined,
|
|
305
|
-
// > = {
|
|
306
|
-
// name: string;
|
|
307
|
-
// steps: Steps;
|
|
308
|
-
// entrySteps: StepDef<Reg>[];
|
|
309
|
-
// endSteps: StepDef<Reg>[];
|
|
310
|
-
// input: Input;
|
|
311
|
-
// results: Results;
|
|
312
|
-
// outputResolver?: (ctx: any) => Output;
|
|
313
|
-
// };
|
|
314
|
-
//
|
|
315
|
-
// type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
316
|
-
// k: infer I,
|
|
317
|
-
// ) => void
|
|
318
|
-
// ? I
|
|
319
|
-
// : never;
|
|
320
|
-
//
|
|
321
|
-
// export class WorkflowBuilder<
|
|
322
|
-
// Reg extends ActionRegistry,
|
|
323
|
-
// Input = unknown,
|
|
324
|
-
// Steps extends StepDef<Reg, any, any>[] = [],
|
|
325
|
-
// Results = {},
|
|
326
|
-
// > {
|
|
327
|
-
// private steps: StepDef<Reg, any, any>[] = [];
|
|
328
|
-
// private frontier: string[] = [];
|
|
329
|
-
//
|
|
330
|
-
// private outputResolver?: (ctx: any) => any;
|
|
331
|
-
// constructor(
|
|
332
|
-
// private name: string,
|
|
333
|
-
// private registry: Reg,
|
|
334
|
-
// ) {}
|
|
335
|
-
//
|
|
336
|
-
// /* ------------------------------------------------ */
|
|
337
|
-
// /* Base Step */
|
|
276
|
+
// /* Conditional */
|
|
338
277
|
// /* ------------------------------------------------ */
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
// id: ID,
|
|
342
|
-
// action: ActionName,
|
|
343
|
-
// resolve: (ctx: {
|
|
278
|
+
// when(
|
|
279
|
+
// predicate: (ctx: {
|
|
344
280
|
// input: Input;
|
|
345
281
|
// results: Results;
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
// ):
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
// > {
|
|
354
|
-
// const deps = dependsOn ?? [...this.frontier];
|
|
355
|
-
//
|
|
356
|
-
// this.steps.push({
|
|
357
|
-
// id,
|
|
358
|
-
// action,
|
|
359
|
-
// resolve,
|
|
360
|
-
// dependsOn: deps,
|
|
361
|
-
// });
|
|
362
|
-
//
|
|
363
|
-
// this.frontier = [id];
|
|
364
|
-
//
|
|
365
|
-
// return this as any;
|
|
366
|
-
// }
|
|
367
|
-
//
|
|
368
|
-
// /* ------------------------------------------------ */
|
|
369
|
-
// /* Sequential shortcut */
|
|
370
|
-
// /* ------------------------------------------------ */
|
|
371
|
-
//
|
|
372
|
-
// seq<ID extends string, ActionName extends keyof Reg & string>(
|
|
373
|
-
// id: ID,
|
|
374
|
-
// action: ActionName,
|
|
375
|
-
// resolve: (ctx: {
|
|
376
|
-
// input: Input;
|
|
377
|
-
// results: Results;
|
|
378
|
-
// }) => Parameters<Reg[ActionName]>[0],
|
|
379
|
-
// ) {
|
|
380
|
-
// return this.step(id, action, resolve);
|
|
381
|
-
// }
|
|
382
|
-
//
|
|
383
|
-
// /* ------------------------------------------------ */
|
|
384
|
-
// /* Parallel branches */
|
|
385
|
-
// parallel<Branches extends WorkflowBuilder<Reg, Input, any, any>[]>(
|
|
386
|
-
// ...branches: {
|
|
387
|
-
// [K in keyof Branches]: (
|
|
388
|
-
// builder: WorkflowBuilder<Reg, Input, [], Results>,
|
|
389
|
-
// ) => Branches[K];
|
|
390
|
-
// }
|
|
391
|
-
// ): WorkflowBuilder<
|
|
392
|
-
// Reg,
|
|
393
|
-
// Input,
|
|
394
|
-
// [
|
|
395
|
-
// ...Steps,
|
|
396
|
-
// ...(Branches[number] extends WorkflowBuilder<Reg, any, infer S, any>
|
|
397
|
-
// ? S
|
|
398
|
-
// : never),
|
|
399
|
-
// ],
|
|
400
|
-
// Results &
|
|
401
|
-
// (Branches[number] extends WorkflowBuilder<Reg, any, any, infer R>
|
|
402
|
-
// ? UnionToIntersection<R>
|
|
403
|
-
// : {})
|
|
404
|
-
// > {
|
|
405
|
-
// const parentFrontier = [...this.frontier];
|
|
406
|
-
// const branchEnds: string[] = [];
|
|
407
|
-
//
|
|
408
|
-
// branches.forEach((branch) => {
|
|
409
|
-
// const b = new WorkflowBuilder<Reg, Input, [], Results>(
|
|
410
|
-
// this.name,
|
|
411
|
-
// this.registry,
|
|
412
|
-
// );
|
|
413
|
-
//
|
|
414
|
-
// b.frontier = parentFrontier;
|
|
415
|
-
//
|
|
416
|
-
// branch(b);
|
|
417
|
-
//
|
|
418
|
-
// branchEnds.push(...b.frontier);
|
|
419
|
-
//
|
|
420
|
-
// this.steps.push(...(b as any).steps);
|
|
421
|
-
// });
|
|
422
|
-
//
|
|
423
|
-
// this.frontier = branchEnds;
|
|
424
|
-
//
|
|
425
|
-
// return this as any;
|
|
426
|
-
// }
|
|
427
|
-
//
|
|
428
|
-
// /* ------------------------------------------------ */
|
|
429
|
-
// /* Join helper */
|
|
430
|
-
// /* ------------------------------------------------ */
|
|
431
|
-
//
|
|
432
|
-
// join<ID extends string, ActionName extends keyof Reg & string>(
|
|
433
|
-
// id: ID,
|
|
434
|
-
// action: ActionName,
|
|
435
|
-
// resolve: (ctx: {
|
|
436
|
-
// input: Input;
|
|
437
|
-
// results: Results;
|
|
438
|
-
// }) => Parameters<Reg[ActionName]>[0],
|
|
439
|
-
// ) {
|
|
440
|
-
// return this.step(id, action, resolve, [...this.frontier]);
|
|
282
|
+
// context: Context;
|
|
283
|
+
// }) => boolean,
|
|
284
|
+
// ): this {
|
|
285
|
+
// const lastStep = this.steps[this.steps.length - 1];
|
|
286
|
+
// if (!lastStep) throw new Error("when() must follow a step");
|
|
287
|
+
// lastStep.when = predicate;
|
|
288
|
+
// return this;
|
|
441
289
|
// }
|
|
442
290
|
//
|
|
443
291
|
// /* ------------------------------------------------ */
|
|
444
|
-
// /*
|
|
292
|
+
// /* Workflow output */
|
|
445
293
|
// /* ------------------------------------------------ */
|
|
446
|
-
//
|
|
447
|
-
// subflow<
|
|
448
|
-
// Prefix extends string,
|
|
449
|
-
// SubInput,
|
|
450
|
-
// SubResults,
|
|
451
|
-
// SubSteps extends StepDef<Reg, any, any>[],
|
|
452
|
-
// >(
|
|
453
|
-
// prefix: Prefix,
|
|
454
|
-
// workflow: WorkflowDef<Reg, SubInput, SubResults, SubSteps>,
|
|
455
|
-
// resolveInput: (ctx: { input: Input; results: Results }) => SubInput,
|
|
456
|
-
// ): WorkflowBuilder<
|
|
457
|
-
// Reg,
|
|
458
|
-
// Input,
|
|
459
|
-
// [...Steps, ...SubSteps],
|
|
460
|
-
// Results & { [K in Prefix]: SubResults }
|
|
461
|
-
// > {
|
|
462
|
-
// const idMap = new Map<string, string>();
|
|
463
|
-
//
|
|
464
|
-
// workflow.steps.forEach((step) => {
|
|
465
|
-
// idMap.set(step.id, `${prefix}.${step.id}`);
|
|
466
|
-
// });
|
|
467
|
-
//
|
|
468
|
-
// workflow.steps.forEach((step) => {
|
|
469
|
-
// const newStep = {
|
|
470
|
-
// ...step,
|
|
471
|
-
//
|
|
472
|
-
// id: idMap.get(step.id)!,
|
|
473
|
-
//
|
|
474
|
-
// dependsOn: step.dependsOn.map((d) => idMap.get(d)!),
|
|
475
|
-
//
|
|
476
|
-
// resolve: (ctx: any) => {
|
|
477
|
-
// const subInput = resolveInput(ctx);
|
|
478
|
-
//
|
|
479
|
-
// return step.resolve({
|
|
480
|
-
// input: subInput,
|
|
481
|
-
// results: ctx.results,
|
|
482
|
-
// });
|
|
483
|
-
// },
|
|
484
|
-
// };
|
|
485
|
-
//
|
|
486
|
-
// if (workflow.entrySteps.find((e) => e.id === step.id)) {
|
|
487
|
-
// newStep.dependsOn = [...this.frontier];
|
|
488
|
-
// }
|
|
489
|
-
//
|
|
490
|
-
// this.steps.push(newStep);
|
|
491
|
-
// });
|
|
492
|
-
//
|
|
493
|
-
// this.frontier = workflow.endSteps.map((e) => idMap.get(e.id)!);
|
|
494
|
-
//
|
|
495
|
-
// return this as any;
|
|
496
|
-
// }
|
|
497
|
-
//
|
|
498
294
|
// output<Output>(
|
|
499
|
-
// fn: (ctx: { input: Input; results: Results }) => Output,
|
|
295
|
+
// fn: (ctx: { input: Input; results: Results; context: Context }) => Output,
|
|
500
296
|
// ): WorkflowDef<Reg, Input, Results, Steps, Output> {
|
|
501
297
|
// this.outputResolver = fn;
|
|
502
298
|
// return this.build() as WorkflowDef<Reg, Input, Results, Steps, Output>;
|
|
503
299
|
// }
|
|
504
|
-
//
|
|
505
|
-
// /* Build */
|
|
506
|
-
// /* ------------------------------------------------ */
|
|
300
|
+
//
|
|
507
301
|
// build(): WorkflowDef<Reg, Input, Results, Steps> {
|
|
508
302
|
// this.validateDependencies();
|
|
509
303
|
//
|
|
@@ -515,35 +309,62 @@
|
|
|
515
309
|
// input: {} as Input,
|
|
516
310
|
// results: {} as Results,
|
|
517
311
|
// outputResolver: this.outputResolver,
|
|
312
|
+
// __context: this.context,
|
|
518
313
|
// };
|
|
519
314
|
// }
|
|
520
315
|
//
|
|
521
|
-
// /* ------------------------------------------------ */
|
|
522
|
-
//
|
|
523
316
|
// private validateDependencies() {
|
|
524
317
|
// const stepIds = new Set(this.steps.map((s) => s.id));
|
|
525
|
-
//
|
|
526
318
|
// for (const step of this.steps) {
|
|
527
319
|
// for (const dep of step.dependsOn) {
|
|
528
|
-
// if (!stepIds.has(dep))
|
|
320
|
+
// if (!stepIds.has(dep))
|
|
529
321
|
// throw new Error(`Step ${step.id} depends on unknown step ${dep}`);
|
|
530
|
-
// }
|
|
531
322
|
// }
|
|
532
323
|
// }
|
|
533
324
|
// }
|
|
534
325
|
//
|
|
535
326
|
// private getEndSteps() {
|
|
536
327
|
// const hasDependents = new Set<string>();
|
|
537
|
-
//
|
|
538
328
|
// for (const step of this.steps) {
|
|
539
|
-
// for (const dep of step.dependsOn)
|
|
540
|
-
// hasDependents.add(dep);
|
|
541
|
-
// }
|
|
329
|
+
// for (const dep of step.dependsOn) hasDependents.add(dep);
|
|
542
330
|
// }
|
|
543
|
-
//
|
|
544
331
|
// return this.steps.filter((s) => !hasDependents.has(s.id));
|
|
545
332
|
// }
|
|
333
|
+
//
|
|
334
|
+
// /* ------------------------------------------------ */
|
|
335
|
+
// /* STATIC HELPERS FOR NORMALIZATION */
|
|
336
|
+
// /* ------------------------------------------------ */
|
|
337
|
+
// static args = (...args: any[]): NormalizedCall => ({
|
|
338
|
+
// kind: "positional",
|
|
339
|
+
// args,
|
|
340
|
+
// });
|
|
341
|
+
// static obj = (args: any): NormalizedCall => ({ kind: "object", args });
|
|
342
|
+
// static none = (): NormalizedCall => ({ kind: "none" });
|
|
343
|
+
// static loop = (items: NormalizedCall[]): ResolvedStepInput => ({
|
|
344
|
+
// kind: "loop",
|
|
345
|
+
// items,
|
|
346
|
+
// });
|
|
546
347
|
// }
|
|
348
|
+
//
|
|
349
|
+
// /* ------------------------------------------------ */
|
|
350
|
+
// /* WORKFLOW CREATOR */
|
|
351
|
+
// /* ------------------------------------------------ */
|
|
352
|
+
// export function createWorkflow<
|
|
353
|
+
// Reg extends ActionRegistry,
|
|
354
|
+
// Context extends Record<string, any> = {},
|
|
355
|
+
// >(registry: Reg, context?: Context) {
|
|
356
|
+
// return function workflow<Input = unknown>(name: string) {
|
|
357
|
+
// return new WorkflowBuilder<Reg, Input, Context>(
|
|
358
|
+
// name,
|
|
359
|
+
// registry,
|
|
360
|
+
// context || ({} as Context),
|
|
361
|
+
// );
|
|
362
|
+
// };
|
|
363
|
+
// }
|
|
364
|
+
//
|
|
365
|
+
/* ------------------------------------------------ */
|
|
366
|
+
/* FLUENT WORKFLOW BUILDER */
|
|
367
|
+
/* ------------------------------------------------ */
|
|
547
368
|
export class WorkflowBuilder {
|
|
548
369
|
constructor(name, registry, context) {
|
|
549
370
|
this.name = name;
|
|
@@ -553,29 +374,28 @@ export class WorkflowBuilder {
|
|
|
553
374
|
this.frontier = [];
|
|
554
375
|
}
|
|
555
376
|
/* ------------------------------------------------ */
|
|
556
|
-
/* Base Step
|
|
377
|
+
/* Base Step */
|
|
557
378
|
/* ------------------------------------------------ */
|
|
558
|
-
step(id, action,
|
|
559
|
-
// 👇 resolve returns whatever the action expects (tuple, object, single value, etc.)
|
|
560
|
-
resolve, dependsOn) {
|
|
379
|
+
step(id, action, resolve, dependsOn, options) {
|
|
561
380
|
const deps = dependsOn ?? [...this.frontier];
|
|
562
381
|
this.steps.push({
|
|
563
382
|
id,
|
|
564
383
|
action,
|
|
565
|
-
resolve:
|
|
384
|
+
resolve: resolve ?? (() => ({ kind: "none" })),
|
|
566
385
|
dependsOn: deps,
|
|
567
386
|
});
|
|
568
387
|
this.frontier = [id];
|
|
569
388
|
return this;
|
|
570
389
|
}
|
|
571
390
|
/* ------------------------------------------------ */
|
|
572
|
-
/* Sequential shortcut
|
|
391
|
+
/* Sequential shortcut */
|
|
573
392
|
/* ------------------------------------------------ */
|
|
574
|
-
seq(id, action, resolve) {
|
|
575
|
-
return this.step(id, action, resolve);
|
|
393
|
+
seq(id, action, resolve, options) {
|
|
394
|
+
return this.step(id, action, resolve, undefined, options);
|
|
576
395
|
}
|
|
577
396
|
/* ------------------------------------------------ */
|
|
578
|
-
/* Parallel branches
|
|
397
|
+
/* Parallel branches */
|
|
398
|
+
/* ------------------------------------------------ */
|
|
579
399
|
parallel(...branches) {
|
|
580
400
|
const parentFrontier = [...this.frontier];
|
|
581
401
|
const branchEnds = [];
|
|
@@ -590,15 +410,69 @@ export class WorkflowBuilder {
|
|
|
590
410
|
return this;
|
|
591
411
|
}
|
|
592
412
|
/* ------------------------------------------------ */
|
|
593
|
-
/* Join helper
|
|
413
|
+
/* Join helper */
|
|
594
414
|
/* ------------------------------------------------ */
|
|
595
|
-
join(id, action, resolve) {
|
|
596
|
-
return this.step(id, action, resolve, [...this.frontier]);
|
|
415
|
+
join(id, action, resolve, options) {
|
|
416
|
+
return this.step(id, action, resolve, [...this.frontier], options);
|
|
597
417
|
}
|
|
598
418
|
/* ------------------------------------------------ */
|
|
599
|
-
/* Subflow
|
|
419
|
+
/* Subflow */
|
|
600
420
|
/* ------------------------------------------------ */
|
|
601
|
-
subflow
|
|
421
|
+
// subflow<
|
|
422
|
+
// Prefix extends string,
|
|
423
|
+
// SubInput,
|
|
424
|
+
// SubResults,
|
|
425
|
+
// SubSteps extends StepDef<Reg, any, any>[],
|
|
426
|
+
// SubOutput,
|
|
427
|
+
// >(
|
|
428
|
+
// prefix: Prefix,
|
|
429
|
+
// workflow: WorkflowDef<Reg, SubInput, SubResults, SubSteps, SubOutput>,
|
|
430
|
+
// resolveInput?: (ctx: {
|
|
431
|
+
// input: Input;
|
|
432
|
+
// results: Results;
|
|
433
|
+
// context: Context;
|
|
434
|
+
// }) => SubInput,
|
|
435
|
+
// options?: { loop?: boolean },
|
|
436
|
+
// ): WorkflowBuilder<
|
|
437
|
+
// Reg,
|
|
438
|
+
// Input,
|
|
439
|
+
// Context,
|
|
440
|
+
// [...Steps, ...SubSteps],
|
|
441
|
+
// Results & { [K in Prefix]: SubflowResult<SubResults, SubOutput> }
|
|
442
|
+
// > {
|
|
443
|
+
// const idMap = new Map<string, string>();
|
|
444
|
+
//
|
|
445
|
+
// workflow.steps.forEach((step) => {
|
|
446
|
+
// idMap.set(step.id, `${prefix}.${step.id}`);
|
|
447
|
+
// });
|
|
448
|
+
//
|
|
449
|
+
// workflow.steps.forEach((step) => {
|
|
450
|
+
// const newStep: StepDef<Reg, any, any> = {
|
|
451
|
+
// ...step,
|
|
452
|
+
// id: idMap.get(step.id)!,
|
|
453
|
+
// dependsOn: step.dependsOn.map((d) => idMap.get(d)!),
|
|
454
|
+
// resolve: (ctx: any) => {
|
|
455
|
+
// const subInput = resolveInput ? resolveInput(ctx) : undefined;
|
|
456
|
+
// return step.resolve({
|
|
457
|
+
// input: subInput,
|
|
458
|
+
// results: ctx.results,
|
|
459
|
+
// context: ctx.context,
|
|
460
|
+
// });
|
|
461
|
+
// },
|
|
462
|
+
// ...(options ? { loop: options.loop ?? step.loop } : {}),
|
|
463
|
+
// };
|
|
464
|
+
//
|
|
465
|
+
// if (workflow.entrySteps.find((e) => e.id === step.id)) {
|
|
466
|
+
// newStep.dependsOn = [...this.frontier];
|
|
467
|
+
// }
|
|
468
|
+
//
|
|
469
|
+
// this.steps.push(newStep);
|
|
470
|
+
// });
|
|
471
|
+
//
|
|
472
|
+
// this.frontier = workflow.endSteps.map((e) => idMap.get(e.id)!);
|
|
473
|
+
// return this as any;
|
|
474
|
+
// }
|
|
475
|
+
subflow(prefix, workflow, resolveInput, options) {
|
|
602
476
|
const idMap = new Map();
|
|
603
477
|
workflow.steps.forEach((step) => {
|
|
604
478
|
idMap.set(step.id, `${prefix}.${step.id}`);
|
|
@@ -625,14 +499,19 @@ export class WorkflowBuilder {
|
|
|
625
499
|
this.frontier = workflow.endSteps.map((e) => idMap.get(e.id));
|
|
626
500
|
return this;
|
|
627
501
|
}
|
|
502
|
+
/* ------------------------------------------------ */
|
|
503
|
+
/* Conditional */
|
|
504
|
+
/* ------------------------------------------------ */
|
|
628
505
|
when(predicate) {
|
|
629
506
|
const lastStep = this.steps[this.steps.length - 1];
|
|
630
|
-
if (!lastStep)
|
|
507
|
+
if (!lastStep)
|
|
631
508
|
throw new Error("when() must follow a step");
|
|
632
|
-
}
|
|
633
509
|
lastStep.when = predicate;
|
|
634
510
|
return this;
|
|
635
511
|
}
|
|
512
|
+
/* ------------------------------------------------ */
|
|
513
|
+
/* Workflow output */
|
|
514
|
+
/* ------------------------------------------------ */
|
|
636
515
|
output(fn) {
|
|
637
516
|
this.outputResolver = fn;
|
|
638
517
|
return this.build();
|
|
@@ -654,22 +533,23 @@ export class WorkflowBuilder {
|
|
|
654
533
|
const stepIds = new Set(this.steps.map((s) => s.id));
|
|
655
534
|
for (const step of this.steps) {
|
|
656
535
|
for (const dep of step.dependsOn) {
|
|
657
|
-
if (!stepIds.has(dep))
|
|
536
|
+
if (!stepIds.has(dep))
|
|
658
537
|
throw new Error(`Step ${step.id} depends on unknown step ${dep}`);
|
|
659
|
-
}
|
|
660
538
|
}
|
|
661
539
|
}
|
|
662
540
|
}
|
|
663
541
|
getEndSteps() {
|
|
664
542
|
const hasDependents = new Set();
|
|
665
543
|
for (const step of this.steps) {
|
|
666
|
-
for (const dep of step.dependsOn)
|
|
544
|
+
for (const dep of step.dependsOn)
|
|
667
545
|
hasDependents.add(dep);
|
|
668
|
-
}
|
|
669
546
|
}
|
|
670
547
|
return this.steps.filter((s) => !hasDependents.has(s.id));
|
|
671
548
|
}
|
|
672
549
|
}
|
|
550
|
+
/* ------------------------------------------------ */
|
|
551
|
+
/* WORKFLOW CREATOR */
|
|
552
|
+
/* ------------------------------------------------ */
|
|
673
553
|
export function createWorkflow(registry, context) {
|
|
674
554
|
return function workflow(name) {
|
|
675
555
|
return new WorkflowBuilder(name, registry, context || {});
|