rivetkit 2.0.6 → 2.0.7-rc.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/schemas/actor-persist/v1.ts +0 -6
- package/dist/tsup/actor-router-consts-B3Lu87yJ.d.cts +28 -0
- package/dist/tsup/actor-router-consts-B3Lu87yJ.d.ts +28 -0
- package/dist/tsup/{chunk-MRRT2CZD.cjs → chunk-3MBP4WNC.cjs} +7 -7
- package/dist/tsup/{chunk-MRRT2CZD.cjs.map → chunk-3MBP4WNC.cjs.map} +1 -1
- package/dist/tsup/{chunk-TWGATZ3X.cjs → chunk-3Y45CIF4.cjs} +922 -872
- package/dist/tsup/chunk-3Y45CIF4.cjs.map +1 -0
- package/dist/tsup/chunk-4GP7BZSR.js +102 -0
- package/dist/tsup/chunk-4GP7BZSR.js.map +1 -0
- package/dist/tsup/{chunk-UFWAK3X2.cjs → chunk-5ZOHIKWG.cjs} +660 -385
- package/dist/tsup/chunk-5ZOHIKWG.cjs.map +1 -0
- package/dist/tsup/{chunk-5JBFVV4C.cjs → chunk-6EUWRXLT.cjs} +21 -7
- package/dist/tsup/chunk-6EUWRXLT.cjs.map +1 -0
- package/dist/tsup/{chunk-UTI5NCES.cjs → chunk-6OVKCDSH.cjs} +6 -6
- package/dist/tsup/{chunk-UTI5NCES.cjs.map → chunk-6OVKCDSH.cjs.map} +1 -1
- package/dist/tsup/{chunk-VPV4MWXR.js → chunk-7N56ZUC7.js} +3 -3
- package/dist/tsup/{chunk-DIAYNQTE.cjs → chunk-B3TLRM4Q.cjs} +12 -12
- package/dist/tsup/{chunk-DIAYNQTE.cjs.map → chunk-B3TLRM4Q.cjs.map} +1 -1
- package/dist/tsup/{chunk-4CKHQRXG.js → chunk-BW5DPM6Z.js} +515 -240
- package/dist/tsup/chunk-BW5DPM6Z.js.map +1 -0
- package/dist/tsup/{chunk-NTCUGYSD.cjs → chunk-DFS77KAA.cjs} +34 -31
- package/dist/tsup/chunk-DFS77KAA.cjs.map +1 -0
- package/dist/tsup/{chunk-VCEHU56K.js → chunk-E4UVJKSV.js} +2 -2
- package/dist/tsup/chunk-G4ABMAQY.cjs +102 -0
- package/dist/tsup/chunk-G4ABMAQY.cjs.map +1 -0
- package/dist/tsup/{chunk-ZYLTS2EM.js → chunk-GZVBFXBI.js} +2 -2
- package/dist/tsup/{chunk-W6LN7AF5.js → chunk-HPT3I7UU.js} +866 -816
- package/dist/tsup/chunk-HPT3I7UU.js.map +1 -0
- package/dist/tsup/{chunk-7OUKNSTU.js → chunk-JD54PXWP.js} +17 -14
- package/dist/tsup/chunk-JD54PXWP.js.map +1 -0
- package/dist/tsup/{chunk-KG3C7MKR.cjs → chunk-K4ENQCC4.cjs} +3 -3
- package/dist/tsup/{chunk-KG3C7MKR.cjs.map → chunk-K4ENQCC4.cjs.map} +1 -1
- package/dist/tsup/{chunk-WC2PSJWN.js → chunk-PUSQNDJG.js} +2 -2
- package/dist/tsup/{chunk-RGQR2J7S.js → chunk-RVP5RUSC.js} +20 -6
- package/dist/tsup/chunk-RVP5RUSC.js.map +1 -0
- package/dist/tsup/{chunk-TCUI5JFE.cjs → chunk-SAZCNSVY.cjs} +45 -18
- package/dist/tsup/chunk-SAZCNSVY.cjs.map +1 -0
- package/dist/tsup/{chunk-G75SVQON.js → chunk-SBKRVQS2.js} +9 -5
- package/dist/tsup/chunk-SBKRVQS2.js.map +1 -0
- package/dist/tsup/{chunk-6P6RA47N.cjs → chunk-TZGUSEIJ.cjs} +14 -10
- package/dist/tsup/chunk-TZGUSEIJ.cjs.map +1 -0
- package/dist/tsup/{chunk-2K3JMDAN.js → chunk-YQ4XQYPM.js} +40 -13
- package/dist/tsup/chunk-YQ4XQYPM.js.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +7 -8
- package/dist/tsup/client/mod.d.ts +7 -8
- package/dist/tsup/client/mod.js +8 -8
- 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/{connection-BLemxi4f.d.ts → conn-DCSQgIlw.d.ts} +1605 -1353
- package/dist/tsup/{connection-CpDIydXf.d.cts → conn-DdzHTm2E.d.cts} +1605 -1353
- package/dist/tsup/driver-helpers/mod.cjs +31 -5
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +7 -8
- package/dist/tsup/driver-helpers/mod.d.ts +7 -8
- package/dist/tsup/driver-helpers/mod.js +33 -7
- package/dist/tsup/driver-test-suite/mod.cjs +317 -222
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +7 -7
- package/dist/tsup/driver-test-suite/mod.d.ts +7 -7
- package/dist/tsup/driver-test-suite/mod.js +582 -487
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +16 -6
- package/dist/tsup/inspector/mod.cjs.map +1 -1
- package/dist/tsup/inspector/mod.d.cts +34 -7
- package/dist/tsup/inspector/mod.d.ts +34 -7
- package/dist/tsup/inspector/mod.js +17 -7
- package/dist/tsup/mod.cjs +10 -20
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +9 -7
- package/dist/tsup/mod.d.ts +9 -7
- package/dist/tsup/mod.js +9 -19
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.d.cts +6 -7
- package/dist/tsup/test/mod.d.ts +6 -7
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +4 -2
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.d.cts +11 -1
- package/dist/tsup/utils.d.ts +11 -1
- package/dist/tsup/utils.js +3 -1
- package/package.json +8 -4
- package/src/actor/action.ts +1 -1
- package/src/actor/config.ts +1 -1
- package/src/actor/conn-drivers.ts +205 -0
- package/src/actor/conn-socket.ts +6 -0
- package/src/actor/{connection.ts → conn.ts} +78 -84
- package/src/actor/context.ts +1 -1
- package/src/actor/driver.ts +4 -43
- package/src/actor/instance.ts +162 -86
- package/src/actor/mod.ts +1 -11
- package/src/actor/persisted.ts +2 -5
- package/src/actor/protocol/old.ts +1 -1
- package/src/actor/router-endpoints.ts +142 -106
- package/src/actor/router.ts +81 -45
- package/src/actor/utils.ts +5 -1
- package/src/client/actor-conn.ts +154 -23
- package/src/client/client.ts +1 -1
- package/src/client/config.ts +7 -0
- package/src/common/actor-router-consts.ts +29 -8
- package/src/common/router.ts +2 -1
- package/src/common/versioned-data.ts +5 -5
- package/src/driver-helpers/mod.ts +14 -1
- package/src/driver-test-suite/mod.ts +11 -2
- package/src/driver-test-suite/test-inline-client-driver.ts +36 -18
- package/src/driver-test-suite/tests/actor-conn-state.ts +66 -22
- package/src/driver-test-suite/tests/actor-conn.ts +65 -126
- package/src/driver-test-suite/tests/actor-reconnect.ts +160 -0
- package/src/driver-test-suite/tests/actor-sleep.ts +0 -1
- package/src/driver-test-suite/tests/raw-websocket.ts +0 -35
- package/src/driver-test-suite/utils.ts +3 -3
- package/src/drivers/default.ts +8 -7
- package/src/drivers/engine/actor-driver.ts +53 -31
- package/src/drivers/engine/config.ts +4 -0
- package/src/drivers/file-system/actor.ts +0 -6
- package/src/drivers/file-system/global-state.ts +3 -14
- package/src/drivers/file-system/manager.ts +12 -8
- package/src/inspector/actor.ts +4 -3
- package/src/inspector/config.ts +10 -1
- package/src/inspector/mod.ts +1 -0
- package/src/inspector/utils.ts +23 -4
- package/src/manager/driver.ts +11 -1
- package/src/manager/gateway.ts +407 -0
- package/src/manager/router.ts +269 -468
- package/src/manager-api/actors.ts +61 -0
- package/src/manager-api/common.ts +4 -0
- package/src/mod.ts +1 -1
- package/src/registry/mod.ts +119 -10
- package/src/remote-manager-driver/actor-http-client.ts +30 -19
- package/src/remote-manager-driver/actor-websocket-client.ts +43 -16
- package/src/remote-manager-driver/api-endpoints.ts +19 -21
- package/src/remote-manager-driver/api-utils.ts +10 -1
- package/src/remote-manager-driver/mod.ts +51 -48
- package/src/remote-manager-driver/ws-proxy.ts +2 -9
- package/src/test/mod.ts +6 -2
- package/src/utils.ts +21 -2
- package/dist/tsup/actor-router-consts-BK6arfy8.d.cts +0 -17
- package/dist/tsup/actor-router-consts-BK6arfy8.d.ts +0 -17
- package/dist/tsup/chunk-2K3JMDAN.js.map +0 -1
- package/dist/tsup/chunk-42I3OZ3Q.js +0 -15
- package/dist/tsup/chunk-42I3OZ3Q.js.map +0 -1
- package/dist/tsup/chunk-4CKHQRXG.js.map +0 -1
- package/dist/tsup/chunk-5JBFVV4C.cjs.map +0 -1
- package/dist/tsup/chunk-6P6RA47N.cjs.map +0 -1
- package/dist/tsup/chunk-7OUKNSTU.js.map +0 -1
- package/dist/tsup/chunk-G75SVQON.js.map +0 -1
- package/dist/tsup/chunk-KUPQZYUQ.cjs +0 -15
- package/dist/tsup/chunk-KUPQZYUQ.cjs.map +0 -1
- package/dist/tsup/chunk-NTCUGYSD.cjs.map +0 -1
- package/dist/tsup/chunk-RGQR2J7S.js.map +0 -1
- package/dist/tsup/chunk-TCUI5JFE.cjs.map +0 -1
- package/dist/tsup/chunk-TWGATZ3X.cjs.map +0 -1
- package/dist/tsup/chunk-UFWAK3X2.cjs.map +0 -1
- package/dist/tsup/chunk-W6LN7AF5.js.map +0 -1
- package/dist/tsup/common-CXCe7s6i.d.cts +0 -218
- package/dist/tsup/common-CXCe7s6i.d.ts +0 -218
- package/src/actor/generic-conn-driver.ts +0 -246
- package/src/common/fake-event-source.ts +0 -267
- package/src/manager-api/routes/actors-create.ts +0 -16
- package/src/manager-api/routes/actors-delete.ts +0 -4
- package/src/manager-api/routes/actors-get-by-id.ts +0 -7
- package/src/manager-api/routes/actors-get-or-create-by-id.ts +0 -29
- package/src/manager-api/routes/actors-get.ts +0 -7
- package/src/manager-api/routes/common.ts +0 -18
- /package/dist/tsup/{chunk-VPV4MWXR.js.map → chunk-7N56ZUC7.js.map} +0 -0
- /package/dist/tsup/{chunk-VCEHU56K.js.map → chunk-E4UVJKSV.js.map} +0 -0
- /package/dist/tsup/{chunk-ZYLTS2EM.js.map → chunk-GZVBFXBI.js.map} +0 -0
- /package/dist/tsup/{chunk-WC2PSJWN.js.map → chunk-PUSQNDJG.js.map} +0 -0
package/src/client/actor-conn.ts
CHANGED
|
@@ -32,7 +32,12 @@ import {
|
|
|
32
32
|
encodingIsBinary,
|
|
33
33
|
serializeWithEncoding,
|
|
34
34
|
} from "@/serde";
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
bufferToArrayBuffer,
|
|
37
|
+
getEnvUniversal,
|
|
38
|
+
httpUserAgent,
|
|
39
|
+
promiseWithResolvers,
|
|
40
|
+
} from "@/utils";
|
|
36
41
|
import type { ActorDefinitionActions } from "./actor-common";
|
|
37
42
|
import { queryActor } from "./actor-query";
|
|
38
43
|
import { ACTOR_CONNS_SYMBOL, type ClientRaw, TRANSPORT_SYMBOL } from "./client";
|
|
@@ -94,7 +99,7 @@ export class ActorConnRaw {
|
|
|
94
99
|
/** If attempting to connect. Helpful for knowing if in a retry loop when reconnecting. */
|
|
95
100
|
#connecting = false;
|
|
96
101
|
|
|
97
|
-
//
|
|
102
|
+
// Connection info, used for reconnection and HTTP requests
|
|
98
103
|
#actorId?: string;
|
|
99
104
|
#connectionId?: string;
|
|
100
105
|
#connectionToken?: string;
|
|
@@ -119,7 +124,7 @@ export class ActorConnRaw {
|
|
|
119
124
|
#keepNodeAliveInterval: NodeJS.Timeout;
|
|
120
125
|
|
|
121
126
|
/** Promise used to indicate the socket has connected successfully. This will be rejected if the connection fails. */
|
|
122
|
-
#onOpenPromise?:
|
|
127
|
+
#onOpenPromise?: ReturnType<typeof promiseWithResolvers<undefined>>;
|
|
123
128
|
|
|
124
129
|
#client: ClientRaw;
|
|
125
130
|
#driver: ManagerDriver;
|
|
@@ -177,7 +182,7 @@ export class ActorConnRaw {
|
|
|
177
182
|
this.#actionIdCounter += 1;
|
|
178
183
|
|
|
179
184
|
const { promise, resolve, reject } =
|
|
180
|
-
|
|
185
|
+
promiseWithResolvers<protocol.ActionResponse>();
|
|
181
186
|
this.#actionsInFlight.set(actionId, { name: opts.name, resolve, reject });
|
|
182
187
|
|
|
183
188
|
this.#sendMessage({
|
|
@@ -253,7 +258,7 @@ enc
|
|
|
253
258
|
// Create promise for open
|
|
254
259
|
if (this.#onOpenPromise)
|
|
255
260
|
throw new Error("#onOpenPromise already defined");
|
|
256
|
-
this.#onOpenPromise =
|
|
261
|
+
this.#onOpenPromise = promiseWithResolvers();
|
|
257
262
|
|
|
258
263
|
// Connect transport
|
|
259
264
|
if (this.#client[TRANSPORT_SYMBOL] === "websocket") {
|
|
@@ -277,15 +282,37 @@ enc
|
|
|
277
282
|
this.#actorQuery,
|
|
278
283
|
this.#driver,
|
|
279
284
|
);
|
|
285
|
+
|
|
286
|
+
// Check if we have connection info for reconnection
|
|
287
|
+
const isReconnection = this.#connectionId && this.#connectionToken;
|
|
288
|
+
if (isReconnection) {
|
|
289
|
+
logger().debug({
|
|
290
|
+
msg: "attempting websocket reconnection",
|
|
291
|
+
connectionId: this.#connectionId,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
280
295
|
const ws = await this.#driver.openWebSocket(
|
|
281
296
|
PATH_CONNECT_WEBSOCKET,
|
|
282
297
|
actorId,
|
|
283
298
|
this.#encoding,
|
|
284
299
|
this.#params,
|
|
300
|
+
// Pass connection ID and token for reconnection if available
|
|
301
|
+
isReconnection ? this.#connectionId : undefined,
|
|
302
|
+
isReconnection ? this.#connectionToken : undefined,
|
|
285
303
|
);
|
|
304
|
+
logger().debug({
|
|
305
|
+
msg: "transport set to new websocket",
|
|
306
|
+
connectionId: this.#connectionId,
|
|
307
|
+
readyState: ws.readyState,
|
|
308
|
+
messageQueueLength: this.#messageQueue.length,
|
|
309
|
+
});
|
|
286
310
|
this.#transport = { websocket: ws };
|
|
287
311
|
ws.addEventListener("open", () => {
|
|
288
|
-
logger().debug({
|
|
312
|
+
logger().debug({
|
|
313
|
+
msg: "client websocket open",
|
|
314
|
+
connectionId: this.#connectionId,
|
|
315
|
+
});
|
|
289
316
|
});
|
|
290
317
|
ws.addEventListener("message", async (ev) => {
|
|
291
318
|
this.#handleOnMessage(ev.data);
|
|
@@ -316,6 +343,8 @@ enc
|
|
|
316
343
|
encoding: this.#encoding,
|
|
317
344
|
});
|
|
318
345
|
|
|
346
|
+
const isReconnection = this.#connectionId && this.#connectionToken;
|
|
347
|
+
|
|
319
348
|
const eventSource = new EventSource("http://actor/connect/sse", {
|
|
320
349
|
fetch: (input, init) => {
|
|
321
350
|
return this.#driver.sendRequest(
|
|
@@ -329,6 +358,12 @@ enc
|
|
|
329
358
|
...(this.#params !== undefined
|
|
330
359
|
? { [HEADER_CONN_PARAMS]: JSON.stringify(this.#params) }
|
|
331
360
|
: {}),
|
|
361
|
+
...(isReconnection
|
|
362
|
+
? {
|
|
363
|
+
[HEADER_CONN_ID]: this.#connectionId,
|
|
364
|
+
[HEADER_CONN_TOKEN]: this.#connectionToken,
|
|
365
|
+
}
|
|
366
|
+
: {}),
|
|
332
367
|
},
|
|
333
368
|
}),
|
|
334
369
|
);
|
|
@@ -338,6 +373,9 @@ enc
|
|
|
338
373
|
this.#transport = { sse: eventSource };
|
|
339
374
|
|
|
340
375
|
eventSource.addEventListener("message", (ev: UniversalMessageEvent) => {
|
|
376
|
+
// Ignore pings
|
|
377
|
+
if (ev.type === "ping") return;
|
|
378
|
+
|
|
341
379
|
this.#handleOnMessage(ev.data);
|
|
342
380
|
});
|
|
343
381
|
|
|
@@ -351,6 +389,7 @@ enc
|
|
|
351
389
|
logger().debug({
|
|
352
390
|
msg: "socket open",
|
|
353
391
|
messageQueueLength: this.#messageQueue.length,
|
|
392
|
+
connectionId: this.#connectionId,
|
|
354
393
|
});
|
|
355
394
|
|
|
356
395
|
// Resolve open promise
|
|
@@ -370,6 +409,10 @@ enc
|
|
|
370
409
|
// If the message fails to send, the message will be re-queued
|
|
371
410
|
const queue = this.#messageQueue;
|
|
372
411
|
this.#messageQueue = [];
|
|
412
|
+
logger().debug({
|
|
413
|
+
msg: "flushing message queue",
|
|
414
|
+
queueLength: queue.length,
|
|
415
|
+
});
|
|
373
416
|
for (const msg of queue) {
|
|
374
417
|
this.#sendMessage(msg);
|
|
375
418
|
}
|
|
@@ -395,7 +438,7 @@ enc
|
|
|
395
438
|
);
|
|
396
439
|
|
|
397
440
|
if (response.body.tag === "Init") {
|
|
398
|
-
//
|
|
441
|
+
// Store connection info for reconnection
|
|
399
442
|
this.#actorId = response.body.val.actorId;
|
|
400
443
|
this.#connectionId = response.body.val.connectionId;
|
|
401
444
|
this.#connectionToken = response.body.val.connectionToken;
|
|
@@ -491,26 +534,46 @@ enc
|
|
|
491
534
|
//
|
|
492
535
|
// These properties will be undefined
|
|
493
536
|
const closeEvent = event as CloseEvent;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
537
|
+
const wasClean = closeEvent.wasClean;
|
|
538
|
+
|
|
539
|
+
logger().info({
|
|
540
|
+
msg: "socket closed",
|
|
541
|
+
code: closeEvent.code,
|
|
542
|
+
reason: closeEvent.reason,
|
|
543
|
+
wasClean: wasClean,
|
|
544
|
+
connectionId: this.#connectionId,
|
|
545
|
+
messageQueueLength: this.#messageQueue.length,
|
|
546
|
+
actionsInFlight: this.#actionsInFlight.size,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// Reject all in-flight actions
|
|
550
|
+
if (this.#actionsInFlight.size > 0) {
|
|
551
|
+
logger().debug({
|
|
552
|
+
msg: "rejecting in-flight actions after disconnect",
|
|
553
|
+
count: this.#actionsInFlight.size,
|
|
554
|
+
connectionId: this.#connectionId,
|
|
555
|
+
wasClean,
|
|
507
556
|
});
|
|
557
|
+
|
|
558
|
+
const disconnectError = new Error(
|
|
559
|
+
wasClean ? "Connection closed" : "Connection lost",
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
for (const actionInfo of this.#actionsInFlight.values()) {
|
|
563
|
+
actionInfo.reject(disconnectError);
|
|
564
|
+
}
|
|
565
|
+
this.#actionsInFlight.clear();
|
|
508
566
|
}
|
|
509
567
|
|
|
510
568
|
this.#transport = undefined;
|
|
511
569
|
|
|
512
570
|
// Automatically reconnect. Skip if already attempting to connect.
|
|
513
571
|
if (!this.#disposed && !this.#connecting) {
|
|
572
|
+
logger().debug({
|
|
573
|
+
msg: "triggering reconnect",
|
|
574
|
+
connectionId: this.#connectionId,
|
|
575
|
+
messageQueueLength: this.#messageQueue.length,
|
|
576
|
+
});
|
|
514
577
|
// TODO: Fetch actor to check if it's destroyed
|
|
515
578
|
// TODO: Add backoff for reconnect
|
|
516
579
|
// TODO: Add a way of preserving connection ID for connection state
|
|
@@ -660,9 +723,26 @@ enc
|
|
|
660
723
|
let queueMessage = false;
|
|
661
724
|
if (!this.#transport) {
|
|
662
725
|
// No transport connected yet
|
|
726
|
+
logger().debug({ msg: "no transport, queueing message" });
|
|
663
727
|
queueMessage = true;
|
|
664
728
|
} else if ("websocket" in this.#transport) {
|
|
665
|
-
|
|
729
|
+
const readyState = this.#transport.websocket.readyState;
|
|
730
|
+
logger().debug({
|
|
731
|
+
msg: "websocket send attempt",
|
|
732
|
+
readyState,
|
|
733
|
+
readyStateString:
|
|
734
|
+
readyState === 0
|
|
735
|
+
? "CONNECTING"
|
|
736
|
+
: readyState === 1
|
|
737
|
+
? "OPEN"
|
|
738
|
+
: readyState === 2
|
|
739
|
+
? "CLOSING"
|
|
740
|
+
: "CLOSED",
|
|
741
|
+
connectionId: this.#connectionId,
|
|
742
|
+
messageType: (message.body as any).tag,
|
|
743
|
+
actionName: (message.body as any).val?.name,
|
|
744
|
+
});
|
|
745
|
+
if (readyState === 1) {
|
|
666
746
|
try {
|
|
667
747
|
const messageSerialized = serializeWithEncoding(
|
|
668
748
|
this.#encoding,
|
|
@@ -678,12 +758,17 @@ enc
|
|
|
678
758
|
logger().warn({
|
|
679
759
|
msg: "failed to send message, added to queue",
|
|
680
760
|
error,
|
|
761
|
+
connectionId: this.#connectionId,
|
|
681
762
|
});
|
|
682
763
|
|
|
683
764
|
// Assuming the socket is disconnected and will be reconnected soon
|
|
684
765
|
queueMessage = true;
|
|
685
766
|
}
|
|
686
767
|
} else {
|
|
768
|
+
logger().debug({
|
|
769
|
+
msg: "websocket not open, queueing message",
|
|
770
|
+
readyState,
|
|
771
|
+
});
|
|
687
772
|
queueMessage = true;
|
|
688
773
|
}
|
|
689
774
|
} else if ("sse" in this.#transport) {
|
|
@@ -699,7 +784,13 @@ enc
|
|
|
699
784
|
|
|
700
785
|
if (!opts?.ephemeral && queueMessage) {
|
|
701
786
|
this.#messageQueue.push(message);
|
|
702
|
-
logger().debug({
|
|
787
|
+
logger().debug({
|
|
788
|
+
msg: "queued connection message",
|
|
789
|
+
queueLength: this.#messageQueue.length,
|
|
790
|
+
connectionId: this.#connectionId,
|
|
791
|
+
messageType: (message.body as any).tag,
|
|
792
|
+
actionName: (message.body as any).val?.name,
|
|
793
|
+
});
|
|
703
794
|
}
|
|
704
795
|
}
|
|
705
796
|
|
|
@@ -778,6 +869,22 @@ enc
|
|
|
778
869
|
return deserializeWithEncoding(this.#encoding, buffer, TO_CLIENT_VERSIONED);
|
|
779
870
|
}
|
|
780
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Get the actor ID (for testing purposes).
|
|
874
|
+
* @internal
|
|
875
|
+
*/
|
|
876
|
+
get actorId(): string | undefined {
|
|
877
|
+
return this.#actorId;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Get the connection ID (for testing purposes).
|
|
882
|
+
* @internal
|
|
883
|
+
*/
|
|
884
|
+
get connectionId(): string | undefined {
|
|
885
|
+
return this.#connectionId;
|
|
886
|
+
}
|
|
887
|
+
|
|
781
888
|
/**
|
|
782
889
|
* Disconnects from the actor.
|
|
783
890
|
*
|
|
@@ -815,7 +922,7 @@ enc
|
|
|
815
922
|
) {
|
|
816
923
|
logger().debug({ msg: "ws already closed or closing" });
|
|
817
924
|
} else {
|
|
818
|
-
const { promise, resolve } =
|
|
925
|
+
const { promise, resolve } = promiseWithResolvers();
|
|
819
926
|
ws.addEventListener("close", () => {
|
|
820
927
|
logger().debug({ msg: "ws closed" });
|
|
821
928
|
resolve(undefined);
|
|
@@ -824,6 +931,30 @@ enc
|
|
|
824
931
|
await promise;
|
|
825
932
|
}
|
|
826
933
|
} else if ("sse" in this.#transport) {
|
|
934
|
+
// Send close request to server for SSE connections
|
|
935
|
+
if (this.#connectionId && this.#connectionToken) {
|
|
936
|
+
try {
|
|
937
|
+
await sendHttpRequest({
|
|
938
|
+
url: "http://actor/connections/close",
|
|
939
|
+
method: "POST",
|
|
940
|
+
headers: {
|
|
941
|
+
[HEADER_CONN_ID]: this.#connectionId,
|
|
942
|
+
[HEADER_CONN_TOKEN]: this.#connectionToken,
|
|
943
|
+
},
|
|
944
|
+
encoding: this.#encoding,
|
|
945
|
+
skipParseResponse: true,
|
|
946
|
+
customFetch: this.#driver.sendRequest.bind(
|
|
947
|
+
this.#driver,
|
|
948
|
+
this.#actorId!,
|
|
949
|
+
),
|
|
950
|
+
requestVersionedDataHandler: TO_SERVER_VERSIONED,
|
|
951
|
+
responseVersionedDataHandler: TO_CLIENT_VERSIONED,
|
|
952
|
+
});
|
|
953
|
+
} catch (error) {
|
|
954
|
+
// Ignore errors when closing - connection may already be closed
|
|
955
|
+
logger().warn({ msg: "failed to send close request", error });
|
|
956
|
+
}
|
|
957
|
+
}
|
|
827
958
|
this.#transport.sse.close();
|
|
828
959
|
} else {
|
|
829
960
|
assertUnreachable(this.#transport);
|
package/src/client/client.ts
CHANGED
|
@@ -491,7 +491,7 @@ function createActorProxy<AD extends AnyActorDefinition>(
|
|
|
491
491
|
|
|
492
492
|
// Handle built-in Promise methods and existing properties
|
|
493
493
|
if (prop === "constructor" || prop in target) {
|
|
494
|
-
const value = Reflect.get(target, prop,
|
|
494
|
+
const value = Reflect.get(target, prop, target);
|
|
495
495
|
// Preserve method binding
|
|
496
496
|
if (typeof value === "function") {
|
|
497
497
|
return value.bind(target);
|
package/src/client/config.ts
CHANGED
|
@@ -14,6 +14,13 @@ export const ClientConfigSchema = z.object({
|
|
|
14
14
|
})
|
|
15
15
|
.default({}),
|
|
16
16
|
|
|
17
|
+
token: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.transform((x) => x ?? getEnvUniversal("RIVET_TOKEN")),
|
|
21
|
+
|
|
22
|
+
headers: z.record(z.string()).optional().default({}),
|
|
23
|
+
|
|
17
24
|
/** Endpoint to connect to the Rivet engine. Can be configured via RIVET_ENGINE env var. */
|
|
18
25
|
endpoint: z
|
|
19
26
|
.string()
|
|
@@ -5,21 +5,39 @@ export const PATH_CONNECT_WEBSOCKET = "/connect/websocket";
|
|
|
5
5
|
export const PATH_RAW_WEBSOCKET_PREFIX = "/raw/websocket/";
|
|
6
6
|
|
|
7
7
|
// MARK: Headers
|
|
8
|
-
export const HEADER_ACTOR_QUERY = "
|
|
8
|
+
export const HEADER_ACTOR_QUERY = "x-rivet-query";
|
|
9
9
|
|
|
10
|
-
export const HEADER_ENCODING = "
|
|
10
|
+
export const HEADER_ENCODING = "x-rivet-encoding";
|
|
11
11
|
|
|
12
12
|
// IMPORTANT: Params must be in headers or in an E2EE part of the request (i.e. NOT the URL or query string) in order to ensure that tokens can be securely passed in params.
|
|
13
|
-
export const HEADER_CONN_PARAMS = "
|
|
13
|
+
export const HEADER_CONN_PARAMS = "x-rivet-conn-params";
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
export const HEADER_AUTH_DATA = "X-RivetKit-Auth-Data";
|
|
15
|
+
export const HEADER_ACTOR_ID = "x-rivet-actor";
|
|
17
16
|
|
|
18
|
-
export const
|
|
17
|
+
export const HEADER_CONN_ID = "x-rivet-conn";
|
|
19
18
|
|
|
20
|
-
export const
|
|
19
|
+
export const HEADER_CONN_TOKEN = "x-rivet-conn-token";
|
|
21
20
|
|
|
22
|
-
export const
|
|
21
|
+
export const HEADER_RIVET_TOKEN = "x-rivet-token";
|
|
22
|
+
|
|
23
|
+
// MARK: Manager Gateway Headers
|
|
24
|
+
export const HEADER_RIVET_TARGET = "x-rivet-target";
|
|
25
|
+
export const HEADER_RIVET_ACTOR = "x-rivet-actor";
|
|
26
|
+
|
|
27
|
+
// MARK: WebSocket Protocol Prefixes
|
|
28
|
+
/** Some servers (such as node-ws & Cloudflare) require explicitly match a certain WebSocket protocol. This gives us a static protocol to match against. */
|
|
29
|
+
export const WS_PROTOCOL_STANDARD = "rivet";
|
|
30
|
+
export const WS_PROTOCOL_TARGET = "rivet_target.";
|
|
31
|
+
export const WS_PROTOCOL_ACTOR = "rivet_actor.";
|
|
32
|
+
export const WS_PROTOCOL_ENCODING = "rivet_encoding.";
|
|
33
|
+
export const WS_PROTOCOL_CONN_PARAMS = "rivet_conn_params.";
|
|
34
|
+
export const WS_PROTOCOL_CONN_ID = "rivet_conn.";
|
|
35
|
+
export const WS_PROTOCOL_CONN_TOKEN = "rivet_conn_token.";
|
|
36
|
+
export const WS_PROTOCOL_TOKEN = "rivet_token.";
|
|
37
|
+
|
|
38
|
+
// MARK: WebSocket Inline Test Protocol Prefixes
|
|
39
|
+
export const WS_PROTOCOL_TRANSPORT = "test_transport.";
|
|
40
|
+
export const WS_PROTOCOL_PATH = "test_path.";
|
|
23
41
|
|
|
24
42
|
/**
|
|
25
43
|
* Headers that publics can send from public clients.
|
|
@@ -35,4 +53,7 @@ export const ALLOWED_PUBLIC_HEADERS = [
|
|
|
35
53
|
HEADER_ACTOR_ID,
|
|
36
54
|
HEADER_CONN_ID,
|
|
37
55
|
HEADER_CONN_TOKEN,
|
|
56
|
+
HEADER_RIVET_TARGET,
|
|
57
|
+
HEADER_RIVET_ACTOR,
|
|
58
|
+
HEADER_RIVET_TOKEN,
|
|
38
59
|
];
|
package/src/common/router.ts
CHANGED
|
@@ -63,8 +63,8 @@ export class VersionedDataHandler<T> {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
private embedVersion(data: VersionedData<Uint8Array>): Uint8Array {
|
|
66
|
-
const versionBytes = new Uint8Array(
|
|
67
|
-
new DataView(versionBytes.buffer).
|
|
66
|
+
const versionBytes = new Uint8Array(2);
|
|
67
|
+
new DataView(versionBytes.buffer).setUint16(0, data.version, true);
|
|
68
68
|
|
|
69
69
|
const result = new Uint8Array(versionBytes.length + data.data.length);
|
|
70
70
|
result.set(versionBytes);
|
|
@@ -74,15 +74,15 @@ export class VersionedDataHandler<T> {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
private extractVersion(bytes: Uint8Array): VersionedData<Uint8Array> {
|
|
77
|
-
if (bytes.length <
|
|
77
|
+
if (bytes.length < 2) {
|
|
78
78
|
throw new Error("Invalid versioned data: too short");
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const version = new DataView(bytes.buffer, bytes.byteOffset).
|
|
81
|
+
const version = new DataView(bytes.buffer, bytes.byteOffset).getUint16(
|
|
82
82
|
0,
|
|
83
83
|
true,
|
|
84
84
|
);
|
|
85
|
-
const data = bytes.slice(
|
|
85
|
+
const data = bytes.slice(2);
|
|
86
86
|
|
|
87
87
|
return { version, data };
|
|
88
88
|
}
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
export type { ActorDriver } from "@/actor/driver";
|
|
2
2
|
export type { ActorInstance, AnyActorInstance } from "@/actor/instance";
|
|
3
3
|
export {
|
|
4
|
+
ALLOWED_PUBLIC_HEADERS,
|
|
4
5
|
HEADER_ACTOR_ID,
|
|
5
6
|
HEADER_ACTOR_QUERY,
|
|
6
|
-
HEADER_AUTH_DATA,
|
|
7
7
|
HEADER_CONN_ID,
|
|
8
8
|
HEADER_CONN_PARAMS,
|
|
9
9
|
HEADER_CONN_TOKEN,
|
|
10
10
|
HEADER_ENCODING,
|
|
11
|
+
HEADER_RIVET_ACTOR,
|
|
12
|
+
HEADER_RIVET_TARGET,
|
|
13
|
+
PATH_CONNECT_WEBSOCKET,
|
|
14
|
+
PATH_RAW_WEBSOCKET_PREFIX,
|
|
15
|
+
WS_PROTOCOL_ACTOR,
|
|
16
|
+
WS_PROTOCOL_CONN_ID,
|
|
17
|
+
WS_PROTOCOL_CONN_PARAMS,
|
|
18
|
+
WS_PROTOCOL_CONN_TOKEN,
|
|
19
|
+
WS_PROTOCOL_ENCODING,
|
|
20
|
+
WS_PROTOCOL_PATH,
|
|
21
|
+
WS_PROTOCOL_STANDARD,
|
|
22
|
+
WS_PROTOCOL_TARGET,
|
|
23
|
+
WS_PROTOCOL_TRANSPORT,
|
|
11
24
|
} from "@/common/actor-router-consts";
|
|
12
25
|
export type {
|
|
13
26
|
ActorOutput,
|
|
@@ -4,6 +4,7 @@ import { bundleRequire } from "bundle-require";
|
|
|
4
4
|
import invariant from "invariant";
|
|
5
5
|
import { describe } from "vitest";
|
|
6
6
|
import type { Transport } from "@/client/mod";
|
|
7
|
+
import { configureInspectorAccessToken } from "@/inspector/utils";
|
|
7
8
|
import { createManagerRouter } from "@/manager/router";
|
|
8
9
|
import type { DriverConfig, Registry, RunConfig } from "@/mod";
|
|
9
10
|
import { RunConfigSchema } from "@/registry/run-config";
|
|
@@ -22,6 +23,7 @@ import { runActorInlineClientTests } from "./tests/actor-inline-client";
|
|
|
22
23
|
import { runActorInspectorTests } from "./tests/actor-inspector";
|
|
23
24
|
import { runActorMetadataTests } from "./tests/actor-metadata";
|
|
24
25
|
import { runActorOnStateChangeTests } from "./tests/actor-onstatechange";
|
|
26
|
+
import { runActorReconnectTests } from "./tests/actor-reconnect";
|
|
25
27
|
import { runActorVarsTests } from "./tests/actor-vars";
|
|
26
28
|
import { runManagerDriverTests } from "./tests/manager-driver";
|
|
27
29
|
import { runRawHttpTests } from "./tests/raw-http";
|
|
@@ -32,6 +34,7 @@ import { runRequestAccessTests } from "./tests/request-access";
|
|
|
32
34
|
export interface SkipTests {
|
|
33
35
|
schedule?: boolean;
|
|
34
36
|
sleep?: boolean;
|
|
37
|
+
sse?: boolean;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
export interface DriverTestConfig {
|
|
@@ -86,7 +89,10 @@ export function runDriverTests(
|
|
|
86
89
|
runActorDriverTests(driverTestConfig);
|
|
87
90
|
runManagerDriverTests(driverTestConfig);
|
|
88
91
|
|
|
89
|
-
|
|
92
|
+
const transports: Transport[] = driverTestConfig.skip?.sse
|
|
93
|
+
? ["websocket"]
|
|
94
|
+
: ["websocket", "sse"];
|
|
95
|
+
for (const transport of transports) {
|
|
90
96
|
describe(`transport (${transport})`, () => {
|
|
91
97
|
runActorConnTests({
|
|
92
98
|
...driverTestConfig,
|
|
@@ -95,6 +101,8 @@ export function runDriverTests(
|
|
|
95
101
|
|
|
96
102
|
runActorConnStateTests({ ...driverTestConfig, transport });
|
|
97
103
|
|
|
104
|
+
runActorReconnectTests({ ...driverTestConfig, transport });
|
|
105
|
+
|
|
98
106
|
runRequestAccessTests({ ...driverTestConfig, transport });
|
|
99
107
|
|
|
100
108
|
runActorDriverTestsWithTransport({ ...driverTestConfig, transport });
|
|
@@ -193,11 +201,12 @@ export async function createTestRuntime(
|
|
|
193
201
|
|
|
194
202
|
// Create router
|
|
195
203
|
const managerDriver = driver.manager(registry.config, config);
|
|
204
|
+
configureInspectorAccessToken(config, managerDriver);
|
|
196
205
|
const { router } = createManagerRouter(
|
|
197
206
|
registry.config,
|
|
198
207
|
config,
|
|
199
208
|
managerDriver,
|
|
200
|
-
|
|
209
|
+
undefined,
|
|
201
210
|
);
|
|
202
211
|
|
|
203
212
|
// Inject WebSocket
|
|
@@ -10,6 +10,12 @@ import {
|
|
|
10
10
|
HEADER_ACTOR_QUERY,
|
|
11
11
|
HEADER_CONN_PARAMS,
|
|
12
12
|
HEADER_ENCODING,
|
|
13
|
+
WS_PROTOCOL_ACTOR,
|
|
14
|
+
WS_PROTOCOL_CONN_PARAMS,
|
|
15
|
+
WS_PROTOCOL_ENCODING,
|
|
16
|
+
WS_PROTOCOL_PATH,
|
|
17
|
+
WS_PROTOCOL_TARGET,
|
|
18
|
+
WS_PROTOCOL_TRANSPORT,
|
|
13
19
|
} from "@/common/actor-router-consts";
|
|
14
20
|
import type { UniversalEventSource } from "@/common/eventsource-interface";
|
|
15
21
|
import type { DeconstructedError } from "@/common/utils";
|
|
@@ -111,7 +117,8 @@ export function createTestInlineClientDriver(
|
|
|
111
117
|
headers,
|
|
112
118
|
body: actorRequest.body,
|
|
113
119
|
signal: actorRequest.signal,
|
|
114
|
-
|
|
120
|
+
duplex: "half",
|
|
121
|
+
} as RequestInit),
|
|
115
122
|
);
|
|
116
123
|
|
|
117
124
|
// Check if it's an error response from our handler
|
|
@@ -153,39 +160,48 @@ export function createTestInlineClientDriver(
|
|
|
153
160
|
actorId: string,
|
|
154
161
|
encoding: Encoding,
|
|
155
162
|
params: unknown,
|
|
163
|
+
connId?: string,
|
|
164
|
+
connToken?: string,
|
|
156
165
|
): Promise<UniversalWebSocket> {
|
|
157
166
|
const WebSocket = await importWebSocket();
|
|
158
167
|
|
|
159
168
|
// Normalize path to match other drivers
|
|
160
169
|
const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
|
|
161
170
|
|
|
162
|
-
logger().debug({
|
|
163
|
-
msg: "creating websocket connection via test inline driver",
|
|
164
|
-
});
|
|
165
|
-
|
|
166
171
|
// Create WebSocket connection to the test endpoint
|
|
167
|
-
// Use a placeholder path and pass the actual path as a query param to avoid mixing user query params with internal ones
|
|
168
172
|
const wsUrl = new URL(
|
|
169
173
|
`${endpoint}/.test/inline-driver/connect-websocket/ws`,
|
|
170
174
|
);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
wsUrl.
|
|
175
|
-
|
|
175
|
+
|
|
176
|
+
logger().debug({
|
|
177
|
+
msg: "creating websocket connection via test inline driver",
|
|
178
|
+
url: wsUrl.toString(),
|
|
179
|
+
});
|
|
176
180
|
|
|
177
181
|
// Convert http/https to ws/wss
|
|
178
182
|
const wsProtocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
179
|
-
const finalWsUrl = `${wsProtocol}//${wsUrl.host}${wsUrl.pathname}
|
|
183
|
+
const finalWsUrl = `${wsProtocol}//${wsUrl.host}${wsUrl.pathname}`;
|
|
180
184
|
|
|
181
185
|
logger().debug({ msg: "connecting to websocket", url: finalWsUrl });
|
|
182
186
|
|
|
187
|
+
// Build protocols for the connection
|
|
188
|
+
const protocols: string[] = [];
|
|
189
|
+
protocols.push(`${WS_PROTOCOL_TARGET}actor`);
|
|
190
|
+
protocols.push(`${WS_PROTOCOL_ACTOR}${actorId}`);
|
|
191
|
+
protocols.push(`${WS_PROTOCOL_ENCODING}${encoding}`);
|
|
192
|
+
protocols.push(`${WS_PROTOCOL_TRANSPORT}${transport}`);
|
|
193
|
+
protocols.push(
|
|
194
|
+
`${WS_PROTOCOL_PATH}${encodeURIComponent(normalizedPath)}`,
|
|
195
|
+
);
|
|
196
|
+
if (params !== undefined) {
|
|
197
|
+
protocols.push(
|
|
198
|
+
`${WS_PROTOCOL_CONN_PARAMS}${encodeURIComponent(JSON.stringify(params))}`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
183
202
|
// Create and return the WebSocket
|
|
184
203
|
// Node & browser WebSocket types are incompatible
|
|
185
|
-
const ws = new WebSocket(finalWsUrl,
|
|
186
|
-
// HACK: See packages/drivers/cloudflare-workers/src/websocket.ts
|
|
187
|
-
"rivetkit",
|
|
188
|
-
]) as any;
|
|
204
|
+
const ws = new WebSocket(finalWsUrl, protocols) as any;
|
|
189
205
|
|
|
190
206
|
return ws;
|
|
191
207
|
},
|
|
@@ -202,7 +218,6 @@ export function createTestInlineClientDriver(
|
|
|
202
218
|
_actorId: string,
|
|
203
219
|
_encoding: Encoding,
|
|
204
220
|
_params: unknown,
|
|
205
|
-
_authData: unknown,
|
|
206
221
|
): Promise<Response> {
|
|
207
222
|
throw "UNIMPLEMENTED";
|
|
208
223
|
// const upgradeWebSocket = this.#runConfig.getUpgradeWebSocket?.();
|
|
@@ -214,6 +229,8 @@ export function createTestInlineClientDriver(
|
|
|
214
229
|
displayInformation(): ManagerDisplayInformation {
|
|
215
230
|
return { name: "Test Inline", properties: {} };
|
|
216
231
|
},
|
|
232
|
+
// TODO:
|
|
233
|
+
getOrCreateInspectorAccessToken: () => "",
|
|
217
234
|
|
|
218
235
|
// action: async <Args extends Array<unknown> = unknown[], Response = unknown>(
|
|
219
236
|
// _c: HonoContext | undefined,
|
|
@@ -560,7 +577,8 @@ async function makeInlineRequest<T>(
|
|
|
560
577
|
method,
|
|
561
578
|
args,
|
|
562
579
|
} satisfies TestInlineDriverCallRequest),
|
|
563
|
-
|
|
580
|
+
duplex: "half",
|
|
581
|
+
} as RequestInit);
|
|
564
582
|
|
|
565
583
|
if (!response.ok) {
|
|
566
584
|
throw new Error(`Failed to call inline ${method}: ${response.statusText}`);
|