rivetkit 2.3.12-rc.3 → 2.3.12
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/browser/client.d.ts +17 -2
- package/dist/browser/client.js +32 -24
- package/dist/browser/client.js.map +1 -1
- package/dist/browser/inspector/client.js +2 -1
- package/dist/browser/inspector/client.js.map +1 -1
- package/dist/tsup/actor/errors.cjs +2 -2
- package/dist/tsup/actor/errors.js +1 -1
- package/dist/tsup/agent-os/index.cjs +2 -1
- package/dist/tsup/agent-os/index.cjs.map +1 -1
- package/dist/tsup/agent-os/index.d.cts +17 -2
- package/dist/tsup/agent-os/index.d.ts +17 -2
- package/dist/tsup/agent-os/index.js +2 -1
- package/dist/tsup/agent-os/index.js.map +1 -1
- package/dist/tsup/{chunk-SPJORZMM.cjs → chunk-2JSYYH6P.cjs} +1 -1
- package/dist/tsup/chunk-2JSYYH6P.cjs.map +1 -0
- package/dist/tsup/{chunk-IJWTHKH6.cjs → chunk-AN7WO7BM.cjs} +3 -3
- package/dist/tsup/{chunk-IJWTHKH6.cjs.map → chunk-AN7WO7BM.cjs.map} +1 -1
- package/dist/tsup/{chunk-PSXMESQJ.js → chunk-EBXARMGI.js} +2 -2
- package/dist/tsup/{chunk-O6H3BUN4.cjs → chunk-FGQABMXZ.cjs} +3 -3
- package/dist/tsup/{chunk-O6H3BUN4.cjs.map → chunk-FGQABMXZ.cjs.map} +1 -1
- package/dist/tsup/{chunk-WVFTFWJL.js → chunk-GEN4NHXM.js} +2 -2
- package/dist/tsup/{chunk-XI4MEUUI.js → chunk-JYTXOMM7.js} +2 -2
- package/dist/tsup/{chunk-OX6WVQE6.cjs → chunk-MEF7RVQ7.cjs} +10 -10
- package/dist/tsup/{chunk-OX6WVQE6.cjs.map → chunk-MEF7RVQ7.cjs.map} +1 -1
- package/dist/tsup/{chunk-OLGQYFUW.js → chunk-MF42PRF2.js} +9 -3
- package/dist/tsup/chunk-MF42PRF2.js.map +1 -0
- package/dist/tsup/{chunk-IXBD7BXC.js → chunk-OUQUIBVW.js} +1 -1
- package/dist/tsup/chunk-OUQUIBVW.js.map +1 -0
- package/dist/tsup/{chunk-QLOBB27M.cjs → chunk-PW2VSGUR.cjs} +4 -4
- package/dist/tsup/{chunk-QLOBB27M.cjs.map → chunk-PW2VSGUR.cjs.map} +1 -1
- package/dist/tsup/{chunk-NHI5L7C7.js → chunk-RBXJWAJB.js} +3 -3
- package/dist/tsup/{chunk-UBB32UOG.cjs → chunk-SSPTEGTX.cjs} +7 -7
- package/dist/tsup/{chunk-UBB32UOG.cjs.map → chunk-SSPTEGTX.cjs.map} +1 -1
- package/dist/tsup/{chunk-4ESGNS3Z.cjs → chunk-TAG6UEL5.cjs} +17 -11
- package/dist/tsup/chunk-TAG6UEL5.cjs.map +1 -0
- package/dist/tsup/{chunk-EQKSWDIH.cjs → chunk-TXUHVBKH.cjs} +3 -3
- package/dist/tsup/{chunk-EQKSWDIH.cjs.map → chunk-TXUHVBKH.cjs.map} +1 -1
- package/dist/tsup/{chunk-PZBWSCQR.cjs → chunk-TZPNSOG2.cjs} +203 -195
- package/dist/tsup/chunk-TZPNSOG2.cjs.map +1 -0
- package/dist/tsup/{chunk-YYIDQBBM.js → chunk-VOYNLY57.js} +41 -33
- package/dist/tsup/chunk-VOYNLY57.js.map +1 -0
- package/dist/tsup/{chunk-UN5JNDBT.js → chunk-XUODFCRJ.js} +4 -4
- package/dist/tsup/{chunk-34Q4YVCX.js → chunk-ZIPXAVKR.js} +2 -2
- package/dist/tsup/client/mod.cjs +6 -6
- package/dist/tsup/client/mod.d.cts +2 -2
- package/dist/tsup/client/mod.d.ts +2 -2
- package/dist/tsup/client/mod.js +5 -5
- package/dist/tsup/common/log.cjs +3 -3
- package/dist/tsup/common/log.js +2 -2
- package/dist/tsup/common/websocket.cjs +4 -4
- package/dist/tsup/common/websocket.js +3 -3
- package/dist/tsup/{config-Bi1r6gI7.d.ts → config-BGTanB_3.d.ts} +18 -2
- package/dist/tsup/{config-BXSV17G7.d.cts → config-NHz6oMb9.d.cts} +18 -2
- package/dist/tsup/{context-4fJemY0D.d.cts → context-DB8zG_Ib.d.cts} +1 -1
- package/dist/tsup/{context-D4sNYboo.d.ts → context-SHomdVV8.d.ts} +1 -1
- package/dist/tsup/db/drizzle.cjs +3 -3
- package/dist/tsup/db/drizzle.js +2 -2
- package/dist/tsup/db/mod.cjs +4 -4
- package/dist/tsup/db/mod.js +3 -3
- package/dist/tsup/dynamic/mod.d.cts +1 -1
- package/dist/tsup/dynamic/mod.d.ts +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/inspector/workflow.cjs +5 -5
- package/dist/tsup/inspector/workflow.js +4 -4
- package/dist/tsup/inspector-tab/mod.d.cts +2 -2
- package/dist/tsup/inspector-tab/mod.d.ts +2 -2
- package/dist/tsup/mod.cjs +423 -177
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +3 -3
- package/dist/tsup/mod.d.ts +3 -3
- package/dist/tsup/mod.js +322 -76
- package/dist/tsup/mod.js.map +1 -1
- package/dist/tsup/test/mod.cjs +7 -7
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +5 -5
- package/dist/tsup/utils.cjs +3 -3
- package/dist/tsup/utils.js +2 -2
- package/dist/tsup/workflow/mod.cjs +11 -11
- package/dist/tsup/workflow/mod.d.cts +3 -3
- package/dist/tsup/workflow/mod.d.ts +3 -3
- package/dist/tsup/workflow/mod.js +5 -5
- package/package.json +9 -8
- package/src/actor/errors.ts +2 -1
- package/src/client/actor-handle.ts +10 -3
- package/src/client/raw-utils.ts +8 -0
- package/src/common/actor-router-consts.ts +2 -0
- package/src/engine-client/actor-http-client.ts +6 -22
- package/src/engine-client/mod.ts +1 -1
- package/src/registry/config/index.ts +27 -1
- package/src/registry/index.ts +48 -14
- package/src/registry/napi-runtime.ts +32 -5
- package/src/registry/native-http.ts +225 -0
- package/src/registry/native.ts +130 -64
- package/src/registry/runtime.ts +30 -2
- package/src/utils/env-vars.ts +4 -0
- package/dist/tsup/chunk-4ESGNS3Z.cjs.map +0 -1
- package/dist/tsup/chunk-IXBD7BXC.js.map +0 -1
- package/dist/tsup/chunk-OLGQYFUW.js.map +0 -1
- package/dist/tsup/chunk-PZBWSCQR.cjs.map +0 -1
- package/dist/tsup/chunk-SPJORZMM.cjs.map +0 -1
- package/dist/tsup/chunk-YYIDQBBM.js.map +0 -1
- /package/dist/tsup/{chunk-PSXMESQJ.js.map → chunk-EBXARMGI.js.map} +0 -0
- /package/dist/tsup/{chunk-WVFTFWJL.js.map → chunk-GEN4NHXM.js.map} +0 -0
- /package/dist/tsup/{chunk-XI4MEUUI.js.map → chunk-JYTXOMM7.js.map} +0 -0
- /package/dist/tsup/{chunk-NHI5L7C7.js.map → chunk-RBXJWAJB.js.map} +0 -0
- /package/dist/tsup/{chunk-UN5JNDBT.js.map → chunk-XUODFCRJ.js.map} +0 -0
- /package/dist/tsup/{chunk-34Q4YVCX.js.map → chunk-ZIPXAVKR.js.map} +0 -0
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
CancellationToken as NativeCancellationToken,
|
|
5
5
|
ConnHandle as NativeConnHandle,
|
|
6
6
|
CoreRegistry as NativeCoreRegistry,
|
|
7
|
+
HttpResponseBodyStream as NativeHttpResponseBodyStream,
|
|
7
8
|
WebSocket as NativeWebSocket,
|
|
8
9
|
} from "@rivetkit/rivetkit-napi";
|
|
9
10
|
import type {
|
|
@@ -21,6 +22,7 @@ import type {
|
|
|
21
22
|
RuntimeCronFire,
|
|
22
23
|
RuntimeCronJobInfo,
|
|
23
24
|
RuntimeHttpRequest,
|
|
25
|
+
RuntimeHttpResponseBodyStream,
|
|
24
26
|
RuntimeKvEntry,
|
|
25
27
|
RuntimeKvListOptions,
|
|
26
28
|
RuntimeListenerConfig,
|
|
@@ -154,23 +156,48 @@ function toNapiApplication(application: RuntimeApplicationFetch) {
|
|
|
154
156
|
url: string;
|
|
155
157
|
headers: Record<string, string>;
|
|
156
158
|
body: Buffer;
|
|
159
|
+
cancelToken?: NativeCancellationToken;
|
|
160
|
+
responseBodyStream?: RuntimeHttpResponseBodyStream;
|
|
157
161
|
},
|
|
158
162
|
) => {
|
|
159
163
|
if (error) throw error;
|
|
160
164
|
if (!request) {
|
|
161
165
|
throw new Error("application fetch callback received no request");
|
|
162
166
|
}
|
|
163
|
-
const response = await application(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
const response = await application(
|
|
168
|
+
{
|
|
169
|
+
...request,
|
|
170
|
+
body: new Uint8Array(request.body),
|
|
171
|
+
cancelToken:
|
|
172
|
+
request.cancelToken as unknown as CancellationTokenHandle,
|
|
173
|
+
},
|
|
174
|
+
request.responseBodyStream
|
|
175
|
+
? fromNapiHttpResponseBodyStream(
|
|
176
|
+
request.responseBodyStream as unknown as NativeHttpResponseBodyStream,
|
|
177
|
+
)
|
|
178
|
+
: undefined,
|
|
179
|
+
);
|
|
167
180
|
return {
|
|
168
181
|
...response,
|
|
169
|
-
body:
|
|
182
|
+
body:
|
|
183
|
+
response.body === undefined
|
|
184
|
+
? undefined
|
|
185
|
+
: toNapiBuffer(response.body),
|
|
170
186
|
};
|
|
171
187
|
};
|
|
172
188
|
}
|
|
173
189
|
|
|
190
|
+
function fromNapiHttpResponseBodyStream(
|
|
191
|
+
stream: NativeHttpResponseBodyStream,
|
|
192
|
+
): RuntimeHttpResponseBodyStream {
|
|
193
|
+
return {
|
|
194
|
+
cancelled: () => stream.cancelled(),
|
|
195
|
+
write: (chunk) => stream.write(Buffer.from(chunk)),
|
|
196
|
+
end: () => stream.end(),
|
|
197
|
+
error: (message) => stream.error(message),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
174
201
|
function toNapiHttpRequest(
|
|
175
202
|
request?: RuntimeHttpRequest | undefined | null,
|
|
176
203
|
): Parameters<NativeActorContext["connectConn"]>[1] {
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { ResponseLike } from "@/common/fetch-like";
|
|
2
|
+
import { HEADER_ORIGINAL_REQUEST_URL } from "@/common/actor-router-consts";
|
|
3
|
+
import { stringifyError } from "@/common/utils";
|
|
4
|
+
import { logger } from "./log";
|
|
5
|
+
import type { RuntimeBytes, RuntimeHttpResponse } from "./runtime";
|
|
6
|
+
|
|
7
|
+
const HTTP_BODY_CHUNK_SIZE = 64 * 1024;
|
|
8
|
+
|
|
9
|
+
// Structural types keep the HTTP adapter independent of the N-API package.
|
|
10
|
+
export interface NativeHttpResponseBodyStream {
|
|
11
|
+
cancelled(): Promise<void>;
|
|
12
|
+
write(chunk: Uint8Array): Promise<void>;
|
|
13
|
+
end(): Promise<void>;
|
|
14
|
+
error(message: string): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface NativeHttpRequestBodyStream {
|
|
18
|
+
read(): Promise<Uint8Array | null | undefined>;
|
|
19
|
+
cancel(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface NativeHttpRequestInit {
|
|
23
|
+
method: string;
|
|
24
|
+
uri: string;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
body?: RuntimeBytes;
|
|
27
|
+
bodyStream?: NativeHttpRequestBodyStream;
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
abortController?: AbortController;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface NativeHttpResponseConversion {
|
|
33
|
+
response: RuntimeHttpResponse;
|
|
34
|
+
// Keeps request-scoped actor resources alive until the response body closes.
|
|
35
|
+
bodyCompletion?: Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function runtimeBytesToArrayBuffer(value: RuntimeBytes): ArrayBuffer {
|
|
39
|
+
return value.buffer.slice(
|
|
40
|
+
value.byteOffset,
|
|
41
|
+
value.byteOffset + value.byteLength,
|
|
42
|
+
) as ArrayBuffer;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function buildNativeHttpRequest(init: NativeHttpRequestInit): Request {
|
|
46
|
+
const headers = new Headers(init.headers);
|
|
47
|
+
const originalUrl = headers.get(HEADER_ORIGINAL_REQUEST_URL);
|
|
48
|
+
headers.delete(HEADER_ORIGINAL_REQUEST_URL);
|
|
49
|
+
const url =
|
|
50
|
+
originalUrl ??
|
|
51
|
+
(init.uri.startsWith("http")
|
|
52
|
+
? init.uri
|
|
53
|
+
: new URL(init.uri, "http://127.0.0.1").toString());
|
|
54
|
+
const method = init.method.toUpperCase();
|
|
55
|
+
const bodyForbidden = method === "GET" || method === "HEAD";
|
|
56
|
+
const body = bodyForbidden
|
|
57
|
+
? undefined
|
|
58
|
+
: init.bodyStream
|
|
59
|
+
? new ReadableStream<Uint8Array>({
|
|
60
|
+
async pull(controller) {
|
|
61
|
+
try {
|
|
62
|
+
if (init.body && init.body.length > 0) {
|
|
63
|
+
controller.enqueue(new Uint8Array(init.body));
|
|
64
|
+
init.body = undefined;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const chunk = await init.bodyStream?.read();
|
|
68
|
+
if (!chunk) {
|
|
69
|
+
controller.close();
|
|
70
|
+
} else {
|
|
71
|
+
controller.enqueue(new Uint8Array(chunk));
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
init.abortController?.abort(error);
|
|
75
|
+
controller.error(error);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async cancel() {
|
|
79
|
+
await init.bodyStream?.cancel();
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
: init.body && init.body.length > 0
|
|
83
|
+
? runtimeBytesToArrayBuffer(init.body)
|
|
84
|
+
: undefined;
|
|
85
|
+
const streamInit =
|
|
86
|
+
init.bodyStream && !bodyForbidden ? { duplex: "half" } : {};
|
|
87
|
+
return new Request(url, {
|
|
88
|
+
method,
|
|
89
|
+
headers,
|
|
90
|
+
body,
|
|
91
|
+
signal: init.abortController?.signal ?? init.signal,
|
|
92
|
+
...streamInit,
|
|
93
|
+
} as RequestInit);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function cancelNativeHttpRequestBody(
|
|
97
|
+
bodyStream?: NativeHttpRequestBodyStream,
|
|
98
|
+
) {
|
|
99
|
+
await bodyStream?.cancel();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function writeResponseChunk(
|
|
103
|
+
stream: NativeHttpResponseBodyStream,
|
|
104
|
+
chunk: Uint8Array,
|
|
105
|
+
) {
|
|
106
|
+
for (
|
|
107
|
+
let offset = 0;
|
|
108
|
+
offset < chunk.byteLength;
|
|
109
|
+
offset += HTTP_BODY_CHUNK_SIZE
|
|
110
|
+
) {
|
|
111
|
+
await stream.write(
|
|
112
|
+
chunk.subarray(offset, offset + HTTP_BODY_CHUNK_SIZE),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function pumpResponseBody(
|
|
118
|
+
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
119
|
+
stream: NativeHttpResponseBodyStream,
|
|
120
|
+
) {
|
|
121
|
+
// Awaiting each write applies native-side backpressure. Racing the next read
|
|
122
|
+
// against cancellation also stops an otherwise unbounded SSE response when
|
|
123
|
+
// its downstream receiver disappears.
|
|
124
|
+
const cancelled = stream
|
|
125
|
+
.cancelled()
|
|
126
|
+
.then(() => ({ cancelled: true }) as const);
|
|
127
|
+
try {
|
|
128
|
+
for (;;) {
|
|
129
|
+
const read = reader
|
|
130
|
+
.read()
|
|
131
|
+
.then((next) => ({ cancelled: false, next }) as const);
|
|
132
|
+
const result = await Promise.race([read, cancelled]);
|
|
133
|
+
if (result.cancelled) {
|
|
134
|
+
// Web Streams closes pending reads before waiting for the source's cancel
|
|
135
|
+
// callback. Do not let an arbitrary user cancel promise retain the native
|
|
136
|
+
// request and connection cleanup indefinitely.
|
|
137
|
+
void reader
|
|
138
|
+
.cancel(
|
|
139
|
+
new Error(
|
|
140
|
+
"native http response stream receiver dropped",
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
.catch((error) => {
|
|
144
|
+
logger().debug({
|
|
145
|
+
msg: "failed to cancel native http response reader",
|
|
146
|
+
error,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const { next } = result;
|
|
152
|
+
if (next.done) {
|
|
153
|
+
await stream.end();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (next.value?.byteLength) {
|
|
157
|
+
await writeResponseChunk(stream, next.value);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
try {
|
|
162
|
+
await stream.error(stringifyError(error));
|
|
163
|
+
} catch (streamError) {
|
|
164
|
+
logger().debug({
|
|
165
|
+
msg: "failed to report native http response stream error",
|
|
166
|
+
error: streamError,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
void reader.cancel(error).catch(() => {
|
|
170
|
+
// Reader may already be closed after a native-side disconnect.
|
|
171
|
+
});
|
|
172
|
+
} finally {
|
|
173
|
+
try {
|
|
174
|
+
reader.releaseLock();
|
|
175
|
+
} catch (error) {
|
|
176
|
+
logger().debug({
|
|
177
|
+
msg: "failed to release native http response reader lock",
|
|
178
|
+
error,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export async function convertNativeHttpResponse(
|
|
185
|
+
response: ResponseLike,
|
|
186
|
+
responseBodyStream?: NativeHttpResponseBodyStream,
|
|
187
|
+
): Promise<NativeHttpResponseConversion> {
|
|
188
|
+
const headers = Object.fromEntries(response.headers.entries());
|
|
189
|
+
if (!response.body) {
|
|
190
|
+
return {
|
|
191
|
+
response: {
|
|
192
|
+
status: response.status,
|
|
193
|
+
headers,
|
|
194
|
+
body: new Uint8Array(),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (responseBodyStream) {
|
|
200
|
+
const reader = response.body.getReader();
|
|
201
|
+
const bodyCompletion = pumpResponseBody(reader, responseBodyStream);
|
|
202
|
+
return {
|
|
203
|
+
response: {
|
|
204
|
+
status: response.status,
|
|
205
|
+
headers,
|
|
206
|
+
stream: true,
|
|
207
|
+
},
|
|
208
|
+
bodyCompletion,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
response: {
|
|
214
|
+
status: response.status,
|
|
215
|
+
headers,
|
|
216
|
+
body: new Uint8Array(await response.arrayBuffer()),
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const nativeHttpTestInternals = {
|
|
222
|
+
buildRequest: buildNativeHttpRequest,
|
|
223
|
+
cancelRequestBody: cancelNativeHttpRequestBody,
|
|
224
|
+
convertRuntimeHttpResponse: convertNativeHttpResponse,
|
|
225
|
+
};
|
package/src/registry/native.ts
CHANGED
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
} from "@/common/database/mod";
|
|
58
58
|
import { wrapJsNativeDatabase } from "@/common/database/native-database";
|
|
59
59
|
import { assertJsonCompatValue, type JsonCompatValue } from "@/common/encoding";
|
|
60
|
-
import { isResponseLike
|
|
60
|
+
import { isResponseLike } from "@/common/fetch-like";
|
|
61
61
|
import {
|
|
62
62
|
decodeWorkflowHistoryTransport,
|
|
63
63
|
type WorkflowHistoryBytes,
|
|
@@ -85,6 +85,13 @@ import {
|
|
|
85
85
|
} from "@/utils/node";
|
|
86
86
|
import { logger } from "./log";
|
|
87
87
|
import { loadNapiRuntime } from "./napi-runtime";
|
|
88
|
+
import {
|
|
89
|
+
buildNativeHttpRequest,
|
|
90
|
+
cancelNativeHttpRequestBody,
|
|
91
|
+
convertNativeHttpResponse,
|
|
92
|
+
type NativeHttpRequestBodyStream,
|
|
93
|
+
type NativeHttpResponseBodyStream,
|
|
94
|
+
} from "./native-http";
|
|
88
95
|
import {
|
|
89
96
|
type NativeValidationConfig,
|
|
90
97
|
validateActionArgs,
|
|
@@ -105,7 +112,6 @@ import type {
|
|
|
105
112
|
RuntimeBytes,
|
|
106
113
|
RuntimeCronFire,
|
|
107
114
|
RuntimeCronJobInfo,
|
|
108
|
-
RuntimeHttpResponse,
|
|
109
115
|
RuntimeInspectorTabEntry,
|
|
110
116
|
RuntimeQueueMessage,
|
|
111
117
|
RuntimeScheduledEventInfo,
|
|
@@ -746,6 +752,12 @@ async function loadEngineCli(): Promise<typeof import("@rivetkit/engine-cli")> {
|
|
|
746
752
|
return import(["@rivetkit", "engine-cli"].join("/"));
|
|
747
753
|
}
|
|
748
754
|
|
|
755
|
+
async function loadServices(): Promise<typeof import("@rivet-dev/services")> {
|
|
756
|
+
// Keep the native binary opaque to edge bundlers for the same reason as
|
|
757
|
+
// engine-cli above.
|
|
758
|
+
return import(["@rivet-dev", "services"].join("/"));
|
|
759
|
+
}
|
|
760
|
+
|
|
749
761
|
function decodeValue<T>(value?: RuntimeBytes | null): T {
|
|
750
762
|
if (!value || value.length === 0) {
|
|
751
763
|
return undefined as T;
|
|
@@ -1250,38 +1262,6 @@ function decodeArgs(value?: RuntimeBytes | null): unknown[] {
|
|
|
1250
1262
|
return normalizeArgs(decoded);
|
|
1251
1263
|
}
|
|
1252
1264
|
|
|
1253
|
-
function buildRequest(init: {
|
|
1254
|
-
method: string;
|
|
1255
|
-
uri: string;
|
|
1256
|
-
headers?: Record<string, string>;
|
|
1257
|
-
body?: RuntimeBytes;
|
|
1258
|
-
}): Request {
|
|
1259
|
-
const url = init.uri.startsWith("http")
|
|
1260
|
-
? init.uri
|
|
1261
|
-
: new URL(init.uri, "http://127.0.0.1").toString();
|
|
1262
|
-
const body =
|
|
1263
|
-
init.body && init.body.length > 0
|
|
1264
|
-
? runtimeBytesToArrayBuffer(init.body)
|
|
1265
|
-
: undefined;
|
|
1266
|
-
return new Request(url, {
|
|
1267
|
-
method: init.method,
|
|
1268
|
-
headers: init.headers,
|
|
1269
|
-
body,
|
|
1270
|
-
});
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
async function toRuntimeHttpResponse(
|
|
1274
|
-
response: ResponseLike,
|
|
1275
|
-
): Promise<RuntimeHttpResponse> {
|
|
1276
|
-
const headers = Object.fromEntries(response.headers.entries());
|
|
1277
|
-
const body = new Uint8Array(await response.arrayBuffer());
|
|
1278
|
-
return {
|
|
1279
|
-
status: response.status,
|
|
1280
|
-
headers,
|
|
1281
|
-
body,
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
1265
|
function toActorKey(
|
|
1286
1266
|
segments: Array<{
|
|
1287
1267
|
kind: string;
|
|
@@ -4068,19 +4048,22 @@ export function buildNativeFactory(
|
|
|
4068
4048
|
);
|
|
4069
4049
|
const maybeHandleNativeInspectorRequest = async (
|
|
4070
4050
|
ctx: ActorContextHandle,
|
|
4071
|
-
|
|
4051
|
+
rawRequest: {
|
|
4072
4052
|
method: string;
|
|
4073
4053
|
uri: string;
|
|
4074
4054
|
headers?: Record<string, string>;
|
|
4075
4055
|
body?: RuntimeBytes;
|
|
4076
4056
|
},
|
|
4077
|
-
jsRequest: Request,
|
|
4078
4057
|
): Promise<Response | undefined> => {
|
|
4079
|
-
const
|
|
4058
|
+
const rawUrl = rawRequest.uri.startsWith("http")
|
|
4059
|
+
? rawRequest.uri
|
|
4060
|
+
: new URL(rawRequest.uri, "http://127.0.0.1").toString();
|
|
4061
|
+
const url = new URL(rawUrl);
|
|
4080
4062
|
if (!url.pathname.startsWith("/inspector/")) {
|
|
4081
4063
|
return undefined;
|
|
4082
4064
|
}
|
|
4083
4065
|
|
|
4066
|
+
const jsRequest = buildNativeHttpRequest(rawRequest);
|
|
4084
4067
|
const jsonResponse = (body: unknown, init?: ResponseInit) =>
|
|
4085
4068
|
new Response(JSON.stringify(body), {
|
|
4086
4069
|
status: init?.status ?? 200,
|
|
@@ -4772,7 +4755,9 @@ export function buildNativeFactory(
|
|
|
4772
4755
|
);
|
|
4773
4756
|
const actorCtx = makeActorCtx(
|
|
4774
4757
|
ctx,
|
|
4775
|
-
request
|
|
4758
|
+
request
|
|
4759
|
+
? buildNativeHttpRequest(request)
|
|
4760
|
+
: undefined,
|
|
4776
4761
|
);
|
|
4777
4762
|
try {
|
|
4778
4763
|
await config.onBeforeConnect(
|
|
@@ -4809,7 +4794,9 @@ export function buildNativeFactory(
|
|
|
4809
4794
|
unwrapTsfnPayload(error, payload);
|
|
4810
4795
|
const actorCtx = makeActorCtx(
|
|
4811
4796
|
ctx,
|
|
4812
|
-
request
|
|
4797
|
+
request
|
|
4798
|
+
? buildNativeHttpRequest(request)
|
|
4799
|
+
: undefined,
|
|
4813
4800
|
);
|
|
4814
4801
|
const connAdapter = new NativeConnAdapter(
|
|
4815
4802
|
runtime,
|
|
@@ -4865,7 +4852,9 @@ export function buildNativeFactory(
|
|
|
4865
4852
|
);
|
|
4866
4853
|
const actorCtx = makeActorCtx(
|
|
4867
4854
|
ctx,
|
|
4868
|
-
request
|
|
4855
|
+
request
|
|
4856
|
+
? buildNativeHttpRequest(request)
|
|
4857
|
+
: undefined,
|
|
4869
4858
|
);
|
|
4870
4859
|
const connAdapter = new NativeConnAdapter(
|
|
4871
4860
|
runtime,
|
|
@@ -5017,38 +5006,63 @@ export function buildNativeFactory(
|
|
|
5017
5006
|
uri: string;
|
|
5018
5007
|
headers?: Record<string, string>;
|
|
5019
5008
|
body?: RuntimeBytes;
|
|
5009
|
+
bodyStream?: NativeHttpRequestBodyStream;
|
|
5020
5010
|
};
|
|
5021
5011
|
cancelToken?: CancellationTokenHandle;
|
|
5012
|
+
responseBodyStream?: NativeHttpResponseBodyStream;
|
|
5022
5013
|
},
|
|
5023
5014
|
) => {
|
|
5024
5015
|
try {
|
|
5025
|
-
const { ctx, request, cancelToken } =
|
|
5026
|
-
error,
|
|
5027
|
-
payload,
|
|
5028
|
-
);
|
|
5029
|
-
const jsRequest = buildRequest(request);
|
|
5016
|
+
const { ctx, request, cancelToken, responseBodyStream } =
|
|
5017
|
+
unwrapTsfnPayload(error, payload);
|
|
5030
5018
|
const inspectorResponse =
|
|
5031
|
-
await maybeHandleNativeInspectorRequest(
|
|
5032
|
-
ctx,
|
|
5033
|
-
request,
|
|
5034
|
-
jsRequest,
|
|
5035
|
-
);
|
|
5019
|
+
await maybeHandleNativeInspectorRequest(ctx, request);
|
|
5036
5020
|
if (inspectorResponse) {
|
|
5037
|
-
|
|
5021
|
+
await cancelNativeHttpRequestBody(request.bodyStream);
|
|
5022
|
+
return (
|
|
5023
|
+
await convertNativeHttpResponse(
|
|
5024
|
+
inspectorResponse,
|
|
5025
|
+
responseBodyStream,
|
|
5026
|
+
)
|
|
5027
|
+
).response;
|
|
5038
5028
|
}
|
|
5039
5029
|
|
|
5040
5030
|
if (typeof config.onRequest !== "function") {
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5031
|
+
await cancelNativeHttpRequestBody(request.bodyStream);
|
|
5032
|
+
return (
|
|
5033
|
+
await convertNativeHttpResponse(
|
|
5034
|
+
new Response(null, { status: 404 }),
|
|
5035
|
+
responseBodyStream,
|
|
5036
|
+
)
|
|
5037
|
+
).response;
|
|
5044
5038
|
}
|
|
5045
5039
|
|
|
5040
|
+
const requestAbortController = new AbortController();
|
|
5041
|
+
const handlerRequest = buildNativeHttpRequest({
|
|
5042
|
+
...request,
|
|
5043
|
+
abortController: requestAbortController,
|
|
5044
|
+
});
|
|
5046
5045
|
const rawConnParams =
|
|
5047
|
-
|
|
5046
|
+
handlerRequest.headers.get(HEADER_CONN_PARAMS);
|
|
5048
5047
|
let requestCtx:
|
|
5049
5048
|
| ReturnType<typeof withConnContext>
|
|
5050
5049
|
| undefined;
|
|
5051
5050
|
let conn: ConnHandle | undefined;
|
|
5051
|
+
let removeRequestAbortListener: (() => void) | undefined;
|
|
5052
|
+
let cleanupDeferredToBody = false;
|
|
5053
|
+
let cleanedUp = false;
|
|
5054
|
+
const cleanupRequest = async () => {
|
|
5055
|
+
if (cleanedUp) return;
|
|
5056
|
+
cleanedUp = true;
|
|
5057
|
+
removeRequestAbortListener?.();
|
|
5058
|
+
try {
|
|
5059
|
+
await requestCtx?.dispose();
|
|
5060
|
+
} finally {
|
|
5061
|
+
if (conn) {
|
|
5062
|
+
await runtime.connDisconnect(conn);
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
};
|
|
5052
5066
|
try {
|
|
5053
5067
|
const connParams = validateConnParams(
|
|
5054
5068
|
schemaConfig.connParamsSchema,
|
|
@@ -5066,23 +5080,62 @@ export function buildNativeFactory(
|
|
|
5066
5080
|
requestCtx = makeConnCtx(
|
|
5067
5081
|
ctx,
|
|
5068
5082
|
conn,
|
|
5069
|
-
|
|
5083
|
+
handlerRequest,
|
|
5070
5084
|
cancelToken,
|
|
5071
5085
|
);
|
|
5086
|
+
const ctxAbortSignal = requestCtx.abortSignal;
|
|
5087
|
+
const abortRequest = () =>
|
|
5088
|
+
requestAbortController.abort(ctxAbortSignal.reason);
|
|
5089
|
+
if (ctxAbortSignal.aborted) {
|
|
5090
|
+
abortRequest();
|
|
5091
|
+
} else {
|
|
5092
|
+
ctxAbortSignal.addEventListener(
|
|
5093
|
+
"abort",
|
|
5094
|
+
abortRequest,
|
|
5095
|
+
{ once: true },
|
|
5096
|
+
);
|
|
5097
|
+
removeRequestAbortListener = () =>
|
|
5098
|
+
ctxAbortSignal.removeEventListener(
|
|
5099
|
+
"abort",
|
|
5100
|
+
abortRequest,
|
|
5101
|
+
);
|
|
5102
|
+
}
|
|
5072
5103
|
const response = await config.onRequest(
|
|
5073
5104
|
requestCtx,
|
|
5074
|
-
|
|
5105
|
+
handlerRequest,
|
|
5075
5106
|
);
|
|
5076
5107
|
if (!isResponseLike(response)) {
|
|
5077
5108
|
throw new Error(
|
|
5078
5109
|
"onRequest handler must return a Response",
|
|
5079
5110
|
);
|
|
5080
5111
|
}
|
|
5081
|
-
|
|
5112
|
+
const conversion = await convertNativeHttpResponse(
|
|
5113
|
+
response,
|
|
5114
|
+
responseBodyStream,
|
|
5115
|
+
);
|
|
5116
|
+
if (conversion.bodyCompletion) {
|
|
5117
|
+
cleanupDeferredToBody = true;
|
|
5118
|
+
void conversion.bodyCompletion
|
|
5119
|
+
.then(cleanupRequest)
|
|
5120
|
+
.catch((cleanupError) => {
|
|
5121
|
+
logger().error({
|
|
5122
|
+
msg: "failed to clean up native streaming http request",
|
|
5123
|
+
error: cleanupError,
|
|
5124
|
+
});
|
|
5125
|
+
});
|
|
5126
|
+
}
|
|
5127
|
+
return conversion.response;
|
|
5082
5128
|
} finally {
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5129
|
+
try {
|
|
5130
|
+
// Handler completion ends upload ownership even when
|
|
5131
|
+
// the Web Request body is locked or partly consumed.
|
|
5132
|
+
await cancelNativeHttpRequestBody(
|
|
5133
|
+
request.bodyStream,
|
|
5134
|
+
);
|
|
5135
|
+
} finally {
|
|
5136
|
+
if (!cleanupDeferredToBody) {
|
|
5137
|
+
await cleanupRequest();
|
|
5138
|
+
}
|
|
5086
5139
|
}
|
|
5087
5140
|
}
|
|
5088
5141
|
} catch (error) {
|
|
@@ -5114,7 +5167,7 @@ export function buildNativeFactory(
|
|
|
5114
5167
|
const { ctx, conn, ws, request } =
|
|
5115
5168
|
unwrapTsfnPayload(error, payload);
|
|
5116
5169
|
const jsRequest = request
|
|
5117
|
-
?
|
|
5170
|
+
? buildNativeHttpRequest(request)
|
|
5118
5171
|
: undefined;
|
|
5119
5172
|
const actorCtx = makeConnCtx(ctx, conn, jsRequest);
|
|
5120
5173
|
try {
|
|
@@ -5281,7 +5334,7 @@ export function buildNativeFactory(
|
|
|
5281
5334
|
timeoutMs,
|
|
5282
5335
|
cancelToken,
|
|
5283
5336
|
} = unwrapTsfnPayload(error, payload);
|
|
5284
|
-
const jsRequest =
|
|
5337
|
+
const jsRequest = buildNativeHttpRequest(request);
|
|
5285
5338
|
const actorCtx = withConnContext(
|
|
5286
5339
|
runtime,
|
|
5287
5340
|
ctx,
|
|
@@ -5380,6 +5433,7 @@ export function buildNativeFactory(
|
|
|
5380
5433
|
|
|
5381
5434
|
export async function buildServeConfig(
|
|
5382
5435
|
config: RegistryConfig,
|
|
5436
|
+
resolveServicesBinary = true,
|
|
5383
5437
|
): Promise<RuntimeServeConfig> {
|
|
5384
5438
|
if (!config.endpoint) {
|
|
5385
5439
|
throw nativeEndpointNotConfiguredError();
|
|
@@ -5391,6 +5445,7 @@ export async function buildServeConfig(
|
|
|
5391
5445
|
token: config.token,
|
|
5392
5446
|
namespace: config.namespace,
|
|
5393
5447
|
poolName: config.envoy.poolName,
|
|
5448
|
+
startServices: resolveServicesBinary && config.startServices,
|
|
5394
5449
|
handleInspectorHttpInRuntime: true,
|
|
5395
5450
|
serverlessBasePath: config.serverless.basePath,
|
|
5396
5451
|
serverlessPackageVersion: VERSION,
|
|
@@ -5422,6 +5477,17 @@ export async function buildServeConfig(
|
|
|
5422
5477
|
error: stringifyError(error),
|
|
5423
5478
|
});
|
|
5424
5479
|
}
|
|
5480
|
+
if (resolveServicesBinary && config.startServices) {
|
|
5481
|
+
try {
|
|
5482
|
+
const { getServicesPath } = await loadServices();
|
|
5483
|
+
serveConfig.servicesBinaryPath = getServicesPath();
|
|
5484
|
+
} catch (error) {
|
|
5485
|
+
logger().warn({
|
|
5486
|
+
msg: "could not resolve the Services binary; a local engine will fail with services.binary_unavailable — set RIVET_RUN_SERVICES=0 to disable Services or RIVET_SERVICES_BINARY to a local rivet-services binary",
|
|
5487
|
+
error: stringifyError(error),
|
|
5488
|
+
});
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5425
5491
|
serveConfig.engineHost = config.engineHost;
|
|
5426
5492
|
serveConfig.enginePort = config.enginePort;
|
|
5427
5493
|
if (config.test?.enabled) {
|
|
@@ -5467,7 +5533,7 @@ export async function buildRegistryWithRuntime(
|
|
|
5467
5533
|
return {
|
|
5468
5534
|
runtime,
|
|
5469
5535
|
registry,
|
|
5470
|
-
serveConfig: await buildServeConfig(config),
|
|
5536
|
+
serveConfig: await buildServeConfig(config, runtime.kind === "napi"),
|
|
5471
5537
|
};
|
|
5472
5538
|
}
|
|
5473
5539
|
|