rivetkit 2.0.2 → 2.0.4

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 (246) hide show
  1. package/README.md +3 -5
  2. package/dist/schemas/actor-persist/v1.ts +225 -0
  3. package/dist/schemas/client-protocol/v1.ts +435 -0
  4. package/dist/schemas/file-system-driver/v1.ts +102 -0
  5. package/dist/tsup/actor/errors.cjs +77 -0
  6. package/dist/tsup/actor/errors.cjs.map +1 -0
  7. package/dist/tsup/actor/errors.d.cts +156 -0
  8. package/dist/tsup/actor/errors.d.ts +156 -0
  9. package/dist/tsup/actor/errors.js +77 -0
  10. package/dist/tsup/actor/errors.js.map +1 -0
  11. package/dist/tsup/chunk-3F2YSRJL.js +117 -0
  12. package/dist/tsup/chunk-3F2YSRJL.js.map +1 -0
  13. package/dist/tsup/chunk-4CXBCT26.cjs +250 -0
  14. package/dist/tsup/chunk-4CXBCT26.cjs.map +1 -0
  15. package/dist/tsup/chunk-4R73YDN3.cjs +20 -0
  16. package/dist/tsup/chunk-4R73YDN3.cjs.map +1 -0
  17. package/dist/tsup/chunk-6LJT3QRL.cjs +539 -0
  18. package/dist/tsup/chunk-6LJT3QRL.cjs.map +1 -0
  19. package/dist/tsup/chunk-GICQ3YCU.cjs +1792 -0
  20. package/dist/tsup/chunk-GICQ3YCU.cjs.map +1 -0
  21. package/dist/tsup/chunk-H26RP6GD.js +251 -0
  22. package/dist/tsup/chunk-H26RP6GD.js.map +1 -0
  23. package/dist/tsup/chunk-HI3HWJRC.js +20 -0
  24. package/dist/tsup/chunk-HI3HWJRC.js.map +1 -0
  25. package/dist/tsup/chunk-HLLF4B4Q.js +1792 -0
  26. package/dist/tsup/chunk-HLLF4B4Q.js.map +1 -0
  27. package/dist/tsup/chunk-IH6CKNDW.cjs +117 -0
  28. package/dist/tsup/chunk-IH6CKNDW.cjs.map +1 -0
  29. package/dist/tsup/chunk-LV2S3OU3.js +250 -0
  30. package/dist/tsup/chunk-LV2S3OU3.js.map +1 -0
  31. package/dist/tsup/chunk-LWNKVZG5.cjs +251 -0
  32. package/dist/tsup/chunk-LWNKVZG5.cjs.map +1 -0
  33. package/dist/tsup/chunk-NFU2BBT5.js +374 -0
  34. package/dist/tsup/chunk-NFU2BBT5.js.map +1 -0
  35. package/dist/tsup/chunk-PQY7KKTL.js +539 -0
  36. package/dist/tsup/chunk-PQY7KKTL.js.map +1 -0
  37. package/dist/tsup/chunk-QK72M5JB.js +45 -0
  38. package/dist/tsup/chunk-QK72M5JB.js.map +1 -0
  39. package/dist/tsup/chunk-QNNXFOQV.cjs +45 -0
  40. package/dist/tsup/chunk-QNNXFOQV.cjs.map +1 -0
  41. package/dist/tsup/chunk-SBHHJ6QS.cjs +374 -0
  42. package/dist/tsup/chunk-SBHHJ6QS.cjs.map +1 -0
  43. package/dist/tsup/chunk-TQ62L3X7.js +325 -0
  44. package/dist/tsup/chunk-TQ62L3X7.js.map +1 -0
  45. package/dist/tsup/chunk-VO7ZRVVD.cjs +6293 -0
  46. package/dist/tsup/chunk-VO7ZRVVD.cjs.map +1 -0
  47. package/dist/tsup/chunk-WHBPJNGW.cjs +325 -0
  48. package/dist/tsup/chunk-WHBPJNGW.cjs.map +1 -0
  49. package/dist/tsup/chunk-XJQHKJ4P.js +6293 -0
  50. package/dist/tsup/chunk-XJQHKJ4P.js.map +1 -0
  51. package/dist/tsup/client/mod.cjs +32 -0
  52. package/dist/tsup/client/mod.cjs.map +1 -0
  53. package/dist/tsup/client/mod.d.cts +20 -0
  54. package/dist/tsup/client/mod.d.ts +20 -0
  55. package/dist/tsup/client/mod.js +32 -0
  56. package/dist/tsup/client/mod.js.map +1 -0
  57. package/dist/tsup/common/log.cjs +21 -0
  58. package/dist/tsup/common/log.cjs.map +1 -0
  59. package/dist/tsup/common/log.d.cts +26 -0
  60. package/dist/tsup/common/log.d.ts +26 -0
  61. package/dist/tsup/common/log.js +21 -0
  62. package/dist/tsup/common/log.js.map +1 -0
  63. package/dist/tsup/common/websocket.cjs +10 -0
  64. package/dist/tsup/common/websocket.cjs.map +1 -0
  65. package/dist/tsup/common/websocket.d.cts +3 -0
  66. package/dist/tsup/common/websocket.d.ts +3 -0
  67. package/dist/tsup/common/websocket.js +10 -0
  68. package/dist/tsup/common/websocket.js.map +1 -0
  69. package/dist/tsup/common-CXCe7s6i.d.cts +218 -0
  70. package/dist/tsup/common-CXCe7s6i.d.ts +218 -0
  71. package/dist/tsup/connection-BI-6UIBJ.d.ts +2087 -0
  72. package/dist/tsup/connection-Dyd4NLGW.d.cts +2087 -0
  73. package/dist/tsup/driver-helpers/mod.cjs +30 -0
  74. package/dist/tsup/driver-helpers/mod.cjs.map +1 -0
  75. package/dist/tsup/driver-helpers/mod.d.cts +17 -0
  76. package/dist/tsup/driver-helpers/mod.d.ts +17 -0
  77. package/dist/tsup/driver-helpers/mod.js +30 -0
  78. package/dist/tsup/driver-helpers/mod.js.map +1 -0
  79. package/dist/tsup/driver-test-suite/mod.cjs +3411 -0
  80. package/dist/tsup/driver-test-suite/mod.cjs.map +1 -0
  81. package/dist/tsup/driver-test-suite/mod.d.cts +63 -0
  82. package/dist/tsup/driver-test-suite/mod.d.ts +63 -0
  83. package/dist/tsup/driver-test-suite/mod.js +3411 -0
  84. package/dist/tsup/driver-test-suite/mod.js.map +1 -0
  85. package/dist/tsup/inspector/mod.cjs +51 -0
  86. package/dist/tsup/inspector/mod.cjs.map +1 -0
  87. package/dist/tsup/inspector/mod.d.cts +408 -0
  88. package/dist/tsup/inspector/mod.d.ts +408 -0
  89. package/dist/tsup/inspector/mod.js +51 -0
  90. package/dist/tsup/inspector/mod.js.map +1 -0
  91. package/dist/tsup/mod.cjs +67 -0
  92. package/dist/tsup/mod.cjs.map +1 -0
  93. package/dist/tsup/mod.d.cts +105 -0
  94. package/dist/tsup/mod.d.ts +105 -0
  95. package/dist/tsup/mod.js +67 -0
  96. package/dist/tsup/mod.js.map +1 -0
  97. package/dist/tsup/router-endpoints-BTe_Rsdn.d.cts +65 -0
  98. package/dist/tsup/router-endpoints-CBSrKHmo.d.ts +65 -0
  99. package/dist/tsup/test/mod.cjs +17 -0
  100. package/dist/tsup/test/mod.cjs.map +1 -0
  101. package/dist/tsup/test/mod.d.cts +26 -0
  102. package/dist/tsup/test/mod.d.ts +26 -0
  103. package/dist/tsup/test/mod.js +17 -0
  104. package/dist/tsup/test/mod.js.map +1 -0
  105. package/dist/tsup/utils-fwx3o3K9.d.cts +18 -0
  106. package/dist/tsup/utils-fwx3o3K9.d.ts +18 -0
  107. package/dist/tsup/utils.cjs +26 -0
  108. package/dist/tsup/utils.cjs.map +1 -0
  109. package/dist/tsup/utils.d.cts +36 -0
  110. package/dist/tsup/utils.d.ts +36 -0
  111. package/dist/tsup/utils.js +26 -0
  112. package/dist/tsup/utils.js.map +1 -0
  113. package/package.json +208 -5
  114. package/src/actor/action.ts +178 -0
  115. package/src/actor/config.ts +497 -0
  116. package/src/actor/connection.ts +257 -0
  117. package/src/actor/context.ts +168 -0
  118. package/src/actor/database.ts +23 -0
  119. package/src/actor/definition.ts +82 -0
  120. package/src/actor/driver.ts +84 -0
  121. package/src/actor/errors.ts +422 -0
  122. package/src/actor/generic-conn-driver.ts +246 -0
  123. package/src/actor/instance.ts +1844 -0
  124. package/src/actor/keys.test.ts +266 -0
  125. package/src/actor/keys.ts +89 -0
  126. package/src/actor/log.ts +6 -0
  127. package/src/actor/mod.ts +108 -0
  128. package/src/actor/persisted.ts +42 -0
  129. package/src/actor/protocol/old.ts +297 -0
  130. package/src/actor/protocol/serde.ts +131 -0
  131. package/src/actor/router-endpoints.ts +688 -0
  132. package/src/actor/router.ts +265 -0
  133. package/src/actor/schedule.ts +17 -0
  134. package/src/actor/unstable-react.ts +110 -0
  135. package/src/actor/utils.ts +102 -0
  136. package/src/client/actor-common.ts +30 -0
  137. package/src/client/actor-conn.ts +865 -0
  138. package/src/client/actor-handle.ts +268 -0
  139. package/src/client/actor-query.ts +65 -0
  140. package/src/client/client.ts +554 -0
  141. package/src/client/config.ts +44 -0
  142. package/src/client/errors.ts +42 -0
  143. package/src/client/log.ts +5 -0
  144. package/src/client/mod.ts +60 -0
  145. package/src/client/raw-utils.ts +149 -0
  146. package/src/client/test.ts +44 -0
  147. package/src/client/utils.ts +152 -0
  148. package/src/common/eventsource-interface.ts +47 -0
  149. package/src/common/eventsource.ts +80 -0
  150. package/src/common/fake-event-source.ts +267 -0
  151. package/src/common/inline-websocket-adapter2.ts +454 -0
  152. package/src/common/log-levels.ts +27 -0
  153. package/src/common/log.ts +214 -0
  154. package/src/common/logfmt.ts +219 -0
  155. package/src/common/network.ts +2 -0
  156. package/src/common/router.ts +80 -0
  157. package/src/common/utils.ts +336 -0
  158. package/src/common/versioned-data.ts +95 -0
  159. package/src/common/websocket-interface.ts +49 -0
  160. package/src/common/websocket.ts +42 -0
  161. package/src/driver-helpers/mod.ts +22 -0
  162. package/src/driver-helpers/utils.ts +17 -0
  163. package/src/driver-test-suite/log.ts +5 -0
  164. package/src/driver-test-suite/mod.ts +239 -0
  165. package/src/driver-test-suite/tests/action-features.ts +136 -0
  166. package/src/driver-test-suite/tests/actor-conn-state.ts +249 -0
  167. package/src/driver-test-suite/tests/actor-conn.ts +349 -0
  168. package/src/driver-test-suite/tests/actor-driver.ts +25 -0
  169. package/src/driver-test-suite/tests/actor-error-handling.ts +158 -0
  170. package/src/driver-test-suite/tests/actor-handle.ts +292 -0
  171. package/src/driver-test-suite/tests/actor-inline-client.ts +152 -0
  172. package/src/driver-test-suite/tests/actor-inspector.ts +570 -0
  173. package/src/driver-test-suite/tests/actor-metadata.ts +116 -0
  174. package/src/driver-test-suite/tests/actor-onstatechange.ts +95 -0
  175. package/src/driver-test-suite/tests/actor-schedule.ts +108 -0
  176. package/src/driver-test-suite/tests/actor-sleep.ts +413 -0
  177. package/src/driver-test-suite/tests/actor-state.ts +54 -0
  178. package/src/driver-test-suite/tests/actor-vars.ts +93 -0
  179. package/src/driver-test-suite/tests/manager-driver.ts +367 -0
  180. package/src/driver-test-suite/tests/raw-http-direct-registry.ts +227 -0
  181. package/src/driver-test-suite/tests/raw-http-request-properties.ts +414 -0
  182. package/src/driver-test-suite/tests/raw-http.ts +347 -0
  183. package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +393 -0
  184. package/src/driver-test-suite/tests/raw-websocket.ts +484 -0
  185. package/src/driver-test-suite/tests/request-access.ts +230 -0
  186. package/src/driver-test-suite/utils.ts +71 -0
  187. package/src/drivers/default.ts +34 -0
  188. package/src/drivers/engine/actor-driver.ts +369 -0
  189. package/src/drivers/engine/config.ts +31 -0
  190. package/src/drivers/engine/kv.ts +3 -0
  191. package/src/drivers/engine/log.ts +5 -0
  192. package/src/drivers/engine/mod.ts +35 -0
  193. package/src/drivers/file-system/actor.ts +91 -0
  194. package/src/drivers/file-system/global-state.ts +686 -0
  195. package/src/drivers/file-system/log.ts +5 -0
  196. package/src/drivers/file-system/manager.ts +329 -0
  197. package/src/drivers/file-system/mod.ts +48 -0
  198. package/src/drivers/file-system/utils.ts +109 -0
  199. package/src/globals.d.ts +6 -0
  200. package/src/inspector/actor.ts +298 -0
  201. package/src/inspector/config.ts +88 -0
  202. package/src/inspector/log.ts +5 -0
  203. package/src/inspector/manager.ts +86 -0
  204. package/src/inspector/mod.ts +2 -0
  205. package/src/inspector/protocol/actor.ts +10 -0
  206. package/src/inspector/protocol/common.ts +196 -0
  207. package/src/inspector/protocol/manager.ts +10 -0
  208. package/src/inspector/protocol/mod.ts +2 -0
  209. package/src/inspector/utils.ts +76 -0
  210. package/src/manager/driver.ts +88 -0
  211. package/src/manager/hono-websocket-adapter.ts +342 -0
  212. package/src/manager/log.ts +5 -0
  213. package/src/manager/mod.ts +2 -0
  214. package/src/manager/protocol/mod.ts +24 -0
  215. package/src/manager/protocol/query.ts +89 -0
  216. package/src/manager/router.ts +412 -0
  217. package/src/manager-api/routes/actors-create.ts +16 -0
  218. package/src/manager-api/routes/actors-delete.ts +4 -0
  219. package/src/manager-api/routes/actors-get-by-id.ts +7 -0
  220. package/src/manager-api/routes/actors-get-or-create-by-id.ts +29 -0
  221. package/src/manager-api/routes/actors-get.ts +7 -0
  222. package/src/manager-api/routes/common.ts +18 -0
  223. package/src/mod.ts +18 -0
  224. package/src/registry/config.ts +32 -0
  225. package/src/registry/log.ts +5 -0
  226. package/src/registry/mod.ts +157 -0
  227. package/src/registry/run-config.ts +52 -0
  228. package/src/registry/serve.ts +52 -0
  229. package/src/remote-manager-driver/actor-http-client.ts +72 -0
  230. package/src/remote-manager-driver/actor-websocket-client.ts +63 -0
  231. package/src/remote-manager-driver/api-endpoints.ts +79 -0
  232. package/src/remote-manager-driver/api-utils.ts +43 -0
  233. package/src/remote-manager-driver/log.ts +5 -0
  234. package/src/remote-manager-driver/mod.ts +274 -0
  235. package/src/remote-manager-driver/ws-proxy.ts +180 -0
  236. package/src/schemas/actor-persist/mod.ts +1 -0
  237. package/src/schemas/actor-persist/versioned.ts +25 -0
  238. package/src/schemas/client-protocol/mod.ts +1 -0
  239. package/src/schemas/client-protocol/versioned.ts +63 -0
  240. package/src/schemas/file-system-driver/mod.ts +1 -0
  241. package/src/schemas/file-system-driver/versioned.ts +28 -0
  242. package/src/serde.ts +90 -0
  243. package/src/test/config.ts +16 -0
  244. package/src/test/log.ts +5 -0
  245. package/src/test/mod.ts +154 -0
  246. package/src/utils.ts +172 -0
@@ -0,0 +1,230 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import type { DriverTestConfig } from "../mod";
3
+ import { setupDriverTest } from "../utils";
4
+
5
+ export function runRequestAccessTests(driverTestConfig: DriverTestConfig) {
6
+ describe("Request Access in Lifecycle Hooks", () => {
7
+ test("should have access to request object in onBeforeConnect and createConnState", async (c) => {
8
+ const { client } = await setupDriverTest(c, driverTestConfig);
9
+
10
+ // Create actor with request tracking enabled
11
+ const handle = client.requestAccessActor.getOrCreate(["test-request"], {
12
+ params: { trackRequest: true },
13
+ });
14
+ const connection = handle.connect();
15
+
16
+ // Get request info that was captured in onBeforeConnect
17
+ const requestInfo = await connection.getRequestInfo();
18
+
19
+ // Verify request was accessible in HTTP mode, but not in inline mode
20
+ if (driverTestConfig.clientType === "http") {
21
+ // Check onBeforeConnect
22
+ expect(requestInfo.onBeforeConnect.hasRequest).toBe(true);
23
+ expect(requestInfo.onBeforeConnect.requestUrl).toBeDefined();
24
+ expect(requestInfo.onBeforeConnect.requestMethod).toBeDefined();
25
+ expect(requestInfo.onBeforeConnect.requestHeaders).toBeDefined();
26
+
27
+ // Check createConnState
28
+ expect(requestInfo.createConnState.hasRequest).toBe(true);
29
+ expect(requestInfo.createConnState.requestUrl).toBeDefined();
30
+ expect(requestInfo.createConnState.requestMethod).toBeDefined();
31
+ expect(requestInfo.createConnState.requestHeaders).toBeDefined();
32
+ } else {
33
+ // Inline client may or may not have request object depending on the driver
34
+ //
35
+ // e.g.
36
+ // - File system does not have a request for inline requests
37
+ // - Rivet Engine proxies the request so it has access to the request object
38
+ }
39
+
40
+ // Clean up
41
+ await connection.dispose();
42
+ });
43
+
44
+ test("should not have request when trackRequest is false", async (c) => {
45
+ const { client } = await setupDriverTest(c, driverTestConfig);
46
+
47
+ // Create actor without request tracking
48
+ const handle = client.requestAccessActor.getOrCreate(
49
+ ["test-no-request"],
50
+ {
51
+ params: { trackRequest: false },
52
+ },
53
+ );
54
+ const connection = handle.connect();
55
+
56
+ // Get request info
57
+ const requestInfo = await connection.getRequestInfo();
58
+
59
+ // Verify request was not tracked
60
+ expect(requestInfo.onBeforeConnect.hasRequest).toBe(false);
61
+ expect(requestInfo.onBeforeConnect.requestUrl).toBeNull();
62
+ expect(requestInfo.onBeforeConnect.requestMethod).toBeNull();
63
+ expect(
64
+ Object.keys(requestInfo.onBeforeConnect.requestHeaders),
65
+ ).toHaveLength(0);
66
+
67
+ expect(requestInfo.createConnState.hasRequest).toBe(false);
68
+ expect(requestInfo.createConnState.requestUrl).toBeNull();
69
+ expect(requestInfo.createConnState.requestMethod).toBeNull();
70
+ expect(
71
+ Object.keys(requestInfo.createConnState.requestHeaders),
72
+ ).toHaveLength(0);
73
+
74
+ // Clean up
75
+ await connection.dispose();
76
+ });
77
+
78
+ test("should capture request headers and method", async (c) => {
79
+ const { client } = await setupDriverTest(c, driverTestConfig);
80
+
81
+ // Create actor and connect with request tracking
82
+ const handle = client.requestAccessActor.getOrCreate(["test-headers"], {
83
+ params: { trackRequest: true },
84
+ });
85
+ const connection = handle.connect();
86
+
87
+ // Get request info
88
+ const requestInfo = await connection.getRequestInfo();
89
+
90
+ if (driverTestConfig.clientType === "http") {
91
+ // Verify request details were captured in both hooks
92
+ expect(requestInfo.onBeforeConnect.hasRequest).toBe(true);
93
+ expect(requestInfo.onBeforeConnect.requestMethod).toBeTruthy();
94
+ expect(requestInfo.onBeforeConnect.requestUrl).toBeTruthy();
95
+ expect(requestInfo.onBeforeConnect.requestHeaders).toBeTruthy();
96
+ expect(typeof requestInfo.onBeforeConnect.requestHeaders).toBe(
97
+ "object",
98
+ );
99
+
100
+ expect(requestInfo.createConnState.hasRequest).toBe(true);
101
+ expect(requestInfo.createConnState.requestMethod).toBeTruthy();
102
+ expect(requestInfo.createConnState.requestUrl).toBeTruthy();
103
+ expect(requestInfo.createConnState.requestHeaders).toBeTruthy();
104
+ expect(typeof requestInfo.createConnState.requestHeaders).toBe(
105
+ "object",
106
+ );
107
+ } else {
108
+ // Inline client may or may not have request object depending on the driver
109
+ //
110
+ // See "should have access to request object in onBeforeConnect and createConnState"
111
+ }
112
+
113
+ // Clean up
114
+ await connection.dispose();
115
+ });
116
+
117
+ // TODO: re-expose this once we can have actor queries on the gateway
118
+ // test("should have access to request object in onFetch", async (c) => {
119
+ // const { client, endpoint } = await setupDriverTest(c, driverTestConfig);
120
+ //
121
+ // // Create actor
122
+ // const handle = client.requestAccessActor.getOrCreate(["test-fetch"]);
123
+ //
124
+ // // Make a raw HTTP request to the actor
125
+ // await handle.resolve(); // Ensure actor is created
126
+ //
127
+ // const actorQuery = {
128
+ // getOrCreateForKey: {
129
+ // name: "requestAccessActor",
130
+ // key: ["test-fetch"],
131
+ // },
132
+ // };
133
+ //
134
+ // const url = `${endpoint}/registry/actors/raw/http/test-path`;
135
+ // const response = await fetch(url, {
136
+ // method: "POST",
137
+ // headers: {
138
+ // "Content-Type": "application/json",
139
+ // "X-Test-Header": "test-value",
140
+ // "X-RivetKit-Query": JSON.stringify(actorQuery),
141
+ // },
142
+ // body: JSON.stringify({ test: "data" }),
143
+ // });
144
+ //
145
+ // if (!response.ok) {
146
+ // const errorText = await response.text();
147
+ // console.error(
148
+ // `HTTP request failed: ${response.status} ${response.statusText}`,
149
+ // errorText,
150
+ // );
151
+ // }
152
+ //
153
+ // expect(response.ok).toBe(true);
154
+ // const data = await response.json();
155
+ //
156
+ // // Verify request info from onFetch
157
+ // expect((data as any).hasRequest).toBe(true);
158
+ // expect((data as any).requestUrl).toContain("/test-path");
159
+ // expect((data as any).requestMethod).toBe("POST");
160
+ // expect((data as any).requestHeaders).toBeDefined();
161
+ // expect((data as any).requestHeaders["content-type"]).toBe(
162
+ // "application/json",
163
+ // );
164
+ // expect((data as any).requestHeaders["x-test-header"]).toBe("test-value");
165
+ // });
166
+
167
+ // test("should have access to request object in onWebSocket", async (c) => {
168
+ // const { client, endpoint } = await setupDriverTest(c, driverTestConfig);
169
+ //
170
+ // // Only test in environments that support WebSocket
171
+ // if (typeof WebSocket !== "undefined") {
172
+ // // Create actor
173
+ // const handle = client.requestAccessActor.getOrCreate([
174
+ // "test-websocket",
175
+ // ]);
176
+ // await handle.resolve(); // Ensure actor is created
177
+ //
178
+ // const actorQuery = {
179
+ // getOrCreateForKey: {
180
+ // name: "requestAccessActor",
181
+ // key: ["test-websocket"],
182
+ // },
183
+ // };
184
+ //
185
+ // // Encode query as WebSocket subprotocol
186
+ // const queryProtocol = `query.${encodeURIComponent(JSON.stringify(actorQuery))}`;
187
+ //
188
+ // // Create raw WebSocket connection
189
+ // const wsUrl = endpoint
190
+ // .replace("http://", "ws://")
191
+ // .replace("https://", "wss://");
192
+ // const ws = new WebSocket(
193
+ // `${wsUrl}/registry/actors/raw/websocket/test-path`,
194
+ // [
195
+ // queryProtocol,
196
+ // "rivetkit", // Required protocol
197
+ // ],
198
+ // );
199
+ //
200
+ // // Wait for connection and first message
201
+ // await new Promise<void>((resolve, reject) => {
202
+ // ws.onopen = () => {
203
+ // // Connection established
204
+ // };
205
+ //
206
+ // ws.onmessage = (event) => {
207
+ // try {
208
+ // const data = JSON.parse(event.data);
209
+ //
210
+ // // Verify request info from onWebSocket
211
+ // expect(data.hasRequest).toBe(true);
212
+ // expect(data.requestUrl).toContain("/test-path");
213
+ // expect(data.requestMethod).toBe("GET");
214
+ // expect(data.requestHeaders).toBeDefined();
215
+ //
216
+ // ws.close();
217
+ // resolve();
218
+ // } catch (error) {
219
+ // reject(error);
220
+ // }
221
+ // };
222
+ //
223
+ // ws.onerror = (error) => {
224
+ // reject(error);
225
+ // };
226
+ // });
227
+ // }
228
+ // });
229
+ });
230
+ }
@@ -0,0 +1,71 @@
1
+ import { resolve } from "node:path";
2
+ import { type TestContext, vi } from "vitest";
3
+ import { assertUnreachable } from "@/actor/utils";
4
+ import { type Client, createClient } from "@/client/mod";
5
+ import type { registry } from "../../fixtures/driver-test-suite/registry";
6
+ import type { DriverTestConfig } from "./mod";
7
+
8
+ export const FAKE_TIME = new Date("2024-01-01T00:00:00.000Z");
9
+
10
+ // Must use `TestContext` since global hooks do not work when running concurrently
11
+ export async function setupDriverTest(
12
+ c: TestContext,
13
+ driverTestConfig: DriverTestConfig,
14
+ ): Promise<{
15
+ client: Client<typeof registry>;
16
+ endpoint: string;
17
+ }> {
18
+ if (!driverTestConfig.useRealTimers) {
19
+ vi.useFakeTimers();
20
+ vi.setSystemTime(FAKE_TIME);
21
+ }
22
+
23
+ // Build drivers
24
+ const projectPath = resolve(__dirname, "../../fixtures/driver-test-suite");
25
+ const { endpoint, namespace, runnerName, cleanup } =
26
+ await driverTestConfig.start(projectPath);
27
+ c.onTestFinished(cleanup);
28
+
29
+ let client: Client<typeof registry>;
30
+ if (driverTestConfig.clientType === "http") {
31
+ // Create client
32
+ client = createClient<typeof registry>({
33
+ endpoint,
34
+ namespace,
35
+ runnerName,
36
+ transport: driverTestConfig.transport,
37
+ });
38
+ } else if (driverTestConfig.clientType === "inline") {
39
+ throw "TODO";
40
+ // // Use inline client from driver
41
+ // const clientDriver = createTestInlineClientDriver(
42
+ // endpoint,
43
+ // driverTestConfig.transport ?? "websocket",
44
+ // );
45
+ // client = createClientWithDriver(clientDriver);
46
+ } else {
47
+ assertUnreachable(driverTestConfig.clientType);
48
+ }
49
+
50
+ // Cleanup client
51
+ if (!driverTestConfig.HACK_skipCleanupNet) {
52
+ c.onTestFinished(async () => await client.dispose());
53
+ }
54
+
55
+ return {
56
+ client,
57
+ endpoint,
58
+ };
59
+ }
60
+
61
+ export async function waitFor(
62
+ driverTestConfig: DriverTestConfig,
63
+ ms: number,
64
+ ): Promise<void> {
65
+ if (driverTestConfig.useRealTimers) {
66
+ return new Promise((resolve) => setTimeout(resolve, ms));
67
+ } else {
68
+ vi.advanceTimersByTime(ms);
69
+ return Promise.resolve();
70
+ }
71
+ }
@@ -0,0 +1,34 @@
1
+ import { UserError } from "@/actor/errors";
2
+ import { loggerWithoutContext } from "@/actor/log";
3
+ import { createEngineDriver } from "@/drivers/engine/mod";
4
+ import { createFileSystemOrMemoryDriver } from "@/drivers/file-system/mod";
5
+ import type { DriverConfig, RunConfig } from "@/registry/run-config";
6
+ import { getEnvUniversal } from "@/utils";
7
+
8
+ /**
9
+ * Chooses the appropriate driver based on the run configuration.
10
+ */
11
+ export function chooseDefaultDriver(runConfig: RunConfig): DriverConfig {
12
+ const engineEndpoint = runConfig.endpoint ?? getEnvUniversal("RIVET_ENGINE");
13
+
14
+ if (engineEndpoint && runConfig.driver) {
15
+ throw new UserError(
16
+ "Cannot specify both 'engine' and 'driver' in configuration",
17
+ );
18
+ }
19
+
20
+ if (runConfig.driver) {
21
+ return runConfig.driver;
22
+ }
23
+
24
+ if (engineEndpoint) {
25
+ loggerWithoutContext().debug({
26
+ msg: "using rivet engine driver",
27
+ endpoint: engineEndpoint,
28
+ });
29
+ return createEngineDriver({ endpoint: engineEndpoint });
30
+ }
31
+
32
+ loggerWithoutContext().debug({ msg: "using default file system driver" });
33
+ return createFileSystemOrMemoryDriver(true);
34
+ }
@@ -0,0 +1,369 @@
1
+ import type {
2
+ ActorConfig as RunnerActorConfig,
3
+ RunnerConfig,
4
+ } from "@rivetkit/engine-runner";
5
+ import { Runner } from "@rivetkit/engine-runner";
6
+ import * as cbor from "cbor-x";
7
+ import { WSContext } from "hono/ws";
8
+ import invariant from "invariant";
9
+ import { deserializeActorKey } from "@/actor/keys";
10
+ import { EncodingSchema } from "@/actor/protocol/serde";
11
+ import type { Client } from "@/client/client";
12
+ import { getLogger } from "@/common/log";
13
+ import {
14
+ type ActorDriver,
15
+ type AnyActorInstance,
16
+ HEADER_AUTH_DATA,
17
+ HEADER_CONN_PARAMS,
18
+ HEADER_ENCODING,
19
+ type ManagerDriver,
20
+ serializeEmptyPersistData,
21
+ } from "@/driver-helpers/mod";
22
+ import type {
23
+ ActorRouter,
24
+ RegistryConfig,
25
+ RunConfig,
26
+ UniversalWebSocket,
27
+ UpgradeWebSocketArgs,
28
+ } from "@/mod";
29
+ import {
30
+ createActorRouter,
31
+ createGenericConnDrivers,
32
+ GenericConnGlobalState,
33
+ handleRawWebSocketHandler,
34
+ handleWebSocketConnect,
35
+ lookupInRegistry,
36
+ PATH_CONNECT_WEBSOCKET,
37
+ PATH_RAW_WEBSOCKET_PREFIX,
38
+ } from "@/mod";
39
+ import type { Config } from "./config";
40
+ import { KEYS } from "./kv";
41
+ import { logger } from "./log";
42
+
43
+ interface ActorHandler {
44
+ actor?: AnyActorInstance;
45
+ actorStartPromise?: PromiseWithResolvers<void>;
46
+ genericConnGlobalState: GenericConnGlobalState;
47
+ persistedData?: Uint8Array;
48
+ }
49
+
50
+ export type DriverContext = {};
51
+
52
+ export class EngineActorDriver implements ActorDriver {
53
+ #registryConfig: RegistryConfig;
54
+ #runConfig: RunConfig;
55
+ #managerDriver: ManagerDriver;
56
+ #inlineClient: Client<any>;
57
+ #config: Config;
58
+ #runner: Runner;
59
+ #actors: Map<string, ActorHandler> = new Map();
60
+ #actorRouter: ActorRouter;
61
+ #version: number = 1; // Version for the runner protocol
62
+
63
+ constructor(
64
+ registryConfig: RegistryConfig,
65
+ runConfig: RunConfig,
66
+ managerDriver: ManagerDriver,
67
+ inlineClient: Client<any>,
68
+ config: Config,
69
+ ) {
70
+ this.#registryConfig = registryConfig;
71
+ this.#runConfig = runConfig;
72
+ this.#managerDriver = managerDriver;
73
+ this.#inlineClient = inlineClient;
74
+ this.#config = config;
75
+ this.#actorRouter = createActorRouter(runConfig, this);
76
+
77
+ // Create runner configuration
78
+ let hasDisconnected = false;
79
+ const runnerConfig: RunnerConfig = {
80
+ version: this.#version,
81
+ endpoint: config.endpoint,
82
+ pegboardEndpoint: config.pegboardEndpoint,
83
+ namespace: config.namespace,
84
+ totalSlots: config.totalSlots,
85
+ runnerName: config.runnerName,
86
+ runnerKey: config.runnerKey,
87
+ metadata: {
88
+ inspectorToken: this.#runConfig.inspector.token(),
89
+ },
90
+ prepopulateActorNames: Object.fromEntries(
91
+ Object.keys(this.#registryConfig.use).map((name) => [
92
+ name,
93
+ { metadata: {} },
94
+ ]),
95
+ ),
96
+ onConnected: () => {
97
+ if (hasDisconnected) {
98
+ logger().info({
99
+ msg: "runner reconnected",
100
+ namespace: this.#config.namespace,
101
+ runnerName: this.#config.runnerName,
102
+ });
103
+ } else {
104
+ logger().debug({
105
+ msg: "runner connected",
106
+ namespace: this.#config.namespace,
107
+ runnerName: this.#config.runnerName,
108
+ });
109
+ }
110
+ },
111
+ onDisconnected: () => {
112
+ logger().warn({
113
+ msg: "runner disconnected",
114
+ namespace: this.#config.namespace,
115
+ runnerName: this.#config.runnerName,
116
+ });
117
+ hasDisconnected = true;
118
+ },
119
+ onShutdown: () => {},
120
+ fetch: this.#runnerFetch.bind(this),
121
+ websocket: this.#runnerWebSocket.bind(this),
122
+ onActorStart: this.#runnerOnActorStart.bind(this),
123
+ onActorStop: this.#runnerOnActorStop.bind(this),
124
+ logger: getLogger("engine-runner"),
125
+ };
126
+
127
+ // Create and start runner
128
+ this.#runner = new Runner(runnerConfig);
129
+ this.#runner.start();
130
+ logger().debug({
131
+ msg: "engine runner started",
132
+ endpoint: config.endpoint,
133
+ namespace: config.namespace,
134
+ runnerName: config.runnerName,
135
+ });
136
+ }
137
+
138
+ async #loadActorHandler(actorId: string): Promise<ActorHandler> {
139
+ // Check if actor is already loaded
140
+ const handler = this.#actors.get(actorId);
141
+ if (!handler) throw new Error(`Actor handler does not exist ${actorId}`);
142
+ if (handler.actorStartPromise) await handler.actorStartPromise.promise;
143
+ if (!handler.actor) throw new Error("Actor should be loaded");
144
+ return handler;
145
+ }
146
+
147
+ async loadActor(actorId: string): Promise<AnyActorInstance> {
148
+ const handler = await this.#loadActorHandler(actorId);
149
+ if (!handler.actor) throw new Error(`Actor ${actorId} failed to load`);
150
+ return handler.actor;
151
+ }
152
+
153
+ getGenericConnGlobalState(actorId: string): GenericConnGlobalState {
154
+ const handler = this.#actors.get(actorId);
155
+ if (!handler) {
156
+ throw new Error(`Actor ${actorId} not loaded`);
157
+ }
158
+ return handler.genericConnGlobalState;
159
+ }
160
+
161
+ getContext(actorId: string): DriverContext {
162
+ return {};
163
+ }
164
+
165
+ async readPersistedData(actorId: string): Promise<Uint8Array | undefined> {
166
+ const handler = this.#actors.get(actorId);
167
+ if (!handler) throw new Error(`Actor ${actorId} not loaded`);
168
+ if (handler.persistedData) return handler.persistedData;
169
+
170
+ const [value] = await this.#runner.kvGet(actorId, [KEYS.PERSIST_DATA]);
171
+
172
+ if (value !== null) {
173
+ handler.persistedData = value;
174
+ return value;
175
+ } else {
176
+ return undefined;
177
+ }
178
+ }
179
+
180
+ async writePersistedData(actorId: string, data: Uint8Array): Promise<void> {
181
+ const handler = this.#actors.get(actorId);
182
+ if (!handler) throw new Error(`Actor ${actorId} not loaded`);
183
+
184
+ handler.persistedData = data;
185
+
186
+ await this.#runner.kvPut(actorId, [[KEYS.PERSIST_DATA, data]]);
187
+ }
188
+
189
+ async setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {
190
+ // TODO: Set timeout
191
+ // TODO: Use alarm on sleep
192
+ // TODO: Send alarm to runner
193
+ }
194
+
195
+ async getDatabase(_actorId: string): Promise<unknown | undefined> {
196
+ return undefined;
197
+ }
198
+
199
+ // Runner lifecycle callbacks
200
+ async #runnerOnActorStart(
201
+ actorId: string,
202
+ generation: number,
203
+ config: RunnerActorConfig,
204
+ ): Promise<void> {
205
+ logger().debug({
206
+ msg: "runner actor starting",
207
+ actorId,
208
+ name: config.name,
209
+ key: config.key,
210
+ generation,
211
+ });
212
+
213
+ // Deserialize input
214
+ let input: any;
215
+ if (config.input) {
216
+ input = cbor.decode(config.input);
217
+ }
218
+
219
+ // Get or create handler
220
+ let handler = this.#actors.get(actorId);
221
+ if (!handler) {
222
+ handler = {
223
+ genericConnGlobalState: new GenericConnGlobalState(),
224
+ actorStartPromise: Promise.withResolvers(),
225
+ persistedData: serializeEmptyPersistData(input),
226
+ };
227
+ this.#actors.set(actorId, handler);
228
+ }
229
+
230
+ const name = config.name as string;
231
+ invariant(config.key, "actor should have a key");
232
+ const key = deserializeActorKey(config.key);
233
+
234
+ // Create actor instance
235
+ const definition = lookupInRegistry(
236
+ this.#registryConfig,
237
+ config.name as string, // TODO: Remove cast
238
+ );
239
+ handler.actor = definition.instantiate();
240
+
241
+ // Start actor
242
+ const connDrivers = createGenericConnDrivers(
243
+ handler.genericConnGlobalState,
244
+ );
245
+ await handler.actor.start(
246
+ connDrivers,
247
+ this,
248
+ this.#inlineClient,
249
+ actorId,
250
+ name,
251
+ key,
252
+ "unknown", // TODO: Add regions
253
+ );
254
+
255
+ // Resolve promise if waiting
256
+ handler.actorStartPromise?.resolve();
257
+ handler.actorStartPromise = undefined;
258
+
259
+ logger().debug({ msg: "runner actor started", actorId, name, key });
260
+ }
261
+
262
+ async #runnerOnActorStop(actorId: string, generation: number): Promise<void> {
263
+ logger().debug({ msg: "runner actor stopping", actorId, generation });
264
+
265
+ const handler = this.#actors.get(actorId);
266
+ if (handler?.actor) {
267
+ await handler.actor._stop();
268
+ this.#actors.delete(actorId);
269
+ }
270
+
271
+ logger().debug({ msg: "runner actor stopped", actorId });
272
+ }
273
+
274
+ async #runnerFetch(actorId: string, request: Request): Promise<Response> {
275
+ logger().debug({
276
+ msg: "runner fetch",
277
+ actorId,
278
+ url: request.url,
279
+ method: request.method,
280
+ });
281
+ return await this.#actorRouter.fetch(request, { actorId });
282
+ }
283
+
284
+ async #runnerWebSocket(
285
+ actorId: string,
286
+ websocketRaw: any,
287
+ request: Request,
288
+ ): Promise<void> {
289
+ const websocket = websocketRaw as UniversalWebSocket;
290
+
291
+ logger().debug({ msg: "runner websocket", actorId, url: request.url });
292
+
293
+ const url = new URL(request.url);
294
+
295
+ // Parse headers
296
+ const encodingRaw = request.headers.get(HEADER_ENCODING);
297
+ const connParamsRaw = request.headers.get(HEADER_CONN_PARAMS);
298
+ const authDataRaw = request.headers.get(HEADER_AUTH_DATA);
299
+
300
+ const encoding = EncodingSchema.parse(encodingRaw);
301
+ const connParams = connParamsRaw ? JSON.parse(connParamsRaw) : undefined;
302
+ const authData = authDataRaw ? JSON.parse(authDataRaw) : undefined;
303
+
304
+ // Fetch WS handler
305
+ //
306
+ // We store the promise since we need to add WebSocket event listeners immediately that will wait for the promise to resolve
307
+ let wsHandlerPromise: Promise<UpgradeWebSocketArgs>;
308
+ if (url.pathname === PATH_CONNECT_WEBSOCKET) {
309
+ wsHandlerPromise = handleWebSocketConnect(
310
+ request,
311
+ this.#runConfig,
312
+ this,
313
+ actorId,
314
+ encoding,
315
+ connParams,
316
+ authData,
317
+ );
318
+ } else if (url.pathname.startsWith(PATH_RAW_WEBSOCKET_PREFIX)) {
319
+ wsHandlerPromise = handleRawWebSocketHandler(
320
+ request,
321
+ url.pathname + url.search,
322
+ this,
323
+ actorId,
324
+ authData,
325
+ );
326
+ } else {
327
+ throw new Error(`Unreachable path: ${url.pathname}`);
328
+ }
329
+
330
+ // TODO: Add close
331
+
332
+ // Connect the Hono WS hook to the adapter
333
+ const wsContext = new WSContext(websocket);
334
+
335
+ wsHandlerPromise.catch((err) => {
336
+ logger().error({ msg: "building websocket handlers errored", err });
337
+ wsContext.close(1011, `${err}`);
338
+ });
339
+
340
+ if (websocket.readyState === 1) {
341
+ wsHandlerPromise.then((x) => x.onOpen?.(new Event("open"), wsContext));
342
+ } else {
343
+ websocket.addEventListener("open", (event) => {
344
+ wsHandlerPromise.then((x) => x.onOpen?.(event, wsContext));
345
+ });
346
+ }
347
+
348
+ websocket.addEventListener("message", (event) => {
349
+ wsHandlerPromise.then((x) => x.onMessage?.(event, wsContext));
350
+ });
351
+
352
+ websocket.addEventListener("close", (event) => {
353
+ wsHandlerPromise.then((x) => x.onClose?.(event, wsContext));
354
+ });
355
+
356
+ websocket.addEventListener("error", (event) => {
357
+ wsHandlerPromise.then((x) => x.onError?.(event, wsContext));
358
+ });
359
+ }
360
+
361
+ async sleep(actorId: string) {
362
+ this.#runner.sleepActor(actorId);
363
+ }
364
+
365
+ async shutdown(immediate: boolean): Promise<void> {
366
+ logger().info({ msg: "stopping engine actor driver" });
367
+ await this.#runner.shutdown(immediate);
368
+ }
369
+ }