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.
- package/README.md +57 -64
- package/dist/exports/errors.d.ts.map +1 -1
- package/dist/exports/sdk-http.d.ts +8 -0
- package/dist/exports/sdk-http.d.ts.map +1 -1
- package/dist/exports/sdk-local.d.ts +19 -2
- package/dist/exports/sdk-local.d.ts.map +1 -1
- package/dist/exports/workflow.d.ts +14 -2
- package/dist/exports/workflow.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +791 -137
- package/dist/internal/effect-call.d.ts +60 -0
- package/dist/internal/effect-call.d.ts.map +1 -0
- package/dist/internal/runtime-call.d.ts +39 -0
- package/dist/internal/runtime-call.d.ts.map +1 -0
- package/dist/internal/serialise.d.ts.map +1 -1
- package/dist/internal/step-record.d.ts +13 -0
- package/dist/internal/step-record.d.ts.map +1 -0
- package/dist/internal/step-runner.d.ts +93 -9
- package/dist/internal/step-runner.d.ts.map +1 -1
- package/dist/internal/utils.d.ts +11 -0
- package/dist/internal/utils.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/exports/errors.ts +4 -0
- package/src/exports/sdk-http.ts +60 -27
- package/src/exports/sdk-local.ts +41 -15
- package/src/exports/workflow.ts +159 -99
- package/src/index.ts +51 -1
- package/src/internal/effect-call.ts +217 -0
- package/src/internal/runtime-call.ts +95 -0
- package/src/internal/step-record.ts +110 -0
- package/src/internal/step-runner.ts +748 -53
- package/src/internal/utils.ts +24 -10
- package/dist/exports/scheduler.d.ts +0 -49
- package/dist/exports/scheduler.d.ts.map +0 -1
- package/dist/exports/simplified-types.d.ts +0 -31
- package/dist/exports/simplified-types.d.ts.map +0 -1
- package/dist/exports/types.d.ts +0 -6
- package/dist/exports/types.d.ts.map +0 -1
- package/dist/internal/types.d.ts +0 -9
- package/dist/internal/types.d.ts.map +0 -1
- package/src/internal/serialise.ts +0 -44
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InfrastructureError,
|
|
3
|
+
StepResponse,
|
|
4
|
+
StoreValidationError,
|
|
5
|
+
WorkflowValueError,
|
|
6
|
+
type ExecutionClient,
|
|
7
|
+
type StoreClient,
|
|
8
|
+
} from "@yieldstar/core";
|
|
9
|
+
|
|
10
|
+
export type RuntimeClients = {
|
|
11
|
+
stores: StoreClient;
|
|
12
|
+
executions: ExecutionClient;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type RuntimeCallResult<T> =
|
|
16
|
+
| { ok: true; value: T }
|
|
17
|
+
| { ok: false; error: unknown };
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Yielded by a step that needs a runtime client. Steps never call runtime
|
|
21
|
+
* clients themselves: the driver makes the call outside workflow code and
|
|
22
|
+
* resumes the step with a `RuntimeCallResult`. Only failures that belong to
|
|
23
|
+
* workflow code (see `isWorkflowFacingError`) resume the step; any other
|
|
24
|
+
* failure is an infrastructure error and ends the delivery without an outcome.
|
|
25
|
+
*/
|
|
26
|
+
export class StepRuntimeCall<T = unknown> extends StepResponse {
|
|
27
|
+
static type = "runtime-call";
|
|
28
|
+
readonly type = "runtime-call";
|
|
29
|
+
readonly call: (clients: RuntimeClients) => Promise<T>;
|
|
30
|
+
|
|
31
|
+
constructor(call: (clients: RuntimeClients) => Promise<T>) {
|
|
32
|
+
super();
|
|
33
|
+
this.call = call;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Marks an error thrown by a workflow-supplied callback run by a client. */
|
|
38
|
+
export class StepCallbackError extends Error {
|
|
39
|
+
constructor(cause: unknown) {
|
|
40
|
+
super(cause instanceof Error ? cause.message : String(cause), { cause });
|
|
41
|
+
this.name = "StepCallbackError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Wraps a workflow-supplied callback (updater, selector, claim, initial state)
|
|
47
|
+
* that a runtime client will run, so its errors reach workflow code rather
|
|
48
|
+
* than being mistaken for infrastructure errors.
|
|
49
|
+
*/
|
|
50
|
+
export function workflowCallback<A extends unknown[], R>(
|
|
51
|
+
fn: (...args: A) => R,
|
|
52
|
+
assertResult?: (result: R) => void
|
|
53
|
+
): (...args: A) => R {
|
|
54
|
+
return (...args) => {
|
|
55
|
+
try {
|
|
56
|
+
const result = fn(...args);
|
|
57
|
+
assertResult?.(result);
|
|
58
|
+
return result;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
throw new StepCallbackError(err);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function executeRuntimeCall<T>(
|
|
66
|
+
request: StepRuntimeCall<T>,
|
|
67
|
+
clients: RuntimeClients
|
|
68
|
+
): Promise<RuntimeCallResult<T>> {
|
|
69
|
+
try {
|
|
70
|
+
return { ok: true, value: await request.call(clients) };
|
|
71
|
+
} catch (err) {
|
|
72
|
+
if (InfrastructureError.is(err)) throw err;
|
|
73
|
+
if (err instanceof StepCallbackError) return { ok: false, error: err.cause };
|
|
74
|
+
if (isWorkflowFacingError(err)) return { ok: false, error: err };
|
|
75
|
+
throw new InfrastructureError(err);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Store failures caused by the workflow's own data, which it may catch. */
|
|
80
|
+
function isWorkflowFacingError(err: unknown) {
|
|
81
|
+
return (
|
|
82
|
+
err instanceof StoreValidationError ||
|
|
83
|
+
err instanceof WorkflowValueError ||
|
|
84
|
+
(err instanceof Error && err.name === "StoreValidationError")
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Runs a heap operation, tagging its failure as an infrastructure error. */
|
|
89
|
+
export async function infrastructure<T>(fn: () => Promise<T>): Promise<T> {
|
|
90
|
+
try {
|
|
91
|
+
return await fn();
|
|
92
|
+
} catch (err) {
|
|
93
|
+
throw InfrastructureError.is(err) ? err : new InfrastructureError(err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DurableValueError,
|
|
3
|
+
StepDelay,
|
|
4
|
+
StepEffectWait,
|
|
5
|
+
StepError,
|
|
6
|
+
StepInvalid,
|
|
7
|
+
StepResult,
|
|
8
|
+
StepResponse,
|
|
9
|
+
StepStoreWait,
|
|
10
|
+
WorkflowResult,
|
|
11
|
+
WorkflowValueError,
|
|
12
|
+
decodeParsedValue,
|
|
13
|
+
durableValueCodec,
|
|
14
|
+
encodeThrownValue,
|
|
15
|
+
} from "@yieldstar/core";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Encodes a step response for its step record, and returns the response that
|
|
19
|
+
* workflow code must see: the record decoded again, so that a first run sees
|
|
20
|
+
* exactly what replay reads. A result the codec rejects is recorded as a step
|
|
21
|
+
* error with `WorkflowValueError` (`INVALID_STEP_VALUE`).
|
|
22
|
+
*/
|
|
23
|
+
export function encodeStepRecord(
|
|
24
|
+
response: StepResponse,
|
|
25
|
+
stepKey: string
|
|
26
|
+
): { json: string; response: StepResponse } {
|
|
27
|
+
const json = encodeResponse(response, stepKey);
|
|
28
|
+
return { json, response: decodeStepRecord(json) };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function encodeResponse(response: StepResponse, stepKey: string): string {
|
|
32
|
+
if (response instanceof StepResult || response instanceof WorkflowResult) {
|
|
33
|
+
const result = durableValueCodec.encode(response.result);
|
|
34
|
+
if (result.status === "invalid") {
|
|
35
|
+
const label =
|
|
36
|
+
response instanceof WorkflowResult ? "Workflow result" : `Step "${stepKey}" result`;
|
|
37
|
+
return encodeResponse(
|
|
38
|
+
new StepError(
|
|
39
|
+
new WorkflowValueError(
|
|
40
|
+
"INVALID_STEP_VALUE",
|
|
41
|
+
stepKey,
|
|
42
|
+
`${label} is not a durable value: ${result.message}`
|
|
43
|
+
)
|
|
44
|
+
),
|
|
45
|
+
stepKey
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const schema =
|
|
49
|
+
response instanceof StepResult && response.schema !== undefined
|
|
50
|
+
? `,"schema":${JSON.stringify(response.schema)}`
|
|
51
|
+
: "";
|
|
52
|
+
return `{"result":${result.canonical}${schema},"type":${JSON.stringify(response.type)}}`;
|
|
53
|
+
}
|
|
54
|
+
if (response instanceof StepError) {
|
|
55
|
+
return (
|
|
56
|
+
`{"backOffStrategy":${JSON.stringify(response.backOffStrategy)}` +
|
|
57
|
+
`,"err":${encodeThrownValue(response.err)}` +
|
|
58
|
+
`,"maxAttempts":${JSON.stringify(response.maxAttempts)}` +
|
|
59
|
+
`,"retryInterval":${JSON.stringify(response.retryInterval)}` +
|
|
60
|
+
`,"type":"step-error"}`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (response instanceof StepDelay) {
|
|
64
|
+
return `{"type":"step-delay","wakeAt":${JSON.stringify(response.wakeAt)}}`;
|
|
65
|
+
}
|
|
66
|
+
if (response instanceof StepEffectWait) {
|
|
67
|
+
return `{"type":"effect-wait","wakeAt":${JSON.stringify(response.wakeAt)}}`;
|
|
68
|
+
}
|
|
69
|
+
if (response instanceof StepStoreWait || response instanceof StepInvalid) {
|
|
70
|
+
return `{"type":${JSON.stringify(response.type)}}`;
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Cannot record a ${response.type} step response`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function decodeStepRecord(json: string): StepResponse {
|
|
76
|
+
const record = JSON.parse(json);
|
|
77
|
+
try {
|
|
78
|
+
switch (record.type) {
|
|
79
|
+
case "step-result":
|
|
80
|
+
return new StepResult(
|
|
81
|
+
decodeParsedValue(record.result, "result"),
|
|
82
|
+
record.schema === "accepted" || record.schema === "rejected"
|
|
83
|
+
? record.schema
|
|
84
|
+
: undefined
|
|
85
|
+
);
|
|
86
|
+
case "workflow-result":
|
|
87
|
+
return new WorkflowResult(decodeParsedValue(record.result, "result"));
|
|
88
|
+
case "step-error":
|
|
89
|
+
return new StepError(decodeParsedValue(record.err, "err"), {
|
|
90
|
+
maxAttempts: record.maxAttempts,
|
|
91
|
+
retryInterval: record.retryInterval,
|
|
92
|
+
backoffStrategy: record.backOffStrategy,
|
|
93
|
+
});
|
|
94
|
+
case "step-delay":
|
|
95
|
+
return new StepDelay(record.wakeAt);
|
|
96
|
+
case "effect-wait":
|
|
97
|
+
return new StepEffectWait(record.wakeAt);
|
|
98
|
+
case "store-wait":
|
|
99
|
+
return new StepStoreWait();
|
|
100
|
+
case "step-invalid":
|
|
101
|
+
return new StepInvalid();
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof DurableValueError) {
|
|
105
|
+
throw new Error(`Invalid step record: ${err.message}`);
|
|
106
|
+
}
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`Unknown step record type: ${record.type}`);
|
|
110
|
+
}
|