@restatedev/restate-sdk-cloudflare-workers 1.10.4 → 1.11.1
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/context_impl.cjs +17 -6
- package/dist/context_impl.js +17 -6
- package/dist/context_impl.js.map +1 -1
- package/dist/endpoint/endpoint.cjs +2 -2
- package/dist/endpoint/endpoint.js +2 -2
- package/dist/endpoint/fetch_endpoint.cjs +2 -2
- package/dist/endpoint/fetch_endpoint.js +2 -2
- package/dist/endpoint/fetch_endpoint.js.map +1 -1
- package/dist/endpoint/handlers/core_logging.cjs +52 -0
- package/dist/endpoint/handlers/core_logging.js +51 -0
- package/dist/endpoint/handlers/core_logging.js.map +1 -0
- package/dist/endpoint/handlers/discovery.cjs +58 -0
- package/dist/endpoint/handlers/discovery.js +59 -0
- package/dist/endpoint/handlers/discovery.js.map +1 -0
- package/dist/endpoint/handlers/fetch.cjs +23 -11
- package/dist/endpoint/handlers/fetch.js +24 -11
- package/dist/endpoint/handlers/fetch.js.map +1 -1
- package/dist/endpoint/handlers/generic.cjs +167 -253
- package/dist/endpoint/handlers/generic.js +166 -249
- package/dist/endpoint/handlers/generic.js.map +1 -1
- package/dist/endpoint/handlers/lambda.cjs +64 -61
- package/dist/endpoint/handlers/lambda.js +64 -60
- package/dist/endpoint/handlers/lambda.js.map +1 -1
- package/dist/endpoint/handlers/utils.cjs +51 -0
- package/dist/endpoint/handlers/utils.js +48 -0
- package/dist/endpoint/handlers/utils.js.map +1 -0
- package/dist/endpoint/handlers/vm/sdk_shared_core_wasm_bindings.d.ts +13 -13
- package/dist/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.js +20 -20
- package/dist/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.wasm +0 -0
- package/dist/endpoint/lambda_endpoint.cjs +2 -2
- package/dist/endpoint/lambda_endpoint.js +2 -2
- package/dist/endpoint/lambda_endpoint.js.map +1 -1
- package/dist/endpoint/node_endpoint.cjs +41 -41
- package/dist/endpoint/node_endpoint.js +41 -40
- package/dist/endpoint/node_endpoint.js.map +1 -1
- package/dist/io.cjs +2 -2
- package/dist/io.js +2 -2
- package/dist/io.js.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/dist/types/errors.cjs +2 -0
- package/dist/types/errors.d.cts +8 -0
- package/dist/types/errors.d.cts.map +1 -1
- package/dist/types/errors.d.ts +8 -0
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/errors.js +2 -0
- package/dist/types/errors.js.map +1 -1
- package/package.json +3 -3
- package/dist/utils/streams.cjs +0 -14
- package/dist/utils/streams.js +0 -13
- package/dist/utils/streams.js.map +0 -1
|
@@ -1,46 +1,26 @@
|
|
|
1
|
+
import { LogSource, LoggerContext, RestateLogLevel } from "../../logging/logger_transport.js";
|
|
2
|
+
import { DEFAULT_CONSOLE_LOGGER_LOG_LEVEL } from "../../logging/console_logger_transport.js";
|
|
3
|
+
import { destroyLogger, registerLogger } from "./core_logging.js";
|
|
4
|
+
import { LogLevel, WasmHeader, WasmIdentityVerifier, WasmVM, set_log_level } from "./vm/sdk_shared_core_wasm_bindings.js";
|
|
1
5
|
import { RestateError, RetryableError, TerminalError, ensureError, logError } from "../../types/errors.js";
|
|
2
6
|
import { HandlerKind } from "../../types/rpc.js";
|
|
3
|
-
import { parseUrlComponents } from "../components.js";
|
|
4
|
-
import { X_RESTATE_SERVER } from "../../user_agent.js";
|
|
5
|
-
import { OnceStream } from "../../utils/streams.js";
|
|
6
7
|
import { CompletablePromise } from "../../utils/completable_promise.js";
|
|
7
|
-
import { LogSource, LoggerContext, RestateLogLevel } from "../../logging/logger_transport.js";
|
|
8
|
-
import { createLogger } from "../../logging/logger.js";
|
|
9
|
-
import { DEFAULT_CONSOLE_LOGGER_LOG_LEVEL, defaultLoggerTransport } from "../../logging/console_logger_transport.js";
|
|
10
|
-
import { LogLevel, WasmHeader, WasmIdentityVerifier, WasmVM, set_log_level } from "./vm/sdk_shared_core_wasm_bindings.js";
|
|
11
8
|
import { ContextImpl } from "../../context_impl.js";
|
|
9
|
+
import { parseUrlComponents } from "../components.js";
|
|
10
|
+
import { createLogger } from "../../logging/logger.js";
|
|
11
|
+
import { X_RESTATE_SERVER } from "../../user_agent.js";
|
|
12
|
+
import { errorResponse, invocationIdFromHeaders, simpleResponse, tryCreateContextualLogger } from "./utils.js";
|
|
13
|
+
import { handleDiscovery } from "./discovery.js";
|
|
12
14
|
import { millisOrDurationToMillis } from "@restatedev/restate-sdk-core";
|
|
13
|
-
import { TransformStream } from "node:stream/web";
|
|
14
15
|
|
|
15
16
|
//#region src/endpoint/handlers/generic.ts
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const ENDPOINT_MANIFEST_V4 = "application/vnd.restate.endpointmanifest.v4+json";
|
|
19
|
-
function tryCreateContextualLogger(loggerTransport, url, headers, additionalContext) {
|
|
20
|
-
try {
|
|
21
|
-
const path = new URL(url, "https://example.com").pathname;
|
|
22
|
-
const parsed = parseUrlComponents(path);
|
|
23
|
-
if (parsed.type !== "invoke") return;
|
|
24
|
-
const invocationId = invocationIdFromHeaders(headers);
|
|
25
|
-
return createLogger(loggerTransport, LogSource.SYSTEM, new LoggerContext(invocationId, parsed.componentName, parsed.handlerName, void 0, void 0, additionalContext));
|
|
26
|
-
} catch {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function invocationIdFromHeaders(headers) {
|
|
31
|
-
const invocationIdHeader = headers["x-restate-invocation-id"];
|
|
32
|
-
return typeof invocationIdHeader === "string" ? invocationIdHeader : Array.isArray(invocationIdHeader) ? invocationIdHeader[0] ?? "unknown id" : "unknown id";
|
|
17
|
+
function createRestateHandler(endpoint, protocolMode, additionalDiscoveryFields) {
|
|
18
|
+
return new RestateHandlerImpl(endpoint, protocolMode, additionalDiscoveryFields);
|
|
33
19
|
}
|
|
34
20
|
/**
|
|
35
|
-
* This is
|
|
36
|
-
* It supports both request-reply mode and bidirectional streaming mode.
|
|
37
|
-
*
|
|
38
|
-
* An individual handler will have to convert the shape of the incoming request
|
|
39
|
-
* to a RestateRequest, and then pass it to this handler, and eventually convert back
|
|
40
|
-
* the response.
|
|
41
|
-
* Different runtimes have slightly different shapes of the incoming request, and responses.
|
|
21
|
+
* This is the RestateHandler implementation
|
|
42
22
|
*/
|
|
43
|
-
var
|
|
23
|
+
var RestateHandlerImpl = class {
|
|
44
24
|
identityVerifier;
|
|
45
25
|
constructor(endpoint, protocolMode, additionalDiscoveryFields) {
|
|
46
26
|
this.endpoint = endpoint;
|
|
@@ -53,57 +33,31 @@ var GenericHandler = class {
|
|
|
53
33
|
}
|
|
54
34
|
set_log_level(restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL));
|
|
55
35
|
}
|
|
56
|
-
|
|
36
|
+
handle(request, context) {
|
|
57
37
|
try {
|
|
58
|
-
return
|
|
38
|
+
return this._handle(request, context);
|
|
59
39
|
} catch (e) {
|
|
60
40
|
const error = ensureError(e);
|
|
61
41
|
(tryCreateContextualLogger(this.endpoint.loggerTransport, request.url, request.headers) ?? this.endpoint.rlog).error("Error while handling request: " + (error.stack ?? error.message));
|
|
62
|
-
return
|
|
42
|
+
return errorResponse(error instanceof RestateError ? error.code : 500, error.message);
|
|
63
43
|
}
|
|
64
44
|
}
|
|
65
|
-
|
|
45
|
+
_handle(request, context) {
|
|
66
46
|
const path = new URL(request.url, "https://example.com").pathname;
|
|
67
47
|
const parsed = parseUrlComponents(path);
|
|
68
48
|
if (parsed.type === "unknown") {
|
|
69
49
|
const msg = `Invalid path. Allowed are /health, or /discover, or /invoke/SvcName/handlerName, but was: ${path}`;
|
|
70
50
|
this.endpoint.rlog.trace(msg);
|
|
71
|
-
return
|
|
51
|
+
return errorResponse(404, msg);
|
|
72
52
|
}
|
|
73
|
-
if (parsed.type === "health") return {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"x-restate-server": X_RESTATE_SERVER
|
|
78
|
-
},
|
|
79
|
-
statusCode: 200
|
|
80
|
-
};
|
|
53
|
+
if (parsed.type === "health") return simpleResponse(200, {
|
|
54
|
+
"content-type": "application/text",
|
|
55
|
+
"x-restate-server": X_RESTATE_SERVER
|
|
56
|
+
}, new TextEncoder().encode("OK"));
|
|
81
57
|
const error = this.validateConnectionSignature(path, request.headers);
|
|
82
58
|
if (error !== null) return error;
|
|
83
|
-
if (parsed.type === "discover") return this.
|
|
84
|
-
|
|
85
|
-
const errorMessage = "Missing content-type header";
|
|
86
|
-
this.endpoint.rlog.warn(errorMessage);
|
|
87
|
-
return this.toErrorResponse(415, errorMessage);
|
|
88
|
-
}
|
|
89
|
-
const service = this.endpoint.components.get(parsed.componentName);
|
|
90
|
-
if (!service) {
|
|
91
|
-
const msg = `No service found for URL: ${JSON.stringify(parsed)}`;
|
|
92
|
-
this.endpoint.rlog.error(msg);
|
|
93
|
-
return this.toErrorResponse(404, msg);
|
|
94
|
-
}
|
|
95
|
-
const handler = service?.handlerMatching(parsed);
|
|
96
|
-
if (!handler) {
|
|
97
|
-
const msg = `No service found for URL: ${JSON.stringify(parsed)}`;
|
|
98
|
-
this.endpoint.rlog.error(msg);
|
|
99
|
-
return this.toErrorResponse(404, msg);
|
|
100
|
-
}
|
|
101
|
-
if (!request.body) {
|
|
102
|
-
const msg = "The incoming message body was null";
|
|
103
|
-
this.endpoint.rlog.error(msg);
|
|
104
|
-
return this.toErrorResponse(400, msg);
|
|
105
|
-
}
|
|
106
|
-
return this.handleInvoke(service, handler, request.body, request.headers, request.extraArgs, request.abortSignal, context ?? {});
|
|
59
|
+
if (parsed.type === "discover") return handleDiscovery(this.endpoint, this.protocolMode, this.additionalDiscoveryFields, request.headers["accept"]);
|
|
60
|
+
return this.handleInvoke(parsed, request.headers, request.extraArgs, context ?? {});
|
|
107
61
|
}
|
|
108
62
|
validateConnectionSignature(path, headers) {
|
|
109
63
|
if (!this.identityVerifier) return null;
|
|
@@ -113,207 +67,170 @@ var GenericHandler = class {
|
|
|
113
67
|
return null;
|
|
114
68
|
} catch (e) {
|
|
115
69
|
this.endpoint.rlog.error(`Rejecting request as its JWT did not validate: ${e}`);
|
|
116
|
-
return
|
|
70
|
+
return errorResponse(401, "Unauthorized");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
handleInvoke(invokePathComponent, headers, extraArgs, additionalContext) {
|
|
74
|
+
if (typeof headers["content-type"] !== "string") {
|
|
75
|
+
const errorMessage = "Missing content-type header";
|
|
76
|
+
this.endpoint.rlog.warn(errorMessage);
|
|
77
|
+
return errorResponse(415, errorMessage);
|
|
78
|
+
}
|
|
79
|
+
const service = this.endpoint.components.get(invokePathComponent.componentName);
|
|
80
|
+
if (!service) {
|
|
81
|
+
const msg = `No service found for URL: ${JSON.stringify(invokePathComponent)}`;
|
|
82
|
+
this.endpoint.rlog.error(msg);
|
|
83
|
+
return errorResponse(404, msg);
|
|
84
|
+
}
|
|
85
|
+
const handler = service?.handlerMatching(invokePathComponent);
|
|
86
|
+
if (!handler) {
|
|
87
|
+
const msg = `No service found for URL: ${JSON.stringify(invokePathComponent)}`;
|
|
88
|
+
this.endpoint.rlog.error(msg);
|
|
89
|
+
return errorResponse(404, msg);
|
|
117
90
|
}
|
|
91
|
+
return new RestateInvokeResponse(service, handler, headers, extraArgs, additionalContext, this.endpoint.journalValueCodec, this.endpoint.loggerTransport);
|
|
118
92
|
}
|
|
119
|
-
|
|
120
|
-
|
|
93
|
+
};
|
|
94
|
+
var RestateInvokeResponse = class {
|
|
95
|
+
headers;
|
|
96
|
+
statusCode;
|
|
97
|
+
loggerId;
|
|
98
|
+
vmLogger;
|
|
99
|
+
coreVm;
|
|
100
|
+
constructor(service, handler, attemptHeaders, extraArgs, additionalContext, journalValueCodecInit, loggerTransport) {
|
|
101
|
+
this.service = service;
|
|
102
|
+
this.handler = handler;
|
|
103
|
+
this.attemptHeaders = attemptHeaders;
|
|
104
|
+
this.extraArgs = extraArgs;
|
|
105
|
+
this.additionalContext = additionalContext;
|
|
106
|
+
this.journalValueCodecInit = journalValueCodecInit;
|
|
107
|
+
this.loggerTransport = loggerTransport;
|
|
108
|
+
this.loggerId = Math.floor(Math.random() * 4294967295);
|
|
109
|
+
const isJournalCodecDefined = this.journalValueCodecInit !== void 0;
|
|
110
|
+
const vmHeaders = Object.entries(this.attemptHeaders).filter(([, v]) => v !== void 0).map(([k, v]) => new WasmHeader(k, v instanceof Array ? v[0] : v));
|
|
111
|
+
this.coreVm = new WasmVM(vmHeaders, restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL), this.loggerId, isJournalCodecDefined);
|
|
112
|
+
const responseHead = this.coreVm.get_response_head();
|
|
113
|
+
this.statusCode = responseHead.status_code;
|
|
114
|
+
this.headers = responseHead.headers.reduce((headers, { key, value }) => ({
|
|
115
|
+
[key]: value,
|
|
116
|
+
...headers
|
|
117
|
+
}), { "x-restate-server": X_RESTATE_SERVER });
|
|
118
|
+
this.vmLogger = createLogger(this.loggerTransport, LogSource.JOURNAL, new LoggerContext(invocationIdFromHeaders(this.attemptHeaders), this.service.name(), this.handler.name(), void 0, void 0, this.additionalContext));
|
|
119
|
+
}
|
|
120
|
+
async process({ inputReader, outputWriter, abortSignal }) {
|
|
121
|
+
abortSignal.addEventListener("abort", () => {
|
|
122
|
+
destroyLogger(this.loggerId);
|
|
123
|
+
}, { once: true });
|
|
124
|
+
registerLogger(this.loggerId, this.vmLogger);
|
|
125
|
+
const journalValueCodec = this.journalValueCodecInit ? await this.journalValueCodecInit : {
|
|
121
126
|
encode: (entry) => entry,
|
|
122
127
|
decode: (entry) => Promise.resolve(entry)
|
|
123
128
|
};
|
|
124
|
-
const
|
|
129
|
+
const invocationEndPromise = new CompletablePromise();
|
|
130
|
+
let ctx;
|
|
125
131
|
try {
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const responseHead = coreVm.get_response_head();
|
|
129
|
-
const responseHeaders = responseHead.headers.reduce((headers$1, { key, value }) => ({
|
|
130
|
-
[key]: value,
|
|
131
|
-
...headers$1
|
|
132
|
-
}), { "x-restate-server": X_RESTATE_SERVER });
|
|
133
|
-
invocationLoggers.set(loggerId, createLogger(this.endpoint.loggerTransport, LogSource.JOURNAL, new LoggerContext(invocationIdFromHeaders(headers), service.name(), handler.name(), void 0, void 0, additionalContext)));
|
|
134
|
-
const inputReader = body.getReader();
|
|
135
|
-
abortSignal.addEventListener("abort", () => {
|
|
136
|
-
invocationLoggers.delete(loggerId);
|
|
137
|
-
inputReader.cancel();
|
|
138
|
-
}, { once: true });
|
|
139
|
-
while (!coreVm.is_ready_to_execute()) {
|
|
140
|
-
const nextValue = await inputReader.read();
|
|
141
|
-
if (nextValue.value !== void 0) coreVm.notify_input(nextValue.value);
|
|
142
|
-
if (nextValue.done) {
|
|
143
|
-
coreVm.notify_input_closed();
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const input = coreVm.sys_input();
|
|
132
|
+
await bufferJournalReplayInCoreVm(this.coreVm, inputReader);
|
|
133
|
+
const input = this.coreVm.sys_input();
|
|
148
134
|
const invocationRequest = {
|
|
149
135
|
id: input.invocation_id,
|
|
150
|
-
headers: input.headers.reduce((headers
|
|
151
|
-
headers
|
|
152
|
-
return headers
|
|
136
|
+
headers: input.headers.reduce((headers, { key, value }) => {
|
|
137
|
+
headers.set(key, value);
|
|
138
|
+
return headers;
|
|
153
139
|
}, /* @__PURE__ */ new Map()),
|
|
154
|
-
attemptHeaders: Object.entries(
|
|
155
|
-
if (value !== void 0) headers
|
|
156
|
-
return headers
|
|
140
|
+
attemptHeaders: Object.entries(this.attemptHeaders).reduce((headers, [key, value]) => {
|
|
141
|
+
if (value !== void 0) headers.set(key, value instanceof Array ? value[0] : value);
|
|
142
|
+
return headers;
|
|
157
143
|
}, /* @__PURE__ */ new Map()),
|
|
158
144
|
body: input.input,
|
|
159
|
-
extraArgs,
|
|
145
|
+
extraArgs: this.extraArgs,
|
|
160
146
|
attemptCompletedSignal: abortSignal
|
|
161
147
|
};
|
|
162
|
-
const loggerContext = new LoggerContext(input.invocation_id, handler.component().name(), handler.name(), handler.kind() === HandlerKind.SERVICE ? void 0 : input.key, invocationRequest, additionalContext);
|
|
163
|
-
const ctxLogger = createLogger(this.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (!coreVm.is_processing()) vmLogger.info("Replaying invocation.");
|
|
167
|
-
else vmLogger.info("Starting invocation.");
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
coreVm.sys_end();
|
|
175
|
-
vmLogger.info("Invocation completed successfully.");
|
|
176
|
-
}).catch((e) => {
|
|
177
|
-
const error = ensureError(e, service.options?.asTerminalError);
|
|
178
|
-
logError(vmLogger, error);
|
|
179
|
-
if (error instanceof TerminalError) {
|
|
180
|
-
coreVm.sys_write_output_failure({
|
|
181
|
-
code: error.code,
|
|
182
|
-
message: error.message,
|
|
183
|
-
metadata: []
|
|
184
|
-
});
|
|
185
|
-
coreVm.sys_end();
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
throw error;
|
|
189
|
-
}).catch((e) => {
|
|
190
|
-
const error = ensureError(e);
|
|
191
|
-
if (error instanceof RetryableError) coreVm.notify_error_with_delay_override(error.message, error.stack, error.retryAfter !== void 0 ? BigInt(millisOrDurationToMillis(error.retryAfter)) : void 0);
|
|
192
|
-
else coreVm.notify_error(error.message, error.stack);
|
|
193
|
-
}).finally(() => {
|
|
194
|
-
invocationEndPromise.resolve();
|
|
195
|
-
});
|
|
196
|
-
invocationEndPromise.promise.then(async () => {
|
|
197
|
-
let nextOutput = coreVm.take_output();
|
|
198
|
-
while (nextOutput !== null && nextOutput !== void 0) {
|
|
199
|
-
await outputWriter.write(nextOutput);
|
|
200
|
-
nextOutput = coreVm.take_output();
|
|
201
|
-
}
|
|
202
|
-
let inputClosed = false;
|
|
203
|
-
while (!inputClosed) try {
|
|
204
|
-
inputClosed = (await inputReader.read()).done;
|
|
205
|
-
} catch (e) {
|
|
206
|
-
inputClosed = true;
|
|
207
|
-
}
|
|
208
|
-
await outputWriter.close();
|
|
209
|
-
}).finally(() => {
|
|
210
|
-
invocationLoggers.delete(loggerId);
|
|
211
|
-
}).catch(() => {});
|
|
212
|
-
return {
|
|
213
|
-
headers: responseHeaders,
|
|
214
|
-
statusCode: responseHead.status_code,
|
|
215
|
-
body: responseTransformStream.readable
|
|
216
|
-
};
|
|
217
|
-
} catch (error) {
|
|
218
|
-
invocationLoggers.delete(loggerId);
|
|
219
|
-
throw error;
|
|
148
|
+
const loggerContext = new LoggerContext(input.invocation_id, this.handler.component().name(), this.handler.name(), this.handler.kind() === HandlerKind.SERVICE ? void 0 : input.key, invocationRequest, this.additionalContext);
|
|
149
|
+
const ctxLogger = createLogger(this.loggerTransport, LogSource.USER, loggerContext, () => !this.coreVm.is_processing());
|
|
150
|
+
this.vmLogger = createLogger(this.loggerTransport, LogSource.JOURNAL, loggerContext);
|
|
151
|
+
registerLogger(this.loggerId, this.vmLogger);
|
|
152
|
+
if (!this.coreVm.is_processing()) this.vmLogger.info("Replaying invocation.");
|
|
153
|
+
else this.vmLogger.info("Starting invocation.");
|
|
154
|
+
ctx = new ContextImpl(this.coreVm, input, ctxLogger, this.handler.kind(), this.vmLogger, invocationRequest, invocationEndPromise, inputReader, outputWriter, journalValueCodec, this.service.options?.serde, this.service.options?.asTerminalError);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
const error = ensureError(e);
|
|
157
|
+
this.coreVm.notify_error(error.message, error.message);
|
|
158
|
+
await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
|
|
159
|
+
return;
|
|
220
160
|
}
|
|
161
|
+
startUserHandler(ctx, this.service, this.handler, journalValueCodec).finally(() => {
|
|
162
|
+
invocationEndPromise.resolve();
|
|
163
|
+
});
|
|
164
|
+
await invocationEndPromise.promise;
|
|
165
|
+
await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
|
|
221
166
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (acceptVersionsString.includes(ENDPOINT_MANIFEST_V4)) manifestVersion = 4;
|
|
230
|
-
else if (acceptVersionsString.includes(ENDPOINT_MANIFEST_V3)) manifestVersion = 3;
|
|
231
|
-
else if (acceptVersionsString.includes(ENDPOINT_MANIFEST_V2)) manifestVersion = 2;
|
|
232
|
-
else {
|
|
233
|
-
const errorMessage = `Unsupported service discovery protocol version '${acceptVersionsString}'`;
|
|
234
|
-
this.endpoint.rlog.warn(errorMessage);
|
|
235
|
-
return this.toErrorResponse(415, errorMessage);
|
|
236
|
-
}
|
|
237
|
-
const discovery = {
|
|
238
|
-
...this.endpoint.discoveryMetadata,
|
|
239
|
-
...this.additionalDiscoveryFields,
|
|
240
|
-
protocolMode: this.protocolMode
|
|
241
|
-
};
|
|
242
|
-
const checkUnsupportedFeature = (obj, ...fields) => {
|
|
243
|
-
for (const field of fields) if (field in obj && obj[field] !== void 0) return this.toErrorResponse(500, `The code uses the new discovery feature '${String(field)}' but the runtime doesn't support it yet (discovery protocol negotiated version ${manifestVersion}). Either remove the usage of this feature, or upgrade the runtime.`);
|
|
244
|
-
};
|
|
245
|
-
if (manifestVersion < 3) for (const service of discovery.services) {
|
|
246
|
-
const error = checkUnsupportedFeature(service, "journalRetention", "idempotencyRetention", "inactivityTimeout", "abortTimeout", "enableLazyState", "ingressPrivate");
|
|
247
|
-
if (error !== void 0) return error;
|
|
248
|
-
for (const handler of service.handlers) {
|
|
249
|
-
const error$1 = checkUnsupportedFeature(handler, "journalRetention", "idempotencyRetention", "workflowCompletionRetention", "inactivityTimeout", "abortTimeout", "enableLazyState", "ingressPrivate");
|
|
250
|
-
if (error$1 !== void 0) return error$1;
|
|
251
|
-
}
|
|
167
|
+
};
|
|
168
|
+
async function bufferJournalReplayInCoreVm(coreVm, inputReader) {
|
|
169
|
+
while (!coreVm.is_ready_to_execute()) {
|
|
170
|
+
const nextValue = await inputReader.next();
|
|
171
|
+
if (nextValue.done) {
|
|
172
|
+
coreVm.notify_input_closed();
|
|
173
|
+
break;
|
|
252
174
|
}
|
|
253
|
-
if (
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
175
|
+
if (nextValue.value !== void 0) coreVm.notify_input(nextValue.value);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function startUserHandler(ctx, service, handler, journalValueCodec) {
|
|
179
|
+
try {
|
|
180
|
+
try {
|
|
181
|
+
const decodedInput = await journalValueCodec.decode(ctx.request().body).catch((e) => Promise.reject(new TerminalError(`Failed to decode input using journal value codec: ${ensureError(e).message}`, { errorCode: 400 })));
|
|
182
|
+
const output = await handler.invoke(ctx, decodedInput);
|
|
183
|
+
const encodedOutput = journalValueCodec.encode(output);
|
|
184
|
+
ctx.coreVm.sys_write_output_success(encodedOutput);
|
|
185
|
+
ctx.coreVm.sys_end();
|
|
186
|
+
ctx.vmLogger.info("Invocation completed successfully.");
|
|
187
|
+
} catch (e) {
|
|
188
|
+
const error = ensureError(e, service.options?.asTerminalError);
|
|
189
|
+
logError(ctx.vmLogger, error);
|
|
190
|
+
if (error instanceof TerminalError) {
|
|
191
|
+
ctx.coreVm.sys_write_output_failure({
|
|
192
|
+
code: error.code,
|
|
193
|
+
message: error.message,
|
|
194
|
+
metadata: Object.entries(error.metadata ?? {}).map(([key, value]) => ({
|
|
195
|
+
key,
|
|
196
|
+
value
|
|
197
|
+
}))
|
|
198
|
+
});
|
|
199
|
+
ctx.coreVm.sys_end();
|
|
200
|
+
return;
|
|
262
201
|
}
|
|
202
|
+
throw error;
|
|
263
203
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
"x-restate-server": X_RESTATE_SERVER
|
|
269
|
-
},
|
|
270
|
-
statusCode: 200,
|
|
271
|
-
body: OnceStream(new TextEncoder().encode(body))
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
toErrorResponse(code, message) {
|
|
275
|
-
return {
|
|
276
|
-
headers: {
|
|
277
|
-
"content-type": "application/json",
|
|
278
|
-
"x-restate-server": X_RESTATE_SERVER
|
|
279
|
-
},
|
|
280
|
-
statusCode: code,
|
|
281
|
-
body: OnceStream(new TextEncoder().encode(JSON.stringify({ message })))
|
|
282
|
-
};
|
|
204
|
+
} catch (e) {
|
|
205
|
+
const error = ensureError(e);
|
|
206
|
+
if (error instanceof RetryableError) ctx.coreVm.notify_error_with_delay_override(error.message, error.stack, error.retryAfter !== void 0 ? BigInt(millisOrDurationToMillis(error.retryAfter)) : void 0);
|
|
207
|
+
else ctx.coreVm.notify_error(error.message, error.stack);
|
|
283
208
|
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* The shared core propagates logs to the SDK invoking this method.
|
|
289
|
-
* When possible it provides an invocationId, which is used to access the registered invocationLoggers, that should contain the logger per invocation id.
|
|
290
|
-
*/
|
|
291
|
-
function vm_log(level, strBytes, loggerId) {
|
|
209
|
+
}
|
|
210
|
+
async function flushAndClose(coreVm, vmLogger, inputReader, outputWriter) {
|
|
211
|
+
let inputClosed = false;
|
|
292
212
|
try {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
213
|
+
let nextOutput = coreVm.take_output();
|
|
214
|
+
while (nextOutput !== null && nextOutput !== void 0) {
|
|
215
|
+
await outputWriter.write(nextOutput);
|
|
216
|
+
nextOutput = coreVm.take_output();
|
|
217
|
+
}
|
|
218
|
+
while (!inputClosed) try {
|
|
219
|
+
inputClosed = (await inputReader.next()).done ?? false;
|
|
220
|
+
} catch (e) {
|
|
221
|
+
inputClosed = true;
|
|
222
|
+
}
|
|
223
|
+
await outputWriter.close();
|
|
301
224
|
} catch (e) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
225
|
+
const error = ensureError(e);
|
|
226
|
+
const abortErrorOnWrite = isAbortErrorOnWrite(error);
|
|
227
|
+
if (inputClosed && abortErrorOnWrite) return;
|
|
228
|
+
if (abortErrorOnWrite) vmLogger.error("Got abort error from connection: " + error.message + "\nThis might indicate that:\n* The restate-server aborted the connection after hitting the 'abort-timeout'\n* The connection with the restate-server was lost\n\nPlease check the invocation in the Restate UI for more details.");
|
|
229
|
+
else vmLogger.error("Error while handling request: " + (error.stack ?? error.message));
|
|
307
230
|
}
|
|
308
231
|
}
|
|
309
|
-
function
|
|
310
|
-
|
|
311
|
-
case LogLevel.TRACE: return RestateLogLevel.TRACE;
|
|
312
|
-
case LogLevel.DEBUG: return RestateLogLevel.DEBUG;
|
|
313
|
-
case LogLevel.INFO: return RestateLogLevel.INFO;
|
|
314
|
-
case LogLevel.WARN: return RestateLogLevel.WARN;
|
|
315
|
-
case LogLevel.ERROR: return RestateLogLevel.ERROR;
|
|
316
|
-
}
|
|
232
|
+
function isAbortErrorOnWrite(error) {
|
|
233
|
+
return error.name === "AbortError" || error.message === "Invalid state: WritableStream is closed" || error.code === "ERR_HTTP2_INVALID_STREAM";
|
|
317
234
|
}
|
|
318
235
|
function restateLogLevelToWasmLogLevel(level) {
|
|
319
236
|
switch (level) {
|
|
@@ -326,5 +243,5 @@ function restateLogLevelToWasmLogLevel(level) {
|
|
|
326
243
|
}
|
|
327
244
|
|
|
328
245
|
//#endregion
|
|
329
|
-
export {
|
|
246
|
+
export { createRestateHandler };
|
|
330
247
|
//# sourceMappingURL=generic.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic.js","names":["endpoint: Endpoint","protocolMode: ProtocolMode","additionalDiscoveryFields: Partial<EndpointManifest>","vm.WasmIdentityVerifier","vm.WasmHeader","journalValueCodec: JournalValueCodec","vm.WasmVM","headers","invocationRequest: Request","error","invocationLoggers: Map<number, Logger>"],"sources":["../../../src/endpoint/handlers/generic.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH\n *\n * This file is part of the Restate SDK for Node.js/TypeScript,\n * which is released under the MIT license.\n *\n * You can find a copy of the license in file LICENSE in the root\n * directory of this repository or package, or at\n * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE\n */\n\nimport {\n ensureError,\n logError,\n RestateError,\n RetryableError,\n TerminalError,\n} from \"../../types/errors.js\";\nimport type {\n Endpoint as EndpointManifest,\n ProtocolMode,\n} from \"../discovery.js\";\nimport type { Component, ComponentHandler } from \"../components.js\";\nimport { parseUrlComponents } from \"../components.js\";\nimport { X_RESTATE_SERVER } from \"../../user_agent.js\";\nimport { type ReadableStream, TransformStream } from \"node:stream/web\";\nimport { OnceStream } from \"../../utils/streams.js\";\nimport { ContextImpl } from \"../../context_impl.js\";\nimport type { Request } from \"../../context.js\";\nimport * as vm from \"./vm/sdk_shared_core_wasm_bindings.js\";\nimport { CompletablePromise } from \"../../utils/completable_promise.js\";\nimport { HandlerKind } from \"../../types/rpc.js\";\nimport { createLogger, type Logger } from \"../../logging/logger.js\";\nimport {\n DEFAULT_CONSOLE_LOGGER_LOG_LEVEL,\n defaultLoggerTransport,\n} from \"../../logging/console_logger_transport.js\";\nimport {\n LoggerContext,\n type LoggerTransport,\n LogSource,\n RestateLogLevel,\n} from \"../../logging/logger_transport.js\";\nimport {\n type JournalValueCodec,\n millisOrDurationToMillis,\n} from \"@restatedev/restate-sdk-core\";\nimport type { Endpoint } from \"../endpoint.js\";\n\nexport interface Headers {\n [name: string]: string | string[] | undefined;\n}\n\nexport interface ResponseHeaders {\n [name: string]: string;\n}\n\nexport interface AdditionalContext {\n [name: string]: string;\n}\n\nexport interface RestateRequest {\n readonly url: string;\n readonly headers: Headers;\n readonly body: ReadableStream<Uint8Array> | null;\n readonly extraArgs: unknown[];\n readonly abortSignal: AbortSignal;\n}\n\nexport interface RestateResponse {\n readonly headers: ResponseHeaders;\n readonly statusCode: number;\n readonly body: ReadableStream<Uint8Array>;\n}\n\nexport interface RestateHandler {\n handle(\n request: RestateRequest,\n context?: AdditionalContext\n ): Promise<RestateResponse>;\n}\n\nconst ENDPOINT_MANIFEST_V2 = \"application/vnd.restate.endpointmanifest.v2+json\";\nconst ENDPOINT_MANIFEST_V3 = \"application/vnd.restate.endpointmanifest.v3+json\";\nconst ENDPOINT_MANIFEST_V4 = \"application/vnd.restate.endpointmanifest.v4+json\";\n\nexport function tryCreateContextualLogger(\n loggerTransport: LoggerTransport,\n url: string,\n headers: Headers,\n additionalContext?: { [name: string]: string }\n): Logger | undefined {\n try {\n const path = new URL(url, \"https://example.com\").pathname;\n const parsed = parseUrlComponents(path);\n if (parsed.type !== \"invoke\") {\n return undefined;\n }\n const invocationId = invocationIdFromHeaders(headers);\n return createLogger(\n loggerTransport,\n LogSource.SYSTEM,\n new LoggerContext(\n invocationId,\n parsed.componentName,\n parsed.handlerName,\n undefined,\n undefined,\n additionalContext\n )\n );\n } catch {\n return undefined;\n }\n}\n\nfunction invocationIdFromHeaders(headers: Headers) {\n const invocationIdHeader = headers[\"x-restate-invocation-id\"];\n const invocationId =\n typeof invocationIdHeader === \"string\"\n ? invocationIdHeader\n : Array.isArray(invocationIdHeader)\n ? (invocationIdHeader[0] ?? \"unknown id\")\n : \"unknown id\";\n return invocationId;\n}\n\n/**\n * This is an internal API to support 'fetch' like handlers.\n * It supports both request-reply mode and bidirectional streaming mode.\n *\n * An individual handler will have to convert the shape of the incoming request\n * to a RestateRequest, and then pass it to this handler, and eventually convert back\n * the response.\n * Different runtimes have slightly different shapes of the incoming request, and responses.\n */\nexport class GenericHandler implements RestateHandler {\n private readonly identityVerifier?: vm.WasmIdentityVerifier;\n\n constructor(\n readonly endpoint: Endpoint,\n private readonly protocolMode: ProtocolMode,\n private readonly additionalDiscoveryFields: Partial<EndpointManifest>\n ) {\n // Setup identity verifier\n if (\n this.endpoint.keySet === undefined ||\n this.endpoint.keySet.length === 0\n ) {\n this.endpoint.rlog.warn(\n `Accepting requests without validating request signatures; handler access must be restricted`\n );\n } else {\n this.endpoint.rlog.info(\n `Validating requests using signing keys [${this.endpoint.keySet}]`\n );\n this.identityVerifier = new vm.WasmIdentityVerifier(this.endpoint.keySet);\n }\n\n // Set the logging level in the shared core too!\n vm.set_log_level(\n restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL)\n );\n }\n\n // handle does not throw.\n public async handle(\n request: RestateRequest,\n context?: AdditionalContext\n ): Promise<RestateResponse> {\n try {\n return await this._handle(request, context);\n } catch (e) {\n const error = ensureError(e);\n (\n tryCreateContextualLogger(\n this.endpoint.loggerTransport,\n request.url,\n request.headers\n ) ?? this.endpoint.rlog\n ).error(\n \"Error while handling request: \" + (error.stack ?? error.message)\n );\n return this.toErrorResponse(\n error instanceof RestateError ? error.code : 500,\n error.message\n );\n }\n }\n\n private async _handle(\n request: RestateRequest,\n context?: AdditionalContext\n ): Promise<RestateResponse> {\n // this is the recommended way to get the relative path from a url that may be relative or absolute\n const path = new URL(request.url, \"https://example.com\").pathname;\n const parsed = parseUrlComponents(path);\n\n if (parsed.type === \"unknown\") {\n const msg = `Invalid path. Allowed are /health, or /discover, or /invoke/SvcName/handlerName, but was: ${path}`;\n this.endpoint.rlog.trace(msg);\n return this.toErrorResponse(404, msg);\n }\n\n if (parsed.type === \"health\") {\n return {\n body: OnceStream(new TextEncoder().encode(\"OK\")),\n headers: {\n \"content-type\": \"application/text\",\n \"x-restate-server\": X_RESTATE_SERVER,\n },\n statusCode: 200,\n };\n }\n\n const error = this.validateConnectionSignature(path, request.headers);\n if (error !== null) {\n return error;\n }\n if (parsed.type === \"discover\") {\n return this.handleDiscovery(request.headers[\"accept\"]);\n }\n const serviceProtocolVersionString = request.headers[\"content-type\"];\n if (typeof serviceProtocolVersionString !== \"string\") {\n const errorMessage = \"Missing content-type header\";\n this.endpoint.rlog.warn(errorMessage);\n return this.toErrorResponse(415, errorMessage);\n }\n const service = this.endpoint.components.get(parsed.componentName);\n if (!service) {\n const msg = `No service found for URL: ${JSON.stringify(parsed)}`;\n this.endpoint.rlog.error(msg);\n return this.toErrorResponse(404, msg);\n }\n const handler = service?.handlerMatching(parsed);\n if (!handler) {\n const msg = `No service found for URL: ${JSON.stringify(parsed)}`;\n this.endpoint.rlog.error(msg);\n return this.toErrorResponse(404, msg);\n }\n if (!request.body) {\n const msg = \"The incoming message body was null\";\n this.endpoint.rlog.error(msg);\n return this.toErrorResponse(400, msg);\n }\n\n return this.handleInvoke(\n service,\n handler,\n request.body,\n request.headers,\n request.extraArgs,\n request.abortSignal,\n context ?? {}\n );\n }\n\n private validateConnectionSignature(\n path: string,\n headers: Headers\n ): RestateResponse | null {\n if (!this.identityVerifier) {\n // not validating\n return null;\n }\n\n const vmHeaders = Object.entries(headers)\n .filter(([, v]) => v !== undefined)\n .map(\n ([k, v]) =>\n new vm.WasmHeader(k, v instanceof Array ? v[0]! : (v as string))\n );\n\n try {\n this.identityVerifier.verify_identity(path, vmHeaders);\n return null;\n } catch (e) {\n this.endpoint.rlog.error(\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Rejecting request as its JWT did not validate: ${e}`\n );\n return this.toErrorResponse(401, \"Unauthorized\");\n }\n }\n\n private async handleInvoke(\n service: Component,\n handler: ComponentHandler,\n body: ReadableStream<Uint8Array>,\n headers: Headers,\n extraArgs: unknown[],\n abortSignal: AbortSignal,\n additionalContext: AdditionalContext\n ): Promise<RestateResponse> {\n const journalValueCodec: JournalValueCodec = this.endpoint.journalValueCodec\n ? await this.endpoint.journalValueCodec\n : {\n encode: (entry) => entry,\n decode: (entry) => Promise.resolve(entry),\n };\n const loggerId = Math.floor(Math.random() * 4_294_967_295 /* u32::MAX */);\n\n try {\n // Instantiate core vm and prepare response headers\n const vmHeaders = Object.entries(headers)\n .filter(([, v]) => v !== undefined)\n .map(\n ([k, v]) =>\n new vm.WasmHeader(k, v instanceof Array ? v[0]! : (v as string))\n );\n const coreVm = new vm.WasmVM(\n vmHeaders,\n restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL),\n loggerId,\n this.endpoint.journalValueCodec !== undefined\n );\n const responseHead = coreVm.get_response_head();\n const responseHeaders = responseHead.headers.reduce(\n (headers, { key, value }) => ({\n [key]: value,\n ...headers,\n }),\n {\n \"x-restate-server\": X_RESTATE_SERVER,\n }\n );\n\n // Use a default logger that still respects the endpoint custom logger\n // We will override this later with a logger that has a LoggerContext\n // See vm_log below for more details\n invocationLoggers.set(\n loggerId,\n createLogger(\n this.endpoint.loggerTransport,\n LogSource.JOURNAL,\n new LoggerContext(\n invocationIdFromHeaders(headers),\n service.name(),\n handler.name(),\n undefined,\n undefined,\n additionalContext\n )\n )\n );\n\n const inputReader = body.getReader();\n abortSignal.addEventListener(\n \"abort\",\n () => {\n invocationLoggers.delete(loggerId);\n void inputReader.cancel();\n },\n { once: true }\n );\n\n // Now buffer input entries\n while (!coreVm.is_ready_to_execute()) {\n const nextValue = await inputReader.read();\n if (nextValue.value !== undefined) {\n coreVm.notify_input(nextValue.value);\n }\n if (nextValue.done) {\n coreVm.notify_input_closed();\n break;\n }\n }\n\n // Get input\n const input = coreVm.sys_input();\n\n const invocationRequest: Request = {\n id: input.invocation_id,\n headers: input.headers.reduce((headers, { key, value }) => {\n headers.set(key, value);\n return headers;\n }, new Map()),\n attemptHeaders: Object.entries(headers).reduce(\n (headers, [key, value]) => {\n if (value !== undefined) {\n headers.set(key, value instanceof Array ? value[0] : value);\n }\n return headers;\n },\n new Map()\n ),\n body: input.input,\n extraArgs,\n attemptCompletedSignal: abortSignal,\n };\n\n // Prepare logger\n const loggerContext = new LoggerContext(\n input.invocation_id,\n handler.component().name(),\n handler.name(),\n handler.kind() === HandlerKind.SERVICE ? undefined : input.key,\n invocationRequest,\n additionalContext\n );\n const ctxLogger = createLogger(\n this.endpoint.loggerTransport,\n LogSource.USER,\n loggerContext,\n () => !coreVm.is_processing()\n );\n const vmLogger = createLogger(\n this.endpoint.loggerTransport,\n LogSource.JOURNAL,\n loggerContext\n // Filtering is done within the shared core\n );\n // See vm_log below for more details\n invocationLoggers.set(loggerId, vmLogger);\n if (!coreVm.is_processing()) {\n vmLogger.info(\"Replaying invocation.\");\n } else {\n vmLogger.info(\"Starting invocation.\");\n }\n\n // This promise is used to signal the end of the computation,\n // which can be either the user returns a value,\n // or an exception gets catched, or the state machine fails/suspends.\n //\n // The last case is handled internally within the ContextImpl.\n const invocationEndPromise = new CompletablePromise<void>();\n\n // Prepare response stream\n const responseTransformStream = new TransformStream<Uint8Array>();\n const outputWriter = responseTransformStream.writable.getWriter();\n\n // Prepare context\n const ctx = new ContextImpl(\n coreVm,\n input,\n ctxLogger,\n handler.kind(),\n vmLogger,\n invocationRequest,\n invocationEndPromise,\n inputReader,\n outputWriter,\n journalValueCodec,\n service.options?.serde,\n service.options?.asTerminalError\n );\n\n journalValueCodec\n .decode(input.input)\n .catch((e) =>\n Promise.reject(\n new TerminalError(\n `Failed to decode input using journal value codec: ${\n ensureError(e).message\n }`,\n {\n errorCode: 400,\n }\n )\n )\n )\n .then((decodedInput) =>\n // Invoke user handler code\n handler.invoke(ctx, decodedInput)\n )\n .then((output) => {\n // Write output result\n coreVm.sys_write_output_success(journalValueCodec.encode(output));\n coreVm.sys_end();\n vmLogger.info(\"Invocation completed successfully.\");\n })\n .catch((e) => {\n // Convert to Error\n const error = ensureError(e, service.options?.asTerminalError);\n logError(vmLogger, error);\n\n // If TerminalError, handle it here.\n // NOTE: this can still fail!\n if (error instanceof TerminalError) {\n coreVm.sys_write_output_failure({\n code: error.code,\n message: error.message,\n metadata: [],\n });\n coreVm.sys_end();\n return;\n }\n\n // Not a terminal error, have the below catch handle it\n throw error;\n })\n .catch((e) => {\n // Handle any other error now (retryable errors)\n const error = ensureError(e);\n if (error instanceof RetryableError) {\n coreVm.notify_error_with_delay_override(\n error.message,\n error.stack,\n error.retryAfter !== undefined\n ? BigInt(millisOrDurationToMillis(error.retryAfter))\n : undefined\n );\n } else {\n coreVm.notify_error(error.message, error.stack);\n }\n })\n .finally(() => {\n invocationEndPromise.resolve();\n });\n\n // Let's wire up invocationEndPromise with consuming all the output and closing the streams.\n invocationEndPromise.promise\n .then(async () => {\n // Consume output till the end, write it out, then close the stream\n let nextOutput = coreVm.take_output() as\n | Uint8Array\n | null\n | undefined;\n while (nextOutput !== null && nextOutput !== undefined) {\n await outputWriter.write(nextOutput);\n nextOutput = coreVm.take_output() as Uint8Array | null | undefined;\n }\n\n // --- After this point, we should have flushed the shared core internal buffer\n\n // Let's make sure we properly close the request stream before closing the response stream\n let inputClosed = false;\n while (!inputClosed) {\n try {\n const res = await inputReader.read();\n inputClosed = res.done;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n inputClosed = true;\n }\n }\n\n // Close the response stream\n await outputWriter.close();\n })\n .finally(() => {\n invocationLoggers.delete(loggerId);\n })\n .catch(() => {});\n\n return {\n headers: responseHeaders,\n statusCode: responseHead.status_code,\n body: responseTransformStream.readable as ReadableStream<Uint8Array>,\n };\n } catch (error) {\n invocationLoggers.delete(loggerId);\n throw error;\n }\n }\n\n private handleDiscovery(\n acceptVersionsString: string | string[] | undefined\n ): RestateResponse {\n if (typeof acceptVersionsString !== \"string\") {\n const errorMessage = \"Missing accept header\";\n this.endpoint.rlog.warn(errorMessage);\n return this.toErrorResponse(415, errorMessage);\n }\n\n // Negotiate version to use\n let manifestVersion;\n if (acceptVersionsString.includes(ENDPOINT_MANIFEST_V4)) {\n manifestVersion = 4;\n } else if (acceptVersionsString.includes(ENDPOINT_MANIFEST_V3)) {\n manifestVersion = 3;\n } else if (acceptVersionsString.includes(ENDPOINT_MANIFEST_V2)) {\n manifestVersion = 2;\n } else {\n const errorMessage = `Unsupported service discovery protocol version '${acceptVersionsString}'`;\n this.endpoint.rlog.warn(errorMessage);\n return this.toErrorResponse(415, errorMessage);\n }\n\n const discovery = {\n ...this.endpoint.discoveryMetadata,\n ...this.additionalDiscoveryFields,\n protocolMode: this.protocolMode,\n };\n\n const checkUnsupportedFeature = <T extends object>(\n obj: T,\n ...fields: Array<keyof T>\n ) => {\n for (const field of fields) {\n if (field in obj && obj[field] !== undefined) {\n return this.toErrorResponse(\n 500,\n `The code uses the new discovery feature '${String(\n field\n )}' but the runtime doesn't support it yet (discovery protocol negotiated version ${manifestVersion}). Either remove the usage of this feature, or upgrade the runtime.`\n );\n }\n }\n return;\n };\n\n // Verify none of the manifest v3 configuration options are used.\n if (manifestVersion < 3) {\n for (const service of discovery.services) {\n const error = checkUnsupportedFeature(\n service,\n \"journalRetention\",\n \"idempotencyRetention\",\n \"inactivityTimeout\",\n \"abortTimeout\",\n \"enableLazyState\",\n \"ingressPrivate\"\n );\n if (error !== undefined) {\n return error;\n }\n for (const handler of service.handlers) {\n const error = checkUnsupportedFeature(\n handler,\n \"journalRetention\",\n \"idempotencyRetention\",\n \"workflowCompletionRetention\",\n \"inactivityTimeout\",\n \"abortTimeout\",\n \"enableLazyState\",\n \"ingressPrivate\"\n );\n if (error !== undefined) {\n return error;\n }\n }\n }\n }\n\n if (manifestVersion < 4) {\n // Blank the lambda compression field. No need to fail in this case.\n discovery.lambdaCompression = undefined;\n for (const service of discovery.services) {\n const error = checkUnsupportedFeature(\n service,\n \"retryPolicyExponentiationFactor\",\n \"retryPolicyInitialInterval\",\n \"retryPolicyMaxAttempts\",\n \"retryPolicyMaxInterval\",\n \"retryPolicyOnMaxAttempts\"\n );\n if (error !== undefined) {\n return error;\n }\n for (const handler of service.handlers) {\n const error = checkUnsupportedFeature(\n handler,\n \"retryPolicyExponentiationFactor\",\n \"retryPolicyInitialInterval\",\n \"retryPolicyMaxAttempts\",\n \"retryPolicyMaxInterval\",\n \"retryPolicyOnMaxAttempts\"\n );\n if (error !== undefined) {\n return error;\n }\n }\n }\n }\n\n const body = JSON.stringify(discovery);\n return {\n headers: {\n \"content-type\":\n manifestVersion === 2\n ? ENDPOINT_MANIFEST_V2\n : manifestVersion === 3\n ? ENDPOINT_MANIFEST_V3\n : ENDPOINT_MANIFEST_V4,\n \"x-restate-server\": X_RESTATE_SERVER,\n },\n statusCode: 200,\n body: OnceStream(new TextEncoder().encode(body)),\n };\n }\n\n private toErrorResponse(code: number, message: string): RestateResponse {\n return {\n headers: {\n \"content-type\": \"application/json\",\n \"x-restate-server\": X_RESTATE_SERVER,\n },\n statusCode: code,\n body: OnceStream(new TextEncoder().encode(JSON.stringify({ message }))),\n };\n }\n}\n\n// See vm_log below for more details\nconst invocationLoggers: Map<number, Logger> = new Map<number, Logger>();\nconst logsTextDecoder = new TextDecoder(\"utf-8\", { fatal: false });\n\n/**\n * The shared core propagates logs to the SDK invoking this method.\n * When possible it provides an invocationId, which is used to access the registered invocationLoggers, that should contain the logger per invocation id.\n */\nexport function vm_log(\n level: vm.LogLevel,\n strBytes: Uint8Array,\n loggerId?: number\n) {\n try {\n const logger = (loggerId && invocationLoggers.get(loggerId)) || undefined;\n const str = logsTextDecoder.decode(strBytes);\n if (logger !== undefined) {\n logger.logForLevel(wasmLogLevelToRestateLogLevel(level), str);\n } else {\n defaultLoggerTransport(\n {\n level: wasmLogLevelToRestateLogLevel(level),\n replaying: false,\n source: LogSource.JOURNAL,\n },\n str\n );\n }\n } catch (e) {\n // This function CAN'T EVER propagate an error,\n // because otherwise it will cause an awesome error in the shared core due to concurrent usage of it.\n defaultLoggerTransport(\n {\n level: RestateLogLevel.ERROR,\n replaying: false,\n source: LogSource.SYSTEM,\n },\n \"Unexpected error thrown while trying to log: \" + e?.toString()\n );\n }\n}\n\nfunction wasmLogLevelToRestateLogLevel(level: vm.LogLevel): RestateLogLevel {\n switch (level) {\n case vm.LogLevel.TRACE:\n return RestateLogLevel.TRACE;\n case vm.LogLevel.DEBUG:\n return RestateLogLevel.DEBUG;\n case vm.LogLevel.INFO:\n return RestateLogLevel.INFO;\n case vm.LogLevel.WARN:\n return RestateLogLevel.WARN;\n case vm.LogLevel.ERROR:\n return RestateLogLevel.ERROR;\n }\n}\n\nfunction restateLogLevelToWasmLogLevel(level: RestateLogLevel): vm.LogLevel {\n switch (level) {\n case RestateLogLevel.TRACE:\n return vm.LogLevel.TRACE;\n case RestateLogLevel.DEBUG:\n return vm.LogLevel.DEBUG;\n case RestateLogLevel.INFO:\n return vm.LogLevel.INFO;\n case RestateLogLevel.WARN:\n return vm.LogLevel.WARN;\n case RestateLogLevel.ERROR:\n return vm.LogLevel.ERROR;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generic.js","names":["endpoint: Endpoint","protocolMode: ProtocolMode","additionalDiscoveryFields: Partial<EndpointManifest>","vm.WasmIdentityVerifier","vm.WasmHeader","service: Component","handler: ComponentHandler","attemptHeaders: Headers","extraArgs: unknown[]","additionalContext: AdditionalContext","journalValueCodecInit:\n | Promise<JournalValueCodec>\n | undefined","loggerTransport: LoggerTransport","vm.WasmVM","journalValueCodec: JournalValueCodec","ctx: ContextImpl","invocationRequest: Request"],"sources":["../../../src/endpoint/handlers/generic.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH\n *\n * This file is part of the Restate SDK for Node.js/TypeScript,\n * which is released under the MIT license.\n *\n * You can find a copy of the license in file LICENSE in the root\n * directory of this repository or package, or at\n * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE\n */\n\nimport {\n ensureError,\n logError,\n RestateError,\n RetryableError,\n TerminalError,\n} from \"../../types/errors.js\";\nimport type {\n Endpoint as EndpointManifest,\n ProtocolMode,\n} from \"../discovery.js\";\nimport {\n Component,\n ComponentHandler,\n InvokePathComponents,\n} from \"../components.js\";\nimport { parseUrlComponents } from \"../components.js\";\nimport { X_RESTATE_SERVER } from \"../../user_agent.js\";\nimport { ContextImpl } from \"../../context_impl.js\";\nimport type { Request } from \"../../context.js\";\nimport * as vm from \"./vm/sdk_shared_core_wasm_bindings.js\";\nimport { CompletablePromise } from \"../../utils/completable_promise.js\";\nimport { HandlerKind } from \"../../types/rpc.js\";\nimport { createLogger, type Logger } from \"../../logging/logger.js\";\nimport { DEFAULT_CONSOLE_LOGGER_LOG_LEVEL } from \"../../logging/console_logger_transport.js\";\nimport {\n LoggerContext,\n LoggerTransport,\n LogSource,\n RestateLogLevel,\n} from \"../../logging/logger_transport.js\";\nimport {\n type JournalValueCodec,\n millisOrDurationToMillis,\n} from \"@restatedev/restate-sdk-core\";\nimport type { Endpoint } from \"../endpoint.js\";\nimport {\n type RestateHandler,\n type Headers,\n type RestateRequest,\n type AdditionalContext,\n type RestateResponse,\n ResponseHeaders,\n InputReader,\n OutputWriter,\n} from \"./types.js\";\nimport { handleDiscovery } from \"./discovery.js\";\nimport {\n errorResponse,\n invocationIdFromHeaders,\n simpleResponse,\n tryCreateContextualLogger,\n} from \"./utils.js\";\nimport { destroyLogger, registerLogger } from \"./core_logging.js\";\n\nexport function createRestateHandler(\n endpoint: Endpoint,\n protocolMode: ProtocolMode,\n additionalDiscoveryFields: Partial<EndpointManifest>\n): RestateHandler {\n return new RestateHandlerImpl(\n endpoint,\n protocolMode,\n additionalDiscoveryFields\n );\n}\n\n/**\n * This is the RestateHandler implementation\n */\nclass RestateHandlerImpl implements RestateHandler {\n private readonly identityVerifier?: vm.WasmIdentityVerifier;\n\n constructor(\n readonly endpoint: Endpoint,\n private readonly protocolMode: ProtocolMode,\n private readonly additionalDiscoveryFields: Partial<EndpointManifest>\n ) {\n // Setup identity verifier\n if (\n this.endpoint.keySet === undefined ||\n this.endpoint.keySet.length === 0\n ) {\n this.endpoint.rlog.warn(\n `Accepting requests without validating request signatures; handler access must be restricted`\n );\n } else {\n this.endpoint.rlog.info(\n `Validating requests using signing keys [${this.endpoint.keySet}]`\n );\n this.identityVerifier = new vm.WasmIdentityVerifier(this.endpoint.keySet);\n }\n\n // Set the logging level in the shared core too!\n vm.set_log_level(\n restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL)\n );\n }\n\n // handle does not throw.\n public handle(\n request: RestateRequest,\n context?: AdditionalContext\n ): RestateResponse {\n try {\n return this._handle(request, context);\n } catch (e) {\n const error = ensureError(e);\n (\n tryCreateContextualLogger(\n this.endpoint.loggerTransport,\n request.url,\n request.headers\n ) ?? this.endpoint.rlog\n ).error(\n \"Error while handling request: \" + (error.stack ?? error.message)\n );\n return errorResponse(\n error instanceof RestateError ? error.code : 500,\n error.message\n );\n }\n }\n\n private _handle(\n request: RestateRequest,\n context?: AdditionalContext\n ): RestateResponse {\n // this is the recommended way to get the relative path from a url that may be relative or absolute\n const path = new URL(request.url, \"https://example.com\").pathname;\n const parsed = parseUrlComponents(path);\n\n if (parsed.type === \"unknown\") {\n const msg = `Invalid path. Allowed are /health, or /discover, or /invoke/SvcName/handlerName, but was: ${path}`;\n this.endpoint.rlog.trace(msg);\n return errorResponse(404, msg);\n }\n if (parsed.type === \"health\") {\n return simpleResponse(\n 200,\n {\n \"content-type\": \"application/text\",\n \"x-restate-server\": X_RESTATE_SERVER,\n },\n new TextEncoder().encode(\"OK\")\n );\n }\n\n // Discovery and handling invocations require identity verification\n const error = this.validateConnectionSignature(path, request.headers);\n if (error !== null) {\n return error;\n }\n if (parsed.type === \"discover\") {\n return handleDiscovery(\n this.endpoint,\n this.protocolMode,\n this.additionalDiscoveryFields,\n request.headers[\"accept\"]\n );\n }\n\n return this.handleInvoke(\n parsed,\n request.headers,\n request.extraArgs,\n context ?? {}\n );\n }\n\n private validateConnectionSignature(\n path: string,\n headers: Headers\n ): RestateResponse | null {\n if (!this.identityVerifier) {\n // not validating\n return null;\n }\n\n const vmHeaders = Object.entries(headers)\n .filter(([, v]) => v !== undefined)\n .map(\n ([k, v]) =>\n new vm.WasmHeader(k, v instanceof Array ? v[0]! : (v as string))\n );\n\n try {\n this.identityVerifier.verify_identity(path, vmHeaders);\n return null;\n } catch (e) {\n this.endpoint.rlog.error(\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Rejecting request as its JWT did not validate: ${e}`\n );\n return errorResponse(401, \"Unauthorized\");\n }\n }\n\n private handleInvoke(\n invokePathComponent: InvokePathComponents,\n headers: Headers,\n extraArgs: unknown[],\n additionalContext: AdditionalContext\n ): RestateResponse {\n // Check if we support this protocol version\n const serviceProtocolVersionString = headers[\"content-type\"];\n if (typeof serviceProtocolVersionString !== \"string\") {\n const errorMessage = \"Missing content-type header\";\n this.endpoint.rlog.warn(errorMessage);\n return errorResponse(415, errorMessage);\n }\n\n // Resolve service and handler\n const service = this.endpoint.components.get(\n invokePathComponent.componentName\n );\n if (!service) {\n const msg = `No service found for URL: ${JSON.stringify(invokePathComponent)}`;\n this.endpoint.rlog.error(msg);\n return errorResponse(404, msg);\n }\n const handler = service?.handlerMatching(invokePathComponent);\n if (!handler) {\n const msg = `No service found for URL: ${JSON.stringify(invokePathComponent)}`;\n this.endpoint.rlog.error(msg);\n return errorResponse(404, msg);\n }\n\n return new RestateInvokeResponse(\n service,\n handler,\n headers,\n extraArgs,\n additionalContext,\n this.endpoint.journalValueCodec,\n this.endpoint.loggerTransport\n );\n }\n}\n\nclass RestateInvokeResponse implements RestateResponse {\n public headers: ResponseHeaders;\n public statusCode: number;\n\n private readonly loggerId: number;\n private vmLogger: Logger;\n private readonly coreVm: vm.WasmVM;\n\n constructor(\n private readonly service: Component,\n private readonly handler: ComponentHandler,\n private readonly attemptHeaders: Headers,\n private readonly extraArgs: unknown[],\n private readonly additionalContext: AdditionalContext,\n private readonly journalValueCodecInit:\n | Promise<JournalValueCodec>\n | undefined,\n private readonly loggerTransport: LoggerTransport\n ) {\n this.loggerId = Math.floor(Math.random() * 4_294_967_295 /* u32::MAX */);\n const isJournalCodecDefined = this.journalValueCodecInit !== undefined;\n\n // Instantiate core vm and prepare response headers\n const vmHeaders = Object.entries(this.attemptHeaders)\n .filter(([, v]) => v !== undefined)\n .map(\n ([k, v]) =>\n new vm.WasmHeader(k, v instanceof Array ? v[0]! : (v as string))\n );\n this.coreVm = new vm.WasmVM(\n vmHeaders,\n restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL),\n this.loggerId,\n isJournalCodecDefined\n );\n const responseHead = this.coreVm.get_response_head();\n this.statusCode = responseHead.status_code;\n this.headers = responseHead.headers.reduce(\n (headers, { key, value }) => ({\n [key]: value,\n ...headers,\n }),\n {\n \"x-restate-server\": X_RESTATE_SERVER,\n }\n );\n this.vmLogger = createLogger(\n this.loggerTransport,\n LogSource.JOURNAL,\n new LoggerContext(\n invocationIdFromHeaders(this.attemptHeaders),\n this.service.name(),\n this.handler.name(),\n undefined,\n undefined,\n this.additionalContext\n )\n );\n }\n\n async process({\n inputReader,\n outputWriter,\n abortSignal,\n }: {\n inputReader: InputReader;\n outputWriter: OutputWriter;\n abortSignal: AbortSignal;\n }): Promise<void> {\n abortSignal.addEventListener(\n \"abort\",\n () => {\n // In any case, on abort remove the invocation logger to avoid memory leaks\n destroyLogger(this.loggerId);\n },\n { once: true }\n );\n // Use a default logger that still respects the endpoint custom logger\n // We will override this later with a logger that has a LoggerContext\n // See vm_log below for more details\n registerLogger(this.loggerId, this.vmLogger);\n\n const journalValueCodec: JournalValueCodec = this.journalValueCodecInit\n ? await this.journalValueCodecInit\n : {\n encode: (entry) => entry,\n decode: (entry) => Promise.resolve(entry),\n };\n\n // This promise is used to signal the end of the computation,\n // which can be either the user returns a value,\n // or an exception gets caught, or the state machine fails/suspends.\n //\n // The last case is handled internally within the ContextImpl.\n const invocationEndPromise = new CompletablePromise<void>();\n let ctx: ContextImpl;\n\n // Initial phase before running user code\n // -> Buffer in shared core the journal entries\n // -> Initiate loggers\n // -> Initialize the ContextImpl\n try {\n // Buffer journal inside shared core\n await bufferJournalReplayInCoreVm(this.coreVm, inputReader);\n\n // Get input from coreVm to build the request object\n const input = this.coreVm.sys_input();\n const invocationRequest: Request = {\n id: input.invocation_id,\n headers: input.headers.reduce((headers, { key, value }) => {\n headers.set(key, value);\n return headers;\n }, new Map()),\n attemptHeaders: Object.entries(this.attemptHeaders).reduce(\n (headers, [key, value]) => {\n if (value !== undefined) {\n headers.set(key, value instanceof Array ? value[0] : value);\n }\n return headers;\n },\n new Map()\n ),\n body: input.input,\n extraArgs: this.extraArgs,\n attemptCompletedSignal: abortSignal,\n };\n\n // Prepare logger\n const loggerContext = new LoggerContext(\n input.invocation_id,\n this.handler.component().name(),\n this.handler.name(),\n this.handler.kind() === HandlerKind.SERVICE ? undefined : input.key,\n invocationRequest,\n this.additionalContext\n );\n const ctxLogger = createLogger(\n this.loggerTransport,\n LogSource.USER,\n loggerContext,\n () => !this.coreVm.is_processing()\n );\n // Override the vmLogger created before with more info!\n this.vmLogger = createLogger(\n this.loggerTransport,\n LogSource.JOURNAL,\n loggerContext\n // Filtering is done within the shared core\n );\n\n // See vm_log below for more details\n registerLogger(this.loggerId, this.vmLogger);\n if (!this.coreVm.is_processing()) {\n this.vmLogger.info(\"Replaying invocation.\");\n } else {\n this.vmLogger.info(\"Starting invocation.\");\n }\n\n // Prepare context\n ctx = new ContextImpl(\n this.coreVm,\n input,\n ctxLogger,\n this.handler.kind(),\n this.vmLogger,\n invocationRequest,\n invocationEndPromise,\n inputReader,\n outputWriter,\n journalValueCodec,\n this.service.options?.serde,\n this.service.options?.asTerminalError\n );\n } catch (e) {\n // That's \"preflight\" failure cases, where stuff fails before running user code\n // In this scenario, we close the coreVm, then flush and close\n const error = ensureError(e);\n this.coreVm.notify_error(error.message, error.message);\n await flushAndClose(\n this.coreVm,\n this.vmLogger,\n inputReader,\n outputWriter\n );\n return;\n }\n\n // Start the user code but don't await it directly.\n // We await invocationEndPromise instead, which works as follows:\n // * In the happy path, that is no errors, invocationEndPromise gets resolved by the line below in this finally branch\n // * In the transient error case that happens within the handler, invocationEndPromise gets resolved by the ContextImpl itself, unblocking the await line below\n void startUserHandler(\n ctx,\n this.service,\n this.handler,\n journalValueCodec\n ).finally(() => {\n invocationEndPromise.resolve();\n });\n await invocationEndPromise.promise;\n\n // Then flush and close\n await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);\n }\n}\n\nasync function bufferJournalReplayInCoreVm(\n coreVm: vm.WasmVM,\n inputReader: InputReader\n) {\n while (!coreVm.is_ready_to_execute()) {\n const nextValue = await inputReader.next();\n if (nextValue.done) {\n coreVm.notify_input_closed();\n break;\n }\n if (nextValue.value !== undefined) {\n coreVm.notify_input(nextValue.value);\n }\n }\n}\n\nasync function startUserHandler(\n ctx: ContextImpl,\n service: Component,\n handler: ComponentHandler,\n journalValueCodec: JournalValueCodec\n) {\n try {\n try {\n const decodedInput = await journalValueCodec\n .decode(ctx.request().body)\n .catch((e) =>\n // Re-throw as terminal error, to fail on input errors\n Promise.reject(\n new TerminalError(\n `Failed to decode input using journal value codec: ${\n ensureError(e).message\n }`,\n {\n errorCode: 400,\n }\n )\n )\n );\n\n // Then run user code\n const output = await handler.invoke(ctx, decodedInput);\n\n // Encode user code output\n const encodedOutput = journalValueCodec.encode(output);\n\n // Write out and end\n ctx.coreVm.sys_write_output_success(encodedOutput);\n ctx.coreVm.sys_end();\n ctx.vmLogger.info(\"Invocation completed successfully.\");\n } catch (e) {\n // Convert to Error\n const error = ensureError(e, service.options?.asTerminalError);\n logError(ctx.vmLogger, error);\n\n // If TerminalError, handle it here.\n // NOTE: this can still fail, that's why the double catch!\n if (error instanceof TerminalError) {\n ctx.coreVm.sys_write_output_failure({\n code: error.code,\n message: error.message,\n metadata: Object.entries(error.metadata ?? {}).map(\n ([key, value]) => ({ key, value })\n ),\n });\n ctx.coreVm.sys_end();\n return;\n }\n\n // Not a terminal error, have the below catch handle it\n throw error;\n }\n } catch (e) {\n // Handle any other error now (retryable errors)\n const error = ensureError(e);\n if (error instanceof RetryableError) {\n ctx.coreVm.notify_error_with_delay_override(\n error.message,\n error.stack,\n error.retryAfter !== undefined\n ? BigInt(millisOrDurationToMillis(error.retryAfter))\n : undefined\n );\n } else {\n ctx.coreVm.notify_error(error.message, error.stack);\n }\n }\n}\n\nasync function flushAndClose(\n coreVm: vm.WasmVM,\n vmLogger: Logger,\n inputReader: InputReader,\n outputWriter: OutputWriter\n): Promise<void> {\n let inputClosed = false;\n try {\n // Consume output till the end, write it out, then close the stream\n let nextOutput = coreVm.take_output() as Uint8Array | null | undefined;\n while (nextOutput !== null && nextOutput !== undefined) {\n await outputWriter.write(nextOutput);\n nextOutput = coreVm.take_output() as Uint8Array | null | undefined;\n }\n\n // --- After this point, we should have flushed the shared core internal buffer\n\n // Let's make sure we properly close the request stream before closing the response stream\n while (!inputClosed) {\n try {\n const res = await inputReader.next();\n inputClosed = res.done ?? false;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n inputClosed = true;\n }\n }\n\n // Close the response stream\n await outputWriter.close();\n } catch (e) {\n // In case of failure, we can do little here except just logging stuff out,\n // because outputWriter is not usable here.\n const error = ensureError(e);\n const abortErrorOnWrite = isAbortErrorOnWrite(error);\n\n if (inputClosed && abortErrorOnWrite) {\n // Because we closed the input already,\n // these errors are benign and are caused by\n // synchronization issues wrt closing the response stream in the runtime\n // This will be fixed in the runtime with https://github.com/restatedev/restate/issues/4456\n return;\n }\n\n if (abortErrorOnWrite) {\n vmLogger.error(\n \"Got abort error from connection: \" +\n error.message +\n \"\\n\" +\n \"This might indicate that:\\n\" +\n \"* The restate-server aborted the connection after hitting the 'abort-timeout'\\n\" +\n \"* The connection with the restate-server was lost\\n\" +\n \"\\n\" +\n \"Please check the invocation in the Restate UI for more details.\"\n );\n } else {\n vmLogger.error(\n \"Error while handling request: \" + (error.stack ?? error.message)\n );\n }\n }\n}\n\nfunction isAbortErrorOnWrite(error: Error) {\n return (\n error.name === \"AbortError\" ||\n error.message === \"Invalid state: WritableStream is closed\" ||\n /**\n * Node stream closed error thrown on writes\n */\n (error as { code?: string }).code === \"ERR_HTTP2_INVALID_STREAM\"\n );\n}\n\nfunction restateLogLevelToWasmLogLevel(level: RestateLogLevel): vm.LogLevel {\n switch (level) {\n case RestateLogLevel.TRACE:\n return vm.LogLevel.TRACE;\n case RestateLogLevel.DEBUG:\n return vm.LogLevel.DEBUG;\n case RestateLogLevel.INFO:\n return vm.LogLevel.INFO;\n case RestateLogLevel.WARN:\n return vm.LogLevel.WARN;\n case RestateLogLevel.ERROR:\n return vm.LogLevel.ERROR;\n }\n}\n"],"mappings":"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"}
|