rivetkit 2.0.4 → 2.0.5
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/tsup/actor/errors.cjs +2 -2
- package/dist/tsup/actor/errors.js +1 -1
- package/dist/tsup/{chunk-HLLF4B4Q.js → chunk-2MD57QF4.js} +7 -5
- package/dist/tsup/chunk-2MD57QF4.js.map +1 -0
- package/dist/tsup/{chunk-SBHHJ6QS.cjs → chunk-5QGQK44L.cjs} +2 -2
- package/dist/tsup/{chunk-SBHHJ6QS.cjs.map → chunk-5QGQK44L.cjs.map} +1 -1
- package/dist/tsup/{chunk-4CXBCT26.cjs → chunk-5YTI25C3.cjs} +7 -7
- package/dist/tsup/{chunk-4CXBCT26.cjs.map → chunk-5YTI25C3.cjs.map} +1 -1
- package/dist/tsup/{chunk-TQ62L3X7.js → chunk-B2QGJGZQ.js} +18 -5
- package/dist/tsup/chunk-B2QGJGZQ.js.map +1 -0
- package/dist/tsup/{chunk-PQY7KKTL.js → chunk-CFFKMUYH.js} +32 -7
- package/dist/tsup/{chunk-PQY7KKTL.js.map → chunk-CFFKMUYH.js.map} +1 -1
- package/dist/tsup/{chunk-H26RP6GD.js → chunk-CKA54YQN.js} +2 -2
- package/dist/tsup/chunk-D7NWUCRK.cjs +20 -0
- package/dist/tsup/{chunk-4R73YDN3.cjs.map → chunk-D7NWUCRK.cjs.map} +1 -1
- package/dist/tsup/{chunk-IH6CKNDW.cjs → chunk-FGFT4FVX.cjs} +9 -9
- package/dist/tsup/{chunk-IH6CKNDW.cjs.map → chunk-FGFT4FVX.cjs.map} +1 -1
- package/dist/tsup/{chunk-HI3HWJRC.js → chunk-I5VTWPHW.js} +4 -4
- package/dist/tsup/{chunk-HI3HWJRC.js.map → chunk-I5VTWPHW.js.map} +1 -1
- package/dist/tsup/{chunk-GICQ3YCU.cjs → chunk-IRMBWX36.cjs} +39 -37
- package/dist/tsup/chunk-IRMBWX36.cjs.map +1 -0
- package/dist/tsup/{chunk-XJQHKJ4P.js → chunk-L7QRXNWP.js} +331 -62
- package/dist/tsup/chunk-L7QRXNWP.js.map +1 -0
- package/dist/tsup/{chunk-LWNKVZG5.cjs → chunk-LZIBTLEY.cjs} +12 -12
- package/dist/tsup/{chunk-LWNKVZG5.cjs.map → chunk-LZIBTLEY.cjs.map} +1 -1
- package/dist/tsup/{chunk-VO7ZRVVD.cjs → chunk-MRZS2J4X.cjs} +581 -312
- package/dist/tsup/chunk-MRZS2J4X.cjs.map +1 -0
- package/dist/tsup/{chunk-QK72M5JB.js → chunk-PG3K2LI7.js} +2 -2
- package/dist/tsup/{chunk-QNNXFOQV.cjs → chunk-PHSQJ6QI.cjs} +3 -3
- package/dist/tsup/{chunk-QNNXFOQV.cjs.map → chunk-PHSQJ6QI.cjs.map} +1 -1
- package/dist/tsup/{chunk-WHBPJNGW.cjs → chunk-RM2SVURR.cjs} +29 -16
- package/dist/tsup/chunk-RM2SVURR.cjs.map +1 -0
- package/dist/tsup/{chunk-6LJT3QRL.cjs → chunk-WADSS5X4.cjs} +37 -12
- package/dist/tsup/chunk-WADSS5X4.cjs.map +1 -0
- package/dist/tsup/{chunk-LV2S3OU3.js → chunk-WNGOBAA7.js} +2 -2
- package/dist/tsup/{chunk-NFU2BBT5.js → chunk-YPZFLUO6.js} +2 -2
- package/dist/tsup/chunk-YPZFLUO6.js.map +1 -0
- package/dist/tsup/{chunk-3F2YSRJL.js → chunk-YW6Y6VNE.js} +4 -4
- package/dist/tsup/client/mod.cjs +10 -10
- package/dist/tsup/client/mod.d.cts +2 -2
- package/dist/tsup/client/mod.d.ts +2 -2
- package/dist/tsup/client/mod.js +9 -9
- package/dist/tsup/common/log.cjs +4 -4
- package/dist/tsup/common/log.js +3 -3
- package/dist/tsup/common/websocket.cjs +5 -5
- package/dist/tsup/common/websocket.js +4 -4
- package/dist/tsup/{connection-BI-6UIBJ.d.ts → connection-BvE-Oq7t.d.ts} +22 -11
- package/dist/tsup/{connection-Dyd4NLGW.d.cts → connection-DTzmWwU5.d.cts} +22 -11
- package/dist/tsup/driver-helpers/mod.cjs +6 -6
- package/dist/tsup/driver-helpers/mod.d.cts +2 -2
- package/dist/tsup/driver-helpers/mod.d.ts +2 -2
- package/dist/tsup/driver-helpers/mod.js +5 -5
- package/dist/tsup/driver-test-suite/mod.cjs +606 -140
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +2 -2
- package/dist/tsup/driver-test-suite/mod.d.ts +2 -2
- package/dist/tsup/driver-test-suite/mod.js +563 -97
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +9 -9
- package/dist/tsup/mod.d.cts +3 -3
- package/dist/tsup/mod.d.ts +3 -3
- package/dist/tsup/mod.js +8 -8
- package/dist/tsup/{router-endpoints-BTe_Rsdn.d.cts → router-endpoints-CctffZNL.d.cts} +1 -1
- package/dist/tsup/{router-endpoints-CBSrKHmo.d.ts → router-endpoints-DFm1BglJ.d.ts} +1 -1
- package/dist/tsup/test/mod.cjs +10 -10
- package/dist/tsup/test/mod.d.cts +1 -1
- package/dist/tsup/test/mod.d.ts +1 -1
- package/dist/tsup/test/mod.js +9 -9
- package/dist/tsup/utils.cjs +5 -3
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.d.cts +18 -1
- package/dist/tsup/utils.d.ts +18 -1
- package/dist/tsup/utils.js +4 -2
- package/package.json +4 -4
- package/src/actor/errors.ts +1 -1
- package/src/actor/router-endpoints.ts +6 -1
- package/src/actor/router.ts +2 -1
- package/src/client/mod.ts +1 -1
- package/src/client/raw-utils.ts +1 -1
- package/src/driver-test-suite/mod.ts +1 -1
- package/src/driver-test-suite/test-inline-client-driver.ts +588 -0
- package/src/driver-test-suite/tests/actor-error-handling.ts +4 -12
- package/src/driver-test-suite/tests/actor-inspector.ts +2 -1
- package/src/driver-test-suite/utils.ts +10 -9
- package/src/drivers/file-system/global-state.ts +3 -1
- package/src/drivers/file-system/manager.ts +16 -21
- package/src/manager/router.ts +373 -5
- package/src/registry/mod.ts +29 -33
- package/src/registry/run-config.ts +16 -1
- package/src/remote-manager-driver/actor-http-client.ts +3 -1
- package/src/remote-manager-driver/actor-websocket-client.ts +2 -1
- package/src/remote-manager-driver/api-utils.ts +4 -1
- package/src/remote-manager-driver/mod.ts +2 -1
- package/src/utils.ts +53 -0
- package/dist/tsup/chunk-4R73YDN3.cjs +0 -20
- package/dist/tsup/chunk-6LJT3QRL.cjs.map +0 -1
- package/dist/tsup/chunk-GICQ3YCU.cjs.map +0 -1
- package/dist/tsup/chunk-HLLF4B4Q.js.map +0 -1
- package/dist/tsup/chunk-NFU2BBT5.js.map +0 -1
- package/dist/tsup/chunk-TQ62L3X7.js.map +0 -1
- package/dist/tsup/chunk-VO7ZRVVD.cjs.map +0 -1
- package/dist/tsup/chunk-WHBPJNGW.cjs.map +0 -1
- package/dist/tsup/chunk-XJQHKJ4P.js.map +0 -1
- /package/dist/tsup/{chunk-H26RP6GD.js.map → chunk-CKA54YQN.js.map} +0 -0
- /package/dist/tsup/{chunk-QK72M5JB.js.map → chunk-PG3K2LI7.js.map} +0 -0
- /package/dist/tsup/{chunk-LV2S3OU3.js.map → chunk-WNGOBAA7.js.map} +0 -0
- /package/dist/tsup/{chunk-3F2YSRJL.js.map → chunk-YW6Y6VNE.js.map} +0 -0
|
@@ -142,10 +142,10 @@ export class FileSystemManagerDriver implements ManagerDriver {
|
|
|
142
142
|
encoding: Encoding,
|
|
143
143
|
params: unknown,
|
|
144
144
|
): Promise<UniversalWebSocket> {
|
|
145
|
-
// TODO:
|
|
146
|
-
|
|
147
145
|
// Handle raw WebSocket paths
|
|
148
|
-
|
|
146
|
+
const pathOnly = path.split("?")[0];
|
|
147
|
+
const normalizedPath = pathOnly.startsWith("/") ? pathOnly : `/${pathOnly}`;
|
|
148
|
+
if (normalizedPath === PATH_CONNECT_WEBSOCKET) {
|
|
149
149
|
// Handle standard connect
|
|
150
150
|
const wsHandler = await handleWebSocketConnect(
|
|
151
151
|
undefined,
|
|
@@ -158,16 +158,14 @@ export class FileSystemManagerDriver implements ManagerDriver {
|
|
|
158
158
|
);
|
|
159
159
|
return new InlineWebSocketAdapter2(wsHandler);
|
|
160
160
|
} else if (
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
normalizedPath.startsWith(PATH_RAW_WEBSOCKET_PREFIX) ||
|
|
162
|
+
normalizedPath === "/raw/websocket"
|
|
163
163
|
) {
|
|
164
164
|
// Handle websocket proxy
|
|
165
|
-
//
|
|
166
|
-
const normalizedPath =
|
|
167
|
-
path === "/raw/websocket" ? "/raw/websocket/" : path;
|
|
165
|
+
// Use the full path with query parameters
|
|
168
166
|
const wsHandler = await handleRawWebSocketHandler(
|
|
169
167
|
undefined,
|
|
170
|
-
|
|
168
|
+
path,
|
|
171
169
|
this.#actorDriver,
|
|
172
170
|
actorId,
|
|
173
171
|
undefined,
|
|
@@ -194,13 +192,14 @@ export class FileSystemManagerDriver implements ManagerDriver {
|
|
|
194
192
|
actorId: string,
|
|
195
193
|
encoding: Encoding,
|
|
196
194
|
connParams: unknown,
|
|
197
|
-
authData: unknown,
|
|
198
195
|
): Promise<Response> {
|
|
199
196
|
const upgradeWebSocket = this.#runConfig.getUpgradeWebSocket?.();
|
|
200
197
|
invariant(upgradeWebSocket, "missing getUpgradeWebSocket");
|
|
201
198
|
|
|
202
199
|
// Handle raw WebSocket paths
|
|
203
|
-
|
|
200
|
+
const pathOnly = path.split("?")[0];
|
|
201
|
+
const normalizedPath = pathOnly.startsWith("/") ? pathOnly : `/${pathOnly}`;
|
|
202
|
+
if (normalizedPath === PATH_CONNECT_WEBSOCKET) {
|
|
204
203
|
// Handle standard connect
|
|
205
204
|
const wsHandler = await handleWebSocketConnect(
|
|
206
205
|
c.req.raw,
|
|
@@ -209,26 +208,22 @@ export class FileSystemManagerDriver implements ManagerDriver {
|
|
|
209
208
|
actorId,
|
|
210
209
|
encoding,
|
|
211
210
|
connParams,
|
|
212
|
-
|
|
211
|
+
undefined,
|
|
213
212
|
);
|
|
214
|
-
|
|
215
213
|
return upgradeWebSocket(() => wsHandler)(c, noopNext());
|
|
216
214
|
} else if (
|
|
217
|
-
|
|
218
|
-
|
|
215
|
+
normalizedPath.startsWith(PATH_RAW_WEBSOCKET_PREFIX) ||
|
|
216
|
+
normalizedPath === "/raw/websocket"
|
|
219
217
|
) {
|
|
220
218
|
// Handle websocket proxy
|
|
221
|
-
//
|
|
222
|
-
const normalizedPath =
|
|
223
|
-
path === "/raw/websocket" ? "/raw/websocket/" : path;
|
|
219
|
+
// Use the full path with query parameters
|
|
224
220
|
const wsHandler = await handleRawWebSocketHandler(
|
|
225
221
|
c.req.raw,
|
|
226
|
-
|
|
222
|
+
path,
|
|
227
223
|
this.#actorDriver,
|
|
228
224
|
actorId,
|
|
229
|
-
|
|
225
|
+
undefined,
|
|
230
226
|
);
|
|
231
|
-
|
|
232
227
|
return upgradeWebSocket(() => wsHandler)(c, noopNext());
|
|
233
228
|
} else {
|
|
234
229
|
throw new Error(`Unreachable path: ${path}`);
|
package/src/manager/router.ts
CHANGED
|
@@ -3,6 +3,8 @@ import * as cbor from "cbor-x";
|
|
|
3
3
|
import { Hono } from "hono";
|
|
4
4
|
import { cors as corsMiddleware } from "hono/cors";
|
|
5
5
|
import { createMiddleware } from "hono/factory";
|
|
6
|
+
import type { WSContext } from "hono/ws";
|
|
7
|
+
import invariant from "invariant";
|
|
6
8
|
import { z } from "zod";
|
|
7
9
|
import {
|
|
8
10
|
ActorNotFound,
|
|
@@ -11,11 +13,18 @@ import {
|
|
|
11
13
|
Unsupported,
|
|
12
14
|
WebSocketsNotEnabled,
|
|
13
15
|
} from "@/actor/errors";
|
|
16
|
+
import type { Encoding } from "@/client/mod";
|
|
14
17
|
import {
|
|
15
18
|
handleRouteError,
|
|
16
19
|
handleRouteNotFound,
|
|
17
20
|
loggerMiddleware,
|
|
18
21
|
} from "@/common/router";
|
|
22
|
+
import { deconstructError, noopNext } from "@/common/utils";
|
|
23
|
+
import { HEADER_ACTOR_ID } from "@/driver-helpers/mod";
|
|
24
|
+
import type {
|
|
25
|
+
TestInlineDriverCallRequest,
|
|
26
|
+
TestInlineDriverCallResponse,
|
|
27
|
+
} from "@/driver-test-suite/test-inline-client-driver";
|
|
19
28
|
import { createManagerInspectorRouter } from "@/inspector/manager";
|
|
20
29
|
import { secureInspector } from "@/inspector/utils";
|
|
21
30
|
import {
|
|
@@ -32,8 +41,10 @@ import {
|
|
|
32
41
|
ActorsGetOrCreateByIdResponseSchema,
|
|
33
42
|
} from "@/manager-api/routes/actors-get-or-create-by-id";
|
|
34
43
|
import { RivetIdSchema } from "@/manager-api/routes/common";
|
|
44
|
+
import type { UniversalWebSocket, UpgradeWebSocketArgs } from "@/mod";
|
|
35
45
|
import type { RegistryConfig } from "@/registry/config";
|
|
36
46
|
import type { RunConfig } from "@/registry/run-config";
|
|
47
|
+
import { stringifyError } from "@/utils";
|
|
37
48
|
import type { ManagerDriver } from "./driver";
|
|
38
49
|
import { logger } from "./log";
|
|
39
50
|
|
|
@@ -137,9 +148,10 @@ export function createManagerRouter(
|
|
|
137
148
|
const proxyUrl = new URL(`http://actor${url.pathname}${url.search}`);
|
|
138
149
|
|
|
139
150
|
const proxyRequest = new Request(proxyUrl, {
|
|
140
|
-
method: c.req.method,
|
|
151
|
+
method: c.req.raw.method,
|
|
141
152
|
headers: proxyHeaders,
|
|
142
153
|
body: c.req.raw.body,
|
|
154
|
+
signal: c.req.raw.signal,
|
|
143
155
|
});
|
|
144
156
|
|
|
145
157
|
return await managerDriver.proxyRequest(c, proxyRequest, actorId);
|
|
@@ -288,8 +300,8 @@ export function createManagerRouter(
|
|
|
288
300
|
actor_id: actorOutput.actorId,
|
|
289
301
|
name: actorOutput.name,
|
|
290
302
|
key: actorOutput.key,
|
|
291
|
-
namespace_id: "", //
|
|
292
|
-
runner_name_selector: "", //
|
|
303
|
+
namespace_id: "default", // Assert default namespace
|
|
304
|
+
runner_name_selector: "rivetkit", // Assert rivetkit runner
|
|
293
305
|
create_ts: Date.now(), // Not available from driver
|
|
294
306
|
connectable_ts: null,
|
|
295
307
|
destroy_ts: null,
|
|
@@ -350,8 +362,8 @@ export function createManagerRouter(
|
|
|
350
362
|
actor_id: actorOutput.actorId,
|
|
351
363
|
name: actorOutput.name,
|
|
352
364
|
key: actorOutput.key,
|
|
353
|
-
namespace_id: "", //
|
|
354
|
-
runner_name_selector:
|
|
365
|
+
namespace_id: "default", // Assert default namespace
|
|
366
|
+
runner_name_selector: "rivetkit", // Assert rivetkit runner
|
|
355
367
|
create_ts: Date.now(),
|
|
356
368
|
connectable_ts: null,
|
|
357
369
|
destroy_ts: null,
|
|
@@ -387,6 +399,139 @@ export function createManagerRouter(
|
|
|
387
399
|
// });
|
|
388
400
|
// }
|
|
389
401
|
|
|
402
|
+
if (registryConfig.test.enabled) {
|
|
403
|
+
// Add HTTP endpoint to test the inline client
|
|
404
|
+
//
|
|
405
|
+
// We have to do this in a router since this needs to run in the same server as the RivetKit registry. Some test contexts to not run in the same server.
|
|
406
|
+
router.post(".test/inline-driver/call", async (c) => {
|
|
407
|
+
// TODO: use openapi instead
|
|
408
|
+
const buffer = await c.req.arrayBuffer();
|
|
409
|
+
const { encoding, transport, method, args }: TestInlineDriverCallRequest =
|
|
410
|
+
cbor.decode(new Uint8Array(buffer));
|
|
411
|
+
|
|
412
|
+
logger().debug({
|
|
413
|
+
msg: "received inline request",
|
|
414
|
+
encoding,
|
|
415
|
+
transport,
|
|
416
|
+
method,
|
|
417
|
+
args,
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Forward inline driver request
|
|
421
|
+
let response: TestInlineDriverCallResponse<unknown>;
|
|
422
|
+
try {
|
|
423
|
+
const output = await ((managerDriver as any)[method] as any)(...args);
|
|
424
|
+
response = { ok: output };
|
|
425
|
+
} catch (rawErr) {
|
|
426
|
+
const err = deconstructError(rawErr, logger(), {}, true);
|
|
427
|
+
response = { err };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return c.body(cbor.encode(response));
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
router.get(".test/inline-driver/connect-websocket/*", async (c) => {
|
|
434
|
+
const upgradeWebSocket = runConfig.getUpgradeWebSocket?.();
|
|
435
|
+
invariant(upgradeWebSocket, "websockets not supported on this platform");
|
|
436
|
+
|
|
437
|
+
return upgradeWebSocket(async (c: any) => {
|
|
438
|
+
const {
|
|
439
|
+
path,
|
|
440
|
+
actorId,
|
|
441
|
+
params: paramsRaw,
|
|
442
|
+
encodingKind,
|
|
443
|
+
} = c.req.query() as {
|
|
444
|
+
path: string;
|
|
445
|
+
actorId: string;
|
|
446
|
+
params?: string;
|
|
447
|
+
encodingKind: Encoding;
|
|
448
|
+
};
|
|
449
|
+
const params =
|
|
450
|
+
paramsRaw !== undefined ? JSON.parse(paramsRaw) : undefined;
|
|
451
|
+
|
|
452
|
+
logger().debug({
|
|
453
|
+
msg: "received test inline driver websocket",
|
|
454
|
+
actorId,
|
|
455
|
+
params,
|
|
456
|
+
encodingKind,
|
|
457
|
+
path: path,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Connect to the actor using the inline client driver - this returns a Promise<WebSocket>
|
|
461
|
+
const clientWsPromise = managerDriver.openWebSocket(
|
|
462
|
+
path,
|
|
463
|
+
actorId,
|
|
464
|
+
encodingKind,
|
|
465
|
+
params,
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
return await createTestWebSocketProxy(clientWsPromise, "standard");
|
|
469
|
+
})(c, noopNext());
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
router.all(".test/inline-driver/send-request/*", async (c) => {
|
|
473
|
+
// Extract parameters from headers
|
|
474
|
+
const actorId = c.req.header(HEADER_ACTOR_ID);
|
|
475
|
+
|
|
476
|
+
if (!actorId) {
|
|
477
|
+
return c.text("Missing required headers", 400);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Extract the path after /send-request/
|
|
481
|
+
const pathOnly =
|
|
482
|
+
c.req.path.split("/.test/inline-driver/send-request/")[1] || "";
|
|
483
|
+
|
|
484
|
+
// Include query string
|
|
485
|
+
const url = new URL(c.req.url);
|
|
486
|
+
const pathWithQuery = pathOnly + url.search;
|
|
487
|
+
|
|
488
|
+
logger().debug({
|
|
489
|
+
msg: "received test inline driver raw http",
|
|
490
|
+
actorId,
|
|
491
|
+
path: pathWithQuery,
|
|
492
|
+
method: c.req.method,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
try {
|
|
496
|
+
// Forward the request using the inline client driver
|
|
497
|
+
const response = await managerDriver.sendRequest(
|
|
498
|
+
actorId,
|
|
499
|
+
new Request(`http://actor/${pathWithQuery}`, {
|
|
500
|
+
method: c.req.method,
|
|
501
|
+
headers: c.req.raw.headers,
|
|
502
|
+
body: c.req.raw.body,
|
|
503
|
+
}),
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
// Return the response directly
|
|
507
|
+
return response;
|
|
508
|
+
} catch (error) {
|
|
509
|
+
logger().error({
|
|
510
|
+
msg: "error in test inline raw http",
|
|
511
|
+
error: stringifyError(error),
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// Return error response
|
|
515
|
+
const err = deconstructError(error, logger(), {}, true);
|
|
516
|
+
return c.json(
|
|
517
|
+
{
|
|
518
|
+
error: {
|
|
519
|
+
code: err.code,
|
|
520
|
+
message: err.message,
|
|
521
|
+
metadata: err.metadata,
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
err.statusCode,
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
managerDriver.modifyManagerRouter?.(
|
|
531
|
+
registryConfig,
|
|
532
|
+
router as unknown as Hono,
|
|
533
|
+
);
|
|
534
|
+
|
|
390
535
|
if (runConfig.inspector?.enabled) {
|
|
391
536
|
if (!managerDriver.inspector) {
|
|
392
537
|
throw new Unsupported("inspector");
|
|
@@ -410,3 +555,226 @@ export function createManagerRouter(
|
|
|
410
555
|
|
|
411
556
|
return { router: router as Hono, openapi: router };
|
|
412
557
|
}
|
|
558
|
+
/**
|
|
559
|
+
* Creates a WebSocket proxy for test endpoints that forwards messages between server and client WebSockets
|
|
560
|
+
*/
|
|
561
|
+
async function createTestWebSocketProxy(
|
|
562
|
+
clientWsPromise: Promise<UniversalWebSocket>,
|
|
563
|
+
connectionType: string,
|
|
564
|
+
): Promise<UpgradeWebSocketArgs> {
|
|
565
|
+
// Store a reference to the resolved WebSocket
|
|
566
|
+
let clientWs: UniversalWebSocket | null = null;
|
|
567
|
+
try {
|
|
568
|
+
// Resolve the client WebSocket promise
|
|
569
|
+
logger().debug({ msg: "awaiting client websocket promise" });
|
|
570
|
+
const ws = await clientWsPromise;
|
|
571
|
+
clientWs = ws;
|
|
572
|
+
logger().debug({
|
|
573
|
+
msg: "client websocket promise resolved",
|
|
574
|
+
constructor: ws?.constructor.name,
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// Wait for ws to open
|
|
578
|
+
await new Promise<void>((resolve, reject) => {
|
|
579
|
+
const onOpen = () => {
|
|
580
|
+
logger().debug({ msg: "test websocket connection opened" });
|
|
581
|
+
resolve();
|
|
582
|
+
};
|
|
583
|
+
const onError = (error: any) => {
|
|
584
|
+
logger().error({ msg: "test websocket connection failed", error });
|
|
585
|
+
reject(
|
|
586
|
+
new Error(`Failed to open WebSocket: ${error.message || error}`),
|
|
587
|
+
);
|
|
588
|
+
};
|
|
589
|
+
ws.addEventListener("open", onOpen);
|
|
590
|
+
ws.addEventListener("error", onError);
|
|
591
|
+
});
|
|
592
|
+
} catch (error) {
|
|
593
|
+
logger().error({
|
|
594
|
+
msg: `failed to establish client ${connectionType} websocket connection`,
|
|
595
|
+
error,
|
|
596
|
+
});
|
|
597
|
+
return {
|
|
598
|
+
onOpen: (_evt, serverWs) => {
|
|
599
|
+
serverWs.close(1011, "Failed to establish connection");
|
|
600
|
+
},
|
|
601
|
+
onMessage: () => {},
|
|
602
|
+
onError: () => {},
|
|
603
|
+
onClose: () => {},
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Create WebSocket proxy handlers to relay messages between client and server
|
|
608
|
+
return {
|
|
609
|
+
onOpen: (_evt: any, serverWs: WSContext) => {
|
|
610
|
+
logger().debug({
|
|
611
|
+
msg: `test ${connectionType} websocket connection opened`,
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// Check WebSocket type
|
|
615
|
+
logger().debug({
|
|
616
|
+
msg: "clientWs info",
|
|
617
|
+
constructor: clientWs.constructor.name,
|
|
618
|
+
hasAddEventListener: typeof clientWs.addEventListener === "function",
|
|
619
|
+
readyState: clientWs.readyState,
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// Add message handler to forward messages from client to server
|
|
623
|
+
clientWs.addEventListener("message", (clientEvt: MessageEvent) => {
|
|
624
|
+
logger().debug({
|
|
625
|
+
msg: `test ${connectionType} websocket connection message from client`,
|
|
626
|
+
dataType: typeof clientEvt.data,
|
|
627
|
+
isBlob: clientEvt.data instanceof Blob,
|
|
628
|
+
isArrayBuffer: clientEvt.data instanceof ArrayBuffer,
|
|
629
|
+
dataConstructor: clientEvt.data?.constructor?.name,
|
|
630
|
+
dataStr:
|
|
631
|
+
typeof clientEvt.data === "string"
|
|
632
|
+
? clientEvt.data.substring(0, 100)
|
|
633
|
+
: undefined,
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
if (serverWs.readyState === 1) {
|
|
637
|
+
// OPEN
|
|
638
|
+
// Handle Blob data
|
|
639
|
+
if (clientEvt.data instanceof Blob) {
|
|
640
|
+
clientEvt.data
|
|
641
|
+
.arrayBuffer()
|
|
642
|
+
.then((buffer) => {
|
|
643
|
+
logger().debug({
|
|
644
|
+
msg: "converted client blob to arraybuffer, sending to server",
|
|
645
|
+
bufferSize: buffer.byteLength,
|
|
646
|
+
});
|
|
647
|
+
serverWs.send(buffer as any);
|
|
648
|
+
})
|
|
649
|
+
.catch((error) => {
|
|
650
|
+
logger().error({
|
|
651
|
+
msg: "failed to convert blob to arraybuffer",
|
|
652
|
+
error,
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
} else {
|
|
656
|
+
logger().debug({
|
|
657
|
+
msg: "sending client data directly to server",
|
|
658
|
+
dataType: typeof clientEvt.data,
|
|
659
|
+
dataLength:
|
|
660
|
+
typeof clientEvt.data === "string"
|
|
661
|
+
? clientEvt.data.length
|
|
662
|
+
: undefined,
|
|
663
|
+
});
|
|
664
|
+
serverWs.send(clientEvt.data as any);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// Add close handler to close server when client closes
|
|
670
|
+
clientWs.addEventListener("close", (clientEvt: any) => {
|
|
671
|
+
logger().debug({
|
|
672
|
+
msg: `test ${connectionType} websocket connection closed`,
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
if (serverWs.readyState !== 3) {
|
|
676
|
+
// Not CLOSED
|
|
677
|
+
serverWs.close(clientEvt.code, clientEvt.reason);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// Add error handler
|
|
682
|
+
clientWs.addEventListener("error", () => {
|
|
683
|
+
logger().debug({
|
|
684
|
+
msg: `test ${connectionType} websocket connection error`,
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
if (serverWs.readyState !== 3) {
|
|
688
|
+
// Not CLOSED
|
|
689
|
+
serverWs.close(1011, "Error in client websocket");
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
},
|
|
693
|
+
onMessage: (evt: { data: any }) => {
|
|
694
|
+
logger().debug({
|
|
695
|
+
msg: "received message from server",
|
|
696
|
+
dataType: typeof evt.data,
|
|
697
|
+
isBlob: evt.data instanceof Blob,
|
|
698
|
+
isArrayBuffer: evt.data instanceof ArrayBuffer,
|
|
699
|
+
dataConstructor: evt.data?.constructor?.name,
|
|
700
|
+
dataStr:
|
|
701
|
+
typeof evt.data === "string" ? evt.data.substring(0, 100) : undefined,
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
// Forward messages from server websocket to client websocket
|
|
705
|
+
if (clientWs.readyState === 1) {
|
|
706
|
+
// OPEN
|
|
707
|
+
// Handle Blob data
|
|
708
|
+
if (evt.data instanceof Blob) {
|
|
709
|
+
evt.data
|
|
710
|
+
.arrayBuffer()
|
|
711
|
+
.then((buffer) => {
|
|
712
|
+
logger().debug({
|
|
713
|
+
msg: "converted blob to arraybuffer, sending",
|
|
714
|
+
bufferSize: buffer.byteLength,
|
|
715
|
+
});
|
|
716
|
+
clientWs.send(buffer);
|
|
717
|
+
})
|
|
718
|
+
.catch((error) => {
|
|
719
|
+
logger().error({
|
|
720
|
+
msg: "failed to convert blob to arraybuffer",
|
|
721
|
+
error,
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
} else {
|
|
725
|
+
logger().debug({
|
|
726
|
+
msg: "sending data directly",
|
|
727
|
+
dataType: typeof evt.data,
|
|
728
|
+
dataLength:
|
|
729
|
+
typeof evt.data === "string" ? evt.data.length : undefined,
|
|
730
|
+
});
|
|
731
|
+
clientWs.send(evt.data);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
onClose: (
|
|
736
|
+
event: {
|
|
737
|
+
wasClean: boolean;
|
|
738
|
+
code: number;
|
|
739
|
+
reason: string;
|
|
740
|
+
},
|
|
741
|
+
serverWs: WSContext,
|
|
742
|
+
) => {
|
|
743
|
+
logger().debug({
|
|
744
|
+
msg: `server ${connectionType} websocket closed`,
|
|
745
|
+
wasClean: event.wasClean,
|
|
746
|
+
code: event.code,
|
|
747
|
+
reason: event.reason,
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
// HACK: Close socket in order to fix bug with Cloudflare leaving WS in closing state
|
|
751
|
+
// https://github.com/cloudflare/workerd/issues/2569
|
|
752
|
+
serverWs.close(1000, "hack_force_close");
|
|
753
|
+
|
|
754
|
+
// Close the client websocket when the server websocket closes
|
|
755
|
+
if (
|
|
756
|
+
clientWs &&
|
|
757
|
+
clientWs.readyState !== clientWs.CLOSED &&
|
|
758
|
+
clientWs.readyState !== clientWs.CLOSING
|
|
759
|
+
) {
|
|
760
|
+
// Don't pass code/message since this may affect how close events are triggered
|
|
761
|
+
clientWs.close(1000, event.reason);
|
|
762
|
+
}
|
|
763
|
+
},
|
|
764
|
+
onError: (error: unknown) => {
|
|
765
|
+
logger().error({
|
|
766
|
+
msg: `error in server ${connectionType} websocket`,
|
|
767
|
+
error,
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// Close the client websocket on error
|
|
771
|
+
if (
|
|
772
|
+
clientWs &&
|
|
773
|
+
clientWs.readyState !== clientWs.CLOSED &&
|
|
774
|
+
clientWs.readyState !== clientWs.CLOSING
|
|
775
|
+
) {
|
|
776
|
+
clientWs.close(1011, "Error in server websocket");
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
};
|
|
780
|
+
}
|
package/src/registry/mod.ts
CHANGED
|
@@ -21,12 +21,10 @@ import {
|
|
|
21
21
|
import { crossPlatformServe } from "./serve";
|
|
22
22
|
|
|
23
23
|
interface ServerOutput<A extends Registry<any>> {
|
|
24
|
-
|
|
25
|
-
driver: DriverConfig;
|
|
24
|
+
/** Client to communicate with the actors. */
|
|
26
25
|
client: Client<A>;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
serve: (hono?: Hono) => void;
|
|
26
|
+
/** Fetch handler to manually route requests to the Rivet manager API. */
|
|
27
|
+
fetch: (request: Request, ...args: any) => Response | Promise<Response>;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
export class Registry<A extends RegistryActors> {
|
|
@@ -43,7 +41,7 @@ export class Registry<A extends RegistryActors> {
|
|
|
43
41
|
/**
|
|
44
42
|
* Runs the registry for a server.
|
|
45
43
|
*/
|
|
46
|
-
public
|
|
44
|
+
public start(inputConfig?: RunConfigInput): ServerOutput<this> {
|
|
47
45
|
const config = RunConfigSchema.parse(inputConfig);
|
|
48
46
|
|
|
49
47
|
// Configure logger
|
|
@@ -62,6 +60,13 @@ export class Registry<A extends RegistryActors> {
|
|
|
62
60
|
// TODO: Find cleaner way of disabling by default
|
|
63
61
|
if (driver.name === "engine") {
|
|
64
62
|
config.inspector.enabled = false;
|
|
63
|
+
config.disableServer = true;
|
|
64
|
+
}
|
|
65
|
+
if (driver.name === "cloudflare-workers") {
|
|
66
|
+
config.inspector.enabled = false;
|
|
67
|
+
config.disableServer = true;
|
|
68
|
+
config.disableActorDriver = true;
|
|
69
|
+
config.noWelcome = true;
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
// Configure getUpgradeWebSocket lazily so we can assign it in crossPlatformServe
|
|
@@ -109,41 +114,32 @@ export class Registry<A extends RegistryActors> {
|
|
|
109
114
|
console.log();
|
|
110
115
|
}
|
|
111
116
|
|
|
117
|
+
// HACK: We need to find a better way to let the driver itself decide when to start the actor driver
|
|
112
118
|
// Create runner
|
|
113
119
|
//
|
|
114
120
|
// Even though we do not use the return value, this is required to start the code that will handle incoming actors
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
if (!config.disableActorDriver) {
|
|
122
|
+
const _actorDriver = driver.actor(
|
|
123
|
+
this.#config,
|
|
124
|
+
config,
|
|
125
|
+
managerDriver,
|
|
126
|
+
client,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Start server
|
|
131
|
+
if (!config.disableServer) {
|
|
132
|
+
(async () => {
|
|
133
|
+
const out = await crossPlatformServe(hono, undefined);
|
|
134
|
+
upgradeWebSocket = out.upgradeWebSocket;
|
|
135
|
+
})();
|
|
136
|
+
}
|
|
121
137
|
|
|
122
138
|
return {
|
|
123
|
-
config,
|
|
124
|
-
driver,
|
|
125
139
|
client,
|
|
126
|
-
hono,
|
|
127
|
-
handler: async (req: Request) => await hono.fetch(req),
|
|
128
|
-
serve: async (app) => {
|
|
129
|
-
const out = await crossPlatformServe(hono, app);
|
|
130
|
-
upgradeWebSocket = out.upgradeWebSocket;
|
|
131
|
-
},
|
|
140
|
+
fetch: hono.fetch.bind(hono),
|
|
132
141
|
};
|
|
133
142
|
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Runs the registry as a standalone server.
|
|
137
|
-
*/
|
|
138
|
-
public async runServer(inputConfig?: RunConfigInput) {
|
|
139
|
-
const { driver, serve } = this.createServer(inputConfig);
|
|
140
|
-
|
|
141
|
-
// TODO: FInd better way of doing this
|
|
142
|
-
// Don't run server by default
|
|
143
|
-
if (driver.name !== "engine") {
|
|
144
|
-
serve();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
143
|
}
|
|
148
144
|
|
|
149
145
|
export function setup<A extends RegistryActors>(
|
|
@@ -29,16 +29,31 @@ export const RunConfigSchema = ClientConfigSchema.extend({
|
|
|
29
29
|
|
|
30
30
|
inspector: InspectorConfigSchema,
|
|
31
31
|
|
|
32
|
+
/** @experimental */
|
|
33
|
+
disableServer: z.boolean().optional().default(false),
|
|
34
|
+
|
|
35
|
+
/** @experimental */
|
|
36
|
+
disableActorDriver: z.boolean().optional().default(false),
|
|
37
|
+
|
|
32
38
|
/**
|
|
39
|
+
* @experimental
|
|
40
|
+
*
|
|
33
41
|
* Base path for the router. This is used to prefix all routes.
|
|
34
42
|
* For example, if the base path is `/api`, then the route `/actors` will be
|
|
35
43
|
* available at `/api/actors`.
|
|
36
44
|
*/
|
|
37
45
|
basePath: z.string().optional().default("/"),
|
|
38
46
|
|
|
39
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* @experimental
|
|
49
|
+
*
|
|
50
|
+
* Disable welcome message.
|
|
51
|
+
* */
|
|
40
52
|
noWelcome: z.boolean().optional().default(false),
|
|
41
53
|
|
|
54
|
+
/**
|
|
55
|
+
* @experimental
|
|
56
|
+
* */
|
|
42
57
|
logging: z
|
|
43
58
|
.object({
|
|
44
59
|
baseLogger: z.custom<Logger>().optional(),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ClientConfig } from "@/client/config";
|
|
2
|
+
import { combineUrlPath } from "@/utils";
|
|
2
3
|
import { getEndpoint } from "./api-utils";
|
|
3
4
|
|
|
4
5
|
export async function sendHttpRequestToActor(
|
|
@@ -9,7 +10,7 @@ export async function sendHttpRequestToActor(
|
|
|
9
10
|
// Route through guard port
|
|
10
11
|
const url = new URL(actorRequest.url);
|
|
11
12
|
const endpoint = getEndpoint(runConfig);
|
|
12
|
-
const guardUrl =
|
|
13
|
+
const guardUrl = combineUrlPath(endpoint, url.pathname + url.search);
|
|
13
14
|
|
|
14
15
|
// Handle body properly based on method and presence
|
|
15
16
|
let bodyToSend: ArrayBuffer | null = null;
|
|
@@ -43,6 +44,7 @@ export async function sendHttpRequestToActor(
|
|
|
43
44
|
method: actorRequest.method,
|
|
44
45
|
headers: guardHeaders,
|
|
45
46
|
body: bodyToSend,
|
|
47
|
+
signal: actorRequest.signal,
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
return mutableResponse(await fetch(guardRequest));
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import type { ClientConfig } from "@/client/config";
|
|
7
7
|
import { importWebSocket } from "@/common/websocket";
|
|
8
8
|
import type { Encoding, UniversalWebSocket } from "@/mod";
|
|
9
|
+
import { combineUrlPath } from "@/utils";
|
|
9
10
|
import { getEndpoint } from "./api-utils";
|
|
10
11
|
import { logger } from "./log";
|
|
11
12
|
|
|
@@ -20,7 +21,7 @@ export async function openWebSocketToActor(
|
|
|
20
21
|
|
|
21
22
|
// WebSocket connections go through guard
|
|
22
23
|
const endpoint = getEndpoint(runConfig);
|
|
23
|
-
const guardUrl =
|
|
24
|
+
const guardUrl = combineUrlPath(endpoint, path);
|
|
24
25
|
|
|
25
26
|
logger().debug({
|
|
26
27
|
msg: "opening websocket to actor via guard",
|