rivetkit 2.0.24-rc.1 → 2.0.25-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.
Files changed (231) hide show
  1. package/dist/schemas/actor-persist/v1.ts +6 -0
  2. package/dist/schemas/actor-persist/v2.ts +9 -3
  3. package/dist/schemas/actor-persist/v3.ts +280 -0
  4. package/dist/schemas/client-protocol/v1.ts +6 -0
  5. package/dist/schemas/client-protocol/v2.ts +438 -0
  6. package/dist/schemas/file-system-driver/v1.ts +6 -0
  7. package/dist/schemas/file-system-driver/v2.ts +142 -0
  8. package/dist/tsup/actor/errors.cjs +2 -4
  9. package/dist/tsup/actor/errors.cjs.map +1 -1
  10. package/dist/tsup/actor/errors.d.cts +7 -10
  11. package/dist/tsup/actor/errors.d.ts +7 -10
  12. package/dist/tsup/actor/errors.js +9 -11
  13. package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.cts → actor-router-consts-DzI2szci.d.cts} +5 -9
  14. package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.ts → actor-router-consts-DzI2szci.d.ts} +5 -9
  15. package/dist/tsup/{chunk-ZTH3KYFH.cjs → chunk-3FG5OJ3G.cjs} +3 -3
  16. package/dist/tsup/{chunk-ZTH3KYFH.cjs.map → chunk-3FG5OJ3G.cjs.map} +1 -1
  17. package/dist/tsup/{chunk-BLK27ES3.js → chunk-6JN6W6G3.js} +44 -56
  18. package/dist/tsup/chunk-6JN6W6G3.js.map +1 -0
  19. package/dist/tsup/chunk-7IBNNGQ2.js +514 -0
  20. package/dist/tsup/chunk-7IBNNGQ2.js.map +1 -0
  21. package/dist/tsup/{chunk-36JJ4IQB.cjs → chunk-AZATXPR4.cjs} +4 -8
  22. package/dist/tsup/chunk-AZATXPR4.cjs.map +1 -0
  23. package/dist/tsup/chunk-B7MENRD5.cjs +5694 -0
  24. package/dist/tsup/chunk-B7MENRD5.cjs.map +1 -0
  25. package/dist/tsup/{chunk-BOMZS2TJ.js → chunk-BBVFDEYD.js} +9 -9
  26. package/dist/tsup/chunk-BBVFDEYD.js.map +1 -0
  27. package/dist/tsup/{chunk-KSRXX3Z4.cjs → chunk-D6762AOA.cjs} +20 -25
  28. package/dist/tsup/chunk-D6762AOA.cjs.map +1 -0
  29. package/dist/tsup/{chunk-2JYPS5YM.cjs → chunk-E63WZNMR.cjs} +6 -6
  30. package/dist/tsup/chunk-E63WZNMR.cjs.map +1 -0
  31. package/dist/tsup/{chunk-YBG6R7LX.js → chunk-EDGN4OC7.js} +3 -7
  32. package/dist/tsup/chunk-EDGN4OC7.js.map +1 -0
  33. package/dist/tsup/{chunk-BYMKMOBS.js → chunk-FLOQ3UWM.js} +1844 -1681
  34. package/dist/tsup/chunk-FLOQ3UWM.js.map +1 -0
  35. package/dist/tsup/{chunk-7L65NNWP.cjs → chunk-H7GV5DIW.cjs} +187 -185
  36. package/dist/tsup/chunk-H7GV5DIW.cjs.map +1 -0
  37. package/dist/tsup/{chunk-227FEWMB.js → chunk-HZYZ7JSF.js} +3322 -2251
  38. package/dist/tsup/chunk-HZYZ7JSF.js.map +1 -0
  39. package/dist/tsup/{chunk-FX7TWFQR.js → chunk-IDJK7ILQ.js} +2 -6
  40. package/dist/tsup/chunk-IDJK7ILQ.js.map +1 -0
  41. package/dist/tsup/{chunk-VHGY7PU5.cjs → chunk-ILFXA4AL.cjs} +1900 -1737
  42. package/dist/tsup/chunk-ILFXA4AL.cjs.map +1 -0
  43. package/dist/tsup/chunk-MV6M3FDL.cjs +514 -0
  44. package/dist/tsup/chunk-MV6M3FDL.cjs.map +1 -0
  45. package/dist/tsup/{chunk-PLUN2NQT.js → chunk-NWBKMCWC.js} +189 -187
  46. package/dist/tsup/chunk-NWBKMCWC.js.map +1 -0
  47. package/dist/tsup/{chunk-CD33GT6Z.js → chunk-QIHBDXTO.js} +2 -2
  48. package/dist/tsup/{chunk-G64QUEDJ.js → chunk-W6RDS6NW.js} +23 -28
  49. package/dist/tsup/chunk-W6RDS6NW.js.map +1 -0
  50. package/dist/tsup/{chunk-INNFK746.cjs → chunk-WQU4M4ZC.cjs} +10 -14
  51. package/dist/tsup/chunk-WQU4M4ZC.cjs.map +1 -0
  52. package/dist/tsup/{chunk-SHVX2QUR.cjs → chunk-XKZA47XS.cjs} +17 -17
  53. package/dist/tsup/chunk-XKZA47XS.cjs.map +1 -0
  54. package/dist/tsup/{chunk-HHFKKVLR.cjs → chunk-YHWIOWVA.cjs} +45 -57
  55. package/dist/tsup/chunk-YHWIOWVA.cjs.map +1 -0
  56. package/dist/tsup/{chunk-YBHYXIP6.js → chunk-YVL6IRUM.js} +3 -3
  57. package/dist/tsup/chunk-YVL6IRUM.js.map +1 -0
  58. package/dist/tsup/client/mod.cjs +9 -9
  59. package/dist/tsup/client/mod.d.cts +5 -7
  60. package/dist/tsup/client/mod.d.ts +5 -7
  61. package/dist/tsup/client/mod.js +8 -8
  62. package/dist/tsup/common/log.cjs +3 -3
  63. package/dist/tsup/common/log.js +2 -2
  64. package/dist/tsup/common/websocket.cjs +4 -4
  65. package/dist/tsup/common/websocket.js +3 -3
  66. package/dist/tsup/{conn-B3Vhbgnd.d.ts → config-BRDYDraU.d.cts} +1119 -1047
  67. package/dist/tsup/{conn-DJWL3nGx.d.cts → config-Bo-blHpJ.d.ts} +1119 -1047
  68. package/dist/tsup/driver-helpers/mod.cjs +5 -13
  69. package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
  70. package/dist/tsup/driver-helpers/mod.d.cts +11 -9
  71. package/dist/tsup/driver-helpers/mod.d.ts +11 -9
  72. package/dist/tsup/driver-helpers/mod.js +14 -22
  73. package/dist/tsup/driver-test-suite/mod.cjs +474 -303
  74. package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
  75. package/dist/tsup/driver-test-suite/mod.d.cts +6 -9
  76. package/dist/tsup/driver-test-suite/mod.d.ts +6 -9
  77. package/dist/tsup/driver-test-suite/mod.js +1085 -914
  78. package/dist/tsup/driver-test-suite/mod.js.map +1 -1
  79. package/dist/tsup/inspector/mod.cjs +6 -6
  80. package/dist/tsup/inspector/mod.d.cts +5 -7
  81. package/dist/tsup/inspector/mod.d.ts +5 -7
  82. package/dist/tsup/inspector/mod.js +5 -5
  83. package/dist/tsup/mod.cjs +10 -16
  84. package/dist/tsup/mod.cjs.map +1 -1
  85. package/dist/tsup/mod.d.cts +23 -25
  86. package/dist/tsup/mod.d.ts +23 -25
  87. package/dist/tsup/mod.js +17 -23
  88. package/dist/tsup/test/mod.cjs +11 -11
  89. package/dist/tsup/test/mod.d.cts +4 -6
  90. package/dist/tsup/test/mod.d.ts +4 -6
  91. package/dist/tsup/test/mod.js +10 -10
  92. package/dist/tsup/utils.cjs +3 -5
  93. package/dist/tsup/utils.cjs.map +1 -1
  94. package/dist/tsup/utils.d.cts +1 -2
  95. package/dist/tsup/utils.d.ts +1 -2
  96. package/dist/tsup/utils.js +2 -4
  97. package/package.json +13 -6
  98. package/src/actor/config.ts +56 -44
  99. package/src/actor/conn/driver.ts +61 -0
  100. package/src/actor/conn/drivers/http.ts +17 -0
  101. package/src/actor/conn/drivers/raw-request.ts +24 -0
  102. package/src/actor/conn/drivers/raw-websocket.ts +65 -0
  103. package/src/actor/conn/drivers/websocket.ts +129 -0
  104. package/src/actor/conn/mod.ts +232 -0
  105. package/src/actor/conn/persisted.ts +81 -0
  106. package/src/actor/conn/state-manager.ts +196 -0
  107. package/src/actor/contexts/action.ts +23 -0
  108. package/src/actor/{context.ts → contexts/actor.ts} +19 -8
  109. package/src/actor/contexts/conn-init.ts +31 -0
  110. package/src/actor/contexts/conn.ts +48 -0
  111. package/src/actor/contexts/create-conn-state.ts +13 -0
  112. package/src/actor/contexts/on-before-connect.ts +13 -0
  113. package/src/actor/contexts/on-connect.ts +22 -0
  114. package/src/actor/contexts/request.ts +48 -0
  115. package/src/actor/contexts/websocket.ts +48 -0
  116. package/src/actor/definition.ts +3 -3
  117. package/src/actor/driver.ts +36 -5
  118. package/src/actor/errors.ts +19 -24
  119. package/src/actor/instance/connection-manager.ts +465 -0
  120. package/src/actor/instance/event-manager.ts +292 -0
  121. package/src/actor/instance/kv.ts +15 -0
  122. package/src/actor/instance/mod.ts +1107 -0
  123. package/src/actor/instance/persisted.ts +67 -0
  124. package/src/actor/instance/schedule-manager.ts +349 -0
  125. package/src/actor/instance/state-manager.ts +502 -0
  126. package/src/actor/mod.ts +13 -16
  127. package/src/actor/protocol/old.ts +131 -43
  128. package/src/actor/protocol/serde.ts +19 -4
  129. package/src/actor/router-endpoints.ts +61 -586
  130. package/src/actor/router-websocket-endpoints.ts +408 -0
  131. package/src/actor/router.ts +63 -197
  132. package/src/actor/schedule.ts +1 -1
  133. package/src/client/actor-conn.ts +183 -249
  134. package/src/client/actor-handle.ts +29 -6
  135. package/src/client/client.ts +0 -4
  136. package/src/client/config.ts +1 -4
  137. package/src/client/mod.ts +0 -1
  138. package/src/client/raw-utils.ts +3 -3
  139. package/src/client/utils.ts +85 -39
  140. package/src/common/actor-router-consts.ts +5 -12
  141. package/src/common/{inline-websocket-adapter2.ts → inline-websocket-adapter.ts} +26 -48
  142. package/src/common/log.ts +1 -1
  143. package/src/common/router.ts +28 -17
  144. package/src/common/utils.ts +2 -0
  145. package/src/driver-helpers/mod.ts +7 -10
  146. package/src/driver-helpers/utils.ts +18 -9
  147. package/src/driver-test-suite/mod.ts +26 -50
  148. package/src/driver-test-suite/test-inline-client-driver.ts +27 -51
  149. package/src/driver-test-suite/tests/actor-conn-hibernation.ts +150 -0
  150. package/src/driver-test-suite/tests/actor-conn-state.ts +1 -4
  151. package/src/driver-test-suite/tests/actor-conn.ts +5 -9
  152. package/src/driver-test-suite/tests/actor-destroy.ts +294 -0
  153. package/src/driver-test-suite/tests/actor-driver.ts +0 -7
  154. package/src/driver-test-suite/tests/actor-handle.ts +12 -12
  155. package/src/driver-test-suite/tests/actor-metadata.ts +1 -1
  156. package/src/driver-test-suite/tests/manager-driver.ts +1 -1
  157. package/src/driver-test-suite/tests/raw-http-direct-registry.ts +8 -8
  158. package/src/driver-test-suite/tests/raw-http-request-properties.ts +6 -5
  159. package/src/driver-test-suite/tests/raw-http.ts +5 -5
  160. package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +7 -7
  161. package/src/driver-test-suite/tests/request-access.ts +4 -4
  162. package/src/driver-test-suite/utils.ts +6 -10
  163. package/src/drivers/engine/actor-driver.ts +614 -424
  164. package/src/drivers/engine/mod.ts +0 -1
  165. package/src/drivers/file-system/actor.ts +24 -12
  166. package/src/drivers/file-system/global-state.ts +427 -37
  167. package/src/drivers/file-system/manager.ts +71 -83
  168. package/src/drivers/file-system/mod.ts +3 -0
  169. package/src/drivers/file-system/utils.ts +18 -8
  170. package/src/engine-process/mod.ts +38 -38
  171. package/src/inspector/utils.ts +7 -5
  172. package/src/manager/driver.ts +11 -4
  173. package/src/manager/gateway.ts +4 -29
  174. package/src/manager/protocol/mod.ts +0 -2
  175. package/src/manager/protocol/query.ts +0 -4
  176. package/src/manager/router.ts +67 -64
  177. package/src/manager-api/actors.ts +13 -0
  178. package/src/mod.ts +1 -3
  179. package/src/registry/mod.ts +20 -20
  180. package/src/registry/serve.ts +9 -14
  181. package/src/remote-manager-driver/actor-websocket-client.ts +1 -16
  182. package/src/remote-manager-driver/api-endpoints.ts +13 -1
  183. package/src/remote-manager-driver/api-utils.ts +8 -0
  184. package/src/remote-manager-driver/metadata.ts +58 -0
  185. package/src/remote-manager-driver/mod.ts +47 -62
  186. package/src/remote-manager-driver/ws-proxy.ts +1 -1
  187. package/src/schemas/actor-persist/mod.ts +1 -1
  188. package/src/schemas/actor-persist/versioned.ts +56 -31
  189. package/src/schemas/client-protocol/mod.ts +1 -1
  190. package/src/schemas/client-protocol/versioned.ts +41 -21
  191. package/src/schemas/client-protocol-zod/mod.ts +103 -0
  192. package/src/schemas/file-system-driver/mod.ts +1 -1
  193. package/src/schemas/file-system-driver/versioned.ts +42 -19
  194. package/src/serde.ts +33 -11
  195. package/src/test/mod.ts +7 -3
  196. package/src/utils/node.ts +173 -0
  197. package/src/utils.ts +0 -4
  198. package/dist/tsup/chunk-227FEWMB.js.map +0 -1
  199. package/dist/tsup/chunk-2JYPS5YM.cjs.map +0 -1
  200. package/dist/tsup/chunk-36JJ4IQB.cjs.map +0 -1
  201. package/dist/tsup/chunk-7L65NNWP.cjs.map +0 -1
  202. package/dist/tsup/chunk-BLK27ES3.js.map +0 -1
  203. package/dist/tsup/chunk-BOMZS2TJ.js.map +0 -1
  204. package/dist/tsup/chunk-BYMKMOBS.js.map +0 -1
  205. package/dist/tsup/chunk-FX7TWFQR.js.map +0 -1
  206. package/dist/tsup/chunk-G64QUEDJ.js.map +0 -1
  207. package/dist/tsup/chunk-HHFKKVLR.cjs.map +0 -1
  208. package/dist/tsup/chunk-INNFK746.cjs.map +0 -1
  209. package/dist/tsup/chunk-KSRXX3Z4.cjs.map +0 -1
  210. package/dist/tsup/chunk-O44LFKSB.cjs +0 -4623
  211. package/dist/tsup/chunk-O44LFKSB.cjs.map +0 -1
  212. package/dist/tsup/chunk-PLUN2NQT.js.map +0 -1
  213. package/dist/tsup/chunk-S4UJG7ZE.js +0 -1119
  214. package/dist/tsup/chunk-S4UJG7ZE.js.map +0 -1
  215. package/dist/tsup/chunk-SHVX2QUR.cjs.map +0 -1
  216. package/dist/tsup/chunk-VFB23BYZ.cjs +0 -1119
  217. package/dist/tsup/chunk-VFB23BYZ.cjs.map +0 -1
  218. package/dist/tsup/chunk-VHGY7PU5.cjs.map +0 -1
  219. package/dist/tsup/chunk-YBG6R7LX.js.map +0 -1
  220. package/dist/tsup/chunk-YBHYXIP6.js.map +0 -1
  221. package/src/actor/action.ts +0 -178
  222. package/src/actor/conn-drivers.ts +0 -216
  223. package/src/actor/conn-socket.ts +0 -8
  224. package/src/actor/conn.ts +0 -272
  225. package/src/actor/instance.ts +0 -2336
  226. package/src/actor/persisted.ts +0 -49
  227. package/src/actor/unstable-react.ts +0 -110
  228. package/src/driver-test-suite/tests/actor-reconnect.ts +0 -170
  229. package/src/drivers/engine/kv.ts +0 -3
  230. package/src/manager/hono-websocket-adapter.ts +0 -393
  231. /package/dist/tsup/{chunk-CD33GT6Z.js.map → chunk-QIHBDXTO.js.map} +0 -0
@@ -0,0 +1,502 @@
1
+ import { idToStr } from "@rivetkit/engine-runner";
2
+ import onChange from "on-change";
3
+ import { isCborSerializable, stringifyError } from "@/common/utils";
4
+ import {
5
+ ACTOR_VERSIONED,
6
+ CONN_VERSIONED,
7
+ } from "@/schemas/actor-persist/versioned";
8
+ import { promiseWithResolvers, SinglePromiseQueue } from "@/utils";
9
+ import { type AnyConn, CONN_STATE_MANAGER_SYMBOL } from "../conn/mod";
10
+ import { convertConnToBarePersistedConn } from "../conn/persisted";
11
+ import type { ActorDriver } from "../driver";
12
+ import * as errors from "../errors";
13
+ import { isConnStatePath, isStatePath } from "../utils";
14
+ import { KEYS, makeConnKey } from "./kv";
15
+ import type { ActorInstance } from "./mod";
16
+ import { convertActorToBarePersisted, type PersistedActor } from "./persisted";
17
+
18
+ export interface SaveStateOptions {
19
+ /**
20
+ * Forces the state to be saved immediately. This function will return when the state has saved successfully.
21
+ */
22
+ immediate?: boolean;
23
+ /** Bypass ready check for stopping. */
24
+ allowStoppingState?: boolean;
25
+ /**
26
+ * Maximum time in milliseconds to wait before forcing a save.
27
+ *
28
+ * If a save is already scheduled to occur later than this deadline, it will be rescheduled earlier.
29
+ */
30
+ maxWait?: number;
31
+ }
32
+
33
+ /**
34
+ * Manages actor state persistence, proxying, and synchronization.
35
+ * Handles automatic state change detection and throttled persistence to KV storage.
36
+ */
37
+ export class StateManager<S, CP, CS, I> {
38
+ #actor: ActorInstance<S, CP, CS, any, I, any>;
39
+ #actorDriver: ActorDriver;
40
+
41
+ // State tracking
42
+ #persist!: PersistedActor<S, I>;
43
+ #persistRaw!: PersistedActor<S, I>;
44
+ #persistChanged = false;
45
+ #isInOnStateChange = false;
46
+
47
+ // Save management
48
+ #persistWriteQueue = new SinglePromiseQueue();
49
+ #lastSaveTime = 0;
50
+ #pendingSaveTimeout?: NodeJS.Timeout;
51
+ #pendingSaveScheduledTimestamp?: number;
52
+ #onPersistSavedPromise?: ReturnType<typeof promiseWithResolvers<void>>;
53
+
54
+ // Configuration
55
+ #config: any; // ActorConfig type
56
+ #stateSaveInterval: number;
57
+
58
+ constructor(
59
+ actor: ActorInstance<S, CP, CS, any, I, any>,
60
+ actorDriver: ActorDriver,
61
+ config: any,
62
+ ) {
63
+ this.#actor = actor;
64
+ this.#actorDriver = actorDriver;
65
+ this.#config = config;
66
+ this.#stateSaveInterval = config.options.stateSaveInterval || 100;
67
+ }
68
+
69
+ // MARK: - Public API
70
+
71
+ get persist(): PersistedActor<S, I> {
72
+ return this.#persist;
73
+ }
74
+
75
+ get persistRaw(): PersistedActor<S, I> {
76
+ return this.#persistRaw;
77
+ }
78
+
79
+ get persistChanged(): boolean {
80
+ return this.#persistChanged;
81
+ }
82
+
83
+ get state(): S {
84
+ this.#validateStateEnabled();
85
+ return this.#persist.state;
86
+ }
87
+
88
+ set state(value: S) {
89
+ this.#validateStateEnabled();
90
+ this.#persist.state = value;
91
+ }
92
+
93
+ get stateEnabled(): boolean {
94
+ return "createState" in this.#config || "state" in this.#config;
95
+ }
96
+
97
+ // MARK: - Initialization
98
+
99
+ /**
100
+ * Initializes state from persisted data or creates new state.
101
+ */
102
+ async initializeState(persistData: PersistedActor<S, I>): Promise<void> {
103
+ if (!persistData.hasInitialized) {
104
+ // Create initial state
105
+ let stateData: unknown;
106
+ if (this.stateEnabled) {
107
+ this.#actor.rLog.info({ msg: "actor state initializing" });
108
+
109
+ if ("createState" in this.#config) {
110
+ stateData = await this.#config.createState(
111
+ this.#actor.actorContext,
112
+ persistData.input!,
113
+ );
114
+ } else if ("state" in this.#config) {
115
+ stateData = structuredClone(this.#config.state);
116
+ } else {
117
+ throw new Error(
118
+ "Both 'createState' or 'state' were not defined",
119
+ );
120
+ }
121
+ } else {
122
+ this.#actor.rLog.debug({ msg: "state not enabled" });
123
+ }
124
+
125
+ // Update persisted data
126
+ persistData.state = stateData as S;
127
+ persistData.hasInitialized = true;
128
+
129
+ // Save initial state
130
+ //
131
+ // We don't use #savePersistInner because the actor is not fully
132
+ // initialized yet
133
+ const bareData = convertActorToBarePersisted<S, I>(persistData);
134
+ await this.#actorDriver.kvBatchPut(this.#actor.id, [
135
+ [
136
+ KEYS.PERSIST_DATA,
137
+ ACTOR_VERSIONED.serializeWithEmbeddedVersion(bareData),
138
+ ],
139
+ ]);
140
+ }
141
+
142
+ // Initialize proxy
143
+ this.initPersistProxy(persistData);
144
+ }
145
+
146
+ /**
147
+ * Creates proxy for persist object that handles automatic state change detection.
148
+ */
149
+ initPersistProxy(target: PersistedActor<S, I>) {
150
+ // Set raw persist object
151
+ this.#persistRaw = target;
152
+
153
+ // Validate serializability
154
+ if (target === null || typeof target !== "object") {
155
+ let invalidPath = "";
156
+ if (
157
+ !isCborSerializable(
158
+ target,
159
+ (path) => {
160
+ invalidPath = path;
161
+ },
162
+ "",
163
+ )
164
+ ) {
165
+ throw new errors.InvalidStateType({ path: invalidPath });
166
+ }
167
+ return target;
168
+ }
169
+
170
+ // Unsubscribe from old state
171
+ if (this.#persist) {
172
+ onChange.unsubscribe(this.#persist);
173
+ }
174
+
175
+ // Listen for changes to automatically write state
176
+ this.#persist = onChange(
177
+ target,
178
+ (
179
+ path: string,
180
+ value: any,
181
+ _previousValue: any,
182
+ _applyData: any,
183
+ ) => {
184
+ this.#handleStateChange(path, value);
185
+ },
186
+ { ignoreDetached: true },
187
+ );
188
+ }
189
+
190
+ // MARK: - State Persistence
191
+
192
+ /**
193
+ * Forces the state to get saved.
194
+ */
195
+ async saveState(opts: SaveStateOptions): Promise<void> {
196
+ this.#actor.assertReady(opts.allowStoppingState);
197
+
198
+ if (this.#persistChanged) {
199
+ if (opts.immediate) {
200
+ await this.#savePersistInner();
201
+ } else {
202
+ // Create promise for waiting
203
+ if (!this.#onPersistSavedPromise) {
204
+ this.#onPersistSavedPromise = promiseWithResolvers();
205
+ }
206
+
207
+ // Save throttled
208
+ this.savePersistThrottled(opts.maxWait);
209
+
210
+ // Wait for save
211
+ await this.#onPersistSavedPromise?.promise;
212
+ }
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Throttled save state method. Used to write to KV at a reasonable cadence.
218
+ *
219
+ * Passing a maxWait will override the stateSaveInterval with the min
220
+ * between that and the maxWait.
221
+ */
222
+ savePersistThrottled(maxWait?: number) {
223
+ const now = Date.now();
224
+ const timeSinceLastSave = now - this.#lastSaveTime;
225
+
226
+ // Calculate when the save should happen based on throttle interval
227
+ let saveDelay = Math.max(
228
+ 0,
229
+ this.#stateSaveInterval - timeSinceLastSave,
230
+ );
231
+ if (maxWait !== undefined) {
232
+ saveDelay = Math.min(saveDelay, maxWait);
233
+ }
234
+
235
+ // Check if we need to reschedule the same timeout
236
+ if (
237
+ this.#pendingSaveTimeout !== undefined &&
238
+ this.#pendingSaveScheduledTimestamp !== undefined
239
+ ) {
240
+ // Check if we have an earlier save deadline
241
+ const newScheduledTimestamp = now + saveDelay;
242
+ if (newScheduledTimestamp < this.#pendingSaveScheduledTimestamp) {
243
+ // Cancel existing timeout and reschedule
244
+ clearTimeout(this.#pendingSaveTimeout);
245
+ this.#pendingSaveTimeout = undefined;
246
+ this.#pendingSaveScheduledTimestamp = undefined;
247
+ } else {
248
+ // Current schedule is fine, don't reschedule
249
+ return;
250
+ }
251
+ }
252
+
253
+ if (saveDelay > 0) {
254
+ // Schedule save
255
+ this.#pendingSaveScheduledTimestamp = now + saveDelay;
256
+ this.#pendingSaveTimeout = setTimeout(() => {
257
+ this.#pendingSaveTimeout = undefined;
258
+ this.#pendingSaveScheduledTimestamp = undefined;
259
+ this.#savePersistInner();
260
+ }, saveDelay);
261
+ } else {
262
+ // Save immediately
263
+ this.#savePersistInner();
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Clears any pending save timeout.
269
+ */
270
+ clearPendingSaveTimeout() {
271
+ if (this.#pendingSaveTimeout) {
272
+ clearTimeout(this.#pendingSaveTimeout);
273
+ this.#pendingSaveTimeout = undefined;
274
+ this.#pendingSaveScheduledTimestamp = undefined;
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Waits for any pending write operations to complete.
280
+ */
281
+ async waitForPendingWrites(): Promise<void> {
282
+ if (this.#persistWriteQueue.runningDrainLoop) {
283
+ await this.#persistWriteQueue.runningDrainLoop;
284
+ }
285
+ }
286
+
287
+ // MARK: - Private Helpers
288
+
289
+ #validateStateEnabled() {
290
+ if (!this.stateEnabled) {
291
+ throw new errors.StateNotEnabled();
292
+ }
293
+ }
294
+
295
+ #handleStateChange(path: string, value: any) {
296
+ const actorStatePath = isStatePath(path);
297
+ const connStatePath = isConnStatePath(path);
298
+
299
+ // Validate CBOR serializability
300
+ if (actorStatePath || connStatePath) {
301
+ let invalidPath = "";
302
+ if (
303
+ !isCborSerializable(
304
+ value,
305
+ (invalidPathPart) => {
306
+ invalidPath = invalidPathPart;
307
+ },
308
+ "",
309
+ )
310
+ ) {
311
+ throw new errors.InvalidStateType({
312
+ path: path + (invalidPath ? `.${invalidPath}` : ""),
313
+ });
314
+ }
315
+ }
316
+
317
+ this.#actor.rLog.debug({
318
+ msg: "onChange triggered, setting persistChanged=true",
319
+ path,
320
+ });
321
+ this.#persistChanged = true;
322
+
323
+ // Inform inspector about state changes
324
+ if (actorStatePath) {
325
+ this.#actor.inspector.emitter.emit(
326
+ "stateUpdated",
327
+ this.#persist.state,
328
+ );
329
+ }
330
+
331
+ // Call onStateChange lifecycle hook
332
+ if (
333
+ actorStatePath &&
334
+ this.#config.onStateChange &&
335
+ this.#actor.isReady() &&
336
+ !this.#isInOnStateChange
337
+ ) {
338
+ try {
339
+ this.#isInOnStateChange = true;
340
+ this.#config.onStateChange(
341
+ this.#actor.actorContext,
342
+ this.#persistRaw.state,
343
+ );
344
+ } catch (error) {
345
+ this.#actor.rLog.error({
346
+ msg: "error in `_onStateChange`",
347
+ error: stringifyError(error),
348
+ });
349
+ } finally {
350
+ this.#isInOnStateChange = false;
351
+ }
352
+ }
353
+ }
354
+
355
+ async #savePersistInner() {
356
+ this.#actor.rLog.info({
357
+ msg: "savePersistInner called",
358
+ persistChanged: this.#persistChanged,
359
+ connsWithPersistChangedSize:
360
+ this.#actor.connectionManager.connsWithPersistChanged.size,
361
+ connsWithPersistChangedIds: Array.from(
362
+ this.#actor.connectionManager.connsWithPersistChanged,
363
+ ),
364
+ });
365
+
366
+ try {
367
+ this.#lastSaveTime = Date.now();
368
+
369
+ // Check if either actor state or connections have changed
370
+ const hasChanges =
371
+ this.#persistChanged ||
372
+ this.#actor.connectionManager.connsWithPersistChanged.size > 0;
373
+
374
+ if (hasChanges) {
375
+ await this.#persistWriteQueue.enqueue(async () => {
376
+ this.#actor.rLog.debug({
377
+ msg: "saving persist",
378
+ actorChanged: this.#persistChanged,
379
+ connectionsChanged:
380
+ this.#actor.connectionManager
381
+ .connsWithPersistChanged.size,
382
+ });
383
+
384
+ const entries: Array<[Uint8Array, Uint8Array]> = [];
385
+
386
+ // Build actor entries
387
+ if (this.#persistChanged) {
388
+ this.#persistChanged = false;
389
+ const bareData = convertActorToBarePersisted<S, I>(
390
+ this.#persistRaw,
391
+ );
392
+ entries.push([
393
+ KEYS.PERSIST_DATA,
394
+ ACTOR_VERSIONED.serializeWithEmbeddedVersion(
395
+ bareData,
396
+ ),
397
+ ]);
398
+ }
399
+
400
+ // Build connection entries
401
+ const connections: Array<AnyConn> = [];
402
+ for (const connId of this.#actor.connectionManager
403
+ .connsWithPersistChanged) {
404
+ const conn = this.#actor.conns.get(connId);
405
+ if (!conn) {
406
+ this.#actor.rLog.warn({
407
+ msg: "connection not found in conns map",
408
+ connId,
409
+ });
410
+ continue;
411
+ }
412
+
413
+ const connStateManager =
414
+ conn[CONN_STATE_MANAGER_SYMBOL];
415
+ const hibernatableDataRaw =
416
+ connStateManager.hibernatableDataRaw;
417
+ if (!hibernatableDataRaw) {
418
+ this.#actor.log.warn({
419
+ msg: "missing raw hibernatable data for conn in getChangedConnectionsData",
420
+ connId: conn.id,
421
+ });
422
+ continue;
423
+ }
424
+
425
+ this.#actor.rLog.info({
426
+ msg: "persisting connection",
427
+ connId,
428
+ gatewayId: idToStr(hibernatableDataRaw.requestId),
429
+ requestId: idToStr(hibernatableDataRaw.requestId),
430
+ serverMessageIndex:
431
+ hibernatableDataRaw.serverMessageIndex,
432
+ clientMessageIndex:
433
+ hibernatableDataRaw.clientMessageIndex,
434
+ hasState: hibernatableDataRaw.state !== undefined,
435
+ });
436
+
437
+ const bareData = convertConnToBarePersistedConn<CP, CS>(
438
+ hibernatableDataRaw,
439
+ );
440
+ const connData =
441
+ CONN_VERSIONED.serializeWithEmbeddedVersion(
442
+ bareData,
443
+ );
444
+
445
+ entries.push([makeConnKey(connId), connData]);
446
+ connections.push(conn);
447
+ }
448
+
449
+ this.#actor.rLog.info({
450
+ msg: "prepared entries for kvBatchPut",
451
+ totalEntries: entries.length,
452
+ connectionEntries: connections.length,
453
+ connectionIds: connections.map((c) => c.id),
454
+ });
455
+
456
+ // Notify driver before persisting connections
457
+ if (this.#actorDriver.onBeforePersistConn) {
458
+ for (const conn of connections) {
459
+ this.#actorDriver.onBeforePersistConn(conn);
460
+ }
461
+ }
462
+
463
+ // Clear changed connections
464
+ this.#actor.connectionManager.clearConnWithPersistChanged();
465
+
466
+ // Write data
467
+ this.#actor.rLog.info({
468
+ msg: "calling kvBatchPut",
469
+ actorId: this.#actor.id,
470
+ entriesCount: entries.length,
471
+ });
472
+ await this.#actorDriver.kvBatchPut(this.#actor.id, entries);
473
+ this.#actor.rLog.info({
474
+ msg: "kvBatchPut completed successfully",
475
+ });
476
+
477
+ // Notify driver after persisting connections
478
+ if (this.#actorDriver.onAfterPersistConn) {
479
+ for (const conn of connections) {
480
+ this.#actorDriver.onAfterPersistConn(conn);
481
+ }
482
+ }
483
+
484
+ this.#actor.rLog.debug({ msg: "persist saved" });
485
+ });
486
+ } else {
487
+ this.#actor.rLog.info({
488
+ msg: "savePersistInner skipped - no changes",
489
+ });
490
+ }
491
+
492
+ this.#onPersistSavedPromise?.resolve();
493
+ } catch (error) {
494
+ this.#actor.rLog.error({
495
+ msg: "error saving persist",
496
+ error: stringifyError(error),
497
+ });
498
+ this.#onPersistSavedPromise?.reject(error);
499
+ throw error;
500
+ }
501
+ }
502
+ }
package/src/actor/mod.ts CHANGED
@@ -55,8 +55,8 @@ export function actor<
55
55
  export type { Encoding } from "@/actor/protocol/serde";
56
56
  export {
57
57
  ALLOWED_PUBLIC_HEADERS,
58
- PATH_CONNECT_WEBSOCKET,
59
- PATH_RAW_WEBSOCKET_PREFIX,
58
+ PATH_CONNECT,
59
+ PATH_WEBSOCKET_PREFIX,
60
60
  } from "@/common/actor-router-consts";
61
61
  export type {
62
62
  UniversalErrorEvent,
@@ -64,7 +64,6 @@ export type {
64
64
  UniversalEventSource,
65
65
  UniversalMessageEvent,
66
66
  } from "@/common/eventsource-interface";
67
- export type { UpgradeWebSocketArgs } from "@/common/inline-websocket-adapter2";
68
67
  export type {
69
68
  RivetCloseEvent,
70
69
  RivetEvent,
@@ -72,15 +71,16 @@ export type {
72
71
  UniversalWebSocket,
73
72
  } from "@/common/websocket-interface";
74
73
  export type { ActorKey } from "@/manager/protocol/query";
75
- export type { ActionContext } from "./action";
76
74
  export type * from "./config";
77
- export type {
78
- Conn,
79
- ConnectionStatus,
80
- generateConnId,
81
- generateConnToken,
82
- } from "./conn";
83
- export type { ActorContext } from "./context";
75
+ export type { AnyConn, Conn } from "./conn/mod";
76
+ export type { ActionContext } from "./contexts/action";
77
+ export type { ActorContext } from "./contexts/actor";
78
+ export type { ConnInitContext } from "./contexts/conn-init";
79
+ export type { CreateConnStateContext } from "./contexts/create-conn-state";
80
+ export type { OnBeforeConnectContext } from "./contexts/on-before-connect";
81
+ export type { OnConnectContext } from "./contexts/on-connect";
82
+ export type { RequestContext } from "./contexts/request";
83
+ export type { WebSocketContext } from "./contexts/websocket";
84
84
  export type {
85
85
  ActionContextOf,
86
86
  ActorContextOf,
@@ -89,12 +89,9 @@ export type {
89
89
  } from "./definition";
90
90
  export { lookupInRegistry } from "./definition";
91
91
  export { UserError, type UserErrorOptions } from "./errors";
92
- export type { AnyActorInstance } from "./instance";
92
+ export type { AnyActorInstance } from "./instance/mod";
93
93
  export {
94
94
  type ActorRouter,
95
95
  createActorRouter,
96
96
  } from "./router";
97
- export {
98
- handleRawWebSocketHandler,
99
- handleWebSocketConnect,
100
- } from "./router-endpoints";
97
+ export { routeWebSocket } from "./router-websocket-endpoints";