rivetkit 2.0.2 → 2.0.3

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