rivetkit 2.0.24-rc.1 → 2.0.24
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/v2.ts +3 -3
- package/dist/schemas/actor-persist/v3.ts +274 -0
- package/dist/schemas/client-protocol/v2.ts +432 -0
- package/dist/schemas/file-system-driver/v2.ts +136 -0
- package/dist/tsup/actor/errors.cjs +2 -4
- package/dist/tsup/actor/errors.cjs.map +1 -1
- package/dist/tsup/actor/errors.d.cts +7 -10
- package/dist/tsup/actor/errors.d.ts +7 -10
- package/dist/tsup/actor/errors.js +9 -11
- package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.cts → actor-router-consts-DzI2szci.d.cts} +5 -9
- package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.ts → actor-router-consts-DzI2szci.d.ts} +5 -9
- package/dist/tsup/{chunk-HHFKKVLR.cjs → chunk-3543NCSN.cjs} +45 -57
- package/dist/tsup/chunk-3543NCSN.cjs.map +1 -0
- package/dist/tsup/chunk-4SHILYS5.cjs +5694 -0
- package/dist/tsup/chunk-4SHILYS5.cjs.map +1 -0
- package/dist/tsup/{chunk-ZTH3KYFH.cjs → chunk-5BZO5XPS.cjs} +3 -3
- package/dist/tsup/{chunk-ZTH3KYFH.cjs.map → chunk-5BZO5XPS.cjs.map} +1 -1
- package/dist/tsup/{chunk-PLUN2NQT.js → chunk-BAIGSF64.js} +189 -187
- package/dist/tsup/chunk-BAIGSF64.js.map +1 -0
- package/dist/tsup/{chunk-SHVX2QUR.cjs → chunk-CHLZBSI2.cjs} +17 -17
- package/dist/tsup/chunk-CHLZBSI2.cjs.map +1 -0
- package/dist/tsup/chunk-D3SLADUD.cjs +512 -0
- package/dist/tsup/chunk-D3SLADUD.cjs.map +1 -0
- package/dist/tsup/{chunk-KSRXX3Z4.cjs → chunk-D6762AOA.cjs} +20 -25
- package/dist/tsup/chunk-D6762AOA.cjs.map +1 -0
- package/dist/tsup/{chunk-7L65NNWP.cjs → chunk-DLK5YCTN.cjs} +187 -185
- package/dist/tsup/chunk-DLK5YCTN.cjs.map +1 -0
- package/dist/tsup/{chunk-YBG6R7LX.js → chunk-DUJQWGYD.js} +3 -7
- package/dist/tsup/chunk-DUJQWGYD.js.map +1 -0
- package/dist/tsup/{chunk-CD33GT6Z.js → chunk-EIPANQMF.js} +2 -2
- package/dist/tsup/{chunk-2JYPS5YM.cjs → chunk-ESMTDP7G.cjs} +6 -6
- package/dist/tsup/chunk-ESMTDP7G.cjs.map +1 -0
- package/dist/tsup/{chunk-VHGY7PU5.cjs → chunk-FVAKREFB.cjs} +1900 -1737
- package/dist/tsup/chunk-FVAKREFB.cjs.map +1 -0
- package/dist/tsup/{chunk-BLK27ES3.js → chunk-I3XT7WOF.js} +44 -56
- package/dist/tsup/chunk-I3XT7WOF.js.map +1 -0
- package/dist/tsup/{chunk-YBHYXIP6.js → chunk-IMDS5T42.js} +3 -3
- package/dist/tsup/chunk-IMDS5T42.js.map +1 -0
- package/dist/tsup/{chunk-INNFK746.cjs → chunk-J3HZJF2P.cjs} +10 -14
- package/dist/tsup/chunk-J3HZJF2P.cjs.map +1 -0
- package/dist/tsup/{chunk-BYMKMOBS.js → chunk-MBBJUHSP.js} +1844 -1681
- package/dist/tsup/chunk-MBBJUHSP.js.map +1 -0
- package/dist/tsup/{chunk-BOMZS2TJ.js → chunk-MO5CB6MD.js} +9 -9
- package/dist/tsup/chunk-MO5CB6MD.js.map +1 -0
- package/dist/tsup/chunk-OFOTPKAH.js +512 -0
- package/dist/tsup/chunk-OFOTPKAH.js.map +1 -0
- package/dist/tsup/{chunk-G64QUEDJ.js → chunk-W6RDS6NW.js} +23 -28
- package/dist/tsup/chunk-W6RDS6NW.js.map +1 -0
- package/dist/tsup/{chunk-36JJ4IQB.cjs → chunk-YC5DUHPM.cjs} +4 -8
- package/dist/tsup/chunk-YC5DUHPM.cjs.map +1 -0
- package/dist/tsup/{chunk-FX7TWFQR.js → chunk-YC7YPM2T.js} +2 -6
- package/dist/tsup/chunk-YC7YPM2T.js.map +1 -0
- package/dist/tsup/{chunk-227FEWMB.js → chunk-ZSPU5R4C.js} +3322 -2251
- package/dist/tsup/chunk-ZSPU5R4C.js.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +5 -7
- package/dist/tsup/client/mod.d.ts +5 -7
- 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/{conn-B3Vhbgnd.d.ts → config-BRDYDraU.d.cts} +1119 -1047
- package/dist/tsup/{conn-DJWL3nGx.d.cts → config-Bo-blHpJ.d.ts} +1119 -1047
- package/dist/tsup/driver-helpers/mod.cjs +5 -13
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +11 -9
- package/dist/tsup/driver-helpers/mod.d.ts +11 -9
- package/dist/tsup/driver-helpers/mod.js +14 -22
- package/dist/tsup/driver-test-suite/mod.cjs +474 -303
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +6 -9
- package/dist/tsup/driver-test-suite/mod.d.ts +6 -9
- package/dist/tsup/driver-test-suite/mod.js +1085 -914
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.d.cts +5 -7
- package/dist/tsup/inspector/mod.d.ts +5 -7
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +10 -16
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +23 -25
- package/dist/tsup/mod.d.ts +23 -25
- package/dist/tsup/mod.js +17 -23
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.d.cts +4 -6
- package/dist/tsup/test/mod.d.ts +4 -6
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +3 -5
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.d.cts +1 -2
- package/dist/tsup/utils.d.ts +1 -2
- package/dist/tsup/utils.js +2 -4
- package/package.json +13 -6
- package/src/actor/config.ts +56 -44
- package/src/actor/conn/driver.ts +61 -0
- package/src/actor/conn/drivers/http.ts +17 -0
- package/src/actor/conn/drivers/raw-request.ts +24 -0
- package/src/actor/conn/drivers/raw-websocket.ts +65 -0
- package/src/actor/conn/drivers/websocket.ts +129 -0
- package/src/actor/conn/mod.ts +232 -0
- package/src/actor/conn/persisted.ts +81 -0
- package/src/actor/conn/state-manager.ts +196 -0
- package/src/actor/contexts/action.ts +23 -0
- package/src/actor/{context.ts → contexts/actor.ts} +19 -8
- package/src/actor/contexts/conn-init.ts +31 -0
- package/src/actor/contexts/conn.ts +48 -0
- package/src/actor/contexts/create-conn-state.ts +13 -0
- package/src/actor/contexts/on-before-connect.ts +13 -0
- package/src/actor/contexts/on-connect.ts +22 -0
- package/src/actor/contexts/request.ts +48 -0
- package/src/actor/contexts/websocket.ts +48 -0
- package/src/actor/definition.ts +3 -3
- package/src/actor/driver.ts +36 -5
- package/src/actor/errors.ts +19 -24
- package/src/actor/instance/connection-manager.ts +465 -0
- package/src/actor/instance/event-manager.ts +292 -0
- package/src/actor/instance/kv.ts +15 -0
- package/src/actor/instance/mod.ts +1107 -0
- package/src/actor/instance/persisted.ts +67 -0
- package/src/actor/instance/schedule-manager.ts +349 -0
- package/src/actor/instance/state-manager.ts +502 -0
- package/src/actor/mod.ts +13 -16
- package/src/actor/protocol/old.ts +131 -43
- package/src/actor/protocol/serde.ts +19 -4
- package/src/actor/router-endpoints.ts +61 -586
- package/src/actor/router-websocket-endpoints.ts +408 -0
- package/src/actor/router.ts +63 -197
- package/src/actor/schedule.ts +1 -1
- package/src/client/actor-conn.ts +183 -249
- package/src/client/actor-handle.ts +29 -6
- package/src/client/client.ts +0 -4
- package/src/client/config.ts +1 -4
- package/src/client/mod.ts +0 -1
- package/src/client/raw-utils.ts +3 -3
- package/src/client/utils.ts +85 -39
- package/src/common/actor-router-consts.ts +5 -12
- package/src/common/{inline-websocket-adapter2.ts → inline-websocket-adapter.ts} +26 -48
- package/src/common/log.ts +1 -1
- package/src/common/router.ts +28 -17
- package/src/common/utils.ts +2 -0
- package/src/driver-helpers/mod.ts +7 -10
- package/src/driver-helpers/utils.ts +18 -9
- package/src/driver-test-suite/mod.ts +26 -50
- package/src/driver-test-suite/test-inline-client-driver.ts +27 -51
- package/src/driver-test-suite/tests/actor-conn-hibernation.ts +150 -0
- package/src/driver-test-suite/tests/actor-conn-state.ts +1 -4
- package/src/driver-test-suite/tests/actor-conn.ts +5 -9
- package/src/driver-test-suite/tests/actor-destroy.ts +294 -0
- package/src/driver-test-suite/tests/actor-driver.ts +0 -7
- package/src/driver-test-suite/tests/actor-handle.ts +12 -12
- package/src/driver-test-suite/tests/actor-metadata.ts +1 -1
- package/src/driver-test-suite/tests/manager-driver.ts +1 -1
- package/src/driver-test-suite/tests/raw-http-direct-registry.ts +8 -8
- package/src/driver-test-suite/tests/raw-http-request-properties.ts +6 -5
- package/src/driver-test-suite/tests/raw-http.ts +5 -5
- package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +7 -7
- package/src/driver-test-suite/tests/request-access.ts +4 -4
- package/src/driver-test-suite/utils.ts +6 -10
- package/src/drivers/engine/actor-driver.ts +614 -424
- package/src/drivers/engine/mod.ts +0 -1
- package/src/drivers/file-system/actor.ts +24 -12
- package/src/drivers/file-system/global-state.ts +427 -37
- package/src/drivers/file-system/manager.ts +71 -83
- package/src/drivers/file-system/mod.ts +3 -0
- package/src/drivers/file-system/utils.ts +18 -8
- package/src/engine-process/mod.ts +38 -38
- package/src/inspector/utils.ts +7 -5
- package/src/manager/driver.ts +11 -4
- package/src/manager/gateway.ts +4 -29
- package/src/manager/protocol/mod.ts +0 -2
- package/src/manager/protocol/query.ts +0 -4
- package/src/manager/router.ts +67 -64
- package/src/manager-api/actors.ts +13 -0
- package/src/mod.ts +1 -3
- package/src/registry/mod.ts +20 -20
- package/src/registry/serve.ts +9 -14
- package/src/remote-manager-driver/actor-websocket-client.ts +1 -16
- package/src/remote-manager-driver/api-endpoints.ts +13 -1
- package/src/remote-manager-driver/api-utils.ts +8 -0
- package/src/remote-manager-driver/metadata.ts +58 -0
- package/src/remote-manager-driver/mod.ts +47 -62
- package/src/remote-manager-driver/ws-proxy.ts +1 -1
- package/src/schemas/actor-persist/mod.ts +1 -1
- package/src/schemas/actor-persist/versioned.ts +56 -31
- package/src/schemas/client-protocol/mod.ts +1 -1
- package/src/schemas/client-protocol/versioned.ts +41 -21
- package/src/schemas/client-protocol-zod/mod.ts +103 -0
- package/src/schemas/file-system-driver/mod.ts +1 -1
- package/src/schemas/file-system-driver/versioned.ts +42 -19
- package/src/serde.ts +33 -11
- package/src/test/mod.ts +7 -3
- package/src/utils/node.ts +173 -0
- package/src/utils.ts +0 -4
- package/dist/tsup/chunk-227FEWMB.js.map +0 -1
- package/dist/tsup/chunk-2JYPS5YM.cjs.map +0 -1
- package/dist/tsup/chunk-36JJ4IQB.cjs.map +0 -1
- package/dist/tsup/chunk-7L65NNWP.cjs.map +0 -1
- package/dist/tsup/chunk-BLK27ES3.js.map +0 -1
- package/dist/tsup/chunk-BOMZS2TJ.js.map +0 -1
- package/dist/tsup/chunk-BYMKMOBS.js.map +0 -1
- package/dist/tsup/chunk-FX7TWFQR.js.map +0 -1
- package/dist/tsup/chunk-G64QUEDJ.js.map +0 -1
- package/dist/tsup/chunk-HHFKKVLR.cjs.map +0 -1
- package/dist/tsup/chunk-INNFK746.cjs.map +0 -1
- package/dist/tsup/chunk-KSRXX3Z4.cjs.map +0 -1
- package/dist/tsup/chunk-O44LFKSB.cjs +0 -4623
- package/dist/tsup/chunk-O44LFKSB.cjs.map +0 -1
- package/dist/tsup/chunk-PLUN2NQT.js.map +0 -1
- package/dist/tsup/chunk-S4UJG7ZE.js +0 -1119
- package/dist/tsup/chunk-S4UJG7ZE.js.map +0 -1
- package/dist/tsup/chunk-SHVX2QUR.cjs.map +0 -1
- package/dist/tsup/chunk-VFB23BYZ.cjs +0 -1119
- package/dist/tsup/chunk-VFB23BYZ.cjs.map +0 -1
- package/dist/tsup/chunk-VHGY7PU5.cjs.map +0 -1
- package/dist/tsup/chunk-YBG6R7LX.js.map +0 -1
- package/dist/tsup/chunk-YBHYXIP6.js.map +0 -1
- package/src/actor/action.ts +0 -178
- package/src/actor/conn-drivers.ts +0 -216
- package/src/actor/conn-socket.ts +0 -8
- package/src/actor/conn.ts +0 -272
- package/src/actor/instance.ts +0 -2336
- package/src/actor/persisted.ts +0 -49
- package/src/actor/unstable-react.ts +0 -110
- package/src/driver-test-suite/tests/actor-reconnect.ts +0 -170
- package/src/drivers/engine/kv.ts +0 -3
- package/src/manager/hono-websocket-adapter.ts +0 -393
- /package/dist/tsup/{chunk-CD33GT6Z.js.map → chunk-EIPANQMF.js.map} +0 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { HibernatingWebSocketMetadata } from "@rivetkit/engine-runner";
|
|
2
|
+
import * as cbor from "cbor-x";
|
|
3
|
+
import invariant from "invariant";
|
|
4
|
+
import { CONN_VERSIONED } from "@/schemas/actor-persist/versioned";
|
|
5
|
+
import { TO_CLIENT_VERSIONED } from "@/schemas/client-protocol/versioned";
|
|
6
|
+
import { ToClientSchema } from "@/schemas/client-protocol-zod/mod";
|
|
7
|
+
import { arrayBuffersEqual, stringifyError } from "@/utils";
|
|
8
|
+
import type { ConnDriver } from "../conn/driver";
|
|
9
|
+
import {
|
|
10
|
+
CONN_CONNECTED_SYMBOL,
|
|
11
|
+
CONN_DRIVER_SYMBOL,
|
|
12
|
+
CONN_SEND_MESSAGE_SYMBOL,
|
|
13
|
+
CONN_SPEAKS_RIVETKIT_SYMBOL,
|
|
14
|
+
CONN_STATE_MANAGER_SYMBOL,
|
|
15
|
+
Conn,
|
|
16
|
+
type ConnId,
|
|
17
|
+
} from "../conn/mod";
|
|
18
|
+
import {
|
|
19
|
+
convertConnToBarePersistedConn,
|
|
20
|
+
type PersistedConn,
|
|
21
|
+
} from "../conn/persisted";
|
|
22
|
+
import type { ConnDataInput } from "../conn/state-manager";
|
|
23
|
+
import { CreateConnStateContext } from "../contexts/create-conn-state";
|
|
24
|
+
import { OnBeforeConnectContext } from "../contexts/on-before-connect";
|
|
25
|
+
import { OnConnectContext } from "../contexts/on-connect";
|
|
26
|
+
import type { AnyDatabaseProvider } from "../database";
|
|
27
|
+
import { CachedSerializer } from "../protocol/serde";
|
|
28
|
+
import { deadline } from "../utils";
|
|
29
|
+
import { makeConnKey } from "./kv";
|
|
30
|
+
import type { ActorInstance } from "./mod";
|
|
31
|
+
/**
|
|
32
|
+
* Manages all connection-related operations for an actor instance.
|
|
33
|
+
* Handles connection creation, tracking, hibernation, and cleanup.
|
|
34
|
+
*/
|
|
35
|
+
export class ConnectionManager<
|
|
36
|
+
S,
|
|
37
|
+
CP,
|
|
38
|
+
CS,
|
|
39
|
+
V,
|
|
40
|
+
I,
|
|
41
|
+
DB extends AnyDatabaseProvider,
|
|
42
|
+
> {
|
|
43
|
+
#actor: ActorInstance<S, CP, CS, V, I, DB>;
|
|
44
|
+
#connections = new Map<ConnId, Conn<S, CP, CS, V, I, DB>>();
|
|
45
|
+
|
|
46
|
+
/** Connections that have had their state changed and need to be persisted. */
|
|
47
|
+
#connsWithPersistChanged = new Set<ConnId>();
|
|
48
|
+
|
|
49
|
+
constructor(actor: ActorInstance<S, CP, CS, V, I, DB>) {
|
|
50
|
+
this.#actor = actor;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get connections(): Map<ConnId, Conn<S, CP, CS, V, I, DB>> {
|
|
54
|
+
return this.#connections;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getConnForId(id: string): Conn<S, CP, CS, V, I, DB> | undefined {
|
|
58
|
+
return this.#connections.get(id);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get connsWithPersistChanged(): Set<ConnId> {
|
|
62
|
+
return this.#connsWithPersistChanged;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
clearConnWithPersistChanged() {
|
|
66
|
+
this.#connsWithPersistChanged.clear();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
markConnWithPersistChanged(conn: Conn<S, CP, CS, V, I, DB>) {
|
|
70
|
+
invariant(
|
|
71
|
+
conn.isHibernatable,
|
|
72
|
+
"cannot mark non-hibernatable conn for persist",
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
this.#actor.rLog.debug({
|
|
76
|
+
msg: "marked connection as changed",
|
|
77
|
+
connId: conn.id,
|
|
78
|
+
totalChanged: this.#connsWithPersistChanged.size,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
this.#connsWithPersistChanged.add(conn.id);
|
|
82
|
+
|
|
83
|
+
this.#actor.stateManager.savePersistThrottled();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// MARK: - Connection Lifecycle
|
|
87
|
+
/**
|
|
88
|
+
* Handles pre-connection logic (i.e. auth & create state) before actually connecting the connection.
|
|
89
|
+
*/
|
|
90
|
+
async prepareConn(
|
|
91
|
+
driver: ConnDriver,
|
|
92
|
+
params: CP,
|
|
93
|
+
request: Request | undefined,
|
|
94
|
+
requestPath: string | undefined,
|
|
95
|
+
requestHeaders: Record<string, string> | undefined,
|
|
96
|
+
isHibernatable: boolean,
|
|
97
|
+
isRestoringHibernatable: boolean,
|
|
98
|
+
): Promise<Conn<S, CP, CS, V, I, DB>> {
|
|
99
|
+
this.#actor.assertReady();
|
|
100
|
+
|
|
101
|
+
// TODO: Add back
|
|
102
|
+
// const url = request?.url;
|
|
103
|
+
// invariant(
|
|
104
|
+
// url?.startsWith("http://actor/") ?? true,
|
|
105
|
+
// `url ${url} must start with 'http://actor/'`,
|
|
106
|
+
// );
|
|
107
|
+
|
|
108
|
+
// Check for hibernatable websocket reconnection
|
|
109
|
+
if (isRestoringHibernatable) {
|
|
110
|
+
return this.#reconnectHibernatableConn(driver);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Create new connection
|
|
114
|
+
if (this.#actor.config.onBeforeConnect) {
|
|
115
|
+
const ctx = new OnBeforeConnectContext(this.#actor, request);
|
|
116
|
+
await this.#actor.config.onBeforeConnect(ctx, params);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Create connection state if enabled
|
|
120
|
+
let connState: CS | undefined;
|
|
121
|
+
if (this.#actor.connStateEnabled) {
|
|
122
|
+
connState = await this.#createConnState(params, request);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Create connection persist data
|
|
126
|
+
let connData: ConnDataInput<CP, CS>;
|
|
127
|
+
if (isHibernatable) {
|
|
128
|
+
const hibernatable = driver.hibernatable;
|
|
129
|
+
invariant(hibernatable, "must have hibernatable");
|
|
130
|
+
invariant(requestPath, "missing requestPath for hibernatable ws");
|
|
131
|
+
invariant(
|
|
132
|
+
requestHeaders,
|
|
133
|
+
"missing requestHeaders for hibernatable ws",
|
|
134
|
+
);
|
|
135
|
+
connData = {
|
|
136
|
+
hibernatable: {
|
|
137
|
+
id: crypto.randomUUID(),
|
|
138
|
+
parameters: params,
|
|
139
|
+
state: connState as CS,
|
|
140
|
+
subscriptions: [],
|
|
141
|
+
gatewayId: hibernatable.gatewayId,
|
|
142
|
+
requestId: hibernatable.requestId,
|
|
143
|
+
clientMessageIndex: 0,
|
|
144
|
+
// First message index will be 1, so we start at 0
|
|
145
|
+
serverMessageIndex: 0,
|
|
146
|
+
requestPath,
|
|
147
|
+
requestHeaders,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
} else {
|
|
151
|
+
connData = {
|
|
152
|
+
ephemeral: {
|
|
153
|
+
id: crypto.randomUUID(),
|
|
154
|
+
parameters: params,
|
|
155
|
+
state: connState as CS,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Create connection instance
|
|
161
|
+
const conn = new Conn<S, CP, CS, V, I, DB>(this.#actor, connData);
|
|
162
|
+
conn[CONN_DRIVER_SYMBOL] = driver;
|
|
163
|
+
|
|
164
|
+
return conn;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Adds a connection form prepareConn to the actor and calls onConnect.
|
|
169
|
+
*
|
|
170
|
+
* This method is intentionally not async since it needs to be called in
|
|
171
|
+
* `onOpen` for WebSockets. If this is async, the order of open events will
|
|
172
|
+
* be messed up and cause race conditions that can drop WebSocket messages.
|
|
173
|
+
* So all async work in prepareConn.
|
|
174
|
+
*/
|
|
175
|
+
connectConn(conn: Conn<S, CP, CS, V, I, DB>) {
|
|
176
|
+
invariant(!this.#connections.has(conn.id), "conn already connected");
|
|
177
|
+
|
|
178
|
+
this.#connections.set(conn.id, conn);
|
|
179
|
+
|
|
180
|
+
// Notify driver about new connection BEFORE marking as changed
|
|
181
|
+
//
|
|
182
|
+
// This ensures the driver can set up any necessary state (like #hwsMessageIndex)
|
|
183
|
+
// before saveState is triggered by markConnWithPersistChanged
|
|
184
|
+
if (this.#actor.driver.onCreateConn) {
|
|
185
|
+
this.#actor.driver.onCreateConn(conn);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (conn.isHibernatable) {
|
|
189
|
+
this.markConnWithPersistChanged(conn);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.#callOnConnect(conn);
|
|
193
|
+
|
|
194
|
+
this.#actor.inspector.emitter.emit("connectionUpdated");
|
|
195
|
+
|
|
196
|
+
this.#actor.resetSleepTimer();
|
|
197
|
+
|
|
198
|
+
conn[CONN_CONNECTED_SYMBOL] = true;
|
|
199
|
+
|
|
200
|
+
// Send init message
|
|
201
|
+
if (conn[CONN_SPEAKS_RIVETKIT_SYMBOL]) {
|
|
202
|
+
const initData = { actorId: this.#actor.id, connectionId: conn.id };
|
|
203
|
+
conn[CONN_SEND_MESSAGE_SYMBOL](
|
|
204
|
+
new CachedSerializer(
|
|
205
|
+
initData,
|
|
206
|
+
TO_CLIENT_VERSIONED,
|
|
207
|
+
ToClientSchema,
|
|
208
|
+
// JSON: identity conversion (no nested data to encode)
|
|
209
|
+
(value) => ({
|
|
210
|
+
body: {
|
|
211
|
+
tag: "Init" as const,
|
|
212
|
+
val: value,
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
215
|
+
// BARE/CBOR: identity conversion (no nested data to encode)
|
|
216
|
+
(value) => ({
|
|
217
|
+
body: {
|
|
218
|
+
tag: "Init" as const,
|
|
219
|
+
val: value,
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#reconnectHibernatableConn(driver: ConnDriver): Conn<S, CP, CS, V, I, DB> {
|
|
228
|
+
invariant(driver.hibernatable, "missing requestIdBuf");
|
|
229
|
+
const existingConn = this.findHibernatableConn(
|
|
230
|
+
driver.hibernatable.gatewayId,
|
|
231
|
+
driver.hibernatable.requestId,
|
|
232
|
+
);
|
|
233
|
+
invariant(
|
|
234
|
+
existingConn,
|
|
235
|
+
"cannot find connection for restoring connection",
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
this.#actor.rLog.debug({
|
|
239
|
+
msg: "reconnecting hibernatable websocket connection",
|
|
240
|
+
connectionId: existingConn.id,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Clean up existing driver state if present
|
|
244
|
+
if (existingConn[CONN_DRIVER_SYMBOL]) {
|
|
245
|
+
this.#disconnectExistingDriver(existingConn);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Update connection with new socket
|
|
249
|
+
existingConn[CONN_DRIVER_SYMBOL] = driver;
|
|
250
|
+
|
|
251
|
+
// Reset sleep timer since we have an active connection
|
|
252
|
+
this.#actor.resetSleepTimer();
|
|
253
|
+
|
|
254
|
+
// Mark connection as connected
|
|
255
|
+
existingConn[CONN_CONNECTED_SYMBOL] = true;
|
|
256
|
+
|
|
257
|
+
this.#actor.inspector.emitter.emit("connectionUpdated");
|
|
258
|
+
|
|
259
|
+
return existingConn;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#disconnectExistingDriver(conn: Conn<S, CP, CS, V, I, DB>) {
|
|
263
|
+
const driver = conn[CONN_DRIVER_SYMBOL];
|
|
264
|
+
if (driver?.disconnect) {
|
|
265
|
+
driver.disconnect(
|
|
266
|
+
this.#actor,
|
|
267
|
+
conn,
|
|
268
|
+
"Reconnecting hibernatable websocket with new driver state",
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Handle connection disconnection.
|
|
275
|
+
*
|
|
276
|
+
* This is called by `Conn.disconnect`. This should not call `Conn.disconnect.`
|
|
277
|
+
*/
|
|
278
|
+
async connDisconnected(conn: Conn<S, CP, CS, V, I, DB>) {
|
|
279
|
+
// Remove from tracking
|
|
280
|
+
this.#connections.delete(conn.id);
|
|
281
|
+
|
|
282
|
+
this.#actor.rLog.debug({ msg: "removed conn", connId: conn.id });
|
|
283
|
+
|
|
284
|
+
// Notify driver about connection removal
|
|
285
|
+
if (this.#actor.driver.onDestroyConn) {
|
|
286
|
+
this.#actor.driver.onDestroyConn(conn);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
for (const eventName of [...conn.subscriptions.values()]) {
|
|
290
|
+
this.#actor.eventManager.removeSubscription(eventName, conn, true);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
this.#actor.resetSleepTimer();
|
|
294
|
+
|
|
295
|
+
this.#actor.inspector.emitter.emit("connectionUpdated");
|
|
296
|
+
|
|
297
|
+
// Trigger disconnect
|
|
298
|
+
if (this.#actor.config.onDisconnect) {
|
|
299
|
+
try {
|
|
300
|
+
const result = this.#actor.config.onDisconnect(
|
|
301
|
+
this.#actor.actorContext,
|
|
302
|
+
conn,
|
|
303
|
+
);
|
|
304
|
+
if (result instanceof Promise) {
|
|
305
|
+
result.catch((error) => {
|
|
306
|
+
this.#actor.rLog.error({
|
|
307
|
+
msg: "error in `onDisconnect`",
|
|
308
|
+
error: stringifyError(error),
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
} catch (error) {
|
|
313
|
+
this.#actor.rLog.error({
|
|
314
|
+
msg: "error in `onDisconnect`",
|
|
315
|
+
error: stringifyError(error),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Remove from connsWithPersistChanged after onDisconnect to handle any
|
|
321
|
+
// state changes made during the disconnect callback. Disconnected connections
|
|
322
|
+
// are removed from KV storage via kvBatchDelete below, not through the
|
|
323
|
+
// normal persist save flow, so they should not trigger persist saves.
|
|
324
|
+
this.#connsWithPersistChanged.delete(conn.id);
|
|
325
|
+
|
|
326
|
+
// Remove from KV storage
|
|
327
|
+
if (conn.isHibernatable) {
|
|
328
|
+
const key = makeConnKey(conn.id);
|
|
329
|
+
try {
|
|
330
|
+
await this.#actor.driver.kvBatchDelete(this.#actor.id, [key]);
|
|
331
|
+
this.#actor.rLog.debug({
|
|
332
|
+
msg: "removed connection from KV",
|
|
333
|
+
connId: conn.id,
|
|
334
|
+
});
|
|
335
|
+
} catch (err) {
|
|
336
|
+
this.#actor.rLog.error({
|
|
337
|
+
msg: "kvBatchDelete failed for conn",
|
|
338
|
+
err: stringifyError(err),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Utilify function for call sites that don't need a separate prepare and connect phase.
|
|
346
|
+
*/
|
|
347
|
+
async prepareAndConnectConn(
|
|
348
|
+
driver: ConnDriver,
|
|
349
|
+
params: CP,
|
|
350
|
+
request: Request | undefined,
|
|
351
|
+
requestPath: string | undefined,
|
|
352
|
+
requestHeaders: Record<string, string> | undefined,
|
|
353
|
+
): Promise<Conn<S, CP, CS, V, I, DB>> {
|
|
354
|
+
const conn = await this.prepareConn(
|
|
355
|
+
driver,
|
|
356
|
+
params,
|
|
357
|
+
request,
|
|
358
|
+
requestPath,
|
|
359
|
+
requestHeaders,
|
|
360
|
+
false,
|
|
361
|
+
false,
|
|
362
|
+
);
|
|
363
|
+
this.connectConn(conn);
|
|
364
|
+
return conn;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// MARK: - Persistence
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Restores connections from persisted data during actor initialization.
|
|
371
|
+
*/
|
|
372
|
+
restoreConnections(connections: PersistedConn<CP, CS>[]) {
|
|
373
|
+
for (const connPersist of connections) {
|
|
374
|
+
// Create connection instance
|
|
375
|
+
const conn = new Conn<S, CP, CS, V, I, DB>(this.#actor, {
|
|
376
|
+
hibernatable: connPersist,
|
|
377
|
+
});
|
|
378
|
+
this.#connections.set(conn.id, conn);
|
|
379
|
+
|
|
380
|
+
// Notify driver about restored connection
|
|
381
|
+
if (this.#actor.driver.onCreateConn) {
|
|
382
|
+
this.#actor.driver.onCreateConn(conn);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Restore subscriptions
|
|
386
|
+
for (const sub of connPersist.subscriptions) {
|
|
387
|
+
this.#actor.eventManager.addSubscription(
|
|
388
|
+
sub.eventName,
|
|
389
|
+
conn,
|
|
390
|
+
true,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// MARK: - Private Helpers
|
|
397
|
+
|
|
398
|
+
findHibernatableConn(
|
|
399
|
+
gatewayIdBuf: ArrayBuffer,
|
|
400
|
+
requestIdBuf: ArrayBuffer,
|
|
401
|
+
): Conn<S, CP, CS, V, I, DB> | undefined {
|
|
402
|
+
return Array.from(this.#connections.values()).find((conn) => {
|
|
403
|
+
const connStateManager = conn[CONN_STATE_MANAGER_SYMBOL];
|
|
404
|
+
const h = connStateManager.hibernatableDataRaw;
|
|
405
|
+
return (
|
|
406
|
+
h &&
|
|
407
|
+
arrayBuffersEqual(h.gatewayId, gatewayIdBuf) &&
|
|
408
|
+
arrayBuffersEqual(h.requestId, requestIdBuf)
|
|
409
|
+
);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async #createConnState(
|
|
414
|
+
params: CP,
|
|
415
|
+
request: Request | undefined,
|
|
416
|
+
): Promise<CS | undefined> {
|
|
417
|
+
if ("createConnState" in this.#actor.config) {
|
|
418
|
+
const ctx = new CreateConnStateContext(this.#actor, request);
|
|
419
|
+
const dataOrPromise = this.#actor.config.createConnState(
|
|
420
|
+
ctx,
|
|
421
|
+
params,
|
|
422
|
+
);
|
|
423
|
+
if (dataOrPromise instanceof Promise) {
|
|
424
|
+
return await deadline(
|
|
425
|
+
dataOrPromise,
|
|
426
|
+
this.#actor.config.options.createConnStateTimeout,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
return dataOrPromise;
|
|
430
|
+
} else if ("connState" in this.#actor.config) {
|
|
431
|
+
return structuredClone(this.#actor.config.connState);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
throw new Error(
|
|
435
|
+
"Could not create connection state from 'createConnState' or 'connState'",
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
#callOnConnect(conn: Conn<S, CP, CS, V, I, DB>) {
|
|
440
|
+
if (this.#actor.config.onConnect) {
|
|
441
|
+
try {
|
|
442
|
+
const ctx = new OnConnectContext(this.#actor, conn);
|
|
443
|
+
const result = this.#actor.config.onConnect(ctx, conn);
|
|
444
|
+
if (result instanceof Promise) {
|
|
445
|
+
deadline(
|
|
446
|
+
result,
|
|
447
|
+
this.#actor.config.options.onConnectTimeout,
|
|
448
|
+
).catch((error) => {
|
|
449
|
+
this.#actor.rLog.error({
|
|
450
|
+
msg: "error in `onConnect`, closing socket",
|
|
451
|
+
error,
|
|
452
|
+
});
|
|
453
|
+
conn?.disconnect("`onConnect` failed");
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
} catch (error) {
|
|
457
|
+
this.#actor.rLog.error({
|
|
458
|
+
msg: "error in `onConnect`",
|
|
459
|
+
error: stringifyError(error),
|
|
460
|
+
});
|
|
461
|
+
conn?.disconnect("`onConnect` failed");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|