@pogodisco/zephyr 1.5.1 → 1.5.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/types.d.ts CHANGED
@@ -32,3 +32,25 @@ export interface Executor {
32
32
  export type ServiceRegistry = Record<string, Record<string, (...args: any[]) => any>>;
33
33
  export type ServiceParams<S extends ServiceRegistry, K extends keyof S, M extends keyof S[K]> = Parameters<S[K][M]>;
34
34
  export type ServiceReturn<S extends ServiceRegistry, K extends keyof S, M extends keyof S[K]> = Awaited<ReturnType<S[K][M]>>;
35
+ export type NormalizedCall = {
36
+ kind: "none";
37
+ } | {
38
+ kind: "positional";
39
+ args: any[];
40
+ } | {
41
+ kind: "object";
42
+ args: any;
43
+ };
44
+ export type CallHelpers<Reg extends ActionRegistry, ActionName extends keyof Reg> = {
45
+ args: (...args: ActionParams<Reg, ActionName>) => {
46
+ kind: "positional";
47
+ args: ActionParams<Reg, ActionName>;
48
+ };
49
+ obj: ActionParams<Reg, ActionName> extends [infer A] ? (arg: A) => {
50
+ kind: "object";
51
+ args: A;
52
+ } : never;
53
+ none: () => {
54
+ kind: "none";
55
+ };
56
+ };
package/dist/types.js CHANGED
@@ -1 +1,2 @@
1
1
  export {};
2
+ /// pipe
@@ -0,0 +1,71 @@
1
+ import { ActionRegistry, ActionReturn, CallHelpers, NormalizedCall, ServiceParams, ServiceRegistry, ServiceReturn } from "./types.js";
2
+ type PipeStep = {
3
+ type: "action" | "service";
4
+ action?: string;
5
+ service?: string;
6
+ method?: string;
7
+ resolve: (ctx: any) => NormalizedCall;
8
+ };
9
+ export type PipeDef = {
10
+ input: (ctx: any) => any[];
11
+ steps: PipeStep[];
12
+ };
13
+ type InferStepReturn<Step, Reg extends ActionRegistry, Services extends ServiceRegistry> = Step extends {
14
+ type: "action";
15
+ action: infer A;
16
+ } ? A extends keyof Reg ? ActionReturn<Reg, A> : never : Step extends {
17
+ type: "service";
18
+ service: infer SK;
19
+ method: infer MK;
20
+ } ? SK extends keyof Services ? MK extends keyof Services[SK] ? ServiceReturn<Services, SK, MK> : never : never : never;
21
+ export type PipeFinalType<Steps, Reg extends ActionRegistry, Services extends ServiceRegistry> = Steps extends [...any[], infer Last] ? InferStepReturn<Last, Reg, Services> : never;
22
+ export type StepsOfPipeBuilder<PB> = PB extends PipeBuilder<any, any, any, any, infer S> ? S : never;
23
+ export declare class PipeBuilder<Current, Reg extends ActionRegistry, Services extends ServiceRegistry, Results, Steps extends any[] = []> {
24
+ private steps;
25
+ action<A extends keyof Reg & string>(action: A, resolve: (ctx: {
26
+ current: Current;
27
+ results: Results;
28
+ } & CallHelpers<Reg, A>) => NormalizedCall): PipeBuilder<ActionReturn<Reg, A>, Reg, Services, Results, [
29
+ ...Steps,
30
+ {
31
+ type: "action";
32
+ action: A;
33
+ }
34
+ ]>;
35
+ service<SK extends keyof Services & string, MK extends keyof Services[SK] & string>(id: string, service: SK, method: MK, resolve: (ctx: {
36
+ current: Current;
37
+ results: Results;
38
+ } & {
39
+ args: (...args: ServiceParams<Services, SK, MK>) => {
40
+ kind: "positional";
41
+ args: ServiceParams<Services, SK, MK>;
42
+ };
43
+ obj: ServiceParams<Services, SK, MK> extends [infer A] ? (arg: A) => {
44
+ kind: "object";
45
+ args: A;
46
+ } : never;
47
+ none: () => {
48
+ kind: "none";
49
+ };
50
+ loop: (items: {
51
+ kind: "positional";
52
+ args: ServiceParams<Services, SK, MK>;
53
+ }[] | {
54
+ kind: "object";
55
+ args: ServiceParams<Services, SK, MK>[0];
56
+ }[]) => {
57
+ kind: "loop";
58
+ items: typeof items;
59
+ };
60
+ }) => NormalizedCall): PipeBuilder<ServiceReturn<Services, SK, MK>, Reg, Services, Results, [
61
+ ...Steps,
62
+ {
63
+ type: "service";
64
+ service: SK;
65
+ method: MK;
66
+ }
67
+ ]>;
68
+ getSteps(): Steps;
69
+ build(): PipeStep[];
70
+ }
71
+ export {};
@@ -0,0 +1,28 @@
1
+ export class PipeBuilder {
2
+ constructor() {
3
+ this.steps = [];
4
+ }
5
+ action(action, resolve) {
6
+ this.steps.push({
7
+ type: "action",
8
+ action,
9
+ resolve,
10
+ });
11
+ return this;
12
+ }
13
+ service(id, service, method, resolve) {
14
+ this.steps.push({
15
+ type: "service",
16
+ service,
17
+ method,
18
+ resolve,
19
+ });
20
+ return this;
21
+ }
22
+ getSteps() {
23
+ return this.steps;
24
+ }
25
+ build() {
26
+ return this.steps;
27
+ }
28
+ }
@@ -1,40 +1,9 @@
1
- import { ActionParams, ActionRegistry, ActionReturn, ServiceParams, ServiceRegistry, ServiceReturn, Simplify } from "./types.js";
2
- type NormalizedCall = {
3
- kind: "none";
4
- } | {
5
- kind: "positional";
6
- args: any[];
7
- } | {
8
- kind: "object";
9
- args: any;
10
- };
1
+ import { ActionRegistry, ActionReturn, CallHelpers, NormalizedCall, ServiceParams, ServiceRegistry, ServiceReturn, Simplify } from "./types.js";
2
+ import { PipeBuilder, PipeDef, PipeFinalType, StepsOfPipeBuilder } from "./workflow-composer-pipe.js";
11
3
  export type ResolvedStepInput = NormalizedCall | {
12
4
  kind: "loop";
13
5
  items: NormalizedCall[];
14
6
  };
15
- type CallHelpers<Reg extends ActionRegistry, ActionName extends keyof Reg> = {
16
- args: (...args: ActionParams<Reg, ActionName>) => {
17
- kind: "positional";
18
- args: ActionParams<Reg, ActionName>;
19
- };
20
- obj: ActionParams<Reg, ActionName> extends [infer A] ? (arg: A) => {
21
- kind: "object";
22
- args: A;
23
- } : never;
24
- none: () => {
25
- kind: "none";
26
- };
27
- loop: (items: {
28
- kind: "positional";
29
- args: ActionParams<Reg, ActionName>;
30
- }[] | {
31
- kind: "object";
32
- args: ActionParams<Reg, ActionName>[0];
33
- }[]) => {
34
- kind: "loop";
35
- items: typeof items;
36
- };
37
- };
38
7
  type WorkflowInput<T> = T extends WorkflowDef<any, infer I, any, any, any> ? I : never;
39
8
  type WorkflowOutput<T> = T extends WorkflowDef<any, any, any, any, infer O> ? unknown extends O ? undefined : O : undefined;
40
9
  export type StepResultFromResolve<Reg extends ActionRegistry, ActionName extends keyof Reg, R extends ResolvedStepInput> = R extends {
@@ -52,6 +21,7 @@ export type StepDef<Reg extends ActionRegistry, ID extends string = string, Acti
52
21
  options?: StepOptions<any, any>;
53
22
  __subflowId?: string;
54
23
  serviceCall?: ServiceCall;
24
+ pipe?: PipeDef;
55
25
  };
56
26
  export type WorkflowDef<Reg extends ActionRegistry, Input, Results, Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[], Output = undefined> = {
57
27
  name: string;
@@ -122,22 +92,21 @@ Input = unknown, Steps extends StepDef<Reg, any, any>[] = [], Results = {}, Outp
122
92
  none: () => {
123
93
  kind: "none";
124
94
  };
125
- loop: (items: {
126
- kind: "positional";
127
- args: ServiceParams<Services, SK, MK>;
128
- }[] | {
129
- kind: "object";
130
- args: ServiceParams<Services, SK, MK>[0];
131
- }[]) => {
132
- kind: "loop";
133
- items: typeof items;
134
- };
135
95
  }) => ResolveOut, options?: StepOptions<Input, Results>): WorkflowBuilder<Reg, Services, WFReg, Input, [
136
96
  ...Steps,
137
97
  StepDef<Reg, ID, any>
138
98
  ], Simplify<Results & {
139
99
  [K in ID]: ServiceStepResultFromResolve<Services, SK, MK, ResolveOut>;
140
100
  }>>;
101
+ pipe<ID extends string, Arr extends any[], PB extends PipeBuilder<any, Reg, Services, Results, any>>(id: ID, input: (ctx: {
102
+ input: Input;
103
+ results: Results;
104
+ }) => Arr, builder: (p: PipeBuilder<Arr[number], Reg, Services, Results>) => PB, options?: StepOptions<Input, Results>): WorkflowBuilder<Reg, Services, WFReg, Input, [
105
+ ...Steps,
106
+ StepDef<Reg, ID, any>
107
+ ], Simplify<Results & {
108
+ [K in ID]: PipeFinalType<StepsOfPipeBuilder<PB>, Reg, Services>[];
109
+ }>>;
141
110
  as<NewType>(): WorkflowBuilder<Reg, Services, WFReg, Input, Steps, Steps extends [...infer Rest, infer Last] ? Last extends StepDef<Reg, infer ID, any> ? Simplify<Omit<Results, ID> & {
142
111
  [K in ID]: NewType;
143
112
  }> : Results : Results, Output>;
@@ -1,66 +1,469 @@
1
+ // import {
2
+ // ActionParams,
3
+ // ActionRegistry,
4
+ // ActionReturn,
5
+ // ServiceParams,
6
+ // ServiceRegistry,
7
+ // ServiceReturn,
8
+ // Simplify,
9
+ // } from "./types.js";
10
+ // import { generateWorkflowId } from "./utils.js";
11
+ //
12
+ // /* ------------------------------------------------ */
13
+ // /* STEP INPUT NORMALIZATION TYPES */
14
+ // /* ------------------------------------------------ */
15
+ //
16
+ // type NormalizedCall =
17
+ // | { kind: "none" }
18
+ // | { kind: "positional"; args: any[] }
19
+ // | { kind: "object"; args: any };
20
+ //
21
+ // export type ResolvedStepInput =
22
+ // | NormalizedCall
23
+ // | { kind: "loop"; items: NormalizedCall[] };
24
+ //
25
+ // type CallHelpers<Reg extends ActionRegistry, ActionName extends keyof Reg> = {
26
+ // args: (...args: ActionParams<Reg, ActionName>) => {
27
+ // kind: "positional";
28
+ // args: ActionParams<Reg, ActionName>;
29
+ // };
30
+ //
31
+ // obj: ActionParams<Reg, ActionName> extends [infer A]
32
+ // ? (arg: A) => { kind: "object"; args: A }
33
+ // : never;
34
+ //
35
+ // none: () => { kind: "none" };
36
+ // loop: (
37
+ // items:
38
+ // | { kind: "positional"; args: ActionParams<Reg, ActionName> }[]
39
+ // | { kind: "object"; args: ActionParams<Reg, ActionName>[0] }[],
40
+ // ) => {
41
+ // kind: "loop";
42
+ // items: typeof items;
43
+ // };
44
+ // };
45
+ //
46
+ // /* ------------------------------------------------ */
47
+ // /* STEP RESULT TYPES */
48
+ // /* ------------------------------------------------ */
49
+ //
50
+ // type WorkflowInput<T> =
51
+ // T extends WorkflowDef<any, infer I, any, any, any> ? I : never;
52
+ //
53
+ // // type WorkflowOutput<T> =
54
+ // // T extends WorkflowDef<any, any, any, any, infer O> ? O : never;
55
+ //
56
+ // type WorkflowOutput<T> =
57
+ // T extends WorkflowDef<any, any, any, any, infer O>
58
+ // ? unknown extends O
59
+ // ? undefined
60
+ // : O
61
+ // : undefined;
62
+ //
63
+ // export type StepResultFromResolve<
64
+ // Reg extends ActionRegistry,
65
+ // ActionName extends keyof Reg,
66
+ // R extends ResolvedStepInput,
67
+ // > = R extends { kind: "loop" }
68
+ // ? ActionReturn<Reg, ActionName>[]
69
+ // : ActionReturn<Reg, ActionName>;
70
+ //
71
+ // export type ServiceStepResultFromResolve<
72
+ // S extends ServiceRegistry,
73
+ // SK extends keyof S,
74
+ // MK extends keyof S[SK],
75
+ // R extends ResolvedStepInput,
76
+ // > = R extends { kind: "loop" }
77
+ // ? ServiceReturn<S, SK, MK>[]
78
+ // : ServiceReturn<S, SK, MK>;
79
+ //
80
+ // /* ------------------------------------------------ */
81
+ // /* STEP DEFINITION */
82
+ // /* ------------------------------------------------ */
83
+ //
84
+ // export type StepDef<
85
+ // Reg extends ActionRegistry,
86
+ // ID extends string = string,
87
+ // ActionName extends keyof Reg = any,
88
+ // > = {
89
+ // id: ID;
90
+ // action: ActionName | "__service__";
91
+ // dependsOn: string[];
92
+ // resolve: (ctx: any) => ResolvedStepInput;
93
+ // when?: (ctx: any) => boolean;
94
+ // options?: StepOptions<any, any>;
95
+ // __subflowId?: string;
96
+ //
97
+ // serviceCall?: ServiceCall;
98
+ // };
99
+ //
100
+ // /* ------------------------------------------------ */
101
+ // /* WORKFLOW DEFINITION */
102
+ // /* ------------------------------------------------ */
103
+ //
104
+ // export type WorkflowDef<
105
+ // Reg extends ActionRegistry,
106
+ // Input,
107
+ // Results,
108
+ // Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[],
109
+ // Output = undefined,
110
+ // > = {
111
+ // name: string;
112
+ // _id: string;
113
+ // steps: Steps;
114
+ // entrySteps: StepDef<Reg>[];
115
+ // endSteps: StepDef<Reg>[];
116
+ // input: Input;
117
+ // results: Results;
118
+ // outputResolver?: (ctx: any) => Output;
119
+ // // __context?: any;
120
+ // };
121
+ // type StepRuntimeCtx<I, R> = {
122
+ // input: I;
123
+ // results: R;
124
+ // };
125
+ // type StepOptions<Input, Results> = {
126
+ // retry?: number;
127
+ // retryDelay?: number | ((attempt: number) => number);
128
+ // timeout?: number;
129
+ //
130
+ // continueOnError?: boolean;
131
+ //
132
+ // onError?: (err: unknown, ctx: StepRuntimeCtx<Input, Results>) => any;
133
+ //
134
+ // // optional later:
135
+ // label?: string;
136
+ // meta?: Record<string, any>;
137
+ // };
138
+ //
139
+ // type ServiceCall = {
140
+ // service: string;
141
+ // method: string;
142
+ // };
143
+ //
144
+ // /* ------------------------------------------------ */
145
+ // /* HELPER TYPES */
146
+ // /* ------------------------------------------------ */
147
+ //
148
+ // type MergeBranchResults<
149
+ // Branches extends readonly any[],
150
+ // Acc,
151
+ // > = Branches extends readonly [infer Head, ...infer Tail]
152
+ // ? MergeBranchResults<
153
+ // Tail,
154
+ // Acc &
155
+ // (Head extends WorkflowBuilder<any, any, any, any, any, infer Results>
156
+ // ? Results
157
+ // : {})
158
+ // >
159
+ // : Acc;
160
+ //
161
+ // type MergeBranchSteps<
162
+ // Branches extends readonly any[],
163
+ // Acc extends any[],
164
+ // > = Branches extends readonly [infer Head, ...infer Tail]
165
+ // ? MergeBranchSteps<
166
+ // Tail,
167
+ // [
168
+ // ...Acc,
169
+ // ...(Head extends WorkflowBuilder<any, any, any, any, infer Steps, any>
170
+ // ? Steps
171
+ // : []),
172
+ // ]
173
+ // >
174
+ // : Acc;
175
+ //
176
+ // /* ------------------------------------------------ */
177
+ // /* FLUENT WORKFLOW BUILDER */
178
+ // /* ------------------------------------------------ */
179
+ // export class WorkflowBuilder<
180
+ // Reg extends ActionRegistry,
181
+ // Services extends ServiceRegistry,
182
+ // WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, //
183
+ // Input = unknown,
184
+ // Steps extends StepDef<Reg, any, any>[] = [],
185
+ // Results = {},
186
+ // Output = undefined,
187
+ // > {
188
+ // private steps: StepDef<Reg, any, any>[] = [];
189
+ // private frontier: string[] = [];
190
+ //
191
+ // private pendingWhen?: (ctx: { input: Input; results: Results }) => boolean;
192
+ // private outputResolver?: (ctx: any) => any;
193
+ // private clearPendingWhen() {
194
+ // this.pendingWhen = undefined;
195
+ // }
196
+ //
197
+ // constructor(private name: string) {}
198
+ //
199
+ // step<
200
+ // ID extends string,
201
+ // ActionName extends keyof Reg & string,
202
+ // ResolveOut extends ResolvedStepInput = ResolvedStepInput,
203
+ // >(
204
+ // id: ID,
205
+ // action: ActionName,
206
+ // resolve?: (
207
+ // ctx: {
208
+ // input: Input;
209
+ // results: Results;
210
+ // } & CallHelpers<Reg, ActionName>,
211
+ // ) => ResolveOut,
212
+ // dependsOn?: string[],
213
+ // options?: StepOptions<Input, Results>,
214
+ // ): WorkflowBuilder<
215
+ // Reg,
216
+ // Services,
217
+ // WFReg,
218
+ // Input,
219
+ // [...Steps, StepDef<Reg, ID, ActionName>],
220
+ // Simplify<
221
+ // Results & {
222
+ // [K in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut>;
223
+ // }
224
+ // >
225
+ // > {
226
+ // const deps = dependsOn ?? [...this.frontier];
227
+ //
228
+ // this.steps.push({
229
+ // id,
230
+ // action,
231
+ // resolve: resolve ?? (() => ({ kind: "none" })),
232
+ // dependsOn: deps,
233
+ // when: this.pendingWhen,
234
+ // options,
235
+ // });
236
+ //
237
+ // this.frontier = [id];
238
+ //
239
+ // return this as any;
240
+ // }
241
+ //
1
242
  // /* ------------------------------------------------ */
2
- // /* Subflow */
243
+ // /* Sequential shortcut */
3
244
  // /* ------------------------------------------------ */
245
+ // seq<
246
+ // ID extends string = string,
247
+ // ActionName extends keyof Reg & string = any,
248
+ // ResolveOut extends ResolvedStepInput = ResolvedStepInput,
249
+ // >(
250
+ // id: ID,
251
+ // action: ActionName,
252
+ // resolve?: (
253
+ // ctx: {
254
+ // input: Input;
255
+ // results: Results;
256
+ // } & CallHelpers<Reg, ActionName>,
257
+ // ) => ResolveOut,
258
+ // options?: StepOptions<Input, Results>,
259
+ // ) {
260
+ // return this.step<ID, ActionName, ResolveOut>(
261
+ // id,
262
+ // action,
263
+ // resolve,
264
+ // undefined,
265
+ // options,
266
+ // );
267
+ // }
4
268
  //
5
- // subflow<
6
- // Prefix extends string,
7
- // SubSteps extends StepDef<Reg, any, any>[],
8
- // WF extends WorkflowDef<Reg, any, any, SubSteps, any>,
269
+ // service<
270
+ // ID extends string,
271
+ // SK extends keyof Services & string,
272
+ // MK extends keyof Services[SK] & string,
273
+ // ResolveOut extends NormalizedCall = NormalizedCall,
9
274
  // >(
10
- // prefix: Prefix,
11
- // workflow: WF,
12
- // resolveInput: WorkflowInput<WF> extends undefined
13
- // ?
14
- // | ((ctx: {
15
- // input: Input;
16
- // results: Results;
17
- // context: Context;
18
- // }) => WorkflowInput<WF>)
19
- // | undefined
20
- // : (ctx: {
21
- // input: Input;
22
- // results: Results;
23
- // context: Context;
24
- // }) => WorkflowInput<WF>,
25
- // options?: StepOptions<Input, Results, Context>,
275
+ // id: ID,
276
+ // service: SK,
277
+ // method: MK,
278
+ // resolve?: (
279
+ // ctx: {
280
+ // input: Input;
281
+ // results: Results;
282
+ // } & {
283
+ // args: (...args: ServiceParams<Services, SK, MK>) => {
284
+ // kind: "positional";
285
+ // args: ServiceParams<Services, SK, MK>;
286
+ // };
287
+ // obj: ServiceParams<Services, SK, MK> extends [infer A]
288
+ // ? (arg: A) => { kind: "object"; args: A }
289
+ // : never;
290
+ // none: () => { kind: "none" };
291
+ // loop: (
292
+ // items:
293
+ // | { kind: "positional"; args: ServiceParams<Services, SK, MK> }[]
294
+ // | { kind: "object"; args: ServiceParams<Services, SK, MK>[0] }[],
295
+ // ) => {
296
+ // kind: "loop";
297
+ // items: typeof items;
298
+ // };
299
+ // },
300
+ // ) => ResolveOut,
301
+ // options?: StepOptions<Input, Results>,
26
302
  // ): WorkflowBuilder<
27
303
  // Reg,
304
+ // Services,
305
+ // WFReg,
28
306
  // Input,
29
- // Context,
30
- // [...Steps, ...WF["steps"]],
31
- // Results & {
32
- // [K in Prefix]: SubflowResult<WorkflowResults<WF>, WorkflowOutput<WF>>;
307
+ // [...Steps, StepDef<Reg, ID, any>],
308
+ // // Simplify<
309
+ // // Results & {
310
+ // // [K in ID]: ResolveOut extends { kind: "loop" }
311
+ // // ? ServiceReturn<Services, SK, MK>[]
312
+ // // : ServiceReturn<Services, SK, MK>;
313
+ // // }
314
+ // // >
315
+ // Simplify<
316
+ // Results & {
317
+ // [K in ID]: ServiceStepResultFromResolve<Services, SK, MK, ResolveOut>;
318
+ // }
319
+ // >
320
+ // > {
321
+ // const deps = [...this.frontier];
322
+ //
323
+ // this.steps.push({
324
+ // id,
325
+ // action: "__service__",
326
+ // serviceCall: {
327
+ // service,
328
+ // method,
329
+ // },
330
+ // resolve: resolve ?? (() => ({ kind: "none" })),
331
+ // dependsOn: deps,
332
+ // when: this.pendingWhen,
333
+ // options,
334
+ // });
335
+ //
336
+ // this.frontier = [id];
337
+ //
338
+ // return this as any;
339
+ // }
340
+ //
341
+ // /* ------------------------------------------------ */
342
+ // /* Override the result of the last step */
343
+ // /* ------------------------------------------------ */
344
+ // as<NewType>(): WorkflowBuilder<
345
+ // Reg,
346
+ // Services,
347
+ // WFReg,
348
+ // Input,
349
+ // Steps,
350
+ // // Override the result of the last step only
351
+ // Steps extends [...infer Rest, infer Last]
352
+ // ? Last extends StepDef<Reg, infer ID, any>
353
+ // ? Simplify<Omit<Results, ID> & { [K in ID]: NewType }>
354
+ // : Results
355
+ // : Results,
356
+ // Output
357
+ // > {
358
+ // return this as any;
359
+ // }
360
+ //
361
+ // parallel<
362
+ // Branches extends readonly WorkflowBuilder<
363
+ // Reg,
364
+ // Services,
365
+ // WFReg,
366
+ // Input,
367
+ // any,
368
+ // any
369
+ // >[],
370
+ // >(
371
+ // ...branches: {
372
+ // [K in keyof Branches]: (
373
+ // builder: WorkflowBuilder<Reg, Services, WFReg, Input, [], Results>,
374
+ // ) => Branches[K];
33
375
  // }
376
+ // ): WorkflowBuilder<
377
+ // Reg,
378
+ // Services,
379
+ // WFReg,
380
+ // Input,
381
+ // MergeBranchSteps<Branches, Steps>,
382
+ // // [
383
+ // // ...Steps,
384
+ // // ...(Branches[number] extends WorkflowBuilder<Reg, any, any, infer S, any>
385
+ // // ? S
386
+ // // : never),
387
+ // // ],
388
+ // Simplify<MergeBranchResults<Branches, Results>>
34
389
  // > {
35
- // const idMap = new Map<string, string>();
390
+ // const parentFrontier = [...this.frontier];
391
+ // const branchEnds: string[] = [];
392
+ //
393
+ // branches.forEach((branch) => {
394
+ // const b = new WorkflowBuilder<Reg, Services, WFReg, Input, [], Results>(
395
+ // this.name,
396
+ // );
36
397
  //
37
- // workflow.steps.forEach((step) => {
38
- // idMap.set(step.id, `${prefix}.${step.id}`);
398
+ // b.frontier = parentFrontier;
399
+ // b.pendingWhen = this.pendingWhen;
400
+ // branch(b);
401
+ // branchEnds.push(...b.frontier);
402
+ // this.steps.push(...(b as any).steps);
39
403
  // });
40
404
  //
41
- // workflow.steps.forEach((step) => {
42
- // const newStep: StepDef<Reg, any, any> = {
43
- // ...step,
44
- // id: idMap.get(step.id)!,
45
- // dependsOn: step.dependsOn.map((d) => idMap.get(d)!),
46
- // resolve: (ctx: any) => {
47
- // const subInput = resolveInput ? resolveInput(ctx) : undefined;
48
- // return step.resolve({
49
- // input: subInput,
50
- // results: ctx.results,
51
- // context: ctx.context,
52
- // });
53
- // },
54
- // };
55
- //
56
- // if (workflow.entrySteps.find((e) => e.id === step.id)) {
57
- // newStep.dependsOn = [...this.frontier];
58
- // }
405
+ // this.frontier = branchEnds;
406
+ //
407
+ // return this as any;
408
+ // }
59
409
  //
60
- // this.steps.push(newStep);
410
+ // /* ------------------------------------------------ */
411
+ // /* Join helper */
412
+ // /* ------------------------------------------------ */
413
+ // join<
414
+ // ID extends string = string,
415
+ // ActionName extends keyof Reg & string = any,
416
+ // ResolveOut extends ResolvedStepInput = ResolvedStepInput,
417
+ // >(
418
+ // id: ID,
419
+ // action: ActionName,
420
+ // resolve?: (
421
+ // ctx: {
422
+ // input: Input;
423
+ // results: Results;
424
+ // } & CallHelpers<Reg, ActionName>,
425
+ // ) => ResolveOut,
426
+ // options?: StepOptions<Input, Results>,
427
+ // ) {
428
+ // const result = this.step<ID, ActionName, ResolveOut>(
429
+ // id,
430
+ // action,
431
+ // resolve,
432
+ // [...this.frontier],
433
+ // options,
434
+ // );
435
+ //
436
+ // this.clearPendingWhen();
437
+ // return result;
438
+ // }
439
+ //
440
+ // subflow<Prefix extends string, K extends keyof WFReg & string>(
441
+ // prefix: Prefix,
442
+ // workflowKey: K,
443
+ // resolveInput: (ctx: {
444
+ // input: Input;
445
+ // results: Results;
446
+ // }) => WorkflowInput<WFReg[K]>,
447
+ // options?: StepOptions<Input, Results>,
448
+ // ): WorkflowBuilder<
449
+ // Reg,
450
+ // Services,
451
+ // WFReg,
452
+ // Input,
453
+ // Steps,
454
+ // Results & { [P in Prefix]: WorkflowOutput<WFReg[K]> }
455
+ // > {
456
+ // this.steps.push({
457
+ // id: prefix,
458
+ // action: "__subflow__",
459
+ // dependsOn: [...this.frontier],
460
+ // when: this.pendingWhen,
461
+ // resolve: (ctx: any) => resolveInput(ctx),
462
+ // options,
463
+ // __subflowId: workflowKey, // 👈 STRING ONLY
61
464
  // });
62
465
  //
63
- // this.frontier = workflow.endSteps.map((e) => idMap.get(e.id)!);
466
+ // this.frontier = [prefix];
64
467
  // return this as any;
65
468
  // }
66
469
  //
@@ -68,7 +471,79 @@
68
471
  // /* Conditional */
69
472
  // /* ------------------------------------------------ */
70
473
  //
474
+ // when(
475
+ // predicate: (ctx: { input: Input; results: Results }) => boolean,
476
+ // ): WorkflowBuilder<Reg, Services, WFReg, Input, Steps, Results, Output> {
477
+ // this.pendingWhen = predicate;
478
+ // return this;
479
+ // }
480
+ //
481
+ // endWhen(): this {
482
+ // this.clearPendingWhen();
483
+ //
484
+ // return this;
485
+ // }
486
+ //
487
+ // /* ------------------------------------------------ */
488
+ // /* Workflow output */
489
+ // /* ------------------------------------------------ */
490
+ // output<Output>(
491
+ // fn: (ctx: { input: Input; results: Results }) => Output,
492
+ // ): WorkflowDef<Reg, Input, Results, Steps, Output> {
493
+ // this.outputResolver = fn;
494
+ // return this.build() as WorkflowDef<Reg, Input, Results, Steps, Output>;
495
+ // }
496
+ //
497
+ // build(): WorkflowDef<Reg, Input, Results, Steps> {
498
+ // this.validateDependencies();
499
+ //
500
+ // return {
501
+ // _id: generateWorkflowId(this.name),
502
+ // name: this.name,
503
+ // steps: this.steps as Steps,
504
+ // entrySteps: this.steps.filter((s) => s.dependsOn.length === 0),
505
+ // endSteps: this.getEndSteps(),
506
+ // input: {} as Input,
507
+ // results: {} as Results,
508
+ // outputResolver: this.outputResolver,
509
+ // };
510
+ // }
511
+ //
512
+ // private validateDependencies() {
513
+ // const stepIds = new Set(this.steps.map((s) => s.id));
514
+ // for (const step of this.steps) {
515
+ // for (const dep of step.dependsOn) {
516
+ // if (!stepIds.has(dep))
517
+ // throw new Error(`Step ${step.id} depends on unknown step ${dep}`);
518
+ // }
519
+ // }
520
+ // }
521
+ //
522
+ // private getEndSteps() {
523
+ // const hasDependents = new Set<string>();
524
+ // for (const step of this.steps) {
525
+ // for (const dep of step.dependsOn) hasDependents.add(dep);
526
+ // }
527
+ // return this.steps.filter((s) => !hasDependents.has(s.id));
528
+ // }
529
+ // }
530
+ //
531
+ // export function createWorkflow<
532
+ // Reg extends ActionRegistry,
533
+ // WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, // 👈 NEW
534
+ // Services extends ServiceRegistry,
535
+ // >() {
536
+ // return function workflow<Input = unknown>(name: string) {
537
+ // return new WorkflowBuilder<Reg, Services, WFReg, Input>(name);
538
+ // };
539
+ // }
540
+ //
541
+ //
542
+ //
543
+ //
544
+ //
71
545
  import { generateWorkflowId } from "./utils.js";
546
+ import { PipeBuilder, } from "./workflow-composer-pipe.js";
72
547
  /* ------------------------------------------------ */
73
548
  /* FLUENT WORKFLOW BUILDER */
74
549
  /* ------------------------------------------------ */
@@ -117,6 +592,29 @@ export class WorkflowBuilder {
117
592
  this.frontier = [id];
118
593
  return this;
119
594
  }
595
+ /* ----------------------- */
596
+ /* Pipe */
597
+ /* ----------------------- */
598
+ pipe(id, input, builder, options) {
599
+ const deps = [...this.frontier];
600
+ const pb = new PipeBuilder();
601
+ const built = builder(pb);
602
+ const steps = built.getSteps();
603
+ this.steps.push({
604
+ id,
605
+ action: "__pipe__",
606
+ pipe: {
607
+ input,
608
+ steps,
609
+ },
610
+ dependsOn: deps,
611
+ when: this.pendingWhen,
612
+ resolve: () => ({ kind: "none" }),
613
+ options,
614
+ });
615
+ this.frontier = [id];
616
+ return this;
617
+ }
120
618
  /* ------------------------------------------------ */
121
619
  /* Override the result of the last step */
122
620
  /* ------------------------------------------------ */
@@ -77,8 +77,10 @@ export async function executeWorkflow({ workflow, actionRegistry, services, inpu
77
77
  return await action(...input.args);
78
78
  case "object":
79
79
  return await action(input.args);
80
- case "loop":
81
- return await Promise.all(input.items.map((item) => runAction(item, action)));
80
+ // case "loop":
81
+ // return await Promise.all(
82
+ // input.items.map((item) => runAction(item, action)),
83
+ // );
82
84
  default:
83
85
  throw new Error(`Unknown ResolvedStepInput kind: ${input.kind}`);
84
86
  }
@@ -125,9 +127,44 @@ export async function executeWorkflow({ workflow, actionRegistry, services, inpu
125
127
  return undefined;
126
128
  }
127
129
  // -----------------------------
128
- // Subflow handling
130
+ // Pipe handling
129
131
  // -----------------------------
132
+ if (step.pipe && step.pipe.steps) {
133
+ const items = step.pipe.input(stepCtx);
134
+ const pipeResults = await Promise.all(items.map(async (item) => {
135
+ let current = item;
136
+ for (const pipeStep of step.pipe.steps) {
137
+ const pipeCtx = {
138
+ current,
139
+ results,
140
+ ...createCallHelpers(),
141
+ };
142
+ const resolved = pipeStep.resolve(pipeCtx);
143
+ let action;
144
+ if (pipeStep.type === "action") {
145
+ action = actionRegistry[pipeStep.action];
146
+ }
147
+ else {
148
+ action = services[pipeStep.service][pipeStep.method];
149
+ }
150
+ if (!action) {
151
+ throw new Error(`Unknown ${pipeStep.type} in pipe step: ${pipeStep.type === "action"
152
+ ? pipeStep.action
153
+ : `${pipeStep.service}.${pipeStep.method}`}`);
154
+ }
155
+ current = await runAction(resolved, action);
156
+ }
157
+ return current;
158
+ }));
159
+ results[step.id] = pipeResults;
160
+ frame.output = pipeResults;
161
+ frame.end = Date.now();
162
+ return pipeResults;
163
+ }
130
164
  if (step.__subflowId) {
165
+ // -----------------------------
166
+ // Subflow handling
167
+ // -----------------------------
131
168
  const [modId, subWfId] = step.__subflowId.split(".");
132
169
  const exec = depsExecutors[modId];
133
170
  const subExecution = await exec.run(subWfId, resolvedArgs, services, observers);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pogodisco/zephyr",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },