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.
- package/README.md +3 -5
- package/dist/schemas/actor-persist/v1.ts +225 -0
- package/dist/schemas/client-protocol/v1.ts +435 -0
- package/dist/schemas/file-system-driver/v1.ts +102 -0
- package/dist/tsup/actor/errors.cjs +77 -0
- package/dist/tsup/actor/errors.cjs.map +1 -0
- package/dist/tsup/actor/errors.d.cts +156 -0
- package/dist/tsup/actor/errors.d.ts +156 -0
- package/dist/tsup/actor/errors.js +77 -0
- package/dist/tsup/actor/errors.js.map +1 -0
- package/dist/tsup/chunk-3F2YSRJL.js +117 -0
- package/dist/tsup/chunk-3F2YSRJL.js.map +1 -0
- package/dist/tsup/chunk-4CXBCT26.cjs +250 -0
- package/dist/tsup/chunk-4CXBCT26.cjs.map +1 -0
- package/dist/tsup/chunk-4R73YDN3.cjs +20 -0
- package/dist/tsup/chunk-4R73YDN3.cjs.map +1 -0
- package/dist/tsup/chunk-6LJT3QRL.cjs +539 -0
- package/dist/tsup/chunk-6LJT3QRL.cjs.map +1 -0
- package/dist/tsup/chunk-GICQ3YCU.cjs +1792 -0
- package/dist/tsup/chunk-GICQ3YCU.cjs.map +1 -0
- package/dist/tsup/chunk-H26RP6GD.js +251 -0
- package/dist/tsup/chunk-H26RP6GD.js.map +1 -0
- package/dist/tsup/chunk-HI3HWJRC.js +20 -0
- package/dist/tsup/chunk-HI3HWJRC.js.map +1 -0
- package/dist/tsup/chunk-HLLF4B4Q.js +1792 -0
- package/dist/tsup/chunk-HLLF4B4Q.js.map +1 -0
- package/dist/tsup/chunk-IH6CKNDW.cjs +117 -0
- package/dist/tsup/chunk-IH6CKNDW.cjs.map +1 -0
- package/dist/tsup/chunk-LV2S3OU3.js +250 -0
- package/dist/tsup/chunk-LV2S3OU3.js.map +1 -0
- package/dist/tsup/chunk-LWNKVZG5.cjs +251 -0
- package/dist/tsup/chunk-LWNKVZG5.cjs.map +1 -0
- package/dist/tsup/chunk-NFU2BBT5.js +374 -0
- package/dist/tsup/chunk-NFU2BBT5.js.map +1 -0
- package/dist/tsup/chunk-PQY7KKTL.js +539 -0
- package/dist/tsup/chunk-PQY7KKTL.js.map +1 -0
- package/dist/tsup/chunk-QK72M5JB.js +45 -0
- package/dist/tsup/chunk-QK72M5JB.js.map +1 -0
- package/dist/tsup/chunk-QNNXFOQV.cjs +45 -0
- package/dist/tsup/chunk-QNNXFOQV.cjs.map +1 -0
- package/dist/tsup/chunk-SBHHJ6QS.cjs +374 -0
- package/dist/tsup/chunk-SBHHJ6QS.cjs.map +1 -0
- package/dist/tsup/chunk-TQ62L3X7.js +325 -0
- package/dist/tsup/chunk-TQ62L3X7.js.map +1 -0
- package/dist/tsup/chunk-VO7ZRVVD.cjs +6293 -0
- package/dist/tsup/chunk-VO7ZRVVD.cjs.map +1 -0
- package/dist/tsup/chunk-WHBPJNGW.cjs +325 -0
- package/dist/tsup/chunk-WHBPJNGW.cjs.map +1 -0
- package/dist/tsup/chunk-XJQHKJ4P.js +6293 -0
- package/dist/tsup/chunk-XJQHKJ4P.js.map +1 -0
- package/dist/tsup/client/mod.cjs +32 -0
- package/dist/tsup/client/mod.cjs.map +1 -0
- package/dist/tsup/client/mod.d.cts +20 -0
- package/dist/tsup/client/mod.d.ts +20 -0
- package/dist/tsup/client/mod.js +32 -0
- package/dist/tsup/client/mod.js.map +1 -0
- package/dist/tsup/common/log.cjs +21 -0
- package/dist/tsup/common/log.cjs.map +1 -0
- package/dist/tsup/common/log.d.cts +26 -0
- package/dist/tsup/common/log.d.ts +26 -0
- package/dist/tsup/common/log.js +21 -0
- package/dist/tsup/common/log.js.map +1 -0
- package/dist/tsup/common/websocket.cjs +10 -0
- package/dist/tsup/common/websocket.cjs.map +1 -0
- package/dist/tsup/common/websocket.d.cts +3 -0
- package/dist/tsup/common/websocket.d.ts +3 -0
- package/dist/tsup/common/websocket.js +10 -0
- package/dist/tsup/common/websocket.js.map +1 -0
- package/dist/tsup/common-CXCe7s6i.d.cts +218 -0
- package/dist/tsup/common-CXCe7s6i.d.ts +218 -0
- package/dist/tsup/connection-BI-6UIBJ.d.ts +2087 -0
- package/dist/tsup/connection-Dyd4NLGW.d.cts +2087 -0
- package/dist/tsup/driver-helpers/mod.cjs +30 -0
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -0
- package/dist/tsup/driver-helpers/mod.d.cts +17 -0
- package/dist/tsup/driver-helpers/mod.d.ts +17 -0
- package/dist/tsup/driver-helpers/mod.js +30 -0
- package/dist/tsup/driver-helpers/mod.js.map +1 -0
- package/dist/tsup/driver-test-suite/mod.cjs +3411 -0
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -0
- package/dist/tsup/driver-test-suite/mod.d.cts +63 -0
- package/dist/tsup/driver-test-suite/mod.d.ts +63 -0
- package/dist/tsup/driver-test-suite/mod.js +3411 -0
- package/dist/tsup/driver-test-suite/mod.js.map +1 -0
- package/dist/tsup/inspector/mod.cjs +51 -0
- package/dist/tsup/inspector/mod.cjs.map +1 -0
- package/dist/tsup/inspector/mod.d.cts +408 -0
- package/dist/tsup/inspector/mod.d.ts +408 -0
- package/dist/tsup/inspector/mod.js +51 -0
- package/dist/tsup/inspector/mod.js.map +1 -0
- package/dist/tsup/mod.cjs +67 -0
- package/dist/tsup/mod.cjs.map +1 -0
- package/dist/tsup/mod.d.cts +105 -0
- package/dist/tsup/mod.d.ts +105 -0
- package/dist/tsup/mod.js +67 -0
- package/dist/tsup/mod.js.map +1 -0
- package/dist/tsup/router-endpoints-BTe_Rsdn.d.cts +65 -0
- package/dist/tsup/router-endpoints-CBSrKHmo.d.ts +65 -0
- package/dist/tsup/test/mod.cjs +17 -0
- package/dist/tsup/test/mod.cjs.map +1 -0
- package/dist/tsup/test/mod.d.cts +26 -0
- package/dist/tsup/test/mod.d.ts +26 -0
- package/dist/tsup/test/mod.js +17 -0
- package/dist/tsup/test/mod.js.map +1 -0
- package/dist/tsup/utils-fwx3o3K9.d.cts +18 -0
- package/dist/tsup/utils-fwx3o3K9.d.ts +18 -0
- package/dist/tsup/utils.cjs +26 -0
- package/dist/tsup/utils.cjs.map +1 -0
- package/dist/tsup/utils.d.cts +36 -0
- package/dist/tsup/utils.d.ts +36 -0
- package/dist/tsup/utils.js +26 -0
- package/dist/tsup/utils.js.map +1 -0
- package/package.json +208 -5
- package/src/actor/action.ts +178 -0
- package/src/actor/config.ts +497 -0
- package/src/actor/connection.ts +257 -0
- package/src/actor/context.ts +168 -0
- package/src/actor/database.ts +23 -0
- package/src/actor/definition.ts +82 -0
- package/src/actor/driver.ts +84 -0
- package/src/actor/errors.ts +422 -0
- package/src/actor/generic-conn-driver.ts +246 -0
- package/src/actor/instance.ts +1844 -0
- package/src/actor/keys.test.ts +266 -0
- package/src/actor/keys.ts +89 -0
- package/src/actor/log.ts +6 -0
- package/src/actor/mod.ts +108 -0
- package/src/actor/persisted.ts +42 -0
- package/src/actor/protocol/old.ts +297 -0
- package/src/actor/protocol/serde.ts +131 -0
- package/src/actor/router-endpoints.ts +688 -0
- package/src/actor/router.ts +265 -0
- package/src/actor/schedule.ts +17 -0
- package/src/actor/unstable-react.ts +110 -0
- package/src/actor/utils.ts +102 -0
- package/src/client/actor-common.ts +30 -0
- package/src/client/actor-conn.ts +865 -0
- package/src/client/actor-handle.ts +268 -0
- package/src/client/actor-query.ts +65 -0
- package/src/client/client.ts +554 -0
- package/src/client/config.ts +44 -0
- package/src/client/errors.ts +42 -0
- package/src/client/log.ts +5 -0
- package/src/client/mod.ts +60 -0
- package/src/client/raw-utils.ts +149 -0
- package/src/client/test.ts +44 -0
- package/src/client/utils.ts +152 -0
- package/src/common/eventsource-interface.ts +47 -0
- package/src/common/eventsource.ts +80 -0
- package/src/common/fake-event-source.ts +267 -0
- package/src/common/inline-websocket-adapter2.ts +454 -0
- package/src/common/log-levels.ts +27 -0
- package/src/common/log.ts +214 -0
- package/src/common/logfmt.ts +219 -0
- package/src/common/network.ts +2 -0
- package/src/common/router.ts +80 -0
- package/src/common/utils.ts +336 -0
- package/src/common/versioned-data.ts +95 -0
- package/src/common/websocket-interface.ts +49 -0
- package/src/common/websocket.ts +42 -0
- package/src/driver-helpers/mod.ts +22 -0
- package/src/driver-helpers/utils.ts +17 -0
- package/src/driver-test-suite/log.ts +5 -0
- package/src/driver-test-suite/mod.ts +239 -0
- package/src/driver-test-suite/tests/action-features.ts +136 -0
- package/src/driver-test-suite/tests/actor-conn-state.ts +249 -0
- package/src/driver-test-suite/tests/actor-conn.ts +349 -0
- package/src/driver-test-suite/tests/actor-driver.ts +25 -0
- package/src/driver-test-suite/tests/actor-error-handling.ts +158 -0
- package/src/driver-test-suite/tests/actor-handle.ts +292 -0
- package/src/driver-test-suite/tests/actor-inline-client.ts +152 -0
- package/src/driver-test-suite/tests/actor-inspector.ts +570 -0
- package/src/driver-test-suite/tests/actor-metadata.ts +116 -0
- package/src/driver-test-suite/tests/actor-onstatechange.ts +95 -0
- package/src/driver-test-suite/tests/actor-schedule.ts +108 -0
- package/src/driver-test-suite/tests/actor-sleep.ts +413 -0
- package/src/driver-test-suite/tests/actor-state.ts +54 -0
- package/src/driver-test-suite/tests/actor-vars.ts +93 -0
- package/src/driver-test-suite/tests/manager-driver.ts +367 -0
- package/src/driver-test-suite/tests/raw-http-direct-registry.ts +227 -0
- package/src/driver-test-suite/tests/raw-http-request-properties.ts +414 -0
- package/src/driver-test-suite/tests/raw-http.ts +347 -0
- package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +393 -0
- package/src/driver-test-suite/tests/raw-websocket.ts +484 -0
- package/src/driver-test-suite/tests/request-access.ts +230 -0
- package/src/driver-test-suite/utils.ts +71 -0
- package/src/drivers/default.ts +34 -0
- package/src/drivers/engine/actor-driver.ts +369 -0
- package/src/drivers/engine/config.ts +31 -0
- package/src/drivers/engine/kv.ts +3 -0
- package/src/drivers/engine/log.ts +5 -0
- package/src/drivers/engine/mod.ts +35 -0
- package/src/drivers/file-system/actor.ts +91 -0
- package/src/drivers/file-system/global-state.ts +686 -0
- package/src/drivers/file-system/log.ts +5 -0
- package/src/drivers/file-system/manager.ts +329 -0
- package/src/drivers/file-system/mod.ts +48 -0
- package/src/drivers/file-system/utils.ts +109 -0
- package/src/globals.d.ts +6 -0
- package/src/inspector/actor.ts +298 -0
- package/src/inspector/config.ts +88 -0
- package/src/inspector/log.ts +5 -0
- package/src/inspector/manager.ts +86 -0
- package/src/inspector/mod.ts +2 -0
- package/src/inspector/protocol/actor.ts +10 -0
- package/src/inspector/protocol/common.ts +196 -0
- package/src/inspector/protocol/manager.ts +10 -0
- package/src/inspector/protocol/mod.ts +2 -0
- package/src/inspector/utils.ts +76 -0
- package/src/manager/driver.ts +88 -0
- package/src/manager/hono-websocket-adapter.ts +342 -0
- package/src/manager/log.ts +5 -0
- package/src/manager/mod.ts +2 -0
- package/src/manager/protocol/mod.ts +24 -0
- package/src/manager/protocol/query.ts +89 -0
- package/src/manager/router.ts +412 -0
- package/src/manager-api/routes/actors-create.ts +16 -0
- package/src/manager-api/routes/actors-delete.ts +4 -0
- package/src/manager-api/routes/actors-get-by-id.ts +7 -0
- package/src/manager-api/routes/actors-get-or-create-by-id.ts +29 -0
- package/src/manager-api/routes/actors-get.ts +7 -0
- package/src/manager-api/routes/common.ts +18 -0
- package/src/mod.ts +18 -0
- package/src/registry/config.ts +32 -0
- package/src/registry/log.ts +5 -0
- package/src/registry/mod.ts +157 -0
- package/src/registry/run-config.ts +52 -0
- package/src/registry/serve.ts +52 -0
- package/src/remote-manager-driver/actor-http-client.ts +72 -0
- package/src/remote-manager-driver/actor-websocket-client.ts +63 -0
- package/src/remote-manager-driver/api-endpoints.ts +79 -0
- package/src/remote-manager-driver/api-utils.ts +43 -0
- package/src/remote-manager-driver/log.ts +5 -0
- package/src/remote-manager-driver/mod.ts +274 -0
- package/src/remote-manager-driver/ws-proxy.ts +180 -0
- package/src/schemas/actor-persist/mod.ts +1 -0
- package/src/schemas/actor-persist/versioned.ts +25 -0
- package/src/schemas/client-protocol/mod.ts +1 -0
- package/src/schemas/client-protocol/versioned.ts +63 -0
- package/src/schemas/file-system-driver/mod.ts +1 -0
- package/src/schemas/file-system-driver/versioned.ts +28 -0
- package/src/serde.ts +90 -0
- package/src/test/config.ts +16 -0
- package/src/test/log.ts +5 -0
- package/src/test/mod.ts +154 -0
- 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
|
+
}
|