@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.
@@ -1,6 +1,6 @@
1
- import { ActionRegistry, WorkflowMiddleware } from "./types.js";
2
- import { WorkflowDef } from "./workflow-composer.js";
3
- export declare function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>(workflow: WorkflowDef<Reg, I, R, any, O>, registry: Reg, input: I, middleware?: WorkflowMiddleware<Reg>[]): Promise<{
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, observers?: WorkflowObserver<Reg>[]): Promise<{
4
4
  results: R;
5
5
  output: O;
6
6
  extras: Record<string, any>;
@@ -1,215 +1,23 @@
1
- // import { composeMiddleware } from "../middleware/index.js";
2
- // import { ActionRegistry } from "../registry/types.js";
3
- // import { WorkflowDef } from "./main.js";
4
- // import { ExecutionFrame, WorkflowMiddleware } from "./types.js";
5
- //
6
- // export async function executeWorkflow<Reg extends ActionRegistry, I, R>(
7
- // workflow: WorkflowDef<Reg, I, R>,
8
- // registry: Reg,
9
- // input: I,
10
- // middleware: WorkflowMiddleware<Reg>[] = [],
11
- // ): Promise<{ results: R; extras: Record<string, any> }> {
12
- // const results: Record<string, any> = {};
13
- // const extras: Record<string, any> = {};
14
- // extras.frames = {} as Record<string, ExecutionFrame>; // <-- store execution frames per step
15
- //
16
- // const stepById = new Map(workflow.steps.map((s) => [s.id, s]));
17
- // const remainingDeps = new Map<string, number>();
18
- // const dependents = new Map<string, string[]>();
19
- // const ready: string[] = [];
20
- //
21
- // // Build dependency graph
22
- // for (const step of workflow.steps) {
23
- // remainingDeps.set(step.id, step.dependsOn.length);
24
- // if (step.dependsOn.length === 0) ready.push(step.id);
25
- //
26
- // for (const dep of step.dependsOn) {
27
- // if (!dependents.has(dep)) dependents.set(dep, []);
28
- // dependents.get(dep)!.push(step.id);
29
- // }
30
- // }
31
- //
32
- // let completed = 0;
33
- //
34
- // // Scheduler loop
35
- // while (ready.length > 0) {
36
- // const batch = ready.splice(0);
37
- //
38
- // await Promise.all(
39
- // batch.map(async (stepId) => {
40
- // const step = stepById.get(stepId)!;
41
- //
42
- // const frame: ExecutionFrame = {
43
- // stepId,
44
- // attempts: 0,
45
- // start: Date.now(),
46
- // };
47
- // extras.frames[stepId] = frame;
48
- //
49
- // const ctx = {
50
- // stepId,
51
- // input,
52
- // results,
53
- // registry,
54
- // extras,
55
- // frame,
56
- // };
57
- //
58
- // const core = async () => {
59
- // frame.attempts++;
60
- // frame.input = step.resolve({ input, results });
61
- //
62
- // try {
63
- // const action = registry[step.action];
64
- //
65
- // const result = await action(frame.input);
66
- // frame.output = result;
67
- // frame.end = Date.now();
68
- //
69
- // results[step.id] = result;
70
- // return result;
71
- // } catch (err) {
72
- // frame.error = err;
73
- // frame.end = Date.now();
74
- // throw err;
75
- // }
76
- // };
77
- //
78
- // const composed = composeMiddleware(middleware, ctx, core);
79
- // await composed();
80
- //
81
- // // Activate dependents
82
- // for (const childId of dependents.get(stepId) ?? []) {
83
- // const remaining = remainingDeps.get(childId)! - 1;
84
- // remainingDeps.set(childId, remaining);
85
- // if (remaining === 0) ready.push(childId);
86
- // }
87
- //
88
- // completed++;
89
- // }),
90
- // );
91
- // }
92
- //
93
- // // Deadlock detection
94
- // if (completed !== workflow.steps.length) {
95
- // throw new Error("Workflow execution failed (cycle or missing dependency)");
96
- // }
97
- //
98
- // return { results: results as R, extras };
99
- // }
100
- // import { composeMiddleware } from "./middleware.js";
101
- // import { ActionRegistry, ExecutionFrame, WorkflowMiddleware } from "./types.js";
102
- // import { StepDef, WorkflowDef } from "./workflow-composer.js";
103
- //
104
- // export async function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>(
105
- // workflow: WorkflowDef<Reg, I, R, any, O>,
106
- // registry: Reg,
107
- // input: I,
108
- // middleware: WorkflowMiddleware<Reg>[] = [],
109
- // ): Promise<{ results: R; output: O; extras: Record<string, any> }> {
110
- // const results: Record<string, any> = {};
111
- // const extras: Record<string, any> = {};
112
- // extras.frames = {} as Record<string, ExecutionFrame>;
113
- //
114
- // // --- strongly type steps ---
115
- //
116
- // const stepById = new Map<string, StepDef<Reg, any, any>>(
117
- // workflow.steps.map((s: StepDef<Reg, any, any>) => [s.id, s]),
118
- // );
119
- // const remainingDeps = new Map<string, number>();
120
- // const dependents = new Map<string, string[]>();
121
- // const ready: string[] = [];
122
- //
123
- // // Build dependency graph
124
- // for (const step of workflow.steps) {
125
- // remainingDeps.set(step.id, step.dependsOn.length);
126
- // if (step.dependsOn.length === 0) ready.push(step.id);
127
- //
128
- // for (const dep of step.dependsOn) {
129
- // if (!dependents.has(dep)) dependents.set(dep, []);
130
- // dependents.get(dep)!.push(step.id);
131
- // }
132
- // }
133
- //
134
- // let completed = 0;
135
- //
136
- // while (ready.length > 0) {
137
- // const batch = ready.splice(0);
138
- //
139
- // await Promise.all(
140
- // batch.map(async (stepId) => {
141
- // const step = stepById.get(stepId)!;
142
- //
143
- // const frame: ExecutionFrame = {
144
- // stepId,
145
- // attempts: 0,
146
- // start: Date.now(),
147
- // };
148
- // extras.frames[stepId] = frame;
149
- //
150
- // const ctx = { stepId, input, results, registry, extras, frame };
151
- //
152
- // const core = async () => {
153
- // frame.attempts++;
154
- // frame.input = step.resolve?.({ input, results }) ?? undefined;
155
- //
156
- // try {
157
- // const action = registry[step.action];
158
- // const result =
159
- // frame.input === undefined
160
- // ? await (action as () => Promise<any>)()
161
- // : await action(frame.input);
162
- // // const result = await action(frame.input);
163
- // frame.output = result;
164
- // frame.end = Date.now();
165
- //
166
- // results[step.id] = result;
167
- // return result;
168
- // } catch (err) {
169
- // frame.error = err;
170
- // frame.end = Date.now();
171
- // throw err;
172
- // }
173
- // };
174
- //
175
- // const composed = composeMiddleware(middleware, ctx, core);
176
- // await composed();
177
- //
178
- // for (const childId of dependents.get(stepId) ?? []) {
179
- // const remaining = remainingDeps.get(childId)! - 1;
180
- // remainingDeps.set(childId, remaining);
181
- // if (remaining === 0) ready.push(childId);
182
- // }
183
- //
184
- // completed++;
185
- // }),
186
- // );
187
- // }
188
- //
189
- // if (completed !== workflow.steps.length) {
190
- // throw new Error("Workflow execution failed (cycle or missing dependency)");
191
- // }
192
- //
193
- // // Resolve output
194
- // const output: O = workflow.outputResolver
195
- // ? workflow.outputResolver({ input, results: results as R })
196
- // : (results as unknown as O);
197
- //
198
- // return { results: results as R, output, extras };
199
- // }
200
- //
201
- //
202
- ///// with any fn shape
203
- import { composeMiddleware } from "./middleware.js";
204
- export async function executeWorkflow(workflow, registry, input, middleware = []) {
1
+ import { composeObserver } from "./observer.js";
2
+ function createCallHelpers() {
3
+ return {
4
+ args: (...args) => ({ kind: "positional", args }),
5
+ obj: (arg) => ({ kind: "object", args: arg }),
6
+ none: () => ({ kind: "none" }),
7
+ loop: (items) => ({ kind: "loop", items }),
8
+ };
9
+ }
10
+ export async function executeWorkflow(workflow, registry, input, observers = []) {
205
11
  const results = {};
206
12
  const extras = {};
207
13
  extras.frames = {};
14
+ // -----------------------------
15
+ // Fully typed step map
16
+ // -----------------------------
208
17
  const stepById = new Map(workflow.steps.map((s) => [s.id, s]));
209
18
  const remainingDeps = new Map();
210
19
  const dependents = new Map();
211
20
  const ready = [];
212
- // Build dependency graph
213
21
  for (const step of workflow.steps) {
214
22
  remainingDeps.set(step.id, step.dependsOn.length);
215
23
  if (step.dependsOn.length === 0)
@@ -221,6 +29,28 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
221
29
  }
222
30
  }
223
31
  let completed = 0;
32
+ // -----------------------------
33
+ // Normalized runner
34
+ // -----------------------------
35
+ const runAction = async (input, action) => {
36
+ if (!input)
37
+ return await action();
38
+ switch (input.kind) {
39
+ case "none":
40
+ return await action();
41
+ case "positional":
42
+ return await action(...input.args);
43
+ case "object":
44
+ return await action(input.args);
45
+ case "loop":
46
+ return await Promise.all(input.items.map((item) => runAction(item, action)));
47
+ default:
48
+ throw new Error(`Unknown ResolvedStepInput kind: ${input.kind}`);
49
+ }
50
+ };
51
+ // -----------------------------
52
+ // Execution loop
53
+ // -----------------------------
224
54
  while (ready.length > 0) {
225
55
  const batch = ready.splice(0);
226
56
  await Promise.all(batch.map(async (stepId) => {
@@ -243,7 +73,8 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
243
73
  };
244
74
  const core = async () => {
245
75
  frame.attempts++;
246
- const stepCtx = { input, results, context };
76
+ const stepCtx = { input, results, context, ...createCallHelpers() };
77
+ // Conditional skip
247
78
  if (step.when && !step.when(stepCtx)) {
248
79
  frame.output = undefined;
249
80
  frame.end = Date.now();
@@ -251,25 +82,11 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
251
82
  results[step.id] = undefined;
252
83
  return undefined;
253
84
  }
254
- // 👇 Get the resolved arguments (should be a tuple or undefined)
255
- // const resolvedArgs = step.resolve?.({ input, results, context });
256
85
  const resolvedArgs = step.resolve?.(stepCtx);
257
86
  frame.input = resolvedArgs;
258
87
  try {
259
88
  const action = registry[step.action];
260
- let result;
261
- if (resolvedArgs === undefined) {
262
- // No arguments - call with nothing
263
- result = await action();
264
- }
265
- else if (Array.isArray(resolvedArgs)) {
266
- // Tuple arguments - spread as positional params
267
- result = await action(...resolvedArgs);
268
- }
269
- else {
270
- // Single object argument (backward compatibility)
271
- result = await action(resolvedArgs);
272
- }
89
+ const result = await runAction(resolvedArgs, action);
273
90
  frame.output = result;
274
91
  frame.end = Date.now();
275
92
  results[step.id] = result;
@@ -281,7 +98,7 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
281
98
  throw err;
282
99
  }
283
100
  };
284
- const composed = composeMiddleware(middleware, ctx, core);
101
+ const composed = composeObserver(observers, ctx, core);
285
102
  await composed();
286
103
  for (const childId of dependents.get(stepId) ?? []) {
287
104
  const remaining = remainingDeps.get(childId) - 1;
@@ -295,9 +112,12 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
295
112
  if (completed !== workflow.steps.length) {
296
113
  throw new Error("Workflow execution failed (cycle or missing dependency)");
297
114
  }
298
- // Resolve output
299
115
  const output = workflow.outputResolver
300
- ? workflow.outputResolver({ input, results: results })
116
+ ? workflow.outputResolver({
117
+ input,
118
+ results: results,
119
+ context: workflow.__context,
120
+ })
301
121
  : results;
302
122
  return { results: results, output, extras };
303
123
  }
@@ -1,18 +1,23 @@
1
1
  import { ActionRegistry } from "./types.js";
2
2
  import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
3
- export declare function workflowModule<Reg extends ActionRegistry, Context extends Record<string, any>, Flows extends Record<string, any>>(registry: Reg, context: Context, factory: (wf: ReturnType<typeof createWorkflow<Reg, Context>>) => Flows): {
4
- wf: <Input = unknown>(name: string) => import("./workflow-composer.js").WorkflowBuilder<Reg, Input, Context, [], {}, undefined>;
5
- } & Flows & {
6
- extend<Extra extends Record<string, any>>(overrides: Extra): Flows & Extra;
7
- };
8
3
  type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
9
4
  type ModuleShape = Record<string, AnyWorkflow>;
10
5
  type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
11
6
  type UnionToIntersectionOrEmpty<U> = [U] extends [never] ? {} : UnionToIntersection<U>;
7
+ export type ModuleContext<Reg extends ActionRegistry, Context extends Record<string, any>, Deps extends ModuleShape> = {
8
+ wf: ReturnType<typeof createWorkflow<Reg, Context>>;
9
+ deps: Deps;
10
+ context: Context;
11
+ tools: <T>(factory: (ctx: {
12
+ wf: ModuleContext<Reg, Context, Deps>["wf"];
13
+ deps: Deps;
14
+ context: Context;
15
+ }) => T) => T;
16
+ };
12
17
  export declare function createModule<Reg extends ActionRegistry, Context extends Record<string, any>, Use extends ModuleShape[] = [], Deps extends ModuleShape = UnionToIntersectionOrEmpty<Use[number]> & ModuleShape, Own extends ModuleShape = {}>({ registry, context, use, define, }: {
13
18
  registry: Reg;
14
19
  context: Context;
15
20
  use?: [...Use];
16
- define: (wf: ReturnType<typeof createWorkflow<Reg, Context>>, deps: Deps) => Own;
21
+ define: (ctx: ModuleContext<Reg, Context, Deps>) => Own;
17
22
  }): Deps & Own;
18
23
  export {};
@@ -1,44 +1,14 @@
1
- import { createWorkflow } from "./workflow-composer.js";
2
- // export function workflowModule<
3
- // Reg extends ActionRegistry,
4
- // Context extends Record<string, any>,
5
- // Flows extends Record<string, any>,
6
- // >(
7
- // registry: Reg,
8
- // context: Context,
9
- // factory: (wf: ReturnType<typeof createWorkflow<Reg, Context>>) => Flows,
10
- // ) {
11
- // const wf = createWorkflow(registry, context);
12
- // const flows = factory(wf);
13
- //
14
- // return {
15
- // ...flows,
16
- // extend(overrides: Partial<Flows>): Flows {
17
- // return {
18
- // ...flows,
19
- // ...overrides,
20
- // };
21
- // },
22
- // };
23
- // }
24
- export function workflowModule(registry, context, factory) {
25
- const wf = createWorkflow(registry, context);
26
- const flows = factory(wf);
27
- return {
28
- wf,
29
- ...flows,
30
- extend(overrides) {
31
- return {
32
- ...flows,
33
- ...overrides,
34
- };
35
- },
36
- };
37
- }
1
+ import { createWorkflow, } from "./workflow-composer.js";
38
2
  export function createModule({ registry, context, use, define, }) {
39
3
  const wf = createWorkflow(registry, context);
40
4
  const deps = (use ? Object.assign({}, ...use) : {});
41
- const own = define(wf, deps);
5
+ const moduleCtx = {
6
+ wf,
7
+ deps,
8
+ context,
9
+ tools: (factory) => factory({ wf, deps, context }),
10
+ };
11
+ const own = define(moduleCtx);
42
12
  return {
43
13
  ...deps,
44
14
  ...own,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pogodisco/zephyr",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },