@pogodisco/zephyr 1.3.20 → 1.4.0

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