rivetkit 2.0.2 → 2.0.4-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 (403) hide show
  1. package/README.md +3 -5
  2. package/dist/browser/client.d.ts +2485 -0
  3. package/dist/browser/client.js +5182 -0
  4. package/dist/browser/client.js.map +1 -0
  5. package/dist/browser/inspector/client.d.ts +130 -0
  6. package/dist/browser/inspector/client.js +2854 -0
  7. package/dist/browser/inspector/client.js.map +1 -0
  8. package/dist/browser/v3-DnYObHH3.d.ts +279 -0
  9. package/dist/inspector.tar.gz +0 -0
  10. package/dist/schemas/actor-inspector/v1.ts +784 -0
  11. package/dist/schemas/actor-inspector/v2.ts +796 -0
  12. package/dist/schemas/actor-inspector/v3.ts +899 -0
  13. package/dist/schemas/actor-persist/v1.ts +225 -0
  14. package/dist/schemas/actor-persist/v2.ts +268 -0
  15. package/dist/schemas/actor-persist/v3.ts +280 -0
  16. package/dist/schemas/actor-persist/v4.ts +406 -0
  17. package/dist/schemas/client-protocol/v1.ts +441 -0
  18. package/dist/schemas/client-protocol/v2.ts +438 -0
  19. package/dist/schemas/client-protocol/v3.ts +554 -0
  20. package/dist/schemas/file-system-driver/v1.ts +108 -0
  21. package/dist/schemas/file-system-driver/v2.ts +142 -0
  22. package/dist/schemas/file-system-driver/v3.ts +167 -0
  23. package/dist/schemas/persist/v1.ts +781 -0
  24. package/dist/schemas/transport/v1.ts +697 -0
  25. package/dist/tsup/actor/errors.cjs +106 -0
  26. package/dist/tsup/actor/errors.cjs.map +1 -0
  27. package/dist/tsup/actor/errors.d.cts +188 -0
  28. package/dist/tsup/actor/errors.d.ts +188 -0
  29. package/dist/tsup/actor/errors.js +106 -0
  30. package/dist/tsup/actor/errors.js.map +1 -0
  31. package/dist/tsup/actor-router-consts-D29T1Z-K.d.cts +24 -0
  32. package/dist/tsup/actor-router-consts-D29T1Z-K.d.ts +24 -0
  33. package/dist/tsup/chunk-325TLXJT.js +1060 -0
  34. package/dist/tsup/chunk-325TLXJT.js.map +1 -0
  35. package/dist/tsup/chunk-424PT5DM.js +23 -0
  36. package/dist/tsup/chunk-424PT5DM.js.map +1 -0
  37. package/dist/tsup/chunk-4JVIG3SS.cjs +6289 -0
  38. package/dist/tsup/chunk-4JVIG3SS.cjs.map +1 -0
  39. package/dist/tsup/chunk-6LJAZ5R4.cjs +96 -0
  40. package/dist/tsup/chunk-6LJAZ5R4.cjs.map +1 -0
  41. package/dist/tsup/chunk-6XU3FMCB.cjs +534 -0
  42. package/dist/tsup/chunk-6XU3FMCB.cjs.map +1 -0
  43. package/dist/tsup/chunk-7HTNH26M.js +509 -0
  44. package/dist/tsup/chunk-7HTNH26M.js.map +1 -0
  45. package/dist/tsup/chunk-AUVH72RE.cjs +5977 -0
  46. package/dist/tsup/chunk-AUVH72RE.cjs.map +1 -0
  47. package/dist/tsup/chunk-D4BYUPNQ.js +645 -0
  48. package/dist/tsup/chunk-D4BYUPNQ.js.map +1 -0
  49. package/dist/tsup/chunk-HDQ2JUQT.cjs +23 -0
  50. package/dist/tsup/chunk-HDQ2JUQT.cjs.map +1 -0
  51. package/dist/tsup/chunk-HHXX2VRM.js +6289 -0
  52. package/dist/tsup/chunk-HHXX2VRM.js.map +1 -0
  53. package/dist/tsup/chunk-JEAEA2PB.js +49 -0
  54. package/dist/tsup/chunk-JEAEA2PB.js.map +1 -0
  55. package/dist/tsup/chunk-JYSEG3VF.cjs +642 -0
  56. package/dist/tsup/chunk-JYSEG3VF.cjs.map +1 -0
  57. package/dist/tsup/chunk-K6DGYILQ.js +2657 -0
  58. package/dist/tsup/chunk-K6DGYILQ.js.map +1 -0
  59. package/dist/tsup/chunk-KJSYAUOM.js +96 -0
  60. package/dist/tsup/chunk-KJSYAUOM.js.map +1 -0
  61. package/dist/tsup/chunk-L47L3ZWJ.cjs +509 -0
  62. package/dist/tsup/chunk-L47L3ZWJ.cjs.map +1 -0
  63. package/dist/tsup/chunk-LXUQ667X.js +2006 -0
  64. package/dist/tsup/chunk-LXUQ667X.js.map +1 -0
  65. package/dist/tsup/chunk-MXNPAB5W.js +5977 -0
  66. package/dist/tsup/chunk-MXNPAB5W.js.map +1 -0
  67. package/dist/tsup/chunk-N4KRDJ56.js +72 -0
  68. package/dist/tsup/chunk-N4KRDJ56.js.map +1 -0
  69. package/dist/tsup/chunk-NIYZDWMW.cjs +2006 -0
  70. package/dist/tsup/chunk-NIYZDWMW.cjs.map +1 -0
  71. package/dist/tsup/chunk-PQZHDKRW.cjs +1060 -0
  72. package/dist/tsup/chunk-PQZHDKRW.cjs.map +1 -0
  73. package/dist/tsup/chunk-PVOE6BU7.cjs +1050 -0
  74. package/dist/tsup/chunk-PVOE6BU7.cjs.map +1 -0
  75. package/dist/tsup/chunk-Q4UD2GA4.cjs +1810 -0
  76. package/dist/tsup/chunk-Q4UD2GA4.cjs.map +1 -0
  77. package/dist/tsup/chunk-QUD664YZ.js +1810 -0
  78. package/dist/tsup/chunk-QUD664YZ.js.map +1 -0
  79. package/dist/tsup/chunk-RTOCTWME.js +1050 -0
  80. package/dist/tsup/chunk-RTOCTWME.js.map +1 -0
  81. package/dist/tsup/chunk-SAZZ4SB2.cjs +2657 -0
  82. package/dist/tsup/chunk-SAZZ4SB2.cjs.map +1 -0
  83. package/dist/tsup/chunk-SR3KQE7Q.cjs +72 -0
  84. package/dist/tsup/chunk-SR3KQE7Q.cjs.map +1 -0
  85. package/dist/tsup/chunk-V2GHLYC6.cjs +49 -0
  86. package/dist/tsup/chunk-V2GHLYC6.cjs.map +1 -0
  87. package/dist/tsup/chunk-V3WG7XTW.cjs +645 -0
  88. package/dist/tsup/chunk-V3WG7XTW.cjs.map +1 -0
  89. package/dist/tsup/chunk-VKVNIQRQ.js +257 -0
  90. package/dist/tsup/chunk-VKVNIQRQ.js.map +1 -0
  91. package/dist/tsup/chunk-WMPW7JYC.js +642 -0
  92. package/dist/tsup/chunk-WMPW7JYC.js.map +1 -0
  93. package/dist/tsup/chunk-Z7HNQ2WF.js +534 -0
  94. package/dist/tsup/chunk-Z7HNQ2WF.js.map +1 -0
  95. package/dist/tsup/chunk-ZFY5J2EP.cjs +257 -0
  96. package/dist/tsup/chunk-ZFY5J2EP.cjs.map +1 -0
  97. package/dist/tsup/client/mod.cjs +33 -0
  98. package/dist/tsup/client/mod.cjs.map +1 -0
  99. package/dist/tsup/client/mod.d.cts +64 -0
  100. package/dist/tsup/client/mod.d.ts +64 -0
  101. package/dist/tsup/client/mod.js +33 -0
  102. package/dist/tsup/client/mod.js.map +1 -0
  103. package/dist/tsup/common/log.cjs +21 -0
  104. package/dist/tsup/common/log.cjs.map +1 -0
  105. package/dist/tsup/common/log.d.cts +34 -0
  106. package/dist/tsup/common/log.d.ts +34 -0
  107. package/dist/tsup/common/log.js +21 -0
  108. package/dist/tsup/common/log.js.map +1 -0
  109. package/dist/tsup/common/websocket.cjs +10 -0
  110. package/dist/tsup/common/websocket.cjs.map +1 -0
  111. package/dist/tsup/common/websocket.d.cts +3 -0
  112. package/dist/tsup/common/websocket.d.ts +3 -0
  113. package/dist/tsup/common/websocket.js +10 -0
  114. package/dist/tsup/common/websocket.js.map +1 -0
  115. package/dist/tsup/config-BiNoIHRs.d.cts +80 -0
  116. package/dist/tsup/config-BiNoIHRs.d.ts +80 -0
  117. package/dist/tsup/config-P3XujgRr.d.ts +2594 -0
  118. package/dist/tsup/config-_gfywqqI.d.cts +2594 -0
  119. package/dist/tsup/context-Bxd8Cx4H.d.cts +75 -0
  120. package/dist/tsup/context-uNA4TRn3.d.ts +75 -0
  121. package/dist/tsup/db/drizzle/mod.cjs +49 -0
  122. package/dist/tsup/db/drizzle/mod.cjs.map +1 -0
  123. package/dist/tsup/db/drizzle/mod.d.cts +17 -0
  124. package/dist/tsup/db/drizzle/mod.d.ts +17 -0
  125. package/dist/tsup/db/drizzle/mod.js +49 -0
  126. package/dist/tsup/db/drizzle/mod.js.map +1 -0
  127. package/dist/tsup/db/mod.cjs +9 -0
  128. package/dist/tsup/db/mod.cjs.map +1 -0
  129. package/dist/tsup/db/mod.d.cts +9 -0
  130. package/dist/tsup/db/mod.d.ts +9 -0
  131. package/dist/tsup/db/mod.js +9 -0
  132. package/dist/tsup/db/mod.js.map +1 -0
  133. package/dist/tsup/driver-BcLvZcKl.d.cts +13 -0
  134. package/dist/tsup/driver-CPGHKXyh.d.ts +13 -0
  135. package/dist/tsup/driver-helpers/mod.cjs +53 -0
  136. package/dist/tsup/driver-helpers/mod.cjs.map +1 -0
  137. package/dist/tsup/driver-helpers/mod.d.cts +47 -0
  138. package/dist/tsup/driver-helpers/mod.d.ts +47 -0
  139. package/dist/tsup/driver-helpers/mod.js +53 -0
  140. package/dist/tsup/driver-helpers/mod.js.map +1 -0
  141. package/dist/tsup/driver-test-suite/mod.cjs +4974 -0
  142. package/dist/tsup/driver-test-suite/mod.cjs.map +1 -0
  143. package/dist/tsup/driver-test-suite/mod.d.cts +73 -0
  144. package/dist/tsup/driver-test-suite/mod.d.ts +73 -0
  145. package/dist/tsup/driver-test-suite/mod.js +4974 -0
  146. package/dist/tsup/driver-test-suite/mod.js.map +1 -0
  147. package/dist/tsup/inspector/mod.cjs +164 -0
  148. package/dist/tsup/inspector/mod.cjs.map +1 -0
  149. package/dist/tsup/inspector/mod.d.cts +130 -0
  150. package/dist/tsup/inspector/mod.d.ts +130 -0
  151. package/dist/tsup/inspector/mod.js +164 -0
  152. package/dist/tsup/inspector/mod.js.map +1 -0
  153. package/dist/tsup/keys-CydblqMh.d.cts +13 -0
  154. package/dist/tsup/keys-CydblqMh.d.ts +13 -0
  155. package/dist/tsup/mod.cjs +82 -0
  156. package/dist/tsup/mod.cjs.map +1 -0
  157. package/dist/tsup/mod.d.cts +126 -0
  158. package/dist/tsup/mod.d.ts +126 -0
  159. package/dist/tsup/mod.js +82 -0
  160. package/dist/tsup/mod.js.map +1 -0
  161. package/dist/tsup/serve-test-suite/mod.cjs +2601 -0
  162. package/dist/tsup/serve-test-suite/mod.cjs.map +1 -0
  163. package/dist/tsup/serve-test-suite/mod.d.cts +9 -0
  164. package/dist/tsup/serve-test-suite/mod.d.ts +9 -0
  165. package/dist/tsup/serve-test-suite/mod.js +2601 -0
  166. package/dist/tsup/serve-test-suite/mod.js.map +1 -0
  167. package/dist/tsup/test/mod.cjs +90 -0
  168. package/dist/tsup/test/mod.cjs.map +1 -0
  169. package/dist/tsup/test/mod.d.cts +26 -0
  170. package/dist/tsup/test/mod.d.ts +26 -0
  171. package/dist/tsup/test/mod.js +90 -0
  172. package/dist/tsup/test/mod.js.map +1 -0
  173. package/dist/tsup/utils-fwx3o3K9.d.cts +18 -0
  174. package/dist/tsup/utils-fwx3o3K9.d.ts +18 -0
  175. package/dist/tsup/utils.cjs +43 -0
  176. package/dist/tsup/utils.cjs.map +1 -0
  177. package/dist/tsup/utils.d.cts +148 -0
  178. package/dist/tsup/utils.d.ts +148 -0
  179. package/dist/tsup/utils.js +43 -0
  180. package/dist/tsup/utils.js.map +1 -0
  181. package/dist/tsup/v3-DnYObHH3.d.cts +279 -0
  182. package/dist/tsup/v3-DnYObHH3.d.ts +279 -0
  183. package/dist/tsup/workflow/mod.cjs +16 -0
  184. package/dist/tsup/workflow/mod.cjs.map +1 -0
  185. package/dist/tsup/workflow/mod.d.cts +25 -0
  186. package/dist/tsup/workflow/mod.d.ts +25 -0
  187. package/dist/tsup/workflow/mod.js +16 -0
  188. package/dist/tsup/workflow/mod.js.map +1 -0
  189. package/package.json +293 -5
  190. package/src/actor/config.ts +1221 -0
  191. package/src/actor/conn/driver.ts +61 -0
  192. package/src/actor/conn/drivers/http.ts +17 -0
  193. package/src/actor/conn/drivers/raw-request.ts +24 -0
  194. package/src/actor/conn/drivers/raw-websocket.ts +65 -0
  195. package/src/actor/conn/drivers/websocket.ts +144 -0
  196. package/src/actor/conn/mod.ts +288 -0
  197. package/src/actor/conn/persisted.ts +81 -0
  198. package/src/actor/conn/state-manager.ts +196 -0
  199. package/src/actor/contexts/action.ts +47 -0
  200. package/src/actor/contexts/base/actor.ts +347 -0
  201. package/src/actor/contexts/base/conn-init.ts +68 -0
  202. package/src/actor/contexts/base/conn.ts +73 -0
  203. package/src/actor/contexts/before-action-response.ts +42 -0
  204. package/src/actor/contexts/before-connect.ts +31 -0
  205. package/src/actor/contexts/connect.ts +42 -0
  206. package/src/actor/contexts/create-conn-state.ts +32 -0
  207. package/src/actor/contexts/create-vars.ts +39 -0
  208. package/src/actor/contexts/create.ts +39 -0
  209. package/src/actor/contexts/destroy.ts +42 -0
  210. package/src/actor/contexts/disconnect.ts +43 -0
  211. package/src/actor/contexts/index.ts +33 -0
  212. package/src/actor/contexts/request.ts +80 -0
  213. package/src/actor/contexts/run.ts +47 -0
  214. package/src/actor/contexts/sleep.ts +42 -0
  215. package/src/actor/contexts/state-change.ts +42 -0
  216. package/src/actor/contexts/wake.ts +42 -0
  217. package/src/actor/contexts/websocket.ts +80 -0
  218. package/src/actor/database.ts +13 -0
  219. package/src/actor/definition.ts +64 -0
  220. package/src/actor/driver.ts +114 -0
  221. package/src/actor/errors.ts +556 -0
  222. package/src/actor/instance/connection-manager.ts +574 -0
  223. package/src/actor/instance/event-manager.ts +314 -0
  224. package/src/actor/instance/keys.ts +146 -0
  225. package/src/actor/instance/kv.ts +241 -0
  226. package/src/actor/instance/mod.ts +1658 -0
  227. package/src/actor/instance/persisted.ts +67 -0
  228. package/src/actor/instance/queue-manager.ts +603 -0
  229. package/src/actor/instance/queue.ts +345 -0
  230. package/src/actor/instance/schedule-manager.ts +392 -0
  231. package/src/actor/instance/state-manager.ts +542 -0
  232. package/src/actor/instance/traces-driver.ts +128 -0
  233. package/src/actor/keys.test.ts +275 -0
  234. package/src/actor/keys.ts +89 -0
  235. package/src/actor/log.ts +6 -0
  236. package/src/actor/mod.ts +110 -0
  237. package/src/actor/protocol/old.ts +416 -0
  238. package/src/actor/protocol/serde.ts +222 -0
  239. package/src/actor/router-endpoints.ts +400 -0
  240. package/src/actor/router-websocket-endpoints.test.ts +54 -0
  241. package/src/actor/router-websocket-endpoints.ts +405 -0
  242. package/src/actor/router.ts +380 -0
  243. package/src/actor/schedule.ts +17 -0
  244. package/src/actor/schema.ts +291 -0
  245. package/src/actor/utils.test.ts +48 -0
  246. package/src/actor/utils.ts +158 -0
  247. package/src/client/actor-common.ts +32 -0
  248. package/src/client/actor-conn.ts +1262 -0
  249. package/src/client/actor-handle.ts +344 -0
  250. package/src/client/actor-query.ts +112 -0
  251. package/src/client/client.ts +558 -0
  252. package/src/client/config.ts +151 -0
  253. package/src/client/errors.ts +76 -0
  254. package/src/client/log.ts +5 -0
  255. package/src/client/mod.browser.ts +2 -0
  256. package/src/client/mod.ts +70 -0
  257. package/src/client/queue.ts +146 -0
  258. package/src/client/raw-utils.ts +149 -0
  259. package/src/client/test.ts +44 -0
  260. package/src/client/utils.ts +252 -0
  261. package/src/common/actor-router-consts.ts +59 -0
  262. package/src/common/cors.ts +57 -0
  263. package/src/common/eventsource-interface.ts +47 -0
  264. package/src/common/eventsource.ts +44 -0
  265. package/src/common/inline-websocket-adapter.ts +154 -0
  266. package/src/common/log-levels.ts +27 -0
  267. package/src/common/log.ts +229 -0
  268. package/src/common/logfmt.ts +221 -0
  269. package/src/common/network.ts +2 -0
  270. package/src/common/router.ts +174 -0
  271. package/src/common/utils.ts +339 -0
  272. package/src/common/websocket-interface.ts +7 -0
  273. package/src/common/websocket.ts +43 -0
  274. package/src/db/config.ts +100 -0
  275. package/src/db/drizzle/mod.ts +226 -0
  276. package/src/db/drizzle/sqlite-core.ts +22 -0
  277. package/src/db/mod.ts +125 -0
  278. package/src/db/shared.ts +92 -0
  279. package/src/db/sqlite-vfs.ts +12 -0
  280. package/src/devtools-loader/index.ts +33 -0
  281. package/src/devtools-loader/log.ts +5 -0
  282. package/src/driver-helpers/mod.ts +33 -0
  283. package/src/driver-helpers/utils.ts +54 -0
  284. package/src/driver-test-suite/log.ts +5 -0
  285. package/src/driver-test-suite/mod.ts +293 -0
  286. package/src/driver-test-suite/test-inline-client-driver.ts +307 -0
  287. package/src/driver-test-suite/tests/access-control.ts +218 -0
  288. package/src/driver-test-suite/tests/action-features.ts +203 -0
  289. package/src/driver-test-suite/tests/actor-conn-hibernation.ts +152 -0
  290. package/src/driver-test-suite/tests/actor-conn-state.ts +300 -0
  291. package/src/driver-test-suite/tests/actor-conn.ts +596 -0
  292. package/src/driver-test-suite/tests/actor-db-raw.ts +73 -0
  293. package/src/driver-test-suite/tests/actor-db.ts +477 -0
  294. package/src/driver-test-suite/tests/actor-destroy.ts +294 -0
  295. package/src/driver-test-suite/tests/actor-driver.ts +18 -0
  296. package/src/driver-test-suite/tests/actor-error-handling.ts +150 -0
  297. package/src/driver-test-suite/tests/actor-handle.ts +312 -0
  298. package/src/driver-test-suite/tests/actor-inline-client.ts +163 -0
  299. package/src/driver-test-suite/tests/actor-inspector.ts +264 -0
  300. package/src/driver-test-suite/tests/actor-kv.ts +65 -0
  301. package/src/driver-test-suite/tests/actor-metadata.ts +116 -0
  302. package/src/driver-test-suite/tests/actor-onstatechange.ts +95 -0
  303. package/src/driver-test-suite/tests/actor-queue.ts +325 -0
  304. package/src/driver-test-suite/tests/actor-run.ts +181 -0
  305. package/src/driver-test-suite/tests/actor-schedule.ts +97 -0
  306. package/src/driver-test-suite/tests/actor-sleep.ts +415 -0
  307. package/src/driver-test-suite/tests/actor-state.ts +54 -0
  308. package/src/driver-test-suite/tests/actor-stateless.ts +70 -0
  309. package/src/driver-test-suite/tests/actor-vars.ts +97 -0
  310. package/src/driver-test-suite/tests/actor-workflow.ts +118 -0
  311. package/src/driver-test-suite/tests/manager-driver.ts +388 -0
  312. package/src/driver-test-suite/tests/raw-http-direct-registry.ts +227 -0
  313. package/src/driver-test-suite/tests/raw-http-request-properties.ts +454 -0
  314. package/src/driver-test-suite/tests/raw-http.ts +359 -0
  315. package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +393 -0
  316. package/src/driver-test-suite/tests/raw-websocket.ts +513 -0
  317. package/src/driver-test-suite/tests/request-access.ts +240 -0
  318. package/src/driver-test-suite/utils.ts +80 -0
  319. package/src/drivers/default.ts +38 -0
  320. package/src/drivers/engine/actor-driver.ts +1027 -0
  321. package/src/drivers/engine/config.ts +43 -0
  322. package/src/drivers/engine/log.ts +5 -0
  323. package/src/drivers/engine/mod.ts +36 -0
  324. package/src/drivers/file-system/actor.ts +102 -0
  325. package/src/drivers/file-system/global-state.ts +1445 -0
  326. package/src/drivers/file-system/kv-limits.ts +70 -0
  327. package/src/drivers/file-system/log.ts +5 -0
  328. package/src/drivers/file-system/manager.ts +300 -0
  329. package/src/drivers/file-system/mod.ts +78 -0
  330. package/src/drivers/file-system/sqlite-runtime.ts +210 -0
  331. package/src/drivers/file-system/utils.ts +125 -0
  332. package/src/engine-process/constants.ts +2 -0
  333. package/src/engine-process/log.ts +5 -0
  334. package/src/engine-process/mod.ts +464 -0
  335. package/src/globals.d.ts +35 -0
  336. package/src/inspector/actor-inspector.ts +352 -0
  337. package/src/inspector/config.ts +49 -0
  338. package/src/inspector/handler.ts +273 -0
  339. package/src/inspector/log.ts +5 -0
  340. package/src/inspector/mod.browser.ts +8 -0
  341. package/src/inspector/mod.ts +4 -0
  342. package/src/inspector/serve-ui.ts +40 -0
  343. package/src/inspector/transport.ts +18 -0
  344. package/src/inspector/utils.ts +32 -0
  345. package/src/manager/driver.ts +106 -0
  346. package/src/manager/gateway.ts +668 -0
  347. package/src/manager/log.ts +5 -0
  348. package/src/manager/mod.ts +2 -0
  349. package/src/manager/protocol/mod.ts +22 -0
  350. package/src/manager/protocol/query.ts +85 -0
  351. package/src/manager/router-schema.ts +22 -0
  352. package/src/manager/router.ts +660 -0
  353. package/src/manager-api/actors.ts +83 -0
  354. package/src/manager-api/common.ts +4 -0
  355. package/src/mod.ts +24 -0
  356. package/src/registry/config/driver.ts +21 -0
  357. package/src/registry/config/index.ts +510 -0
  358. package/src/registry/config/legacy-runner.ts +157 -0
  359. package/src/registry/config/runner.ts +21 -0
  360. package/src/registry/config/serverless.ts +94 -0
  361. package/src/registry/index.ts +194 -0
  362. package/src/registry/log.ts +5 -0
  363. package/src/remote-manager-driver/actor-http-client.ts +84 -0
  364. package/src/remote-manager-driver/actor-websocket-client.ts +81 -0
  365. package/src/remote-manager-driver/api-endpoints.ts +159 -0
  366. package/src/remote-manager-driver/api-utils.ts +69 -0
  367. package/src/remote-manager-driver/log.ts +5 -0
  368. package/src/remote-manager-driver/metadata.ts +64 -0
  369. package/src/remote-manager-driver/mod.ts +414 -0
  370. package/src/remote-manager-driver/ws-proxy.ts +189 -0
  371. package/src/schemas/actor-inspector/mod.ts +1 -0
  372. package/src/schemas/actor-inspector/versioned.ts +233 -0
  373. package/src/schemas/actor-persist/mod.ts +1 -0
  374. package/src/schemas/actor-persist/versioned.ts +217 -0
  375. package/src/schemas/client-protocol/mod.ts +1 -0
  376. package/src/schemas/client-protocol/versioned.ts +330 -0
  377. package/src/schemas/client-protocol-zod/mod.ts +118 -0
  378. package/src/schemas/file-system-driver/mod.ts +1 -0
  379. package/src/schemas/file-system-driver/versioned.ts +135 -0
  380. package/src/schemas/persist/mod.ts +1 -0
  381. package/src/schemas/transport/mod.ts +1 -0
  382. package/src/serde.ts +138 -0
  383. package/src/serve-test-suite/mod.ts +148 -0
  384. package/src/serverless/configure.ts +82 -0
  385. package/src/serverless/log.ts +5 -0
  386. package/src/serverless/router.test.ts +299 -0
  387. package/src/serverless/router.ts +215 -0
  388. package/src/test/log.ts +5 -0
  389. package/src/test/mod.ts +99 -0
  390. package/src/utils/crypto.ts +24 -0
  391. package/src/utils/endpoint-parser.test.ts +202 -0
  392. package/src/utils/endpoint-parser.ts +124 -0
  393. package/src/utils/env-vars.ts +78 -0
  394. package/src/utils/node.ts +178 -0
  395. package/src/utils/router.ts +83 -0
  396. package/src/utils/serve.ts +212 -0
  397. package/src/utils.test.ts +34 -0
  398. package/src/utils.ts +437 -0
  399. package/src/workflow/constants.ts +2 -0
  400. package/src/workflow/context.ts +597 -0
  401. package/src/workflow/driver.ts +194 -0
  402. package/src/workflow/inspector.ts +268 -0
  403. package/src/workflow/mod.ts +128 -0
@@ -0,0 +1,1027 @@
1
+ import type {
2
+ ActorConfig as EngineActorConfig,
3
+ RunnerConfig as EngineRunnerConfig,
4
+ HibernatingWebSocketMetadata,
5
+ } from "@rivetkit/engine-runner";
6
+ import type { SqliteVfs } from "@rivetkit/sqlite-vfs";
7
+ import { idToStr, Runner } from "@rivetkit/engine-runner";
8
+ import * as cbor from "cbor-x";
9
+ import type { Context as HonoContext } from "hono";
10
+ import { streamSSE } from "hono/streaming";
11
+ import { WSContext, type WSContextInit } from "hono/ws";
12
+ import invariant from "invariant";
13
+ import { type AnyConn, CONN_STATE_MANAGER_SYMBOL } from "@/actor/conn/mod";
14
+ import { lookupInRegistry } from "@/actor/definition";
15
+ import { KEYS } from "@/actor/instance/keys";
16
+ import { deserializeActorKey } from "@/actor/keys";
17
+ import { getValueLength } from "@/actor/protocol/old";
18
+ import { type ActorRouter, createActorRouter } from "@/actor/router";
19
+ import {
20
+ parseWebSocketProtocols,
21
+ routeWebSocket,
22
+ truncateRawWebSocketPathPrefix,
23
+ type UpgradeWebSocketArgs,
24
+ } from "@/actor/router-websocket-endpoints";
25
+ import type { Client } from "@/client/client";
26
+ import {
27
+ PATH_CONNECT,
28
+ PATH_INSPECTOR_CONNECT,
29
+ PATH_WEBSOCKET_BASE,
30
+ PATH_WEBSOCKET_PREFIX,
31
+ } from "@/common/actor-router-consts";
32
+ import { getLogger } from "@/common/log";
33
+ import type {
34
+ RivetMessageEvent,
35
+ UniversalWebSocket,
36
+ } from "@/common/websocket-interface";
37
+ import {
38
+ type ActorDriver,
39
+ type AnyActorInstance,
40
+ getInitialActorKvState,
41
+ type ManagerDriver,
42
+ } from "@/driver-helpers/mod";
43
+ import { buildActorNames, type RegistryConfig } from "@/registry/config";
44
+ import { getEndpoint } from "@/remote-manager-driver/api-utils";
45
+ import {
46
+ type LongTimeoutHandle,
47
+ promiseWithResolvers,
48
+ setLongTimeout,
49
+ stringifyError,
50
+ VERSION,
51
+ } from "@/utils";
52
+ import { logger } from "./log";
53
+
54
+ const RUNNER_SSE_PING_INTERVAL = 1000;
55
+ const RUNNER_STOP_WAIT_MS = 15_000;
56
+
57
+ // Message ack deadline is 30s on the gateway, but we will ack more frequently
58
+ // in order to minimize the message buffer size on the gateway and to give
59
+ // generous breathing room for the timeout.
60
+ //
61
+ // See engine/packages/pegboard-gateway/src/shared_state.rs
62
+ // (HWS_MESSAGE_ACK_TIMEOUT)
63
+ const CONN_MESSAGE_ACK_DEADLINE = 5_000;
64
+
65
+ // Force saveState when cumulative message size reaches this threshold (0.5 MB)
66
+ //
67
+ // See engine/packages/pegboard-gateway/src/shared_state.rs
68
+ // (HWS_MAX_PENDING_MSGS_SIZE_PER_REQ)
69
+ const CONN_BUFFERED_MESSAGE_SIZE_THRESHOLD = 500_000;
70
+
71
+ interface ActorHandler {
72
+ actor?: AnyActorInstance;
73
+ actorStartPromise?: ReturnType<typeof promiseWithResolvers<void>>;
74
+ actorStartError?: Error;
75
+ alarmTimeout?: LongTimeoutHandle;
76
+ }
77
+
78
+ export type DriverContext = {};
79
+
80
+ export class EngineActorDriver implements ActorDriver {
81
+ #config: RegistryConfig;
82
+ #managerDriver: ManagerDriver;
83
+ #inlineClient: Client<any>;
84
+ #runner: Runner;
85
+ #actors: Map<string, ActorHandler> = new Map();
86
+ #actorRouter: ActorRouter;
87
+
88
+ #runnerStarted: PromiseWithResolvers<undefined> = promiseWithResolvers((reason) => logger().warn({ msg: "unhandled runner started promise rejection", reason }));
89
+ #runnerStopped: PromiseWithResolvers<undefined> = promiseWithResolvers((reason) => logger().warn({ msg: "unhandled runner stopped promise rejection", reason }));
90
+ #isRunnerStopped: boolean = false;
91
+
92
+ // HACK: Track actor stop intent locally since the runner protocol doesn't
93
+ // pass the stop reason to onActorStop. This will be fixed when the runner
94
+ // protocol is updated to send the intent directly (see RVT-5284)
95
+ #actorStopIntent: Map<string, "sleep" | "destroy"> = new Map();
96
+
97
+ // Map of conn IDs to message index waiting to be persisted before sending
98
+ // an ack
99
+ //
100
+ // serverMessageIndex is updated and pendingAck is flagged in needed in
101
+ // onBeforePersistConnect, then the HWS ack message is sent in
102
+ // onAfterPersistConn. This allows us to track what's about to be written
103
+ // to storage to prevent race conditions with the serverMessageIndex being
104
+ // updated while writing the existing state.
105
+ //
106
+ // bufferedMessageSize tracks the total bytes received since last persist
107
+ // to force a saveState when threshold is reached. This is the amount of
108
+ // data currently buffered on the gateway.
109
+ #hwsMessageIndex = new Map<
110
+ string,
111
+ {
112
+ serverMessageIndex: number;
113
+ bufferedMessageSize: number;
114
+ pendingAckFromMessageIndex: boolean;
115
+ pendingAckFromBufferSize: boolean;
116
+ }
117
+ >();
118
+
119
+ constructor(
120
+ config: RegistryConfig,
121
+ managerDriver: ManagerDriver,
122
+ inlineClient: Client<any>,
123
+ ) {
124
+ this.#config = config;
125
+ this.#managerDriver = managerDriver;
126
+ this.#inlineClient = inlineClient;
127
+
128
+ // HACK: Override inspector token (which are likely to be
129
+ // removed later on) with token from x-rivet-token header
130
+ const token = config.token;
131
+ // TODO:
132
+ // if (token && runConfig.inspector && runConfig.inspector.enabled) {
133
+ // runConfig.inspector.token = () => token;
134
+ // }
135
+
136
+ this.#actorRouter = createActorRouter(
137
+ config,
138
+ this,
139
+ undefined,
140
+ config.test.enabled,
141
+ );
142
+
143
+ // Create runner configuration
144
+ const engineRunnerConfig: EngineRunnerConfig = {
145
+ version: config.runner.version,
146
+ endpoint: getEndpoint(config),
147
+ token,
148
+ namespace: config.namespace,
149
+ totalSlots: config.runner.totalSlots,
150
+ runnerName: config.runner.runnerName,
151
+ runnerKey: config.runner.runnerKey ?? crypto.randomUUID(),
152
+ metadata: {
153
+ rivetkit: { version: VERSION },
154
+ },
155
+ prepopulateActorNames: buildActorNames(config),
156
+ onConnected: () => {
157
+ this.#runnerStarted.resolve(undefined);
158
+ },
159
+ onDisconnected: (_code, _reason) => {},
160
+ onShutdown: () => {
161
+ this.#runnerStopped.resolve(undefined);
162
+ this.#isRunnerStopped = true;
163
+ },
164
+ fetch: this.#runnerFetch.bind(this),
165
+ websocket: this.#runnerWebSocket.bind(this),
166
+ hibernatableWebSocket: {
167
+ canHibernate: this.#hwsCanHibernate.bind(this),
168
+ },
169
+ onActorStart: this.#runnerOnActorStart.bind(this),
170
+ onActorStop: this.#runnerOnActorStop.bind(this),
171
+ logger: getLogger("engine-runner"),
172
+ };
173
+
174
+ // Create and start runner
175
+ this.#runner = new Runner(engineRunnerConfig);
176
+ this.#runner.start();
177
+ logger().debug({
178
+ msg: "engine runner started",
179
+ endpoint: config.endpoint,
180
+ namespace: config.namespace,
181
+ runnerName: config.runner.runnerName,
182
+ });
183
+ }
184
+
185
+ getExtraActorLogParams(): Record<string, string> {
186
+ return { runnerId: this.#runner.runnerId ?? "-" };
187
+ }
188
+
189
+ async #loadActorHandler(actorId: string): Promise<ActorHandler> {
190
+ // Check if actor is already loaded
191
+ const handler = this.#actors.get(actorId);
192
+ if (!handler)
193
+ throw new Error(`Actor handler does not exist ${actorId}`);
194
+ if (handler.actorStartPromise) await handler.actorStartPromise.promise;
195
+ if (handler.actorStartError) throw handler.actorStartError;
196
+ if (!handler.actor) throw new Error("Actor should be loaded");
197
+ return handler;
198
+ }
199
+
200
+ getContext(actorId: string): DriverContext {
201
+ return {};
202
+ }
203
+
204
+ async setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {
205
+ const handler = this.#actors.get(actor.id);
206
+ if (!handler) {
207
+ logger().warn({
208
+ msg: "no handler for actor to set alarm",
209
+ });
210
+
211
+ return;
212
+ }
213
+
214
+ // Clear prev timeout
215
+ if (handler.alarmTimeout) {
216
+ handler.alarmTimeout.abort();
217
+ handler.alarmTimeout = undefined;
218
+ }
219
+
220
+ // Set alarm
221
+ const delay = Math.max(0, timestamp - Date.now());
222
+ handler.alarmTimeout = setLongTimeout(() => {
223
+ actor.onAlarm();
224
+ handler.alarmTimeout = undefined;
225
+ }, delay);
226
+
227
+ // TODO: This call may not be needed on ActorInstance.start, but it does help ensure that the local state is synced with the alarm state
228
+ // Set alarm on Rivet
229
+ //
230
+ // This does not call an "alarm" event like Durable Objects.
231
+ // Instead, it just wakes the actor on the alarm (if not
232
+ // already awake).
233
+ //
234
+ // onAlarm is automatically called on `ActorInstance.start` when waking
235
+ // again.
236
+ this.#runner.setAlarm(actor.id, timestamp);
237
+ }
238
+
239
+ // No database overrides - will use KV-backed implementation from rivetkit/db
240
+
241
+ // MARK: - Batch KV operations
242
+ async kvBatchPut(
243
+ actorId: string,
244
+ entries: [Uint8Array, Uint8Array][],
245
+ ): Promise<void> {
246
+ await this.#runner.kvPut(actorId, entries);
247
+ }
248
+
249
+ async kvBatchGet(
250
+ actorId: string,
251
+ keys: Uint8Array[],
252
+ ): Promise<(Uint8Array | null)[]> {
253
+ return await this.#runner.kvGet(actorId, keys);
254
+ }
255
+
256
+ async kvBatchDelete(actorId: string, keys: Uint8Array[]): Promise<void> {
257
+ await this.#runner.kvDelete(actorId, keys);
258
+ }
259
+
260
+ async kvList(actorId: string): Promise<Uint8Array[]> {
261
+ const entries = await this.#runner.kvListPrefix(
262
+ actorId,
263
+ new Uint8Array(),
264
+ );
265
+ const keys = entries.map(([key]) => key);
266
+ logger().info({
267
+ msg: "kvList called",
268
+ actorId,
269
+ keysCount: keys.length,
270
+ keys: keys.map((k) => new TextDecoder().decode(k)),
271
+ });
272
+ return keys;
273
+ }
274
+
275
+ async kvListPrefix(
276
+ actorId: string,
277
+ prefix: Uint8Array,
278
+ ): Promise<[Uint8Array, Uint8Array][]> {
279
+ const result = await this.#runner.kvListPrefix(actorId, prefix);
280
+ logger().info({
281
+ msg: "kvListPrefix called",
282
+ actorId,
283
+ prefixStr: new TextDecoder().decode(prefix),
284
+ entriesCount: result.length,
285
+ keys: result.map(([key]) => new TextDecoder().decode(key)),
286
+ });
287
+ return result;
288
+ }
289
+
290
+ /** Creates a SQLite VFS instance for creating KV-backed databases */
291
+ async createSqliteVfs(): Promise<SqliteVfs> {
292
+ // Dynamic import keeps @rivetkit/sqlite out of the main entrypoint bundle.
293
+ // Returning a fresh SqliteVfs gives each actor an isolated sqlite module
294
+ // instance, avoiding async re-entrancy across actors.
295
+ const specifier = "@rivetkit/" + "sqlite-vfs";
296
+ const { SqliteVfs } = await import(specifier);
297
+ return new SqliteVfs();
298
+ }
299
+
300
+ // MARK: - Actor Lifecycle
301
+ async loadActor(actorId: string): Promise<AnyActorInstance> {
302
+ const handler = await this.#loadActorHandler(actorId);
303
+ if (!handler.actor) throw new Error(`Actor ${actorId} failed to load`);
304
+ return handler.actor;
305
+ }
306
+
307
+ startSleep(actorId: string) {
308
+ // HACK: Track intent for onActorStop (see RVT-5284)
309
+ this.#actorStopIntent.set(actorId, "sleep");
310
+ this.#runner.sleepActor(actorId);
311
+ }
312
+
313
+ startDestroy(actorId: string) {
314
+ // HACK: Track intent for onActorStop (see RVT-5284)
315
+ this.#actorStopIntent.set(actorId, "destroy");
316
+ this.#runner.stopActor(actorId);
317
+ }
318
+
319
+ async shutdownRunner(immediate: boolean): Promise<void> {
320
+ logger().info({ msg: "stopping engine actor driver", immediate });
321
+
322
+ // TODO: We need to update the runner to have a draining state so:
323
+ // 1. Send ToServerDraining
324
+ // - This causes Pegboard to stop allocating actors to this runner
325
+ // 2. Pegboard sends ToClientStopActor for all actors on this runner which handles the graceful migration of each actor independently
326
+ // 3. Send ToServerStopping once all actors have successfully stopped
327
+ //
328
+ // What's happening right now is:
329
+ // 1. All actors enter stopped state
330
+ // 2. Actors still respond to requests because only RivetKit knows it's
331
+ // stopping, this causes all requests to issue errors that the actor is
332
+ // stopping. (This will NOT return a 503 bc the runner has no idea the
333
+ // actors are stopping.)
334
+ // 3. Once the last actor stops, then the runner finally stops + actors
335
+ // reschedule
336
+ //
337
+ // This means that:
338
+ // - All actors on this runner are bricked until the slowest onStop finishes
339
+ // - Guard will not gracefully handle requests bc it's not receiving a 503
340
+ // - Actors can still be scheduled to this runner while the other
341
+ // actors are stopping, meaning that those actors will NOT get onStop
342
+ // and will potentiall corrupt their state
343
+ //
344
+ // HACK: Stop all actors to allow state to be saved
345
+ // NOTE: onStop is only supposed to be called by the runner, we're
346
+ // abusing it here
347
+ logger().debug({
348
+ msg: "stopping all actors before shutdown",
349
+ actorCount: this.#actors.size,
350
+ });
351
+ const stopPromises: Promise<void>[] = [];
352
+ for (const [_actorId, handler] of this.#actors.entries()) {
353
+ if (handler.actor) {
354
+ stopPromises.push(
355
+ handler.actor.onStop("sleep").catch((err) => {
356
+ handler.actor?.rLog.error({
357
+ msg: "onStop errored",
358
+ error: stringifyError(err),
359
+ });
360
+ }),
361
+ );
362
+ }
363
+ }
364
+ await Promise.all(stopPromises);
365
+ logger().debug({ msg: "all actors stopped" });
366
+
367
+ try {
368
+ await this.#runner.shutdown(immediate);
369
+ } catch (error) {
370
+ const message = error instanceof Error ? error.message : String(error);
371
+ if (message.includes("WebSocket connection closed during shutdown")) {
372
+ logger().debug({
373
+ msg: "ignoring shutdown websocket close race",
374
+ error: message,
375
+ });
376
+ } else {
377
+ throw error;
378
+ }
379
+ }
380
+
381
+ const stopped = await Promise.race([
382
+ this.#runnerStopped.promise.then(() => true),
383
+ new Promise<false>((resolve) =>
384
+ setTimeout(() => resolve(false), RUNNER_STOP_WAIT_MS),
385
+ ),
386
+ ]);
387
+ if (!stopped) {
388
+ logger().warn({
389
+ msg: "timed out waiting for runner shutdown",
390
+ waitMs: RUNNER_STOP_WAIT_MS,
391
+ });
392
+ }
393
+ }
394
+
395
+ async serverlessHandleStart(c: HonoContext): Promise<Response> {
396
+ return streamSSE(c, async (stream) => {
397
+ // NOTE: onAbort does not work reliably
398
+ stream.onAbort(() => {});
399
+ c.req.raw.signal.addEventListener("abort", () => {
400
+ logger().debug("SSE aborted, shutting down runner");
401
+
402
+ // We cannot assume that the request will always be closed gracefully by Rivet. We always proceed with a graceful shutdown in case the request was terminated for any other reason.
403
+ //
404
+ // If we did not use a graceful shutdown, the runner would
405
+ this.shutdownRunner(false);
406
+ });
407
+
408
+ await this.#runnerStarted.promise;
409
+
410
+ // Runner id should be set if the runner started
411
+ const payload = this.#runner.getServerlessInitPacket();
412
+ invariant(payload, "runnerId not set");
413
+ await stream.writeSSE({ data: payload });
414
+
415
+ // Send ping every second to keep the connection alive
416
+ while (true) {
417
+ if (this.#isRunnerStopped) {
418
+ logger().debug({
419
+ msg: "runner is stopped",
420
+ });
421
+ break;
422
+ }
423
+
424
+ if (stream.closed || stream.aborted) {
425
+ logger().debug({
426
+ msg: "runner sse stream closed",
427
+ closed: stream.closed,
428
+ aborted: stream.aborted,
429
+ });
430
+ break;
431
+ }
432
+
433
+ await stream.writeSSE({ event: "ping", data: "" });
434
+ await stream.sleep(RUNNER_SSE_PING_INTERVAL);
435
+ }
436
+
437
+ // Wait for the runner to stop if the SSE stream aborted early for any reason
438
+ await this.#runnerStopped.promise;
439
+ });
440
+ }
441
+
442
+ async #runnerOnActorStart(
443
+ actorId: string,
444
+ generation: number,
445
+ actorConfig: EngineActorConfig,
446
+ ): Promise<void> {
447
+ logger().debug({
448
+ msg: "runner actor starting",
449
+ actorId,
450
+ name: actorConfig.name,
451
+ key: actorConfig.key,
452
+ generation,
453
+ });
454
+
455
+ // Deserialize input
456
+ let input: any;
457
+ if (actorConfig.input) {
458
+ input = cbor.decode(actorConfig.input);
459
+ }
460
+
461
+ // Get or create handler
462
+ let handler = this.#actors.get(actorId);
463
+ if (!handler) {
464
+ // IMPORTANT: We must set the handler in the map synchronously before doing any
465
+ // async operations to avoid race conditions where multiple calls might try to
466
+ // create the same handler simultaneously.
467
+ handler = {
468
+ actorStartPromise: promiseWithResolvers((reason) => logger().warn({ msg: "unhandled actor start promise rejection", reason })),
469
+ };
470
+ this.#actors.set(actorId, handler);
471
+ }
472
+ handler.actorStartError = undefined;
473
+
474
+ const name = actorConfig.name as string;
475
+ invariant(actorConfig.key, "actor should have a key");
476
+ const key = deserializeActorKey(actorConfig.key);
477
+
478
+ try {
479
+ // Initialize storage
480
+ const [persistDataBuffer] = await this.#runner.kvGet(actorId, [
481
+ KEYS.PERSIST_DATA,
482
+ ]);
483
+ if (persistDataBuffer === null) {
484
+ const initialKvState = getInitialActorKvState(input);
485
+ await this.#runner.kvPut(actorId, initialKvState);
486
+ logger().debug({
487
+ msg: "initialized persist data for new actor",
488
+ actorId,
489
+ });
490
+ } else {
491
+ logger().debug({
492
+ msg: "found existing persist data for actor",
493
+ actorId,
494
+ dataSize: persistDataBuffer.byteLength,
495
+ });
496
+ }
497
+
498
+ // Create actor instance
499
+ const definition = lookupInRegistry(this.#config, actorConfig.name);
500
+ handler.actor = await definition.instantiate();
501
+
502
+ // Start actor
503
+ await handler.actor.start(
504
+ this,
505
+ this.#inlineClient,
506
+ actorId,
507
+ name,
508
+ key,
509
+ "unknown", // TODO: Add regions
510
+ );
511
+
512
+ logger().debug({ msg: "runner actor started", actorId, name, key });
513
+ } catch (innerError) {
514
+ const error =
515
+ innerError instanceof Error
516
+ ? new Error(
517
+ `Failed to start actor ${actorId}: ${innerError.message}`,
518
+ { cause: innerError },
519
+ )
520
+ : new Error(`Failed to start actor ${actorId}: ${String(innerError)}`);
521
+ handler.actor = undefined;
522
+ handler.actorStartError = error;
523
+ handler.actorStartPromise?.reject(error);
524
+ handler.actorStartPromise = undefined;
525
+ logger().error({
526
+ msg: "runner actor failed to start",
527
+ actorId,
528
+ name,
529
+ key,
530
+ err: stringifyError(error),
531
+ });
532
+
533
+ try {
534
+ this.#runner.stopActor(actorId);
535
+ } catch (stopError) {
536
+ logger().debug({
537
+ msg: "failed to stop actor after start failure",
538
+ actorId,
539
+ err: stringifyError(stopError),
540
+ });
541
+ }
542
+ }
543
+ }
544
+
545
+ async #runnerOnActorStop(
546
+ actorId: string,
547
+ generation: number,
548
+ ): Promise<void> {
549
+ logger().debug({ msg: "runner actor stopping", actorId, generation });
550
+
551
+ // HACK: Retrieve the stop intent we tracked locally (see RVT-5284)
552
+ // Default to "sleep" if no intent was recorded (e.g., if the runner
553
+ // initiated the stop)
554
+ //
555
+ // TODO: This will not work if the actor is destroyed from the API
556
+ // correctly. Currently, it will use the sleep intent, but it's
557
+ // actually a destroy intent.
558
+ const reason = this.#actorStopIntent.get(actorId) ?? "sleep";
559
+ this.#actorStopIntent.delete(actorId);
560
+
561
+ const handler = this.#actors.get(actorId);
562
+ if (handler?.actorStartPromise) {
563
+ const startError =
564
+ handler.actorStartError ??
565
+ new Error(`Actor ${actorId} stopped before start completed`);
566
+ handler.actorStartError = startError;
567
+ handler.actorStartPromise.reject(startError);
568
+ handler.actorStartPromise = undefined;
569
+ }
570
+ if (handler?.actor) {
571
+ try {
572
+ await handler.actor.onStop(reason);
573
+ } catch (err) {
574
+ logger().error({
575
+ msg: "error in onStop, proceeding with removing actor",
576
+ err: stringifyError(err),
577
+ });
578
+ }
579
+ }
580
+ if (handler) this.#actors.delete(actorId);
581
+
582
+ logger().debug({ msg: "runner actor stopped", actorId, reason });
583
+ }
584
+
585
+ // MARK: - Runner Networking
586
+ async #runnerFetch(
587
+ _runner: Runner,
588
+ actorId: string,
589
+ _gatewayIdBuf: ArrayBuffer,
590
+ _requestIdBuf: ArrayBuffer,
591
+ request: Request,
592
+ ): Promise<Response> {
593
+ logger().debug({
594
+ msg: "runner fetch",
595
+ actorId,
596
+ url: request.url,
597
+ method: request.method,
598
+ });
599
+ return await this.#actorRouter.fetch(request, { actorId });
600
+ }
601
+
602
+ async #runnerWebSocket(
603
+ _runner: Runner,
604
+ actorId: string,
605
+ websocketRaw: any,
606
+ gatewayIdBuf: ArrayBuffer,
607
+ requestIdBuf: ArrayBuffer,
608
+ request: Request,
609
+ requestPath: string,
610
+ requestHeaders: Record<string, string>,
611
+ isHibernatable: boolean,
612
+ isRestoringHibernatable: boolean,
613
+ ): Promise<void> {
614
+ const websocket = websocketRaw as UniversalWebSocket;
615
+
616
+ // Add a unique ID to track this WebSocket object
617
+ const wsUniqueId = `ws_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
618
+ (websocket as any).__rivet_ws_id = wsUniqueId;
619
+
620
+ logger().debug({
621
+ msg: "runner websocket",
622
+ actorId,
623
+ url: request.url,
624
+ isRestoringHibernatable,
625
+ websocketObjectId: websocketRaw
626
+ ? Object.prototype.toString.call(websocketRaw)
627
+ : "null",
628
+ websocketType: websocketRaw?.constructor?.name,
629
+ wsUniqueId,
630
+ websocketProps: websocketRaw
631
+ ? Object.keys(websocketRaw).join(", ")
632
+ : "null",
633
+ });
634
+
635
+ // Parse configuration from Sec-WebSocket-Protocol header (optional for path-based routing)
636
+ const protocols = request.headers.get("sec-websocket-protocol");
637
+ const { encoding, connParams } = parseWebSocketProtocols(protocols);
638
+
639
+ // Fetch WS handler
640
+ //
641
+ // We store the promise since we need to add WebSocket event listeners immediately that will wait for the promise to resolve
642
+ let wsHandler: UpgradeWebSocketArgs;
643
+ try {
644
+ wsHandler = await routeWebSocket(
645
+ request,
646
+ requestPath,
647
+ requestHeaders,
648
+ this.#config,
649
+ this,
650
+ actorId,
651
+ encoding,
652
+ connParams,
653
+ gatewayIdBuf,
654
+ requestIdBuf,
655
+ isHibernatable,
656
+ isRestoringHibernatable,
657
+ );
658
+ } catch (err) {
659
+ logger().error({ msg: "building websocket handlers errored", err });
660
+ websocketRaw.close(1011, "ws.route_error");
661
+ return;
662
+ }
663
+
664
+ // Connect the Hono WS hook to the adapter
665
+ //
666
+ // We need to assign to `raw` in order for WSContext to expose it on
667
+ // `ws.raw`
668
+ (websocket as WSContextInit).raw = websocket;
669
+ const wsContext = new WSContext(websocket);
670
+
671
+ // Get connection and actor from wsHandler (may be undefined for inspector endpoint)
672
+ const conn = wsHandler.conn;
673
+ const actor = wsHandler.actor;
674
+ const connStateManager = conn?.[CONN_STATE_MANAGER_SYMBOL];
675
+
676
+ // Bind event listeners to Hono WebSocket handlers
677
+ //
678
+ // We update the HWS data after calling handlers in order to ensure
679
+ // that the handler ran successfully. By doing this, we ensure at least
680
+ // once delivery of events to the event handlers.
681
+
682
+ // Log when attaching event listeners
683
+ logger().debug({
684
+ msg: "attaching websocket event listeners",
685
+ actorId,
686
+ connId: conn?.id,
687
+ wsUniqueId: (websocket as any).__rivet_ws_id,
688
+ isRestoringHibernatable,
689
+ websocketType: websocket?.constructor?.name,
690
+ });
691
+
692
+ if (isRestoringHibernatable) {
693
+ wsHandler.onRestore?.(wsContext);
694
+ }
695
+
696
+ websocket.addEventListener("open", (event) => {
697
+ wsHandler.onOpen(event, wsContext);
698
+ });
699
+
700
+ websocket.addEventListener("message", (event: RivetMessageEvent) => {
701
+ logger().debug({
702
+ msg: "websocket message event listener triggered",
703
+ connId: conn?.id,
704
+ actorId: actor?.id,
705
+ messageIndex: event.rivetMessageIndex,
706
+ hasWsHandler: !!wsHandler,
707
+ hasOnMessage: !!wsHandler?.onMessage,
708
+ actorIsStopping: actor?.isStopping,
709
+ websocketType: websocket?.constructor?.name,
710
+ wsUniqueId: (websocket as any).__rivet_ws_id,
711
+ eventTargetWsId: (event.target as any)?.__rivet_ws_id,
712
+ });
713
+
714
+ // Check if actor is stopping - if so, don't process new messages.
715
+ // These messages will be reprocessed when the actor wakes up from hibernation.
716
+ // TODO: This will never retransmit the socket and the socket will close
717
+ if (actor?.isStopping) {
718
+ logger().debug({
719
+ msg: "ignoring ws message, actor is stopping",
720
+ connId: conn?.id,
721
+ actorId: actor?.id,
722
+ messageIndex: event.rivetMessageIndex,
723
+ });
724
+ return;
725
+ }
726
+
727
+ // Process message
728
+ logger().debug({
729
+ msg: "calling wsHandler.onMessage",
730
+ connId: conn?.id,
731
+ messageIndex: event.rivetMessageIndex,
732
+ });
733
+ wsHandler.onMessage(event, wsContext);
734
+
735
+ // Persist message index for hibernatable connections
736
+ const hibernate = connStateManager?.hibernatableData;
737
+
738
+ if (hibernate && conn && actor) {
739
+ invariant(
740
+ typeof event.rivetMessageIndex === "number",
741
+ "missing event.rivetMessageIndex",
742
+ );
743
+
744
+ // Persist message index
745
+ const previousMsgIndex = hibernate.serverMessageIndex;
746
+ hibernate.serverMessageIndex = event.rivetMessageIndex;
747
+ logger().info({
748
+ msg: "persisting message index",
749
+ connId: conn.id,
750
+ previousMsgIndex,
751
+ newMsgIndex: event.rivetMessageIndex,
752
+ });
753
+
754
+ // Calculate message size and track cumulative size
755
+ const entry = this.#hwsMessageIndex.get(conn.id);
756
+ if (entry) {
757
+ // Track message length
758
+ const messageLength = getValueLength(event.data);
759
+ entry.bufferedMessageSize += messageLength;
760
+
761
+ if (
762
+ entry.bufferedMessageSize >=
763
+ CONN_BUFFERED_MESSAGE_SIZE_THRESHOLD
764
+ ) {
765
+ // Reset buffered message size immeidatley (instead
766
+ // of waiting for onAfterPersistConn) since we may
767
+ // receive more messages before onAfterPersistConn
768
+ // is called, which would called saveState
769
+ // immediate multiple times
770
+ entry.bufferedMessageSize = 0;
771
+ entry.pendingAckFromBufferSize = true;
772
+
773
+ // Save state immediately if approaching buffer threshold
774
+ actor.stateManager.saveState({
775
+ immediate: true,
776
+ });
777
+ } else {
778
+ // Save message index. The maxWait is set to the ack deadline
779
+ // since we ack the message immediately after persisting the index.
780
+ // If cumulative size exceeds threshold, force immediate persist.
781
+ //
782
+ // This will call EngineActorDriver.onAfterPersistConn after
783
+ // persist to send the ack to the gateway.
784
+ actor.stateManager.saveState({
785
+ maxWait: CONN_MESSAGE_ACK_DEADLINE,
786
+ });
787
+ }
788
+ } else {
789
+ // Fallback if entry missing
790
+ actor.stateManager.saveState({
791
+ maxWait: CONN_MESSAGE_ACK_DEADLINE,
792
+ });
793
+ }
794
+ }
795
+ });
796
+
797
+ websocket.addEventListener("close", (event) => {
798
+ wsHandler.onClose(event, wsContext);
799
+
800
+ // NOTE: Persisted connection is removed when `conn.disconnect`
801
+ // is called by the WebSocket route
802
+ });
803
+
804
+ websocket.addEventListener("error", (event) => {
805
+ wsHandler.onError(event, wsContext);
806
+ });
807
+
808
+ // Log event listener attachment for restored connections
809
+ if (isRestoringHibernatable) {
810
+ logger().info({
811
+ msg: "event listeners attached to restored websocket",
812
+ actorId,
813
+ connId: conn?.id,
814
+ gatewayId: idToStr(gatewayIdBuf),
815
+ requestId: idToStr(requestIdBuf),
816
+ websocketType: websocket?.constructor?.name,
817
+ hasMessageListener: !!websocket.addEventListener,
818
+ });
819
+ }
820
+ }
821
+
822
+ // MARK: - Hibernating WebSockets
823
+ #hwsCanHibernate(
824
+ actorId: string,
825
+ gatewayId: ArrayBuffer,
826
+ requestId: ArrayBuffer,
827
+ request: Request,
828
+ ): boolean {
829
+ const url = new URL(request.url);
830
+ const path = url.pathname;
831
+
832
+ // Get actor instance from runner to access actor name
833
+ const actorInstance = this.#runner.getActor(actorId);
834
+ if (!actorInstance) {
835
+ logger().warn({
836
+ msg: "actor not found in #hwsCanHibernate",
837
+ actorId,
838
+ });
839
+ return false;
840
+ }
841
+
842
+ // Load actor handler to access persisted data
843
+ const handler = this.#actors.get(actorId);
844
+ if (!handler) {
845
+ logger().warn({
846
+ msg: "actor handler not found in #hwsCanHibernate",
847
+ actorId,
848
+ });
849
+ return false;
850
+ }
851
+ if (!handler.actor) {
852
+ logger().warn({
853
+ msg: "actor not found in #hwsCanHibernate",
854
+ actorId,
855
+ });
856
+ return false;
857
+ }
858
+
859
+ // Determine configuration for new WS
860
+ logger().debug({
861
+ msg: "no existing hibernatable websocket found",
862
+ gatewayId: idToStr(gatewayId),
863
+ requestId: idToStr(requestId),
864
+ });
865
+ if (path === PATH_CONNECT) {
866
+ return true;
867
+ } else if (
868
+ path === PATH_WEBSOCKET_BASE ||
869
+ path.startsWith(PATH_WEBSOCKET_PREFIX)
870
+ ) {
871
+ // Find actor config
872
+ const definition = lookupInRegistry(
873
+ this.#config,
874
+ actorInstance.config.name,
875
+ );
876
+
877
+ // Check if can hibernate
878
+ const canHibernateWebSocket =
879
+ definition.config.options?.canHibernateWebSocket;
880
+ if (canHibernateWebSocket === true) {
881
+ return true;
882
+ } else if (typeof canHibernateWebSocket === "function") {
883
+ try {
884
+ // Truncate the path to match the behavior on onRawWebSocket
885
+ const newPath = truncateRawWebSocketPathPrefix(
886
+ url.pathname,
887
+ );
888
+ const truncatedRequest = new Request(
889
+ `http://actor${newPath}`,
890
+ request,
891
+ );
892
+
893
+ const canHibernate =
894
+ canHibernateWebSocket(truncatedRequest);
895
+ return canHibernate;
896
+ } catch (error) {
897
+ logger().error({
898
+ msg: "error calling canHibernateWebSocket",
899
+ error,
900
+ });
901
+ return false;
902
+ }
903
+ } else {
904
+ return false;
905
+ }
906
+ } else if (path === PATH_INSPECTOR_CONNECT) {
907
+ return false;
908
+ } else {
909
+ logger().warn({
910
+ msg: "unexpected path for getActorHibernationConfig",
911
+ path,
912
+ });
913
+ return false;
914
+ }
915
+ }
916
+
917
+ async #hwsLoadAll(
918
+ actorId: string,
919
+ ): Promise<HibernatingWebSocketMetadata[]> {
920
+ const actor = await this.loadActor(actorId);
921
+ return actor.conns
922
+ .values()
923
+ .map((conn) => {
924
+ const connStateManager = conn[CONN_STATE_MANAGER_SYMBOL];
925
+ const hibernatable = connStateManager.hibernatableData;
926
+ if (!hibernatable) return undefined;
927
+ return {
928
+ gatewayId: hibernatable.gatewayId,
929
+ requestId: hibernatable.requestId,
930
+ serverMessageIndex: hibernatable.serverMessageIndex,
931
+ clientMessageIndex: hibernatable.clientMessageIndex,
932
+ path: hibernatable.requestPath,
933
+ headers: hibernatable.requestHeaders,
934
+ } satisfies HibernatingWebSocketMetadata;
935
+ })
936
+ .filter((x) => x !== undefined)
937
+ .toArray();
938
+ }
939
+
940
+ async onBeforeActorStart(actor: AnyActorInstance): Promise<void> {
941
+ // Resolve promise if waiting
942
+ const handler = this.#actors.get(actor.id);
943
+ invariant(handler, "missing actor handler in onBeforeActorReady");
944
+ handler.actorStartError = undefined;
945
+ handler.actorStartPromise?.resolve();
946
+ handler.actorStartPromise = undefined;
947
+
948
+ // Restore hibernating requests
949
+ const metaEntries = await this.#hwsLoadAll(actor.id);
950
+ await this.#runner.restoreHibernatingRequests(actor.id, metaEntries);
951
+ }
952
+
953
+ onCreateConn(conn: AnyConn) {
954
+ const hibernatable = conn[CONN_STATE_MANAGER_SYMBOL].hibernatableData;
955
+ if (!hibernatable) return;
956
+
957
+ this.#hwsMessageIndex.set(conn.id, {
958
+ serverMessageIndex: hibernatable.serverMessageIndex,
959
+ bufferedMessageSize: 0,
960
+ pendingAckFromMessageIndex: false,
961
+ pendingAckFromBufferSize: false,
962
+ });
963
+
964
+ logger().debug({
965
+ msg: "created #hwsMessageIndex entry",
966
+ connId: conn.id,
967
+ serverMessageIndex: hibernatable.serverMessageIndex,
968
+ });
969
+ }
970
+
971
+ onDestroyConn(conn: AnyConn) {
972
+ this.#hwsMessageIndex.delete(conn.id);
973
+
974
+ logger().debug({
975
+ msg: "removed #hwsMessageIndex entry",
976
+ connId: conn.id,
977
+ });
978
+ }
979
+
980
+ onBeforePersistConn(conn: AnyConn) {
981
+ const stateManager = conn[CONN_STATE_MANAGER_SYMBOL];
982
+ const hibernatable = stateManager.hibernatableDataOrError();
983
+
984
+ const entry = this.#hwsMessageIndex.get(conn.id);
985
+ if (!entry) {
986
+ logger().warn({
987
+ msg: "missing EngineActorDriver.#hwsMessageIndex entry for conn",
988
+ connId: conn.id,
989
+ });
990
+ return;
991
+ }
992
+
993
+ // There is a newer message index
994
+ entry.pendingAckFromMessageIndex =
995
+ hibernatable.serverMessageIndex > entry.serverMessageIndex;
996
+ entry.serverMessageIndex = hibernatable.serverMessageIndex;
997
+ }
998
+
999
+ onAfterPersistConn(conn: AnyConn) {
1000
+ const stateManager = conn[CONN_STATE_MANAGER_SYMBOL];
1001
+ const hibernatable = stateManager.hibernatableDataOrError();
1002
+
1003
+ const entry = this.#hwsMessageIndex.get(conn.id);
1004
+ if (!entry) {
1005
+ logger().warn({
1006
+ msg: "missing EngineActorDriver.#hwsMessageIndex entry for conn",
1007
+ connId: conn.id,
1008
+ });
1009
+ return;
1010
+ }
1011
+
1012
+ // Ack entry
1013
+ if (
1014
+ entry.pendingAckFromMessageIndex ||
1015
+ entry.pendingAckFromBufferSize
1016
+ ) {
1017
+ this.#runner.sendHibernatableWebSocketMessageAck(
1018
+ hibernatable.gatewayId,
1019
+ hibernatable.requestId,
1020
+ entry.serverMessageIndex,
1021
+ );
1022
+ entry.pendingAckFromMessageIndex = false;
1023
+ entry.pendingAckFromBufferSize = false;
1024
+ entry.bufferedMessageSize = 0;
1025
+ }
1026
+ }
1027
+ }