@pogodisco/zephyr 1.2.8 → 1.3.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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/registry.d.ts +6 -2
- package/dist/registry.js +44 -0
- package/dist/session.d.ts +25 -0
- package/dist/session.js +58 -0
- package/dist/types.d.ts +3 -1
- package/dist/types.js +46 -0
- package/dist/utils.d.ts +14 -8
- package/dist/utils.js +51 -21
- package/dist/workflow-composer.d.ts +10 -9
- package/dist/workflow-composer.js +5 -3
- package/dist/workflow-executor.js +129 -7
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/registry.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ActionRegistry, WorkflowMiddleware } from "./types.js";
|
|
2
|
+
import { WorkflowDef } from "./workflow-composer.js";
|
|
3
|
+
type ModuleFlows<Reg extends ActionRegistry> = Record<string, WorkflowDef<Reg, any, any>>;
|
|
4
|
+
export declare class WorkflowSession<Reg extends ActionRegistry, State extends Record<string, any>> {
|
|
5
|
+
private registry;
|
|
6
|
+
private subscribers;
|
|
7
|
+
private running;
|
|
8
|
+
private queue;
|
|
9
|
+
state: State;
|
|
10
|
+
middleware: WorkflowMiddleware[];
|
|
11
|
+
private moduleFlows;
|
|
12
|
+
/**
|
|
13
|
+
* @param baseModule Module to extend (inherits workflows)
|
|
14
|
+
* @param registry Action registry
|
|
15
|
+
* @param initialContext Initial context (session state) — will be mutated in workflows
|
|
16
|
+
*/
|
|
17
|
+
constructor(baseModule: ModuleFlows<Reg>, registry: Reg, initialContext: State, middleware: WorkflowMiddleware[]);
|
|
18
|
+
getState(): State;
|
|
19
|
+
subscribe(fn: (state: Partial<State>) => void): () => boolean;
|
|
20
|
+
private notify;
|
|
21
|
+
/** Queue a workflow execution by key and input; state/context is automatically updated */
|
|
22
|
+
dispatch(key: keyof ModuleFlows<Reg>, input?: any): void;
|
|
23
|
+
private processQueue;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { executeWorkflow } from "./workflow-executor.js";
|
|
2
|
+
import { createModule } from "./workflow-module.js";
|
|
3
|
+
export class WorkflowSession {
|
|
4
|
+
/**
|
|
5
|
+
* @param baseModule Module to extend (inherits workflows)
|
|
6
|
+
* @param registry Action registry
|
|
7
|
+
* @param initialContext Initial context (session state) — will be mutated in workflows
|
|
8
|
+
*/
|
|
9
|
+
constructor(baseModule, registry, initialContext, middleware) {
|
|
10
|
+
this.registry = registry;
|
|
11
|
+
this.subscribers = new Set();
|
|
12
|
+
this.running = false;
|
|
13
|
+
this.queue = [];
|
|
14
|
+
this.middleware = [];
|
|
15
|
+
// Use the same object for session state and module context
|
|
16
|
+
this.state = initialContext;
|
|
17
|
+
this.middleware = middleware;
|
|
18
|
+
// Per-session module: inherits workflows, shares the same state/context object
|
|
19
|
+
this.moduleFlows = createModule({
|
|
20
|
+
registry: this.registry,
|
|
21
|
+
context: this.state,
|
|
22
|
+
use: [baseModule],
|
|
23
|
+
define: () => ({}), // no new flows needed
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
getState() {
|
|
27
|
+
return this.state;
|
|
28
|
+
}
|
|
29
|
+
subscribe(fn) {
|
|
30
|
+
this.subscribers.add(fn);
|
|
31
|
+
fn(this.state); // immediately notify
|
|
32
|
+
return () => this.subscribers.delete(fn);
|
|
33
|
+
}
|
|
34
|
+
notify() {
|
|
35
|
+
for (const fn of this.subscribers)
|
|
36
|
+
fn(this.state);
|
|
37
|
+
}
|
|
38
|
+
/** Queue a workflow execution by key and input; state/context is automatically updated */
|
|
39
|
+
dispatch(key, input = {}) {
|
|
40
|
+
this.queue.push({ key, input });
|
|
41
|
+
if (!this.running)
|
|
42
|
+
this.processQueue();
|
|
43
|
+
}
|
|
44
|
+
async processQueue() {
|
|
45
|
+
this.running = true;
|
|
46
|
+
while (this.queue.length) {
|
|
47
|
+
const { key, input } = this.queue.shift();
|
|
48
|
+
const workflow = this.moduleFlows[key];
|
|
49
|
+
if (!workflow)
|
|
50
|
+
throw new Error(`Workflow ${String(key)} not found`);
|
|
51
|
+
// state/context already lives on the module; no need to pass a separate context
|
|
52
|
+
await executeWorkflow(workflow, this.registry, input, this.middleware);
|
|
53
|
+
// Notify subscribers after workflow mutates state
|
|
54
|
+
this.notify();
|
|
55
|
+
}
|
|
56
|
+
this.running = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createWorkflow } from "./workflow-composer.js";
|
|
2
|
-
export type Action<
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare function
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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 () =>
|
|
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 () =>
|
|
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> =
|
|
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) =>
|
|
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> = {
|
|
@@ -16,6 +16,7 @@ export type WorkflowDef<Reg extends ActionRegistry, Input, Results, Steps extend
|
|
|
16
16
|
input: Input;
|
|
17
17
|
results: Results;
|
|
18
18
|
outputResolver?: (ctx: any) => Output;
|
|
19
|
+
__context?: any;
|
|
19
20
|
};
|
|
20
21
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
21
22
|
export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown, Context extends Record<string, any> = {}, Steps extends StepDef<Reg, any, any>[] = [], Results = {}, Output = undefined> {
|
|
@@ -26,7 +27,7 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
|
|
|
26
27
|
private frontier;
|
|
27
28
|
private outputResolver?;
|
|
28
29
|
constructor(name: string, registry: Reg, context: Context);
|
|
29
|
-
step<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?:
|
|
30
|
+
step<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: ActionParams<Reg, ActionName> extends [] ? (ctx?: {
|
|
30
31
|
input: Input;
|
|
31
32
|
results: Results;
|
|
32
33
|
context: Context;
|
|
@@ -34,13 +35,13 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
|
|
|
34
35
|
input: Input;
|
|
35
36
|
results: Results;
|
|
36
37
|
context: Context;
|
|
37
|
-
}) =>
|
|
38
|
+
}) => ActionParams<Reg, ActionName>, dependsOn?: string[]): WorkflowBuilder<Reg, Input, Context, [
|
|
38
39
|
...Steps,
|
|
39
40
|
StepDef<Reg, ID, ActionName>
|
|
40
41
|
], Results & {
|
|
41
42
|
[K in ID]: StepResult<Reg, ActionName>;
|
|
42
43
|
}>;
|
|
43
|
-
seq<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?:
|
|
44
|
+
seq<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: ActionParams<Reg, ActionName> extends [] ? (ctx?: {
|
|
44
45
|
input: Input;
|
|
45
46
|
results: Results;
|
|
46
47
|
context: Context;
|
|
@@ -48,20 +49,20 @@ export declare class WorkflowBuilder<Reg extends ActionRegistry, Input = unknown
|
|
|
48
49
|
input: Input;
|
|
49
50
|
results: Results;
|
|
50
51
|
context: Context;
|
|
51
|
-
}) =>
|
|
52
|
+
}) => ActionParams<Reg, ActionName>): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K in ID]: Awaited<ReturnType<Reg[ActionName]>>; }, undefined>;
|
|
52
53
|
parallel<Branches extends WorkflowBuilder<Reg, Input, Context, any, any>[]>(...branches: {
|
|
53
54
|
[K in keyof Branches]: (builder: WorkflowBuilder<Reg, Input, Context, [], Results>) => Branches[K];
|
|
54
55
|
}): WorkflowBuilder<Reg, Input, Context, [
|
|
55
56
|
...Steps,
|
|
56
57
|
...(Branches[number] extends WorkflowBuilder<Reg, any, any, infer S, any> ? S : never)
|
|
57
58
|
], 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?:
|
|
59
|
+
join<ID extends string, ActionName extends keyof Reg & string>(id: ID, action: ActionName, resolve?: ActionParams<Reg, ActionName> extends [] ? (ctx?: {
|
|
59
60
|
input: Input;
|
|
60
61
|
results: Results;
|
|
61
62
|
}) => undefined : (ctx: {
|
|
62
63
|
input: Input;
|
|
63
64
|
results: Results;
|
|
64
|
-
}) =>
|
|
65
|
+
}) => ActionParams<Reg, ActionName>): WorkflowBuilder<Reg, Input, Context, [...Steps, StepDef<Reg, ID, ActionName>], Results & { [K in ID]: Awaited<ReturnType<Reg[ActionName]>>; }, undefined>;
|
|
65
66
|
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
67
|
input: Input;
|
|
67
68
|
results: Results;
|
|
@@ -555,12 +555,14 @@ export class WorkflowBuilder {
|
|
|
555
555
|
/* ------------------------------------------------ */
|
|
556
556
|
/* Base Step */
|
|
557
557
|
/* ------------------------------------------------ */
|
|
558
|
-
step(id, action,
|
|
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,
|
|
@@ -638,6 +639,7 @@ export class WorkflowBuilder {
|
|
|
638
639
|
input: {},
|
|
639
640
|
results: {},
|
|
640
641
|
outputResolver: this.outputResolver,
|
|
642
|
+
__context: this.context,
|
|
641
643
|
};
|
|
642
644
|
}
|
|
643
645
|
validateDependencies() {
|
|
@@ -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();
|
|
@@ -129,16 +231,36 @@ export async function executeWorkflow(workflow, registry, input, middleware = []
|
|
|
129
231
|
start: Date.now(),
|
|
130
232
|
};
|
|
131
233
|
extras.frames[stepId] = frame;
|
|
132
|
-
const
|
|
234
|
+
const context = workflow.__context;
|
|
235
|
+
const ctx = {
|
|
236
|
+
stepId,
|
|
237
|
+
input,
|
|
238
|
+
results,
|
|
239
|
+
context,
|
|
240
|
+
registry,
|
|
241
|
+
extras,
|
|
242
|
+
frame,
|
|
243
|
+
};
|
|
133
244
|
const core = async () => {
|
|
134
245
|
frame.attempts++;
|
|
135
|
-
|
|
246
|
+
// 👇 Get the resolved arguments (should be a tuple or undefined)
|
|
247
|
+
const resolvedArgs = step.resolve?.({ input, results, context });
|
|
248
|
+
frame.input = resolvedArgs;
|
|
136
249
|
try {
|
|
137
250
|
const action = registry[step.action];
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
251
|
+
let result;
|
|
252
|
+
if (resolvedArgs === undefined) {
|
|
253
|
+
// No arguments - call with nothing
|
|
254
|
+
result = await action();
|
|
255
|
+
}
|
|
256
|
+
else if (Array.isArray(resolvedArgs)) {
|
|
257
|
+
// Tuple arguments - spread as positional params
|
|
258
|
+
result = await action(...resolvedArgs);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// Single object argument (backward compatibility)
|
|
262
|
+
result = await action(resolvedArgs);
|
|
263
|
+
}
|
|
142
264
|
frame.output = result;
|
|
143
265
|
frame.end = Date.now();
|
|
144
266
|
results[step.id] = result;
|