sonamu 0.11.15 → 0.12.0

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 (71) hide show
  1. package/dist/api/sonamu.d.ts.map +1 -1
  2. package/dist/api/sonamu.js +12 -7
  3. package/dist/bin/build-config.d.ts +3 -1
  4. package/dist/bin/build-config.d.ts.map +1 -1
  5. package/dist/bin/build-config.js +17 -6
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +4 -2
  9. package/dist/stream/__tests__/ws-test-helpers.d.ts +108 -0
  10. package/dist/stream/__tests__/ws-test-helpers.d.ts.map +1 -0
  11. package/dist/stream/__tests__/ws-test-helpers.js +260 -0
  12. package/dist/stream/index.d.ts +1 -0
  13. package/dist/stream/index.d.ts.map +1 -1
  14. package/dist/stream/index.js +4 -2
  15. package/dist/stream/ws-audience-resolver.d.ts +8 -5
  16. package/dist/stream/ws-audience-resolver.d.ts.map +1 -1
  17. package/dist/stream/ws-audience-resolver.js +16 -13
  18. package/dist/stream/ws-cluster-bus.d.ts +9 -4
  19. package/dist/stream/ws-cluster-bus.d.ts.map +1 -1
  20. package/dist/stream/ws-cluster-bus.js +1 -1
  21. package/dist/stream/ws-core.d.ts +10 -0
  22. package/dist/stream/ws-core.d.ts.map +1 -1
  23. package/dist/stream/ws-delivery.d.ts +30 -4
  24. package/dist/stream/ws-delivery.d.ts.map +1 -1
  25. package/dist/stream/ws-delivery.js +199 -52
  26. package/dist/stream/ws-frame.d.ts +15 -0
  27. package/dist/stream/ws-frame.d.ts.map +1 -0
  28. package/dist/stream/ws-frame.js +29 -0
  29. package/dist/stream/ws-local-connection-store.d.ts +4 -4
  30. package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
  31. package/dist/stream/ws-local-connection-store.js +2 -2
  32. package/dist/stream/ws-presence-index.d.ts +36 -0
  33. package/dist/stream/ws-presence-index.d.ts.map +1 -0
  34. package/dist/stream/ws-presence-index.js +177 -0
  35. package/dist/stream/ws-presence-store.d.ts +39 -39
  36. package/dist/stream/ws-presence-store.d.ts.map +1 -1
  37. package/dist/stream/ws-presence-store.js +22 -152
  38. package/dist/stream/ws-registry.d.ts +45 -17
  39. package/dist/stream/ws-registry.d.ts.map +1 -1
  40. package/dist/stream/ws-registry.js +169 -60
  41. package/dist/stream/ws-session-id.d.ts +14 -0
  42. package/dist/stream/ws-session-id.d.ts.map +1 -0
  43. package/dist/stream/ws-session-id.js +37 -0
  44. package/dist/stream/ws.d.ts +20 -10
  45. package/dist/stream/ws.d.ts.map +1 -1
  46. package/dist/stream/ws.js +59 -36
  47. package/package.json +1 -1
  48. package/src/api/__tests__/sonamu.websocket-routes.test.ts +234 -0
  49. package/src/api/__tests__/sonamu.websocket.test.ts +241 -20
  50. package/src/api/sonamu.ts +20 -7
  51. package/src/bin/__tests__/build-web-command.test.ts +57 -0
  52. package/src/bin/__tests__/zod-compiler-build.test.ts +5 -3
  53. package/src/bin/build-config.ts +19 -7
  54. package/src/index.ts +1 -0
  55. package/src/stream/__tests__/ws-contracts.test.ts +483 -265
  56. package/src/stream/__tests__/ws-multinode.test.ts +326 -0
  57. package/src/stream/__tests__/ws-registry-async.test.ts +855 -0
  58. package/src/stream/__tests__/ws-test-helpers.ts +360 -0
  59. package/src/stream/__tests__/ws.test.ts +342 -145
  60. package/src/stream/index.ts +1 -0
  61. package/src/stream/ws-audience-resolver.ts +21 -19
  62. package/src/stream/ws-cluster-bus.ts +12 -4
  63. package/src/stream/ws-core.ts +14 -0
  64. package/src/stream/ws-delivery.ts +321 -97
  65. package/src/stream/ws-frame.ts +39 -0
  66. package/src/stream/ws-local-connection-store.ts +10 -9
  67. package/src/stream/ws-presence-index.ts +273 -0
  68. package/src/stream/ws-presence-store.ts +64 -284
  69. package/src/stream/ws-registry.ts +272 -85
  70. package/src/stream/ws-session-id.ts +39 -0
  71. package/src/stream/ws.ts +91 -50
@@ -0,0 +1,260 @@
1
+ import { InMemoryWebSocketPresenceStore, init_ws_presence_store } from "../ws-presence-store.js";
2
+ import { EventEmitter } from "node:events";
3
+ //#region src/stream/__tests__/ws-test-helpers.ts
4
+ init_ws_presence_store();
5
+ async function flushAsync(rounds = 5) {
6
+ for (let index = 0; index < rounds; index += 1) await new Promise((resolve) => {
7
+ setImmediate(resolve);
8
+ });
9
+ }
10
+ async function flushUntil(predicate, maxRounds = 50) {
11
+ for (let index = 0; index < maxRounds && !predicate(); index += 1) await flushAsync(1);
12
+ }
13
+ function createDeferred() {
14
+ let resolve;
15
+ let reject;
16
+ return {
17
+ promise: new Promise((innerResolve, innerReject) => {
18
+ resolve = innerResolve;
19
+ reject = innerReject;
20
+ }),
21
+ resolve,
22
+ reject
23
+ };
24
+ }
25
+ async function isSettled(promise) {
26
+ let settled = false;
27
+ promise.then(() => {
28
+ settled = true;
29
+ }, () => {
30
+ settled = true;
31
+ });
32
+ await flushAsync(2);
33
+ return settled;
34
+ }
35
+ var FakeWebSocket = class extends EventEmitter {
36
+ readyState = 1;
37
+ bufferedAmount = 0;
38
+ sentMessages = [];
39
+ pingCount = 0;
40
+ closedWith = null;
41
+ send(data) {
42
+ this.sentMessages.push(data);
43
+ }
44
+ close(code, reason) {
45
+ this.closedWith = {
46
+ code,
47
+ reason
48
+ };
49
+ this.readyState = 3;
50
+ this.emit("close", code ?? 1e3, Buffer.from(reason ?? "", "utf-8"));
51
+ }
52
+ ping() {
53
+ this.pingCount += 1;
54
+ }
55
+ terminate() {
56
+ this.readyState = 3;
57
+ this.emit("close", 1006, Buffer.alloc(0));
58
+ }
59
+ };
60
+ function asWebSocket(socket) {
61
+ return {
62
+ get bufferedAmount() {
63
+ return socket.bufferedAmount;
64
+ },
65
+ get readyState() {
66
+ return socket.readyState;
67
+ },
68
+ close(code, reason) {
69
+ socket.close(code, reason?.toString());
70
+ },
71
+ off(...args) {
72
+ socket.off(...args);
73
+ },
74
+ on(...args) {
75
+ socket.on(...args);
76
+ },
77
+ ping() {
78
+ socket.ping();
79
+ },
80
+ send(data) {
81
+ socket.send(data.toString());
82
+ },
83
+ terminate() {
84
+ socket.terminate();
85
+ }
86
+ };
87
+ }
88
+ function parseTestJson(raw) {
89
+ try {
90
+ return JSON.parse(raw);
91
+ } catch (error) {
92
+ throw new Error(`테스트 JSON 파싱 실패: ${raw}`, { cause: error });
93
+ }
94
+ }
95
+ function parseFrames(socket) {
96
+ return socket.sentMessages.map((message) => parseTestJson(message));
97
+ }
98
+ /**
99
+ * InMemoryWebSocketPresenceStore를 감싸 호출 순서를 기록하고, 메서드별로 다음 호출의 동작을 스크립트할 수 있는 store.
100
+ * 호출 기록은 store가 호출을 "받은" 시점에 남긴다.
101
+ */
102
+ var ScriptedPresenceStore = class {
103
+ calls = [];
104
+ inner;
105
+ nodeId;
106
+ interceptors = /* @__PURE__ */ new Map();
107
+ constructor(inner = new InMemoryWebSocketPresenceStore()) {
108
+ this.inner = inner;
109
+ }
110
+ next(method, interceptor) {
111
+ const queue = this.interceptors.get(method) ?? [];
112
+ queue.push(interceptor);
113
+ this.interceptors.set(method, queue);
114
+ return this;
115
+ }
116
+ always(method, interceptor) {
117
+ for (let index = 0; index < 1e3; index += 1) this.next(method, interceptor);
118
+ return this;
119
+ }
120
+ register(input) {
121
+ this.calls.push(`register:${input.sessionId}`);
122
+ return this.run("register", () => this.inner.register(input));
123
+ }
124
+ activate(sessionId) {
125
+ this.calls.push(`activate:${sessionId}`);
126
+ return this.run("activate", () => this.inner.activate(sessionId));
127
+ }
128
+ unregister(sessionId) {
129
+ this.calls.push(`unregister:${sessionId}`);
130
+ return this.run("unregister", () => this.inner.unregister(sessionId));
131
+ }
132
+ setUserId(sessionId, userId) {
133
+ this.calls.push(`setUserId:${sessionId}:${JSON.stringify(userId)}`);
134
+ return this.run("setUserId", () => this.inner.setUserId(sessionId, userId));
135
+ }
136
+ clearUserId(sessionId) {
137
+ this.calls.push(`clearUserId:${sessionId}`);
138
+ return this.run("clearUserId", () => this.inner.clearUserId(sessionId));
139
+ }
140
+ join(sessionId, roomId) {
141
+ this.calls.push(`join:${sessionId}:${roomId}`);
142
+ return this.run("join", () => this.inner.join(sessionId, roomId));
143
+ }
144
+ leave(sessionId, roomId) {
145
+ this.calls.push(`leave:${sessionId}:${roomId}`);
146
+ return this.run("leave", () => this.inner.leave(sessionId, roomId));
147
+ }
148
+ resolveRoute(audience, nodeId) {
149
+ this.calls.push(`resolveRoute:${nodeId}:${audience.type}`);
150
+ return this.run("resolveRoute", () => this.inner.resolveRoute(audience, nodeId));
151
+ }
152
+ getRoomMemberCount(roomId, namespace) {
153
+ this.calls.push(`getRoomMemberCount:${roomId}`);
154
+ return this.run("getRoomMemberCount", () => this.inner.getRoomMemberCount(roomId, namespace));
155
+ }
156
+ shutdown() {
157
+ this.calls.push("shutdown");
158
+ return this.run("shutdown", () => void 0);
159
+ }
160
+ callsOf(prefix) {
161
+ return this.calls.filter((call) => call.startsWith(prefix));
162
+ }
163
+ run(method, delegate) {
164
+ const interceptor = this.interceptors.get(method)?.shift();
165
+ if (!interceptor) return delegate();
166
+ const result = interceptor();
167
+ if (result instanceof Promise) return result.then(() => delegate());
168
+ return delegate();
169
+ }
170
+ };
171
+ /**
172
+ * 한 프로세스 안에서 targetNodeIds 기준으로 envelope을 라우팅하는 fake cluster 네트워크.
173
+ * 전송 구간을 흉내내기 위해 envelope을 JSON 직렬화/역직렬화한 뒤 다음 tick에 전달한다.
174
+ */
175
+ var InProcessClusterNetwork = class {
176
+ published = [];
177
+ handlers = /* @__PURE__ */ new Map();
178
+ bus(nodeId) {
179
+ return new InProcessClusterBus(nodeId, this);
180
+ }
181
+ subscribe(nodeId, handler) {
182
+ const handlers = this.handlers.get(nodeId) ?? /* @__PURE__ */ new Set();
183
+ handlers.add(handler);
184
+ this.handlers.set(nodeId, handlers);
185
+ return () => {
186
+ handlers.delete(handler);
187
+ };
188
+ }
189
+ deliver(envelope) {
190
+ const wire = JSON.stringify(envelope);
191
+ this.published.push(parseTestJson(wire));
192
+ setImmediate(() => {
193
+ for (const targetNodeId of envelope.targetNodeIds ?? []) for (const handler of this.handlers.get(targetNodeId) ?? []) {
194
+ const received = parseTestJson(wire);
195
+ Promise.resolve(handler(received)).catch(() => {});
196
+ }
197
+ });
198
+ }
199
+ };
200
+ var InProcessClusterBus = class {
201
+ nodeId;
202
+ network;
203
+ shutdownCalls = 0;
204
+ unsubscribeCalls = 0;
205
+ constructor(nodeId, network) {
206
+ this.nodeId = nodeId;
207
+ this.network = network;
208
+ }
209
+ publish(envelope) {
210
+ this.network.deliver(envelope);
211
+ }
212
+ async subscribe(handler) {
213
+ const unsubscribe = this.network.subscribe(this.nodeId, handler);
214
+ return () => {
215
+ this.unsubscribeCalls += 1;
216
+ unsubscribe();
217
+ };
218
+ }
219
+ shutdown() {
220
+ this.shutdownCalls += 1;
221
+ }
222
+ };
223
+ var RecordingClusterBus = class {
224
+ published = [];
225
+ handlers = /* @__PURE__ */ new Set();
226
+ shutdownCalls = 0;
227
+ unsubscribeCalls = 0;
228
+ nodeId;
229
+ publish(envelope) {
230
+ this.published.push(envelope);
231
+ }
232
+ subscribe(handler) {
233
+ this.handlers.add(handler);
234
+ return () => {
235
+ this.unsubscribeCalls += 1;
236
+ this.handlers.delete(handler);
237
+ };
238
+ }
239
+ async emit(envelope) {
240
+ for (const handler of this.handlers) await handler(envelope);
241
+ }
242
+ shutdown() {
243
+ this.shutdownCalls += 1;
244
+ }
245
+ };
246
+ function collectUnhandledRejections() {
247
+ const collected = [];
248
+ const listener = (reason) => {
249
+ collected.push(reason);
250
+ };
251
+ process.on("unhandledRejection", listener);
252
+ return () => {
253
+ process.off("unhandledRejection", listener);
254
+ return collected;
255
+ };
256
+ }
257
+ //#endregion
258
+ export { FakeWebSocket, InProcessClusterBus, InProcessClusterNetwork, RecordingClusterBus, ScriptedPresenceStore, asWebSocket, collectUnhandledRejections, createDeferred, flushAsync, flushUntil, isSettled, parseFrames };
259
+
260
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtdGVzdC1oZWxwZXJzLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zdHJlYW0vX190ZXN0c19fL3dzLXRlc3QtaGVscGVycy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tIFwibm9kZTpldmVudHNcIjtcblxuaW1wb3J0IHsgdHlwZSBXZWJTb2NrZXRUcmFuc3BvcnQgfSBmcm9tIFwiLi4vd3NcIjtcbmltcG9ydCB7IHR5cGUgV2ViU29ja2V0QXVkaWVuY2UgfSBmcm9tIFwiLi4vd3MtYXVkaWVuY2VcIjtcbmltcG9ydCB7XG4gIHR5cGUgV2ViU29ja2V0Q2x1c3RlckJ1cyxcbiAgdHlwZSBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGUsXG4gIHR5cGUgV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlSGFuZGxlcixcbn0gZnJvbSBcIi4uL3dzLWNsdXN0ZXItYnVzXCI7XG5pbXBvcnQgeyB0eXBlIFdlYlNvY2tldFJvb21JZCwgdHlwZSBXZWJTb2NrZXRVc2VySWQgfSBmcm9tIFwiLi4vd3MtY29yZVwiO1xuaW1wb3J0IHsgSW5NZW1vcnlXZWJTb2NrZXRQcmVzZW5jZVN0b3JlIH0gZnJvbSBcIi4uL3dzLXByZXNlbmNlLXN0b3JlXCI7XG5cbi8vIHNldEltbWVkaWF0ZSDri6jsnITroZwg7YGQ66W8IOu5hOybjCBmYW4tb3V0L291dGJvdW5kIGZsdXNoL2NsdXN0ZXIg7KCE64us7J20IOuBneuCmOq4sOulvCDquLDri6TrprDri6RcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmbHVzaEFzeW5jKHJvdW5kczogbnVtYmVyID0gNSk6IFByb21pc2U8dm9pZD4ge1xuICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgcm91bmRzOyBpbmRleCArPSAxKSB7XG4gICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcbiAgICAgIHNldEltbWVkaWF0ZShyZXNvbHZlKTtcbiAgICB9KTtcbiAgfVxufVxuXG4vLyDsobDqsbTsnbQg66eM7KGx65CgIOuVjOq5jOyngCBzZXRJbW1lZGlhdGUg64uo7JyE66GcIOq4sOuLpOumsOuLpC4g64Gd64K0IOunjOyhse2VmOyngCDslYrsnLzrqbQg6re464yA66GcIOuwmO2ZmO2VtCBhc3NlcnRpb27sl5DshJwg7Iuk7Yyo7ZWY6rKMIO2VnOuLpFxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZsdXNoVW50aWwocHJlZGljYXRlOiAoKSA9PiBib29sZWFuLCBtYXhSb3VuZHM6IG51bWJlciA9IDUwKTogUHJvbWlzZTx2b2lkPiB7XG4gIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCBtYXhSb3VuZHMgJiYgIXByZWRpY2F0ZSgpOyBpbmRleCArPSAxKSB7XG4gICAgYXdhaXQgZmx1c2hBc3luYygxKTtcbiAgfVxufVxuXG5leHBvcnQgdHlwZSBEZWZlcnJlZDxUPiA9IHtcbiAgcHJvbWlzZTogUHJvbWlzZTxUPjtcbiAgcmVzb2x2ZTogKHZhbHVlOiBUKSA9PiB2b2lkO1xuICByZWplY3Q6IChlcnJvcjogRXJyb3IpID0+IHZvaWQ7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRGVmZXJyZWQ8VCA9IHZvaWQ+KCk6IERlZmVycmVkPFQ+IHtcbiAgbGV0IHJlc29sdmUhOiAodmFsdWU6IFQpID0+IHZvaWQ7XG4gIGxldCByZWplY3QhOiAoZXJyb3I6IEVycm9yKSA9PiB2b2lkO1xuICBjb25zdCBwcm9taXNlID0gbmV3IFByb21pc2U8VD4oKGlubmVyUmVzb2x2ZSwgaW5uZXJSZWplY3QpID0+IHtcbiAgICByZXNvbHZlID0gaW5uZXJSZXNvbHZlO1xuICAgIHJlamVjdCA9IGlubmVyUmVqZWN0O1xuICB9KTtcbiAgcmV0dXJuIHsgcHJvbWlzZSwgcmVzb2x2ZSwgcmVqZWN0IH07XG59XG5cbi8vIHByb21pc2XqsIAg7JWE7KeBIHNldHRsZeuQmOyngCDslYrslZjripTsp4Ag7ZmV7J247ZWc64ukXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaXNTZXR0bGVkPFQ+KHByb21pc2U6IFByb21pc2U8VD4pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgbGV0IHNldHRsZWQgPSBmYWxzZTtcbiAgcHJvbWlzZS50aGVuKFxuICAgICgpID0+IHtcbiAgICAgIHNldHRsZWQgPSB0cnVlO1xuICAgIH0sXG4gICAgKCkgPT4ge1xuICAgICAgc2V0dGxlZCA9IHRydWU7XG4gICAgfSxcbiAgKTtcbiAgYXdhaXQgZmx1c2hBc3luYygyKTtcbiAgcmV0dXJuIHNldHRsZWQ7XG59XG5cbmV4cG9ydCBjbGFzcyBGYWtlV2ViU29ja2V0IGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcbiAgcmVhZHlTdGF0ZTogMCB8IDEgfCAyIHwgMyA9IDE7XG4gIGJ1ZmZlcmVkQW1vdW50ID0gMDtcbiAgc2VudE1lc3NhZ2VzOiBzdHJpbmdbXSA9IFtdO1xuICBwaW5nQ291bnQgPSAwO1xuICBjbG9zZWRXaXRoOiB7IGNvZGU/OiBudW1iZXI7IHJlYXNvbj86IHN0cmluZyB9IHwgbnVsbCA9IG51bGw7XG5cbiAgc2VuZChkYXRhOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLnNlbnRNZXNzYWdlcy5wdXNoKGRhdGEpO1xuICB9XG5cbiAgY2xvc2UoY29kZT86IG51bWJlciwgcmVhc29uPzogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5jbG9zZWRXaXRoID0geyBjb2RlLCByZWFzb24gfTtcbiAgICB0aGlzLnJlYWR5U3RhdGUgPSAzO1xuICAgIHRoaXMuZW1pdChcImNsb3NlXCIsIGNvZGUgPz8gMTAwMCwgQnVmZmVyLmZyb20ocmVhc29uID8/IFwiXCIsIFwidXRmLThcIikpO1xuICB9XG5cbiAgcGluZygpOiB2b2lkIHtcbiAgICB0aGlzLnBpbmdDb3VudCArPSAxO1xuICB9XG5cbiAgdGVybWluYXRlKCk6IHZvaWQge1xuICAgIHRoaXMucmVhZHlTdGF0ZSA9IDM7XG4gICAgdGhpcy5lbWl0KFwiY2xvc2VcIiwgMTAwNiwgQnVmZmVyLmFsbG9jKDApKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gYXNXZWJTb2NrZXQoc29ja2V0OiBGYWtlV2ViU29ja2V0KTogV2ViU29ja2V0VHJhbnNwb3J0IHtcbiAgcmV0dXJuIHtcbiAgICBnZXQgYnVmZmVyZWRBbW91bnQoKSB7XG4gICAgICByZXR1cm4gc29ja2V0LmJ1ZmZlcmVkQW1vdW50O1xuICAgIH0sXG4gICAgZ2V0IHJlYWR5U3RhdGUoKSB7XG4gICAgICByZXR1cm4gc29ja2V0LnJlYWR5U3RhdGU7XG4gICAgfSxcbiAgICBjbG9zZShjb2RlLCByZWFzb24pIHtcbiAgICAgIHNvY2tldC5jbG9zZShjb2RlLCByZWFzb24/LnRvU3RyaW5nKCkpO1xuICAgIH0sXG4gICAgb2ZmKC4uLmFyZ3MpIHtcbiAgICAgIHNvY2tldC5vZmYoLi4uYXJncyk7XG4gICAgfSxcbiAgICBvbiguLi5hcmdzKSB7XG4gICAgICBzb2NrZXQub24oLi4uYXJncyk7XG4gICAgfSxcbiAgICBwaW5nKCkge1xuICAgICAgc29ja2V0LnBpbmcoKTtcbiAgICB9LFxuICAgIHNlbmQoZGF0YSkge1xuICAgICAgc29ja2V0LnNlbmQoZGF0YS50b1N0cmluZygpKTtcbiAgICB9LFxuICAgIHRlcm1pbmF0ZSgpIHtcbiAgICAgIHNvY2tldC50ZXJtaW5hdGUoKTtcbiAgICB9LFxuICB9O1xufVxuXG4vLyDthYzsiqTtirgg642w7J207YSwIOyngeugrO2ZlCDqsrDqs7zrpbwg65CY64+M66aw64ukLiDquajsp4QgSlNPTuydtOuptCDsm5DrrLjsnYQg7Y+s7ZWo7ZWcIOyXkOufrOuhnCDthYzsiqTtirjrpbwg7Iuk7Yyo7Iuc7YKo64ukXG5mdW5jdGlvbiBwYXJzZVRlc3RKc29uPFQ+KHJhdzogc3RyaW5nKTogVCB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIC8qIFNBRkVUWTog7YWM7Iqk7Yq46rCAIOyngeygkSDsp4HroKztmZTtlojqsbDrgpggcnVudGltZeydtCDsp4HroKztmZTtlZwg6rCS66eMIOuEmOq4tOuLpC4gKi8gSlNPTi5wYXJzZShcbiAgICAgIHJhdyxcbiAgICApIGFzIFQ7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGDthYzsiqTtirggSlNPTiDtjIzsi7Eg7Iuk7YyoOiAke3Jhd31gLCB7IGNhdXNlOiBlcnJvciB9KTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VGcmFtZXMoc29ja2V0OiBGYWtlV2ViU29ja2V0KTogQXJyYXk8eyBldmVudDogc3RyaW5nOyBkYXRhOiBvYmplY3QgfT4ge1xuICByZXR1cm4gc29ja2V0LnNlbnRNZXNzYWdlcy5tYXAoKG1lc3NhZ2UpID0+XG4gICAgcGFyc2VUZXN0SnNvbjx7IGV2ZW50OiBzdHJpbmc7IGRhdGE6IG9iamVjdCB9PihtZXNzYWdlKSxcbiAgKTtcbn1cblxudHlwZSBTY3JpcHRlZE1ldGhvZCA9XG4gIHwgXCJyZWdpc3RlclwiXG4gIHwgXCJhY3RpdmF0ZVwiXG4gIHwgXCJ1bnJlZ2lzdGVyXCJcbiAgfCBcInNldFVzZXJJZFwiXG4gIHwgXCJjbGVhclVzZXJJZFwiXG4gIHwgXCJqb2luXCJcbiAgfCBcImxlYXZlXCJcbiAgfCBcInJlc29sdmVSb3V0ZVwiXG4gIHwgXCJnZXRSb29tTWVtYmVyQ291bnRcIlxuICB8IFwic2h1dGRvd25cIjtcblxuLy8g64uk7J2MIO2YuOy2nCDsp4HsoITsl5Ag64G87JuM64Sj7J2EIOuPmeyekS4g64+Z6riwIHRocm93LCByZWplY3QsIOyngOyXsOuQnCBwcm9taXNl66W8IOq3uOuMgOuhnCDtnYnrgrTrgrjri6RcbnR5cGUgU2NyaXB0ZWRJbnRlcmNlcHRvciA9ICgpID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+O1xuXG4vKipcbiAqIEluTWVtb3J5V2ViU29ja2V0UHJlc2VuY2VTdG9yZeulvCDqsJDsi7gg7Zi47LacIOyInOyEnOulvCDquLDroZ3tlZjqs6AsIOuplOyEnOuTnOuzhOuhnCDri6TsnYwg7Zi47Lac7J2YIOuPmeyekeydhCDsiqTtgazrpr3tirjtlaAg7IiYIOyeiOuKlCBzdG9yZS5cbiAqIO2YuOy2nCDquLDroZ3snYAgc3RvcmXqsIAg7Zi47Lac7J2EIFwi67Cb7J2AXCIg7Iuc7KCQ7JeQIOuCqOq4tOuLpC5cbiAqL1xuZXhwb3J0IGNsYXNzIFNjcmlwdGVkUHJlc2VuY2VTdG9yZSB7XG4gIHJlYWRvbmx5IGNhbGxzOiBzdHJpbmdbXSA9IFtdO1xuICByZWFkb25seSBpbm5lcjogSW5NZW1vcnlXZWJTb2NrZXRQcmVzZW5jZVN0b3JlO1xuICBub2RlSWQ/OiBzdHJpbmc7XG4gIHByaXZhdGUgcmVhZG9ubHkgaW50ZXJjZXB0b3JzID0gbmV3IE1hcDxTY3JpcHRlZE1ldGhvZCwgU2NyaXB0ZWRJbnRlcmNlcHRvcltdPigpO1xuXG4gIGNvbnN0cnVjdG9yKGlubmVyOiBJbk1lbW9yeVdlYlNvY2tldFByZXNlbmNlU3RvcmUgPSBuZXcgSW5NZW1vcnlXZWJTb2NrZXRQcmVzZW5jZVN0b3JlKCkpIHtcbiAgICB0aGlzLmlubmVyID0gaW5uZXI7XG4gIH1cblxuICBuZXh0KG1ldGhvZDogU2NyaXB0ZWRNZXRob2QsIGludGVyY2VwdG9yOiBTY3JpcHRlZEludGVyY2VwdG9yKTogdGhpcyB7XG4gICAgY29uc3QgcXVldWUgPSB0aGlzLmludGVyY2VwdG9ycy5nZXQobWV0aG9kKSA/PyBbXTtcbiAgICBxdWV1ZS5wdXNoKGludGVyY2VwdG9yKTtcbiAgICB0aGlzLmludGVyY2VwdG9ycy5zZXQobWV0aG9kLCBxdWV1ZSk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvLyDsnbTtm4Qg66qo65OgIO2YuOy2nOyXkCDqsJnsnYAg64+Z7J6R7J2EIOyggeyaqe2VnOuLpFxuICBhbHdheXMobWV0aG9kOiBTY3JpcHRlZE1ldGhvZCwgaW50ZXJjZXB0b3I6IFNjcmlwdGVkSW50ZXJjZXB0b3IpOiB0aGlzIHtcbiAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgMV8wMDA7IGluZGV4ICs9IDEpIHtcbiAgICAgIHRoaXMubmV4dChtZXRob2QsIGludGVyY2VwdG9yKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICByZWdpc3RlcihpbnB1dDoge1xuICAgIHNlc3Npb25JZDogc3RyaW5nO1xuICAgIG5vZGVJZDogc3RyaW5nO1xuICAgIG5hbWVzcGFjZTogc3RyaW5nO1xuICAgIGFjdGl2ZTogYm9vbGVhbjtcbiAgICBjb25uZWN0ZWRBdDogRGF0ZTtcbiAgfSk6IHZvaWQgfCBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNhbGxzLnB1c2goYHJlZ2lzdGVyOiR7aW5wdXQuc2Vzc2lvbklkfWApO1xuICAgIHJldHVybiB0aGlzLnJ1bihcInJlZ2lzdGVyXCIsICgpID0+IHRoaXMuaW5uZXIucmVnaXN0ZXIoaW5wdXQpKTtcbiAgfVxuXG4gIGFjdGl2YXRlKHNlc3Npb25JZDogc3RyaW5nKTogdm9pZCB8IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuY2FsbHMucHVzaChgYWN0aXZhdGU6JHtzZXNzaW9uSWR9YCk7XG4gICAgcmV0dXJuIHRoaXMucnVuKFwiYWN0aXZhdGVcIiwgKCkgPT4gdGhpcy5pbm5lci5hY3RpdmF0ZShzZXNzaW9uSWQpKTtcbiAgfVxuXG4gIHVucmVnaXN0ZXIoc2Vzc2lvbklkOiBzdHJpbmcpOiB2b2lkIHwgUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5jYWxscy5wdXNoKGB1bnJlZ2lzdGVyOiR7c2Vzc2lvbklkfWApO1xuICAgIHJldHVybiB0aGlzLnJ1bihcInVucmVnaXN0ZXJcIiwgKCkgPT4gdGhpcy5pbm5lci51bnJlZ2lzdGVyKHNlc3Npb25JZCkpO1xuICB9XG5cbiAgc2V0VXNlcklkKHNlc3Npb25JZDogc3RyaW5nLCB1c2VySWQ6IFdlYlNvY2tldFVzZXJJZCk6IHZvaWQgfCBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNhbGxzLnB1c2goYHNldFVzZXJJZDoke3Nlc3Npb25JZH06JHtKU09OLnN0cmluZ2lmeSh1c2VySWQpfWApO1xuICAgIHJldHVybiB0aGlzLnJ1bihcInNldFVzZXJJZFwiLCAoKSA9PiB0aGlzLmlubmVyLnNldFVzZXJJZChzZXNzaW9uSWQsIHVzZXJJZCkpO1xuICB9XG5cbiAgY2xlYXJVc2VySWQoc2Vzc2lvbklkOiBzdHJpbmcpOiB2b2lkIHwgUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5jYWxscy5wdXNoKGBjbGVhclVzZXJJZDoke3Nlc3Npb25JZH1gKTtcbiAgICByZXR1cm4gdGhpcy5ydW4oXCJjbGVhclVzZXJJZFwiLCAoKSA9PiB0aGlzLmlubmVyLmNsZWFyVXNlcklkKHNlc3Npb25JZCkpO1xuICB9XG5cbiAgam9pbihzZXNzaW9uSWQ6IHN0cmluZywgcm9vbUlkOiBXZWJTb2NrZXRSb29tSWQpOiB2b2lkIHwgUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5jYWxscy5wdXNoKGBqb2luOiR7c2Vzc2lvbklkfToke3Jvb21JZH1gKTtcbiAgICByZXR1cm4gdGhpcy5ydW4oXCJqb2luXCIsICgpID0+IHRoaXMuaW5uZXIuam9pbihzZXNzaW9uSWQsIHJvb21JZCkpO1xuICB9XG5cbiAgbGVhdmUoc2Vzc2lvbklkOiBzdHJpbmcsIHJvb21JZDogV2ViU29ja2V0Um9vbUlkKTogdm9pZCB8IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuY2FsbHMucHVzaChgbGVhdmU6JHtzZXNzaW9uSWR9OiR7cm9vbUlkfWApO1xuICAgIHJldHVybiB0aGlzLnJ1bihcImxlYXZlXCIsICgpID0+IHRoaXMuaW5uZXIubGVhdmUoc2Vzc2lvbklkLCByb29tSWQpKTtcbiAgfVxuXG4gIHJlc29sdmVSb3V0ZShcbiAgICBhdWRpZW5jZTogV2ViU29ja2V0QXVkaWVuY2UsXG4gICAgbm9kZUlkOiBzdHJpbmcsXG4gICk6XG4gICAgfCB7IGxvY2FsU2Vzc2lvbklkczogc3RyaW5nW107IHJlbW90ZU5vZGVJZHM6IHN0cmluZ1tdIH1cbiAgICB8IFByb21pc2U8eyBsb2NhbFNlc3Npb25JZHM6IHN0cmluZ1tdOyByZW1vdGVOb2RlSWRzOiBzdHJpbmdbXSB9PiB7XG4gICAgdGhpcy5jYWxscy5wdXNoKGByZXNvbHZlUm91dGU6JHtub2RlSWR9OiR7YXVkaWVuY2UudHlwZX1gKTtcbiAgICByZXR1cm4gdGhpcy5ydW4oXCJyZXNvbHZlUm91dGVcIiwgKCkgPT4gdGhpcy5pbm5lci5yZXNvbHZlUm91dGUoYXVkaWVuY2UsIG5vZGVJZCkpO1xuICB9XG5cbiAgZ2V0Um9vbU1lbWJlckNvdW50KHJvb21JZDogV2ViU29ja2V0Um9vbUlkLCBuYW1lc3BhY2U/OiBzdHJpbmcpOiBudW1iZXIgfCBQcm9taXNlPG51bWJlcj4ge1xuICAgIHRoaXMuY2FsbHMucHVzaChgZ2V0Um9vbU1lbWJlckNvdW50OiR7cm9vbUlkfWApO1xuICAgIHJldHVybiB0aGlzLnJ1bihcImdldFJvb21NZW1iZXJDb3VudFwiLCAoKSA9PiB0aGlzLmlubmVyLmdldFJvb21NZW1iZXJDb3VudChyb29tSWQsIG5hbWVzcGFjZSkpO1xuICB9XG5cbiAgc2h1dGRvd24oKTogdm9pZCB8IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuY2FsbHMucHVzaChcInNodXRkb3duXCIpO1xuICAgIHJldHVybiB0aGlzLnJ1bihcInNodXRkb3duXCIsICgpID0+IHVuZGVmaW5lZCk7XG4gIH1cblxuICBjYWxsc09mKHByZWZpeDogc3RyaW5nKTogc3RyaW5nW10ge1xuICAgIHJldHVybiB0aGlzLmNhbGxzLmZpbHRlcigoY2FsbCkgPT4gY2FsbC5zdGFydHNXaXRoKHByZWZpeCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBydW48VD4obWV0aG9kOiBTY3JpcHRlZE1ldGhvZCwgZGVsZWdhdGU6ICgpID0+IFQgfCBQcm9taXNlPFQ+KTogVCB8IFByb21pc2U8VD4ge1xuICAgIGNvbnN0IGludGVyY2VwdG9yID0gdGhpcy5pbnRlcmNlcHRvcnMuZ2V0KG1ldGhvZCk/LnNoaWZ0KCk7XG4gICAgaWYgKCFpbnRlcmNlcHRvcikge1xuICAgICAgcmV0dXJuIGRlbGVnYXRlKCk7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzdWx0ID0gaW50ZXJjZXB0b3IoKTtcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgUHJvbWlzZSkge1xuICAgICAgcmV0dXJuIHJlc3VsdC50aGVuKCgpID0+IGRlbGVnYXRlKCkpO1xuICAgIH1cbiAgICByZXR1cm4gZGVsZWdhdGUoKTtcbiAgfVxufVxuXG4vKipcbiAqIO2VnCDtlITroZzshLjsiqQg7JWI7JeQ7IScIHRhcmdldE5vZGVJZHMg6riw7KSA7Jy866GcIGVudmVsb3Bl7J2EIOudvOyasO2Mhe2VmOuKlCBmYWtlIGNsdXN0ZXIg64Sk7Yq47JuM7YGsLlxuICog7KCE7IahIOq1rOqwhOydhCDtnYnrgrTrgrTquLAg7JyE7ZW0IGVudmVsb3Bl7J2EIEpTT04g7KeB66Cs7ZmUL+yXreyngeugrO2ZlO2VnCDrkqQg64uk7J2MIHRpY2vsl5Ag7KCE64us7ZWc64ukLlxuICovXG5leHBvcnQgY2xhc3MgSW5Qcm9jZXNzQ2x1c3Rlck5ldHdvcmsge1xuICByZWFkb25seSBwdWJsaXNoZWQ6IFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZVtdID0gW107XG4gIHByaXZhdGUgcmVhZG9ubHkgaGFuZGxlcnMgPSBuZXcgTWFwPHN0cmluZywgU2V0PFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZUhhbmRsZXI+PigpO1xuXG4gIGJ1cyhub2RlSWQ6IHN0cmluZyk6IEluUHJvY2Vzc0NsdXN0ZXJCdXMge1xuICAgIHJldHVybiBuZXcgSW5Qcm9jZXNzQ2x1c3RlckJ1cyhub2RlSWQsIHRoaXMpO1xuICB9XG5cbiAgc3Vic2NyaWJlKG5vZGVJZDogc3RyaW5nLCBoYW5kbGVyOiBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGVIYW5kbGVyKTogKCkgPT4gdm9pZCB7XG4gICAgY29uc3QgaGFuZGxlcnMgPSB0aGlzLmhhbmRsZXJzLmdldChub2RlSWQpID8/IG5ldyBTZXQoKTtcbiAgICBoYW5kbGVycy5hZGQoaGFuZGxlcik7XG4gICAgdGhpcy5oYW5kbGVycy5zZXQobm9kZUlkLCBoYW5kbGVycyk7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGhhbmRsZXJzLmRlbGV0ZShoYW5kbGVyKTtcbiAgICB9O1xuICB9XG5cbiAgZGVsaXZlcihlbnZlbG9wZTogV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlKTogdm9pZCB7XG4gICAgY29uc3Qgd2lyZSA9IEpTT04uc3RyaW5naWZ5KGVudmVsb3BlKTtcbiAgICB0aGlzLnB1Ymxpc2hlZC5wdXNoKHBhcnNlVGVzdEpzb248V2ViU29ja2V0Q2x1c3RlckVudmVsb3BlPih3aXJlKSk7XG4gICAgc2V0SW1tZWRpYXRlKCgpID0+IHtcbiAgICAgIGZvciAoY29uc3QgdGFyZ2V0Tm9kZUlkIG9mIGVudmVsb3BlLnRhcmdldE5vZGVJZHMgPz8gW10pIHtcbiAgICAgICAgZm9yIChjb25zdCBoYW5kbGVyIG9mIHRoaXMuaGFuZGxlcnMuZ2V0KHRhcmdldE5vZGVJZCkgPz8gW10pIHtcbiAgICAgICAgICBjb25zdCByZWNlaXZlZCA9IHBhcnNlVGVzdEpzb248V2ViU29ja2V0Q2x1c3RlckVudmVsb3BlPih3aXJlKTtcbiAgICAgICAgICB2b2lkIFByb21pc2UucmVzb2x2ZShoYW5kbGVyKHJlY2VpdmVkKSkuY2F0Y2goKCkgPT4ge30pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cblxuZXhwb3J0IGNsYXNzIEluUHJvY2Vzc0NsdXN0ZXJCdXMgaW1wbGVtZW50cyBXZWJTb2NrZXRDbHVzdGVyQnVzIHtcbiAgc2h1dGRvd25DYWxscyA9IDA7XG4gIHVuc3Vic2NyaWJlQ2FsbHMgPSAwO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHJlYWRvbmx5IG5vZGVJZDogc3RyaW5nLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbmV0d29yazogSW5Qcm9jZXNzQ2x1c3Rlck5ldHdvcmssXG4gICkge31cblxuICBwdWJsaXNoKGVudmVsb3BlOiBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGUpOiB2b2lkIHtcbiAgICB0aGlzLm5ldHdvcmsuZGVsaXZlcihlbnZlbG9wZSk7XG4gIH1cblxuICAvLyDsi6TsoJwgYWRhcHRlcuyymOufvCDqtazrj4Ug7JmE66OM66W8IHByb21pc2XroZwg7JWM66aw64ukXG4gIGFzeW5jIHN1YnNjcmliZShoYW5kbGVyOiBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGVIYW5kbGVyKTogUHJvbWlzZTwoKSA9PiB2b2lkPiB7XG4gICAgY29uc3QgdW5zdWJzY3JpYmUgPSB0aGlzLm5ldHdvcmsuc3Vic2NyaWJlKHRoaXMubm9kZUlkLCBoYW5kbGVyKTtcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgdGhpcy51bnN1YnNjcmliZUNhbGxzICs9IDE7XG4gICAgICB1bnN1YnNjcmliZSgpO1xuICAgIH07XG4gIH1cblxuICBzaHV0ZG93bigpOiB2b2lkIHtcbiAgICB0aGlzLnNodXRkb3duQ2FsbHMgKz0gMTtcbiAgfVxufVxuXG4vLyDrsJztlonrkJwgZW52ZWxvcGXsnYQg6riw66Gd7ZWY6rOgIO2FjOyKpO2KuOqwgCDsp4HsoJEg7IiY7IugIGVudmVsb3Bl7J2EIOyjvOyehe2VoCDsiJgg7J6I64qUIGJ1c1xuZXhwb3J0IGNsYXNzIFJlY29yZGluZ0NsdXN0ZXJCdXMgaW1wbGVtZW50cyBXZWJTb2NrZXRDbHVzdGVyQnVzIHtcbiAgcmVhZG9ubHkgcHVibGlzaGVkOiBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGVbXSA9IFtdO1xuICByZWFkb25seSBoYW5kbGVycyA9IG5ldyBTZXQ8V2ViU29ja2V0Q2x1c3RlckVudmVsb3BlSGFuZGxlcj4oKTtcbiAgc2h1dGRvd25DYWxscyA9IDA7XG4gIHVuc3Vic2NyaWJlQ2FsbHMgPSAwO1xuICBub2RlSWQ/OiBzdHJpbmc7XG5cbiAgcHVibGlzaChlbnZlbG9wZTogV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlKTogdm9pZCB8IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMucHVibGlzaGVkLnB1c2goZW52ZWxvcGUpO1xuICB9XG5cbiAgc3Vic2NyaWJlKGhhbmRsZXI6IFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZUhhbmRsZXIpOiAoKCkgPT4gdm9pZCkgfCBQcm9taXNlPCgpID0+IHZvaWQ+IHtcbiAgICB0aGlzLmhhbmRsZXJzLmFkZChoYW5kbGVyKTtcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgdGhpcy51bnN1YnNjcmliZUNhbGxzICs9IDE7XG4gICAgICB0aGlzLmhhbmRsZXJzLmRlbGV0ZShoYW5kbGVyKTtcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgZW1pdChlbnZlbG9wZTogV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgZm9yIChjb25zdCBoYW5kbGVyIG9mIHRoaXMuaGFuZGxlcnMpIHtcbiAgICAgIGF3YWl0IGhhbmRsZXIoZW52ZWxvcGUpO1xuICAgIH1cbiAgfVxuXG4gIHNodXRkb3duKCk6IHZvaWQgfCBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLnNodXRkb3duQ2FsbHMgKz0gMTtcbiAgfVxufVxuXG4vLyDsoITsl60gdW5oYW5kbGVkUmVqZWN0aW9u7J2EIOyImOynke2VnOuLpC4g67CY7ZmY7ZWcIO2VqOyImOuhnCDtlbTsoJztlZjqs6Ag7IiY7KeRIOqysOqzvOulvCDrsJvripTri6RcbmV4cG9ydCBmdW5jdGlvbiBjb2xsZWN0VW5oYW5kbGVkUmVqZWN0aW9ucygpOiAoKSA9PiBFcnJvcltdIHtcbiAgY29uc3QgY29sbGVjdGVkOiBFcnJvcltdID0gW107XG4gIGNvbnN0IGxpc3RlbmVyID0gKHJlYXNvbjogRXJyb3IpID0+IHtcbiAgICBjb2xsZWN0ZWQucHVzaChyZWFzb24pO1xuICB9O1xuICBwcm9jZXNzLm9uKFwidW5oYW5kbGVkUmVqZWN0aW9uXCIsIGxpc3RlbmVyKTtcbiAgcmV0dXJuICgpID0+IHtcbiAgICBwcm9jZXNzLm9mZihcInVuaGFuZGxlZFJlamVjdGlvblwiLCBsaXN0ZW5lcik7XG4gICAgcmV0dXJuIGNvbGxlY3RlZDtcbiAgfTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7O3VCQVVxRTtBQUdyRSxlQUFzQixXQUFXLFNBQWlCLEdBQWtCO0NBQ2xFLEtBQUssSUFBSSxRQUFRLEdBQUcs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
@@ -3,6 +3,7 @@ export * from "./ws-audience";
3
3
  export * from "./ws-cluster-bus";
4
4
  export * from "./ws-core";
5
5
  export * from "./ws-presence-store";
6
+ export * from "./ws-session-id";
6
7
  export * from "./ws-telemetry";
7
8
  export * from "./ws-telemetry-memory";
8
9
  export * from "./ws-telemetry-trace";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,MAAM,CAAC;AACrB,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,MAAM,CAAC;AACrB,cAAc,eAAe,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { createMockSSEFactory, createSSEFactory, init_sse } from "./sse.js";
2
2
  import { WebSocketAudience, init_ws_audience } from "./ws-audience.js";
3
+ import { createWebSocketNodeId, createWebSocketSessionId, init_ws_session_id, parseWebSocketSessionNodeId } from "./ws-session-id.js";
3
4
  import { NoopWebSocketClusterBus, init_ws_cluster_bus } from "./ws-cluster-bus.js";
4
5
  import { InMemoryEventStore, InMemoryMetricStore, InMemorySpanStore, init_ws_telemetry_memory } from "./ws-telemetry-memory.js";
5
6
  import { formatTraceParent, generateSpanId, generateTraceId, init_ws_telemetry_trace, parseTraceParent } from "./ws-telemetry-trace.js";
@@ -13,12 +14,13 @@ init_sse();
13
14
  init_ws_audience();
14
15
  init_ws_cluster_bus();
15
16
  init_ws_presence_store();
17
+ init_ws_session_id();
16
18
  init_ws_telemetry();
17
19
  init_ws_telemetry_memory();
18
20
  init_ws_telemetry_trace();
19
21
  init_ws();
20
22
  init_ws_registry();
21
23
  //#endregion
22
- export { DEFAULT_TRACE_OPTIONS, EMPTY_METRICS_SNAPSHOT, InMemoryEventStore, InMemoryMetricStore, InMemorySpanStore, InMemoryWebSocketPresenceStore, NoopWebSocketClusterBus, NoopWebSocketTelemetryController, WebSocketAudience, WebSocketRegistry, WebSocketRuntime, WebSocketTelemetryControllerImpl, createMockSSEFactory, createSSEFactory, createWebSocketRuntime, createWebSocketTelemetryController, formatTraceParent, generateSpanId, generateTraceId, isPromiseLike, parseTraceParent };
24
+ export { DEFAULT_TRACE_OPTIONS, EMPTY_METRICS_SNAPSHOT, InMemoryEventStore, InMemoryMetricStore, InMemorySpanStore, InMemoryWebSocketPresenceStore, NoopWebSocketClusterBus, NoopWebSocketTelemetryController, WebSocketAudience, WebSocketRegistry, WebSocketRuntime, WebSocketTelemetryControllerImpl, createMockSSEFactory, createSSEFactory, createWebSocketNodeId, createWebSocketRuntime, createWebSocketSessionId, createWebSocketTelemetryController, formatTraceParent, generateSpanId, generateTraceId, isPromiseLike, parseTraceParent, parseWebSocketSessionNodeId };
23
25
 
24
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS9pbmRleC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tIFwiLi9zc2VcIjtcbmV4cG9ydCAqIGZyb20gXCIuL3dzLWF1ZGllbmNlXCI7XG5leHBvcnQgKiBmcm9tIFwiLi93cy1jbHVzdGVyLWJ1c1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtY29yZVwiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtcHJlc2VuY2Utc3RvcmVcIjtcbmV4cG9ydCAqIGZyb20gXCIuL3dzLXRlbGVtZXRyeVwiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtdGVsZW1ldHJ5LW1lbW9yeVwiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtdGVsZW1ldHJ5LXRyYWNlXCI7XG5leHBvcnQgKiBmcm9tIFwiLi93c1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtcmVnaXN0cnlcIjtcbiJdLCJtYXBwaW5ncyI6IiJ9
26
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS9pbmRleC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tIFwiLi9zc2VcIjtcbmV4cG9ydCAqIGZyb20gXCIuL3dzLWF1ZGllbmNlXCI7XG5leHBvcnQgKiBmcm9tIFwiLi93cy1jbHVzdGVyLWJ1c1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtY29yZVwiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtcHJlc2VuY2Utc3RvcmVcIjtcbmV4cG9ydCAqIGZyb20gXCIuL3dzLXNlc3Npb24taWRcIjtcbmV4cG9ydCAqIGZyb20gXCIuL3dzLXRlbGVtZXRyeVwiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtdGVsZW1ldHJ5LW1lbW9yeVwiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtdGVsZW1ldHJ5LXRyYWNlXCI7XG5leHBvcnQgKiBmcm9tIFwiLi93c1wiO1xuZXhwb3J0ICogZnJvbSBcIi4vd3MtcmVnaXN0cnlcIjtcbiJdLCJtYXBwaW5ncyI6IiJ9
@@ -1,15 +1,18 @@
1
1
  import { type WebSocketAudience } from "./ws-audience";
2
2
  import { type WebSocketPresenceStore } from "./ws-presence-store";
3
- export type WebSocketRoutingPlan = {
4
- localSessionIds: string[];
5
- remoteNodeIds: string[];
6
- };
3
+ /**
4
+ * publish 대상 중 원격 노드를 찾는다. 로컬 대상은 registry 메모리로 해석하므로 여기서는 원격 노드만 다룬다.
5
+ */
7
6
  export declare class WebSocketAudienceResolver {
8
7
  private readonly options;
9
8
  constructor(options: {
10
9
  nodeId: string;
11
10
  presenceStore: WebSocketPresenceStore;
12
11
  });
13
- resolve(audience: WebSocketAudience): WebSocketRoutingPlan;
12
+ /**
13
+ * `connections` audience는 세션 ID에 든 노드 ID로 store 조회 없이 라우팅한다.
14
+ * 그 밖의 audience는 store.resolveRoute를 호출하며, 동기 throw도 rejection으로 바꾼다.
15
+ */
16
+ resolveRemoteNodeIds(audience: WebSocketAudience): Promise<string[]>;
14
17
  }
15
18
  //# sourceMappingURL=ws-audience-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ws-audience-resolver.d.ts","sourceRoot":"","sources":["../../src/stream/ws-audience-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,MAAM,oBAAoB,GAAG;IACjC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,qBAAa,yBAAyB;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAD1B,YACmB,OAAO,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,sBAAsB,CAAC;KACvC,EACC;IAEJ,OAAO,CAAC,QAAQ,EAAE,iBAAiB,GAAG,oBAAoB,CAiBzD;CACF"}
1
+ {"version":3,"file":"ws-audience-resolver.d.ts","sourceRoot":"","sources":["../../src/stream/ws-audience-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;GAEG;AACH,qBAAa,yBAAyB;IAElC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAD1B,YACmB,OAAO,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,sBAAsB,CAAC;KACvC,EACC;IAEJ;;;OAGG;IACG,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBzE;CACF"}
@@ -1,26 +1,29 @@
1
1
  import { __esmMin } from "../_virtual/_rolldown/runtime.js";
2
+ import { init_ws_session_id, parseWebSocketSessionNodeId } from "./ws-session-id.js";
2
3
  //#region src/stream/ws-audience-resolver.ts
3
4
  var WebSocketAudienceResolver;
4
5
  var init_ws_audience_resolver = __esmMin((() => {
6
+ init_ws_session_id();
5
7
  WebSocketAudienceResolver = class {
6
8
  options;
7
9
  constructor(options) {
8
10
  this.options = options;
9
11
  }
10
- resolve(audience) {
11
- const localSessionIds = [];
12
- const remoteNodeIds = /* @__PURE__ */ new Set();
13
- for (const meta of this.options.presenceStore.queryAudience(audience)) {
14
- if (meta.nodeId === this.options.nodeId) {
15
- localSessionIds.push(meta.sessionId);
16
- continue;
12
+ /**
13
+ * `connections` audience는 세션 ID에 든 노드 ID로 store 조회 없이 라우팅한다.
14
+ * 그 밖의 audience는 store.resolveRoute를 호출하며, 동기 throw도 rejection으로 바꾼다.
15
+ */
16
+ async resolveRemoteNodeIds(audience) {
17
+ if (audience.type === "connections") {
18
+ const nodeIds = /* @__PURE__ */ new Set();
19
+ for (const connectionId of audience.connectionIds) {
20
+ const nodeId = parseWebSocketSessionNodeId(connectionId);
21
+ if (nodeId !== void 0 && nodeId !== this.options.nodeId) nodeIds.add(nodeId);
17
22
  }
18
- remoteNodeIds.add(meta.nodeId);
23
+ return [...nodeIds];
19
24
  }
20
- return {
21
- localSessionIds,
22
- remoteNodeIds: [...remoteNodeIds]
23
- };
25
+ const plan = await Promise.resolve().then(() => this.options.presenceStore.resolveRoute(audience, this.options.nodeId));
26
+ return [...new Set(plan.remoteNodeIds)].filter((nodeId) => nodeId !== this.options.nodeId);
24
27
  }
25
28
  };
26
29
  }));
@@ -28,4 +31,4 @@ var init_ws_audience_resolver = __esmMin((() => {
28
31
  init_ws_audience_resolver();
29
32
  export { WebSocketAudienceResolver, init_ws_audience_resolver };
30
33
 
31
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtYXVkaWVuY2UtcmVzb2x2ZXIuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1hdWRpZW5jZS1yZXNvbHZlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB0eXBlIFdlYlNvY2tldEF1ZGllbmNlIH0gZnJvbSBcIi4vd3MtYXVkaWVuY2VcIjtcbmltcG9ydCB7IHR5cGUgV2ViU29ja2V0UHJlc2VuY2VTdG9yZSB9IGZyb20gXCIuL3dzLXByZXNlbmNlLXN0b3JlXCI7XG5cbmV4cG9ydCB0eXBlIFdlYlNvY2tldFJvdXRpbmdQbGFuID0ge1xuICBsb2NhbFNlc3Npb25JZHM6IHN0cmluZ1tdO1xuICByZW1vdGVOb2RlSWRzOiBzdHJpbmdbXTtcbn07XG5cbmV4cG9ydCBjbGFzcyBXZWJTb2NrZXRBdWRpZW5jZVJlc29sdmVyIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBvcHRpb25zOiB7XG4gICAgICBub2RlSWQ6IHN0cmluZztcbiAgICAgIHByZXNlbmNlU3RvcmU6IFdlYlNvY2tldFByZXNlbmNlU3RvcmU7XG4gICAgfSxcbiAgKSB7fVxuXG4gIHJlc29sdmUoYXVkaWVuY2U6IFdlYlNvY2tldEF1ZGllbmNlKTogV2ViU29ja2V0Um91dGluZ1BsYW4ge1xuICAgIGNvbnN0IGxvY2FsU2Vzc2lvbklkczogc3RyaW5nW10gPSBbXTtcbiAgICBjb25zdCByZW1vdGVOb2RlSWRzID0gbmV3IFNldDxzdHJpbmc+KCk7XG5cbiAgICBmb3IgKGNvbnN0IG1ldGEgb2YgdGhpcy5vcHRpb25zLnByZXNlbmNlU3RvcmUucXVlcnlBdWRpZW5jZShhdWRpZW5jZSkpIHtcbiAgICAgIGlmIChtZXRhLm5vZGVJZCA9PT0gdGhpcy5vcHRpb25zLm5vZGVJZCkge1xuICAgICAgICBsb2NhbFNlc3Npb25JZHMucHVzaChtZXRhLnNlc3Npb25JZCk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICByZW1vdGVOb2RlSWRzLmFkZChtZXRhLm5vZGVJZCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGxvY2FsU2Vzc2lvbklkcyxcbiAgICAgIHJlbW90ZU5vZGVJZHM6IFsuLi5yZW1vdGVOb2RlSWRzXSxcbiAgICB9O1xuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Q0FRYSw0QkFBYixNQUF1QztFQUVsQjtFQURuQixZQUNFLFNBSUE7R0FKaUIsS0FBQSxVQUFBO0VBSWhCO0VBRUgsUUFBUSxVQUFtRDtHQUN6RCxNQUFNLGtCQUE0QixDQUFDO0dBQ25DLE1BQU0sZ0NBQWdCLElBQUksSUFBWTtHQUV0QyxLQUFLLE1BQU0sUUFBUSxLQUFLLFFBQVEsY0FBYyxjQUFjLFFBQVEsR0FBRztJQUNyRSxJQUFJLEtBQUssV0FBVyxLQUFLLFFBQVEsUUFBUTtLQUN2QyxnQkFBZ0IsS0FBSyxLQUFLLFNBQVM7S0FDbkM7SUFDRjtJQUVBLGNBQWMsSUFBSSxLQUFLLE1BQU07R0FDL0I7R0FFQSxPQUFPO0lBQ0w7SUFDQSxlQUFlLENBQUMsR0FBRyxhQUFhO0dBQ2xDO0VBQ0Y7Q0FDRiJ9
34
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtYXVkaWVuY2UtcmVzb2x2ZXIuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1hdWRpZW5jZS1yZXNvbHZlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB0eXBlIFdlYlNvY2tldEF1ZGllbmNlIH0gZnJvbSBcIi4vd3MtYXVkaWVuY2VcIjtcbmltcG9ydCB7IHR5cGUgV2ViU29ja2V0UHJlc2VuY2VTdG9yZSB9IGZyb20gXCIuL3dzLXByZXNlbmNlLXN0b3JlXCI7XG5pbXBvcnQgeyBwYXJzZVdlYlNvY2tldFNlc3Npb25Ob2RlSWQgfSBmcm9tIFwiLi93cy1zZXNzaW9uLWlkXCI7XG5cbi8qKlxuICogcHVibGlzaCDrjIDsg4Eg7KSRIOybkOqyqSDrhbjrk5zrpbwg7LC+64qU64ukLiDroZzsu6wg64yA7IOB7J2AIHJlZ2lzdHJ5IOuplOuqqOumrOuhnCDtlbTshJ3tlZjrr4DroZwg7Jes6riw7ISc64qUIOybkOqyqSDrhbjrk5zrp4wg64uk66Os64ukLlxuICovXG5leHBvcnQgY2xhc3MgV2ViU29ja2V0QXVkaWVuY2VSZXNvbHZlciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgb3B0aW9uczoge1xuICAgICAgbm9kZUlkOiBzdHJpbmc7XG4gICAgICBwcmVzZW5jZVN0b3JlOiBXZWJTb2NrZXRQcmVzZW5jZVN0b3JlO1xuICAgIH0sXG4gICkge31cblxuICAvKipcbiAgICogYGNvbm5lY3Rpb25zYCBhdWRpZW5jZeuKlCDshLjshZggSUTsl5Ag65OgIOuFuOuTnCBJROuhnCBzdG9yZSDsobDtmowg7JeG7J20IOudvOyasO2Mhe2VnOuLpC5cbiAgICog6re4IOuwluydmCBhdWRpZW5jZeuKlCBzdG9yZS5yZXNvbHZlUm91dGXrpbwg7Zi47Lac7ZWY66mwLCDrj5nquLAgdGhyb3frj4QgcmVqZWN0aW9u7Jy866GcIOuwlOq+vOuLpC5cbiAgICovXG4gIGFzeW5jIHJlc29sdmVSZW1vdGVOb2RlSWRzKGF1ZGllbmNlOiBXZWJTb2NrZXRBdWRpZW5jZSk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBpZiAoYXVkaWVuY2UudHlwZSA9PT0gXCJjb25uZWN0aW9uc1wiKSB7XG4gICAgICBjb25zdCBub2RlSWRzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgICBmb3IgKGNvbnN0IGNvbm5lY3Rpb25JZCBvZiBhdWRpZW5jZS5jb25uZWN0aW9uSWRzKSB7XG4gICAgICAgIGNvbnN0IG5vZGVJZCA9IHBhcnNlV2ViU29ja2V0U2Vzc2lvbk5vZGVJZChjb25uZWN0aW9uSWQpO1xuICAgICAgICBpZiAobm9kZUlkICE9PSB1bmRlZmluZWQgJiYgbm9kZUlkICE9PSB0aGlzLm9wdGlvbnMubm9kZUlkKSB7XG4gICAgICAgICAgbm9kZUlkcy5hZGQobm9kZUlkKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIFsuLi5ub2RlSWRzXTtcbiAgICB9XG5cbiAgICBjb25zdCBwbGFuID0gYXdhaXQgUHJvbWlzZS5yZXNvbHZlKCkudGhlbigoKSA9PlxuICAgICAgdGhpcy5vcHRpb25zLnByZXNlbmNlU3RvcmUucmVzb2x2ZVJvdXRlKGF1ZGllbmNlLCB0aGlzLm9wdGlvbnMubm9kZUlkKSxcbiAgICApO1xuICAgIHJldHVybiBbLi4ubmV3IFNldChwbGFuLnJlbW90ZU5vZGVJZHMpXS5maWx0ZXIoKG5vZGVJZCkgPT4gbm9kZUlkICE9PSB0aGlzLm9wdGlvbnMubm9kZUlkKTtcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7OztvQkFFNkQ7Q0FLaEQsNEJBQWIsTUFBdUM7RUFFbEI7RUFEbkIsWUFDRSxTQUlBO0dBSmlCLEtBQUEsVUFBQTtFQUloQjs7Ozs7RUFNSCxNQUFNLHFCQUFxQixVQUFnRDtHQUN6RSxJQUFJLFNBQVMsU0FBUyxlQUFlO0lBQ25DLE1BQU0sMEJBQVUsSUFBSSxJQUFZO0lBQ2hDLEtBQUssTUFBTSxnQkFBZ0IsU0FBUyxlQUFlO0tBQ2pELE1BQU0sU0FBUyw0QkFBNEIsWUFBWTtLQUN2RCxJQUFJLFdBQVcsVUFBYSxXQUFXLEtBQUssUUFBUSxRQUNsRCxRQUFRLElBQUksTUFBTTtJQUV0QjtJQUNBLE9BQU8sQ0FBQyxHQUFHLE9BQU87R0FDcEI7R0FFQSxNQUFNLE9BQU8sTUFBTSxRQUFRLFFBQVEsQ0FBQyxDQUFDLFdBQ25DLEtBQUssUUFBUSxjQUFjLGFBQWEsVUFBVSxLQUFLLFFBQVEsTUFBTSxDQUN2RTtHQUNBLE9BQU8sQ0FBQyxHQUFHLElBQUksSUFBSSxLQUFLLGFBQWEsQ0FBQyxDQUFDLENBQUMsUUFBUSxXQUFXLFdBQVcsS0FBSyxRQUFRLE1BQU07RUFDM0Y7Q0FDRiJ9
@@ -1,18 +1,23 @@
1
1
  import { type WebSocketAudience } from "./ws-audience";
2
+ import { type MaybePromise } from "./ws-core";
3
+ /**
4
+ * 노드 간 publish envelope. 원본 data 대신 발행 노드가 route별로 한 번 검증·인코딩한 frame을 싣는다.
5
+ * 수신 노드는 자기 메모리로 audience를 해석하고, 각 연결의 route frame을 재검증 없이 그대로 보낸다.
6
+ */
2
7
  export type WebSocketClusterEnvelope = {
3
8
  id: string;
4
9
  sourceNodeId: string;
5
- targetNodeIds?: string[];
6
- namespace?: string;
10
+ targetNodeIds: string[];
7
11
  audience: WebSocketAudience;
8
12
  event: string;
9
- data: unknown;
13
+ frames: Record<string, string>;
10
14
  emittedAt: number;
11
15
  };
12
16
  export type WebSocketClusterEnvelopeHandler = (envelope: WebSocketClusterEnvelope) => void | Promise<void>;
13
17
  export interface WebSocketClusterBus {
18
+ readonly nodeId?: string;
14
19
  publish(envelope: WebSocketClusterEnvelope): void | Promise<void>;
15
- subscribe(handler: WebSocketClusterEnvelopeHandler): () => void;
20
+ subscribe(handler: WebSocketClusterEnvelopeHandler): MaybePromise<() => void>;
16
21
  shutdown(): void | Promise<void>;
17
22
  }
18
23
  export declare class NoopWebSocketClusterBus implements WebSocketClusterBus {
@@ -1 +1 @@
1
- {"version":3,"file":"ws-cluster-bus.d.ts","sourceRoot":"","sources":["../../src/stream/ws-cluster-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,MAAM,MAAM,wBAAwB,GAAG;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,CAC5C,QAAQ,EAAE,wBAAwB,KAC/B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,SAAS,CAAC,OAAO,EAAE,+BAA+B,GAAG,MAAM,IAAI,CAAC;IAChE,QAAQ,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,OAAO,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAAG;IAErD,SAAS,CAAC,QAAQ,EAAE,+BAA+B,GAAG,MAAM,IAAI,CAE/D;IAED,QAAQ,IAAI,IAAI,CAAG;CACpB"}
1
+ {"version":3,"file":"ws-cluster-bus.d.ts","sourceRoot":"","sources":["../../src/stream/ws-cluster-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IAEd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG,CAC5C,QAAQ,EAAE,wBAAwB,KAC/B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,mBAAmB;IAElC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE,SAAS,CAAC,OAAO,EAAE,+BAA+B,GAAG,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9E,QAAQ,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,OAAO,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAAG;IAErD,SAAS,CAAC,QAAQ,EAAE,+BAA+B,GAAG,MAAM,IAAI,CAE/D;IAED,QAAQ,IAAI,IAAI,CAAG;CACpB"}
@@ -14,4 +14,4 @@ var init_ws_cluster_bus = __esmMin((() => {
14
14
  init_ws_cluster_bus();
15
15
  export { NoopWebSocketClusterBus, init_ws_cluster_bus };
16
16
 
17
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtY2x1c3Rlci1idXMuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1jbHVzdGVyLWJ1cy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB0eXBlIFdlYlNvY2tldEF1ZGllbmNlIH0gZnJvbSBcIi4vd3MtYXVkaWVuY2VcIjtcblxuZXhwb3J0IHR5cGUgV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlID0ge1xuICBpZDogc3RyaW5nO1xuICBzb3VyY2VOb2RlSWQ6IHN0cmluZztcbiAgdGFyZ2V0Tm9kZUlkcz86IHN0cmluZ1tdO1xuICBuYW1lc3BhY2U/OiBzdHJpbmc7XG4gIGF1ZGllbmNlOiBXZWJTb2NrZXRBdWRpZW5jZTtcbiAgZXZlbnQ6IHN0cmluZztcbiAgZGF0YTogdW5rbm93bjtcbiAgZW1pdHRlZEF0OiBudW1iZXI7XG59O1xuXG5leHBvcnQgdHlwZSBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGVIYW5kbGVyID0gKFxuICBlbnZlbG9wZTogV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlLFxuKSA9PiB2b2lkIHwgUHJvbWlzZTx2b2lkPjtcblxuZXhwb3J0IGludGVyZmFjZSBXZWJTb2NrZXRDbHVzdGVyQnVzIHtcbiAgcHVibGlzaChlbnZlbG9wZTogV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlKTogdm9pZCB8IFByb21pc2U8dm9pZD47XG4gIHN1YnNjcmliZShoYW5kbGVyOiBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGVIYW5kbGVyKTogKCkgPT4gdm9pZDtcbiAgc2h1dGRvd24oKTogdm9pZCB8IFByb21pc2U8dm9pZD47XG59XG5cbmV4cG9ydCBjbGFzcyBOb29wV2ViU29ja2V0Q2x1c3RlckJ1cyBpbXBsZW1lbnRzIFdlYlNvY2tldENsdXN0ZXJCdXMge1xuICBwdWJsaXNoKF9lbnZlbG9wZTogV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlKTogdm9pZCB7fVxuXG4gIHN1YnNjcmliZShfaGFuZGxlcjogV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlSGFuZGxlcik6ICgpID0+IHZvaWQge1xuICAgIHJldHVybiAoKSA9PiB7fTtcbiAgfVxuXG4gIHNodXRkb3duKCk6IHZvaWQge31cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7OztDQXVCYSwwQkFBYixNQUFvRTtFQUNsRSxRQUFRLFdBQTJDLENBQUM7RUFFcEQsVUFBVSxVQUF1RDtHQUMvRCxhQUFhLENBQUM7RUFDaEI7RUFFQSxXQUFpQixDQUFDO0NBQ3BCIn0=
17
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtY2x1c3Rlci1idXMuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1jbHVzdGVyLWJ1cy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB0eXBlIFdlYlNvY2tldEF1ZGllbmNlIH0gZnJvbSBcIi4vd3MtYXVkaWVuY2VcIjtcbmltcG9ydCB7IHR5cGUgTWF5YmVQcm9taXNlIH0gZnJvbSBcIi4vd3MtY29yZVwiO1xuXG4vKipcbiAqIOuFuOuTnCDqsIQgcHVibGlzaCBlbnZlbG9wZS4g7JuQ67O4IGRhdGEg64yA7IugIOuwnO2WiSDrhbjrk5zqsIAgcm91dGXrs4TroZwg7ZWcIOuyiCDqsoDspp3Ct+yduOy9lOuUqe2VnCBmcmFtZeydhCDsi6PripTri6QuXG4gKiDsiJjsi6Ag64W465Oc64qUIOyekOq4sCDrqZTrqqjrpqzroZwgYXVkaWVuY2Xrpbwg7ZW07ISd7ZWY6rOgLCDqsIEg7Jew6rKw7J2YIHJvdXRlIGZyYW1l7J2EIOyerOqygOymnSDsl4bsnbQg6re464yA66GcIOuztOuCuOuLpC5cbiAqL1xuZXhwb3J0IHR5cGUgV2ViU29ja2V0Q2x1c3RlckVudmVsb3BlID0ge1xuICBpZDogc3RyaW5nO1xuICBzb3VyY2VOb2RlSWQ6IHN0cmluZztcbiAgdGFyZ2V0Tm9kZUlkczogc3RyaW5nW107XG4gIGF1ZGllbmNlOiBXZWJTb2NrZXRBdWRpZW5jZTtcbiAgZXZlbnQ6IHN0cmluZztcbiAgLy8gcm91dGVLZXkg4oaSIOyduOy9lOuUqeuQnCBmcmFtZS4g7J2067Kk7Yq466W8IOyEoOyWuO2VmOyngCDslYrslZjqsbDrgpgg6rKA7Kad7JeQIOyLpO2MqO2VnCByb3V0ZeuKlCDruaDsp4Tri6RcbiAgZnJhbWVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICBlbWl0dGVkQXQ6IG51bWJlcjtcbn07XG5cbmV4cG9ydCB0eXBlIFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZUhhbmRsZXIgPSAoXG4gIGVudmVsb3BlOiBXZWJTb2NrZXRDbHVzdGVyRW52ZWxvcGUsXG4pID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+O1xuXG5leHBvcnQgaW50ZXJmYWNlIFdlYlNvY2tldENsdXN0ZXJCdXMge1xuICAvLyDshKTsoJXtlZjrqbQgcnVudGltZSBub2RlSWTsmYAg6rCZ7JWE7JW8IO2VnOuLpC4g64uk66W066m0IHJ1bnRpbWUg7IOd7ISx7J20IOyLpO2MqO2VnOuLpFxuICByZWFkb25seSBub2RlSWQ/OiBzdHJpbmc7XG4gIHB1Ymxpc2goZW52ZWxvcGU6IFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZSk6IHZvaWQgfCBQcm9taXNlPHZvaWQ+O1xuICAvLyDqtazrj4Ug7JmE66OM66W8IHByb21pc2XroZwg7JWM66a0IOyImCDsnojri6QuIOuwmO2ZmO2VnCDtlbTsoJwg7ZWo7IiY64qUIHJ1bnRpbWUgc2h1dGRvd27sl5DshJwg7ZWcIOuyiCDtmLjstpzrkJzri6RcbiAgc3Vic2NyaWJlKGhhbmRsZXI6IFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZUhhbmRsZXIpOiBNYXliZVByb21pc2U8KCkgPT4gdm9pZD47XG4gIHNodXRkb3duKCk6IHZvaWQgfCBQcm9taXNlPHZvaWQ+O1xufVxuXG5leHBvcnQgY2xhc3MgTm9vcFdlYlNvY2tldENsdXN0ZXJCdXMgaW1wbGVtZW50cyBXZWJTb2NrZXRDbHVzdGVyQnVzIHtcbiAgcHVibGlzaChfZW52ZWxvcGU6IFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZSk6IHZvaWQge31cblxuICBzdWJzY3JpYmUoX2hhbmRsZXI6IFdlYlNvY2tldENsdXN0ZXJFbnZlbG9wZUhhbmRsZXIpOiAoKSA9PiB2b2lkIHtcbiAgICByZXR1cm4gKCkgPT4ge307XG4gIH1cblxuICBzaHV0ZG93bigpOiB2b2lkIHt9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Q0ErQmEsMEJBQWIsTUFBb0U7RUFDbEUsUUFBUSxXQUEyQyxDQUFDO0VBRXBELFVBQVUsVUFBdUQ7R0FDL0QsYUFBYSxDQUFDO0VBQ2hCO0VBRUEsV0FBaUIsQ0FBQztDQUNwQiJ9
@@ -1,5 +1,7 @@
1
+ import { type z } from "zod";
1
2
  export type WebSocketUserId = number | string;
2
3
  export type WebSocketRoomId = string;
4
+ export type MaybePromise<T> = T | Promise<T>;
3
5
  export interface ManagedWebSocketConnection {
4
6
  id: string;
5
7
  namespace: string;
@@ -8,6 +10,14 @@ export interface ManagedWebSocketConnection {
8
10
  publishUntyped<Data>(event: string, data: Data): void;
9
11
  close(code?: number, reason?: string): void;
10
12
  }
13
+ /**
14
+ * registry가 소유하는 로컬 연결. 인코딩된 frame을 그대로 보내는 경로와 route 식별자를 가진다.
15
+ */
16
+ export interface WebSocketFrameConnection extends ManagedWebSocketConnection {
17
+ readonly routeKey?: string;
18
+ readonly outEvents: z.ZodObject;
19
+ sendFrame(event: string, frame: string): void;
20
+ }
11
21
  export type WebSocketRegistryStats = {
12
22
  totalConnections: number;
13
23
  totalRooms: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ws-core.d.ts","sourceRoot":"","sources":["../../src/stream/ws-core.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAEhB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC,CAAC"}
1
+ {"version":3,"file":"ws-core.d.ts","sourceRoot":"","sources":["../../src/stream/ws-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAC9C,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AACrC,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7C,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAEhB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,0BAA0B;IAE1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC;IAChC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC,CAAC"}
@@ -1,32 +1,58 @@
1
+ import { type z } from "zod";
1
2
  import { type WebSocketAudience } from "./ws-audience";
2
3
  import { type WebSocketAudienceResolver } from "./ws-audience-resolver";
3
4
  import { type WebSocketClusterBus } from "./ws-cluster-bus";
4
5
  import { type WebSocketLocalConnectionStore } from "./ws-local-connection-store";
5
6
  import { type WebSocketTelemetryController } from "./ws-telemetry";
7
+ export type WebSocketRouteDefinition = {
8
+ routeKey: string;
9
+ outEvents: z.ZodObject;
10
+ };
6
11
  export declare class WebSocketDeliveryEngine {
7
12
  private readonly options;
8
13
  private readonly pendingFanOutJobs;
9
14
  private fanOutFlushScheduled;
10
- private readonly unsubscribe;
15
+ private readonly routes;
16
+ private publishQueue;
17
+ private readonly subscription;
18
+ private stopped;
11
19
  private readonly telemetry;
12
20
  private readonly unregisterMetricSource;
13
21
  constructor(options: {
14
22
  nodeId: string;
15
23
  localConnections: WebSocketLocalConnectionStore;
24
+ resolveLocalSessionIds: (audience: WebSocketAudience) => string[];
16
25
  audienceResolver: WebSocketAudienceResolver;
17
26
  clusterBus: WebSocketClusterBus;
18
27
  telemetryController?: WebSocketTelemetryController;
19
28
  });
20
- publishToAudience<Data>(audience: WebSocketAudience, event: string, data: Data): void;
21
- shutdown(): Promise<void>;
29
+ registerRoute(routeKey: string, outEvents: z.ZodObject): void;
30
+ replaceRoutes(routes: WebSocketRouteDefinition[]): void;
31
+ /**
32
+ * 발행 노드에서 route별로 한 번 검증·인코딩하고, 로컬 fan-out은 즉시 큐에 넣는다.
33
+ * 원격 라우팅과 cluster publish는 노드 단위 직렬 큐에서 처리한다. 반환한 promise는 reject하지 않는다.
34
+ */
35
+ publishToAudience<Data>(audience: WebSocketAudience, event: string, data: Data): Promise<void>;
36
+ drainPublishes(): Promise<void>;
37
+ /**
38
+ * 구독을 해제하고 cluster bus를 종료한다. 구독이 deadline까지 끝나지 않으면 늦게 끝날 때 해제한다.
39
+ */
40
+ shutdown(deadline: Promise<void>): Promise<void>;
22
41
  getTelemetrySnapshot(): {
23
42
  pendingFanOutJobs: number;
24
43
  pendingFanOutTargets: number;
25
44
  };
45
+ private subscribe;
46
+ private safeUnsubscribe;
47
+ private resolveRemoteNodeIds;
48
+ private publishRemote;
26
49
  private handleClusterEnvelope;
50
+ private emitPublishRejected;
51
+ private emitRouteFailed;
52
+ private emitFanOutResolved;
27
53
  private enqueueFanOut;
28
54
  private scheduleFanOutFlush;
29
55
  private flushFanOutJobs;
30
- private safePublish;
56
+ private safeSend;
31
57
  }
32
58
  //# sourceMappingURL=ws-delivery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ws-delivery.d.ts","sourceRoot":"","sources":["../../src/stream/ws-delivery.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,KAAK,mBAAmB,EAAiC,MAAM,kBAAkB,CAAC;AAE3F,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,gBAAgB,CAAC;AAKxB,qBAAa,uBAAuB;IAahC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAK1B;IACR,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IACzD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAa;IAEpD,YACmB,OAAO,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,gBAAgB,EAAE,6BAA6B,CAAC;QAChD,gBAAgB,EAAE,yBAAyB,CAAC;QAC5C,UAAU,EAAE,mBAAmB,CAAC;QAChC,mBAAmB,CAAC,EAAE,4BAA4B,CAAC;KACpD,EASF;IAED,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAwEpF;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAI9B;IAED,oBAAoB;;;MAWnB;IAED,OAAO,CAAC,qBAAqB;IA+C7B,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,eAAe;IA8CvB,OAAO,CAAC,WAAW;CAqBpB"}
1
+ {"version":3,"file":"ws-delivery.d.ts","sourceRoot":"","sources":["../../src/stream/ws-delivery.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,KAAK,mBAAmB,EAAiC,MAAM,kBAAkB,CAAC;AAG3F,OAAO,EAAE,KAAK,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,gBAAgB,CAAC;AAKxB,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC;CACxB,CAAC;AAeF,qBAAa,uBAAuB;IAahC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmB;IACrD,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IAEzD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IACzD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAa;IAEpD,YACmB,OAAO,EAAE;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,gBAAgB,EAAE,6BAA6B,CAAC;QAEhD,sBAAsB,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,MAAM,EAAE,CAAC;QAClE,gBAAgB,EAAE,yBAAyB,CAAC;QAC5C,UAAU,EAAE,mBAAmB,CAAC;QAChC,mBAAmB,CAAC,EAAE,4BAA4B,CAAC;KACpD,EAOF;IAED,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,IAAI,CAE5D;IAED,aAAa,CAAC,MAAM,EAAE,wBAAwB,EAAE,GAAG,IAAI,CAKtD;IAED;;;OAGG;IACH,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA+E7F;IAGK,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAEpC;IAED;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBrD;IAED,oBAAoB;;;MAWnB;IAGD,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,oBAAoB;YAUd,aAAa;IA8D3B,OAAO,CAAC,qBAAqB;IA4D7B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,eAAe;IA8CvB,OAAO,CAAC,QAAQ;CAkBjB"}