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
@@ -6,11 +6,9 @@ import type { AnyActorDefinition } from "@/actor/definition";
6
6
  import { inputDataToBuffer } from "@/actor/protocol/old";
7
7
  import { type Encoding, jsonStringifyCompat } from "@/actor/protocol/serde";
8
8
  import {
9
- HEADER_CONN_ID,
10
9
  HEADER_CONN_PARAMS,
11
- HEADER_CONN_TOKEN,
12
10
  HEADER_ENCODING,
13
- PATH_CONNECT_WEBSOCKET,
11
+ PATH_CONNECT,
14
12
  } from "@/common/actor-router-consts";
15
13
  import { importEventSource } from "@/common/eventsource";
16
14
  import type {
@@ -27,6 +25,12 @@ import {
27
25
  TO_CLIENT_VERSIONED,
28
26
  TO_SERVER_VERSIONED,
29
27
  } from "@/schemas/client-protocol/versioned";
28
+ import {
29
+ type ToClient as ToClientJson,
30
+ ToClientSchema,
31
+ type ToServer as ToServerJson,
32
+ ToServerSchema,
33
+ } from "@/schemas/client-protocol-zod/mod";
30
34
  import {
31
35
  deserializeWithEncoding,
32
36
  encodingIsBinary,
@@ -40,7 +44,7 @@ import {
40
44
  } from "@/utils";
41
45
  import type { ActorDefinitionActions } from "./actor-common";
42
46
  import { queryActor } from "./actor-query";
43
- import { ACTOR_CONNS_SYMBOL, type ClientRaw, TRANSPORT_SYMBOL } from "./client";
47
+ import { ACTOR_CONNS_SYMBOL, type ClientRaw } from "./client";
44
48
  import * as errors from "./errors";
45
49
  import { logger } from "./log";
46
50
  import {
@@ -51,7 +55,7 @@ import {
51
55
 
52
56
  interface ActionInFlight {
53
57
  name: string;
54
- resolve: (response: protocol.ActionResponse) => void;
58
+ resolve: (response: { id: bigint; output: unknown }) => void;
55
59
  reject: (error: Error) => void;
56
60
  }
57
61
 
@@ -79,10 +83,6 @@ export interface SendHttpMessageOpts {
79
83
  signal?: AbortSignal;
80
84
  }
81
85
 
82
- export type ConnTransport =
83
- | { websocket: UniversalWebSocket }
84
- | { sse: UniversalEventSource };
85
-
86
86
  export const CONNECT_SYMBOL = Symbol("connect");
87
87
 
88
88
  /**
@@ -96,17 +96,22 @@ export class ActorConnRaw {
96
96
  /* Will be aborted on dispose. */
97
97
  #abortController = new AbortController();
98
98
 
99
- /** If attempting to connect. Helpful for knowing if in a retry loop when reconnecting. */
100
99
  #connecting = false;
101
100
 
102
- // Connection info, used for reconnection and HTTP requests
103
101
  #actorId?: string;
104
102
  #connectionId?: string;
105
- #connectionToken?: string;
106
-
107
- #transport?: ConnTransport;
108
103
 
109
- #messageQueue: protocol.ToServer[] = [];
104
+ #messageQueue: Array<{
105
+ body:
106
+ | {
107
+ tag: "ActionRequest";
108
+ val: { id: bigint; name: string; args: unknown };
109
+ }
110
+ | {
111
+ tag: "SubscriptionRequest";
112
+ val: { eventName: string; subscribe: boolean };
113
+ };
114
+ }> = [];
110
115
  #actionsInFlight = new Map<number, ActionInFlight>();
111
116
 
112
117
  // biome-ignore lint/suspicious/noExplicitAny: Unknown subscription type
@@ -126,6 +131,8 @@ export class ActorConnRaw {
126
131
  /** Promise used to indicate the socket has connected successfully. This will be rejected if the connection fails. */
127
132
  #onOpenPromise?: ReturnType<typeof promiseWithResolvers<undefined>>;
128
133
 
134
+ #websocket?: UniversalWebSocket;
135
+
129
136
  #client: ClientRaw;
130
137
  #driver: ManagerDriver;
131
138
  #params: unknown;
@@ -181,13 +188,21 @@ export class ActorConnRaw {
181
188
  const actionId = this.#actionIdCounter;
182
189
  this.#actionIdCounter += 1;
183
190
 
184
- const { promise, resolve, reject } =
185
- promiseWithResolvers<protocol.ActionResponse>();
191
+ const { promise, resolve, reject } = promiseWithResolvers<{
192
+ id: bigint;
193
+ output: unknown;
194
+ }>();
186
195
  this.#actionsInFlight.set(actionId, {
187
196
  name: opts.name,
188
197
  resolve,
189
198
  reject,
190
199
  });
200
+ logger().debug({
201
+ msg: "added action to in-flight map",
202
+ actionId,
203
+ actionName: opts.name,
204
+ inFlightCount: this.#actionsInFlight.size,
205
+ });
191
206
 
192
207
  this.#sendMessage({
193
208
  body: {
@@ -195,10 +210,10 @@ export class ActorConnRaw {
195
210
  val: {
196
211
  id: BigInt(actionId),
197
212
  name: opts.name,
198
- args: bufferToArrayBuffer(cbor.encode(opts.args)),
213
+ args: opts.args,
199
214
  },
200
215
  },
201
- } satisfies protocol.ToServer);
216
+ });
202
217
 
203
218
  // TODO: Throw error if disconnect is called
204
219
 
@@ -208,7 +223,7 @@ export class ActorConnRaw {
208
223
  `Request ID ${actionId} does not match response ID ${responseId}`,
209
224
  );
210
225
 
211
- return cbor.decode(new Uint8Array(output)) as Response;
226
+ return output as Response;
212
227
  }
213
228
 
214
229
  /**
@@ -264,14 +279,7 @@ enc
264
279
  throw new Error("#onOpenPromise already defined");
265
280
  this.#onOpenPromise = promiseWithResolvers();
266
281
 
267
- // Connect transport
268
- if (this.#client[TRANSPORT_SYMBOL] === "websocket") {
269
- await this.#connectWebSocket();
270
- } else if (this.#client[TRANSPORT_SYMBOL] === "sse") {
271
- await this.#connectSse();
272
- } else {
273
- assertUnreachable(this.#client[TRANSPORT_SYMBOL]);
274
- }
282
+ await this.#connectWebSocket();
275
283
 
276
284
  // Wait for result
277
285
  await this.#onOpenPromise.promise;
@@ -287,31 +295,19 @@ enc
287
295
  this.#driver,
288
296
  );
289
297
 
290
- // Check if we have connection info for reconnection
291
- const isReconnection = this.#connectionId && this.#connectionToken;
292
- if (isReconnection) {
293
- logger().debug({
294
- msg: "attempting websocket reconnection",
295
- connectionId: this.#connectionId,
296
- });
297
- }
298
-
299
298
  const ws = await this.#driver.openWebSocket(
300
- PATH_CONNECT_WEBSOCKET,
299
+ PATH_CONNECT,
301
300
  actorId,
302
301
  this.#encoding,
303
302
  this.#params,
304
- // Pass connection ID and token for reconnection if available
305
- isReconnection ? this.#connectionId : undefined,
306
- isReconnection ? this.#connectionToken : undefined,
307
303
  );
308
304
  logger().debug({
309
- msg: "transport set to new websocket",
305
+ msg: "opened websocket",
310
306
  connectionId: this.#connectionId,
311
307
  readyState: ws.readyState,
312
308
  messageQueueLength: this.#messageQueue.length,
313
309
  });
314
- this.#transport = { websocket: ws };
310
+ this.#websocket = ws;
315
311
  ws.addEventListener("open", () => {
316
312
  logger().debug({
317
313
  msg: "client websocket open",
@@ -350,70 +346,6 @@ enc
350
346
  });
351
347
  }
352
348
 
353
- async #connectSse() {
354
- const EventSource = await importEventSource();
355
-
356
- // Get the actor ID
357
- const { actorId } = await queryActor(
358
- undefined,
359
- this.#actorQuery,
360
- this.#driver,
361
- );
362
- logger().debug({ msg: "found actor for sse connection", actorId });
363
- invariant(actorId, "Missing actor ID");
364
-
365
- logger().debug({
366
- msg: "opening sse connection",
367
- actorId,
368
- encoding: this.#encoding,
369
- });
370
-
371
- const isReconnection = this.#connectionId && this.#connectionToken;
372
-
373
- const eventSource = new EventSource("http://actor/connect/sse", {
374
- fetch: (input, init) => {
375
- return this.#driver.sendRequest(
376
- actorId,
377
- new Request(input, {
378
- ...init,
379
- headers: {
380
- ...init?.headers,
381
- "User-Agent": httpUserAgent(),
382
- [HEADER_ENCODING]: this.#encoding,
383
- ...(this.#params !== undefined
384
- ? {
385
- [HEADER_CONN_PARAMS]: JSON.stringify(
386
- this.#params,
387
- ),
388
- }
389
- : {}),
390
- ...(isReconnection
391
- ? {
392
- [HEADER_CONN_ID]: this.#connectionId,
393
- [HEADER_CONN_TOKEN]:
394
- this.#connectionToken,
395
- }
396
- : {}),
397
- },
398
- }),
399
- );
400
- },
401
- }) as UniversalEventSource;
402
-
403
- this.#transport = { sse: eventSource };
404
-
405
- eventSource.addEventListener("message", (ev: UniversalMessageEvent) => {
406
- // Ignore pings
407
- if (ev.type === "ping") return;
408
-
409
- this.#handleOnMessage(ev.data);
410
- });
411
-
412
- eventSource.addEventListener("error", (ev: UniversalErrorEvent) => {
413
- this.#handleOnError();
414
- });
415
- }
416
-
417
349
  /** Called by the onopen event from drivers. */
418
350
  #handleOnOpen() {
419
351
  logger().debug({
@@ -470,10 +402,9 @@ enc
470
402
  );
471
403
 
472
404
  if (response.body.tag === "Init") {
473
- // Store connection info for reconnection
405
+ // Store connection info
474
406
  this.#actorId = response.body.val.actorId;
475
407
  this.#connectionId = response.body.val.connectionId;
476
- this.#connectionToken = response.body.val.connectionToken;
477
408
  logger().trace({
478
409
  msg: "received init message",
479
410
  actorId: this.#actorId,
@@ -535,9 +466,11 @@ enc
535
466
  } else if (response.body.tag === "ActionResponse") {
536
467
  // Action response OK
537
468
  const { id: actionId } = response.body.val;
538
- logger().trace({
469
+ logger().debug({
539
470
  msg: "received action response",
540
- actionId,
471
+ actionId: Number(actionId),
472
+ inFlightCount: this.#actionsInFlight.size,
473
+ inFlightIds: Array.from(this.#actionsInFlight.keys()),
541
474
  });
542
475
 
543
476
  const inFlight = this.#takeActionInFlight(Number(actionId));
@@ -598,7 +531,7 @@ enc
598
531
  });
599
532
 
600
533
  const disconnectError = new Error(
601
- wasClean ? "Connection closed" : "Connection lost",
534
+ `${wasClean ? "Connection closed" : "Connection lost"} (code: ${closeEvent.code}, reason: ${closeEvent.reason})`,
602
535
  );
603
536
 
604
537
  for (const actionInfo of this.#actionsInFlight.values()) {
@@ -607,7 +540,7 @@ enc
607
540
  this.#actionsInFlight.clear();
608
541
  }
609
542
 
610
- this.#transport = undefined;
543
+ this.#websocket = undefined;
611
544
 
612
545
  // Automatically reconnect. Skip if already attempting to connect.
613
546
  if (!this.#disposed && !this.#connecting) {
@@ -636,22 +569,39 @@ enc
636
569
  #takeActionInFlight(id: number): ActionInFlight {
637
570
  const inFlight = this.#actionsInFlight.get(id);
638
571
  if (!inFlight) {
572
+ logger().error({
573
+ msg: "action not found in in-flight map",
574
+ lookupId: id,
575
+ inFlightCount: this.#actionsInFlight.size,
576
+ inFlightIds: Array.from(this.#actionsInFlight.keys()),
577
+ inFlightActions: Array.from(
578
+ this.#actionsInFlight.entries(),
579
+ ).map(([id, action]) => ({
580
+ id,
581
+ name: action.name,
582
+ })),
583
+ });
639
584
  throw new errors.InternalError(`No in flight response for ${id}`);
640
585
  }
641
586
  this.#actionsInFlight.delete(id);
587
+ logger().debug({
588
+ msg: "removed action from in-flight map",
589
+ actionId: id,
590
+ actionName: inFlight.name,
591
+ inFlightCount: this.#actionsInFlight.size,
592
+ });
642
593
  return inFlight;
643
594
  }
644
595
 
645
- #dispatchEvent(event: protocol.Event) {
646
- const { name, args: argsRaw } = event;
647
- const args = cbor.decode(new Uint8Array(argsRaw));
596
+ #dispatchEvent(event: { name: string; args: unknown }) {
597
+ const { name, args } = event;
648
598
 
649
599
  const listeners = this.#eventSubscriptions.get(name);
650
600
  if (!listeners) return;
651
601
 
652
602
  // Create a new array to avoid issues with listeners being removed during iteration
653
603
  for (const listener of [...listeners]) {
654
- listener.callback(...args);
604
+ listener.callback(...(args as unknown[]));
655
605
 
656
606
  // Remove if this was a one-time listener
657
607
  if (listener.once) {
@@ -757,18 +707,27 @@ enc
757
707
  };
758
708
  }
759
709
 
760
- #sendMessage(message: protocol.ToServer, opts?: SendHttpMessageOpts) {
710
+ #sendMessage(
711
+ message: {
712
+ body:
713
+ | {
714
+ tag: "ActionRequest";
715
+ val: { id: bigint; name: string; args: unknown };
716
+ }
717
+ | {
718
+ tag: "SubscriptionRequest";
719
+ val: { eventName: string; subscribe: boolean };
720
+ };
721
+ },
722
+ opts?: SendHttpMessageOpts,
723
+ ) {
761
724
  if (this.#disposed) {
762
725
  throw new errors.ActorConnDisposed();
763
726
  }
764
727
 
765
728
  let queueMessage = false;
766
- if (!this.#transport) {
767
- // No transport connected yet
768
- logger().debug({ msg: "no transport, queueing message" });
769
- queueMessage = true;
770
- } else if ("websocket" in this.#transport) {
771
- const readyState = this.#transport.websocket.readyState;
729
+ if (this.#websocket) {
730
+ const readyState = this.#websocket.readyState;
772
731
  logger().debug({
773
732
  msg: "websocket send attempt",
774
733
  readyState,
@@ -790,8 +749,30 @@ enc
790
749
  this.#encoding,
791
750
  message,
792
751
  TO_SERVER_VERSIONED,
752
+ ToServerSchema,
753
+ // JSON: args is the raw value
754
+ (msg): ToServerJson => msg as ToServerJson,
755
+ // BARE: args needs to be CBOR-encoded to ArrayBuffer
756
+ (msg): protocol.ToServer => {
757
+ if (msg.body.tag === "ActionRequest") {
758
+ return {
759
+ body: {
760
+ tag: "ActionRequest",
761
+ val: {
762
+ id: msg.body.val.id,
763
+ name: msg.body.val.name,
764
+ args: bufferToArrayBuffer(
765
+ cbor.encode(msg.body.val.args),
766
+ ),
767
+ },
768
+ },
769
+ };
770
+ } else {
771
+ return msg as protocol.ToServer;
772
+ }
773
+ },
793
774
  );
794
- this.#transport.websocket.send(messageSerialized);
775
+ this.#websocket.send(messageSerialized);
795
776
  logger().trace({
796
777
  msg: "sent websocket message",
797
778
  len: messageLength(messageSerialized),
@@ -813,15 +794,10 @@ enc
813
794
  });
814
795
  queueMessage = true;
815
796
  }
816
- } else if ("sse" in this.#transport) {
817
- if (this.#transport.sse.readyState === 1) {
818
- // Spawn in background since #sendMessage cannot be async
819
- this.#sendHttpMessage(message, opts);
820
- } else {
821
- queueMessage = true;
822
- }
823
797
  } else {
824
- assertUnreachable(this.#transport);
798
+ // No websocket connected yet
799
+ logger().debug({ msg: "no websocket, queueing message" });
800
+ queueMessage = true;
825
801
  }
826
802
 
827
803
  if (!opts?.ephemeral && queueMessage) {
@@ -836,83 +812,23 @@ enc
836
812
  }
837
813
  }
838
814
 
839
- async #sendHttpMessage(
840
- message: protocol.ToServer,
841
- opts?: SendHttpMessageOpts,
842
- ) {
843
- try {
844
- if (!this.#actorId || !this.#connectionId || !this.#connectionToken)
845
- throw new errors.InternalError(
846
- "Missing connection ID or token.",
847
- );
848
-
849
- logger().trace(
850
- getEnvUniversal("_RIVETKIT_LOG_MESSAGE")
851
- ? {
852
- msg: "sent http message",
853
- message: `${jsonStringifyCompat(message).substring(0, 100)}...`,
854
- }
855
- : { msg: "sent http message" },
856
- );
857
-
858
- logger().debug({
859
- msg: "sending http message",
860
- actorId: this.#actorId,
861
- connectionId: this.#connectionId,
862
- });
863
-
864
- // Send an HTTP request to the connections endpoint
865
- await sendHttpRequest({
866
- url: "http://actor/connections/message",
867
- method: "POST",
868
- headers: {
869
- [HEADER_ENCODING]: this.#encoding,
870
- [HEADER_CONN_ID]: this.#connectionId,
871
- [HEADER_CONN_TOKEN]: this.#connectionToken,
872
- },
873
- body: message,
874
- encoding: this.#encoding,
875
- skipParseResponse: true,
876
- customFetch: this.#driver.sendRequest.bind(
877
- this.#driver,
878
- this.#actorId,
879
- ),
880
- requestVersionedDataHandler: TO_SERVER_VERSIONED,
881
- responseVersionedDataHandler: TO_CLIENT_VERSIONED,
882
- });
883
- } catch (error) {
884
- // TODO: This will not automatically trigger a re-broadcast of HTTP events since SSE is separate from the HTTP action
885
-
886
- logger().warn({
887
- msg: "failed to send message, added to queue",
888
- error,
889
- });
890
-
891
- // Assuming the socket is disconnected and will be reconnected soon
892
- //
893
- // Will attempt to resend soon
894
- if (!opts?.ephemeral) {
895
- this.#messageQueue.unshift(message);
896
- }
897
- }
898
- }
899
-
900
- async #parseMessage(data: ConnMessage): Promise<protocol.ToClient> {
901
- invariant(this.#transport, "transport must be defined");
902
-
903
- // Decode base64 since SSE sends raw strings
904
- if (encodingIsBinary(this.#encoding) && "sse" in this.#transport) {
905
- if (typeof data === "string") {
906
- const binaryString = atob(data);
907
- data = new Uint8Array(
908
- [...binaryString].map((char) => char.charCodeAt(0)),
909
- );
910
- } else {
911
- throw new errors.InternalError(
912
- `Expected data to be a string for SSE, got ${data}.`,
913
- );
914
- }
915
- }
815
+ async #parseMessage(data: ConnMessage): Promise<{
816
+ body:
817
+ | { tag: "Init"; val: { actorId: string; connectionId: string } }
818
+ | {
819
+ tag: "Error";
820
+ val: {
821
+ group: string;
822
+ code: string;
823
+ message: string;
824
+ metadata: unknown;
825
+ actionId: bigint | null;
826
+ };
827
+ }
828
+ | { tag: "ActionResponse"; val: { id: bigint; output: unknown } }
829
+ | { tag: "Event"; val: { name: string; args: unknown } };
830
+ }> {
831
+ invariant(this.#websocket, "websocket must be defined");
916
832
 
917
833
  const buffer = await inputDataToBuffer(data);
918
834
 
@@ -920,6 +836,59 @@ enc
920
836
  this.#encoding,
921
837
  buffer,
922
838
  TO_CLIENT_VERSIONED,
839
+ ToClientSchema,
840
+ // JSON: values are already the correct type
841
+ (msg): ToClientJson => msg as ToClientJson,
842
+ // BARE: need to decode ArrayBuffer fields back to unknown
843
+ (msg): any => {
844
+ if (msg.body.tag === "Error") {
845
+ return {
846
+ body: {
847
+ tag: "Error",
848
+ val: {
849
+ group: msg.body.val.group,
850
+ code: msg.body.val.code,
851
+ message: msg.body.val.message,
852
+ metadata: msg.body.val.metadata
853
+ ? cbor.decode(
854
+ new Uint8Array(
855
+ msg.body.val.metadata,
856
+ ),
857
+ )
858
+ : null,
859
+ actionId: msg.body.val.actionId,
860
+ },
861
+ },
862
+ };
863
+ } else if (msg.body.tag === "ActionResponse") {
864
+ return {
865
+ body: {
866
+ tag: "ActionResponse",
867
+ val: {
868
+ id: msg.body.val.id,
869
+ output: cbor.decode(
870
+ new Uint8Array(msg.body.val.output),
871
+ ),
872
+ },
873
+ },
874
+ };
875
+ } else if (msg.body.tag === "Event") {
876
+ return {
877
+ body: {
878
+ tag: "Event",
879
+ val: {
880
+ name: msg.body.val.name,
881
+ args: cbor.decode(
882
+ new Uint8Array(msg.body.val.args),
883
+ ),
884
+ },
885
+ },
886
+ };
887
+ } else {
888
+ // Init has no ArrayBuffer fields
889
+ return msg;
890
+ }
891
+ },
923
892
  );
924
893
  }
925
894
 
@@ -964,13 +933,11 @@ enc
964
933
  // Remove from registry
965
934
  this.#client[ACTOR_CONNS_SYMBOL].delete(this);
966
935
 
967
- // Disconnect transport cleanly
968
- if (!this.#transport) {
969
- // Nothing to do
970
- } else if ("websocket" in this.#transport) {
936
+ // Disconnect websocket cleanly
937
+ if (this.#websocket) {
971
938
  logger().debug("closing ws");
972
939
 
973
- const ws = this.#transport.websocket;
940
+ const ws = this.#websocket;
974
941
  // Check if WebSocket is already closed or closing
975
942
  if (
976
943
  ws.readyState === 2 /* CLOSING */ ||
@@ -986,41 +953,8 @@ enc
986
953
  ws.close(1000, "Normal closure");
987
954
  await promise;
988
955
  }
989
- } else if ("sse" in this.#transport) {
990
- logger().debug("closing sse");
991
-
992
- // Send close request to server for SSE connections
993
- if (this.#connectionId && this.#connectionToken) {
994
- try {
995
- await sendHttpRequest({
996
- url: "http://actor/connections/close",
997
- method: "POST",
998
- headers: {
999
- [HEADER_CONN_ID]: this.#connectionId,
1000
- [HEADER_CONN_TOKEN]: this.#connectionToken,
1001
- },
1002
- encoding: this.#encoding,
1003
- skipParseResponse: true,
1004
- customFetch: this.#driver.sendRequest.bind(
1005
- this.#driver,
1006
- this.#actorId!,
1007
- ),
1008
- requestVersionedDataHandler: TO_SERVER_VERSIONED,
1009
- responseVersionedDataHandler: TO_CLIENT_VERSIONED,
1010
- });
1011
- } catch (error) {
1012
- // Ignore errors when closing - connection may already be closed
1013
- logger().warn({
1014
- msg: "failed to send close request",
1015
- error,
1016
- });
1017
- }
1018
- }
1019
- this.#transport.sse.close();
1020
- } else {
1021
- assertUnreachable(this.#transport);
1022
956
  }
1023
- this.#transport = undefined;
957
+ this.#websocket = undefined;
1024
958
  }
1025
959
 
1026
960
  #sendSubscription(eventName: string, subscribe: boolean) {
@@ -15,6 +15,12 @@ import {
15
15
  HTTP_ACTION_REQUEST_VERSIONED,
16
16
  HTTP_ACTION_RESPONSE_VERSIONED,
17
17
  } from "@/schemas/client-protocol/versioned";
18
+ import {
19
+ type HttpActionRequest as HttpActionRequestJson,
20
+ HttpActionRequestSchema,
21
+ type HttpActionResponse as HttpActionResponseJson,
22
+ HttpActionResponseSchema,
23
+ } from "@/schemas/client-protocol-zod/mod";
18
24
  import { bufferToArrayBuffer } from "@/utils";
19
25
  import type { ActorDefinitionActions } from "./actor-common";
20
26
  import { type ActorConn, ActorConnRaw } from "./actor-conn";
@@ -100,8 +106,12 @@ export class ActorHandleRaw {
100
106
  encoding: this.#encoding,
101
107
  });
102
108
  const responseData = await sendHttpRequest<
103
- protocol.HttpActionRequest,
104
- protocol.HttpActionResponse
109
+ protocol.HttpActionRequest, // Bare type
110
+ protocol.HttpActionResponse, // Bare type
111
+ HttpActionRequestJson, // Json type
112
+ HttpActionResponseJson, // Json type
113
+ unknown[], // Request type (the args array)
114
+ Response // Response type (the output value)
105
115
  >({
106
116
  url: `http://actor/action/${encodeURIComponent(opts.name)}`,
107
117
  method: "POST",
@@ -111,9 +121,7 @@ export class ActorHandleRaw {
111
121
  ? { [HEADER_CONN_PARAMS]: JSON.stringify(this.#params) }
112
122
  : {}),
113
123
  },
114
- body: {
115
- args: bufferToArrayBuffer(cbor.encode(opts.args)),
116
- } satisfies protocol.HttpActionRequest,
124
+ body: opts.args,
117
125
  encoding: this.#encoding,
118
126
  customFetch: this.#driver.sendRequest.bind(
119
127
  this.#driver,
@@ -122,9 +130,24 @@ export class ActorHandleRaw {
122
130
  signal: opts?.signal,
123
131
  requestVersionedDataHandler: HTTP_ACTION_REQUEST_VERSIONED,
124
132
  responseVersionedDataHandler: HTTP_ACTION_RESPONSE_VERSIONED,
133
+ requestZodSchema: HttpActionRequestSchema,
134
+ responseZodSchema: HttpActionResponseSchema,
135
+ // JSON Request: args is the raw value
136
+ requestToJson: (args): HttpActionRequestJson => ({
137
+ args,
138
+ }),
139
+ // BARE Request: args needs to be CBOR-encoded
140
+ requestToBare: (args): protocol.HttpActionRequest => ({
141
+ args: bufferToArrayBuffer(cbor.encode(args)),
142
+ }),
143
+ // JSON Response: output is the raw value
144
+ responseFromJson: (json): Response => json.output as Response,
145
+ // BARE Response: output is ArrayBuffer that needs CBOR-decoding
146
+ responseFromBare: (bare): Response =>
147
+ cbor.decode(new Uint8Array(bare.output)) as Response,
125
148
  });
126
149
 
127
- return cbor.decode(new Uint8Array(responseData.output));
150
+ return responseData;
128
151
  } catch (err) {
129
152
  // Standardize to ClientActorError instead of the native backend error
130
153
  const { group, code, message, metadata } = deconstructError(