@restatedev/restate-sdk-cloudflare-workers 0.0.0-SNAPSHOT-20241001101403
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/esm/src/common_api.d.ts +12 -0
- package/dist/esm/src/common_api.d.ts.map +1 -0
- package/dist/esm/src/common_api.js +16 -0
- package/dist/esm/src/common_api.js.map +1 -0
- package/dist/esm/src/context.d.ts +587 -0
- package/dist/esm/src/context.d.ts.map +1 -0
- package/dist/esm/src/context.js +75 -0
- package/dist/esm/src/context.js.map +1 -0
- package/dist/esm/src/context_impl.d.ts +96 -0
- package/dist/esm/src/context_impl.d.ts.map +1 -0
- package/dist/esm/src/context_impl.js +696 -0
- package/dist/esm/src/context_impl.js.map +1 -0
- package/dist/esm/src/endpoint/endpoint_builder.d.ts +28 -0
- package/dist/esm/src/endpoint/endpoint_builder.d.ts.map +1 -0
- package/dist/esm/src/endpoint/endpoint_builder.js +139 -0
- package/dist/esm/src/endpoint/endpoint_builder.js.map +1 -0
- package/dist/esm/src/endpoint/fetch_endpoint.d.ts +54 -0
- package/dist/esm/src/endpoint/fetch_endpoint.d.ts.map +1 -0
- package/dist/esm/src/endpoint/fetch_endpoint.js +57 -0
- package/dist/esm/src/endpoint/fetch_endpoint.js.map +1 -0
- package/dist/esm/src/endpoint/handlers/fetch.d.ts +6 -0
- package/dist/esm/src/endpoint/handlers/fetch.d.ts.map +1 -0
- package/dist/esm/src/endpoint/handlers/fetch.js +30 -0
- package/dist/esm/src/endpoint/handlers/fetch.js.map +1 -0
- package/dist/esm/src/endpoint/handlers/generic.d.ts +62 -0
- package/dist/esm/src/endpoint/handlers/generic.d.ts.map +1 -0
- package/dist/esm/src/endpoint/handlers/generic.js +289 -0
- package/dist/esm/src/endpoint/handlers/generic.js.map +1 -0
- package/dist/esm/src/endpoint/handlers/lambda.d.ts +8 -0
- package/dist/esm/src/endpoint/handlers/lambda.d.ts.map +1 -0
- package/dist/esm/src/endpoint/handlers/lambda.js +80 -0
- package/dist/esm/src/endpoint/handlers/lambda.js.map +1 -0
- package/dist/esm/src/endpoint/handlers/vm/sdk_shared_core_wasm_bindings.d.ts +270 -0
- package/dist/esm/src/endpoint/handlers/vm/sdk_shared_core_wasm_bindings.js +20 -0
- package/dist/esm/src/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.js +1392 -0
- package/dist/esm/src/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.wasm +0 -0
- package/dist/esm/src/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.wasm.d.ts +61 -0
- package/dist/esm/src/endpoint/lambda_endpoint.d.ts +34 -0
- package/dist/esm/src/endpoint/lambda_endpoint.d.ts.map +1 -0
- package/dist/esm/src/endpoint/lambda_endpoint.js +49 -0
- package/dist/esm/src/endpoint/lambda_endpoint.js.map +1 -0
- package/dist/esm/src/endpoint/node_endpoint.d.ts +21 -0
- package/dist/esm/src/endpoint/node_endpoint.d.ts.map +1 -0
- package/dist/esm/src/endpoint/node_endpoint.js +101 -0
- package/dist/esm/src/endpoint/node_endpoint.js.map +1 -0
- package/dist/esm/src/endpoint/request_signing/basex.d.ts +10 -0
- package/dist/esm/src/endpoint/request_signing/basex.d.ts.map +1 -0
- package/dist/esm/src/endpoint/request_signing/basex.js +136 -0
- package/dist/esm/src/endpoint/request_signing/basex.js.map +1 -0
- package/dist/esm/src/endpoint/request_signing/ed25519.d.ts +17 -0
- package/dist/esm/src/endpoint/request_signing/ed25519.d.ts.map +1 -0
- package/dist/esm/src/endpoint/request_signing/ed25519.js +52 -0
- package/dist/esm/src/endpoint/request_signing/ed25519.js.map +1 -0
- package/dist/esm/src/endpoint/request_signing/v1.d.ts +10 -0
- package/dist/esm/src/endpoint/request_signing/v1.d.ts.map +1 -0
- package/dist/esm/src/endpoint/request_signing/v1.js +143 -0
- package/dist/esm/src/endpoint/request_signing/v1.js.map +1 -0
- package/dist/esm/src/endpoint/request_signing/validate.d.ts +19 -0
- package/dist/esm/src/endpoint/request_signing/validate.d.ts.map +1 -0
- package/dist/esm/src/endpoint/request_signing/validate.js +40 -0
- package/dist/esm/src/endpoint/request_signing/validate.js.map +1 -0
- package/dist/esm/src/endpoint.d.ts +132 -0
- package/dist/esm/src/endpoint.d.ts.map +1 -0
- package/dist/esm/src/endpoint.js +12 -0
- package/dist/esm/src/endpoint.js.map +1 -0
- package/dist/esm/src/fetch.d.ts +8 -0
- package/dist/esm/src/fetch.d.ts.map +1 -0
- package/dist/esm/src/fetch.js +23 -0
- package/dist/esm/src/fetch.js.map +1 -0
- package/dist/esm/src/generated/version.d.ts +2 -0
- package/dist/esm/src/generated/version.d.ts.map +1 -0
- package/dist/esm/src/generated/version.js +2 -0
- package/dist/esm/src/generated/version.js.map +1 -0
- package/dist/esm/src/lambda.d.ts +7 -0
- package/dist/esm/src/lambda.d.ts.map +1 -0
- package/dist/esm/src/lambda.js +19 -0
- package/dist/esm/src/lambda.js.map +1 -0
- package/dist/esm/src/logger.d.ts +35 -0
- package/dist/esm/src/logger.d.ts.map +1 -0
- package/dist/esm/src/logger.js +144 -0
- package/dist/esm/src/logger.js.map +1 -0
- package/dist/esm/src/public_api.d.ts +7 -0
- package/dist/esm/src/public_api.d.ts.map +1 -0
- package/dist/esm/src/public_api.js +19 -0
- package/dist/esm/src/public_api.js.map +1 -0
- package/dist/esm/src/types/components.d.ts +85 -0
- package/dist/esm/src/types/components.d.ts.map +1 -0
- package/dist/esm/src/types/components.js +228 -0
- package/dist/esm/src/types/components.js.map +1 -0
- package/dist/esm/src/types/discovery.d.ts +43 -0
- package/dist/esm/src/types/discovery.d.ts.map +1 -0
- package/dist/esm/src/types/discovery.js +28 -0
- package/dist/esm/src/types/discovery.js.map +1 -0
- package/dist/esm/src/types/errors.d.ts +21 -0
- package/dist/esm/src/types/errors.d.ts.map +1 -0
- package/dist/esm/src/types/errors.js +47 -0
- package/dist/esm/src/types/errors.js.map +1 -0
- package/dist/esm/src/types/rpc.d.ts +417 -0
- package/dist/esm/src/types/rpc.d.ts.map +1 -0
- package/dist/esm/src/types/rpc.js +607 -0
- package/dist/esm/src/types/rpc.js.map +1 -0
- package/dist/esm/src/user_agent.d.ts +2 -0
- package/dist/esm/src/user_agent.d.ts.map +1 -0
- package/dist/esm/src/user_agent.js +13 -0
- package/dist/esm/src/user_agent.js.map +1 -0
- package/dist/esm/src/utils/buffer.d.ts +5 -0
- package/dist/esm/src/utils/buffer.d.ts.map +1 -0
- package/dist/esm/src/utils/buffer.js +53 -0
- package/dist/esm/src/utils/buffer.js.map +1 -0
- package/dist/esm/src/utils/completable_promise.d.ts +9 -0
- package/dist/esm/src/utils/completable_promise.d.ts.map +1 -0
- package/dist/esm/src/utils/completable_promise.js +30 -0
- package/dist/esm/src/utils/completable_promise.js.map +1 -0
- package/dist/esm/src/utils/rand.d.ts +13 -0
- package/dist/esm/src/utils/rand.d.ts.map +1 -0
- package/dist/esm/src/utils/rand.js +106 -0
- package/dist/esm/src/utils/rand.js.map +1 -0
- package/dist/esm/src/utils/streams.d.ts +4 -0
- package/dist/esm/src/utils/streams.d.ts.map +1 -0
- package/dist/esm/src/utils/streams.js +20 -0
- package/dist/esm/src/utils/streams.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
|
|
3
|
+
*
|
|
4
|
+
* This file is part of the Restate SDK for Node.js/TypeScript,
|
|
5
|
+
* which is released under the MIT license.
|
|
6
|
+
*
|
|
7
|
+
* You can find a copy of the license in file LICENSE in the root
|
|
8
|
+
* directory of this repository or package, or at
|
|
9
|
+
* https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
|
|
10
|
+
*/
|
|
11
|
+
import { ensureError, INTERNAL_ERROR_CODE, TerminalError, TimeoutError, UNKNOWN_ERROR_CODE, } from "./types/errors.js";
|
|
12
|
+
import { defaultSerde, HandlerKind, makeRpcCallProxy, makeRpcSendProxy, } from "./types/rpc.js";
|
|
13
|
+
import { serde } from "@restatedev/restate-sdk-core";
|
|
14
|
+
import { RandImpl } from "./utils/rand.js";
|
|
15
|
+
export class ContextImpl {
|
|
16
|
+
coreVm;
|
|
17
|
+
input;
|
|
18
|
+
console;
|
|
19
|
+
handlerKind;
|
|
20
|
+
invocationEndPromise;
|
|
21
|
+
inputReader;
|
|
22
|
+
outputWriter;
|
|
23
|
+
invocationRequest;
|
|
24
|
+
rand;
|
|
25
|
+
date = {
|
|
26
|
+
now: () => {
|
|
27
|
+
return this.run(() => Date.now());
|
|
28
|
+
},
|
|
29
|
+
toJSON: () => {
|
|
30
|
+
return this.run(() => new Date().toJSON());
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
currentRead;
|
|
34
|
+
constructor(coreVm, input, console, handlerKind, attemptHeaders, extraArgs, invocationEndPromise, inputReader, outputWriter) {
|
|
35
|
+
this.coreVm = coreVm;
|
|
36
|
+
this.input = input;
|
|
37
|
+
this.console = console;
|
|
38
|
+
this.handlerKind = handlerKind;
|
|
39
|
+
this.invocationEndPromise = invocationEndPromise;
|
|
40
|
+
this.inputReader = inputReader;
|
|
41
|
+
this.outputWriter = outputWriter;
|
|
42
|
+
this.invocationRequest = {
|
|
43
|
+
id: input.invocation_id,
|
|
44
|
+
headers: input.headers.reduce((headers, { key, value }) => {
|
|
45
|
+
headers.set(key, value);
|
|
46
|
+
return headers;
|
|
47
|
+
}, new Map()),
|
|
48
|
+
attemptHeaders: Object.entries(attemptHeaders).reduce((headers, [key, value]) => {
|
|
49
|
+
if (value != undefined) {
|
|
50
|
+
headers.set(key, value instanceof Array ? value[0] : value);
|
|
51
|
+
}
|
|
52
|
+
return headers;
|
|
53
|
+
}, new Map()),
|
|
54
|
+
body: input.input,
|
|
55
|
+
extraArgs,
|
|
56
|
+
};
|
|
57
|
+
this.rand = new RandImpl(input.invocation_id, () => {
|
|
58
|
+
if (coreVm.is_inside_run()) {
|
|
59
|
+
throw new Error("Cannot generate random numbers within a run closure. Use the random object outside the run closure.");
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
get key() {
|
|
64
|
+
switch (this.handlerKind) {
|
|
65
|
+
case HandlerKind.EXCLUSIVE:
|
|
66
|
+
case HandlerKind.SHARED:
|
|
67
|
+
case HandlerKind.WORKFLOW: {
|
|
68
|
+
return this.input.key;
|
|
69
|
+
}
|
|
70
|
+
default:
|
|
71
|
+
throw new TerminalError("this handler type doesn't support key()");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
request() {
|
|
75
|
+
return this.invocationRequest;
|
|
76
|
+
}
|
|
77
|
+
get(name, serde) {
|
|
78
|
+
return this.processCompletableEntry((vm) => vm.sys_get_state(name), (asyncResultValue) => {
|
|
79
|
+
if (asyncResultValue == "Empty") {
|
|
80
|
+
// Empty
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
else if ("Success" in asyncResultValue) {
|
|
84
|
+
return (serde ?? defaultSerde()).deserialize(asyncResultValue.Success);
|
|
85
|
+
}
|
|
86
|
+
else if ("Failure" in asyncResultValue) {
|
|
87
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
88
|
+
errorCode: asyncResultValue.Failure.code,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
stateKeys() {
|
|
95
|
+
return this.processCompletableEntry((vm) => vm.sys_get_state_keys(), (asyncResultValue) => {
|
|
96
|
+
if (typeof asyncResultValue === "object" &&
|
|
97
|
+
"StateKeys" in asyncResultValue) {
|
|
98
|
+
return asyncResultValue.StateKeys;
|
|
99
|
+
}
|
|
100
|
+
else if (typeof asyncResultValue === "object" &&
|
|
101
|
+
"Failure" in asyncResultValue) {
|
|
102
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
103
|
+
errorCode: asyncResultValue.Failure.code,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
set(name, value, serde) {
|
|
110
|
+
this.processNonCompletableEntry((vm) => vm.sys_set_state(name, (serde ?? defaultSerde()).serialize(value)));
|
|
111
|
+
}
|
|
112
|
+
clear(name) {
|
|
113
|
+
this.processNonCompletableEntry((vm) => vm.sys_clear_state(name));
|
|
114
|
+
}
|
|
115
|
+
clearAll() {
|
|
116
|
+
this.processNonCompletableEntry((vm) => vm.sys_clear_all_state());
|
|
117
|
+
}
|
|
118
|
+
// --- Calls, background calls, etc
|
|
119
|
+
//
|
|
120
|
+
genericCall(call) {
|
|
121
|
+
const requestSerde = call.inputSerde ?? serde.binary;
|
|
122
|
+
const responseSerde = call.outputSerde ?? serde.binary;
|
|
123
|
+
return this.processCompletableEntry((vm) => {
|
|
124
|
+
const parameter = requestSerde.serialize(call.parameter);
|
|
125
|
+
return vm.sys_call(call.service, call.method, parameter, call.key);
|
|
126
|
+
}, (asyncResultValue) => {
|
|
127
|
+
if (typeof asyncResultValue === "object" &&
|
|
128
|
+
"Success" in asyncResultValue) {
|
|
129
|
+
return responseSerde.deserialize(asyncResultValue.Success);
|
|
130
|
+
}
|
|
131
|
+
else if (typeof asyncResultValue === "object" &&
|
|
132
|
+
"Failure" in asyncResultValue) {
|
|
133
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
134
|
+
errorCode: asyncResultValue.Failure.code,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
genericSend(send) {
|
|
141
|
+
this.processNonCompletableEntry((vm) => {
|
|
142
|
+
const requestSerde = send.inputSerde ?? serde.binary;
|
|
143
|
+
const parameter = requestSerde.serialize(send.parameter);
|
|
144
|
+
let delay;
|
|
145
|
+
if (send.delay != undefined) {
|
|
146
|
+
delay = BigInt(send.delay);
|
|
147
|
+
}
|
|
148
|
+
vm.sys_send(send.service, send.method, parameter, send.key, delay);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
serviceClient({ name }) {
|
|
152
|
+
return makeRpcCallProxy((call) => this.genericCall(call), name);
|
|
153
|
+
}
|
|
154
|
+
objectClient({ name }, key) {
|
|
155
|
+
return makeRpcCallProxy((call) => this.genericCall(call), name, key);
|
|
156
|
+
}
|
|
157
|
+
workflowClient({ name }, key) {
|
|
158
|
+
return makeRpcCallProxy((call) => this.genericCall(call), name, key);
|
|
159
|
+
}
|
|
160
|
+
serviceSendClient({ name }, opts) {
|
|
161
|
+
return makeRpcSendProxy((send) => this.genericSend(send), name, undefined, opts?.delay);
|
|
162
|
+
}
|
|
163
|
+
objectSendClient({ name }, key, opts) {
|
|
164
|
+
return makeRpcSendProxy((send) => this.genericSend(send), name, key, opts?.delay);
|
|
165
|
+
}
|
|
166
|
+
workflowSendClient({ name }, key, opts) {
|
|
167
|
+
return makeRpcSendProxy((send) => this.genericSend(send), name, key, opts?.delay);
|
|
168
|
+
}
|
|
169
|
+
// DON'T make this function async!!!
|
|
170
|
+
// The reason is that we want the errors thrown by the initial checks to be propagated in the caller context,
|
|
171
|
+
// and not in the promise context. To understand the semantic difference, make this function async and run the
|
|
172
|
+
// UnawaitedSideEffectShouldFailSubsequentContextCall test.
|
|
173
|
+
run(nameOrAction, actionSecondParameter, options) {
|
|
174
|
+
const { name, action } = unpack(nameOrAction, actionSecondParameter);
|
|
175
|
+
const serde = options?.serde ?? defaultSerde();
|
|
176
|
+
try {
|
|
177
|
+
const runEnterResult = this.coreVm.sys_run_enter(name || "");
|
|
178
|
+
// Check if the run was already executed
|
|
179
|
+
if (typeof runEnterResult === "object" &&
|
|
180
|
+
"ExecutedWithSuccess" in runEnterResult) {
|
|
181
|
+
return Promise.resolve(serde.deserialize(runEnterResult.ExecutedWithSuccess));
|
|
182
|
+
}
|
|
183
|
+
else if (typeof runEnterResult === "object" &&
|
|
184
|
+
"ExecutedWithFailure" in runEnterResult) {
|
|
185
|
+
return Promise.reject(new TerminalError(runEnterResult.ExecutedWithFailure.message, {
|
|
186
|
+
errorCode: runEnterResult.ExecutedWithFailure.code,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
this.handleInvocationEndError(e);
|
|
192
|
+
return pendingPromise();
|
|
193
|
+
}
|
|
194
|
+
// We wrap the rest of the execution in this closure to create a future
|
|
195
|
+
const doRun = async () => {
|
|
196
|
+
let res;
|
|
197
|
+
let err;
|
|
198
|
+
try {
|
|
199
|
+
res = await action();
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
err = ensureError(e);
|
|
203
|
+
}
|
|
204
|
+
// Record the result/failure, get back the handle for the ack.
|
|
205
|
+
let handle;
|
|
206
|
+
try {
|
|
207
|
+
if (err != undefined) {
|
|
208
|
+
if (err instanceof TerminalError) {
|
|
209
|
+
// Record failure, go ahead
|
|
210
|
+
handle = this.coreVm.sys_run_exit_failure({
|
|
211
|
+
code: err.code,
|
|
212
|
+
message: err.message,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// TODO plug retries here!
|
|
217
|
+
throw err;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
222
|
+
// @ts-expect-error
|
|
223
|
+
handle = this.coreVm.sys_run_exit_success(serde.serialize(res));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
this.handleInvocationEndError(e);
|
|
228
|
+
return pendingPromise();
|
|
229
|
+
}
|
|
230
|
+
// Got the handle, wait for the result now (which we get once we get the ack)
|
|
231
|
+
return await this.pollAsyncResult(handle, (asyncResultValue) => {
|
|
232
|
+
if (typeof asyncResultValue === "object" &&
|
|
233
|
+
"Success" in asyncResultValue) {
|
|
234
|
+
return serde.deserialize(asyncResultValue.Success);
|
|
235
|
+
}
|
|
236
|
+
else if (typeof asyncResultValue === "object" &&
|
|
237
|
+
"Failure" in asyncResultValue) {
|
|
238
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
239
|
+
errorCode: asyncResultValue.Failure.code,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
return doRun();
|
|
246
|
+
}
|
|
247
|
+
sleep(millis) {
|
|
248
|
+
return this.processCompletableEntry((vm) => vm.sys_sleep(BigInt(millis)), (asyncResultValue) => {
|
|
249
|
+
if (asyncResultValue == "Empty") {
|
|
250
|
+
// Empty
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
else if ("Failure" in asyncResultValue) {
|
|
254
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
255
|
+
errorCode: asyncResultValue.Failure.code,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// -- Awakeables
|
|
262
|
+
awakeable(serde) {
|
|
263
|
+
let awakeable;
|
|
264
|
+
try {
|
|
265
|
+
awakeable = this.coreVm.sys_awakeable();
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
this.handleInvocationEndError(e);
|
|
269
|
+
return {
|
|
270
|
+
id: "invalid",
|
|
271
|
+
promise: new LazyContextPromise(0, this, () => pendingPromise()),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
id: awakeable.id,
|
|
276
|
+
promise: new LazyContextPromise(awakeable.handle, this, () => this.pollAsyncResult(awakeable.handle, (asyncResultValue) => {
|
|
277
|
+
if (typeof asyncResultValue === "object" &&
|
|
278
|
+
"Success" in asyncResultValue) {
|
|
279
|
+
if (!serde) {
|
|
280
|
+
return defaultSerde().deserialize(asyncResultValue.Success);
|
|
281
|
+
}
|
|
282
|
+
if (asyncResultValue.Success.length == 0) {
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
return serde.deserialize(asyncResultValue.Success);
|
|
286
|
+
}
|
|
287
|
+
else if (typeof asyncResultValue === "object" &&
|
|
288
|
+
"Failure" in asyncResultValue) {
|
|
289
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
290
|
+
errorCode: asyncResultValue.Failure.code,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
294
|
+
})),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
resolveAwakeable(id, payload, serde) {
|
|
298
|
+
this.processNonCompletableEntry((vm) => {
|
|
299
|
+
// We coerce undefined to null as null can be stringified by JSON.stringify
|
|
300
|
+
let value;
|
|
301
|
+
if (serde) {
|
|
302
|
+
value =
|
|
303
|
+
payload == undefined ? new Uint8Array() : serde.serialize(payload);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
value =
|
|
307
|
+
payload != undefined
|
|
308
|
+
? defaultSerde().serialize(payload)
|
|
309
|
+
: defaultSerde().serialize(null);
|
|
310
|
+
}
|
|
311
|
+
vm.sys_complete_awakeable_success(id, value);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
rejectAwakeable(id, reason) {
|
|
315
|
+
this.processNonCompletableEntry((vm) => {
|
|
316
|
+
vm.sys_complete_awakeable_failure(id, {
|
|
317
|
+
code: UNKNOWN_ERROR_CODE,
|
|
318
|
+
message: reason,
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
promise(name, serde) {
|
|
323
|
+
return new DurablePromiseImpl(this, name, serde);
|
|
324
|
+
}
|
|
325
|
+
// Used by static methods of CombineablePromise
|
|
326
|
+
static createCombinator(combinatorType, promises) {
|
|
327
|
+
// Extract context from first promise
|
|
328
|
+
const self = extractContext(promises[0]);
|
|
329
|
+
if (!self) {
|
|
330
|
+
throw new Error("Not a combinable promise");
|
|
331
|
+
}
|
|
332
|
+
// Collect first the promises downcasted to the internal promise type
|
|
333
|
+
const castedPromises = [];
|
|
334
|
+
for (const promise of promises) {
|
|
335
|
+
if (extractContext(promise) !== self) {
|
|
336
|
+
self.handleInvocationEndError(new Error("You're mixing up CombineablePromises from different RestateContext. This is not supported."));
|
|
337
|
+
return pendingPromise();
|
|
338
|
+
}
|
|
339
|
+
castedPromises.push(promise);
|
|
340
|
+
}
|
|
341
|
+
const handles = new Uint32Array(castedPromises.map((p) => p.asyncResultHandle));
|
|
342
|
+
// From now on, lazily executes on await
|
|
343
|
+
return new LazyPromise(async () => {
|
|
344
|
+
let combinatorResultHandle;
|
|
345
|
+
try {
|
|
346
|
+
// Take output
|
|
347
|
+
const nextOutput1 = self.coreVm.take_output();
|
|
348
|
+
if (nextOutput1 instanceof Uint8Array) {
|
|
349
|
+
await self.outputWriter.write(nextOutput1);
|
|
350
|
+
}
|
|
351
|
+
for (;;) {
|
|
352
|
+
switch (combinatorType) {
|
|
353
|
+
case "All":
|
|
354
|
+
combinatorResultHandle =
|
|
355
|
+
self.coreVm.sys_try_complete_all_combinator(handles);
|
|
356
|
+
break;
|
|
357
|
+
case "Any":
|
|
358
|
+
combinatorResultHandle =
|
|
359
|
+
self.coreVm.sys_try_complete_any_combinator(handles);
|
|
360
|
+
break;
|
|
361
|
+
case "AllSettled":
|
|
362
|
+
combinatorResultHandle =
|
|
363
|
+
self.coreVm.sys_try_complete_all_settled_combinator(handles);
|
|
364
|
+
break;
|
|
365
|
+
case "Race":
|
|
366
|
+
case "OrTimeout":
|
|
367
|
+
combinatorResultHandle =
|
|
368
|
+
self.coreVm.sys_try_complete_race_combinator(handles);
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
// We got a result, we're done in this loop
|
|
372
|
+
if (combinatorResultHandle !== undefined) {
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
// No result yet, await the next read
|
|
376
|
+
await self.awaitNextRead();
|
|
377
|
+
}
|
|
378
|
+
// We got a result, we need to take_output to write the combinator entry, then we need to poll the result
|
|
379
|
+
const nextOutput = self.coreVm.take_output();
|
|
380
|
+
if (nextOutput instanceof Uint8Array) {
|
|
381
|
+
await self.outputWriter.write(nextOutput);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
catch (e) {
|
|
385
|
+
if (e instanceof TerminalError) {
|
|
386
|
+
// All good, this is a recorded failure
|
|
387
|
+
throw e;
|
|
388
|
+
}
|
|
389
|
+
// Not good, this is a retryable error.
|
|
390
|
+
self.handleInvocationEndError(e);
|
|
391
|
+
return await pendingPromise();
|
|
392
|
+
}
|
|
393
|
+
const handlesResult = await self.pollAsyncResult(combinatorResultHandle, (asyncResultValue) => {
|
|
394
|
+
if (typeof asyncResultValue === "object" &&
|
|
395
|
+
"CombinatorResult" in asyncResultValue) {
|
|
396
|
+
return asyncResultValue.CombinatorResult;
|
|
397
|
+
}
|
|
398
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
399
|
+
});
|
|
400
|
+
const promisesMap = new Map(castedPromises.map((p) => [p.asyncResultHandle, p]));
|
|
401
|
+
// Now all we need to do is to construct the final output based on the handles,
|
|
402
|
+
// this depends on combinators themselves.
|
|
403
|
+
switch (combinatorType) {
|
|
404
|
+
case "All":
|
|
405
|
+
return this.extractAllCombinatorResult(handlesResult, promisesMap);
|
|
406
|
+
case "Any":
|
|
407
|
+
return this.extractAnyCombinatorResult(handlesResult, promisesMap);
|
|
408
|
+
case "AllSettled":
|
|
409
|
+
return this.extractAllSettledCombinatorResult(handlesResult, promisesMap);
|
|
410
|
+
case "Race":
|
|
411
|
+
// Just one promise succeeded
|
|
412
|
+
return promisesMap.get(handlesResult[0]);
|
|
413
|
+
case "OrTimeout":
|
|
414
|
+
// The sleep promise is always the second one in the list.
|
|
415
|
+
if (handlesResult[0] == castedPromises[1].asyncResultHandle) {
|
|
416
|
+
return Promise.reject(new TimeoutError());
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
return promisesMap.get(handlesResult[0]);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
static async extractAllCombinatorResult(handlesResult, promisesMap) {
|
|
425
|
+
// The result can either all values, or one error
|
|
426
|
+
const resultValues = [];
|
|
427
|
+
for (const handle of handlesResult) {
|
|
428
|
+
try {
|
|
429
|
+
resultValues.push(await promisesMap.get(handle));
|
|
430
|
+
}
|
|
431
|
+
catch (e) {
|
|
432
|
+
return Promise.reject(e);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return Promise.resolve(resultValues);
|
|
436
|
+
}
|
|
437
|
+
static async extractAnyCombinatorResult(handlesResult, promisesMap) {
|
|
438
|
+
// The result can either be one value, or a list of errors
|
|
439
|
+
const resultFailures = [];
|
|
440
|
+
for (const handle of handlesResult) {
|
|
441
|
+
try {
|
|
442
|
+
return Promise.resolve(await promisesMap.get(handle));
|
|
443
|
+
}
|
|
444
|
+
catch (e) {
|
|
445
|
+
resultFailures.push(e);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// Giving back the cause here is completely fine, because all these errors in Aggregate error are Terminal errors!
|
|
449
|
+
return Promise.reject(new TerminalError("All input promises failed", {
|
|
450
|
+
cause: new AggregateError(resultFailures),
|
|
451
|
+
}));
|
|
452
|
+
}
|
|
453
|
+
static async extractAllSettledCombinatorResult(handlesResult, promisesMap) {
|
|
454
|
+
const resultValues = [];
|
|
455
|
+
for (const handle of handlesResult) {
|
|
456
|
+
try {
|
|
457
|
+
resultValues.push(await promisesMap.get(handle));
|
|
458
|
+
}
|
|
459
|
+
catch (e) {
|
|
460
|
+
resultValues.push(e);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return Promise.resolve(resultValues);
|
|
464
|
+
}
|
|
465
|
+
// -- Various private methods
|
|
466
|
+
processNonCompletableEntry(vmCall) {
|
|
467
|
+
try {
|
|
468
|
+
vmCall(this.coreVm);
|
|
469
|
+
}
|
|
470
|
+
catch (e) {
|
|
471
|
+
this.handleInvocationEndError(e);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
processCompletableEntry(vmCall, transformer) {
|
|
475
|
+
let handle;
|
|
476
|
+
try {
|
|
477
|
+
handle = vmCall(this.coreVm);
|
|
478
|
+
}
|
|
479
|
+
catch (e) {
|
|
480
|
+
this.handleInvocationEndError(e);
|
|
481
|
+
return new LazyContextPromise(0, this, () => pendingPromise());
|
|
482
|
+
}
|
|
483
|
+
return new LazyContextPromise(handle, this, () => this.pollAsyncResult(handle, transformer));
|
|
484
|
+
}
|
|
485
|
+
async pollAsyncResult(handle, transformer) {
|
|
486
|
+
try {
|
|
487
|
+
// Take output
|
|
488
|
+
const nextOutput = this.coreVm.take_output();
|
|
489
|
+
if (nextOutput instanceof Uint8Array) {
|
|
490
|
+
await this.outputWriter.write(nextOutput);
|
|
491
|
+
}
|
|
492
|
+
// Now loop waiting for the async result
|
|
493
|
+
let asyncResult = this.coreVm.take_async_result(handle);
|
|
494
|
+
while (asyncResult == "NotReady") {
|
|
495
|
+
await this.awaitNextRead();
|
|
496
|
+
// Using notify_await_point immediately before take_async_result
|
|
497
|
+
// makes sure the state machine will try to suspend only now,
|
|
498
|
+
// in case there aren't other concurrent tasks trying to poll this async result.
|
|
499
|
+
this.coreVm.notify_await_point(handle);
|
|
500
|
+
asyncResult = this.coreVm.take_async_result(handle);
|
|
501
|
+
}
|
|
502
|
+
return transformer(asyncResult);
|
|
503
|
+
}
|
|
504
|
+
catch (e) {
|
|
505
|
+
if (e instanceof TerminalError) {
|
|
506
|
+
// All good, this is a recorded failure
|
|
507
|
+
throw e;
|
|
508
|
+
}
|
|
509
|
+
// Not good, this is a retryable error.
|
|
510
|
+
this.handleInvocationEndError(e);
|
|
511
|
+
return await pendingPromise();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
// This function triggers a read on the input reader,
|
|
515
|
+
// and will notify the caller that a read was executed
|
|
516
|
+
// and the result was piped in the state machine.
|
|
517
|
+
awaitNextRead() {
|
|
518
|
+
if (this.currentRead == undefined) {
|
|
519
|
+
// Register a new read
|
|
520
|
+
this.currentRead = this.readNext().finally(() => {
|
|
521
|
+
this.currentRead = undefined;
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
525
|
+
return new Promise((resolve) => this.currentRead?.finally(resolve));
|
|
526
|
+
}
|
|
527
|
+
async readNext() {
|
|
528
|
+
// Take input, and notify it to the vm
|
|
529
|
+
let nextValue;
|
|
530
|
+
try {
|
|
531
|
+
nextValue = await this.inputReader.read();
|
|
532
|
+
}
|
|
533
|
+
catch (e) {
|
|
534
|
+
this.handleInvocationEndError(e);
|
|
535
|
+
return pendingPromise();
|
|
536
|
+
}
|
|
537
|
+
if (nextValue.value !== undefined) {
|
|
538
|
+
this.coreVm.notify_input(nextValue.value);
|
|
539
|
+
}
|
|
540
|
+
if (nextValue.done) {
|
|
541
|
+
this.coreVm.notify_input_closed();
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
handleInvocationEndError(e) {
|
|
545
|
+
const err = ensureError(e);
|
|
546
|
+
this.coreVm.notify_error(err.message, err.stack);
|
|
547
|
+
// From now on, no progress will be made.
|
|
548
|
+
this.invocationEndPromise.resolve();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function unpack(a, b) {
|
|
552
|
+
if (typeof a == "string") {
|
|
553
|
+
if (typeof b !== "function") {
|
|
554
|
+
throw new TypeError("");
|
|
555
|
+
}
|
|
556
|
+
return { name: a, action: b };
|
|
557
|
+
}
|
|
558
|
+
if (typeof a !== "function") {
|
|
559
|
+
throw new TypeError("unexpected type at the first parameter");
|
|
560
|
+
}
|
|
561
|
+
if (b) {
|
|
562
|
+
throw new TypeError("unexpected a function as a second parameter.");
|
|
563
|
+
}
|
|
564
|
+
return { action: a };
|
|
565
|
+
}
|
|
566
|
+
const RESTATE_CTX_SYMBOL = Symbol("restateContext");
|
|
567
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
568
|
+
function extractContext(n) {
|
|
569
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
570
|
+
return n[RESTATE_CTX_SYMBOL];
|
|
571
|
+
}
|
|
572
|
+
class DurablePromiseImpl {
|
|
573
|
+
ctx;
|
|
574
|
+
name;
|
|
575
|
+
serde;
|
|
576
|
+
constructor(ctx, name, serde) {
|
|
577
|
+
this.ctx = ctx;
|
|
578
|
+
this.name = name;
|
|
579
|
+
this.serde = serde ?? defaultSerde();
|
|
580
|
+
}
|
|
581
|
+
then(onfulfilled, onrejected) {
|
|
582
|
+
return this.get().then(onfulfilled, onrejected);
|
|
583
|
+
}
|
|
584
|
+
catch(onrejected) {
|
|
585
|
+
return this.get().catch(onrejected);
|
|
586
|
+
}
|
|
587
|
+
finally(onfinally) {
|
|
588
|
+
return this.get().finally(onfinally);
|
|
589
|
+
}
|
|
590
|
+
[Symbol.toStringTag] = "DurablePromise";
|
|
591
|
+
get() {
|
|
592
|
+
return this.ctx.processCompletableEntry((vm) => vm.sys_get_promise(this.name), (asyncResultValue) => {
|
|
593
|
+
if (typeof asyncResultValue === "object" &&
|
|
594
|
+
"Success" in asyncResultValue) {
|
|
595
|
+
return this.serde.deserialize(asyncResultValue.Success);
|
|
596
|
+
}
|
|
597
|
+
else if (typeof asyncResultValue === "object" &&
|
|
598
|
+
"Failure" in asyncResultValue) {
|
|
599
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
600
|
+
errorCode: asyncResultValue.Failure.code,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
peek() {
|
|
607
|
+
return this.ctx.processCompletableEntry((vm) => vm.sys_peek_promise(this.name), (asyncResultValue) => {
|
|
608
|
+
if (asyncResultValue === "Empty") {
|
|
609
|
+
return undefined;
|
|
610
|
+
}
|
|
611
|
+
else if (typeof asyncResultValue === "object" &&
|
|
612
|
+
"Success" in asyncResultValue) {
|
|
613
|
+
return this.serde.deserialize(asyncResultValue.Success);
|
|
614
|
+
}
|
|
615
|
+
else if (typeof asyncResultValue === "object" &&
|
|
616
|
+
"Failure" in asyncResultValue) {
|
|
617
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
618
|
+
errorCode: asyncResultValue.Failure.code,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
resolve(value) {
|
|
625
|
+
return this.ctx.processCompletableEntry((vm) => vm.sys_complete_promise_success(this.name, this.serde.serialize(value)), (asyncResultValue) => {
|
|
626
|
+
if (asyncResultValue === "Empty") {
|
|
627
|
+
return undefined;
|
|
628
|
+
}
|
|
629
|
+
else if (typeof asyncResultValue === "object" &&
|
|
630
|
+
"Failure" in asyncResultValue) {
|
|
631
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
632
|
+
errorCode: asyncResultValue.Failure.code,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
reject(errorMsg) {
|
|
639
|
+
return this.ctx.processCompletableEntry((vm) => vm.sys_complete_promise_failure(this.name, {
|
|
640
|
+
code: INTERNAL_ERROR_CODE,
|
|
641
|
+
message: errorMsg,
|
|
642
|
+
}), (asyncResultValue) => {
|
|
643
|
+
if (asyncResultValue === "Empty") {
|
|
644
|
+
return undefined;
|
|
645
|
+
}
|
|
646
|
+
else if (typeof asyncResultValue === "object" &&
|
|
647
|
+
"Failure" in asyncResultValue) {
|
|
648
|
+
throw new TerminalError(asyncResultValue.Failure.message, {
|
|
649
|
+
errorCode: asyncResultValue.Failure.code,
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
throw new Error(`Unexpected variant in async result: ${JSON.stringify(asyncResultValue)}`);
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
class LazyPromise {
|
|
657
|
+
executor;
|
|
658
|
+
_promise;
|
|
659
|
+
constructor(executor) {
|
|
660
|
+
this.executor = executor;
|
|
661
|
+
}
|
|
662
|
+
then(onfulfilled, onrejected) {
|
|
663
|
+
this._promise = this._promise || this.executor();
|
|
664
|
+
return this._promise.then(onfulfilled, onrejected);
|
|
665
|
+
}
|
|
666
|
+
catch(onrejected) {
|
|
667
|
+
this._promise = this._promise || this.executor();
|
|
668
|
+
return this._promise.catch(onrejected);
|
|
669
|
+
}
|
|
670
|
+
finally(onfinally) {
|
|
671
|
+
this._promise = this._promise || this.executor();
|
|
672
|
+
return this._promise.finally(onfinally);
|
|
673
|
+
}
|
|
674
|
+
[Symbol.toStringTag] = "LazyPromise";
|
|
675
|
+
}
|
|
676
|
+
class LazyContextPromise extends LazyPromise {
|
|
677
|
+
asyncResultHandle;
|
|
678
|
+
[RESTATE_CTX_SYMBOL];
|
|
679
|
+
constructor(asyncResultHandle, ctx, executor) {
|
|
680
|
+
super(executor);
|
|
681
|
+
this.asyncResultHandle = asyncResultHandle;
|
|
682
|
+
this[RESTATE_CTX_SYMBOL] = ctx;
|
|
683
|
+
}
|
|
684
|
+
orTimeout(millis) {
|
|
685
|
+
return ContextImpl.createCombinator("OrTimeout", [
|
|
686
|
+
this,
|
|
687
|
+
this[RESTATE_CTX_SYMBOL].sleep(millis),
|
|
688
|
+
]);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// A promise that is never completed
|
|
692
|
+
function pendingPromise() {
|
|
693
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
694
|
+
return new Promise(() => { });
|
|
695
|
+
}
|
|
696
|
+
//# sourceMappingURL=context_impl.js.map
|