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,267 @@
1
+ import type { EventListener } from "eventsource";
2
+ import type { SSEStreamingApi } from "hono/streaming";
3
+ import { getLogger } from "@/common//log";
4
+ import type {
5
+ UniversalEvent,
6
+ UniversalEventSource,
7
+ UniversalMessageEvent,
8
+ } from "@/common/eventsource-interface";
9
+
10
+ export function logger() {
11
+ return getLogger("fake-event-source");
12
+ }
13
+
14
+ /**
15
+ * FakeEventSource provides a minimal implementation of an SSE stream
16
+ * that handles events for the inline client driver
17
+ */
18
+ export class FakeEventSource implements UniversalEventSource {
19
+ // EventSource readyState values
20
+ readonly CONNECTING = 0 as const;
21
+ readonly OPEN = 1 as const;
22
+ readonly CLOSED = 2 as const;
23
+
24
+ url = "http://internal-sse-endpoint";
25
+ readyState: 0 | 1 | 2 = 1; // OPEN
26
+ withCredentials = false;
27
+
28
+ // Event handlers
29
+ onopen: ((event: UniversalEvent) => void) | null = null;
30
+ onmessage: ((event: UniversalMessageEvent) => void) | null = null;
31
+ onerror: ((event: UniversalEvent) => void) | null = null;
32
+
33
+ // Private event listeners
34
+ #listeners: Record<string, Set<EventListener>> = {
35
+ open: new Set(),
36
+ message: new Set(),
37
+ error: new Set(),
38
+ close: new Set(),
39
+ };
40
+
41
+ // Stream that will be passed to the handler
42
+ #stream: SSEStreamingApi;
43
+ #onCloseCallback: () => Promise<void>;
44
+
45
+ /**
46
+ * Creates a new FakeEventSource
47
+ */
48
+ constructor(onCloseCallback: () => Promise<void>) {
49
+ this.#onCloseCallback = onCloseCallback;
50
+
51
+ this.#stream = this.#createStreamApi();
52
+
53
+ // Trigger open event on next tick
54
+ setTimeout(() => {
55
+ if (this.readyState === 1) {
56
+ this.#dispatchEvent("open");
57
+ }
58
+ }, 0);
59
+
60
+ logger().debug("FakeEventSource created");
61
+ }
62
+
63
+ // Creates the SSE streaming API implementation
64
+ #createStreamApi(): SSEStreamingApi {
65
+ // Create self-reference for closures
66
+ const self = this;
67
+
68
+ const streamApi: SSEStreamingApi = {
69
+ write: async (input) => {
70
+ const data =
71
+ typeof input === "string" ? input : new TextDecoder().decode(input);
72
+ self.#dispatchEvent("message", { data });
73
+ return streamApi;
74
+ },
75
+
76
+ writeln: async (input: string) => {
77
+ await streamApi.write(input + "\n");
78
+ return streamApi;
79
+ },
80
+
81
+ writeSSE: async (message: {
82
+ data: string | Promise<string>;
83
+ event?: string;
84
+ id?: string;
85
+ retry?: number;
86
+ }): Promise<void> => {
87
+ const data = await message.data;
88
+
89
+ if (message.event) {
90
+ self.#dispatchEvent(message.event, { data });
91
+ } else {
92
+ self.#dispatchEvent("message", { data });
93
+ }
94
+ },
95
+
96
+ sleep: async (ms: number) => {
97
+ await new Promise((resolve) => setTimeout(resolve, ms));
98
+ return streamApi;
99
+ },
100
+
101
+ close: async () => {
102
+ self.close();
103
+ },
104
+
105
+ pipe: async (_body: ReadableStream) => {
106
+ // No-op implementation
107
+ },
108
+
109
+ onAbort: async (cb: () => void) => {
110
+ self.addEventListener("error", () => {
111
+ cb();
112
+ });
113
+ return streamApi;
114
+ },
115
+
116
+ abort: async () => {
117
+ self.#dispatchEvent("error");
118
+ return streamApi;
119
+ },
120
+
121
+ // Additional required properties
122
+ get responseReadable() {
123
+ return null as unknown as ReadableStream;
124
+ },
125
+
126
+ get aborted() {
127
+ return self.readyState === 2; // CLOSED
128
+ },
129
+
130
+ get closed() {
131
+ return self.readyState === 2; // CLOSED
132
+ },
133
+ };
134
+
135
+ return streamApi;
136
+ }
137
+
138
+ /**
139
+ * Closes the connection
140
+ */
141
+ close(): void {
142
+ if (this.readyState === 2) {
143
+ // CLOSED
144
+ return;
145
+ }
146
+
147
+ logger().debug({ msg: "closing FakeEventSource" });
148
+ this.readyState = 2; // CLOSED
149
+
150
+ // Call the close callback
151
+ this.#onCloseCallback().catch((err) => {
152
+ logger().error({ msg: "error in onClose callback", error: err });
153
+ });
154
+
155
+ // Dispatch close event
156
+ this.#dispatchEvent("close");
157
+ }
158
+
159
+ /**
160
+ * Get the stream API to pass to the handler
161
+ */
162
+ getStream(): SSEStreamingApi {
163
+ return this.#stream;
164
+ }
165
+
166
+ // Implementation of EventTarget-like interface
167
+ addEventListener(type: string, listener: EventListener): void {
168
+ if (!this.#listeners[type]) {
169
+ this.#listeners[type] = new Set();
170
+ }
171
+ this.#listeners[type].add(listener);
172
+ }
173
+
174
+ removeEventListener(type: string, listener: EventListener): void {
175
+ if (this.#listeners[type]) {
176
+ this.#listeners[type].delete(listener);
177
+ }
178
+ }
179
+
180
+ dispatchEvent(event: UniversalEvent): boolean {
181
+ this.#dispatchEvent(event.type, event);
182
+ return true;
183
+ }
184
+
185
+ // Internal method to dispatch events
186
+ #dispatchEvent(type: string, detail?: Record<string, any>): void {
187
+ // Create appropriate event object
188
+ let event: any;
189
+ if (type === "message") {
190
+ event = {
191
+ type: "message",
192
+ target: this,
193
+ data: detail?.data || "",
194
+ origin: "",
195
+ lastEventId: "",
196
+ };
197
+ } else if (type === "close") {
198
+ event = {
199
+ type: "close",
200
+ target: this,
201
+ code: detail?.code || 1000,
202
+ reason: detail?.reason || "",
203
+ wasClean: detail?.wasClean ?? true,
204
+ };
205
+ } else if (type === "error") {
206
+ event = {
207
+ type: "error",
208
+ target: this,
209
+ error: detail?.error,
210
+ };
211
+ } else {
212
+ event = {
213
+ type: type,
214
+ target: this,
215
+ };
216
+ }
217
+
218
+ // Call all listeners first
219
+ if (this.#listeners[type]) {
220
+ for (const listener of this.#listeners[type]) {
221
+ try {
222
+ listener.call(this, event);
223
+ } catch (err) {
224
+ logger().error({
225
+ msg: `error in ${type} event listener`,
226
+ error: err,
227
+ });
228
+ }
229
+ }
230
+ }
231
+
232
+ // Then call specific handler
233
+ switch (type) {
234
+ case "open":
235
+ if (this.onopen) {
236
+ try {
237
+ this.onopen.call(this as any, event);
238
+ } catch (err) {
239
+ logger().error({ msg: "error in onopen handler", error: err });
240
+ }
241
+ }
242
+ break;
243
+ case "message":
244
+ if (this.onmessage) {
245
+ try {
246
+ this.onmessage.call(this as any, event);
247
+ } catch (err) {
248
+ logger().error({ msg: "error in onmessage handler", error: err });
249
+ }
250
+ }
251
+ break;
252
+ case "error":
253
+ if (this.onerror) {
254
+ try {
255
+ this.onerror.call(this as any, event);
256
+ } catch (err) {
257
+ logger().error({ msg: "error in onerror handler", error: err });
258
+ }
259
+ }
260
+ break;
261
+ case "close":
262
+ // Note: EventSource doesn't have onclose in the standard API
263
+ // but we handle it here for consistency
264
+ break;
265
+ }
266
+ }
267
+ }
@@ -0,0 +1,454 @@
1
+ import { WSContext } from "hono/ws";
2
+ import type {
3
+ RivetCloseEvent,
4
+ RivetEvent,
5
+ RivetMessageEvent,
6
+ UniversalWebSocket,
7
+ } from "@/common/websocket-interface";
8
+ import { getLogger } from "./log";
9
+
10
+ export function logger() {
11
+ return getLogger("fake-event-source2");
12
+ }
13
+
14
+ // TODO: Merge with ConnectWebSocketOutput interface
15
+ export interface UpgradeWebSocketArgs {
16
+ onOpen: (event: any, ws: WSContext) => void;
17
+ onMessage: (event: any, ws: WSContext) => void;
18
+ onClose: (event: any, ws: WSContext) => void;
19
+ onError: (error: any, ws: WSContext) => void;
20
+ }
21
+
22
+ // TODO: Remove `2` suffix
23
+ /**
24
+ * InlineWebSocketAdapter implements a WebSocket-like interface
25
+ * that connects to a UpgradeWebSocketArgs handler
26
+ */
27
+ export class InlineWebSocketAdapter2 implements UniversalWebSocket {
28
+ // WebSocket readyState values
29
+ readonly CONNECTING = 0 as const;
30
+ readonly OPEN = 1 as const;
31
+ readonly CLOSING = 2 as const;
32
+ readonly CLOSED = 3 as const;
33
+
34
+ // Private properties
35
+ #handler: UpgradeWebSocketArgs;
36
+ #wsContext: WSContext;
37
+ #readyState: 0 | 1 | 2 | 3 = 0; // Start in CONNECTING state
38
+ #queuedMessages: Array<string | ArrayBuffer | Uint8Array> = [];
39
+ // Event buffering is needed since events can be fired
40
+ // before JavaScript has a chance to add event listeners (e.g. within the same tick)
41
+ #bufferedEvents: Array<{
42
+ type: string;
43
+ event: any;
44
+ }> = [];
45
+
46
+ // Event listeners with buffering
47
+ #eventListeners: Map<string, ((ev: any) => void)[]> = new Map();
48
+
49
+ constructor(handler: UpgradeWebSocketArgs) {
50
+ this.#handler = handler;
51
+
52
+ // Create a fake WSContext to pass to the handler
53
+ this.#wsContext = new WSContext({
54
+ raw: this,
55
+ send: (data: string | ArrayBuffer | Uint8Array) => {
56
+ logger().debug({ msg: "WSContext.send called" });
57
+ this.#handleMessage(data);
58
+ },
59
+ close: (code?: number, reason?: string) => {
60
+ logger().debug({ msg: "WSContext.close called", code, reason });
61
+ this.#handleClose(code || 1000, reason || "");
62
+ },
63
+ // Set readyState to 1 (OPEN) since handlers expect an open connection
64
+ readyState: 1,
65
+ });
66
+
67
+ // Initialize the connection
68
+ this.#initialize();
69
+ }
70
+
71
+ get readyState(): 0 | 1 | 2 | 3 {
72
+ return this.#readyState;
73
+ }
74
+
75
+ get binaryType(): "arraybuffer" | "blob" {
76
+ return "arraybuffer";
77
+ }
78
+
79
+ set binaryType(value: "arraybuffer" | "blob") {
80
+ // Ignored for now - always use arraybuffer
81
+ }
82
+
83
+ get bufferedAmount(): number {
84
+ return 0; // Not tracked in InlineWebSocketAdapter
85
+ }
86
+
87
+ get extensions(): string {
88
+ return ""; // Not available in InlineWebSocketAdapter
89
+ }
90
+
91
+ get protocol(): string {
92
+ return ""; // Not available in InlineWebSocketAdapter
93
+ }
94
+
95
+ get url(): string {
96
+ return ""; // Not available in InlineWebSocketAdapter
97
+ }
98
+
99
+ send(data: string | ArrayBufferLike | Blob | ArrayBufferView): void {
100
+ logger().debug({ msg: "send called", readyState: this.readyState });
101
+
102
+ if (this.readyState !== this.OPEN) {
103
+ const error = new Error("WebSocket is not open");
104
+ logger().warn({
105
+ msg: "cannot send message, websocket not open",
106
+ readyState: this.readyState,
107
+ dataType: typeof data,
108
+ dataLength: typeof data === "string" ? data.length : "binary",
109
+ error,
110
+ });
111
+ this.#fireError(error);
112
+ return;
113
+ }
114
+
115
+ this.#handler.onMessage({ data }, this.#wsContext);
116
+ }
117
+
118
+ /**
119
+ * Closes the connection
120
+ */
121
+ close(code = 1000, reason = ""): void {
122
+ if (this.readyState === this.CLOSED || this.readyState === this.CLOSING) {
123
+ return;
124
+ }
125
+
126
+ logger().debug({ msg: "closing fake websocket", code, reason });
127
+
128
+ this.#readyState = this.CLOSING;
129
+
130
+ // Call the handler's onClose method
131
+ try {
132
+ this.#handler.onClose({ code, reason, wasClean: true }, this.#wsContext);
133
+ } catch (err) {
134
+ logger().error({ msg: "error closing websocket", error: err });
135
+ } finally {
136
+ this.#readyState = this.CLOSED;
137
+
138
+ // Fire the close event
139
+ // Create a close event object since CloseEvent is not available in Node.js
140
+ const closeEvent = {
141
+ type: "close",
142
+ wasClean: code === 1000,
143
+ code,
144
+ reason,
145
+ target: this,
146
+ currentTarget: this,
147
+ } as unknown as RivetCloseEvent;
148
+
149
+ this.#fireClose(closeEvent);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Initialize the connection with the handler
155
+ */
156
+ async #initialize(): Promise<void> {
157
+ try {
158
+ logger().debug({ msg: "fake websocket initializing" });
159
+
160
+ // Call the handler's onOpen method
161
+ logger().debug({ msg: "calling handler.onOpen with WSContext" });
162
+ this.#handler.onOpen(undefined, this.#wsContext);
163
+
164
+ // Update the ready state and fire events
165
+ this.#readyState = this.OPEN;
166
+ logger().debug({ msg: "fake websocket initialized and now OPEN" });
167
+
168
+ // Fire the open event
169
+ this.#fireOpen();
170
+
171
+ // Delay processing queued messages slightly to allow event handlers to be set up
172
+ if (this.#queuedMessages.length > 0) {
173
+ if (this.readyState !== this.OPEN) {
174
+ logger().warn({
175
+ msg: "socket no longer open, dropping queued messages",
176
+ });
177
+ return;
178
+ }
179
+
180
+ logger().debug({
181
+ msg: `now processing ${this.#queuedMessages.length} queued messages`,
182
+ });
183
+
184
+ // Create a copy to avoid issues if new messages arrive during processing
185
+ const messagesToProcess = [...this.#queuedMessages];
186
+ this.#queuedMessages = [];
187
+
188
+ // Process each queued message
189
+ for (const message of messagesToProcess) {
190
+ logger().debug({ msg: "processing queued message" });
191
+ this.#handleMessage(message);
192
+ }
193
+ }
194
+ } catch (err) {
195
+ logger().error({
196
+ msg: "error opening fake websocket",
197
+ error: err,
198
+ errorMessage: err instanceof Error ? err.message : String(err),
199
+ stack: err instanceof Error ? err.stack : undefined,
200
+ });
201
+ this.#fireError(err);
202
+ this.close(1011, "Internal error during initialization");
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Handle messages received from the server via the WSContext
208
+ */
209
+ #handleMessage(data: string | ArrayBuffer | Uint8Array): void {
210
+ // Store messages that arrive before the socket is fully initialized
211
+ if (this.readyState !== this.OPEN) {
212
+ logger().debug({
213
+ msg: "message received before socket is OPEN, queuing",
214
+ readyState: this.readyState,
215
+ dataType: typeof data,
216
+ dataLength:
217
+ typeof data === "string"
218
+ ? data.length
219
+ : data instanceof ArrayBuffer
220
+ ? data.byteLength
221
+ : data instanceof Uint8Array
222
+ ? data.byteLength
223
+ : "unknown",
224
+ });
225
+
226
+ // Queue the message to be processed once the socket is open
227
+ this.#queuedMessages.push(data);
228
+ return;
229
+ }
230
+
231
+ // Log message received from server
232
+ logger().debug({
233
+ msg: "fake websocket received message from server",
234
+ dataType: typeof data,
235
+ dataLength:
236
+ typeof data === "string"
237
+ ? data.length
238
+ : data instanceof ArrayBuffer
239
+ ? data.byteLength
240
+ : data instanceof Uint8Array
241
+ ? data.byteLength
242
+ : "unknown",
243
+ });
244
+
245
+ // Create a MessageEvent-like object
246
+ const event = {
247
+ type: "message",
248
+ data,
249
+ target: this,
250
+ currentTarget: this,
251
+ } as unknown as RivetMessageEvent;
252
+
253
+ // Dispatch the event
254
+ this.#dispatchEvent("message", event);
255
+ }
256
+
257
+ #handleClose(code: number, reason: string): void {
258
+ if (this.readyState === this.CLOSED) return;
259
+
260
+ this.#readyState = this.CLOSED;
261
+
262
+ // Create a CloseEvent-like object
263
+ const event = {
264
+ type: "close",
265
+ code,
266
+ reason,
267
+ wasClean: code === 1000,
268
+ target: this,
269
+ currentTarget: this,
270
+ } as unknown as RivetCloseEvent;
271
+
272
+ // Dispatch the event
273
+ this.#dispatchEvent("close", event);
274
+ }
275
+
276
+ addEventListener(type: string, listener: (ev: any) => void): void {
277
+ if (!this.#eventListeners.has(type)) {
278
+ this.#eventListeners.set(type, []);
279
+ }
280
+ this.#eventListeners.get(type)!.push(listener);
281
+
282
+ // Flush any buffered events for this type
283
+ this.#flushBufferedEvents(type);
284
+ }
285
+
286
+ removeEventListener(type: string, listener: (ev: any) => void): void {
287
+ const listeners = this.#eventListeners.get(type);
288
+ if (listeners) {
289
+ const index = listeners.indexOf(listener);
290
+ if (index !== -1) {
291
+ listeners.splice(index, 1);
292
+ }
293
+ }
294
+ }
295
+
296
+ #dispatchEvent(type: string, event: any): void {
297
+ const listeners = this.#eventListeners.get(type);
298
+ if (listeners && listeners.length > 0) {
299
+ logger().debug(
300
+ `dispatching ${type} event to ${listeners.length} listeners`,
301
+ );
302
+ for (const listener of listeners) {
303
+ try {
304
+ listener(event);
305
+ } catch (err) {
306
+ logger().error({
307
+ msg: `error in ${type} event listener`,
308
+ error: err,
309
+ });
310
+ }
311
+ }
312
+ } else {
313
+ logger().debug({
314
+ msg: `no ${type} listeners registered, buffering event`,
315
+ });
316
+ this.#bufferedEvents.push({ type, event });
317
+ }
318
+
319
+ // Also check for on* properties
320
+ switch (type) {
321
+ case "open":
322
+ if (this.#onopen) {
323
+ try {
324
+ this.#onopen(event);
325
+ } catch (error) {
326
+ logger().error({ msg: "error in onopen handler", error });
327
+ }
328
+ }
329
+ break;
330
+ case "close":
331
+ if (this.#onclose) {
332
+ try {
333
+ this.#onclose(event);
334
+ } catch (error) {
335
+ logger().error({ msg: "error in onclose handler", error });
336
+ }
337
+ }
338
+ break;
339
+ case "error":
340
+ if (this.#onerror) {
341
+ try {
342
+ this.#onerror(event);
343
+ } catch (error) {
344
+ logger().error({ msg: "error in onerror handler", error });
345
+ }
346
+ }
347
+ break;
348
+ case "message":
349
+ if (this.#onmessage) {
350
+ try {
351
+ this.#onmessage(event);
352
+ } catch (error) {
353
+ logger().error({ msg: "error in onmessage handler", error });
354
+ }
355
+ }
356
+ break;
357
+ }
358
+ }
359
+
360
+ dispatchEvent(event: RivetEvent): boolean {
361
+ this.#dispatchEvent(event.type, event);
362
+ return true;
363
+ }
364
+
365
+ #flushBufferedEvents(type: string): void {
366
+ const eventsToFlush = this.#bufferedEvents.filter(
367
+ (buffered) => buffered.type === type,
368
+ );
369
+ this.#bufferedEvents = this.#bufferedEvents.filter(
370
+ (buffered) => buffered.type !== type,
371
+ );
372
+
373
+ for (const { event } of eventsToFlush) {
374
+ this.#dispatchEvent(type, event);
375
+ }
376
+ }
377
+
378
+ #fireOpen(): void {
379
+ try {
380
+ // Create an Event-like object since Event constructor may not be available
381
+ const event = {
382
+ type: "open",
383
+ target: this,
384
+ currentTarget: this,
385
+ } as unknown as RivetEvent;
386
+
387
+ this.#dispatchEvent("open", event);
388
+ } catch (err) {
389
+ logger().error({ msg: "error in open event", error: err });
390
+ }
391
+ }
392
+
393
+ #fireClose(event: RivetCloseEvent): void {
394
+ try {
395
+ this.#dispatchEvent("close", event);
396
+ } catch (err) {
397
+ logger().error({ msg: "error in close event", error: err });
398
+ }
399
+ }
400
+
401
+ #fireError(error: unknown): void {
402
+ try {
403
+ // Create an Event-like object for error
404
+ const event = {
405
+ type: "error",
406
+ target: this,
407
+ currentTarget: this,
408
+ error,
409
+ message: error instanceof Error ? error.message : String(error),
410
+ } as unknown as RivetEvent;
411
+
412
+ this.#dispatchEvent("error", event);
413
+ } catch (err) {
414
+ logger().error({ msg: "error in error event", error: err });
415
+ }
416
+
417
+ // Log the error
418
+ logger().error({ msg: "websocket error", error });
419
+ }
420
+
421
+ // Event handler properties with getters/setters
422
+ #onopen: ((event: RivetEvent) => void) | null = null;
423
+ #onclose: ((event: RivetCloseEvent) => void) | null = null;
424
+ #onerror: ((event: RivetEvent) => void) | null = null;
425
+ #onmessage: ((event: RivetMessageEvent) => void) | null = null;
426
+
427
+ get onopen(): ((event: RivetEvent) => void) | null {
428
+ return this.#onopen;
429
+ }
430
+ set onopen(handler: ((event: RivetEvent) => void) | null) {
431
+ this.#onopen = handler;
432
+ }
433
+
434
+ get onclose(): ((event: RivetCloseEvent) => void) | null {
435
+ return this.#onclose;
436
+ }
437
+ set onclose(handler: ((event: RivetCloseEvent) => void) | null) {
438
+ this.#onclose = handler;
439
+ }
440
+
441
+ get onerror(): ((event: RivetEvent) => void) | null {
442
+ return this.#onerror;
443
+ }
444
+ set onerror(handler: ((event: RivetEvent) => void) | null) {
445
+ this.#onerror = handler;
446
+ }
447
+
448
+ get onmessage(): ((event: RivetMessageEvent) => void) | null {
449
+ return this.#onmessage;
450
+ }
451
+ set onmessage(handler: ((event: RivetMessageEvent) => void) | null) {
452
+ this.#onmessage = handler;
453
+ }
454
+ }