rivetkit 2.0.1 → 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,391 @@
1
+ import * as cbor from "cbor-x";
2
+ import type { Context as HonoContext } from "hono";
3
+ import invariant from "invariant";
4
+ import { ActorAlreadyExists } from "@/actor/errors";
5
+ import {
6
+ HEADER_AUTH_DATA,
7
+ HEADER_CONN_PARAMS,
8
+ HEADER_ENCODING,
9
+ HEADER_EXPOSE_INTERNAL_ERROR,
10
+ } from "@/actor/router-endpoints";
11
+ import { generateRandomString } from "@/actor/utils";
12
+ import { importWebSocket } from "@/common/websocket";
13
+ import type {
14
+ ActorOutput,
15
+ CreateInput,
16
+ GetForIdInput,
17
+ GetOrCreateWithKeyInput,
18
+ GetWithKeyInput,
19
+ ManagerDriver,
20
+ } from "@/driver-helpers/mod";
21
+ import { type Encoding, noopNext, type RunConfig } from "@/mod";
22
+ import {
23
+ createActor,
24
+ destroyActor,
25
+ getActor,
26
+ getActorById,
27
+ getOrCreateActorById,
28
+ } from "./api-endpoints";
29
+ import { EngineApiError } from "./api-utils";
30
+ import type { Config } from "./config";
31
+ import { deserializeActorKey, serializeActorKey } from "./keys";
32
+ import { logger } from "./log";
33
+ import { createWebSocketProxy } from "./ws-proxy";
34
+
35
+ export class EngineManagerDriver implements ManagerDriver {
36
+ #config: Config;
37
+ #runConfig: RunConfig;
38
+ #importWebSocketPromise: Promise<typeof WebSocket>;
39
+
40
+ constructor(config: Config, runConfig: RunConfig) {
41
+ this.#config = config;
42
+ this.#runConfig = runConfig;
43
+ if (!this.#runConfig.inspector.token()) {
44
+ const token = generateRandomString();
45
+ this.#runConfig.inspector.token = () => token;
46
+ }
47
+ this.#importWebSocketPromise = importWebSocket();
48
+ }
49
+
50
+ async sendRequest(actorId: string, actorRequest: Request): Promise<Response> {
51
+ logger().debug("sending request to actor via guard", {
52
+ actorId,
53
+ method: actorRequest.method,
54
+ url: actorRequest.url,
55
+ });
56
+
57
+ return this.#forwardHttpRequest(actorRequest, actorId);
58
+ }
59
+
60
+ async openWebSocket(
61
+ path: string,
62
+ actorId: string,
63
+ encoding: Encoding,
64
+ params: unknown,
65
+ ): Promise<WebSocket> {
66
+ const WebSocket = await this.#importWebSocketPromise;
67
+
68
+ // WebSocket connections go through guard
69
+ const guardUrl = `${this.#config.endpoint}${path}`;
70
+
71
+ logger().debug("opening websocket to actor via guard", {
72
+ actorId,
73
+ path,
74
+ guardUrl,
75
+ });
76
+
77
+ // Create WebSocket connection
78
+ const ws = new WebSocket(guardUrl, {
79
+ headers: buildGuardHeadersForWebSocket(actorId, encoding, params),
80
+ });
81
+
82
+ logger().debug("websocket connection opened", { actorId });
83
+
84
+ return ws;
85
+ }
86
+
87
+ async proxyRequest(
88
+ _c: HonoContext,
89
+ actorRequest: Request,
90
+ actorId: string,
91
+ ): Promise<Response> {
92
+ logger().debug("forwarding request to actor via guard", {
93
+ actorId,
94
+ method: actorRequest.method,
95
+ url: actorRequest.url,
96
+ hasBody: !!actorRequest.body,
97
+ });
98
+
99
+ return this.#forwardHttpRequest(actorRequest, actorId);
100
+ }
101
+
102
+ async proxyWebSocket(
103
+ c: HonoContext,
104
+ path: string,
105
+ actorId: string,
106
+ encoding: Encoding,
107
+ params: unknown,
108
+ authData: unknown,
109
+ ): Promise<Response> {
110
+ const upgradeWebSocket = this.#runConfig.getUpgradeWebSocket?.();
111
+ invariant(upgradeWebSocket, "missing getUpgradeWebSocket");
112
+
113
+ const guardUrl = `${this.#config.endpoint}${path}`;
114
+ const wsGuardUrl = guardUrl.replace("http://", "ws://");
115
+
116
+ logger().debug("forwarding websocket to actor via guard", {
117
+ actorId,
118
+ path,
119
+ guardUrl,
120
+ });
121
+
122
+ // Build headers
123
+ const headers = buildGuardHeadersForWebSocket(
124
+ actorId,
125
+ encoding,
126
+ params,
127
+ authData,
128
+ );
129
+ const args = await createWebSocketProxy(c, wsGuardUrl, headers);
130
+
131
+ return await upgradeWebSocket(() => args)(c, noopNext());
132
+ }
133
+
134
+ extraStartupLog() {
135
+ return {
136
+ engine: this.#config.endpoint,
137
+ namespace: this.#config.namespace,
138
+ runner: this.#config.runnerName,
139
+ address: Object.values(this.#config.addresses)
140
+ .map((v) => `${v.host}:${v.port}`)
141
+ .join(", "),
142
+ };
143
+ }
144
+
145
+ async getForId({
146
+ c,
147
+ name,
148
+ actorId,
149
+ }: GetForIdInput): Promise<ActorOutput | undefined> {
150
+ // Fetch from API if not in cache
151
+ try {
152
+ const response = await getActor(this.#config, actorId);
153
+
154
+ // Validate name matches
155
+ if (response.actor.name !== name) {
156
+ logger().debug("actor name mismatch from api", {
157
+ actorId,
158
+ apiName: response.actor.name,
159
+ requestedName: name,
160
+ });
161
+ return undefined;
162
+ }
163
+
164
+ const keyRaw = response.actor.key;
165
+ invariant(keyRaw, `actor ${actorId} should have key`);
166
+ const key = deserializeActorKey(keyRaw);
167
+
168
+ return {
169
+ actorId,
170
+ name,
171
+ key,
172
+ };
173
+ } catch (error) {
174
+ if (
175
+ error instanceof EngineApiError &&
176
+ (error as EngineApiError).group === "actor" &&
177
+ (error as EngineApiError).code === "not_found"
178
+ ) {
179
+ return undefined;
180
+ }
181
+ throw error;
182
+ }
183
+ }
184
+
185
+ async getWithKey({
186
+ c,
187
+ name,
188
+ key,
189
+ }: GetWithKeyInput): Promise<ActorOutput | undefined> {
190
+ logger().debug("getWithKey: searching for actor", { name, key });
191
+
192
+ // If not in local cache, fetch by key from API
193
+ try {
194
+ const response = await getActorById(this.#config, name, key);
195
+
196
+ if (!response.actor_id) {
197
+ return undefined;
198
+ }
199
+
200
+ const actorId = response.actor_id;
201
+
202
+ logger().debug("getWithKey: found actor via api", {
203
+ actorId,
204
+ name,
205
+ key,
206
+ });
207
+
208
+ return {
209
+ actorId,
210
+ name,
211
+ key,
212
+ };
213
+ } catch (error) {
214
+ if (
215
+ error instanceof EngineApiError &&
216
+ (error as EngineApiError).group === "actor" &&
217
+ (error as EngineApiError).code === "not_found"
218
+ ) {
219
+ return undefined;
220
+ }
221
+ throw error;
222
+ }
223
+ }
224
+
225
+ async getOrCreateWithKey(
226
+ input: GetOrCreateWithKeyInput,
227
+ ): Promise<ActorOutput> {
228
+ const { c, name, key, input: actorInput, region } = input;
229
+
230
+ logger().info(
231
+ "getOrCreateWithKey: getting or creating actor via engine api",
232
+ {
233
+ name,
234
+ key,
235
+ },
236
+ );
237
+
238
+ const response = await getOrCreateActorById(this.#config, {
239
+ name,
240
+ key: serializeActorKey(key),
241
+ runner_name_selector: this.#config.runnerName,
242
+ input: input ? cbor.encode(actorInput).toString("base64") : undefined,
243
+ crash_policy: "sleep",
244
+ });
245
+
246
+ const actorId = response.actor_id;
247
+
248
+ logger().info("getOrCreateWithKey: actor ready", {
249
+ actorId,
250
+ name,
251
+ key,
252
+ created: response.created,
253
+ });
254
+
255
+ return {
256
+ actorId,
257
+ name,
258
+ key,
259
+ };
260
+ }
261
+
262
+ async createActor({
263
+ c,
264
+ name,
265
+ key,
266
+ input,
267
+ }: CreateInput): Promise<ActorOutput> {
268
+ // Check if actor with the same name and key already exists
269
+ const existingActor = await this.getWithKey({ c, name, key });
270
+ if (existingActor) {
271
+ throw new ActorAlreadyExists(name, key);
272
+ }
273
+
274
+ logger().info("creating actor via engine api", { name, key });
275
+
276
+ // Create actor via engine API
277
+ const result = await createActor(this.#config, {
278
+ name,
279
+ runner_name_selector: this.#config.runnerName,
280
+ key: serializeActorKey(key),
281
+ input: input ? cbor.encode(input).toString("base64") : null,
282
+ crash_policy: "sleep",
283
+ });
284
+ const actorId = result.actor.actor_id;
285
+
286
+ logger().info("actor created", { actorId, name, key });
287
+
288
+ return {
289
+ actorId,
290
+ name,
291
+ key,
292
+ };
293
+ }
294
+
295
+ async destroyActor(actorId: string): Promise<void> {
296
+ logger().info("destroying actor via engine api", { actorId });
297
+
298
+ await destroyActor(this.#config, actorId);
299
+
300
+ logger().info("actor destroyed", { actorId });
301
+ }
302
+
303
+ async #forwardHttpRequest(
304
+ actorRequest: Request,
305
+ actorId: string,
306
+ ): Promise<Response> {
307
+ // Route through guard port
308
+ const url = new URL(actorRequest.url);
309
+ const guardUrl = `${this.#config.endpoint}${url.pathname}${url.search}`;
310
+
311
+ // Handle body properly based on method and presence
312
+ let bodyToSend: ArrayBuffer | null = null;
313
+ const guardHeaders = buildGuardHeadersForHttp(actorRequest, actorId);
314
+
315
+ if (
316
+ actorRequest.body &&
317
+ actorRequest.method !== "GET" &&
318
+ actorRequest.method !== "HEAD"
319
+ ) {
320
+ if (actorRequest.bodyUsed) {
321
+ throw new Error("Request body has already been consumed");
322
+ }
323
+
324
+ // TODO: This buffers the entire request in memory every time. We
325
+ // need to properly implement streaming bodies.
326
+ // Clone and read the body to ensure it can be sent
327
+ const clonedRequest = actorRequest.clone();
328
+ bodyToSend = await clonedRequest.arrayBuffer();
329
+
330
+ // If this is a streaming request, we need to convert the headers
331
+ // for the basic array buffer
332
+ guardHeaders.delete("transfer-encoding");
333
+ guardHeaders.set(
334
+ "content-length",
335
+ String((bodyToSend as ArrayBuffer).byteLength),
336
+ );
337
+ }
338
+
339
+ const guardRequest = new Request(guardUrl, {
340
+ method: actorRequest.method,
341
+ headers: guardHeaders,
342
+ body: bodyToSend,
343
+ });
344
+
345
+ return mutableResponse(await fetch(guardRequest));
346
+ }
347
+ }
348
+
349
+ function mutableResponse(fetchRes: Response): Response {
350
+ // We cannot return the raw response from `fetch` since the response type is not mutable.
351
+ //
352
+ // In order for middleware to be able to mutate the response, we need to build a new Response object that is mutable.
353
+ return new Response(fetchRes.body, fetchRes);
354
+ }
355
+
356
+ function buildGuardHeadersForHttp(
357
+ actorRequest: Request,
358
+ actorId: string,
359
+ ): Headers {
360
+ const headers = new Headers();
361
+ // Copy all headers from the original request
362
+ for (const [key, value] of actorRequest.headers.entries()) {
363
+ headers.set(key, value);
364
+ }
365
+ // Add guard-specific headers
366
+ headers.set("x-rivet-target", "actor");
367
+ headers.set("x-rivet-actor", actorId);
368
+ headers.set("x-rivet-port", "main");
369
+ return headers;
370
+ }
371
+
372
+ function buildGuardHeadersForWebSocket(
373
+ actorId: string,
374
+ encoding: Encoding,
375
+ params?: unknown,
376
+ authData?: unknown,
377
+ ): Record<string, string> {
378
+ const headers: Record<string, string> = {};
379
+ headers["x-rivet-target"] = "actor";
380
+ headers["x-rivet-actor"] = actorId;
381
+ headers["x-rivet-port"] = "main";
382
+ headers[HEADER_EXPOSE_INTERNAL_ERROR] = "true";
383
+ headers[HEADER_ENCODING] = encoding;
384
+ if (params) {
385
+ headers[HEADER_CONN_PARAMS] = JSON.stringify(params);
386
+ }
387
+ if (authData) {
388
+ headers[HEADER_AUTH_DATA] = JSON.stringify(authData);
389
+ }
390
+ return headers;
391
+ }
@@ -0,0 +1,36 @@
1
+ import type { Client } from "@/client/client";
2
+ import type { ManagerDriver } from "@/manager/driver";
3
+ import type { RegistryConfig } from "@/registry/config";
4
+ import type { DriverConfig, RunConfig } from "@/registry/run-config";
5
+ import { EngineActorDriver } from "./actor-driver";
6
+ import { ConfigSchema, type InputConfig } from "./config";
7
+ import { EngineManagerDriver } from "./manager-driver";
8
+
9
+ export { EngineActorDriver } from "./actor-driver";
10
+ export { type Config, ConfigSchema, type InputConfig } from "./config";
11
+ export { EngineManagerDriver } from "./manager-driver";
12
+
13
+ export function createEngineDriver(inputConfig?: InputConfig): DriverConfig {
14
+ const config = ConfigSchema.parse(inputConfig);
15
+
16
+ return {
17
+ name: "engine",
18
+ manager: (_registryConfig, runConfig) => {
19
+ return new EngineManagerDriver(config, runConfig);
20
+ },
21
+ actor: (
22
+ registryConfig: RegistryConfig,
23
+ runConfig: RunConfig,
24
+ managerDriver: ManagerDriver,
25
+ inlineClient: Client<any>,
26
+ ) => {
27
+ return new EngineActorDriver(
28
+ registryConfig,
29
+ runConfig,
30
+ managerDriver,
31
+ inlineClient,
32
+ config,
33
+ );
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,170 @@
1
+ import type { Context as HonoContext } from "hono";
2
+ import type { WSContext } from "hono/ws";
3
+ import invariant from "invariant";
4
+ import type { CloseEvent } from "ws";
5
+ import { importWebSocket } from "@/common/websocket";
6
+ import type { UpgradeWebSocketArgs } from "@/mod";
7
+ import { logger } from "./log";
8
+
9
+ /**
10
+ * Returns Hono `upgradeWebSocket` args that will proxy requests from the client to a destination address.
11
+ */
12
+ export async function createWebSocketProxy(
13
+ c: HonoContext,
14
+ targetUrl: string,
15
+ headers: Record<string, string>,
16
+ ): Promise<UpgradeWebSocketArgs> {
17
+ const WebSocket = await importWebSocket();
18
+
19
+ // HACK: Sanitize WebSocket-specific headers. If we don't do this, some WebSocket implementations (i.e. native WebSocket in Node.js) will fail to connect.
20
+ for (const [k, v] of c.req.raw.headers.entries()) {
21
+ if (!k.startsWith("sec-") && k !== "connection" && k !== "upgrade") {
22
+ headers[k] = v;
23
+ }
24
+ }
25
+
26
+ // WebSocket state
27
+ interface WsState {
28
+ targetWs?: WebSocket;
29
+ connectPromise?: Promise<void>;
30
+ }
31
+ const state: WsState = {};
32
+
33
+ return {
34
+ onOpen: async (event: any, clientWs: WSContext) => {
35
+ logger().debug("client websocket connected", { targetUrl });
36
+
37
+ if (clientWs.readyState !== 1) {
38
+ logger().warn("client websocket not open on connection", {
39
+ targetUrl,
40
+ readyState: clientWs.readyState,
41
+ });
42
+ return;
43
+ }
44
+
45
+ // Create WebSocket
46
+ const targetWs = new WebSocket(targetUrl, { headers });
47
+ state.targetWs = targetWs;
48
+
49
+ // Setup connection promise
50
+ state.connectPromise = new Promise<void>((resolve, reject) => {
51
+ targetWs.addEventListener("open", () => {
52
+ logger().debug("target websocket connected", { targetUrl });
53
+
54
+ if (clientWs.readyState !== 1) {
55
+ logger().warn("client websocket closed before target connected", {
56
+ targetUrl,
57
+ clientReadyState: clientWs.readyState,
58
+ });
59
+ targetWs.close(1001, "Client disconnected");
60
+ reject(new Error("Client disconnected"));
61
+ return;
62
+ }
63
+ resolve();
64
+ });
65
+
66
+ targetWs.addEventListener("error", (error) => {
67
+ logger().warn("target websocket error during connection", {
68
+ targetUrl,
69
+ });
70
+ reject(error);
71
+ });
72
+ });
73
+
74
+ // Setup bidirectional forwarding
75
+ state.targetWs.addEventListener("message", (event) => {
76
+ if (
77
+ typeof event.data === "string" ||
78
+ event.data instanceof ArrayBuffer
79
+ ) {
80
+ clientWs.send(event.data);
81
+ } else if (event.data instanceof Blob) {
82
+ event.data.arrayBuffer().then((buffer) => {
83
+ clientWs.send(buffer);
84
+ });
85
+ }
86
+ });
87
+
88
+ state.targetWs.addEventListener("close", (event) => {
89
+ logger().debug("target websocket closed", {
90
+ targetUrl,
91
+ code: event.code,
92
+ reason: event.reason,
93
+ });
94
+ closeWebSocketIfOpen(clientWs, event.code, event.reason);
95
+ });
96
+
97
+ state.targetWs.addEventListener("error", (error) => {
98
+ logger().error("target websocket error", { targetUrl, error });
99
+ closeWebSocketIfOpen(clientWs, 1011, "Target WebSocket error");
100
+ });
101
+ },
102
+
103
+ onMessage: async (event: any, clientWs: WSContext) => {
104
+ if (!state.targetWs || !state.connectPromise) {
105
+ logger().error("websocket state not initialized", { targetUrl });
106
+ return;
107
+ }
108
+
109
+ try {
110
+ await state.connectPromise;
111
+ if (state.targetWs.readyState === WebSocket.OPEN) {
112
+ state.targetWs.send(event.data);
113
+ } else {
114
+ logger().warn("target websocket not open", {
115
+ targetUrl,
116
+ readyState: state.targetWs.readyState,
117
+ });
118
+ }
119
+ } catch (error) {
120
+ logger().error("failed to connect to target websocket", {
121
+ targetUrl,
122
+ error,
123
+ });
124
+ closeWebSocketIfOpen(clientWs, 1011, "Failed to connect to target");
125
+ }
126
+ },
127
+
128
+ onClose: (event: any, clientWs: WSContext) => {
129
+ logger().debug("client websocket closed", {
130
+ targetUrl,
131
+ code: event.code,
132
+ reason: event.reason,
133
+ wasClean: event.wasClean,
134
+ });
135
+
136
+ if (state.targetWs) {
137
+ if (
138
+ state.targetWs.readyState === WebSocket.OPEN ||
139
+ state.targetWs.readyState === WebSocket.CONNECTING
140
+ ) {
141
+ state.targetWs.close(1000, event.reason || "Client disconnected");
142
+ }
143
+ }
144
+ },
145
+
146
+ onError: (event: any, clientWs: WSContext) => {
147
+ logger().error("client websocket error", { targetUrl, event });
148
+
149
+ if (state.targetWs) {
150
+ if (state.targetWs.readyState === WebSocket.OPEN) {
151
+ state.targetWs.close(1011, "Client WebSocket error");
152
+ } else if (state.targetWs.readyState === WebSocket.CONNECTING) {
153
+ state.targetWs.close();
154
+ }
155
+ }
156
+ },
157
+ };
158
+ }
159
+
160
+ function closeWebSocketIfOpen(
161
+ ws: WebSocket | WSContext,
162
+ code: number,
163
+ reason: string,
164
+ ): void {
165
+ if (ws.readyState === 1) {
166
+ ws.close(code, reason);
167
+ } else if ("close" in ws && (ws as WebSocket).readyState === WebSocket.OPEN) {
168
+ ws.close(code, reason);
169
+ }
170
+ }
@@ -0,0 +1,91 @@
1
+ import type { GenericConnGlobalState } from "@/actor/generic-conn-driver";
2
+ import { logger } from "@/actor/log";
3
+ import type { AnyClient } from "@/client/client";
4
+ import type {
5
+ ActorDriver,
6
+ AnyActorInstance,
7
+ ManagerDriver,
8
+ } from "@/driver-helpers/mod";
9
+ import type { RegistryConfig, RunConfig } from "@/mod";
10
+ import { bufferToArrayBuffer } from "@/utils";
11
+ import type { FileSystemGlobalState } from "./global-state";
12
+
13
+ export type ActorDriverContext = Record<never, never>;
14
+
15
+ /**
16
+ * File System implementation of the Actor Driver
17
+ */
18
+ export class FileSystemActorDriver implements ActorDriver {
19
+ #registryConfig: RegistryConfig;
20
+ #runConfig: RunConfig;
21
+ #managerDriver: ManagerDriver;
22
+ #inlineClient: AnyClient;
23
+ #state: FileSystemGlobalState;
24
+
25
+ constructor(
26
+ registryConfig: RegistryConfig,
27
+ runConfig: RunConfig,
28
+ managerDriver: ManagerDriver,
29
+ inlineClient: AnyClient,
30
+ state: FileSystemGlobalState,
31
+ ) {
32
+ this.#registryConfig = registryConfig;
33
+ this.#runConfig = runConfig;
34
+ this.#managerDriver = managerDriver;
35
+ this.#inlineClient = inlineClient;
36
+ this.#state = state;
37
+ }
38
+
39
+ async loadActor(actorId: string): Promise<AnyActorInstance> {
40
+ return this.#state.startActor(
41
+ this.#registryConfig,
42
+ this.#runConfig,
43
+ this.#inlineClient,
44
+ this,
45
+ actorId,
46
+ );
47
+ }
48
+
49
+ getGenericConnGlobalState(actorId: string): GenericConnGlobalState {
50
+ return this.#state.getActorOrError(actorId).genericConnGlobalState;
51
+ }
52
+
53
+ /**
54
+ * Get the current storage directory path
55
+ */
56
+ get storagePath(): string {
57
+ return this.#state.storagePath;
58
+ }
59
+
60
+ getContext(_actorId: string): ActorDriverContext {
61
+ return {};
62
+ }
63
+
64
+ async readPersistedData(actorId: string): Promise<Uint8Array | undefined> {
65
+ return new Uint8Array(
66
+ (await this.#state.loadActorStateOrError(actorId)).persistedData,
67
+ );
68
+ }
69
+
70
+ async writePersistedData(actorId: string, data: Uint8Array): Promise<void> {
71
+ const state = await this.#state.loadActorStateOrError(actorId);
72
+
73
+ // Save state to disk
74
+ await this.#state.writeActor(actorId, {
75
+ ...state,
76
+ persistedData: bufferToArrayBuffer(data),
77
+ });
78
+ }
79
+
80
+ async setAlarm(actor: AnyActorInstance, timestamp: number): Promise<void> {
81
+ await this.#state.setActorAlarm(actor.id, timestamp);
82
+ }
83
+
84
+ getDatabase(actorId: string): Promise<unknown | undefined> {
85
+ return this.#state.createDatabase(actorId);
86
+ }
87
+
88
+ sleep(actorId: string): Promise<void> {
89
+ return this.#state.sleepActor(actorId);
90
+ }
91
+ }