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.
Files changed (228) hide show
  1. package/dist/schemas/actor-persist/v2.ts +3 -3
  2. package/dist/schemas/actor-persist/v3.ts +274 -0
  3. package/dist/schemas/client-protocol/v2.ts +432 -0
  4. package/dist/schemas/file-system-driver/v2.ts +136 -0
  5. package/dist/tsup/actor/errors.cjs +2 -4
  6. package/dist/tsup/actor/errors.cjs.map +1 -1
  7. package/dist/tsup/actor/errors.d.cts +7 -10
  8. package/dist/tsup/actor/errors.d.ts +7 -10
  9. package/dist/tsup/actor/errors.js +9 -11
  10. package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.cts → actor-router-consts-DzI2szci.d.cts} +5 -9
  11. package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.ts → actor-router-consts-DzI2szci.d.ts} +5 -9
  12. package/dist/tsup/{chunk-HHFKKVLR.cjs → chunk-3543NCSN.cjs} +45 -57
  13. package/dist/tsup/chunk-3543NCSN.cjs.map +1 -0
  14. package/dist/tsup/chunk-4SHILYS5.cjs +5694 -0
  15. package/dist/tsup/chunk-4SHILYS5.cjs.map +1 -0
  16. package/dist/tsup/{chunk-ZTH3KYFH.cjs → chunk-5BZO5XPS.cjs} +3 -3
  17. package/dist/tsup/{chunk-ZTH3KYFH.cjs.map → chunk-5BZO5XPS.cjs.map} +1 -1
  18. package/dist/tsup/{chunk-PLUN2NQT.js → chunk-BAIGSF64.js} +189 -187
  19. package/dist/tsup/chunk-BAIGSF64.js.map +1 -0
  20. package/dist/tsup/{chunk-SHVX2QUR.cjs → chunk-CHLZBSI2.cjs} +17 -17
  21. package/dist/tsup/chunk-CHLZBSI2.cjs.map +1 -0
  22. package/dist/tsup/chunk-D3SLADUD.cjs +512 -0
  23. package/dist/tsup/chunk-D3SLADUD.cjs.map +1 -0
  24. package/dist/tsup/{chunk-KSRXX3Z4.cjs → chunk-D6762AOA.cjs} +20 -25
  25. package/dist/tsup/chunk-D6762AOA.cjs.map +1 -0
  26. package/dist/tsup/{chunk-7L65NNWP.cjs → chunk-DLK5YCTN.cjs} +187 -185
  27. package/dist/tsup/chunk-DLK5YCTN.cjs.map +1 -0
  28. package/dist/tsup/{chunk-YBG6R7LX.js → chunk-DUJQWGYD.js} +3 -7
  29. package/dist/tsup/chunk-DUJQWGYD.js.map +1 -0
  30. package/dist/tsup/{chunk-CD33GT6Z.js → chunk-EIPANQMF.js} +2 -2
  31. package/dist/tsup/{chunk-2JYPS5YM.cjs → chunk-ESMTDP7G.cjs} +6 -6
  32. package/dist/tsup/chunk-ESMTDP7G.cjs.map +1 -0
  33. package/dist/tsup/{chunk-VHGY7PU5.cjs → chunk-FVAKREFB.cjs} +1900 -1737
  34. package/dist/tsup/chunk-FVAKREFB.cjs.map +1 -0
  35. package/dist/tsup/{chunk-BLK27ES3.js → chunk-I3XT7WOF.js} +44 -56
  36. package/dist/tsup/chunk-I3XT7WOF.js.map +1 -0
  37. package/dist/tsup/{chunk-YBHYXIP6.js → chunk-IMDS5T42.js} +3 -3
  38. package/dist/tsup/chunk-IMDS5T42.js.map +1 -0
  39. package/dist/tsup/{chunk-INNFK746.cjs → chunk-J3HZJF2P.cjs} +10 -14
  40. package/dist/tsup/chunk-J3HZJF2P.cjs.map +1 -0
  41. package/dist/tsup/{chunk-BYMKMOBS.js → chunk-MBBJUHSP.js} +1844 -1681
  42. package/dist/tsup/chunk-MBBJUHSP.js.map +1 -0
  43. package/dist/tsup/{chunk-BOMZS2TJ.js → chunk-MO5CB6MD.js} +9 -9
  44. package/dist/tsup/chunk-MO5CB6MD.js.map +1 -0
  45. package/dist/tsup/chunk-OFOTPKAH.js +512 -0
  46. package/dist/tsup/chunk-OFOTPKAH.js.map +1 -0
  47. package/dist/tsup/{chunk-G64QUEDJ.js → chunk-W6RDS6NW.js} +23 -28
  48. package/dist/tsup/chunk-W6RDS6NW.js.map +1 -0
  49. package/dist/tsup/{chunk-36JJ4IQB.cjs → chunk-YC5DUHPM.cjs} +4 -8
  50. package/dist/tsup/chunk-YC5DUHPM.cjs.map +1 -0
  51. package/dist/tsup/{chunk-FX7TWFQR.js → chunk-YC7YPM2T.js} +2 -6
  52. package/dist/tsup/chunk-YC7YPM2T.js.map +1 -0
  53. package/dist/tsup/{chunk-227FEWMB.js → chunk-ZSPU5R4C.js} +3322 -2251
  54. package/dist/tsup/chunk-ZSPU5R4C.js.map +1 -0
  55. package/dist/tsup/client/mod.cjs +9 -9
  56. package/dist/tsup/client/mod.d.cts +5 -7
  57. package/dist/tsup/client/mod.d.ts +5 -7
  58. package/dist/tsup/client/mod.js +8 -8
  59. package/dist/tsup/common/log.cjs +3 -3
  60. package/dist/tsup/common/log.js +2 -2
  61. package/dist/tsup/common/websocket.cjs +4 -4
  62. package/dist/tsup/common/websocket.js +3 -3
  63. package/dist/tsup/{conn-B3Vhbgnd.d.ts → config-BRDYDraU.d.cts} +1119 -1047
  64. package/dist/tsup/{conn-DJWL3nGx.d.cts → config-Bo-blHpJ.d.ts} +1119 -1047
  65. package/dist/tsup/driver-helpers/mod.cjs +5 -13
  66. package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
  67. package/dist/tsup/driver-helpers/mod.d.cts +11 -9
  68. package/dist/tsup/driver-helpers/mod.d.ts +11 -9
  69. package/dist/tsup/driver-helpers/mod.js +14 -22
  70. package/dist/tsup/driver-test-suite/mod.cjs +474 -303
  71. package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
  72. package/dist/tsup/driver-test-suite/mod.d.cts +6 -9
  73. package/dist/tsup/driver-test-suite/mod.d.ts +6 -9
  74. package/dist/tsup/driver-test-suite/mod.js +1085 -914
  75. package/dist/tsup/driver-test-suite/mod.js.map +1 -1
  76. package/dist/tsup/inspector/mod.cjs +6 -6
  77. package/dist/tsup/inspector/mod.d.cts +5 -7
  78. package/dist/tsup/inspector/mod.d.ts +5 -7
  79. package/dist/tsup/inspector/mod.js +5 -5
  80. package/dist/tsup/mod.cjs +10 -16
  81. package/dist/tsup/mod.cjs.map +1 -1
  82. package/dist/tsup/mod.d.cts +23 -25
  83. package/dist/tsup/mod.d.ts +23 -25
  84. package/dist/tsup/mod.js +17 -23
  85. package/dist/tsup/test/mod.cjs +11 -11
  86. package/dist/tsup/test/mod.d.cts +4 -6
  87. package/dist/tsup/test/mod.d.ts +4 -6
  88. package/dist/tsup/test/mod.js +10 -10
  89. package/dist/tsup/utils.cjs +3 -5
  90. package/dist/tsup/utils.cjs.map +1 -1
  91. package/dist/tsup/utils.d.cts +1 -2
  92. package/dist/tsup/utils.d.ts +1 -2
  93. package/dist/tsup/utils.js +2 -4
  94. package/package.json +13 -6
  95. package/src/actor/config.ts +56 -44
  96. package/src/actor/conn/driver.ts +61 -0
  97. package/src/actor/conn/drivers/http.ts +17 -0
  98. package/src/actor/conn/drivers/raw-request.ts +24 -0
  99. package/src/actor/conn/drivers/raw-websocket.ts +65 -0
  100. package/src/actor/conn/drivers/websocket.ts +129 -0
  101. package/src/actor/conn/mod.ts +232 -0
  102. package/src/actor/conn/persisted.ts +81 -0
  103. package/src/actor/conn/state-manager.ts +196 -0
  104. package/src/actor/contexts/action.ts +23 -0
  105. package/src/actor/{context.ts → contexts/actor.ts} +19 -8
  106. package/src/actor/contexts/conn-init.ts +31 -0
  107. package/src/actor/contexts/conn.ts +48 -0
  108. package/src/actor/contexts/create-conn-state.ts +13 -0
  109. package/src/actor/contexts/on-before-connect.ts +13 -0
  110. package/src/actor/contexts/on-connect.ts +22 -0
  111. package/src/actor/contexts/request.ts +48 -0
  112. package/src/actor/contexts/websocket.ts +48 -0
  113. package/src/actor/definition.ts +3 -3
  114. package/src/actor/driver.ts +36 -5
  115. package/src/actor/errors.ts +19 -24
  116. package/src/actor/instance/connection-manager.ts +465 -0
  117. package/src/actor/instance/event-manager.ts +292 -0
  118. package/src/actor/instance/kv.ts +15 -0
  119. package/src/actor/instance/mod.ts +1107 -0
  120. package/src/actor/instance/persisted.ts +67 -0
  121. package/src/actor/instance/schedule-manager.ts +349 -0
  122. package/src/actor/instance/state-manager.ts +502 -0
  123. package/src/actor/mod.ts +13 -16
  124. package/src/actor/protocol/old.ts +131 -43
  125. package/src/actor/protocol/serde.ts +19 -4
  126. package/src/actor/router-endpoints.ts +61 -586
  127. package/src/actor/router-websocket-endpoints.ts +408 -0
  128. package/src/actor/router.ts +63 -197
  129. package/src/actor/schedule.ts +1 -1
  130. package/src/client/actor-conn.ts +183 -249
  131. package/src/client/actor-handle.ts +29 -6
  132. package/src/client/client.ts +0 -4
  133. package/src/client/config.ts +1 -4
  134. package/src/client/mod.ts +0 -1
  135. package/src/client/raw-utils.ts +3 -3
  136. package/src/client/utils.ts +85 -39
  137. package/src/common/actor-router-consts.ts +5 -12
  138. package/src/common/{inline-websocket-adapter2.ts → inline-websocket-adapter.ts} +26 -48
  139. package/src/common/log.ts +1 -1
  140. package/src/common/router.ts +28 -17
  141. package/src/common/utils.ts +2 -0
  142. package/src/driver-helpers/mod.ts +7 -10
  143. package/src/driver-helpers/utils.ts +18 -9
  144. package/src/driver-test-suite/mod.ts +26 -50
  145. package/src/driver-test-suite/test-inline-client-driver.ts +27 -51
  146. package/src/driver-test-suite/tests/actor-conn-hibernation.ts +150 -0
  147. package/src/driver-test-suite/tests/actor-conn-state.ts +1 -4
  148. package/src/driver-test-suite/tests/actor-conn.ts +5 -9
  149. package/src/driver-test-suite/tests/actor-destroy.ts +294 -0
  150. package/src/driver-test-suite/tests/actor-driver.ts +0 -7
  151. package/src/driver-test-suite/tests/actor-handle.ts +12 -12
  152. package/src/driver-test-suite/tests/actor-metadata.ts +1 -1
  153. package/src/driver-test-suite/tests/manager-driver.ts +1 -1
  154. package/src/driver-test-suite/tests/raw-http-direct-registry.ts +8 -8
  155. package/src/driver-test-suite/tests/raw-http-request-properties.ts +6 -5
  156. package/src/driver-test-suite/tests/raw-http.ts +5 -5
  157. package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +7 -7
  158. package/src/driver-test-suite/tests/request-access.ts +4 -4
  159. package/src/driver-test-suite/utils.ts +6 -10
  160. package/src/drivers/engine/actor-driver.ts +614 -424
  161. package/src/drivers/engine/mod.ts +0 -1
  162. package/src/drivers/file-system/actor.ts +24 -12
  163. package/src/drivers/file-system/global-state.ts +427 -37
  164. package/src/drivers/file-system/manager.ts +71 -83
  165. package/src/drivers/file-system/mod.ts +3 -0
  166. package/src/drivers/file-system/utils.ts +18 -8
  167. package/src/engine-process/mod.ts +38 -38
  168. package/src/inspector/utils.ts +7 -5
  169. package/src/manager/driver.ts +11 -4
  170. package/src/manager/gateway.ts +4 -29
  171. package/src/manager/protocol/mod.ts +0 -2
  172. package/src/manager/protocol/query.ts +0 -4
  173. package/src/manager/router.ts +67 -64
  174. package/src/manager-api/actors.ts +13 -0
  175. package/src/mod.ts +1 -3
  176. package/src/registry/mod.ts +20 -20
  177. package/src/registry/serve.ts +9 -14
  178. package/src/remote-manager-driver/actor-websocket-client.ts +1 -16
  179. package/src/remote-manager-driver/api-endpoints.ts +13 -1
  180. package/src/remote-manager-driver/api-utils.ts +8 -0
  181. package/src/remote-manager-driver/metadata.ts +58 -0
  182. package/src/remote-manager-driver/mod.ts +47 -62
  183. package/src/remote-manager-driver/ws-proxy.ts +1 -1
  184. package/src/schemas/actor-persist/mod.ts +1 -1
  185. package/src/schemas/actor-persist/versioned.ts +56 -31
  186. package/src/schemas/client-protocol/mod.ts +1 -1
  187. package/src/schemas/client-protocol/versioned.ts +41 -21
  188. package/src/schemas/client-protocol-zod/mod.ts +103 -0
  189. package/src/schemas/file-system-driver/mod.ts +1 -1
  190. package/src/schemas/file-system-driver/versioned.ts +42 -19
  191. package/src/serde.ts +33 -11
  192. package/src/test/mod.ts +7 -3
  193. package/src/utils/node.ts +173 -0
  194. package/src/utils.ts +0 -4
  195. package/dist/tsup/chunk-227FEWMB.js.map +0 -1
  196. package/dist/tsup/chunk-2JYPS5YM.cjs.map +0 -1
  197. package/dist/tsup/chunk-36JJ4IQB.cjs.map +0 -1
  198. package/dist/tsup/chunk-7L65NNWP.cjs.map +0 -1
  199. package/dist/tsup/chunk-BLK27ES3.js.map +0 -1
  200. package/dist/tsup/chunk-BOMZS2TJ.js.map +0 -1
  201. package/dist/tsup/chunk-BYMKMOBS.js.map +0 -1
  202. package/dist/tsup/chunk-FX7TWFQR.js.map +0 -1
  203. package/dist/tsup/chunk-G64QUEDJ.js.map +0 -1
  204. package/dist/tsup/chunk-HHFKKVLR.cjs.map +0 -1
  205. package/dist/tsup/chunk-INNFK746.cjs.map +0 -1
  206. package/dist/tsup/chunk-KSRXX3Z4.cjs.map +0 -1
  207. package/dist/tsup/chunk-O44LFKSB.cjs +0 -4623
  208. package/dist/tsup/chunk-O44LFKSB.cjs.map +0 -1
  209. package/dist/tsup/chunk-PLUN2NQT.js.map +0 -1
  210. package/dist/tsup/chunk-S4UJG7ZE.js +0 -1119
  211. package/dist/tsup/chunk-S4UJG7ZE.js.map +0 -1
  212. package/dist/tsup/chunk-SHVX2QUR.cjs.map +0 -1
  213. package/dist/tsup/chunk-VFB23BYZ.cjs +0 -1119
  214. package/dist/tsup/chunk-VFB23BYZ.cjs.map +0 -1
  215. package/dist/tsup/chunk-VHGY7PU5.cjs.map +0 -1
  216. package/dist/tsup/chunk-YBG6R7LX.js.map +0 -1
  217. package/dist/tsup/chunk-YBHYXIP6.js.map +0 -1
  218. package/src/actor/action.ts +0 -178
  219. package/src/actor/conn-drivers.ts +0 -216
  220. package/src/actor/conn-socket.ts +0 -8
  221. package/src/actor/conn.ts +0 -272
  222. package/src/actor/instance.ts +0 -2336
  223. package/src/actor/persisted.ts +0 -49
  224. package/src/actor/unstable-react.ts +0 -110
  225. package/src/driver-test-suite/tests/actor-reconnect.ts +0 -170
  226. package/src/drivers/engine/kv.ts +0 -3
  227. package/src/manager/hono-websocket-adapter.ts +0 -393
  228. /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
+ }