@pogodisco/zephyr 1.2.8 → 1.2.9

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,8 +1,12 @@
1
- import { Action, ActionRegistry, MergeActionRegistries } from "./types.js";
1
+ import { ActionRegistry, MergeActionRegistries } from "./types.js";
2
2
  export declare class ActionRegistryBuilder<R extends ActionRegistry = {}> {
3
3
  private registry;
4
4
  constructor(initial?: R);
5
- action<K extends string, I, O>(key: K, action: Action<I, O>): ActionRegistryBuilder<MergeActionRegistries<R, Record<K, Action<I, O>>>>;
5
+ /**
6
+ * Accepts ANY function - sync or async, any parameter shape
7
+ * F captures the complete function signature for full type inference
8
+ */
9
+ action<K extends string, F extends (...args: any[]) => any>(key: K, action: F): ActionRegistryBuilder<MergeActionRegistries<R, Record<K, F>>>;
6
10
  extend<Other extends ActionRegistry>(other: ActionRegistryBuilder<Other> | Other): ActionRegistryBuilder<MergeActionRegistries<R, Other>>;
7
11
  build(): R;
8
12
  }
package/dist/registry.js CHANGED
@@ -1,3 +1,43 @@
1
+ // import { Action, ActionRegistry, MergeActionRegistries } from "./types.js";
2
+ //
3
+ // export class ActionRegistryBuilder<R extends ActionRegistry = {}> {
4
+ // private registry: Partial<R> = {};
5
+ //
6
+ // constructor(initial?: R) {
7
+ // if (initial) {
8
+ // this.registry = { ...initial };
9
+ // }
10
+ // }
11
+ //
12
+ // action<K extends string, I, O>(
13
+ // key: K,
14
+ // action: Action<I, O>,
15
+ // ): ActionRegistryBuilder<MergeActionRegistries<R, Record<K, Action<I, O>>>> {
16
+ // (this.registry as any)[key] = action;
17
+ // return this as any;
18
+ // }
19
+ //
20
+ // // Extend with another registry (with override)
21
+ // extend<Other extends ActionRegistry>(
22
+ // other: ActionRegistryBuilder<Other> | Other,
23
+ // ): ActionRegistryBuilder<MergeActionRegistries<R, Other>> {
24
+ // const otherRegistry =
25
+ // other instanceof ActionRegistryBuilder ? other.build() : other;
26
+ //
27
+ // Object.assign(this.registry, otherRegistry);
28
+ // return this as any;
29
+ // }
30
+ //
31
+ // build(): R {
32
+ // return this.registry as R;
33
+ // }
34
+ // }
35
+ //
36
+ // export function createActionRegistry<R extends ActionRegistry = {}>(
37
+ // initial?: R,
38
+ // ): ActionRegistryBuilder<R> {
39
+ // return new ActionRegistryBuilder(initial);
40
+ // }
1
41
  export class ActionRegistryBuilder {
2
42
  constructor(initial) {
3
43
  this.registry = {};
@@ -5,6 +45,10 @@ export class ActionRegistryBuilder {
5
45
  this.registry = { ...initial };
6
46
  }
7
47
  }
48
+ /**
49
+ * Accepts ANY function - sync or async, any parameter shape
50
+ * F captures the complete function signature for full type inference
51
+ */
8
52
  action(key, action) {
9
53
  this.registry[key] = action;
10
54
  return this;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createWorkflow } from "./workflow-composer.js";
2
- export type Action<I = any, O = any> = (args: I) => Promise<O>;
2
+ export type Action<F extends (...args: any[]) => any = (...args: any[]) => any> = F;
3
3
  export interface ActionRegistry {
4
4
  [key: string]: Action;
5
5
  }
@@ -13,6 +13,8 @@ export type ExecutionFrame = {
13
13
  output?: any;
14
14
  error?: any;
15
15
  };
16
+ export type ActionParams<Reg extends ActionRegistry, K extends keyof Reg> = Parameters<Reg[K]>;
17
+ export type ActionReturn<Reg extends ActionRegistry, K extends keyof Reg> = Awaited<ReturnType<Reg[K]>>;
16
18
  export type WorkflowMiddleware<Reg extends ActionRegistry = any> = {
17
19
  (ctx: {
18
20
  stepId: string;
package/dist/types.js CHANGED
@@ -1 +1,47 @@
1
+ // import { createWorkflow } from "./workflow-composer.js";
2
+ //
3
+ // export type Action<I = any, O = any> = (args: I) => Promise<O>;
4
+ //
5
+ // export interface ActionRegistry {
6
+ // [key: string]: Action;
7
+ // }
8
+ //
9
+ // export type MergeActionRegistries<
10
+ // A extends ActionRegistry,
11
+ // B extends ActionRegistry,
12
+ // > = Omit<A, keyof B> & B;
13
+ //
14
+ // export type ExecutionFrame = {
15
+ // stepId: string;
16
+ // attempts: number;
17
+ // start: number;
18
+ // end?: number;
19
+ // input?: any;
20
+ // output?: any;
21
+ // error?: any;
22
+ // };
23
+ //
24
+ // export type WorkflowMiddleware<Reg extends ActionRegistry = any> = {
25
+ // (
26
+ // ctx: {
27
+ // stepId: string;
28
+ // input: any;
29
+ // results: Record<string, any>;
30
+ // registry: Reg;
31
+ // extras: Record<string, any>;
32
+ // frame: ExecutionFrame;
33
+ // },
34
+ // next: () => Promise<any>,
35
+ // ): Promise<any>;
36
+ // };
37
+ //
38
+ // export type WF<
39
+ // Reg extends ActionRegistry,
40
+ // Context extends Record<string, any>,
41
+ // > = ReturnType<typeof createWorkflow<Reg, Context>>;
42
+ // // TODO: this needs enforcing
43
+ // type Observer = (
44
+ // frame: Readonly<ExecutionFrame>,
45
+ // extras: Record<string, any>,
46
+ // ) => void | Promise<void>;
1
47
  export {};
package/dist/utils.d.ts CHANGED
@@ -1,8 +1,14 @@
1
- import { TaskMap, TaskNodeWithContracts, TasksFromFns } from "./node/types.js";
2
- export declare function createAction<FN extends (args: any) => any, // Allow sync or async
3
- Out = Awaited<ReturnType<FN>>>(fn: FN): TaskNodeWithContracts<TasksFromFns<{
4
- run: FN;
5
- }>, Parameters<FN>[0], Out>;
6
- export declare function genericAction<FN extends (args: any) => any>(fn: FN): <T = Awaited<ReturnType<FN>>>() => (initArgs: Parameters<FN>[0]) => Promise<T>;
7
- export declare function fixedAction<FN extends (args: any) => any, T = Awaited<ReturnType<FN>>>(fn: FN): () => (args: Parameters<FN>[0]) => Promise<T>;
8
- export declare function useAction<T extends TaskMap, I extends Record<string, any> | undefined, O>(node: TaskNodeWithContracts<T, I, O>): (initArgs: I) => Promise<O>;
1
+ type AnyFn = (...args: any[]) => Promise<any>;
2
+ type Input<F extends AnyFn> = Parameters<F>[0];
3
+ type Output<F extends AnyFn> = Awaited<ReturnType<F>>;
4
+ /**
5
+ * For generic actions where you want to specify the return type
6
+ * Just a type helper, does nothing at runtime
7
+ */
8
+ export declare function genericAction<F extends AnyFn>(fn: F): <T = Output<F>>() => (args: Input<F>) => Promise<T>;
9
+ /**
10
+ * For fixed actions - just returns the function
11
+ * The type parameter T is for convenience when you want to override the return type
12
+ */
13
+ export declare function fixedAction<F extends AnyFn>(fn: F): <T = Output<F>>() => ((args: Input<F>) => Promise<T>);
14
+ export {};
package/dist/utils.js CHANGED
@@ -1,25 +1,55 @@
1
- import { defineNode, execNode } from "./node/main.js";
2
- export function createAction(fn) {
3
- // Just return the raw node definition, not wrapped with useNode
4
- return defineNode({
5
- run: {
6
- fn, // Function can throw or return data
7
- argMap: (r) => r._init,
8
- },
9
- _output: (r) => r.run,
10
- });
11
- }
1
+ // import { defineNode, execNode } from "./node/main.js";
2
+ // import { TaskMap, TaskNodeWithContracts, TasksFromFns } from "./node/types.js";
3
+ //
4
+ // export function createAction<
5
+ // FN extends (args: any) => any, // Allow sync or async
6
+ // Out = Awaited<ReturnType<FN>>, // Unwrap Promise if present
7
+ // >(fn: FN) {
8
+ // // Just return the raw node definition, not wrapped with useNode
9
+ // return defineNode<TasksFromFns<{ run: FN }>, Parameters<FN>[0], Out>({
10
+ // run: {
11
+ // fn, // Function can throw or return data
12
+ // argMap: (r) => r._init,
13
+ // },
14
+ // _output: (r) => r.run as Out,
15
+ // });
16
+ // }
17
+ //
18
+ // export function genericAction<FN extends (args: any) => any>(fn: FN) {
19
+ // return <T = Awaited<ReturnType<FN>>>() => useAction(createAction<FN, T>(fn));
20
+ // }
21
+ //
22
+ // export function fixedAction<
23
+ // FN extends (args: any) => any,
24
+ // T = Awaited<ReturnType<FN>>,
25
+ // >(fn: FN): () => (args: Parameters<FN>[0]) => Promise<T> {
26
+ // return () => useAction(createAction<FN, T>(fn));
27
+ // }
28
+ //
29
+ // export function useAction<
30
+ // T extends TaskMap,
31
+ // I extends Record<string, any> | undefined,
32
+ // O,
33
+ // >(node: TaskNodeWithContracts<T, I, O>) {
34
+ // // Returns a function that graph can call, but WITHOUT withResponse
35
+ // // Just a simple adapter that calls callNode and returns raw _output
36
+ // return async (initArgs: I): Promise<O> => {
37
+ // const result = await execNode(node, initArgs);
38
+ // return result._output; // Just raw data, throws on error
39
+ // };
40
+ // }
41
+ //
42
+ /**
43
+ * For generic actions where you want to specify the return type
44
+ * Just a type helper, does nothing at runtime
45
+ */
12
46
  export function genericAction(fn) {
13
- return () => useAction(createAction(fn));
47
+ return () => fn;
14
48
  }
49
+ /**
50
+ * For fixed actions - just returns the function
51
+ * The type parameter T is for convenience when you want to override the return type
52
+ */
15
53
  export function fixedAction(fn) {
16
- return () => useAction(createAction(fn));
17
- }
18
- export function useAction(node) {
19
- // Returns a function that graph can call, but WITHOUT withResponse
20
- // Just a simple adapter that calls callNode and returns raw _output
21
- return async (initArgs) => {
22
- const result = await execNode(node, initArgs);
23
- return result._output; // Just raw data, throws on error
24
- };
54
+ return () => fn;
25
55
  }
@@ -1,11 +1,11 @@
1
- import { ActionRegistry } from "./types.js";
1
+ import { ActionRegistry, ActionParams, ActionReturn } from "./types.js";
2
2
  type SubflowResult<SubResults, SubOutput> = SubOutput extends undefined ? SubResults : SubOutput;
3
- type StepResult<Reg extends ActionRegistry, ActionName extends keyof Reg> = Awaited<ReturnType<Reg[ActionName]>>;
3
+ type StepResult<Reg extends ActionRegistry, ActionName extends keyof Reg> = ActionReturn<Reg, ActionName>;
4
4
  export type StepDef<Reg extends ActionRegistry, ID extends string = string, ActionName extends keyof Reg = any> = {
5
5
  id: ID;
6
6
  action: ActionName;
7
7
  dependsOn: string[];
8
- resolve: (ctx: any) => Parameters<Reg[ActionName]>[0];
8
+ resolve: (ctx: any) => ActionParams<Reg, ActionName>;
9
9
  when?: (ctx: any) => boolean;
10
10
  };
11
11
  export type WorkflowDef<Reg extends ActionRegistry, Input, Results, Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[], Output = undefined> = {
@@ -26,7 +26,7 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
26
26
  private frontier;
27
27
  private outputResolver?;
28
28
  constructor(name: string, registry: Reg, context: Context);
29
- step<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: Parameters<Reg[ActionName]>[0] extends undefined ? (ctx?: {
29
+ step<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: ActionParams<Reg, ActionName> extends [] ? (ctx?: {
30
30
  input: Input;
31
31
  results: Results;
32
32
  context: Context;
@@ -34,13 +34,13 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
34
34
  input: Input;
35
35
  results: Results;
36
36
  context: Context;
37
- }) => Parameters<Reg[ActionName]>[0], dependsOn?: string[]): WorkflowBuilder<Reg, Input, Context, [
37
+ }) => ActionParams<Reg, ActionName>, dependsOn?: string[]): WorkflowBuilder<Reg, Input, Context, [
38
38
  ...Steps,
39
39
  StepDef<Reg, ID, ActionName>
40
40
  ], Results & {
41
41
  [K in ID]: StepResult<Reg, ActionName>;
42
42
  }>;
43
- seq<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: Parameters<Reg[ActionName]>[0] extends undefined ? (ctx?: {
43
+ seq<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: ActionParams<Reg, ActionName> extends [] ? (ctx?: {
44
44
  input: Input;
45
45
  results: Results;
46
46
  context: Context;
@@ -48,20 +48,20 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
48
48
  input: Input;
49
49
  results: Results;
50
50
  context: Context;
51
- }) => Parameters<Reg[ActionName]>[0]): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K in ID]: Awaited<ReturnType<Reg[ActionName]>>; }, undefined>;
51
+ }) => ActionParams<Reg, ActionName>): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K in ID]: Awaited<ReturnType<Reg[ActionName]>>; }, undefined>;
52
52
  parallel<Branches extends WorkflowBuilder<Reg, Input, Context, any, any>[]>(...branches: {
53
53
  [K in keyof Branches]: (builder: WorkflowBuilder<Reg, Input, Context, [], Results>) => Branches[K];
54
54
  }): WorkflowBuilder<Reg, Input, Context, [
55
55
  ...Steps,
56
56
  ...(Branches[number] extends WorkflowBuilder<Reg, any, any, infer S, any> ? S : never)
57
57
  ], Results & (Branches[number] extends WorkflowBuilder<Reg, any, any, any, infer R> ? UnionToIntersection<R> : {})>;
58
- join<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: Parameters<Reg[ActionName]>[0] extends undefined ? (ctx?: {
58
+ join<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: ActionParams<Reg, ActionName> extends [] ? (ctx?: {
59
59
  input: Input;
60
60
  results: Results;
61
61
  }) => undefined : (ctx: {
62
62
  input: Input;
63
63
  results: Results;
64
- }) => Parameters<Reg[ActionName]>[0]): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K in ID]: Awaited<ReturnType<Reg[ActionName]>>; }, undefined>;
64
+ }) => ActionParams<Reg, ActionName>): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K in ID]: Awaited<ReturnType<Reg[ActionName]>>; }, undefined>;
65
65
  subflow<Prefix extends string, SubInput, SubResults, SubSteps extends StepDef<Reg, any, any>[], SubOutput>(prefix: Prefix, workflow: WorkflowDef<Reg, SubInput, SubResults, SubSteps, SubOutput>, resolveInput?: (ctx: {
66
66
  input: Input;
67
67
  results: Results;
@@ -555,12 +555,14 @@ export class WorkflowBuilder {
555
555
  /* ------------------------------------------------ */
556
556
  /* Base Step */
557
557
  /* ------------------------------------------------ */
558
- step(id, action, resolve, dependsOn) {
558
+ step(id, action,
559
+ // 👇 resolve returns whatever the action expects (tuple, object, single value, etc.)
560
+ resolve, dependsOn) {
559
561
  const deps = dependsOn ?? [...this.frontier];
560
562
  this.steps.push({
561
563
  id,
562
564
  action,
563
- resolve: resolve ?? (() => undefined),
565
+ resolve: (resolve ?? (() => undefined)),
564
566
  dependsOn: deps,
565
567
  });
566
568
  this.frontier = [id];
@@ -608,7 +610,6 @@ export class WorkflowBuilder {
608
610
  dependsOn: step.dependsOn.map((d) => idMap.get(d)),
609
611
  resolve: (ctx) => {
610
612
  const subInput = resolveInput ? resolveInput(ctx) : undefined;
611
- // const subInput = resolveInput(ctx);
612
613
  return step.resolve({
613
614
  input: subInput,
614
615
  results: ctx.results,
@@ -97,12 +97,114 @@
97
97
  //
98
98
  // return { results: results as R, extras };
99
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
100
203
  import { composeMiddleware } from "./middleware.js";
101
204
  export async function executeWorkflow(workflow, registry, input, middleware = []) {
102
205
  const results = {};
103
206
  const extras = {};
104
207
  extras.frames = {};
105
- // --- strongly type steps ---
106
208
  const stepById = new Map(workflow.steps.map((s) => [s.id, s]));
107
209
  const remainingDeps = new Map();
108
210
  const dependents = new Map();
@@ -132,13 +234,24 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
132
234
  const ctx = { stepId, input, results, registry, extras, frame };
133
235
  const core = async () => {
134
236
  frame.attempts++;
135
- frame.input = step.resolve?.({ input, results }) ?? undefined;
237
+ // 👇 Get the resolved arguments (should be a tuple or undefined)
238
+ const resolvedArgs = step.resolve?.({ input, results });
239
+ frame.input = resolvedArgs;
136
240
  try {
137
241
  const action = registry[step.action];
138
- const result = frame.input === undefined
139
- ? await action()
140
- : await action(frame.input);
141
- // const result = await action(frame.input);
242
+ let result;
243
+ if (resolvedArgs === undefined) {
244
+ // No arguments - call with nothing
245
+ result = await action();
246
+ }
247
+ else if (Array.isArray(resolvedArgs)) {
248
+ // Tuple arguments - spread as positional params
249
+ result = await action(...resolvedArgs);
250
+ }
251
+ else {
252
+ // Single object argument (backward compatibility)
253
+ result = await action(resolvedArgs);
254
+ }
142
255
  frame.output = result;
143
256
  frame.end = Date.now();
144
257
  results[step.id] = result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pogodisco/zephyr",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },