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,274 @@
1
+ import * as cbor from "cbor-x";
2
+ import type { Hono, Context as HonoContext } from "hono";
3
+ import invariant from "invariant";
4
+ import { ActorAlreadyExists } from "@/actor/errors";
5
+ import { deserializeActorKey, serializeActorKey } from "@/actor/keys";
6
+ import type { ClientConfig } from "@/client/client";
7
+ import { noopNext } from "@/common/utils";
8
+ import type {
9
+ ActorOutput,
10
+ CreateInput,
11
+ GetForIdInput,
12
+ GetOrCreateWithKeyInput,
13
+ GetWithKeyInput,
14
+ ManagerDisplayInformation,
15
+ ManagerDriver,
16
+ } from "@/driver-helpers/mod";
17
+ import type { ManagerInspector } from "@/inspector/manager";
18
+ import type { Encoding, RegistryConfig, UniversalWebSocket } from "@/mod";
19
+ import type { RunConfig } from "@/registry/run-config";
20
+ import { sendHttpRequestToActor } from "./actor-http-client";
21
+ import {
22
+ buildGuardHeadersForWebSocket,
23
+ openWebSocketToActor,
24
+ } from "./actor-websocket-client";
25
+ import {
26
+ createActor,
27
+ destroyActor,
28
+ getActor,
29
+ getActorById,
30
+ getOrCreateActorById,
31
+ } from "./api-endpoints";
32
+ import { EngineApiError, getEndpoint } from "./api-utils";
33
+ import { logger } from "./log";
34
+ import { createWebSocketProxy } from "./ws-proxy";
35
+
36
+ // TODO:
37
+ // // Lazily import the dynamic imports so we don't have to turn `createClient` in to an async fn
38
+ // const dynamicImports = (async () => {
39
+ // // Import dynamic dependencies
40
+ // const [WebSocket, EventSource] = await Promise.all([
41
+ // importWebSocket(),
42
+ // importEventSource(),
43
+ // ]);
44
+ // return {
45
+ // WebSocket,
46
+ // EventSource,
47
+ // };
48
+ // })();
49
+
50
+ export class RemoteManagerDriver implements ManagerDriver {
51
+ #config: ClientConfig;
52
+
53
+ constructor(runConfig: ClientConfig) {
54
+ this.#config = runConfig;
55
+ }
56
+
57
+ async getForId({
58
+ c,
59
+ name,
60
+ actorId,
61
+ }: GetForIdInput): Promise<ActorOutput | undefined> {
62
+ // Fetch from API if not in cache
63
+ try {
64
+ const response = await getActor(this.#config, actorId);
65
+
66
+ // Validate name matches
67
+ if (response.actor.name !== name) {
68
+ logger().debug({
69
+ msg: "actor name mismatch from api",
70
+ actorId,
71
+ apiName: response.actor.name,
72
+ requestedName: name,
73
+ });
74
+ return undefined;
75
+ }
76
+
77
+ const keyRaw = response.actor.key;
78
+ invariant(keyRaw, `actor ${actorId} should have key`);
79
+ const key = deserializeActorKey(keyRaw);
80
+
81
+ return {
82
+ actorId,
83
+ name,
84
+ key,
85
+ };
86
+ } catch (error) {
87
+ if (
88
+ error instanceof EngineApiError &&
89
+ (error as EngineApiError).group === "actor" &&
90
+ (error as EngineApiError).code === "not_found"
91
+ ) {
92
+ return undefined;
93
+ }
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ async getWithKey({
99
+ c,
100
+ name,
101
+ key,
102
+ }: GetWithKeyInput): Promise<ActorOutput | undefined> {
103
+ logger().debug({ msg: "getWithKey: searching for actor", name, key });
104
+
105
+ // If not in local cache, fetch by key from API
106
+ try {
107
+ const response = await getActorById(this.#config, name, key);
108
+
109
+ if (!response.actor_id) {
110
+ return undefined;
111
+ }
112
+
113
+ const actorId = response.actor_id;
114
+
115
+ logger().debug({
116
+ msg: "getWithKey: found actor via api",
117
+ actorId,
118
+ name,
119
+ key,
120
+ });
121
+
122
+ return {
123
+ actorId,
124
+ name,
125
+ key,
126
+ };
127
+ } catch (error) {
128
+ if (
129
+ error instanceof EngineApiError &&
130
+ (error as EngineApiError).group === "actor" &&
131
+ (error as EngineApiError).code === "not_found"
132
+ ) {
133
+ return undefined;
134
+ }
135
+ throw error;
136
+ }
137
+ }
138
+
139
+ async getOrCreateWithKey(
140
+ input: GetOrCreateWithKeyInput,
141
+ ): Promise<ActorOutput> {
142
+ const { c, name, key, input: actorInput, region } = input;
143
+
144
+ logger().info({
145
+ msg: "getOrCreateWithKey: getting or creating actor via engine api",
146
+ name,
147
+ key,
148
+ });
149
+
150
+ const response = await getOrCreateActorById(this.#config, {
151
+ name,
152
+ key: serializeActorKey(key),
153
+ runner_name_selector: this.#config.runnerName,
154
+ input: input ? cbor.encode(actorInput).toString("base64") : undefined,
155
+ crash_policy: "sleep",
156
+ });
157
+
158
+ const actorId = response.actor_id;
159
+
160
+ logger().info({
161
+ msg: "getOrCreateWithKey: actor ready",
162
+ actorId,
163
+ name,
164
+ key,
165
+ created: response.created,
166
+ });
167
+
168
+ return {
169
+ actorId,
170
+ name,
171
+ key,
172
+ };
173
+ }
174
+
175
+ async createActor({
176
+ c,
177
+ name,
178
+ key,
179
+ input,
180
+ }: CreateInput): Promise<ActorOutput> {
181
+ logger().info({ msg: "creating actor via engine api", name, key });
182
+
183
+ // Create actor via engine API
184
+ const result = await createActor(this.#config, {
185
+ name,
186
+ runner_name_selector: this.#config.runnerName,
187
+ key: serializeActorKey(key),
188
+ input: input ? cbor.encode(input).toString("base64") : null,
189
+ crash_policy: "sleep",
190
+ });
191
+ const actorId = result.actor.actor_id;
192
+
193
+ logger().info({ msg: "actor created", actorId, name, key });
194
+
195
+ return {
196
+ actorId,
197
+ name,
198
+ key,
199
+ };
200
+ }
201
+
202
+ async destroyActor(actorId: string): Promise<void> {
203
+ logger().info({ msg: "destroying actor via engine api", actorId });
204
+
205
+ await destroyActor(this.#config, actorId);
206
+
207
+ logger().info({ msg: "actor destroyed", actorId });
208
+ }
209
+
210
+ async sendRequest(actorId: string, actorRequest: Request): Promise<Response> {
211
+ return await sendHttpRequestToActor(this.#config, actorId, actorRequest);
212
+ }
213
+
214
+ async openWebSocket(
215
+ path: string,
216
+ actorId: string,
217
+ encoding: Encoding,
218
+ params: unknown,
219
+ ): Promise<UniversalWebSocket> {
220
+ return await openWebSocketToActor(
221
+ this.#config,
222
+ path,
223
+ actorId,
224
+ encoding,
225
+ params,
226
+ );
227
+ }
228
+
229
+ async proxyRequest(
230
+ _c: HonoContext,
231
+ actorRequest: Request,
232
+ actorId: string,
233
+ ): Promise<Response> {
234
+ return await sendHttpRequestToActor(this.#config, actorId, actorRequest);
235
+ }
236
+
237
+ async proxyWebSocket(
238
+ c: HonoContext,
239
+ path: string,
240
+ actorId: string,
241
+ encoding: Encoding,
242
+ params: unknown,
243
+ authData: unknown,
244
+ ): Promise<Response> {
245
+ const upgradeWebSocket = this.#config.getUpgradeWebSocket?.();
246
+ invariant(upgradeWebSocket, "missing getUpgradeWebSocket");
247
+
248
+ const endpoint = getEndpoint(this.#config);
249
+ const guardUrl = `${endpoint}${path}`;
250
+ const wsGuardUrl = guardUrl.replace("http://", "ws://");
251
+
252
+ logger().debug({
253
+ msg: "forwarding websocket to actor via guard",
254
+ actorId,
255
+ path,
256
+ guardUrl,
257
+ });
258
+
259
+ // Build headers
260
+ const headers = buildGuardHeadersForWebSocket(
261
+ actorId,
262
+ encoding,
263
+ params,
264
+ authData,
265
+ );
266
+ const args = await createWebSocketProxy(c, wsGuardUrl, headers);
267
+
268
+ return await upgradeWebSocket(() => args)(c, noopNext());
269
+ }
270
+
271
+ displayInformation(): ManagerDisplayInformation {
272
+ return { name: "Remote", properties: {} };
273
+ }
274
+ }
@@ -0,0 +1,180 @@
1
+ import type { Context as HonoContext } from "hono";
2
+ import type { WSContext } from "hono/ws";
3
+ import { stringifyError } from "@/common/utils";
4
+ import { importWebSocket } from "@/common/websocket";
5
+ import type { UpgradeWebSocketArgs } from "@/mod";
6
+ import { logger } from "./log";
7
+
8
+ /**
9
+ * Returns Hono `upgradeWebSocket` args that will proxy requests from the client to a destination address.
10
+ */
11
+ export async function createWebSocketProxy(
12
+ c: HonoContext,
13
+ targetUrl: string,
14
+ headers: Record<string, string>,
15
+ ): Promise<UpgradeWebSocketArgs> {
16
+ const WebSocket = await importWebSocket();
17
+
18
+ // 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.
19
+ for (const [k, v] of c.req.raw.headers.entries()) {
20
+ if (!k.startsWith("sec-") && k !== "connection" && k !== "upgrade") {
21
+ headers[k] = v;
22
+ }
23
+ }
24
+
25
+ // WebSocket state
26
+ interface WsState {
27
+ targetWs?: WebSocket;
28
+ connectPromise?: Promise<void>;
29
+ }
30
+ const state: WsState = {};
31
+
32
+ return {
33
+ onOpen: async (event: any, clientWs: WSContext) => {
34
+ logger().debug({ msg: "client websocket connected", targetUrl });
35
+
36
+ if (clientWs.readyState !== 1) {
37
+ logger().warn({
38
+ msg: "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({ msg: "target websocket connected", targetUrl });
53
+
54
+ if (clientWs.readyState !== 1) {
55
+ logger().warn({
56
+ msg: "client websocket closed before target connected",
57
+ targetUrl,
58
+ clientReadyState: clientWs.readyState,
59
+ });
60
+ targetWs.close(1001, "Client disconnected");
61
+ reject(new Error("Client disconnected"));
62
+ return;
63
+ }
64
+ resolve();
65
+ });
66
+
67
+ targetWs.addEventListener("error", (error) => {
68
+ logger().warn({
69
+ msg: "target websocket error during connection",
70
+ targetUrl,
71
+ });
72
+ reject(error);
73
+ });
74
+ });
75
+
76
+ // Setup bidirectional forwarding
77
+ state.targetWs.addEventListener("message", (event) => {
78
+ if (
79
+ typeof event.data === "string" ||
80
+ event.data instanceof ArrayBuffer
81
+ ) {
82
+ clientWs.send(event.data);
83
+ } else if (event.data instanceof Blob) {
84
+ event.data.arrayBuffer().then((buffer) => {
85
+ clientWs.send(buffer);
86
+ });
87
+ }
88
+ });
89
+
90
+ state.targetWs.addEventListener("close", (event) => {
91
+ logger().debug({
92
+ msg: "target websocket closed",
93
+ targetUrl,
94
+ code: event.code,
95
+ reason: event.reason,
96
+ });
97
+ closeWebSocketIfOpen(clientWs, event.code, event.reason);
98
+ });
99
+
100
+ state.targetWs.addEventListener("error", (error) => {
101
+ logger().error({
102
+ msg: "target websocket error",
103
+ targetUrl,
104
+ error: stringifyError(error),
105
+ });
106
+ closeWebSocketIfOpen(clientWs, 1011, "Target WebSocket error");
107
+ });
108
+ },
109
+
110
+ onMessage: async (event: any, clientWs: WSContext) => {
111
+ if (!state.targetWs || !state.connectPromise) {
112
+ logger().error({ msg: "websocket state not initialized", targetUrl });
113
+ return;
114
+ }
115
+
116
+ try {
117
+ await state.connectPromise;
118
+ if (state.targetWs.readyState === WebSocket.OPEN) {
119
+ state.targetWs.send(event.data);
120
+ } else {
121
+ logger().warn({
122
+ msg: "target websocket not open",
123
+ targetUrl,
124
+ readyState: state.targetWs.readyState,
125
+ });
126
+ }
127
+ } catch (error) {
128
+ logger().error({
129
+ msg: "failed to connect to target websocket",
130
+ targetUrl,
131
+ error,
132
+ });
133
+ closeWebSocketIfOpen(clientWs, 1011, "Failed to connect to target");
134
+ }
135
+ },
136
+
137
+ onClose: (event: any, clientWs: WSContext) => {
138
+ logger().debug({
139
+ msg: "client websocket closed",
140
+ targetUrl,
141
+ code: event.code,
142
+ reason: event.reason,
143
+ wasClean: event.wasClean,
144
+ });
145
+
146
+ if (state.targetWs) {
147
+ if (
148
+ state.targetWs.readyState === WebSocket.OPEN ||
149
+ state.targetWs.readyState === WebSocket.CONNECTING
150
+ ) {
151
+ state.targetWs.close(1000, event.reason || "Client disconnected");
152
+ }
153
+ }
154
+ },
155
+
156
+ onError: (event: any, clientWs: WSContext) => {
157
+ logger().error({ msg: "client websocket error", targetUrl, event });
158
+
159
+ if (state.targetWs) {
160
+ if (state.targetWs.readyState === WebSocket.OPEN) {
161
+ state.targetWs.close(1011, "Client WebSocket error");
162
+ } else if (state.targetWs.readyState === WebSocket.CONNECTING) {
163
+ state.targetWs.close();
164
+ }
165
+ }
166
+ },
167
+ };
168
+ }
169
+
170
+ function closeWebSocketIfOpen(
171
+ ws: WebSocket | WSContext,
172
+ code: number,
173
+ reason: string,
174
+ ): void {
175
+ if (ws.readyState === 1) {
176
+ ws.close(code, reason);
177
+ } else if ("close" in ws && (ws as WebSocket).readyState === WebSocket.OPEN) {
178
+ ws.close(code, reason);
179
+ }
180
+ }
@@ -0,0 +1 @@
1
+ export * from "../../../dist/schemas/actor-persist/v1";
@@ -0,0 +1,25 @@
1
+ import {
2
+ createVersionedDataHandler,
3
+ type MigrationFn,
4
+ } from "@/common/versioned-data";
5
+ import * as v1 from "../../../dist/schemas/actor-persist/v1";
6
+
7
+ export const CURRENT_VERSION = 1;
8
+
9
+ export type CurrentPersistedActor = v1.PersistedActor;
10
+ export type CurrentPersistedConnection = v1.PersistedConnection;
11
+ export type CurrentPersistedSubscription = v1.PersistedSubscription;
12
+ export type CurrentGenericPersistedScheduleEvent =
13
+ v1.GenericPersistedScheduleEvent;
14
+ export type CurrentPersistedScheduleEventKind = v1.PersistedScheduleEventKind;
15
+ export type CurrentPersistedScheduleEvent = v1.PersistedScheduleEvent;
16
+
17
+ const migrations = new Map<number, MigrationFn<any, any>>();
18
+
19
+ export const PERSISTED_ACTOR_VERSIONED =
20
+ createVersionedDataHandler<CurrentPersistedActor>({
21
+ currentVersion: CURRENT_VERSION,
22
+ migrations,
23
+ serializeVersion: (data) => v1.encodePersistedActor(data),
24
+ deserializeVersion: (bytes) => v1.decodePersistedActor(bytes),
25
+ });
@@ -0,0 +1 @@
1
+ export * from "../../../dist/schemas/client-protocol/v1";
@@ -0,0 +1,63 @@
1
+ import {
2
+ createVersionedDataHandler,
3
+ type MigrationFn,
4
+ } from "@/common/versioned-data";
5
+ import * as v1 from "../../../dist/schemas/client-protocol/v1";
6
+
7
+ export const CURRENT_VERSION = 1;
8
+
9
+ const migrations = new Map<number, MigrationFn<any, any>>();
10
+
11
+ export const TO_SERVER_VERSIONED = createVersionedDataHandler<v1.ToServer>({
12
+ currentVersion: CURRENT_VERSION,
13
+ migrations,
14
+ serializeVersion: (data) => v1.encodeToServer(data),
15
+ deserializeVersion: (bytes) => v1.decodeToServer(bytes),
16
+ });
17
+
18
+ export const TO_CLIENT_VERSIONED = createVersionedDataHandler<v1.ToClient>({
19
+ currentVersion: CURRENT_VERSION,
20
+ migrations,
21
+ serializeVersion: (data) => v1.encodeToClient(data),
22
+ deserializeVersion: (bytes) => v1.decodeToClient(bytes),
23
+ });
24
+
25
+ export const HTTP_ACTION_REQUEST_VERSIONED =
26
+ createVersionedDataHandler<v1.HttpActionRequest>({
27
+ currentVersion: CURRENT_VERSION,
28
+ migrations,
29
+ serializeVersion: (data) => v1.encodeHttpActionRequest(data),
30
+ deserializeVersion: (bytes) => v1.decodeHttpActionRequest(bytes),
31
+ });
32
+
33
+ export const HTTP_ACTION_RESPONSE_VERSIONED =
34
+ createVersionedDataHandler<v1.HttpActionResponse>({
35
+ currentVersion: CURRENT_VERSION,
36
+ migrations,
37
+ serializeVersion: (data) => v1.encodeHttpActionResponse(data),
38
+ deserializeVersion: (bytes) => v1.decodeHttpActionResponse(bytes),
39
+ });
40
+
41
+ export const HTTP_RESPONSE_ERROR_VERSIONED =
42
+ createVersionedDataHandler<v1.HttpResponseError>({
43
+ currentVersion: CURRENT_VERSION,
44
+ migrations,
45
+ serializeVersion: (data) => v1.encodeHttpResponseError(data),
46
+ deserializeVersion: (bytes) => v1.decodeHttpResponseError(bytes),
47
+ });
48
+
49
+ export const HTTP_RESOLVE_REQUEST_VERSIONED =
50
+ createVersionedDataHandler<v1.HttpResolveRequest>({
51
+ currentVersion: CURRENT_VERSION,
52
+ migrations,
53
+ serializeVersion: (_) => new Uint8Array(),
54
+ deserializeVersion: (bytes) => null,
55
+ });
56
+
57
+ export const HTTP_RESOLVE_RESPONSE_VERSIONED =
58
+ createVersionedDataHandler<v1.HttpResolveResponse>({
59
+ currentVersion: CURRENT_VERSION,
60
+ migrations,
61
+ serializeVersion: (data) => v1.encodeHttpResolveResponse(data),
62
+ deserializeVersion: (bytes) => v1.decodeHttpResolveResponse(bytes),
63
+ });
@@ -0,0 +1 @@
1
+ export * from "../../../dist/schemas/file-system-driver/v1";
@@ -0,0 +1,28 @@
1
+ import {
2
+ createVersionedDataHandler,
3
+ type MigrationFn,
4
+ } from "@/common/versioned-data";
5
+ import * as v1 from "../../../dist/schemas/file-system-driver/v1";
6
+
7
+ export const CURRENT_VERSION = 1;
8
+
9
+ export type CurrentActorState = v1.ActorState;
10
+ export type CurrentActorAlarm = v1.ActorAlarm;
11
+
12
+ const migrations = new Map<number, MigrationFn<any, any>>();
13
+
14
+ export const ACTOR_STATE_VERSIONED =
15
+ createVersionedDataHandler<CurrentActorState>({
16
+ currentVersion: CURRENT_VERSION,
17
+ migrations,
18
+ serializeVersion: (data) => v1.encodeActorState(data),
19
+ deserializeVersion: (bytes) => v1.decodeActorState(bytes),
20
+ });
21
+
22
+ export const ACTOR_ALARM_VERSIONED =
23
+ createVersionedDataHandler<CurrentActorAlarm>({
24
+ currentVersion: CURRENT_VERSION,
25
+ migrations,
26
+ serializeVersion: (data) => v1.encodeActorAlarm(data),
27
+ deserializeVersion: (bytes) => v1.decodeActorAlarm(bytes),
28
+ });
package/src/serde.ts ADDED
@@ -0,0 +1,90 @@
1
+ import * as cbor from "cbor-x";
2
+ import invariant from "invariant";
3
+ import { assertUnreachable } from "@/common/utils";
4
+ import type { VersionedDataHandler } from "@/common/versioned-data";
5
+ import type { Encoding } from "@/mod";
6
+ import { jsonStringifyCompat } from "./actor/protocol/serde";
7
+
8
+ export function encodingIsBinary(encoding: Encoding): boolean {
9
+ if (encoding === "json") {
10
+ return false;
11
+ } else if (encoding === "cbor" || encoding === "bare") {
12
+ return true;
13
+ } else {
14
+ assertUnreachable(encoding);
15
+ }
16
+ }
17
+
18
+ export function contentTypeForEncoding(encoding: Encoding): string {
19
+ if (encoding === "json") {
20
+ return "application/json";
21
+ } else if (encoding === "cbor" || encoding === "bare") {
22
+ return "application/octet-stream";
23
+ } else {
24
+ assertUnreachable(encoding);
25
+ }
26
+ }
27
+
28
+ export function wsBinaryTypeForEncoding(
29
+ encoding: Encoding,
30
+ ): "arraybuffer" | "blob" {
31
+ if (encoding === "json") {
32
+ return "blob";
33
+ } else if (encoding === "cbor" || encoding === "bare") {
34
+ return "arraybuffer";
35
+ } else {
36
+ assertUnreachable(encoding);
37
+ }
38
+ }
39
+
40
+ export function serializeWithEncoding<T>(
41
+ encoding: Encoding,
42
+ value: T,
43
+ versionedDataHandler: VersionedDataHandler<T> | undefined,
44
+ ): Uint8Array | string {
45
+ if (encoding === "json") {
46
+ return jsonStringifyCompat(value);
47
+ } else if (encoding === "cbor") {
48
+ return cbor.encode(value);
49
+ } else if (encoding === "bare") {
50
+ if (!versionedDataHandler) {
51
+ throw new Error("VersionedDataHandler is required for 'bare' encoding");
52
+ }
53
+ return versionedDataHandler.serializeWithEmbeddedVersion(value);
54
+ } else {
55
+ assertUnreachable(encoding);
56
+ }
57
+ }
58
+
59
+ export function deserializeWithEncoding<T>(
60
+ encoding: Encoding,
61
+ buffer: Uint8Array | string,
62
+ versionedDataHandler: VersionedDataHandler<T> | undefined,
63
+ ): T {
64
+ if (encoding === "json") {
65
+ if (typeof buffer === "string") {
66
+ return JSON.parse(buffer);
67
+ } else {
68
+ const decoder = new TextDecoder("utf-8");
69
+ const jsonString = decoder.decode(buffer);
70
+ return JSON.parse(jsonString);
71
+ }
72
+ } else if (encoding === "cbor") {
73
+ invariant(
74
+ typeof buffer !== "string",
75
+ "buffer cannot be string for cbor encoding",
76
+ );
77
+ return cbor.decode(buffer);
78
+ } else if (encoding === "bare") {
79
+ invariant(
80
+ typeof buffer !== "string",
81
+ "buffer cannot be string for bare encoding",
82
+ );
83
+ if (!versionedDataHandler) {
84
+ throw new Error("VersionedDataHandler is required for 'bare' encoding");
85
+ }
86
+ return versionedDataHandler.deserializeWithEmbeddedVersion(buffer);
87
+ } else {
88
+ assertUnreachable(encoding);
89
+ }
90
+ }
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ import { RunConfigSchema } from "@/registry/run-config";
3
+
4
+ export const ConfigSchema = RunConfigSchema.removeDefault()
5
+ .extend({
6
+ hostname: z
7
+ .string()
8
+ .optional()
9
+ .default(process.env.HOSTNAME ?? "127.0.0.1"),
10
+ port: z
11
+ .number()
12
+ .optional()
13
+ .default(Number.parseInt(process.env.PORT ?? "8080")),
14
+ })
15
+ .default({});
16
+ export type InputConfig = z.input<typeof ConfigSchema>;
@@ -0,0 +1,5 @@
1
+ import { getLogger } from "@/common/log";
2
+
3
+ export function logger() {
4
+ return getLogger("test");
5
+ }