@yieldstar/core 0.5.0 → 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/dist/base/durable-value.d.ts +71 -0
- package/dist/base/durable-value.d.ts.map +1 -0
- package/dist/base/durable-value.test.d.ts +2 -0
- package/dist/base/durable-value.test.d.ts.map +1 -0
- package/dist/base/effect.d.ts +136 -0
- package/dist/base/effect.d.ts.map +1 -0
- package/dist/base/errors.d.ts +23 -0
- package/dist/base/errors.d.ts.map +1 -0
- package/dist/base/errors.test.d.ts +2 -0
- package/dist/base/errors.test.d.ts.map +1 -0
- package/dist/base/event.d.ts +6 -1
- package/dist/base/event.d.ts.map +1 -1
- package/dist/base/execution.d.ts +244 -0
- package/dist/base/execution.d.ts.map +1 -0
- package/dist/base/heap.d.ts +15 -3
- package/dist/base/heap.d.ts.map +1 -1
- package/dist/base/invoker.d.ts +47 -2
- package/dist/base/invoker.d.ts.map +1 -1
- package/dist/base/runtime.d.ts +27 -4
- package/dist/base/runtime.d.ts.map +1 -1
- package/dist/base/step.d.ts +21 -5
- package/dist/base/step.d.ts.map +1 -1
- package/dist/base/store.d.ts +135 -21
- package/dist/base/store.d.ts.map +1 -1
- package/dist/base/workflow.d.ts +15 -0
- package/dist/base/workflow.d.ts.map +1 -1
- package/dist/index.d.ts +23 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1647 -172
- package/dist/lib/effect-state.d.ts +78 -0
- package/dist/lib/effect-state.d.ts.map +1 -0
- package/dist/lib/execution-dispatcher.d.ts +102 -0
- package/dist/lib/execution-dispatcher.d.ts.map +1 -0
- package/dist/lib/execution-state.d.ts +248 -0
- package/dist/lib/execution-state.d.ts.map +1 -0
- package/dist/lib/execution-state.test.d.ts +2 -0
- package/dist/lib/execution-state.test.d.ts.map +1 -0
- package/dist/lib/wait-for-outcome.d.ts +15 -0
- package/dist/lib/wait-for-outcome.d.ts.map +1 -0
- package/dist/lib/workflow-runner.d.ts +14 -9
- package/dist/lib/workflow-runner.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/base/durable-value.test.ts +285 -0
- package/src/base/durable-value.ts +442 -0
- package/src/base/effect.ts +165 -0
- package/src/base/errors.test.ts +210 -0
- package/src/base/errors.ts +72 -0
- package/src/base/event.ts +6 -1
- package/src/base/execution.ts +303 -0
- package/src/base/heap.ts +17 -3
- package/src/base/invoker.ts +60 -2
- package/src/base/runtime.ts +28 -7
- package/src/base/step.ts +29 -7
- package/src/base/store.ts +339 -49
- package/src/base/workflow.ts +15 -0
- package/src/index.ts +137 -3
- package/src/lib/effect-state.ts +254 -0
- package/src/lib/execution-dispatcher.ts +393 -0
- package/src/lib/execution-state.test.ts +249 -0
- package/src/lib/execution-state.ts +650 -0
- package/src/lib/wait-for-outcome.ts +40 -0
- package/src/lib/workflow-runner.ts +75 -43
- package/src/base/scheduler.ts +0 -8
- package/tsconfig.tsbuildinfo +0 -1
package/src/index.ts
CHANGED
|
@@ -1,7 +1,133 @@
|
|
|
1
1
|
export type { HeapClient, HeapRecord } from "./base/heap";
|
|
2
|
-
export type { WorkflowInvoker } from "./base/invoker";
|
|
3
|
-
export
|
|
4
|
-
export type {
|
|
2
|
+
export type { DeliveryOptions, WorkflowInvoker } from "./base/invoker";
|
|
3
|
+
export { WorkerExitedError } from "./base/invoker";
|
|
4
|
+
export type { RuntimeOptions } from "./base/runtime";
|
|
5
|
+
export { Runtime } from "./base/runtime";
|
|
6
|
+
export type {
|
|
7
|
+
ExecutionAcknowledgeResult,
|
|
8
|
+
ExecutionAdmissionErrorCode,
|
|
9
|
+
ExecutionAdmissionRequest,
|
|
10
|
+
ExecutionAdmissionResult,
|
|
11
|
+
ExecutionClient,
|
|
12
|
+
ExecutionLease,
|
|
13
|
+
ExecutionOutcome,
|
|
14
|
+
ExecutionParent,
|
|
15
|
+
ExecutionRecord,
|
|
16
|
+
ExecutionReleaseResult,
|
|
17
|
+
ExecutionRenewResult,
|
|
18
|
+
ExecutionStartRequest,
|
|
19
|
+
ExecutionStartResult,
|
|
20
|
+
ExecutionStatus,
|
|
21
|
+
ExecutionWakeResult,
|
|
22
|
+
LeaseLost,
|
|
23
|
+
WorkflowRunOutcome,
|
|
24
|
+
WorkflowStartErrorCode,
|
|
25
|
+
} from "./base/execution";
|
|
26
|
+
export {
|
|
27
|
+
ExecutionAdmissionError,
|
|
28
|
+
InfrastructureError,
|
|
29
|
+
LeaseLostError,
|
|
30
|
+
StepConflictError,
|
|
31
|
+
WorkflowStartError,
|
|
32
|
+
isLeaseLost,
|
|
33
|
+
isTerminalExecutionStatus,
|
|
34
|
+
} from "./base/execution";
|
|
35
|
+
export type {
|
|
36
|
+
EffectBeginRequest,
|
|
37
|
+
EffectBeginResult,
|
|
38
|
+
EffectClient,
|
|
39
|
+
EffectConflictReport,
|
|
40
|
+
EffectDeliveryErrorCode,
|
|
41
|
+
EffectRecord,
|
|
42
|
+
EffectSettleRequest,
|
|
43
|
+
EffectSettleResult,
|
|
44
|
+
EffectWatchRequest,
|
|
45
|
+
WorkflowEffectErrorCode,
|
|
46
|
+
} from "./base/effect";
|
|
47
|
+
export {
|
|
48
|
+
EffectClientInvariantError,
|
|
49
|
+
EffectDeliveryError,
|
|
50
|
+
WorkflowEffectError,
|
|
51
|
+
} from "./base/effect";
|
|
52
|
+
export type {
|
|
53
|
+
EffectConflictState,
|
|
54
|
+
EffectState,
|
|
55
|
+
PreparedEffectBegin,
|
|
56
|
+
PreparedEffectSettle,
|
|
57
|
+
} from "./lib/effect-state";
|
|
58
|
+
export {
|
|
59
|
+
createEffectState,
|
|
60
|
+
effectNotFound,
|
|
61
|
+
matchEffectBegin,
|
|
62
|
+
prepareEffectBegin,
|
|
63
|
+
prepareEffectSettle,
|
|
64
|
+
prepareEffectWatch,
|
|
65
|
+
settleEffect,
|
|
66
|
+
toEffectRecord,
|
|
67
|
+
validateEffectRequest,
|
|
68
|
+
} from "./lib/effect-state";
|
|
69
|
+
export type {
|
|
70
|
+
Durable,
|
|
71
|
+
DurableDecoding,
|
|
72
|
+
DurableObject,
|
|
73
|
+
DurableValue,
|
|
74
|
+
DurableValueCodec,
|
|
75
|
+
DurableValueEncoding,
|
|
76
|
+
IfDurable,
|
|
77
|
+
} from "./base/durable-value";
|
|
78
|
+
export {
|
|
79
|
+
DurableValueError,
|
|
80
|
+
decodeParsedValue,
|
|
81
|
+
durableValueCodec,
|
|
82
|
+
encodeThrownValue,
|
|
83
|
+
} from "./base/durable-value";
|
|
84
|
+
export type { ErrorClass, WorkflowValueErrorCode } from "./base/errors";
|
|
85
|
+
export {
|
|
86
|
+
WORKFLOW_RESULT_STEP_KEY,
|
|
87
|
+
WorkflowValueError,
|
|
88
|
+
registerErrorClass,
|
|
89
|
+
} from "./base/errors";
|
|
90
|
+
export type {
|
|
91
|
+
ExecutionState,
|
|
92
|
+
PreparedExecutionStart,
|
|
93
|
+
StartRequestState,
|
|
94
|
+
} from "./lib/execution-state";
|
|
95
|
+
export {
|
|
96
|
+
DEFAULT_LEASE_DURATION,
|
|
97
|
+
REDELIVERY_DELAY_BASE,
|
|
98
|
+
REDELIVERY_DELAY_MAX,
|
|
99
|
+
ExecutionEventEncodingError,
|
|
100
|
+
acknowledgeExecution,
|
|
101
|
+
claimExecution,
|
|
102
|
+
createExecutionState,
|
|
103
|
+
createStartedExecution,
|
|
104
|
+
decodeExecutionEvent,
|
|
105
|
+
decodeExecutionOutcome,
|
|
106
|
+
encodeExecutionEvent,
|
|
107
|
+
encodeExecutionOutcome,
|
|
108
|
+
executionIdConflict,
|
|
109
|
+
executionNotFound,
|
|
110
|
+
executionStatusAt,
|
|
111
|
+
isExecutionClaimable,
|
|
112
|
+
isLeaseCurrent,
|
|
113
|
+
matchStartRequest,
|
|
114
|
+
nextClaimAt,
|
|
115
|
+
prepareExecutionAdmission,
|
|
116
|
+
prepareExecutionStart,
|
|
117
|
+
redeliveryDelay,
|
|
118
|
+
releaseExecutionLease,
|
|
119
|
+
renewExecutionLease,
|
|
120
|
+
requestExecutionWake,
|
|
121
|
+
toExecutionRecord,
|
|
122
|
+
unacknowledgedDeliveryCountAt,
|
|
123
|
+
} from "./lib/execution-state";
|
|
124
|
+
export type { DispatcherOptions } from "./lib/execution-dispatcher";
|
|
125
|
+
export {
|
|
126
|
+
DEFAULT_ABORT_GRACE_PERIOD,
|
|
127
|
+
DEFAULT_DISPATCHER_CONCURRENCY,
|
|
128
|
+
ExecutionDispatcher,
|
|
129
|
+
} from "./lib/execution-dispatcher";
|
|
130
|
+
export { waitForExecutionOutcome } from "./lib/wait-for-outcome";
|
|
5
131
|
export type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
132
|
export type {
|
|
7
133
|
Draft,
|
|
@@ -13,6 +139,7 @@ export type {
|
|
|
13
139
|
StoreSelector,
|
|
14
140
|
StoreSnapshot,
|
|
15
141
|
StoreState,
|
|
142
|
+
DurableStoreSchema,
|
|
16
143
|
StoreStepId,
|
|
17
144
|
StoreTakeResult,
|
|
18
145
|
StoreUpdateFromResult,
|
|
@@ -21,15 +148,20 @@ export type {
|
|
|
21
148
|
StoreWaiter,
|
|
22
149
|
StoreMutation,
|
|
23
150
|
StoreMutationCommitResult,
|
|
151
|
+
StoreDeletion,
|
|
152
|
+
StoreReadSnapshot,
|
|
24
153
|
TrackedStoreUpdater,
|
|
25
154
|
} from "./base/store";
|
|
26
155
|
export {
|
|
27
156
|
StoreClient,
|
|
28
157
|
CasStoreClient,
|
|
158
|
+
StoreValidationError,
|
|
29
159
|
assertSynchronousClaim,
|
|
30
160
|
assertSynchronousUpdater,
|
|
31
161
|
cloneStoreState,
|
|
162
|
+
decodeStoreValue,
|
|
32
163
|
defineStore,
|
|
164
|
+
encodeStoreValue,
|
|
33
165
|
diffStorePaths,
|
|
34
166
|
isStoreSelectorMatch,
|
|
35
167
|
storePathsIntersect,
|
|
@@ -60,6 +192,7 @@ export type {
|
|
|
60
192
|
export {
|
|
61
193
|
StepCacheCheck,
|
|
62
194
|
StepDelay,
|
|
195
|
+
StepEffectWait,
|
|
63
196
|
StepError,
|
|
64
197
|
StepInvalid,
|
|
65
198
|
StepKey,
|
|
@@ -70,6 +203,7 @@ export {
|
|
|
70
203
|
WorkflowRestart,
|
|
71
204
|
WorkflowResult,
|
|
72
205
|
} from "./base/step";
|
|
206
|
+
export type { StepSchemaVerdict } from "./base/step";
|
|
73
207
|
|
|
74
208
|
export { WorkflowRunner } from "./lib/workflow-runner";
|
|
75
209
|
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { durableValueCodec, isPlainObject, type DurableObject, type DurableValue } from "../base/durable-value";
|
|
2
|
+
import {
|
|
3
|
+
EffectClientInvariantError,
|
|
4
|
+
type EffectBeginRequest,
|
|
5
|
+
type EffectBeginResult,
|
|
6
|
+
type EffectRecord,
|
|
7
|
+
type EffectSettleRequest,
|
|
8
|
+
type EffectSettleResult,
|
|
9
|
+
type EffectWatchRequest,
|
|
10
|
+
} from "../base/effect";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* An effect as runtime adapters store it. `input`, `result` and the values of
|
|
14
|
+
* each conflict report are canonical `durableValueCodec` encodings, so equal
|
|
15
|
+
* values compare as equal strings.
|
|
16
|
+
*/
|
|
17
|
+
export type EffectState = {
|
|
18
|
+
effectId: string;
|
|
19
|
+
operation: string;
|
|
20
|
+
input: string;
|
|
21
|
+
status: "pending" | "settled";
|
|
22
|
+
result?: string;
|
|
23
|
+
conflicts: EffectConflictState[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type EffectConflictState = {
|
|
27
|
+
result: string;
|
|
28
|
+
conflictingResult: string;
|
|
29
|
+
reportedAt: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type PreparedEffectBegin = {
|
|
33
|
+
effectId: string;
|
|
34
|
+
operation: string;
|
|
35
|
+
/** The canonical input. */
|
|
36
|
+
input: string;
|
|
37
|
+
executionId: string;
|
|
38
|
+
stepKey: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type PreparedEffectSettle = {
|
|
42
|
+
effectId: string;
|
|
43
|
+
operation: string;
|
|
44
|
+
/** The canonical result. */
|
|
45
|
+
result: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Checks the values `step.effect` passes to `begin`, and returns a message
|
|
50
|
+
* naming the first invalid one. The driver rejects these requests with
|
|
51
|
+
* `INVALID_EFFECT_REQUEST` before calling the client.
|
|
52
|
+
*/
|
|
53
|
+
export function validateEffectRequest(request: {
|
|
54
|
+
effectId: unknown;
|
|
55
|
+
operation: unknown;
|
|
56
|
+
input: unknown;
|
|
57
|
+
}): { ok: true; input: string } | { ok: false; message: string } {
|
|
58
|
+
if (!isNonEmptyString(request.effectId)) {
|
|
59
|
+
return { ok: false, message: "effectId must be a non-empty string" };
|
|
60
|
+
}
|
|
61
|
+
if (!isNonEmptyString(request.operation)) {
|
|
62
|
+
return { ok: false, message: "operation must be a non-empty string" };
|
|
63
|
+
}
|
|
64
|
+
if (!isPlainObject(request.input)) {
|
|
65
|
+
return { ok: false, message: "input must be a plain object" };
|
|
66
|
+
}
|
|
67
|
+
const encoding = durableValueCodec.encode(request.input);
|
|
68
|
+
if (encoding.status === "invalid") {
|
|
69
|
+
return { ok: false, message: `input: ${encoding.message}` };
|
|
70
|
+
}
|
|
71
|
+
return { ok: true, input: encoding.canonical };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Validates a `begin` request and encodes its input canonically. Adapters call
|
|
76
|
+
* this before touching storage; it throws `EffectClientInvariantError` for a
|
|
77
|
+
* request the driver should have rejected.
|
|
78
|
+
*/
|
|
79
|
+
export function prepareEffectBegin(request: EffectBeginRequest): PreparedEffectBegin {
|
|
80
|
+
const validated = validateEffectRequest(request);
|
|
81
|
+
if (!validated.ok) throw invariant(`Invalid effect request: ${validated.message}`);
|
|
82
|
+
assertStepId(request);
|
|
83
|
+
return {
|
|
84
|
+
effectId: request.effectId,
|
|
85
|
+
operation: request.operation,
|
|
86
|
+
input: validated.input,
|
|
87
|
+
executionId: request.executionId,
|
|
88
|
+
stepKey: request.stepKey,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Validates a `settle` request and encodes its result canonically. */
|
|
93
|
+
export function prepareEffectSettle(request: EffectSettleRequest): PreparedEffectSettle {
|
|
94
|
+
if (!isNonEmptyString(request.effectId)) {
|
|
95
|
+
throw invariant("Invalid effect settlement: effectId must be a non-empty string");
|
|
96
|
+
}
|
|
97
|
+
if (!isNonEmptyString(request.operation)) {
|
|
98
|
+
throw invariant("Invalid effect settlement: operation must be a non-empty string");
|
|
99
|
+
}
|
|
100
|
+
const encoding = durableValueCodec.encode(request.result);
|
|
101
|
+
if (encoding.status === "invalid") {
|
|
102
|
+
throw invariant(`Invalid effect settlement: result: ${encoding.message}`);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
effectId: request.effectId,
|
|
106
|
+
operation: request.operation,
|
|
107
|
+
result: encoding.canonical,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Validates a `watch` request. */
|
|
112
|
+
export function prepareEffectWatch(request: EffectWatchRequest): EffectWatchRequest {
|
|
113
|
+
if (!isNonEmptyString(request.effectId)) {
|
|
114
|
+
throw invariant("Invalid effect watch: effectId must be a non-empty string");
|
|
115
|
+
}
|
|
116
|
+
assertStepId(request);
|
|
117
|
+
return {
|
|
118
|
+
effectId: request.effectId,
|
|
119
|
+
executionId: request.executionId,
|
|
120
|
+
stepKey: request.stepKey,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The pending effect a `begin` that finds no effect creates. */
|
|
125
|
+
export function createEffectState(begin: PreparedEffectBegin): EffectState {
|
|
126
|
+
return {
|
|
127
|
+
effectId: begin.effectId,
|
|
128
|
+
operation: begin.operation,
|
|
129
|
+
input: begin.input,
|
|
130
|
+
status: "pending",
|
|
131
|
+
conflicts: [],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Answers a `begin` for an effect that already exists. */
|
|
136
|
+
export function matchEffectBegin(
|
|
137
|
+
state: EffectState,
|
|
138
|
+
begin: PreparedEffectBegin
|
|
139
|
+
): EffectBeginResult {
|
|
140
|
+
if (state.operation !== begin.operation || state.input !== begin.input) {
|
|
141
|
+
return { status: "rejected", code: "EFFECT_REQUEST_MISMATCH" };
|
|
142
|
+
}
|
|
143
|
+
if (state.status === "pending") return { status: "pending" };
|
|
144
|
+
return { status: "settled", result: decodeSettledResult(state) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Applies a settlement. `wake` is true only when this settlement moved the
|
|
149
|
+
* effect from pending to settled: adapters then wake every waiter in the same
|
|
150
|
+
* write. `state` is the input object when nothing changed. Throws
|
|
151
|
+
* `EffectClientInvariantError` when the operation differs from the effect's,
|
|
152
|
+
* which `begin` would have rejected.
|
|
153
|
+
*/
|
|
154
|
+
export function settleEffect(
|
|
155
|
+
state: EffectState,
|
|
156
|
+
settle: PreparedEffectSettle,
|
|
157
|
+
now: number
|
|
158
|
+
): { state: EffectState; result: EffectSettleResult; wake: boolean } {
|
|
159
|
+
if (state.operation !== settle.operation) {
|
|
160
|
+
throw invariant(
|
|
161
|
+
`Cannot settle effect "${state.effectId}" as operation "${settle.operation}": it was begun as "${state.operation}"`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (state.status === "pending") {
|
|
165
|
+
return {
|
|
166
|
+
state: { ...state, status: "settled", result: settle.result },
|
|
167
|
+
result: { status: "settled", result: decodeValue(settle.result, state.effectId) },
|
|
168
|
+
wake: true,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const result = decodeSettledResult(state);
|
|
172
|
+
if (state.result === settle.result) {
|
|
173
|
+
return { state, result: { status: "settled", result }, wake: false };
|
|
174
|
+
}
|
|
175
|
+
const conflictingResult = decodeValue(settle.result, state.effectId);
|
|
176
|
+
// One report per distinct conflicting result, so a settlement repeated after
|
|
177
|
+
// a lost response does not add another.
|
|
178
|
+
const reported = state.conflicts.some(
|
|
179
|
+
(conflict) => conflict.conflictingResult === settle.result
|
|
180
|
+
);
|
|
181
|
+
return {
|
|
182
|
+
state: reported
|
|
183
|
+
? state
|
|
184
|
+
: {
|
|
185
|
+
...state,
|
|
186
|
+
conflicts: [
|
|
187
|
+
...state.conflicts,
|
|
188
|
+
{ result: state.result!, conflictingResult: settle.result, reportedAt: now },
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
result: { status: "conflict", result, conflictingResult },
|
|
192
|
+
wake: false,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function toEffectRecord(state: EffectState): EffectRecord {
|
|
197
|
+
const record: EffectRecord = {
|
|
198
|
+
effectId: state.effectId,
|
|
199
|
+
operation: state.operation,
|
|
200
|
+
input: decodeInput(state),
|
|
201
|
+
status: state.status,
|
|
202
|
+
conflicts: state.conflicts.map((conflict) => ({
|
|
203
|
+
result: decodeValue(conflict.result, state.effectId),
|
|
204
|
+
conflictingResult: decodeValue(conflict.conflictingResult, state.effectId),
|
|
205
|
+
reportedAt: conflict.reportedAt,
|
|
206
|
+
})),
|
|
207
|
+
};
|
|
208
|
+
if (state.status === "settled") record.result = decodeSettledResult(state);
|
|
209
|
+
return record;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function effectNotFound(effectId: string): EffectClientInvariantError {
|
|
213
|
+
return invariant(`Effect "${effectId}" does not exist: begin it first`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function decodeSettledResult(state: EffectState): DurableValue {
|
|
217
|
+
if (state.result === undefined) {
|
|
218
|
+
throw invariant(`Settled effect "${state.effectId}" has no stored result`);
|
|
219
|
+
}
|
|
220
|
+
return decodeValue(state.result, state.effectId);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function decodeInput(state: EffectState): DurableObject {
|
|
224
|
+
const input = decodeValue(state.input, state.effectId);
|
|
225
|
+
if (!isPlainObject(input)) {
|
|
226
|
+
throw invariant(`Stored input of effect "${state.effectId}" is not an object`);
|
|
227
|
+
}
|
|
228
|
+
return input as DurableObject;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function decodeValue(canonical: string, effectId: string): DurableValue {
|
|
232
|
+
const decoded = durableValueCodec.decode(canonical);
|
|
233
|
+
if (decoded.status === "invalid") {
|
|
234
|
+
throw invariant(`Stored value of effect "${effectId}" is invalid: ${decoded.message}`);
|
|
235
|
+
}
|
|
236
|
+
return decoded.value;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function assertStepId(request: { executionId: unknown; stepKey: unknown }) {
|
|
240
|
+
if (!isNonEmptyString(request.executionId)) {
|
|
241
|
+
throw invariant("Invalid effect request: executionId must be a non-empty string");
|
|
242
|
+
}
|
|
243
|
+
if (!isNonEmptyString(request.stepKey)) {
|
|
244
|
+
throw invariant("Invalid effect request: stepKey must be a non-empty string");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
249
|
+
return typeof value === "string" && value !== "";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function invariant(message: string) {
|
|
253
|
+
return new EffectClientInvariantError(message);
|
|
254
|
+
}
|