@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.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/observer.d.ts +4 -0
- package/dist/observer.js +69 -0
- package/dist/registry.js +0 -40
- package/dist/session.d.ts +3 -3
- package/dist/session.js +4 -4
- package/dist/types.d.ts +1 -1
- package/dist/types.js +0 -46
- package/dist/utils.js +0 -41
- package/dist/workflow-composer.d.ts +56 -23
- package/dist/workflow-composer.js +230 -350
- package/dist/workflow-executor.d.ts +3 -3
- package/dist/workflow-executor.js +44 -224
- package/dist/workflow-module.d.ts +11 -6
- package/dist/workflow-module.js +8 -38
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ActionRegistry,
|
|
2
|
-
import { WorkflowDef } from "./workflow-composer.js";
|
|
3
|
-
export declare function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>(workflow: WorkflowDef<Reg, I, R,
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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 =
|
|
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({
|
|
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: (
|
|
21
|
+
define: (ctx: ModuleContext<Reg, Context, Deps>) => Own;
|
|
17
22
|
}): Deps & Own;
|
|
18
23
|
export {};
|
package/dist/workflow-module.js
CHANGED
|
@@ -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
|
|
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,
|