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,1107 @@
1
+ import * as cbor from "cbor-x";
2
+ import invariant from "invariant";
3
+ import type { ActorKey } from "@/actor/mod";
4
+ import type { Client } from "@/client/client";
5
+ import { getBaseLogger, getIncludeTarget, type Logger } from "@/common/log";
6
+ import { stringifyError } from "@/common/utils";
7
+ import type { UniversalWebSocket } from "@/common/websocket-interface";
8
+ import { ActorInspector } from "@/inspector/actor";
9
+ import type { Registry } from "@/mod";
10
+ import {
11
+ ACTOR_VERSIONED,
12
+ CONN_VERSIONED,
13
+ } from "@/schemas/actor-persist/versioned";
14
+ import type * as protocol from "@/schemas/client-protocol/mod";
15
+ import { TO_CLIENT_VERSIONED } from "@/schemas/client-protocol/versioned";
16
+ import { ToClientSchema } from "@/schemas/client-protocol-zod/mod";
17
+ import { EXTRA_ERROR_LOG } from "@/utils";
18
+ import type { ActorConfig, InitContext } from "../config";
19
+ import type { ConnDriver } from "../conn/driver";
20
+ import { createHttpDriver } from "../conn/drivers/http";
21
+ import {
22
+ CONN_DRIVER_SYMBOL,
23
+ CONN_STATE_MANAGER_SYMBOL,
24
+ type Conn,
25
+ type ConnId,
26
+ } from "../conn/mod";
27
+ import {
28
+ convertConnFromBarePersistedConn,
29
+ type PersistedConn,
30
+ } from "../conn/persisted";
31
+ import { ActionContext } from "../contexts/action";
32
+ import { ActorContext } from "../contexts/actor";
33
+ import { RequestContext } from "../contexts/request";
34
+ import { WebSocketContext } from "../contexts/websocket";
35
+ import type { AnyDatabaseProvider, InferDatabaseClient } from "../database";
36
+ import type { ActorDriver } from "../driver";
37
+ import * as errors from "../errors";
38
+ import { serializeActorKey } from "../keys";
39
+ import { processMessage } from "../protocol/old";
40
+ import { CachedSerializer } from "../protocol/serde";
41
+ import { Schedule } from "../schedule";
42
+ import {
43
+ assertUnreachable,
44
+ DeadlineError,
45
+ deadline,
46
+ generateSecureToken,
47
+ } from "../utils";
48
+ import { ConnectionManager } from "./connection-manager";
49
+ import { EventManager } from "./event-manager";
50
+ import { KEYS } from "./kv";
51
+ import {
52
+ convertActorFromBarePersisted,
53
+ type PersistedActor,
54
+ } from "./persisted";
55
+ import { ScheduleManager } from "./schedule-manager";
56
+ import { type SaveStateOptions, StateManager } from "./state-manager";
57
+
58
+ export type { SaveStateOptions };
59
+
60
+ enum CanSleep {
61
+ Yes,
62
+ NotReady,
63
+ NotStarted,
64
+ ActiveConns,
65
+ ActiveHonoHttpRequests,
66
+ }
67
+
68
+ /** Actor type alias with all `any` types. Used for `extends` in classes referencing this actor. */
69
+ export type AnyActorInstance = ActorInstance<any, any, any, any, any, any>;
70
+
71
+ export type ExtractActorState<A extends AnyActorInstance> =
72
+ A extends ActorInstance<infer State, any, any, any, any, any>
73
+ ? State
74
+ : never;
75
+
76
+ export type ExtractActorConnParams<A extends AnyActorInstance> =
77
+ A extends ActorInstance<any, infer ConnParams, any, any, any, any>
78
+ ? ConnParams
79
+ : never;
80
+
81
+ export type ExtractActorConnState<A extends AnyActorInstance> =
82
+ A extends ActorInstance<any, any, infer ConnState, any, any, any>
83
+ ? ConnState
84
+ : never;
85
+
86
+ // MARK: - Main ActorInstance Class
87
+ export class ActorInstance<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
88
+ // MARK: - Core Properties
89
+ actorContext: ActorContext<S, CP, CS, V, I, DB>;
90
+ #config: ActorConfig<S, CP, CS, V, I, DB>;
91
+ driver!: ActorDriver;
92
+ #inlineClient!: Client<Registry<any>>;
93
+ #actorId!: string;
94
+ #name!: string;
95
+ #key!: ActorKey;
96
+ #region!: string;
97
+
98
+ // MARK: - Managers
99
+ connectionManager!: ConnectionManager<S, CP, CS, V, I, DB>;
100
+
101
+ stateManager!: StateManager<S, CP, CS, I>;
102
+
103
+ eventManager!: EventManager<S, CP, CS, V, I, DB>;
104
+
105
+ #scheduleManager!: ScheduleManager<S, CP, CS, V, I, DB>;
106
+
107
+ // MARK: - Logging
108
+ #log!: Logger;
109
+ #rLog!: Logger;
110
+
111
+ // MARK: - Lifecycle State
112
+ /**
113
+ * If the core actor initiation has set up.
114
+ *
115
+ * Almost all actions on this actor will throw an error if false.
116
+ **/
117
+ #ready = false;
118
+ /**
119
+ * If the actor has fully started.
120
+ *
121
+ * The only purpose of this is to prevent sleeping until started.
122
+ */
123
+ #started = false;
124
+ #sleepCalled = false;
125
+ #destroyCalled = false;
126
+ #stopCalled = false;
127
+ #sleepTimeout?: NodeJS.Timeout;
128
+ #abortController = new AbortController();
129
+
130
+ // MARK: - Variables & Database
131
+ #vars?: V;
132
+ #db!: InferDatabaseClient<DB>;
133
+
134
+ // MARK: - Background Tasks
135
+ #backgroundPromises: Promise<void>[] = [];
136
+
137
+ // MARK: - HTTP/WebSocket Tracking
138
+ #activeHonoHttpRequests = 0;
139
+
140
+ // MARK: - Deprecated (kept for compatibility)
141
+ #schedule!: Schedule;
142
+
143
+ // MARK: - Inspector
144
+ #inspectorToken?: string;
145
+ #inspector = new ActorInspector(() => {
146
+ return {
147
+ isDbEnabled: async () => {
148
+ return this.#db !== undefined;
149
+ },
150
+ getDb: async () => {
151
+ return this.db;
152
+ },
153
+ isStateEnabled: async () => {
154
+ return this.stateEnabled;
155
+ },
156
+ getState: async () => {
157
+ if (!this.stateEnabled) {
158
+ throw new errors.StateNotEnabled();
159
+ }
160
+ return this.stateManager.persistRaw.state as Record<
161
+ string,
162
+ any
163
+ > as unknown;
164
+ },
165
+ getRpcs: async () => {
166
+ return Object.keys(this.#config.actions);
167
+ },
168
+ getConnections: async () => {
169
+ return Array.from(
170
+ this.connectionManager.connections.entries(),
171
+ ).map(([id, conn]) => {
172
+ const connStateManager = conn[CONN_STATE_MANAGER_SYMBOL];
173
+ return {
174
+ type: conn[CONN_DRIVER_SYMBOL]?.type,
175
+ id,
176
+ params: conn.params as any,
177
+ stateEnabled: connStateManager.stateEnabled,
178
+ state: connStateManager.stateEnabled
179
+ ? connStateManager.state
180
+ : undefined,
181
+ subscriptions: conn.subscriptions.size,
182
+ isHibernatable: conn.isHibernatable,
183
+ // TODO: Include underlying hibernatable metadata +
184
+ // path + headers
185
+ };
186
+ });
187
+ },
188
+ setState: async (state: unknown) => {
189
+ if (!this.stateEnabled) {
190
+ throw new errors.StateNotEnabled();
191
+ }
192
+ this.stateManager.state = { ...(state as S) };
193
+ await this.stateManager.saveState({ immediate: true });
194
+ },
195
+ executeAction: async (name, params) => {
196
+ const conn = await this.connectionManager.prepareAndConnectConn(
197
+ createHttpDriver(),
198
+ // TODO: This may cause issues
199
+ undefined as unknown as CP,
200
+ undefined,
201
+ undefined,
202
+ undefined,
203
+ );
204
+
205
+ try {
206
+ return await this.executeAction(
207
+ new ActionContext(this, conn),
208
+ name,
209
+ params || [],
210
+ );
211
+ } finally {
212
+ conn.disconnect();
213
+ }
214
+ },
215
+ };
216
+ });
217
+
218
+ // MARK: - Constructor
219
+ constructor(config: ActorConfig<S, CP, CS, V, I, DB>) {
220
+ this.#config = config;
221
+ this.actorContext = new ActorContext(this);
222
+ }
223
+
224
+ // MARK: - Public Getters
225
+ get log(): Logger {
226
+ invariant(this.#log, "log not configured");
227
+ return this.#log;
228
+ }
229
+
230
+ get rLog(): Logger {
231
+ invariant(this.#rLog, "log not configured");
232
+ return this.#rLog;
233
+ }
234
+
235
+ get isStopping(): boolean {
236
+ return this.#stopCalled;
237
+ }
238
+
239
+ get id(): string {
240
+ return this.#actorId;
241
+ }
242
+
243
+ get name(): string {
244
+ return this.#name;
245
+ }
246
+
247
+ get key(): ActorKey {
248
+ return this.#key;
249
+ }
250
+
251
+ get region(): string {
252
+ return this.#region;
253
+ }
254
+
255
+ get inlineClient(): Client<Registry<any>> {
256
+ return this.#inlineClient;
257
+ }
258
+
259
+ get inspector(): ActorInspector {
260
+ return this.#inspector;
261
+ }
262
+
263
+ get inspectorToken(): string | undefined {
264
+ return this.#inspectorToken;
265
+ }
266
+
267
+ get conns(): Map<ConnId, Conn<S, CP, CS, V, I, DB>> {
268
+ return this.connectionManager.connections;
269
+ }
270
+
271
+ get schedule(): Schedule {
272
+ return this.#schedule;
273
+ }
274
+
275
+ get abortSignal(): AbortSignal {
276
+ return this.#abortController.signal;
277
+ }
278
+
279
+ get actions(): string[] {
280
+ return Object.keys(this.#config.actions);
281
+ }
282
+
283
+ get config(): ActorConfig<S, CP, CS, V, I, DB> {
284
+ return this.#config;
285
+ }
286
+
287
+ // MARK: - State Access
288
+ get persist(): PersistedActor<S, I> {
289
+ return this.stateManager.persist;
290
+ }
291
+
292
+ get state(): S {
293
+ return this.stateManager.state;
294
+ }
295
+
296
+ set state(value: S) {
297
+ this.stateManager.state = value;
298
+ }
299
+
300
+ get stateEnabled(): boolean {
301
+ return this.stateManager.stateEnabled;
302
+ }
303
+
304
+ get connStateEnabled(): boolean {
305
+ return "createConnState" in this.#config || "connState" in this.#config;
306
+ }
307
+
308
+ // MARK: - Variables & Database
309
+ get vars(): V {
310
+ this.#validateVarsEnabled();
311
+ invariant(this.#vars !== undefined, "vars not enabled");
312
+ return this.#vars;
313
+ }
314
+
315
+ get db(): InferDatabaseClient<DB> {
316
+ if (!this.#db) {
317
+ throw new errors.DatabaseNotEnabled();
318
+ }
319
+ return this.#db;
320
+ }
321
+
322
+ // MARK: - Initialization
323
+ async start(
324
+ actorDriver: ActorDriver,
325
+ inlineClient: Client<Registry<any>>,
326
+ actorId: string,
327
+ name: string,
328
+ key: ActorKey,
329
+ region: string,
330
+ ) {
331
+ // Initialize properties
332
+ this.driver = actorDriver;
333
+ this.#inlineClient = inlineClient;
334
+ this.#actorId = actorId;
335
+ this.#name = name;
336
+ this.#key = key;
337
+ this.#region = region;
338
+
339
+ // Initialize logging
340
+ this.#initializeLogging();
341
+
342
+ // Initialize managers
343
+ this.connectionManager = new ConnectionManager(this);
344
+ this.stateManager = new StateManager(this, actorDriver, this.#config);
345
+ this.eventManager = new EventManager(this);
346
+ this.#scheduleManager = new ScheduleManager(
347
+ this,
348
+ actorDriver,
349
+ this.#config,
350
+ );
351
+
352
+ // Legacy schedule object (for compatibility)
353
+ this.#schedule = new Schedule(this);
354
+
355
+ // Load state
356
+ await this.#loadState();
357
+
358
+ // Generate or load inspector token
359
+ await this.#initializeInspectorToken();
360
+
361
+ // Initialize variables
362
+ if (this.#varsEnabled) {
363
+ await this.#initializeVars();
364
+ }
365
+
366
+ // Call onStart lifecycle
367
+ await this.#callOnStart();
368
+
369
+ // Setup database
370
+ await this.#setupDatabase();
371
+
372
+ // Initialize alarms
373
+ await this.#scheduleManager.initializeAlarms();
374
+
375
+ // Mark as ready
376
+ this.#ready = true;
377
+
378
+ // Finish up any remaining initiation
379
+ //
380
+ // Do this after #ready = true since this can call any actor callbacks
381
+ // (which require #assertReady)
382
+ await this.driver.onBeforeActorStart?.(this);
383
+
384
+ // Mark as started
385
+ //
386
+ // We do this after onBeforeActorStart to prevent the actor from going
387
+ // to sleep before finishing setup
388
+ this.#started = true;
389
+ this.#rLog.info({ msg: "actor started" });
390
+
391
+ // Start sleep timer after setting #started since this affects the
392
+ // timer
393
+ this.resetSleepTimer();
394
+
395
+ // Trigger any pending alarms
396
+ await this.onAlarm();
397
+ }
398
+
399
+ // MARK: - Ready Check
400
+ isReady(): boolean {
401
+ return this.#ready;
402
+ }
403
+
404
+ assertReady(allowStoppingState: boolean = false) {
405
+ if (!this.#ready) throw new errors.InternalError("Actor not ready");
406
+ if (!allowStoppingState && this.#stopCalled)
407
+ throw new errors.InternalError("Actor is stopping");
408
+ }
409
+
410
+ // MARK: - Stop
411
+ async onStop(mode: "sleep" | "destroy") {
412
+ if (this.#stopCalled) {
413
+ this.#rLog.warn({ msg: "already stopping actor" });
414
+ return;
415
+ }
416
+ this.#stopCalled = true;
417
+ this.#rLog.info({
418
+ msg: "setting stopCalled=true",
419
+ mode,
420
+ });
421
+
422
+ // Clear sleep timeout
423
+ if (this.#sleepTimeout) {
424
+ clearTimeout(this.#sleepTimeout);
425
+ this.#sleepTimeout = undefined;
426
+ }
427
+
428
+ // Abort listeners
429
+ try {
430
+ this.#abortController.abort();
431
+ } catch {}
432
+
433
+ // Call onStop lifecycle
434
+ if (mode === "sleep") {
435
+ await this.#callOnSleep();
436
+ } else if (mode === "destroy") {
437
+ await this.#callOnDestroy();
438
+ } else {
439
+ assertUnreachable(mode);
440
+ }
441
+
442
+ // Disconnect non-hibernatable connections
443
+ await this.#disconnectConnections();
444
+
445
+ // Wait for background tasks
446
+ await this.#waitBackgroundPromises(
447
+ this.#config.options.waitUntilTimeout,
448
+ );
449
+
450
+ // Clear timeouts and save state
451
+ this.#rLog.info({ msg: "clearing pending save timeouts" });
452
+ this.stateManager.clearPendingSaveTimeout();
453
+ this.#rLog.info({ msg: "saving state immediately" });
454
+ await this.stateManager.saveState({
455
+ immediate: true,
456
+ allowStoppingState: true,
457
+ });
458
+
459
+ // Wait for write queues
460
+ await this.stateManager.waitForPendingWrites();
461
+ await this.#scheduleManager.waitForPendingAlarmWrites();
462
+ }
463
+
464
+ // MARK: - Sleep
465
+ startSleep() {
466
+ if (this.#stopCalled || this.#destroyCalled) {
467
+ this.#rLog.debug({
468
+ msg: "cannot call startSleep if actor already stopping",
469
+ });
470
+ return;
471
+ }
472
+
473
+ if (this.#sleepCalled) {
474
+ this.#rLog.warn({
475
+ msg: "cannot call startSleep twice, actor already sleeping",
476
+ });
477
+ return;
478
+ }
479
+ this.#sleepCalled = true;
480
+
481
+ const sleep = this.driver.startSleep?.bind(this.driver, this.#actorId);
482
+ invariant(this.#sleepingSupported, "sleeping not supported");
483
+ invariant(sleep, "no sleep on driver");
484
+
485
+ this.#rLog.info({ msg: "actor sleeping" });
486
+
487
+ // Start sleep on next tick so call site of startSleep can exit
488
+ setImmediate(() => {
489
+ sleep();
490
+ });
491
+ }
492
+
493
+ // MARK: - Destroy
494
+ startDestroy() {
495
+ if (this.#stopCalled || this.#sleepCalled) {
496
+ this.#rLog.debug({
497
+ msg: "cannot call startDestroy if actor already stopping or sleeping",
498
+ });
499
+ return;
500
+ }
501
+
502
+ if (this.#destroyCalled) {
503
+ this.#rLog.warn({
504
+ msg: "cannot call startDestroy twice, actor already destroying",
505
+ });
506
+ return;
507
+ }
508
+ this.#destroyCalled = true;
509
+
510
+ const destroy = this.driver.startDestroy.bind(
511
+ this.driver,
512
+ this.#actorId,
513
+ );
514
+
515
+ this.#rLog.info({ msg: "actor destroying" });
516
+
517
+ // Start destroy on next tick so call site of startDestroy can exit
518
+ setImmediate(() => {
519
+ destroy();
520
+ });
521
+ }
522
+
523
+ // MARK: - HTTP Request Tracking
524
+ beginHonoHttpRequest() {
525
+ this.#activeHonoHttpRequests++;
526
+ this.resetSleepTimer();
527
+ }
528
+
529
+ endHonoHttpRequest() {
530
+ this.#activeHonoHttpRequests--;
531
+ if (this.#activeHonoHttpRequests < 0) {
532
+ this.#activeHonoHttpRequests = 0;
533
+ this.#rLog.warn({
534
+ msg: "active hono requests went below 0, this is a RivetKit bug",
535
+ ...EXTRA_ERROR_LOG,
536
+ });
537
+ }
538
+ this.resetSleepTimer();
539
+ }
540
+
541
+ // MARK: - Message Processing
542
+ async processMessage(
543
+ message: {
544
+ body:
545
+ | {
546
+ tag: "ActionRequest";
547
+ val: { id: bigint; name: string; args: unknown };
548
+ }
549
+ | {
550
+ tag: "SubscriptionRequest";
551
+ val: { eventName: string; subscribe: boolean };
552
+ };
553
+ },
554
+ conn: Conn<S, CP, CS, V, I, DB>,
555
+ ) {
556
+ await processMessage(message, this, conn, {
557
+ onExecuteAction: async (ctx, name, args) => {
558
+ this.inspector.emitter.emit("eventFired", {
559
+ type: "action",
560
+ name,
561
+ args,
562
+ connId: conn.id,
563
+ });
564
+ return await this.executeAction(ctx, name, args);
565
+ },
566
+ onSubscribe: async (eventName, conn) => {
567
+ this.inspector.emitter.emit("eventFired", {
568
+ type: "subscribe",
569
+ eventName,
570
+ connId: conn.id,
571
+ });
572
+ this.eventManager.addSubscription(eventName, conn, false);
573
+ },
574
+ onUnsubscribe: async (eventName, conn) => {
575
+ this.inspector.emitter.emit("eventFired", {
576
+ type: "unsubscribe",
577
+ eventName,
578
+ connId: conn.id,
579
+ });
580
+ this.eventManager.removeSubscription(eventName, conn, false);
581
+ },
582
+ });
583
+ }
584
+
585
+ // MARK: - Action Execution
586
+ async executeAction(
587
+ ctx: ActionContext<S, CP, CS, V, I, DB>,
588
+ actionName: string,
589
+ args: unknown[],
590
+ ): Promise<unknown> {
591
+ this.assertReady();
592
+
593
+ if (!(actionName in this.#config.actions)) {
594
+ this.#rLog.warn({ msg: "action does not exist", actionName });
595
+ throw new errors.ActionNotFound(actionName);
596
+ }
597
+
598
+ const actionFunction = this.#config.actions[actionName];
599
+ if (typeof actionFunction !== "function") {
600
+ this.#rLog.warn({
601
+ msg: "action is not a function",
602
+ actionName,
603
+ type: typeof actionFunction,
604
+ });
605
+ throw new errors.ActionNotFound(actionName);
606
+ }
607
+
608
+ try {
609
+ this.#rLog.debug({
610
+ msg: "executing action",
611
+ actionName,
612
+ args,
613
+ });
614
+
615
+ const outputOrPromise = actionFunction.call(
616
+ undefined,
617
+ ctx,
618
+ ...args,
619
+ );
620
+
621
+ let output: unknown;
622
+ if (outputOrPromise instanceof Promise) {
623
+ output = await deadline(
624
+ outputOrPromise,
625
+ this.#config.options.actionTimeout,
626
+ );
627
+ } else {
628
+ output = outputOrPromise;
629
+ }
630
+
631
+ // Process through onBeforeActionResponse if configured
632
+ if (this.#config.onBeforeActionResponse) {
633
+ try {
634
+ const processedOutput = this.#config.onBeforeActionResponse(
635
+ this.actorContext,
636
+ actionName,
637
+ args,
638
+ output,
639
+ );
640
+ if (processedOutput instanceof Promise) {
641
+ output = await processedOutput;
642
+ } else {
643
+ output = processedOutput;
644
+ }
645
+ } catch (error) {
646
+ this.#rLog.error({
647
+ msg: "error in `onBeforeActionResponse`",
648
+ error: stringifyError(error),
649
+ });
650
+ }
651
+ }
652
+
653
+ return output;
654
+ } catch (error) {
655
+ if (error instanceof DeadlineError) {
656
+ throw new errors.ActionTimedOut();
657
+ }
658
+ this.#rLog.error({
659
+ msg: "action error",
660
+ actionName,
661
+ error: stringifyError(error),
662
+ });
663
+ throw error;
664
+ } finally {
665
+ this.stateManager.savePersistThrottled();
666
+ }
667
+ }
668
+
669
+ // MARK: - HTTP/WebSocket Handlers
670
+ async handleRawRequest(
671
+ conn: Conn<S, CP, CS, V, I, DB>,
672
+ request: Request,
673
+ ): Promise<Response> {
674
+ this.assertReady();
675
+
676
+ if (!this.#config.onRequest) {
677
+ throw new errors.RequestHandlerNotDfeined();
678
+ }
679
+
680
+ try {
681
+ const ctx = new RequestContext(this, conn, request);
682
+ const response = await this.#config.onRequest(ctx, request);
683
+ if (!response) {
684
+ throw new errors.InvalidRequestHandlerResponse();
685
+ }
686
+ return response;
687
+ } catch (error) {
688
+ this.#rLog.error({
689
+ msg: "onRequest error",
690
+ error: stringifyError(error),
691
+ });
692
+ throw error;
693
+ } finally {
694
+ this.stateManager.savePersistThrottled();
695
+ }
696
+ }
697
+
698
+ handleRawWebSocket(
699
+ conn: Conn<S, CP, CS, V, I, DB>,
700
+ websocket: UniversalWebSocket,
701
+ request?: Request,
702
+ ) {
703
+ // NOTE: All code before `onWebSocket` must be synchronous in order to ensure the order of `open` events happen in the correct order.
704
+
705
+ this.assertReady();
706
+
707
+ if (!this.#config.onWebSocket) {
708
+ throw new errors.InternalError("onWebSocket handler not defined");
709
+ }
710
+
711
+ try {
712
+ // Reset sleep timer when handling WebSocket
713
+ this.resetSleepTimer();
714
+
715
+ // Handle WebSocket
716
+ const ctx = new WebSocketContext(this, conn, request);
717
+
718
+ // NOTE: This is async and will run in the background
719
+ const voidOrPromise = this.#config.onWebSocket(ctx, websocket);
720
+
721
+ // Save changes from the WebSocket open
722
+ if (voidOrPromise instanceof Promise) {
723
+ voidOrPromise.then(() => {
724
+ this.stateManager.savePersistThrottled();
725
+ });
726
+ } else {
727
+ this.stateManager.savePersistThrottled();
728
+ }
729
+ } catch (error) {
730
+ this.#rLog.error({
731
+ msg: "onWebSocket error",
732
+ error: stringifyError(error),
733
+ });
734
+ throw error;
735
+ }
736
+ }
737
+
738
+ // MARK: - Scheduling
739
+ async scheduleEvent(
740
+ timestamp: number,
741
+ action: string,
742
+ args: unknown[],
743
+ ): Promise<void> {
744
+ await this.#scheduleManager.scheduleEvent(timestamp, action, args);
745
+ }
746
+
747
+ async onAlarm() {
748
+ this.resetSleepTimer();
749
+ await this.#scheduleManager.onAlarm();
750
+ }
751
+
752
+ // MARK: - Background Tasks
753
+ waitUntil(promise: Promise<void>) {
754
+ this.assertReady();
755
+
756
+ const nonfailablePromise = promise
757
+ .then(() => {
758
+ this.#rLog.debug({ msg: "wait until promise complete" });
759
+ })
760
+ .catch((error) => {
761
+ this.#rLog.error({
762
+ msg: "wait until promise failed",
763
+ error: stringifyError(error),
764
+ });
765
+ });
766
+ this.#backgroundPromises.push(nonfailablePromise);
767
+ }
768
+
769
+ // MARK: - Private Helper Methods
770
+ #initializeLogging() {
771
+ const logParams = {
772
+ actor: this.#name,
773
+ key: serializeActorKey(this.#key),
774
+ actorId: this.#actorId,
775
+ };
776
+
777
+ const extraLogParams = this.driver.getExtraActorLogParams?.();
778
+ if (extraLogParams) Object.assign(logParams, extraLogParams);
779
+
780
+ this.#log = getBaseLogger().child(
781
+ Object.assign(
782
+ getIncludeTarget() ? { target: "actor" } : {},
783
+ logParams,
784
+ ),
785
+ );
786
+ this.#rLog = getBaseLogger().child(
787
+ Object.assign(
788
+ getIncludeTarget() ? { target: "actor-runtime" } : {},
789
+ logParams,
790
+ ),
791
+ );
792
+ }
793
+
794
+ async #loadState() {
795
+ // Read initial state from KV
796
+ const [persistDataBuffer] = await this.driver.kvBatchGet(
797
+ this.#actorId,
798
+ [KEYS.PERSIST_DATA],
799
+ );
800
+ invariant(
801
+ persistDataBuffer !== null,
802
+ "persist data has not been set, it should be set when initialized",
803
+ );
804
+
805
+ const bareData =
806
+ ACTOR_VERSIONED.deserializeWithEmbeddedVersion(persistDataBuffer);
807
+ const persistData = convertActorFromBarePersisted<S, I>(bareData);
808
+
809
+ if (persistData.hasInitialized) {
810
+ // Restore existing actor
811
+ await this.#restoreExistingActor(persistData);
812
+ } else {
813
+ // Create new actor
814
+ await this.#createNewActor(persistData);
815
+ }
816
+
817
+ // Pass persist reference to schedule manager
818
+ this.#scheduleManager.setPersist(this.stateManager.persist);
819
+ }
820
+
821
+ async #createNewActor(persistData: PersistedActor<S, I>) {
822
+ this.#rLog.info({ msg: "actor creating" });
823
+
824
+ // Initialize state
825
+ await this.stateManager.initializeState(persistData);
826
+
827
+ // Call onCreate lifecycle
828
+ if (this.#config.onCreate) {
829
+ await this.#config.onCreate(this.actorContext, persistData.input!);
830
+ }
831
+ }
832
+
833
+ async #restoreExistingActor(persistData: PersistedActor<S, I>) {
834
+ // List all connection keys
835
+ const connEntries = await this.driver.kvListPrefix(
836
+ this.#actorId,
837
+ KEYS.CONN_PREFIX,
838
+ );
839
+
840
+ // Decode connections
841
+ const connections: PersistedConn<CP, CS>[] = [];
842
+ for (const [_key, value] of connEntries) {
843
+ try {
844
+ const bareData = CONN_VERSIONED.deserializeWithEmbeddedVersion(
845
+ new Uint8Array(value),
846
+ );
847
+ const conn = convertConnFromBarePersistedConn<CP, CS>(bareData);
848
+ connections.push(conn);
849
+ } catch (error) {
850
+ this.#rLog.error({
851
+ msg: "failed to decode connection",
852
+ error: stringifyError(error),
853
+ });
854
+ }
855
+ }
856
+
857
+ this.#rLog.info({
858
+ msg: "actor restoring",
859
+ connections: connections.length,
860
+ });
861
+
862
+ // Initialize state
863
+ this.stateManager.initPersistProxy(persistData);
864
+
865
+ // Restore connections
866
+ this.connectionManager.restoreConnections(connections);
867
+ }
868
+
869
+ async #initializeInspectorToken() {
870
+ // Try to load existing token
871
+ const [tokenBuffer] = await this.driver.kvBatchGet(this.#actorId, [
872
+ KEYS.INSPECTOR_TOKEN,
873
+ ]);
874
+
875
+ if (tokenBuffer !== null) {
876
+ // Token exists, decode it
877
+ const decoder = new TextDecoder();
878
+ this.#inspectorToken = decoder.decode(tokenBuffer);
879
+ this.#rLog.debug({ msg: "loaded existing inspector token" });
880
+ } else {
881
+ // Generate new token
882
+ this.#inspectorToken = generateSecureToken();
883
+ const tokenBytes = new TextEncoder().encode(this.#inspectorToken);
884
+ await this.driver.kvBatchPut(this.#actorId, [
885
+ [KEYS.INSPECTOR_TOKEN, tokenBytes],
886
+ ]);
887
+ this.#rLog.debug({ msg: "generated new inspector token" });
888
+ }
889
+ }
890
+
891
+ async #initializeVars() {
892
+ let vars: V | undefined;
893
+ if ("createVars" in this.#config) {
894
+ const dataOrPromise = this.#config.createVars(
895
+ this.actorContext as unknown as InitContext,
896
+ this.driver.getContext(this.#actorId),
897
+ );
898
+ if (dataOrPromise instanceof Promise) {
899
+ vars = await deadline(
900
+ dataOrPromise,
901
+ this.#config.options.createVarsTimeout,
902
+ );
903
+ } else {
904
+ vars = dataOrPromise;
905
+ }
906
+ } else if ("vars" in this.#config) {
907
+ vars = structuredClone(this.#config.vars);
908
+ } else {
909
+ throw new Error(
910
+ "Could not create variables from 'createVars' or 'vars'",
911
+ );
912
+ }
913
+ this.#vars = vars;
914
+ }
915
+
916
+ async #callOnStart() {
917
+ this.#rLog.info({ msg: "actor starting" });
918
+ if (this.#config.onWake) {
919
+ const result = this.#config.onWake(this.actorContext);
920
+ if (result instanceof Promise) {
921
+ await result;
922
+ }
923
+ }
924
+ }
925
+
926
+ async #callOnSleep() {
927
+ if (this.#config.onSleep) {
928
+ try {
929
+ this.#rLog.debug({ msg: "calling onSleep" });
930
+ const result = this.#config.onSleep(this.actorContext);
931
+ if (result instanceof Promise) {
932
+ await deadline(result, this.#config.options.onSleepTimeout);
933
+ }
934
+ this.#rLog.debug({ msg: "onSleep completed" });
935
+ } catch (error) {
936
+ if (error instanceof DeadlineError) {
937
+ this.#rLog.error({ msg: "onSleep timed out" });
938
+ } else {
939
+ this.#rLog.error({
940
+ msg: "error in onSleep",
941
+ error: stringifyError(error),
942
+ });
943
+ }
944
+ }
945
+ }
946
+ }
947
+
948
+ async #callOnDestroy() {
949
+ if (this.#config.onDestroy) {
950
+ try {
951
+ this.#rLog.debug({ msg: "calling onDestroy" });
952
+ const result = this.#config.onDestroy(this.actorContext);
953
+ if (result instanceof Promise) {
954
+ await deadline(
955
+ result,
956
+ this.#config.options.onDestroyTimeout,
957
+ );
958
+ }
959
+ this.#rLog.debug({ msg: "onDestroy completed" });
960
+ } catch (error) {
961
+ if (error instanceof DeadlineError) {
962
+ this.#rLog.error({ msg: "onDestroy timed out" });
963
+ } else {
964
+ this.#rLog.error({
965
+ msg: "error in onDestroy",
966
+ error: stringifyError(error),
967
+ });
968
+ }
969
+ }
970
+ }
971
+ }
972
+
973
+ async #setupDatabase() {
974
+ if ("db" in this.#config && this.#config.db) {
975
+ const client = await this.#config.db.createClient({
976
+ getDatabase: () => this.driver.getDatabase(this.#actorId),
977
+ });
978
+ this.#rLog.info({ msg: "database migration starting" });
979
+ await this.#config.db.onMigrate?.(client);
980
+ this.#rLog.info({ msg: "database migration complete" });
981
+ this.#db = client;
982
+ }
983
+ }
984
+
985
+ async #disconnectConnections() {
986
+ const promises: Promise<unknown>[] = [];
987
+ this.#rLog.debug({
988
+ msg: "disconnecting connections on actor stop",
989
+ totalConns: this.connectionManager.connections.size,
990
+ });
991
+ for (const connection of this.connectionManager.connections.values()) {
992
+ this.#rLog.debug({
993
+ msg: "checking connection for disconnect",
994
+ connId: connection.id,
995
+ isHibernatable: connection.isHibernatable,
996
+ });
997
+ if (!connection.isHibernatable) {
998
+ this.#rLog.debug({
999
+ msg: "disconnecting non-hibernatable connection on actor stop",
1000
+ connId: connection.id,
1001
+ });
1002
+ promises.push(connection.disconnect());
1003
+ } else {
1004
+ this.#rLog.debug({
1005
+ msg: "preserving hibernatable connection on actor stop",
1006
+ connId: connection.id,
1007
+ });
1008
+ }
1009
+ }
1010
+
1011
+ // Wait with timeout
1012
+ const res = await Promise.race([
1013
+ Promise.all(promises).then(() => false),
1014
+ new Promise<boolean>((res) =>
1015
+ globalThis.setTimeout(() => res(true), 1500),
1016
+ ),
1017
+ ]);
1018
+
1019
+ if (res) {
1020
+ this.#rLog.warn({
1021
+ msg: "timed out waiting for connections to close, shutting down anyway",
1022
+ });
1023
+ }
1024
+ }
1025
+
1026
+ async #waitBackgroundPromises(timeoutMs: number) {
1027
+ const pending = this.#backgroundPromises;
1028
+ if (pending.length === 0) {
1029
+ this.#rLog.debug({ msg: "no background promises" });
1030
+ return;
1031
+ }
1032
+
1033
+ const timedOut = await Promise.race([
1034
+ Promise.allSettled(pending).then(() => false),
1035
+ new Promise<true>((resolve) =>
1036
+ setTimeout(() => resolve(true), timeoutMs),
1037
+ ),
1038
+ ]);
1039
+
1040
+ if (timedOut) {
1041
+ this.#rLog.error({
1042
+ msg: "timed out waiting for background tasks",
1043
+ count: pending.length,
1044
+ timeoutMs,
1045
+ });
1046
+ } else {
1047
+ this.#rLog.debug({ msg: "background promises finished" });
1048
+ }
1049
+ }
1050
+
1051
+ resetSleepTimer() {
1052
+ if (this.#config.options.noSleep || !this.#sleepingSupported) return;
1053
+ if (this.#stopCalled) return;
1054
+
1055
+ const canSleep = this.#canSleep();
1056
+
1057
+ this.#rLog.debug({
1058
+ msg: "resetting sleep timer",
1059
+ canSleep: CanSleep[canSleep],
1060
+ existingTimeout: !!this.#sleepTimeout,
1061
+ timeout: this.#config.options.sleepTimeout,
1062
+ });
1063
+
1064
+ if (this.#sleepTimeout) {
1065
+ clearTimeout(this.#sleepTimeout);
1066
+ this.#sleepTimeout = undefined;
1067
+ }
1068
+
1069
+ if (this.#sleepCalled) return;
1070
+
1071
+ if (canSleep === CanSleep.Yes) {
1072
+ this.#sleepTimeout = setTimeout(() => {
1073
+ this.startSleep();
1074
+ }, this.#config.options.sleepTimeout);
1075
+ }
1076
+ }
1077
+
1078
+ #canSleep(): CanSleep {
1079
+ if (!this.#ready) return CanSleep.NotReady;
1080
+ if (!this.#started) return CanSleep.NotReady;
1081
+ if (this.#activeHonoHttpRequests > 0)
1082
+ return CanSleep.ActiveHonoHttpRequests;
1083
+
1084
+ for (const _conn of this.connectionManager.connections.values()) {
1085
+ // TODO: Add back
1086
+ // if (!_conn.isHibernatable) {
1087
+ return CanSleep.ActiveConns;
1088
+ // }
1089
+ }
1090
+
1091
+ return CanSleep.Yes;
1092
+ }
1093
+
1094
+ get #sleepingSupported(): boolean {
1095
+ return this.driver.startSleep !== undefined;
1096
+ }
1097
+
1098
+ get #varsEnabled(): boolean {
1099
+ return "createVars" in this.#config || "vars" in this.#config;
1100
+ }
1101
+
1102
+ #validateVarsEnabled() {
1103
+ if (!this.#varsEnabled) {
1104
+ throw new errors.VarsNotEnabled();
1105
+ }
1106
+ }
1107
+ }