@pogodisco/zephyr 1.4.1 → 1.4.2

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 CHANGED
@@ -3,8 +3,6 @@ export * from "./registry.js";
3
3
  export * from "./workflow-executor.js";
4
4
  export * from "./workflow-composer.js";
5
5
  export * from "./workflow-module.js";
6
- export * from "./utils.js";
7
6
  export * from "./types.js";
8
- export * from "./utils.js";
9
7
  export * from "./session.js";
10
8
  export { useMetrics, useLog } from "./observer.js";
package/dist/index.js CHANGED
@@ -3,8 +3,6 @@ export * from "./registry.js";
3
3
  export * from "./workflow-executor.js";
4
4
  export * from "./workflow-composer.js";
5
5
  export * from "./workflow-module.js";
6
- export * from "./utils.js";
7
6
  export * from "./types.js";
8
- export * from "./utils.js";
9
7
  export * from "./session.js";
10
8
  export { useMetrics, useLog } from "./observer.js";
package/dist/observer.js CHANGED
@@ -15,16 +15,17 @@ export function composeObserver(middleware, ctx, core) {
15
15
  export function useLog() {
16
16
  return async ({ frame, stepId }, next) => {
17
17
  eventStream.emit({
18
- type: "node_start",
19
- node: stepId,
18
+ stepId: frame.stepId,
19
+ state: "start",
20
+ start: frame.start,
20
21
  timestamp: frame.start,
21
22
  input: frame.input,
22
23
  });
23
24
  try {
24
25
  const res = await next();
25
26
  eventStream.emit({
26
- type: "node_success",
27
- node: stepId,
27
+ id: frame.stepId,
28
+ state: frame.skipped ? "skipped" : "success",
28
29
  output: frame.output,
29
30
  duration: frame.end - frame.start,
30
31
  attempts: frame.attempts,
@@ -34,11 +35,11 @@ export function useLog() {
34
35
  }
35
36
  catch (err) {
36
37
  eventStream.emit({
37
- type: "node_fail",
38
- node: stepId,
38
+ ...frame,
39
+ state: "fail",
39
40
  error: frame.error,
40
41
  timestamp: Date.now(),
41
- attempts: frame.attempts,
42
+ // attempts: frame.attempts,
42
43
  });
43
44
  throw err;
44
45
  }
package/dist/types.d.ts CHANGED
@@ -26,3 +26,6 @@ export type WorkflowObserver<Reg extends ActionRegistry = any> = {
26
26
  frame: ExecutionFrame;
27
27
  }, next: () => Promise<any>): Promise<any>;
28
28
  };
29
+ export interface Executor {
30
+ run: (wfId: string, input: Record<string, any>, context: Record<string, any>, obververs?: WorkflowObserver[]) => Promise<any>;
31
+ }
package/dist/utils.d.ts CHANGED
@@ -1,10 +1 @@
1
- type AnyFn = (...args: any[]) => any;
2
- /**
3
- * For generic actions - preserves the generic parameter
4
- */
5
- export declare function genericAction<F extends AnyFn>(fn: F): <T>() => ((...args: Parameters<F>) => ReturnType<F>);
6
- /**
7
- * For fixed actions
8
- */
9
- export declare function fixedAction<F extends AnyFn>(fn: F): () => ((...args: Parameters<F>) => ReturnType<F>);
10
- export {};
1
+ export declare function generateWorkflowId(name: string): string;
package/dist/utils.js CHANGED
@@ -1,35 +1,6 @@
1
- /**
2
- * For generic actions - preserves the generic parameter
3
- */
4
- export function genericAction(fn) {
5
- // Return a function that takes a type parameter and returns the typed function
6
- return () => {
7
- return fn;
8
- };
1
+ export function generateWorkflowId(name) {
2
+ // Short random + timestamp for practically unique ID
3
+ const random = Math.random().toString(36).slice(2, 10);
4
+ const time = Date.now().toString(36);
5
+ return `${name}-${time}-${random}`;
9
6
  }
10
- /**
11
- * For fixed actions
12
- */
13
- export function fixedAction(fn) {
14
- return () => {
15
- return fn;
16
- };
17
- }
18
- // export function createRuntime<Reg extends ActionRegistry, Context>(
19
- // registry: Reg,
20
- // context: Context,
21
- // ) {
22
- // return {
23
- // run: async <W extends WorkflowDef<Reg, any, any, any, any>>(
24
- // workflow: W,
25
- // input: W extends WorkflowDef<Reg, infer I, any, any, any> ? I : never,
26
- // observers?: WorkflowObserver[],
27
- // ): Promise<{
28
- // output: W extends WorkflowDef<Reg, any, any, any, infer O> ? O : never;
29
- // results: W extends WorkflowDef<Reg, any, infer R, any, any> ? R : never;
30
- // extras: Record<string, any>;
31
- // }> => {
32
- // return executeWorkflow(workflow, registry, input, context, observers);
33
- // },
34
- // };
35
- // }
@@ -51,6 +51,7 @@ export type StepDef<Reg extends ActionRegistry, ID extends string = string, Acti
51
51
  };
52
52
  export type WorkflowDef<Reg extends ActionRegistry, Input, Results, Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[], Output = undefined> = {
53
53
  name: string;
54
+ _id: string;
54
55
  steps: Steps;
55
56
  entrySteps: StepDef<Reg>[];
56
57
  endSteps: StepDef<Reg>[];
@@ -1,354 +1,3 @@
1
- // import {
2
- // ActionParams,
3
- // ActionRegistry,
4
- // ActionReturn,
5
- // Simplify,
6
- // } from "./types.js";
7
- //
8
- // /* ------------------------------------------------ */
9
- // /* STEP INPUT NORMALIZATION TYPES */
10
- // /* ------------------------------------------------ */
11
- //
12
- // type NormalizedCall =
13
- // | { kind: "none" }
14
- // | { kind: "positional"; args: any[] }
15
- // | { kind: "object"; args: any };
16
- //
17
- // export type ResolvedStepInput =
18
- // | NormalizedCall
19
- // | { kind: "loop"; items: NormalizedCall[] };
20
- //
21
- // type CallHelpers<Reg extends ActionRegistry, ActionName extends keyof Reg> = {
22
- // args: (...args: ActionParams<Reg, ActionName>) => {
23
- // kind: "positional";
24
- // args: ActionParams<Reg, ActionName>;
25
- // };
26
- //
27
- // obj: ActionParams<Reg, ActionName> extends [infer A]
28
- // ? (arg: A) => { kind: "object"; args: A }
29
- // : never;
30
- //
31
- // none: () => { kind: "none" };
32
- // loop: (
33
- // items:
34
- // | { kind: "positional"; args: ActionParams<Reg, ActionName> }[]
35
- // | { kind: "object"; args: ActionParams<Reg, ActionName>[0] }[],
36
- // ) => {
37
- // kind: "loop";
38
- // items: typeof items;
39
- // };
40
- // };
41
- //
42
- // /* ------------------------------------------------ */
43
- // /* STEP RESULT TYPES */
44
- // /* ------------------------------------------------ */
45
- //
46
- // type SubflowResult<SubResults, SubOutput> = SubOutput extends undefined
47
- // ? SubResults
48
- // : SubOutput;
49
- //
50
- // type WorkflowInput<T> =
51
- // T extends WorkflowDef<any, infer I, any, any, any> ? I : never;
52
- //
53
- // type WorkflowResults<T> =
54
- // T extends WorkflowDef<any, any, infer R, any, any> ? R : never;
55
- //
56
- // type WorkflowOutput<T> =
57
- // T extends WorkflowDef<any, any, any, any, infer O> ? O : never;
58
- //
59
- // export type StepResultFromResolve<
60
- // Reg extends ActionRegistry,
61
- // ActionName extends keyof Reg,
62
- // R extends ResolvedStepInput,
63
- // > = R extends { kind: "loop" }
64
- // ? ActionReturn<Reg, ActionName>[]
65
- // : ActionReturn<Reg, ActionName>;
66
- //
67
- // /* ------------------------------------------------ */
68
- // /* STEP DEFINITION */
69
- // /* ------------------------------------------------ */
70
- //
71
- // export type StepDef<
72
- // Reg extends ActionRegistry,
73
- // ID extends string = string,
74
- // ActionName extends keyof Reg = any,
75
- // > = {
76
- // id: ID;
77
- // action: ActionName;
78
- // dependsOn: string[];
79
- // resolve: (ctx: any) => ResolvedStepInput;
80
- // when?: (ctx: any) => boolean;
81
- // options?: StepOptions<any, any, any>;
82
- // };
83
- //
84
- // /* ------------------------------------------------ */
85
- // /* WORKFLOW DEFINITION */
86
- // /* ------------------------------------------------ */
87
- //
88
- // export type WorkflowDef<
89
- // Reg extends ActionRegistry,
90
- // Input,
91
- // Results,
92
- // Steps extends StepDef<Reg, any, any>[] = StepDef<Reg, any, any>[],
93
- // Output = undefined,
94
- // > = {
95
- // name: string;
96
- // steps: Steps;
97
- // entrySteps: StepDef<Reg>[];
98
- // endSteps: StepDef<Reg>[];
99
- // input: Input;
100
- // results: Results;
101
- // outputResolver?: (ctx: any) => Output;
102
- // // __context?: any;
103
- // };
104
- // type StepRuntimeCtx<I, R, C> = {
105
- // input: I;
106
- // results: R;
107
- // context: C;
108
- // };
109
- // type StepOptions<Input, Results, Context> = {
110
- // retry?: number;
111
- // retryDelay?: number | ((attempt: number) => number);
112
- // timeout?: number;
113
- //
114
- // continueOnError?: boolean;
115
- //
116
- // onError?: (err: unknown, ctx: StepRuntimeCtx<Input, Results, Context>) => any;
117
- //
118
- // // optional later:
119
- // label?: string;
120
- // meta?: Record<string, any>;
121
- // };
122
- //
123
- // /* ------------------------------------------------ */
124
- // /* HELPER TYPES */
125
- // /* ------------------------------------------------ */
126
- //
127
- // type MergeBranchResults<
128
- // Branches extends readonly any[],
129
- // Acc,
130
- // > = Branches extends readonly [infer Head, ...infer Tail]
131
- // ? MergeBranchResults<
132
- // Tail,
133
- // Acc & (Head extends WorkflowBuilder<any, any, any, any, infer R> ? R : {})
134
- // >
135
- // : Acc;
136
- //
137
- // type MergeBranchSteps<
138
- // Branches extends readonly any[],
139
- // Acc extends any[],
140
- // > = Branches extends readonly [infer Head, ...infer Tail]
141
- // ? MergeBranchSteps<
142
- // Tail,
143
- // [
144
- // ...Acc,
145
- // ...(Head extends WorkflowBuilder<any, any, any, infer S, any> ? S : []),
146
- // ]
147
- // >
148
- // : Acc;
149
- //
150
- // // type UnionToIntersection<U> = (U extends any ? (x: U) => any : never) extends (
151
- // // x: infer I,
152
- // // ) => any
153
- // // ? I
154
- // // : never;
155
- // //
156
- // // type ExtractBranchResults<Branches extends readonly any[]> =
157
- // // Branches[number] extends WorkflowBuilder<any, any, any, any, infer R>
158
- // // ? R
159
- // // : never;
160
- // //
161
- // // type MergeBranchResults<Branches extends readonly any[], Acc> = Simplify<
162
- // // Acc & Partial<ExtractBranchResults<Branches>>
163
- // // >;
164
- //
165
- // /* ------------------------------------------------ */
166
- // /* FLUENT WORKFLOW BUILDER */
167
- // /* ------------------------------------------------ */
168
- //
169
- // export class WorkflowBuilder<
170
- // Reg extends ActionRegistry,
171
- // Input = unknown,
172
- // Context = unknown,
173
- // Steps extends StepDef<Reg, any, any>[] = [],
174
- // Results = {},
175
- // Output = undefined,
176
- // > {
177
- // private steps: StepDef<Reg, any, any>[] = [];
178
- // private frontier: string[] = [];
179
- // private pendingWhen?: (ctx: {
180
- // input: Input;
181
- // results: Results;
182
- // context: Context;
183
- // }) => boolean;
184
- // private outputResolver?: (ctx: any) => any;
185
- // private clearPendingWhen() {
186
- // this.pendingWhen = undefined;
187
- // }
188
- //
189
- // constructor(private name: string) {}
190
- //
191
- // step<
192
- // ID extends string,
193
- // ActionName extends keyof Reg & string,
194
- // ResolveOut extends ResolvedStepInput = ResolvedStepInput,
195
- // >(
196
- // id: ID,
197
- // action: ActionName,
198
- // resolve?: (
199
- // ctx: {
200
- // input: Input;
201
- // results: Results;
202
- // context: Context;
203
- // } & CallHelpers<Reg, ActionName>,
204
- // ) => ResolveOut,
205
- // dependsOn?: string[],
206
- // options?: StepOptions<Input, Results, Context>,
207
- // ): WorkflowBuilder<
208
- // Reg,
209
- // Input,
210
- // Context,
211
- // [...Steps, StepDef<Reg, ID, ActionName>],
212
- // Simplify<
213
- // Results & {
214
- // [K in ID]: StepResultFromResolve<Reg, ActionName, ResolveOut>;
215
- // }
216
- // >
217
- // > {
218
- // const deps = dependsOn ?? [...this.frontier];
219
- //
220
- // this.steps.push({
221
- // id,
222
- // action,
223
- // resolve: resolve ?? (() => ({ kind: "none" })),
224
- // dependsOn: deps,
225
- // when: this.pendingWhen,
226
- // options,
227
- // });
228
- //
229
- // this.frontier = [id];
230
- //
231
- // return this as any;
232
- // }
233
- //
234
- // /* ------------------------------------------------ */
235
- // /* Sequential shortcut */
236
- // /* ------------------------------------------------ */
237
- // seq<
238
- // ID extends string = string,
239
- // ActionName extends keyof Reg & string = any,
240
- // ResolveOut extends ResolvedStepInput = ResolvedStepInput,
241
- // >(
242
- // id: ID,
243
- // action: ActionName,
244
- // resolve?: (
245
- // ctx: {
246
- // input: Input;
247
- // results: Results;
248
- // context: Context;
249
- // } & CallHelpers<Reg, ActionName>,
250
- // ) => ResolveOut,
251
- // options?: StepOptions<Input, Results, Context>,
252
- // ) {
253
- // return this.step<ID, ActionName, ResolveOut>(
254
- // id,
255
- // action,
256
- // resolve,
257
- // undefined,
258
- // options,
259
- // );
260
- // }
261
- //
262
- // /* ------------------------------------------------ */
263
- // /* Override the result of the last step */
264
- // /* ------------------------------------------------ */
265
- // as<NewType>(): WorkflowBuilder<
266
- // Reg,
267
- // Input,
268
- // Context,
269
- // Steps,
270
- // // Override the result of the last step only
271
- // Steps extends [...infer Rest, infer Last]
272
- // ? Last extends StepDef<Reg, infer ID, any>
273
- // ? Simplify<Omit<Results, ID> & { [K in ID]: NewType }>
274
- // : Results
275
- // : Results,
276
- // Output
277
- // > {
278
- // return this as any;
279
- // }
280
- //
281
- // parallel<
282
- // Branches extends readonly WorkflowBuilder<Reg, Input, Context, any, any>[],
283
- // >(
284
- // ...branches: {
285
- // [K in keyof Branches]: (
286
- // builder: WorkflowBuilder<Reg, Input, Context, [], Results>,
287
- // ) => Branches[K];
288
- // }
289
- // ): WorkflowBuilder<
290
- // Reg,
291
- // Input,
292
- // Context,
293
- // MergeBranchSteps<Branches, Steps>,
294
- // // [
295
- // // ...Steps,
296
- // // ...(Branches[number] extends WorkflowBuilder<Reg, any, any, infer S, any>
297
- // // ? S
298
- // // : never),
299
- // // ],
300
- // Simplify<MergeBranchResults<Branches, Results>>
301
- // > {
302
- // const parentFrontier = [...this.frontier];
303
- // const branchEnds: string[] = [];
304
- //
305
- // branches.forEach((branch) => {
306
- // const b = new WorkflowBuilder<Reg, Input, Context, [], Results>(
307
- // this.name,
308
- // );
309
- //
310
- // b.frontier = parentFrontier;
311
- // branch(b);
312
- // branchEnds.push(...b.frontier);
313
- // this.steps.push(...(b as any).steps);
314
- // });
315
- //
316
- // this.frontier = branchEnds;
317
- // this.clearPendingWhen();
318
- // return this as any;
319
- // }
320
- //
321
- // /* ------------------------------------------------ */
322
- // /* Join helper */
323
- // /* ------------------------------------------------ */
324
- // join<
325
- // ID extends string = string,
326
- // ActionName extends keyof Reg & string = any,
327
- // ResolveOut extends ResolvedStepInput = ResolvedStepInput,
328
- // >(
329
- // id: ID,
330
- // action: ActionName,
331
- // resolve?: (
332
- // ctx: {
333
- // input: Input;
334
- // results: Results;
335
- // context: Context;
336
- // } & CallHelpers<Reg, ActionName>,
337
- // ) => ResolveOut,
338
- // options?: StepOptions<Input, Results, Context>,
339
- // ) {
340
- // const result = this.step<ID, ActionName, ResolveOut>(
341
- // id,
342
- // action,
343
- // resolve,
344
- // [...this.frontier],
345
- // options,
346
- // );
347
- //
348
- // this.clearPendingWhen();
349
- // return result;
350
- // }
351
- //
352
1
  // /* ------------------------------------------------ */
353
2
  // /* Subflow */
354
3
  // /* ------------------------------------------------ */
@@ -419,90 +68,7 @@
419
68
  // /* Conditional */
420
69
  // /* ------------------------------------------------ */
421
70
  //
422
- // when(
423
- // predicate: (ctx: {
424
- // input: Input;
425
- // results: Results;
426
- // context: Context;
427
- // }) => boolean,
428
- // ): WorkflowBuilder<Reg, Input, Context, Steps, Results, Output> {
429
- // this.pendingWhen = predicate;
430
- // return this;
431
- // }
432
- //
433
- // endWhen(): this {
434
- // this.clearPendingWhen();
435
- //
436
- // return this;
437
- // }
438
- //
439
- // /* ------------------------------------------------ */
440
- // /* Workflow output */
441
- // /* ------------------------------------------------ */
442
- // output<Output>(
443
- // fn: (ctx: { input: Input; results: Results; context: Context }) => Output,
444
- // ): WorkflowDef<Reg, Input, Results, Steps, Output> {
445
- // this.outputResolver = fn;
446
- // return this.build() as WorkflowDef<Reg, Input, Results, Steps, Output>;
447
- // }
448
- //
449
- // build(): WorkflowDef<Reg, Input, Results, Steps> {
450
- // this.validateDependencies();
451
- //
452
- // return {
453
- // name: this.name,
454
- // steps: this.steps as Steps,
455
- // entrySteps: this.steps.filter((s) => s.dependsOn.length === 0),
456
- // endSteps: this.getEndSteps(),
457
- // input: {} as Input,
458
- // results: {} as Results,
459
- // outputResolver: this.outputResolver,
460
- // };
461
- // }
462
- //
463
- // private validateDependencies() {
464
- // const stepIds = new Set(this.steps.map((s) => s.id));
465
- // for (const step of this.steps) {
466
- // for (const dep of step.dependsOn) {
467
- // if (!stepIds.has(dep))
468
- // throw new Error(`Step ${step.id} depends on unknown step ${dep}`);
469
- // }
470
- // }
471
- // }
472
- //
473
- // private getEndSteps() {
474
- // const hasDependents = new Set<string>();
475
- // for (const step of this.steps) {
476
- // for (const dep of step.dependsOn) hasDependents.add(dep);
477
- // }
478
- // return this.steps.filter((s) => !hasDependents.has(s.id));
479
- // }
480
- // }
481
- //
482
- // /* ------------------------------------------------ */
483
- // /* WORKFLOW CREATOR */
484
- // /* ------------------------------------------------ */
485
- // // export function createWorkflow<
486
- // // Reg extends ActionRegistry,
487
- // // Context extends Record<string, any> = {},
488
- // // >(registry: Reg, context?: Context) {
489
- // // return function workflow<Input = unknown>(name: string) {
490
- // // return new WorkflowBuilder<Reg, Input, Context>(name);
491
- // // };
492
- // // }
493
- //
494
- // export function createWorkflow<
495
- // Reg extends ActionRegistry,
496
- // Context extends Record<string, any> = {},
497
- // >() {
498
- // return function workflow<Input = unknown>(name: string) {
499
- // return new WorkflowBuilder<Reg, Input, Context>(name);
500
- // };
501
- // }
502
- //
503
- //
504
- //
505
- //
71
+ import { generateWorkflowId } from "./utils.js";
506
72
  /* ------------------------------------------------ */
507
73
  /* FLUENT WORKFLOW BUILDER */
508
74
  /* ------------------------------------------------ */
@@ -546,12 +112,12 @@ export class WorkflowBuilder {
546
112
  branches.forEach((branch) => {
547
113
  const b = new WorkflowBuilder(this.name);
548
114
  b.frontier = parentFrontier;
115
+ b.pendingWhen = this.pendingWhen;
549
116
  branch(b);
550
117
  branchEnds.push(...b.frontier);
551
118
  this.steps.push(...b.steps);
552
119
  });
553
120
  this.frontier = branchEnds;
554
- this.clearPendingWhen();
555
121
  return this;
556
122
  }
557
123
  /* ------------------------------------------------ */
@@ -567,10 +133,8 @@ export class WorkflowBuilder {
567
133
  id: prefix,
568
134
  action: "__subflow__",
569
135
  dependsOn: [...this.frontier],
570
- resolve: (ctx) => ({
571
- kind: "object",
572
- args: resolveInput(ctx),
573
- }),
136
+ when: this.pendingWhen,
137
+ resolve: (ctx) => resolveInput(ctx),
574
138
  options,
575
139
  __subflowId: workflowKey, // 👈 STRING ONLY
576
140
  });
@@ -598,6 +162,7 @@ export class WorkflowBuilder {
598
162
  build() {
599
163
  this.validateDependencies();
600
164
  return {
165
+ _id: generateWorkflowId(this.name),
601
166
  name: this.name,
602
167
  steps: this.steps,
603
168
  entrySteps: this.steps.filter((s) => s.dependsOn.length === 0),
@@ -625,17 +190,6 @@ export class WorkflowBuilder {
625
190
  return this.steps.filter((s) => !hasDependents.has(s.id));
626
191
  }
627
192
  }
628
- /* ------------------------------------------------ */
629
- /* WORKFLOW CREATOR */
630
- /* ------------------------------------------------ */
631
- // export function createWorkflow<
632
- // Reg extends ActionRegistry,
633
- // Context extends Record<string, any> = {},
634
- // >(registry: Reg, context?: Context) {
635
- // return function workflow<Input = unknown>(name: string) {
636
- // return new WorkflowBuilder<Reg, Input, Context>(name);
637
- // };
638
- // }
639
193
  export function createWorkflow() {
640
194
  return function workflow(name) {
641
195
  return new WorkflowBuilder(name);
@@ -1,6 +1,5 @@
1
- import { ActionRegistry, WorkflowObserver } from "./types.js";
1
+ import { ActionRegistry, Executor, WorkflowObserver } from "./types.js";
2
2
  import { WorkflowDef } from "./workflow-composer.js";
3
- import { WorkflowInput, WorkflowOutput } from "./workflow-module.js";
4
3
  export declare function createCallHelpers(): {
5
4
  args: (...args: any[]) => {
6
5
  kind: string;
@@ -18,7 +17,14 @@ export declare function createCallHelpers(): {
18
17
  items: any[];
19
18
  };
20
19
  };
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]>;
20
+ export declare function executeWorkflow<Reg extends ActionRegistry, I, R, O = R>({ workflow, actionRegistry, input, context, depsExecutors, observers, }: {
21
+ workflow: WorkflowDef<Reg, I, any, any, O>;
22
+ actionRegistry: Reg;
23
+ input: I;
24
+ context: any;
25
+ depsExecutors: Record<string, Executor>;
26
+ observers: WorkflowObserver<Reg>[];
27
+ }): Promise<{
28
+ output: O;
23
29
  extras: Record<string, any>;
24
30
  }>;
@@ -1,234 +1,3 @@
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
- // }
232
1
  import { composeObserver } from "./observer.js";
233
2
  export function createCallHelpers() {
234
3
  return {
@@ -270,11 +39,7 @@ async function runWithRetry(actionFn, stepOptions) {
270
39
  }
271
40
  throw lastError;
272
41
  }
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
- }
42
+ export async function executeWorkflow({ workflow, actionRegistry, input, context, depsExecutors, observers = [], }) {
278
43
  const results = {};
279
44
  const extras = {};
280
45
  extras.frames = {};
@@ -340,33 +105,51 @@ export async function executeWorkflow(workflowId, workflowRegistry, actionRegist
340
105
  extras,
341
106
  frame,
342
107
  };
108
+ const stepCtx = {
109
+ input,
110
+ results,
111
+ context,
112
+ ...createCallHelpers(),
113
+ };
114
+ const resolvedArgs = step.resolve?.(stepCtx);
115
+ frame.input = resolvedArgs;
343
116
  const core = async () => {
344
117
  frame.attempts++;
345
- const stepCtx = { input, results, context, ...createCallHelpers() };
346
- // Conditional skip
118
+ // -----------------------------
119
+ // SINGLE when evaluation
120
+ // -----------------------------
347
121
  if (step.when && !step.when(stepCtx)) {
122
+ frame.input = undefined; // 👈 important for observer consistency
348
123
  frame.output = undefined;
349
124
  frame.end = Date.now();
350
125
  frame.skipped = true;
351
126
  results[step.id] = undefined;
352
127
  return undefined;
353
128
  }
354
- const resolvedArgs = step.resolve?.(stepCtx);
355
- frame.input = resolvedArgs;
356
129
  // -----------------------------
357
130
  // Subflow handling
358
131
  // -----------------------------
359
132
  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
133
+ const [modId, subWfId] = step.__subflowId.split(".");
134
+ const exec = depsExecutors[modId];
135
+ const subExecution = await exec.run(subWfId, resolvedArgs, context, observers);
136
+ // const subExecution = await executeWorkflow(
137
+ // step.__subflowId,
138
+ // workflowRegistry,
139
+ // actionRegistry,
140
+ // resolvedArgs as any,
141
+ // context,
142
+ // observers,
143
+ // );
364
144
  frame.output = subExecution.output;
145
+ frame.end = Date.now();
365
146
  results[step.id] = subExecution.output;
366
- // Merge extras
367
147
  Object.assign(extras, subExecution.extras);
368
148
  return subExecution.output;
369
149
  }
150
+ // -----------------------------
151
+ // Normal action
152
+ // -----------------------------
370
153
  const actionFn = async () => {
371
154
  const action = actionRegistry[step.action];
372
155
  return await runAction(resolvedArgs, action);
@@ -411,7 +194,7 @@ export async function executeWorkflow(workflowId, workflowRegistry, actionRegist
411
194
  ? workflow.outputResolver({
412
195
  input,
413
196
  results,
414
- context: workflow.__context,
197
+ context,
415
198
  })
416
199
  : results;
417
200
  return {
@@ -1,42 +1,47 @@
1
- import { ActionRegistry, WorkflowObserver } from "./types.js";
1
+ import { ActionRegistry, Executor, Simplify, WorkflowObserver } from "./types.js";
2
2
  import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
3
3
  type UnionToIntersection<U> = (U extends any ? (x: U) => any : never) extends (x: infer I) => any ? I : never;
4
- type EnsureWorkflowRecord<T> = T extends Record<string, WorkflowDef<any, any, any, any, any>> ? T : Record<string, WorkflowDef<any, any, any, any, any>>;
5
- type DepWorkflows<Deps extends ModuleMap> = keyof Deps extends never ? {} : EnsureWorkflowRecord<UnionToIntersection<{
4
+ type EnsureWorkflowShape<T> = {
5
+ [K in keyof T]: T[K] extends WorkflowDef<any, any, any, any, any> ? T[K] : never;
6
+ };
7
+ type DepWorkflows<Deps extends ModuleMap> = keyof Deps extends never ? {} : Simplify<EnsureWorkflowShape<UnionToIntersection<{
6
8
  [D in keyof Deps & string]: {
7
9
  [K in keyof Deps[D]["workflows"] & string as `${D}.${K}`]: Deps[D]["workflows"][K];
8
10
  };
9
- }[keyof Deps & string]>>;
11
+ }[keyof Deps & string]>>>;
10
12
  type WorkflowRegistry<Own extends ModuleShape, Deps extends ModuleMap> = Own & DepWorkflows<Deps>;
11
13
  type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
12
14
  type ModuleShape = Record<string, AnyWorkflow>;
13
15
  type ModuleMap = Record<string, Module<any, any, any, any>>;
14
- type ContextFromDeps<Deps> = [keyof Deps] extends [never] ? {} : {
15
- [K in keyof Deps]: Deps[K] extends Module<any, infer Ctx, any, any> ? Ctx : never;
16
- }[keyof Deps];
17
- type FinalContext<Reg extends ActionRegistry, Context extends Record<string, any>, Deps extends ModuleMap> = Context & ContextFromDeps<Deps>;
16
+ type FinalContext<Context extends Record<string, any>, Deps extends ModuleMap> = Context & ContextFromDepsRecursive<Deps>;
17
+ type ContextFromDepsRecursive<Deps extends ModuleMap> = [keyof Deps] extends [
18
+ never
19
+ ] ? {} : UnionToIntersection<{
20
+ [K in keyof Deps]: Deps[K] extends Module<any, infer Ctx, any, infer SubDeps> ? Ctx & ContextFromDepsRecursive<SubDeps> : never;
21
+ }[keyof Deps]>;
18
22
  export type WorkflowInput<W> = W extends WorkflowDef<any, infer I, any, any, any> ? I : never;
19
23
  export type WorkflowResults<W> = W extends WorkflowDef<any, any, infer R, any, any> ? R : never;
20
24
  export type WorkflowOutput<W> = W extends WorkflowDef<any, any, any, any, infer O> ? O : never;
21
25
  type Module<Reg extends ActionRegistry, Context extends Record<string, any>, Own extends ModuleShape, Deps extends ModuleMap> = {
22
26
  workflows: Own;
27
+ __getExecutor: () => Executor;
23
28
  createRuntime: (config: {
24
- registry: Reg;
25
- context: FinalContext<Reg, Context, Deps>;
29
+ context: FinalContext<Context, Deps>;
26
30
  }) => {
27
31
  run: <K extends keyof WorkflowRegistry<Own, Deps>>(workflow: K, input: WorkflowInput<WorkflowRegistry<Own, Deps>[K]>, observers?: WorkflowObserver<Reg>[]) => Promise<{
28
32
  output: WorkflowOutput<WorkflowRegistry<Own, Deps>[K]>;
29
33
  extras: Record<string, any>;
30
34
  }>;
31
- getContext: () => FinalContext<Reg, Context, Deps>;
35
+ getContext: () => FinalContext<Context, Deps>;
32
36
  };
33
37
  };
34
- export type ModuleContext<Reg extends ActionRegistry, WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, Context extends Record<string, any>> = {
38
+ type ModuleContext<Reg extends ActionRegistry, WFReg extends Record<string, WorkflowDef<any, any, any, any, any>>, Context extends Record<string, any>> = {
35
39
  wf: ReturnType<typeof createWorkflow<Reg, WFReg, Context>>;
36
40
  context: Context;
37
41
  };
38
- export declare function createModuleFactory<Reg extends ActionRegistry, Context extends Record<string, any>>(): <Use extends ModuleMap = {}, Own extends ModuleShape = {}>(config: {
42
+ export declare function createModuleFactory<Context extends Record<string, any>>(): <Reg extends ActionRegistry = Record<string, any>, Use extends ModuleMap = {}, Own extends ModuleShape = {}>(config: {
43
+ actionRegistry: Reg;
39
44
  use?: Use;
40
- define: (ctx: ModuleContext<Reg, DepWorkflows<Use>, Context>) => Own;
45
+ define: (ctx: ModuleContext<typeof config.actionRegistry, DepWorkflows<Use>, Context>) => Own;
41
46
  }) => Module<Reg, Context, Own, Use>;
42
47
  export {};
@@ -1,174 +1,50 @@
1
- // import { ActionRegistry, Simplify, WorkflowObserver } from "./types.js";
2
- // import { createWorkflow, WorkflowDef } from "./workflow-composer.js";
3
- // import { executeWorkflow } from "./workflow-executor.js";
4
- //
5
- // type AnyWorkflow = WorkflowDef<any, any, any, any, any>;
6
- //
7
- // type WorkflowFromDeps<Deps extends Record<string, any>> = {
8
- // [K in keyof Deps]: Deps[K] extends Module<any, any, infer Own, infer SubDeps>
9
- // ? Own[keyof Own] | WorkflowFromDeps<SubDeps>
10
- // : never;
11
- // }[keyof Deps];
12
- // type ModuleShape = Record<string, AnyWorkflow>;
13
- //
14
- // type ContextFromDeps<Deps> = [keyof Deps] extends [never]
15
- // ? {} // 👈 THIS is the fix
16
- // : {
17
- // [K in keyof Deps]: Deps[K] extends Module<any, infer Ctx, any, any>
18
- // ? Ctx
19
- // : never;
20
- // }[keyof Deps];
21
- //
22
- // type FinalContext<
23
- // Reg extends ActionRegistry,
24
- // Context extends Record<string, any>,
25
- // Deps extends ModuleMap<Reg>,
26
- // > = Context & ContextFromDeps<Deps>;
27
- //
28
- // type ModuleMap<Reg extends ActionRegistry> = Record<
29
- // string,
30
- // Module<Reg, any, any, any>
31
- // >;
32
- //
33
- // // type Module<Own extends ModuleShape = {}, Deps extends ModuleMap = {}> = Own & {
34
- // // deps: Deps;
35
- // // };
36
- // export type WorkflowInput<W> =
37
- // W extends WorkflowDef<any, infer I, any, any, any> ? I : never;
38
- //
39
- // export type WorkflowResults<W> =
40
- // W extends WorkflowDef<any, any, infer R, any, any> ? R : never;
41
- //
42
- // export type WorkflowOutput<W> =
43
- // W extends WorkflowDef<any, any, any, any, infer O> ? O : never;
44
- // type Module<
45
- // Reg extends ActionRegistry,
46
- // Context extends Record<string, any>,
47
- // Own extends ModuleShape = {},
48
- // Deps extends ModuleMap<Reg> = {},
49
- // > = {
50
- // own: Own;
51
- // deps: Deps;
52
- // createRuntime: (config: {
53
- // registry: Reg;
54
- // context: FinalContext<Reg, Context, Deps>;
55
- // }) => {
56
- // run: <W extends Own[keyof Own] | WorkflowFromDeps<Deps>>(
57
- // workflow: W,
58
- // input: WorkflowInput<W>,
59
- // obververs?: WorkflowObserver<Reg>[],
60
- // ) => Promise<{
61
- // results: WorkflowResults<W>;
62
- // output: WorkflowOutput<W>;
63
- // extras: Record<string, any>;
64
- // }>;
65
- // getContext: () => FinalContext<Reg, Context, Deps>;
66
- // };
67
- // };
68
- //
69
- // export type ModuleContext<
70
- // Reg extends ActionRegistry,
71
- // Context extends Record<string, any>,
72
- // Deps extends ModuleMap<Reg>,
73
- // > = {
74
- // wf: ReturnType<typeof createWorkflow<Reg, Context>>;
75
- //
76
- // deps: Deps;
77
- // context: Context;
78
- //
79
- // tools: <T>(
80
- // factory: (ctx: {
81
- // wf: ModuleContext<Reg, Context, Deps>["wf"];
82
- // deps: Deps;
83
- // context: Context;
84
- // }) => T,
85
- // ) => T;
86
- // };
87
- //
88
- // function createModule<
89
- // Reg extends ActionRegistry,
90
- // Context extends Record<string, any>,
91
- // Use extends ModuleMap<Reg>,
92
- // Own extends ModuleShape,
93
- // >(config: {
94
- // use?: Use;
95
- // define: (ctx: ModuleContext<Reg, Context, Use>) => Own;
96
- // }): Module<Reg, Context, Own, Use> {
97
- // const wf = createWorkflow<Reg, Context>();
98
- //
99
- // const deps = (config.use ?? {}) as Use;
100
- //
101
- // const moduleCtx: ModuleContext<Reg, Context, Use> = {
102
- // wf,
103
- // deps,
104
- // context: {} as Context,
105
- //
106
- // tools: (factory) =>
107
- // factory({
108
- // wf,
109
- // deps,
110
- // context: {} as Context,
111
- // }),
112
- // };
113
- //
114
- // const own = config.define(moduleCtx);
115
- //
116
- // return {
117
- // own,
118
- // deps,
119
- // createRuntime({ registry, context }) {
120
- // const runtimeCtx = { ...context } as FinalContext<Reg, Context, Use>;
121
- // return {
122
- // run: async (workflow, input, observers = []) => {
123
- // return executeWorkflow(workflow, registry, input, context, observers);
124
- // },
125
- // getContext: () => ({ ...runtimeCtx }),
126
- // };
127
- // },
128
- // };
129
- // }
130
- //
131
- // export function createModuleFactory<
132
- // Reg extends ActionRegistry,
133
- // Context extends Record<string, any>,
134
- // >() {
135
- // return function <
136
- // Use extends ModuleMap<Reg> = {},
137
- // Own extends ModuleShape = {},
138
- // >(config: {
139
- // use?: Use;
140
- // define: (ctx: ModuleContext<Reg, Context, Use>) => Own;
141
- // }): Module<Reg, Context, Own, Use> {
142
- // return createModule<Reg, Context, Use, Own>(config);
143
- // };
144
- // }
145
- //
146
1
  import { createWorkflow } from "./workflow-composer.js";
147
2
  import { executeWorkflow } from "./workflow-executor.js";
148
- /* ------------------------------------------------ */
149
- /* CREATE MODULE (FIXED) */
150
- /* ------------------------------------------------ */
151
3
  function createModule(config) {
152
4
  const deps = (config.use ?? {});
153
- // ✅ CRITICAL: wf only sees DEP workflows
154
5
  const wf = createWorkflow();
155
6
  const own = config.define({
156
7
  wf,
157
8
  context: {},
158
9
  });
159
- function buildRegistry() {
10
+ function buildWorkflowMap() {
160
11
  const depWFs = Object.fromEntries(Object.entries(deps).flatMap(([name, mod]) => Object.entries(mod.workflows).map(([k, wf]) => [`${name}.${k}`, wf])));
161
12
  return { ...own, ...depWFs };
162
13
  }
14
+ const workflowMap = buildWorkflowMap();
15
+ const depsExecutors = Object.fromEntries(Object.entries(deps).map(([name, mod]) => [name, mod.__getExecutor()]));
16
+ const executor = {
17
+ run(wfId, input, context, observers = []) {
18
+ const workflow = workflowMap[wfId];
19
+ if (!workflow) {
20
+ throw new Error(`Workflow not found: ${String(wfId)}`);
21
+ }
22
+ return executeWorkflow({
23
+ workflow,
24
+ actionRegistry: config.actionRegistry,
25
+ depsExecutors,
26
+ input,
27
+ context,
28
+ observers,
29
+ });
30
+ },
31
+ };
163
32
  return {
164
33
  workflows: own,
165
- createRuntime({ registry, context }) {
166
- const workflowRegistry = buildRegistry();
34
+ __getExecutor: () => executor,
35
+ createRuntime({ context }) {
36
+ let runtimeActions = config.actionRegistry;
167
37
  return {
168
38
  run: async (workflowId, input, observers = []) => {
169
- return executeWorkflow(workflowId, workflowRegistry, registry, input, context, observers);
39
+ return executor.run(workflowId, input, context, observers);
170
40
  },
171
41
  getContext: () => ({ ...context }),
42
+ setActionRegistry(reg) {
43
+ runtimeActions = reg;
44
+ // ⚠️ optional: if you REALLY want override, you'd need:
45
+ // executor.actions = reg
46
+ // but better keep actions immutable
47
+ },
172
48
  };
173
49
  },
174
50
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pogodisco/zephyr",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,6 +33,8 @@
33
33
  "prepublishOnly": "npm run build",
34
34
  "build": "tsc -p tsconfig.build.json",
35
35
  "dev": "tsc -w -p tsconfig.json",
36
+ "test": "vitest",
37
+ "test:run": "vitest run",
36
38
  "test:types": "tsd"
37
39
  },
38
40
  "tsd": {
@@ -45,6 +47,7 @@
45
47
  "@types/node": "^25.2.3",
46
48
  "chalk": "^5.6.2",
47
49
  "pretty-format": "^30.2.0",
48
- "tsd": "^0.33.0"
50
+ "tsd": "^0.33.0",
51
+ "vitest": "^4.1.2"
49
52
  }
50
53
  }