yieldstar 0.4.6 → 0.6.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.
Files changed (43) hide show
  1. package/README.md +57 -64
  2. package/dist/exports/errors.d.ts.map +1 -1
  3. package/dist/exports/sdk-http.d.ts +8 -0
  4. package/dist/exports/sdk-http.d.ts.map +1 -1
  5. package/dist/exports/sdk-local.d.ts +19 -2
  6. package/dist/exports/sdk-local.d.ts.map +1 -1
  7. package/dist/exports/workflow.d.ts +14 -2
  8. package/dist/exports/workflow.d.ts.map +1 -1
  9. package/dist/index.d.ts +5 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +791 -137
  12. package/dist/internal/effect-call.d.ts +60 -0
  13. package/dist/internal/effect-call.d.ts.map +1 -0
  14. package/dist/internal/runtime-call.d.ts +39 -0
  15. package/dist/internal/runtime-call.d.ts.map +1 -0
  16. package/dist/internal/serialise.d.ts.map +1 -1
  17. package/dist/internal/step-record.d.ts +13 -0
  18. package/dist/internal/step-record.d.ts.map +1 -0
  19. package/dist/internal/step-runner.d.ts +93 -9
  20. package/dist/internal/step-runner.d.ts.map +1 -1
  21. package/dist/internal/utils.d.ts +11 -0
  22. package/dist/internal/utils.d.ts.map +1 -1
  23. package/package.json +8 -4
  24. package/src/exports/errors.ts +4 -0
  25. package/src/exports/sdk-http.ts +60 -27
  26. package/src/exports/sdk-local.ts +41 -15
  27. package/src/exports/workflow.ts +159 -99
  28. package/src/index.ts +51 -1
  29. package/src/internal/effect-call.ts +217 -0
  30. package/src/internal/runtime-call.ts +95 -0
  31. package/src/internal/step-record.ts +110 -0
  32. package/src/internal/step-runner.ts +748 -53
  33. package/src/internal/utils.ts +24 -10
  34. package/dist/exports/scheduler.d.ts +0 -49
  35. package/dist/exports/scheduler.d.ts.map +0 -1
  36. package/dist/exports/simplified-types.d.ts +0 -31
  37. package/dist/exports/simplified-types.d.ts.map +0 -1
  38. package/dist/exports/types.d.ts +0 -6
  39. package/dist/exports/types.d.ts.map +0 -1
  40. package/dist/internal/types.d.ts +0 -9
  41. package/dist/internal/types.d.ts.map +0 -1
  42. package/src/internal/serialise.ts +0 -44
  43. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,60 @@
1
+ import type { Logger } from "pino";
2
+ import { StepResponse, type DurableObject, type DurableValue, type EffectClient } from "@yieldstar/core";
3
+ /** What `lookup` reports about an effect ID (RFC 005). */
4
+ export type EffectLookup<T> = {
5
+ status: "settled";
6
+ result: T;
7
+ } | {
8
+ status: "not-found";
9
+ } | {
10
+ status: "unknown";
11
+ };
12
+ /** The effect request a step yields once its descriptor is valid. */
13
+ export type EffectCallRequest = {
14
+ stepKey: string;
15
+ effectId: string;
16
+ operation: string;
17
+ input: DurableObject;
18
+ retryIn: number;
19
+ lookup(effectId: string): Promise<unknown>;
20
+ perform(effectId: string): Promise<unknown>;
21
+ };
22
+ /**
23
+ * - `settled`: the effect's result, which the step records.
24
+ * - `rejected`: the effect exists with a different operation or input.
25
+ * - `suspended`: the step watches the effect and is due again at `wakeAt`.
26
+ */
27
+ export type EffectCallOutcome = {
28
+ status: "settled";
29
+ result: DurableValue;
30
+ } | {
31
+ status: "rejected";
32
+ code: "EFFECT_REQUEST_MISMATCH";
33
+ } | {
34
+ status: "suspended";
35
+ wakeAt: number;
36
+ };
37
+ /**
38
+ * Yielded by `step.effect`. The driver runs the effect with
39
+ * `executeEffectCall` and resumes the step with its outcome.
40
+ */
41
+ export declare class StepEffectCall extends StepResponse {
42
+ static type: string;
43
+ readonly type = "effect-call";
44
+ readonly request: EffectCallRequest;
45
+ constructor(request: EffectCallRequest);
46
+ }
47
+ /**
48
+ * Runs an effect step's protocol against the runtime's `EffectClient`. Every
49
+ * failure that must not be recorded rejects with an `InfrastructureError`
50
+ * whose cause is an `EffectDeliveryError`, so the delivery is retried.
51
+ */
52
+ export declare function executeEffectCall(request: EffectCallRequest, context: {
53
+ effects: EffectClient;
54
+ executionId: string;
55
+ clock: () => number;
56
+ /** Checked before every callback, so a lost lease makes no external call. */
57
+ assertLease: () => void;
58
+ logger: Logger;
59
+ }): Promise<EffectCallOutcome>;
60
+ //# sourceMappingURL=effect-call.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effect-call.d.ts","sourceRoot":"","sources":["../../src/internal/effect-call.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAIL,YAAY,EAEZ,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAEzB,0DAA0D;AAC1D,MAAM,MAAM,YAAY,CAAC,CAAC,IACtB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1B,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GAC3C;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,yBAAyB,CAAA;CAAE,GACvD;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5C;;;GAGG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,MAAM,CAAC,IAAI,SAAiB;IAC5B,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;gBAExB,OAAO,EAAE,iBAAiB;CAIvC;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE;IACP,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,MAAM,CAAC;IACpB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB,GACA,OAAO,CAAC,iBAAiB,CAAC,CAqH5B"}
@@ -0,0 +1,39 @@
1
+ import { StepResponse, type ExecutionClient, type StoreClient } from "@yieldstar/core";
2
+ export type RuntimeClients = {
3
+ stores: StoreClient;
4
+ executions: ExecutionClient;
5
+ };
6
+ export type RuntimeCallResult<T> = {
7
+ ok: true;
8
+ value: T;
9
+ } | {
10
+ ok: false;
11
+ error: unknown;
12
+ };
13
+ /**
14
+ * Yielded by a step that needs a runtime client. Steps never call runtime
15
+ * clients themselves: the driver makes the call outside workflow code and
16
+ * resumes the step with a `RuntimeCallResult`. Only failures that belong to
17
+ * workflow code (see `isWorkflowFacingError`) resume the step; any other
18
+ * failure is an infrastructure error and ends the delivery without an outcome.
19
+ */
20
+ export declare class StepRuntimeCall<T = unknown> extends StepResponse {
21
+ static type: string;
22
+ readonly type = "runtime-call";
23
+ readonly call: (clients: RuntimeClients) => Promise<T>;
24
+ constructor(call: (clients: RuntimeClients) => Promise<T>);
25
+ }
26
+ /** Marks an error thrown by a workflow-supplied callback run by a client. */
27
+ export declare class StepCallbackError extends Error {
28
+ constructor(cause: unknown);
29
+ }
30
+ /**
31
+ * Wraps a workflow-supplied callback (updater, selector, claim, initial state)
32
+ * that a runtime client will run, so its errors reach workflow code rather
33
+ * than being mistaken for infrastructure errors.
34
+ */
35
+ export declare function workflowCallback<A extends unknown[], R>(fn: (...args: A) => R, assertResult?: (result: R) => void): (...args: A) => R;
36
+ export declare function executeRuntimeCall<T>(request: StepRuntimeCall<T>, clients: RuntimeClients): Promise<RuntimeCallResult<T>>;
37
+ /** Runs a heap operation, tagging its failure as an infrastructure error. */
38
+ export declare function infrastructure<T>(fn: () => Promise<T>): Promise<T>;
39
+ //# sourceMappingURL=runtime-call.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-call.d.ts","sourceRoot":"","sources":["../../src/internal/runtime-call.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EAGZ,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,eAAe,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAElC;;;;;;GAMG;AACH,qBAAa,eAAe,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,YAAY;IAC5D,MAAM,CAAC,IAAI,SAAkB;IAC7B,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;gBAE3C,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC;CAI1D;AAED,6EAA6E;AAC7E,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,KAAK,EAAE,OAAO;CAI3B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EACrD,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,EACrB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,GACjC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,CAUnB;AAED,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAS/B;AAWD,6EAA6E;AAC7E,wBAAsB,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"serialise.d.ts","sourceRoot":"","sources":["../../src/internal/serialise.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EAEb,MAAM,iBAAiB,CAAC;AAGzB,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAShE;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAuBxE"}
1
+ {"version":3,"file":"serialise.d.ts","sourceRoot":"","sources":["../../src/internal/serialise.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EAIb,MAAM,iBAAiB,CAAC;AAGzB,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAShE;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAyBxE"}
@@ -0,0 +1,13 @@
1
+ import { StepResponse } from "@yieldstar/core";
2
+ /**
3
+ * Encodes a step response for its step record, and returns the response that
4
+ * workflow code must see: the record decoded again, so that a first run sees
5
+ * exactly what replay reads. A result the codec rejects is recorded as a step
6
+ * error with `WorkflowValueError` (`INVALID_STEP_VALUE`).
7
+ */
8
+ export declare function encodeStepRecord(response: StepResponse, stepKey: string): {
9
+ json: string;
10
+ response: StepResponse;
11
+ };
12
+ export declare function decodeStepRecord(json: string): StepResponse;
13
+ //# sourceMappingURL=step-record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-record.d.ts","sourceRoot":"","sources":["../../src/internal/step-record.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,YAAY,EAOb,MAAM,iBAAiB,CAAC;AAEzB;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,GACd;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,CAG1C;AA8CD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAmC3D"}
@@ -1,4 +1,7 @@
1
- import { StepResponse, StepError, StepResult } from "@yieldstar/core";
1
+ import { defineStore, type Draft, type DurableObject, type DurableStoreSchema, type IfDurable, type StandardSchemaV1, StepResponse, StepError, StepResult, type StoreDefinition, type StoreDeleteFromResult, type StoreKey, type StoreSelector, type StoreSnapshot, type StoreState, type StoreUpdateFromResult, type StoreUpdateResult, type Durable } from "@yieldstar/core";
2
+ import type { WorkflowEvent } from "@yieldstar/core";
3
+ import { type EffectLookup } from "./effect-call";
4
+ export type { EffectLookup };
2
5
  /**
3
6
  * @description A library of step generators, each of which:
4
7
  * @yields a StepResponse to workflow consumers
@@ -7,16 +10,98 @@ import { StepResponse, StepError, StepResult } from "@yieldstar/core";
7
10
  * @throws any error caught when running the user-defined function,
8
11
  * once retries have been exhausted, or if there are no retry semantics
9
12
  */
10
- export declare const stepRunner: {
13
+ export { defineStore };
14
+ export type WorkflowStore<T> = {
15
+ readonly definition: StoreDefinition<any>;
16
+ readonly id: string;
17
+ readonly key: StoreKey;
18
+ get(key?: string): AsyncGenerator<StepResponse, StoreSnapshot<T>>;
19
+ select<R>(key: string, selector: StoreSelector<T, R> & DurableSelector<T, R>): AsyncGenerator<StepResponse, R>;
20
+ update(key: string, updater: (draft: Draft<T>) => void | T): AsyncGenerator<StepResponse, StoreUpdateResult<T>>;
21
+ updateFrom(key: string, snapshot: StoreSnapshot<T>, updater: (draft: Draft<T>) => void | T): AsyncGenerator<StepResponse, StoreUpdateFromResult<T>>;
22
+ deleteFrom(key: string, snapshot: StoreSnapshot<T>): AsyncGenerator<StepResponse, StoreDeleteFromResult>;
23
+ when<R>(selector: StoreSelector<T, R | undefined | null | false> & DurableMatchSelector<T, R>): AsyncGenerator<StepResponse, NonNullable<R>>;
24
+ when<R>(key: string, selector: StoreSelector<T, R | undefined | null | false> & DurableMatchSelector<T, R>): AsyncGenerator<StepResponse, NonNullable<R>>;
25
+ take<R>(key: string, selector: StoreSelector<T, R | undefined | null | false> & DurableMatchSelector<T, R>, claim: (draft: Draft<T>, selected: NonNullable<R>) => void): AsyncGenerator<StepResponse, NonNullable<R>>;
26
+ };
27
+ /** A store selector whose result, which the step records, is durable. */
28
+ type DurableSelector<T, R> = IfDurable<R, StoreSelector<T, Durable<R>>>;
29
+ /**
30
+ * A `when` or `take` selector whose match is durable. `false` is excluded
31
+ * before the check so that `R` is inferred as it would be without it.
32
+ */
33
+ type DurableMatchSelector<T, R> = IfDurable<Exclude<R, false>, StoreSelector<T, Durable<Exclude<R, false>> | undefined | null | false>>;
34
+ export type StepRunner = ReturnType<typeof createStepRunner>;
35
+ export declare function createStepRunner(params: {
36
+ event: WorkflowEvent;
37
+ clock: () => number;
38
+ }): {
11
39
  run: typeof run;
12
- delay: typeof delay;
40
+ delay: {
41
+ (retryInterval: number): any;
42
+ (key: string, retryInterval: number): any;
43
+ };
13
44
  poll: typeof poll;
45
+ store: <Schema extends StandardSchemaV1>(definition: StoreDefinition<Schema> & {
46
+ schema: DurableStoreSchema<Schema>;
47
+ }, params?: {
48
+ id?: string;
49
+ }) => WorkflowStore<StoreState<Schema>>;
50
+ start: <Params extends Record<string, any>>(stepKey: string, target: {
51
+ workflowId: string;
52
+ params?: Params & Durable<Params>;
53
+ }) => AsyncGenerator<StepResponse, {
54
+ executionId: string;
55
+ }, StepResult | StepError>;
56
+ effect: <T, Input extends Record<string, any> = DurableObject>(stepKey: string, descriptor: EffectDescriptor<T, Input> & DurableEffectResult<T>) => AsyncGenerator<StepResponse, T, StepResult | StepError>;
57
+ };
58
+ /**
59
+ * Describes one external operation for `step.effect`. `effectId` names the
60
+ * operation in the external system, so every caller that means it, in any
61
+ * execution or runtime, uses the same ID, and no caller reuses it for another
62
+ * operation. The external system must guarantee that repeated `perform` calls
63
+ * for one ID make one operation with one result, that `not-found` proves no
64
+ * call exists or can still finish, and that `settled` never changes.
65
+ */
66
+ export type EffectDescriptor<T, Input = DurableObject> = {
67
+ effectId: string;
68
+ /** Names the version of the behaviour of `input`, `lookup` and `perform`. */
69
+ operation: string;
70
+ input: Input & Durable<Input>;
71
+ /** Milliseconds before an uncertain outcome is looked up again. */
72
+ retryIn: number;
73
+ /** Reports what the external system knows about `effectId`. */
74
+ lookup(effectId: string): Promise<EffectLookup<T>>;
75
+ /** Performs the operation under `effectId`. */
76
+ perform(effectId: string): Promise<T>;
14
77
  };
15
- export type StepRunner = typeof stepRunner;
16
- declare function run<T extends any>(fn: () => T | Promise<T>): AsyncGenerator<StepResponse, T, StepResult | StepError>;
17
- declare function run<T extends any>(key: string, fn: () => T | Promise<T>): AsyncGenerator<StepResponse, T, StepResult | StepError>;
18
- declare function delay(retryInterval: number): any;
19
- declare function delay(key: string, retryInterval: number): any;
78
+ /**
79
+ * Checks the effect's result type without taking part in its inference. A
80
+ * `perform` that resolves with nothing settles the effect with `undefined`.
81
+ */
82
+ type DurableEffectResult<T> = [T] extends [void] ? unknown : IfDurable<T, {
83
+ perform(effectId: string): Promise<Durable<T>>;
84
+ }>;
85
+ export type StepRunOptions<S extends StandardSchemaV1> = {
86
+ /**
87
+ * Validates the step's decoded value, on the first run and on every
88
+ * replay; the step returns the schema's output. The step function must
89
+ * return the schema's input as a durable value.
90
+ */
91
+ schema: S;
92
+ };
93
+ /**
94
+ * A step function whose result, or the value its promise resolves to, is a
95
+ * durable value. Checking the whole function type keeps the result's type as
96
+ * TypeScript infers it, and names the offending property in the error.
97
+ */
98
+ export type DurableStepFn<F> = F extends () => infer R ? () => R extends PromiseLike<infer U> ? PromiseLike<DurableUnion<U>> : DurableUnion<R> : never;
99
+ type DurableUnion<T> = T extends unknown ? Durable<T> : never;
100
+ declare function run<F extends () => unknown>(fn: F & DurableStepFn<F>): AsyncGenerator<StepResponse, Awaited<ReturnType<F>>, StepResponse>;
101
+ declare function run<F extends () => unknown>(key: string, fn: F & DurableStepFn<F>): AsyncGenerator<StepResponse, Awaited<ReturnType<F>>, StepResponse>;
102
+ declare function run<S extends StandardSchemaV1>(fn: () => StepSchemaInput<S> | PromiseLike<StepSchemaInput<S>>, options: StepRunOptions<S>): AsyncGenerator<StepResponse, StandardSchemaV1.InferOutput<S>, StepResponse>;
103
+ declare function run<S extends StandardSchemaV1>(key: string, fn: () => StepSchemaInput<S> | PromiseLike<StepSchemaInput<S>>, options: StepRunOptions<S>): AsyncGenerator<StepResponse, StandardSchemaV1.InferOutput<S>, StepResponse>;
104
+ type StepSchemaInput<S extends StandardSchemaV1> = Durable<StandardSchemaV1.InferInput<S>>;
20
105
  type PollOpts = {
21
106
  maxAttempts: number;
22
107
  retryInterval: number;
@@ -24,5 +109,4 @@ type PollOpts = {
24
109
  type PollPredicate = () => boolean | Promise<boolean>;
25
110
  declare function poll(opts: PollOpts, predicate: PollPredicate): any;
26
111
  declare function poll(key: string, opts: PollOpts, predicate: PollPredicate): any;
27
- export {};
28
112
  //# sourceMappingURL=step-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step-runner.d.ts","sourceRoot":"","sources":["../../src/internal/step-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAEZ,SAAS,EACT,UAAU,EAGX,MAAM,iBAAiB,CAAC;AAIzB;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU;;;;CAAuB,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC;AAE3C,iBAAS,GAAG,CAAC,CAAC,SAAS,GAAG,EACxB,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,cAAc,CAAC,YAAY,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC;AAE3D,iBAAS,GAAG,CAAC,CAAC,SAAS,GAAG,EACxB,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,cAAc,CAAC,YAAY,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC;AAiD3D,iBAAS,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC;AAC3C,iBAAS,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC;AA6BxD,KAAK,QAAQ,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/D,KAAK,aAAa,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtD,iBAAS,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,GAAG,CAAC;AAC7D,iBAAS,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,GAAG,CAAC"}
1
+ {"version":3,"file":"step-runner.d.ts","sourceRoot":"","sources":["../../src/internal/step-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,WAAW,EACX,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAId,KAAK,gBAAgB,EACrB,YAAY,EAEZ,SAAS,EACT,UAAU,EAIV,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,UAAU,EAEf,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EAGtB,KAAK,OAAO,EAQb,MAAM,iBAAiB,CAAC;AAQzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC7B,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC;IAC1C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;IACvB,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,CAAC,CAAC,EACN,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GACpD,cAAc,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACnC,MAAM,CACJ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,GACrC,cAAc,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,UAAU,CACR,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,EAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,GACrC,cAAc,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,UAAU,CACR,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GACzB,cAAc,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;IACvD,IAAI,CAAC,CAAC,EACJ,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,GACtD,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC3B,cAAc,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC,EACJ,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,GACtD,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC3B,cAAc,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC,EACJ,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,GACtD,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,EAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,GACzD,cAAc,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACjD,CAAC;AAEF,yEAAyE;AACzE,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAExE;;;GAGG;AACH,KAAK,oBAAoB,CAAC,CAAC,EAAE,CAAC,IAAI,SAAS,CACzC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,EACjB,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,CACxE,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE7D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,MAAM,MAAM,CAAC;CACrB;;;wBA8Y+B,MAAM,GAAG,GAAG;cACtB,MAAM,iBAAiB,MAAM,GAAG,GAAG;;;YAtYxC,MAAM,SAAS,gBAAgB,cAChC,eAAe,CAAC,MAAM,CAAC,GAAG;QACpC,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;KACpC,WACQ;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,KACvB,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAcrB,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WACtC,MAAM,UACP;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;KAAE,KAChE,cAAc,CACf,YAAY,EACZ;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,EACvB,UAAU,GAAG,SAAS,CACvB;aAoBe,CAAC,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,2BACzC,MAAM,cACH,gBAAgB,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,KAC9D,cAAc,CAAC,YAAY,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;EAY3D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,EAAE,KAAK,GAAG,aAAa,IAAI;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAC5C,OAAO,GACP,SAAS,CAAC,CAAC,EAAE;IAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC,CAAC;AAgOrE,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,gBAAgB,IAAI;IACvD;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC;CACX,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,MAAM,CAAC,GAClD,MAAM,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,GACrF,KAAK,CAAC;AAEV,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAE9D,iBAAS,GAAG,CAAC,CAAC,SAAS,MAAM,OAAO,EAClC,EAAE,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GACvB,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAEtE,iBAAS,GAAG,CAAC,CAAC,SAAS,MAAM,OAAO,EAClC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GACvB,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAEtE,iBAAS,GAAG,CAAC,CAAC,SAAS,gBAAgB,EACrC,EAAE,EAAE,MAAM,eAAe,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAC9D,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GACzB,cAAc,CAAC,YAAY,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAE/E,iBAAS,GAAG,CAAC,CAAC,SAAS,gBAAgB,EACrC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,eAAe,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAC9D,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GACzB,cAAc,CAAC,YAAY,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AA+B/E,KAAK,eAAe,CAAC,CAAC,SAAS,gBAAgB,IAAI,OAAO,CACxD,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC,CAC/B,CAAC;AA4CF,KAAK,QAAQ,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/D,KAAK,aAAa,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtD,iBAAS,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,GAAG,CAAC;AAC7D,iBAAS,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,GAAG,CAAC"}
@@ -1,3 +1,14 @@
1
1
  export declare const isIterable: <T>(i: T | Iterable<T>) => i is Iterable<T>;
2
+ /**
3
+ * Derives a replay-stable cache key from the call site of a keyless step.
4
+ *
5
+ * IMPORTANT: `stepFn` must be the synchronously-executing function that the
6
+ * user called directly (e.g. the `step.run` wrapper) – NOT an async generator
7
+ * body. Async generator bodies don't run until the first `.next()`, by which
8
+ * point the user's frame is no longer on the stack. `Error.captureStackTrace`
9
+ * omits every frame above (and including) `stepFn`, so the first captured
10
+ * frame is the user's call site (file:line:column), which is deterministic
11
+ * across executions of the same workflow code.
12
+ */
2
13
  export declare function getCallSiteHash(stepFn: Function): string;
3
14
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAIjE,CAAC;AAGF,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,UAe/C"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAIjE,CAAC;AAGF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAkBxD"}
package/package.json CHANGED
@@ -1,21 +1,25 @@
1
1
  {
2
2
  "name": "yieldstar",
3
- "version": "0.4.6",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "author": "Daniel Grant",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/notationlabs/yieldstar.git",
9
+ "directory": "packages/yieldstar"
10
+ },
6
11
  "type": "module",
7
12
  "main": "dist/index.js",
8
13
  "types": "dist/index.d.ts",
9
14
  "dependencies": {
10
15
  "nanoid": "^5.1.5",
11
16
  "pino": "^9.9.0",
12
- "serialize-error": "^11.0.3",
13
- "@yieldstar/core": "0.4.6"
17
+ "@yieldstar/core": "0.6.0"
14
18
  },
15
19
  "devDependencies": {
16
20
  "@types/node": "^22.18.0"
17
21
  },
18
22
  "scripts": {
19
- "build": "bun build src/index.ts --outfile dist/index.js --target bun --packages=external"
23
+ "build": "bun build src/index.ts --outfile dist/index.js --target node --packages=external"
20
24
  }
21
25
  }
@@ -1,3 +1,5 @@
1
+ import { registerErrorClass } from "@yieldstar/core";
2
+
1
3
  export class RetryableError extends Error {
2
4
  maxAttempts: number;
3
5
  retryInterval: number;
@@ -11,3 +13,5 @@ export class RetryableError extends Error {
11
13
  this.retryInterval = opts.retryInterval;
12
14
  }
13
15
  }
16
+
17
+ registerErrorClass("RetryableError", RetryableError);
@@ -2,12 +2,13 @@ import type {
2
2
  EventParamsOf,
3
3
  WorkflowRouter,
4
4
  TriggerEvent,
5
- ExecutionEvent,
6
5
  WorkflowGeneratorReturnType,
6
+ ExecutionAdmissionErrorCode,
7
+ } from "@yieldstar/core";
8
+ import {
9
+ ExecutionAdmissionError,
10
+ durableValueCodec,
7
11
  } from "@yieldstar/core";
8
- import { nanoid } from "nanoid";
9
- import { deserializeError } from "serialize-error";
10
- import { errorWithOriginalStack } from "@yieldstar/core";
11
12
 
12
13
  type TriggerAck = {
13
14
  executionId: string;
@@ -19,60 +20,92 @@ type TriggerResponse<W extends WorkflowRouter, K extends string & keyof W> = {
19
20
  waitForResult: () => Promise<WorkflowGeneratorReturnType<W[K]>>;
20
21
  };
21
22
 
23
+ const admissionErrorCodes: ExecutionAdmissionErrorCode[] = [
24
+ "EXECUTION_ID_CONFLICT",
25
+ "INVALID_EXECUTION_EVENT",
26
+ ];
27
+
22
28
  export function createHttpSdkFactory<W extends WorkflowRouter>() {
23
29
  return (params: {
24
30
  url: string;
25
31
  fetchOptions?: Omit<RequestInit, "body" | "method">;
26
32
  }) => {
27
33
  return {
34
+ /**
35
+ * Admits an execution through the trigger route. When `executionId` is
36
+ * omitted the runtime chooses one, which the response carries. The body
37
+ * is encoded with the durable value codec, so params and context reach
38
+ * the runtime unchanged.
39
+ * @throws {ExecutionAdmissionError} on `409 Conflict` or an invalid event,
40
+ * including params or context the codec rejects, before any request
41
+ */
28
42
  async trigger<K extends string & keyof W>(
29
43
  event: TriggerEvent<K, EventParamsOf<W[K]>>
30
44
  ): Promise<TriggerResponse<W, K>> {
31
- const executionEvent: ExecutionEvent = {
32
- executionId: event?.executionId ?? nanoid(),
33
- workflowId: event?.workflowId as string,
34
- params: event?.params,
35
- context: event?.context,
36
- };
37
-
45
+ const request: Record<string, unknown> = { workflowId: event?.workflowId };
46
+ if (event?.executionId !== undefined) request.executionId = event.executionId;
47
+ if (event && "params" in event) request.params = event.params;
48
+ if (event?.context !== undefined) request.context = event.context;
49
+ const encoding = durableValueCodec.encode(request);
50
+ if (encoding.status === "invalid") {
51
+ throw new ExecutionAdmissionError(
52
+ "INVALID_EXECUTION_EVENT",
53
+ `Event cannot be stored without loss: ${encoding.message}`
54
+ );
55
+ }
38
56
  const res = await fetch(`${params.url}/trigger`, {
39
57
  ...params.fetchOptions,
40
58
  method: "POST",
41
- body: JSON.stringify(executionEvent),
59
+ headers: { "content-type": "application/json" },
60
+ body: encoding.canonical,
42
61
  });
43
62
 
44
63
  if (res.ok) {
64
+ const ack = (await res.json()) as TriggerAck;
45
65
  return {
46
- executionId: executionEvent.executionId,
47
- ack: () => res.json() as Promise<TriggerAck>,
48
- waitForResult: () =>
49
- this.waitForResult<K>({
50
- executionId: executionEvent.executionId,
51
- }),
66
+ executionId: ack.executionId,
67
+ ack: async () => ack,
68
+ waitForResult: () => this.waitForResult<K>(ack),
52
69
  };
53
- } else {
54
- throw res.statusText;
55
70
  }
71
+
72
+ const body = (await res.json().catch(() => undefined)) as
73
+ | { code?: ExecutionAdmissionErrorCode; message?: string }
74
+ | undefined;
75
+ if (body?.code && admissionErrorCodes.includes(body.code)) {
76
+ throw new ExecutionAdmissionError(body.code, body.message ?? res.statusText);
77
+ }
78
+ throw res.statusText;
56
79
  },
57
80
  async waitForResult<K extends string & keyof W>(
58
81
  ack: TriggerAck
59
82
  ): Promise<WorkflowGeneratorReturnType<W[K]>> {
60
- // todo: set up subscription first (maybe just use ws)
61
83
  const res = await fetch(`${params.url}/events`, {
62
84
  method: "POST",
63
85
  body: JSON.stringify({ executionId: ack.executionId }),
64
86
  ...params.fetchOptions,
65
87
  });
66
88
 
89
+ if (res.status !== 500 && !res.ok) {
90
+ throw res.statusText;
91
+ }
92
+
93
+ const decoding = durableValueCodec.decode(await res.text());
94
+ if (decoding.status === "invalid") {
95
+ throw new Error(`Invalid workflow outcome: ${decoding.message}`);
96
+ }
97
+
67
98
  if (res.status === 500) {
68
- const error = await res.json();
69
- throw errorWithOriginalStack(
70
- deserializeError(error),
71
- this.waitForResult
72
- );
99
+ const error =
100
+ decoding.value instanceof Error
101
+ ? decoding.value
102
+ : new Error(String(decoding.value));
103
+ // Thrown as revived, so it keeps its registered class and the
104
+ // stack recorded where it was first thrown.
105
+ throw error;
73
106
  }
74
107
 
75
- return res.json() as Promise<WorkflowGeneratorReturnType<W[K]>>;
108
+ return decoding.value as WorkflowGeneratorReturnType<W[K]>;
76
109
  },
77
110
  };
78
111
  };
@@ -1,43 +1,69 @@
1
1
  import type {
2
2
  EventParamsOf,
3
+ ExecutionClient,
3
4
  WorkflowRouter,
4
5
  WorkflowInvoker,
5
6
  TriggerEvent,
6
- ExecutionEvent,
7
7
  WorkflowGeneratorReturnType,
8
8
  } from "@yieldstar/core";
9
- import { nanoid } from "nanoid";
9
+ import {
10
+ ExecutionAdmissionError,
11
+ waitForExecutionOutcome,
12
+ } from "@yieldstar/core";
10
13
 
11
14
  type TriggerAck = {
12
15
  executionId: string;
13
16
  };
14
17
 
15
18
  export function createLocalSdk<W extends WorkflowRouter>(
16
- invoker: WorkflowInvoker
19
+ runtime: { executions: ExecutionClient },
20
+ options?: {
21
+ /** Lets `triggerAndWait` settle as soon as the dispatcher acknowledges. */
22
+ invoker?: WorkflowInvoker;
23
+ /** Milliseconds between reads of the execution record. @default 25 */
24
+ pollInterval?: number;
25
+ }
17
26
  ) {
27
+ const { executions } = runtime;
28
+
18
29
  return {
30
+ /**
31
+ * Admits an execution and resolves once admission commits. A dispatcher
32
+ * delivers it; this does not wait for a worker to start.
33
+ * @throws {ExecutionAdmissionError} when the ID is already in use or the
34
+ * event cannot be stored without loss
35
+ */
19
36
  async trigger<K extends string & keyof W>(
20
37
  event: TriggerEvent<K, EventParamsOf<W[K]>>
21
38
  ): Promise<TriggerAck> {
22
- const executionEvent: ExecutionEvent = {
23
- executionId: event.executionId ?? nanoid(),
39
+ const admission = await executions.admit({
40
+ executionId: event.executionId,
24
41
  workflowId: event.workflowId,
25
42
  params: event.params,
26
- };
27
- await invoker.execute(executionEvent);
28
- return { executionId: executionEvent.executionId };
43
+ context: new Map(Object.entries(event.context ?? {})),
44
+ });
45
+ if (admission.status === "rejected") {
46
+ throw new ExecutionAdmissionError(admission.code, admission.message);
47
+ }
48
+ return { executionId: admission.executionId };
29
49
  },
50
+ /**
51
+ * Admits an execution and waits for its record to reach a terminal
52
+ * status. Resolves with the result, or rejects with the recorded error.
53
+ */
30
54
  async triggerAndWait<K extends string & keyof W>(
31
55
  event: TriggerEvent<K, EventParamsOf<W[K]>>
32
56
  ): Promise<WorkflowGeneratorReturnType<W[K]>> {
33
- const executionId = event.executionId ?? nanoid();
34
- const workflowCompletePromise = new Promise((resolve) => {
35
- invoker.workflowEndEmitter.once(executionId, resolve);
57
+ const { executionId } = await this.trigger(event);
58
+ const outcome = await waitForExecutionOutcome(executions, executionId, {
59
+ emitter: options?.invoker?.workflowEndEmitter,
60
+ pollInterval: options?.pollInterval,
36
61
  });
37
- await this.trigger({ ...event, executionId });
38
- return workflowCompletePromise as Promise<
39
- WorkflowGeneratorReturnType<W[K]>
40
- >;
62
+ if (!outcome) {
63
+ throw new Error(`Execution "${executionId}" has no record`);
64
+ }
65
+ if (outcome.status === "failed") throw outcome.error;
66
+ return outcome.result as WorkflowGeneratorReturnType<W[K]>;
41
67
  },
42
68
  };
43
69
  }