@pogodisco/zephyr 1.3.5 → 1.3.7
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 -1
- package/dist/index.js +0 -1
- package/dist/registry.js +0 -40
- package/dist/utils.js +0 -41
- package/dist/workflow-composer.d.ts +53 -28
- package/dist/workflow-composer.js +412 -17
- package/dist/workflow-executor.js +9 -631
- package/dist/workflow-module.d.ts +1 -5
- package/dist/workflow-module.js +1 -5
- package/package.json +1 -1
|
@@ -1,4 +1,367 @@
|
|
|
1
|
-
//
|
|
1
|
+
// import { ActionRegistry, ActionReturn } from "./types.js";
|
|
2
|
+
//
|
|
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<
|
|
25
|
+
// Reg extends ActionRegistry,
|
|
26
|
+
// ActionName extends keyof Reg,
|
|
27
|
+
// Loop extends boolean | undefined = undefined,
|
|
28
|
+
// > = Loop extends true
|
|
29
|
+
// ? ActionReturn<Reg, ActionName>[]
|
|
30
|
+
// : ActionReturn<Reg, ActionName>;
|
|
31
|
+
//
|
|
32
|
+
// /* ------------------------------------------------ */
|
|
33
|
+
// /* STEP DEFINITION */
|
|
34
|
+
// /* ------------------------------------------------ */
|
|
35
|
+
//
|
|
36
|
+
// export type StepDef<
|
|
37
|
+
// Reg extends ActionRegistry,
|
|
38
|
+
// ID extends string = string,
|
|
39
|
+
// ActionName extends keyof Reg = any,
|
|
40
|
+
// > = {
|
|
41
|
+
// id: ID;
|
|
42
|
+
// action: ActionName;
|
|
43
|
+
// dependsOn: string[];
|
|
44
|
+
// resolve: (ctx: any) => ResolvedStepInput;
|
|
45
|
+
// when?: (ctx: any) => boolean;
|
|
46
|
+
// loop?: boolean;
|
|
47
|
+
// };
|
|
48
|
+
//
|
|
49
|
+
// /* ------------------------------------------------ */
|
|
50
|
+
// /* WORKFLOW DEFINITION */
|
|
51
|
+
// /* ------------------------------------------------ */
|
|
52
|
+
//
|
|
53
|
+
// export type WorkflowDef<
|
|
54
|
+
// Reg extends ActionRegistry,
|
|
55
|
+
// Input,
|
|
56
|
+
// Results,
|
|
57
|
+
// Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[],
|
|
58
|
+
// Output = undefined,
|
|
59
|
+
// > = {
|
|
60
|
+
// name: string;
|
|
61
|
+
// steps: Steps;
|
|
62
|
+
// entrySteps: StepDef<Reg>[];
|
|
63
|
+
// endSteps: StepDef<Reg>[];
|
|
64
|
+
// input: Input;
|
|
65
|
+
// results: Results;
|
|
66
|
+
// outputResolver?: (ctx: any) => Output;
|
|
67
|
+
// __context?: any;
|
|
68
|
+
// };
|
|
69
|
+
//
|
|
70
|
+
// /* ------------------------------------------------ */
|
|
71
|
+
// /* HELPER TYPES */
|
|
72
|
+
// /* ------------------------------------------------ */
|
|
73
|
+
//
|
|
74
|
+
// type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
|
|
75
|
+
// k: infer I,
|
|
76
|
+
// ) => void
|
|
77
|
+
// ? I
|
|
78
|
+
// : never;
|
|
79
|
+
//
|
|
80
|
+
// /* ------------------------------------------------ */
|
|
81
|
+
// /* FLUENT WORKFLOW BUILDER */
|
|
82
|
+
// /* ------------------------------------------------ */
|
|
83
|
+
//
|
|
84
|
+
// export class WorkflowBuilder<
|
|
85
|
+
// Reg extends ActionRegistry,
|
|
86
|
+
// Input = unknown,
|
|
87
|
+
// Context extends Record<string, any> = {},
|
|
88
|
+
// Steps extends StepDef<Reg, any, any>[] = [],
|
|
89
|
+
// Results = {},
|
|
90
|
+
// Output = undefined,
|
|
91
|
+
// > {
|
|
92
|
+
// private steps: StepDef<Reg, any, any>[] = [];
|
|
93
|
+
// private frontier: string[] = [];
|
|
94
|
+
// private outputResolver?: (ctx: any) => any;
|
|
95
|
+
//
|
|
96
|
+
// constructor(
|
|
97
|
+
// private name: string,
|
|
98
|
+
// private registry: Reg,
|
|
99
|
+
// private context: Context,
|
|
100
|
+
// ) {}
|
|
101
|
+
//
|
|
102
|
+
// /* ------------------------------------------------ */
|
|
103
|
+
// /* Base Step */
|
|
104
|
+
// /* ------------------------------------------------ */
|
|
105
|
+
// step<
|
|
106
|
+
// ID extends string,
|
|
107
|
+
// ActionName extends keyof Reg & string,
|
|
108
|
+
// Loop extends boolean | undefined = undefined,
|
|
109
|
+
// >(
|
|
110
|
+
// id: ID,
|
|
111
|
+
// action: ActionName,
|
|
112
|
+
// resolve?: (ctx: {
|
|
113
|
+
// input: Input;
|
|
114
|
+
// results: Results;
|
|
115
|
+
// context: Context;
|
|
116
|
+
// }) => ResolvedStepInput,
|
|
117
|
+
// dependsOn?: string[],
|
|
118
|
+
// options?: { loop?: Loop },
|
|
119
|
+
// ): WorkflowBuilder<
|
|
120
|
+
// Reg,
|
|
121
|
+
// Input,
|
|
122
|
+
// Context,
|
|
123
|
+
// [...Steps, StepDef<Reg, ID, ActionName>],
|
|
124
|
+
// Results & { [K in ID]: StepResult<Reg, ActionName, Loop> }
|
|
125
|
+
// > {
|
|
126
|
+
// const deps = dependsOn ?? [...this.frontier];
|
|
127
|
+
//
|
|
128
|
+
// this.steps.push({
|
|
129
|
+
// id,
|
|
130
|
+
// action,
|
|
131
|
+
// resolve: resolve ?? (() => ({ kind: "none" })),
|
|
132
|
+
// dependsOn: deps,
|
|
133
|
+
// loop: options?.loop ?? false,
|
|
134
|
+
// });
|
|
135
|
+
//
|
|
136
|
+
// this.frontier = [id];
|
|
137
|
+
//
|
|
138
|
+
// return this as any;
|
|
139
|
+
// }
|
|
140
|
+
//
|
|
141
|
+
// /* ------------------------------------------------ */
|
|
142
|
+
// /* Sequential shortcut */
|
|
143
|
+
// /* ------------------------------------------------ */
|
|
144
|
+
// seq<ID extends string, ActionName extends keyof Reg & string>(
|
|
145
|
+
// id: ID,
|
|
146
|
+
// action: ActionName,
|
|
147
|
+
// resolve?: (ctx: {
|
|
148
|
+
// input: Input;
|
|
149
|
+
// results: Results;
|
|
150
|
+
// context: Context;
|
|
151
|
+
// }) => ResolvedStepInput,
|
|
152
|
+
// options?: { loop?: boolean },
|
|
153
|
+
// ) {
|
|
154
|
+
// return this.step(id, action, resolve, undefined, options);
|
|
155
|
+
// }
|
|
156
|
+
//
|
|
157
|
+
// /* ------------------------------------------------ */
|
|
158
|
+
// /* Parallel branches */
|
|
159
|
+
// /* ------------------------------------------------ */
|
|
160
|
+
// parallel<Branches extends WorkflowBuilder<Reg, Input, Context, any, any>[]>(
|
|
161
|
+
// ...branches: {
|
|
162
|
+
// [K in keyof Branches]: (
|
|
163
|
+
// builder: WorkflowBuilder<Reg, Input, Context, [], Results>,
|
|
164
|
+
// ) => Branches[K];
|
|
165
|
+
// }
|
|
166
|
+
// ): WorkflowBuilder<
|
|
167
|
+
// Reg,
|
|
168
|
+
// Input,
|
|
169
|
+
// Context,
|
|
170
|
+
// [
|
|
171
|
+
// ...Steps,
|
|
172
|
+
// ...(Branches[number] extends WorkflowBuilder<Reg, any, any, infer S, any>
|
|
173
|
+
// ? S
|
|
174
|
+
// : never),
|
|
175
|
+
// ],
|
|
176
|
+
// Results &
|
|
177
|
+
// (Branches[number] extends WorkflowBuilder<Reg, any, any, any, infer R>
|
|
178
|
+
// ? UnionToIntersection<R>
|
|
179
|
+
// : {})
|
|
180
|
+
// > {
|
|
181
|
+
// const parentFrontier = [...this.frontier];
|
|
182
|
+
// const branchEnds: string[] = [];
|
|
183
|
+
//
|
|
184
|
+
// branches.forEach((branch) => {
|
|
185
|
+
// const b = new WorkflowBuilder<Reg, Input, Context, [], Results>(
|
|
186
|
+
// this.name,
|
|
187
|
+
// this.registry,
|
|
188
|
+
// this.context,
|
|
189
|
+
// );
|
|
190
|
+
//
|
|
191
|
+
// b.frontier = parentFrontier;
|
|
192
|
+
// branch(b);
|
|
193
|
+
// branchEnds.push(...b.frontier);
|
|
194
|
+
// this.steps.push(...(b as any).steps);
|
|
195
|
+
// });
|
|
196
|
+
//
|
|
197
|
+
// this.frontier = branchEnds;
|
|
198
|
+
// return this as any;
|
|
199
|
+
// }
|
|
200
|
+
//
|
|
201
|
+
// /* ------------------------------------------------ */
|
|
202
|
+
// /* Join helper */
|
|
203
|
+
// /* ------------------------------------------------ */
|
|
204
|
+
// join<ID extends string, ActionName extends keyof Reg & string>(
|
|
205
|
+
// id: ID,
|
|
206
|
+
// action: ActionName,
|
|
207
|
+
// resolve?: (ctx: {
|
|
208
|
+
// input: Input;
|
|
209
|
+
// results: Results;
|
|
210
|
+
// context: Context;
|
|
211
|
+
// }) => ResolvedStepInput,
|
|
212
|
+
// options?: { loop?: boolean },
|
|
213
|
+
// ) {
|
|
214
|
+
// return this.step(id, action, resolve, [...this.frontier], options);
|
|
215
|
+
// }
|
|
216
|
+
//
|
|
217
|
+
// /* ------------------------------------------------ */
|
|
218
|
+
// /* Subflow */
|
|
219
|
+
// /* ------------------------------------------------ */
|
|
220
|
+
// subflow<
|
|
221
|
+
// Prefix extends string,
|
|
222
|
+
// SubInput,
|
|
223
|
+
// SubResults,
|
|
224
|
+
// SubSteps extends StepDef<Reg, any, any>[],
|
|
225
|
+
// SubOutput,
|
|
226
|
+
// >(
|
|
227
|
+
// prefix: Prefix,
|
|
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 },
|
|
235
|
+
// ): WorkflowBuilder<
|
|
236
|
+
// Reg,
|
|
237
|
+
// Input,
|
|
238
|
+
// Context,
|
|
239
|
+
// [...Steps, ...SubSteps],
|
|
240
|
+
// Results & { [K in Prefix]: SubflowResult<SubResults, SubOutput> }
|
|
241
|
+
// > {
|
|
242
|
+
// const idMap = new Map<string, string>();
|
|
243
|
+
//
|
|
244
|
+
// workflow.steps.forEach((step) => {
|
|
245
|
+
// idMap.set(step.id, `${prefix}.${step.id}`);
|
|
246
|
+
// });
|
|
247
|
+
//
|
|
248
|
+
// workflow.steps.forEach((step) => {
|
|
249
|
+
// const newStep: StepDef<Reg, any, any> = {
|
|
250
|
+
// ...step,
|
|
251
|
+
// id: idMap.get(step.id)!,
|
|
252
|
+
// dependsOn: step.dependsOn.map((d) => idMap.get(d)!),
|
|
253
|
+
// resolve: (ctx: any) => {
|
|
254
|
+
// const subInput = resolveInput ? resolveInput(ctx) : undefined;
|
|
255
|
+
// return step.resolve({
|
|
256
|
+
// input: subInput,
|
|
257
|
+
// results: ctx.results,
|
|
258
|
+
// context: ctx.context,
|
|
259
|
+
// });
|
|
260
|
+
// },
|
|
261
|
+
// ...(options ? { loop: options.loop ?? step.loop } : {}),
|
|
262
|
+
// };
|
|
263
|
+
//
|
|
264
|
+
// if (workflow.entrySteps.find((e) => e.id === step.id)) {
|
|
265
|
+
// newStep.dependsOn = [...this.frontier];
|
|
266
|
+
// }
|
|
267
|
+
//
|
|
268
|
+
// this.steps.push(newStep);
|
|
269
|
+
// });
|
|
270
|
+
//
|
|
271
|
+
// this.frontier = workflow.endSteps.map((e) => idMap.get(e.id)!);
|
|
272
|
+
// return this as any;
|
|
273
|
+
// }
|
|
274
|
+
//
|
|
275
|
+
// /* ------------------------------------------------ */
|
|
276
|
+
// /* Conditional */
|
|
277
|
+
// /* ------------------------------------------------ */
|
|
278
|
+
// when(
|
|
279
|
+
// predicate: (ctx: {
|
|
280
|
+
// input: Input;
|
|
281
|
+
// results: Results;
|
|
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;
|
|
289
|
+
// }
|
|
290
|
+
//
|
|
291
|
+
// /* ------------------------------------------------ */
|
|
292
|
+
// /* Workflow output */
|
|
293
|
+
// /* ------------------------------------------------ */
|
|
294
|
+
// output<Output>(
|
|
295
|
+
// fn: (ctx: { input: Input; results: Results; context: Context }) => Output,
|
|
296
|
+
// ): WorkflowDef<Reg, Input, Results, Steps, Output> {
|
|
297
|
+
// this.outputResolver = fn;
|
|
298
|
+
// return this.build() as WorkflowDef<Reg, Input, Results, Steps, Output>;
|
|
299
|
+
// }
|
|
300
|
+
//
|
|
301
|
+
// build(): WorkflowDef<Reg, Input, Results, Steps> {
|
|
302
|
+
// this.validateDependencies();
|
|
303
|
+
//
|
|
304
|
+
// return {
|
|
305
|
+
// name: this.name,
|
|
306
|
+
// steps: this.steps as Steps,
|
|
307
|
+
// entrySteps: this.steps.filter((s) => s.dependsOn.length === 0),
|
|
308
|
+
// endSteps: this.getEndSteps(),
|
|
309
|
+
// input: {} as Input,
|
|
310
|
+
// results: {} as Results,
|
|
311
|
+
// outputResolver: this.outputResolver,
|
|
312
|
+
// __context: this.context,
|
|
313
|
+
// };
|
|
314
|
+
// }
|
|
315
|
+
//
|
|
316
|
+
// private validateDependencies() {
|
|
317
|
+
// const stepIds = new Set(this.steps.map((s) => s.id));
|
|
318
|
+
// for (const step of this.steps) {
|
|
319
|
+
// for (const dep of step.dependsOn) {
|
|
320
|
+
// if (!stepIds.has(dep))
|
|
321
|
+
// throw new Error(`Step ${step.id} depends on unknown step ${dep}`);
|
|
322
|
+
// }
|
|
323
|
+
// }
|
|
324
|
+
// }
|
|
325
|
+
//
|
|
326
|
+
// private getEndSteps() {
|
|
327
|
+
// const hasDependents = new Set<string>();
|
|
328
|
+
// for (const step of this.steps) {
|
|
329
|
+
// for (const dep of step.dependsOn) hasDependents.add(dep);
|
|
330
|
+
// }
|
|
331
|
+
// return this.steps.filter((s) => !hasDependents.has(s.id));
|
|
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
|
+
// });
|
|
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
|
+
//
|
|
2
365
|
/* ------------------------------------------------ */
|
|
3
366
|
/* FLUENT WORKFLOW BUILDER */
|
|
4
367
|
/* ------------------------------------------------ */
|
|
@@ -13,6 +376,47 @@ export class WorkflowBuilder {
|
|
|
13
376
|
/* ------------------------------------------------ */
|
|
14
377
|
/* Base Step */
|
|
15
378
|
/* ------------------------------------------------ */
|
|
379
|
+
// step<
|
|
380
|
+
// ID extends string,
|
|
381
|
+
// ActionName extends keyof Reg & string,
|
|
382
|
+
// ResolveOut extends ResolvedStepInput = ResolvedStepInput,
|
|
383
|
+
// >(
|
|
384
|
+
// id: ID,
|
|
385
|
+
// action: ActionName,
|
|
386
|
+
// resolve?: (
|
|
387
|
+
// ctx: {
|
|
388
|
+
// input: Input;
|
|
389
|
+
// results: Results;
|
|
390
|
+
// context: Context;
|
|
391
|
+
// } & CallHelpers<Reg, ActionName>,
|
|
392
|
+
// ) => ResolveOut,
|
|
393
|
+
// dependsOn?: string[],
|
|
394
|
+
// options?: {},
|
|
395
|
+
// ): WorkflowBuilder<
|
|
396
|
+
// Reg,
|
|
397
|
+
// Input,
|
|
398
|
+
// Context,
|
|
399
|
+
// [...Steps, StepDef<Reg, ID, ActionName>],
|
|
400
|
+
// Results & { [K in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut> }
|
|
401
|
+
// > {
|
|
402
|
+
// const deps = dependsOn ?? [...this.frontier];
|
|
403
|
+
//
|
|
404
|
+
// this.steps.push({
|
|
405
|
+
// id,
|
|
406
|
+
// action,
|
|
407
|
+
// resolve: resolve ?? (() => ({ kind: "none" })),
|
|
408
|
+
// dependsOn: deps,
|
|
409
|
+
// });
|
|
410
|
+
//
|
|
411
|
+
// this.frontier = [id];
|
|
412
|
+
//
|
|
413
|
+
// // with Override
|
|
414
|
+
//
|
|
415
|
+
// return this as any;
|
|
416
|
+
// }
|
|
417
|
+
/* ------------------------------------------------ */
|
|
418
|
+
/* Base Step */
|
|
419
|
+
/* ------------------------------------------------ */
|
|
16
420
|
step(id, action, resolve, dependsOn, options) {
|
|
17
421
|
const deps = dependsOn ?? [...this.frontier];
|
|
18
422
|
this.steps.push({
|
|
@@ -20,10 +424,15 @@ export class WorkflowBuilder {
|
|
|
20
424
|
action,
|
|
21
425
|
resolve: resolve ?? (() => ({ kind: "none" })),
|
|
22
426
|
dependsOn: deps,
|
|
23
|
-
loop: options?.loop ?? false,
|
|
24
427
|
});
|
|
25
428
|
this.frontier = [id];
|
|
26
|
-
|
|
429
|
+
// with Override
|
|
430
|
+
const next = this;
|
|
431
|
+
next.as = () => {
|
|
432
|
+
return next;
|
|
433
|
+
};
|
|
434
|
+
return next;
|
|
435
|
+
// return this as any;
|
|
27
436
|
}
|
|
28
437
|
/* ------------------------------------------------ */
|
|
29
438
|
/* Sequential shortcut */
|
|
@@ -74,7 +483,6 @@ export class WorkflowBuilder {
|
|
|
74
483
|
context: ctx.context,
|
|
75
484
|
});
|
|
76
485
|
},
|
|
77
|
-
...(options ? { loop: options.loop ?? step.loop } : {}),
|
|
78
486
|
};
|
|
79
487
|
if (workflow.entrySteps.find((e) => e.id === step.id)) {
|
|
80
488
|
newStep.dependsOn = [...this.frontier];
|
|
@@ -133,19 +541,6 @@ export class WorkflowBuilder {
|
|
|
133
541
|
}
|
|
134
542
|
}
|
|
135
543
|
/* ------------------------------------------------ */
|
|
136
|
-
/* STATIC HELPERS FOR NORMALIZATION */
|
|
137
|
-
/* ------------------------------------------------ */
|
|
138
|
-
WorkflowBuilder.args = (...args) => ({
|
|
139
|
-
kind: "positional",
|
|
140
|
-
args,
|
|
141
|
-
});
|
|
142
|
-
WorkflowBuilder.obj = (args) => ({ kind: "object", args });
|
|
143
|
-
WorkflowBuilder.none = () => ({ kind: "none" });
|
|
144
|
-
WorkflowBuilder.loop = (items) => ({
|
|
145
|
-
kind: "loop",
|
|
146
|
-
items,
|
|
147
|
-
});
|
|
148
|
-
/* ------------------------------------------------ */
|
|
149
544
|
/* WORKFLOW CREATOR */
|
|
150
545
|
/* ------------------------------------------------ */
|
|
151
546
|
export function createWorkflow(registry, context) {
|