sonamu 0.11.16 → 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.
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +12 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/stream/__tests__/ws-test-helpers.d.ts +108 -0
- package/dist/stream/__tests__/ws-test-helpers.d.ts.map +1 -0
- package/dist/stream/__tests__/ws-test-helpers.js +260 -0
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.d.ts.map +1 -1
- package/dist/stream/index.js +4 -2
- package/dist/stream/ws-audience-resolver.d.ts +8 -5
- package/dist/stream/ws-audience-resolver.d.ts.map +1 -1
- package/dist/stream/ws-audience-resolver.js +16 -13
- package/dist/stream/ws-cluster-bus.d.ts +9 -4
- package/dist/stream/ws-cluster-bus.d.ts.map +1 -1
- package/dist/stream/ws-cluster-bus.js +1 -1
- package/dist/stream/ws-core.d.ts +10 -0
- package/dist/stream/ws-core.d.ts.map +1 -1
- package/dist/stream/ws-delivery.d.ts +30 -4
- package/dist/stream/ws-delivery.d.ts.map +1 -1
- package/dist/stream/ws-delivery.js +199 -52
- package/dist/stream/ws-frame.d.ts +15 -0
- package/dist/stream/ws-frame.d.ts.map +1 -0
- package/dist/stream/ws-frame.js +29 -0
- package/dist/stream/ws-local-connection-store.d.ts +4 -4
- package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
- package/dist/stream/ws-local-connection-store.js +2 -2
- package/dist/stream/ws-presence-index.d.ts +36 -0
- package/dist/stream/ws-presence-index.d.ts.map +1 -0
- package/dist/stream/ws-presence-index.js +177 -0
- package/dist/stream/ws-presence-store.d.ts +39 -39
- package/dist/stream/ws-presence-store.d.ts.map +1 -1
- package/dist/stream/ws-presence-store.js +22 -152
- package/dist/stream/ws-registry.d.ts +45 -17
- package/dist/stream/ws-registry.d.ts.map +1 -1
- package/dist/stream/ws-registry.js +169 -60
- package/dist/stream/ws-session-id.d.ts +14 -0
- package/dist/stream/ws-session-id.d.ts.map +1 -0
- package/dist/stream/ws-session-id.js +37 -0
- package/dist/stream/ws.d.ts +20 -10
- package/dist/stream/ws.d.ts.map +1 -1
- package/dist/stream/ws.js +59 -36
- package/package.json +1 -1
- package/src/api/__tests__/sonamu.websocket-routes.test.ts +234 -0
- package/src/api/__tests__/sonamu.websocket.test.ts +241 -20
- package/src/api/sonamu.ts +20 -7
- package/src/index.ts +1 -0
- package/src/stream/__tests__/ws-contracts.test.ts +483 -265
- package/src/stream/__tests__/ws-multinode.test.ts +326 -0
- package/src/stream/__tests__/ws-registry-async.test.ts +855 -0
- package/src/stream/__tests__/ws-test-helpers.ts +360 -0
- package/src/stream/__tests__/ws.test.ts +342 -145
- package/src/stream/index.ts +1 -0
- package/src/stream/ws-audience-resolver.ts +21 -19
- package/src/stream/ws-cluster-bus.ts +12 -4
- package/src/stream/ws-core.ts +14 -0
- package/src/stream/ws-delivery.ts +321 -97
- package/src/stream/ws-frame.ts +39 -0
- package/src/stream/ws-local-connection-store.ts +10 -9
- package/src/stream/ws-presence-index.ts +273 -0
- package/src/stream/ws-presence-store.ts +64 -284
- package/src/stream/ws-registry.ts +272 -85
- package/src/stream/ws-session-id.ts +39 -0
- package/src/stream/ws.ts +91 -50
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { __esmMin } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/stream/ws-frame.ts
|
|
3
|
+
/**
|
|
4
|
+
* outEvents 스키마로 payload를 검증하고 `{event, data}` frame 문자열로 인코딩한다.
|
|
5
|
+
* 같은 스키마로 인코딩한 frame은 로컬·원격 수신자 모두에게 같은 바이트로 전달된다.
|
|
6
|
+
*/
|
|
7
|
+
function encodeWebSocketFrame(outEvents, event, data) {
|
|
8
|
+
const schema = getEventSchema(outEvents, event);
|
|
9
|
+
if (!schema) return { status: "unknownEvent" };
|
|
10
|
+
const parsed = schema.safeParse(data);
|
|
11
|
+
if (!parsed.success) return { status: "invalidPayload" };
|
|
12
|
+
return {
|
|
13
|
+
status: "encoded",
|
|
14
|
+
frame: JSON.stringify({
|
|
15
|
+
event,
|
|
16
|
+
data: parsed.data
|
|
17
|
+
})
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function getEventSchema(outEvents, event) {
|
|
21
|
+
const eventSchemas = outEvents["shape"];
|
|
22
|
+
return Object.hasOwn(eventSchemas, event) ? eventSchemas[event] : void 0;
|
|
23
|
+
}
|
|
24
|
+
var init_ws_frame = __esmMin((() => {}));
|
|
25
|
+
//#endregion
|
|
26
|
+
init_ws_frame();
|
|
27
|
+
export { encodeWebSocketFrame, init_ws_frame };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtZnJhbWUuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1mcmFtZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB0eXBlIHogfSBmcm9tIFwiem9kXCI7XG5cbmV4cG9ydCB0eXBlIFdlYlNvY2tldEZyYW1lRW5jb2RpbmcgPVxuICB8IHsgc3RhdHVzOiBcImVuY29kZWRcIjsgZnJhbWU6IHN0cmluZyB9XG4gIHwgeyBzdGF0dXM6IFwidW5rbm93bkV2ZW50XCIgfVxuICB8IHsgc3RhdHVzOiBcImludmFsaWRQYXlsb2FkXCIgfTtcblxuLyoqXG4gKiBvdXRFdmVudHMg7Iqk7YKk66eI66GcIHBheWxvYWTrpbwg6rKA7Kad7ZWY6rOgIGB7ZXZlbnQsIGRhdGF9YCBmcmFtZSDrrLjsnpDsl7TroZwg7J247L2U65Sp7ZWc64ukLlxuICog6rCZ7J2AIOyKpO2CpOuniOuhnCDsnbjsvZTrlKntlZwgZnJhbWXsnYAg66Gc7Luswrfsm5Dqsqkg7IiY7Iug7J6QIOuqqOuRkOyXkOqyjCDqsJnsnYAg67CU7J207Yq466GcIOyghOuLrOuQnOuLpC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGVuY29kZVdlYlNvY2tldEZyYW1lPERhdGE+KFxuICBvdXRFdmVudHM6IHouWm9kT2JqZWN0LFxuICBldmVudDogc3RyaW5nLFxuICBkYXRhOiBEYXRhLFxuKTogV2ViU29ja2V0RnJhbWVFbmNvZGluZyB7XG4gIGNvbnN0IHNjaGVtYSA9IGdldEV2ZW50U2NoZW1hKG91dEV2ZW50cywgZXZlbnQpO1xuICBpZiAoIXNjaGVtYSkge1xuICAgIHJldHVybiB7IHN0YXR1czogXCJ1bmtub3duRXZlbnRcIiB9O1xuICB9XG5cbiAgY29uc3QgcGFyc2VkID0gc2NoZW1hLnNhZmVQYXJzZShkYXRhKTtcbiAgaWYgKCFwYXJzZWQuc3VjY2Vzcykge1xuICAgIHJldHVybiB7IHN0YXR1czogXCJpbnZhbGlkUGF5bG9hZFwiIH07XG4gIH1cblxuICByZXR1cm4ge1xuICAgIHN0YXR1czogXCJlbmNvZGVkXCIsXG4gICAgZnJhbWU6IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgIGV2ZW50LFxuICAgICAgZGF0YTogcGFyc2VkLmRhdGEsXG4gICAgfSksXG4gIH07XG59XG5cbmZ1bmN0aW9uIGdldEV2ZW50U2NoZW1hKG91dEV2ZW50czogei5ab2RPYmplY3QsIGV2ZW50OiBzdHJpbmcpOiB6LlpvZFR5cGUgfCB1bmRlZmluZWQge1xuICBjb25zdCBldmVudFNjaGVtYXM6IFJlY29yZDxzdHJpbmcsIHouWm9kVHlwZT4gPSBvdXRFdmVudHNbXCJzaGFwZVwiXTtcbiAgcmV0dXJuIE9iamVjdC5oYXNPd24oZXZlbnRTY2hlbWFzLCBldmVudCkgPyBldmVudFNjaGVtYXNbZXZlbnRdIDogdW5kZWZpbmVkO1xufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFXQSxTQUFnQixxQkFDZCxXQUNBLE9BQ0EsTUFDd0I7Q0FDeEIsTUFBTSxTQUFTLGVBQWUsV0FBVyxLQUFLO0NBQzlDLElBQUksQ0FBQyxRQUNILE9BQU8sRUFBRSxRQUFRLGVBQWU7Q0FHbEMsTUFBTSxTQUFTLE9BQU8sVUFBVSxJQUFJO0NBQ3BDLElBQUksQ0FBQyxPQUFPLFNBQ1YsT0FBTyxFQUFFLFFBQVEsaUJBQWlCO0NBR3BDLE9BQU87RUFDTCxRQUFRO0VBQ1IsT0FBTyxLQUFLLFVBQVU7R0FDcEI7R0FDQSxNQUFNLE9BQU87RUFDZixDQUFDO0NBQ0g7QUFDRjtBQUVBLFNBQVMsZUFBZSxXQUF3QixPQUFzQztDQUNwRixNQUFNLGVBQTBDLFVBQVU7Q0FDMUQsT0FBTyxPQUFPLE9BQU8sY0FBYyxLQUFLLElBQUksYUFBYSxTQUFTO0FBQ3BFIn0=
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type WebSocketFrameConnection } from "./ws-core";
|
|
2
2
|
import { type WebSocketTelemetryConnectionSnapshot } from "./ws-telemetry";
|
|
3
3
|
export declare class WebSocketLocalConnectionStore {
|
|
4
4
|
private readonly connections;
|
|
5
|
-
register(connection:
|
|
5
|
+
register(connection: WebSocketFrameConnection): void;
|
|
6
6
|
unregister(connectionId: string): void;
|
|
7
|
-
getConnection(connectionId: string):
|
|
8
|
-
getConnections(connectionIds: string[]):
|
|
7
|
+
getConnection(connectionId: string): WebSocketFrameConnection | undefined;
|
|
8
|
+
getConnections(connectionIds: string[]): WebSocketFrameConnection[];
|
|
9
9
|
getTelemetrySnapshot(): WebSocketTelemetryConnectionSnapshot;
|
|
10
10
|
closeAll(code?: number, reason?: string): void;
|
|
11
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-local-connection-store.d.ts","sourceRoot":"","sources":["../../src/stream/ws-local-connection-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"ws-local-connection-store.d.ts","sourceRoot":"","sources":["../../src/stream/ws-local-connection-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EACL,KAAK,oCAAoC,EAE1C,MAAM,gBAAgB,CAAC;AAExB,qBAAa,6BAA6B;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+C;IAE3E,QAAQ,CAAC,UAAU,EAAE,wBAAwB,GAAG,IAAI,CAEnD;IAED,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAErC;IAED,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAExE;IAED,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,wBAAwB,EAAE,CAmBlE;IAED,oBAAoB,IAAI,oCAAoC,CAmB3D;IAED,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAK7C;CACF"}
|
|
@@ -46,7 +46,7 @@ var init_ws_local_connection_store = __esmMin((() => {
|
|
|
46
46
|
return snapshot;
|
|
47
47
|
}
|
|
48
48
|
closeAll(code, reason) {
|
|
49
|
-
for (const connection of this.connections.values()) connection.close(code, reason);
|
|
49
|
+
for (const connection of Array.from(this.connections.values())) connection.close(code, reason);
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
}));
|
|
@@ -54,4 +54,4 @@ var init_ws_local_connection_store = __esmMin((() => {
|
|
|
54
54
|
init_ws_local_connection_store();
|
|
55
55
|
export { WebSocketLocalConnectionStore, init_ws_local_connection_store };
|
|
56
56
|
|
|
57
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,
|
|
57
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtbG9jYWwtY29ubmVjdGlvbi1zdG9yZS5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RyZWFtL3dzLWxvY2FsLWNvbm5lY3Rpb24tc3RvcmUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgaXNGdW5jdGlvblZhbHVlIH0gZnJvbSBcIi4uL3V0aWxzL3J1bnRpbWUtdmFsdWVcIjtcbmltcG9ydCB7IHR5cGUgV2ViU29ja2V0RnJhbWVDb25uZWN0aW9uIH0gZnJvbSBcIi4vd3MtY29yZVwiO1xuaW1wb3J0IHtcbiAgdHlwZSBXZWJTb2NrZXRUZWxlbWV0cnlDb25uZWN0aW9uU25hcHNob3QsXG4gIHR5cGUgVGVsZW1ldHJ5SW5zcGVjdGFibGVDb25uZWN0aW9uLFxufSBmcm9tIFwiLi93cy10ZWxlbWV0cnlcIjtcblxuZXhwb3J0IGNsYXNzIFdlYlNvY2tldExvY2FsQ29ubmVjdGlvblN0b3JlIHtcbiAgcHJpdmF0ZSByZWFkb25seSBjb25uZWN0aW9ucyA9IG5ldyBNYXA8c3RyaW5nLCBXZWJTb2NrZXRGcmFtZUNvbm5lY3Rpb24+KCk7XG5cbiAgcmVnaXN0ZXIoY29ubmVjdGlvbjogV2ViU29ja2V0RnJhbWVDb25uZWN0aW9uKTogdm9pZCB7XG4gICAgdGhpcy5jb25uZWN0aW9ucy5zZXQoY29ubmVjdGlvbi5pZCwgY29ubmVjdGlvbik7XG4gIH1cblxuICB1bnJlZ2lzdGVyKGNvbm5lY3Rpb25JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5jb25uZWN0aW9ucy5kZWxldGUoY29ubmVjdGlvbklkKTtcbiAgfVxuXG4gIGdldENvbm5lY3Rpb24oY29ubmVjdGlvbklkOiBzdHJpbmcpOiBXZWJTb2NrZXRGcmFtZUNvbm5lY3Rpb24gfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0aGlzLmNvbm5lY3Rpb25zLmdldChjb25uZWN0aW9uSWQpO1xuICB9XG5cbiAgZ2V0Q29ubmVjdGlvbnMoY29ubmVjdGlvbklkczogc3RyaW5nW10pOiBXZWJTb2NrZXRGcmFtZUNvbm5lY3Rpb25bXSB7XG4gICAgY29uc3QgdGFyZ2V0czogV2ViU29ja2V0RnJhbWVDb25uZWN0aW9uW10gPSBbXTtcbiAgICBjb25zdCBzZWVuID0gbmV3IFNldDxzdHJpbmc+KCk7XG5cbiAgICBmb3IgKGNvbnN0IGNvbm5lY3Rpb25JZCBvZiBjb25uZWN0aW9uSWRzKSB7XG4gICAgICBpZiAoc2Vlbi5oYXMoY29ubmVjdGlvbklkKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHNlZW4uYWRkKGNvbm5lY3Rpb25JZCk7XG5cbiAgICAgIGNvbnN0IGNvbm5lY3Rpb24gPSB0aGlzLmNvbm5lY3Rpb25zLmdldChjb25uZWN0aW9uSWQpO1xuICAgICAgaWYgKCFjb25uZWN0aW9uIHx8IGNvbm5lY3Rpb24uY2xvc2VkKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICB0YXJnZXRzLnB1c2goY29ubmVjdGlvbik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRhcmdldHM7XG4gIH1cblxuICBnZXRUZWxlbWV0cnlTbmFwc2hvdCgpOiBXZWJTb2NrZXRUZWxlbWV0cnlDb25uZWN0aW9uU25hcHNob3Qge1xuICAgIGNvbnN0IHNuYXBzaG90OiBXZWJTb2NrZXRUZWxlbWV0cnlDb25uZWN0aW9uU25hcHNob3QgPSB7XG4gICAgICBwZW5kaW5nSW5ib3VuZE1lc3NhZ2VzOiAwLFxuICAgICAgcGVuZGluZ091dGJvdW5kTWVzc2FnZXM6IDAsXG4gICAgICBzb2NrZXRCdWZmZXJlZEJ5dGVzOiAwLFxuICAgIH07XG5cbiAgICBmb3IgKGNvbnN0IGNvbm5lY3Rpb24gb2YgdGhpcy5jb25uZWN0aW9ucy52YWx1ZXMoKSkge1xuICAgICAgaWYgKCFpc1RlbGVtZXRyeUluc3BlY3RhYmxlQ29ubmVjdGlvbihjb25uZWN0aW9uKSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgY29ubmVjdGlvblNuYXBzaG90ID0gY29ubmVjdGlvbi5nZXRUZWxlbWV0cnlTbmFwc2hvdCgpO1xuICAgICAgc25hcHNob3QucGVuZGluZ0luYm91bmRNZXNzYWdlcyArPSBjb25uZWN0aW9uU25hcHNob3QucGVuZGluZ0luYm91bmRNZXNzYWdlcztcbiAgICAgIHNuYXBzaG90LnBlbmRpbmdPdXRib3VuZE1lc3NhZ2VzICs9IGNvbm5lY3Rpb25TbmFwc2hvdC5wZW5kaW5nT3V0Ym91bmRNZXNzYWdlcztcbiAgICAgIHNuYXBzaG90LnNvY2tldEJ1ZmZlcmVkQnl0ZXMgKz0gY29ubmVjdGlvblNuYXBzaG90LnNvY2tldEJ1ZmZlcmVkQnl0ZXM7XG4gICAgfVxuXG4gICAgcmV0dXJuIHNuYXBzaG90O1xuICB9XG5cbiAgY2xvc2VBbGwoY29kZT86IG51bWJlciwgcmVhc29uPzogc3RyaW5nKTogdm9pZCB7XG4gICAgLy8gY2xvc2Ug64+E7KSRIHVucmVnaXN0ZXLqsIAgTWFw7J2EIOuwlOq+uOuvgOuhnCDrs7Xsgqzrs7jsnYQg7Iic7ZqM7ZWc64ukXG4gICAgZm9yIChjb25zdCBjb25uZWN0aW9uIG9mIEFycmF5LmZyb20odGhpcy5jb25uZWN0aW9ucy52YWx1ZXMoKSkpIHtcbiAgICAgIGNvbm5lY3Rpb24uY2xvc2UoY29kZSwgcmVhc29uKTtcbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gaXNUZWxlbWV0cnlJbnNwZWN0YWJsZUNvbm5lY3Rpb24oXG4gIGNvbm5lY3Rpb246IFdlYlNvY2tldEZyYW1lQ29ubmVjdGlvbixcbik6IGNvbm5lY3Rpb24gaXMgV2ViU29ja2V0RnJhbWVDb25uZWN0aW9uICYgVGVsZW1ldHJ5SW5zcGVjdGFibGVDb25uZWN0aW9uIHtcbiAgcmV0dXJuIFwiZ2V0VGVsZW1ldHJ5U25hcHNob3RcIiBpbiBjb25uZWN0aW9uICYmIGlzRnVuY3Rpb25WYWx1ZShjb25uZWN0aW9uLmdldFRlbGVtZXRyeVNuYXBzaG90KTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7O0FBd0VBLFNBQVMsaUNBQ1AsWUFDeUU7Q0FDekUsT0FBTywwQkFBMEIsY0FBYyxnQkFBZ0IsV0FBVyxvQkFBb0I7QUFDaEc7OztvQkE1RXdEO0NBTzNDLGdDQUFiLE1BQTJDO0VBQ3pDLDhCQUErQixJQUFJLElBQXNDO0VBRXpFLFNBQVMsWUFBNEM7R0FDbkQsS0FBSyxZQUFZLElBQUksV0FBVyxJQUFJLFVBQVU7RUFDaEQ7RUFFQSxXQUFXLGNBQTRCO0dBQ3JDLEtBQUssWUFBWSxPQUFPLFlBQVk7RUFDdEM7RUFFQSxjQUFjLGNBQTREO0dBQ3hFLE9BQU8sS0FBSyxZQUFZLElBQUksWUFBWTtFQUMxQztFQUVBLGVBQWUsZUFBcUQ7R0FDbEUsTUFBTSxVQUFzQyxDQUFDO0dBQzdDLE1BQU0sdUJBQU8sSUFBSSxJQUFZO0dBRTdCLEtBQUssTUFBTSxnQkFBZ0IsZUFBZTtJQUN4QyxJQUFJLEtBQUssSUFBSSxZQUFZLEdBQ3ZCO0lBRUYsS0FBSyxJQUFJLFlBQVk7SUFFckIsTUFBTSxhQUFhLEtBQUssWUFBWSxJQUFJLFlBQVk7SUFDcEQsSUFBSSxDQUFDLGNBQWMsV0FBVyxRQUM1QjtJQUdGLFFBQVEsS0FBSyxVQUFVO0dBQ3pCO0dBRUEsT0FBTztFQUNUO0VBRUEsdUJBQTZEO0dBQzNELE1BQU0sV0FBaUQ7SUFDckQsd0JBQXdCO0lBQ3hCLHlCQUF5QjtJQUN6QixxQkFBcUI7R0FDdkI7R0FFQSxLQUFLLE1BQU0sY0FBYyxLQUFLLFlBQVksT0FBTyxHQUFHO0lBQ2xELElBQUksQ0FBQyxpQ0FBaUMsVUFBVSxHQUM5QztJQUdGLE1BQU0scUJBQXFCLFdBQVcscUJBQXFCO0lBQzNELFNBQVMsMEJBQTBCLG1CQUFtQjtJQUN0RCxTQUFTLDJCQUEyQixtQkFBbUI7SUFDdkQsU0FBUyx1QkFBdUIsbUJBQW1CO0dBQ3JEO0dBRUEsT0FBTztFQUNUO0VBRUEsU0FBUyxNQUFlLFFBQXVCO0dBRTdDLEtBQUssTUFBTSxjQUFjLE1BQU0sS0FBSyxLQUFLLFlBQVksT0FBTyxDQUFDLEdBQzNELFdBQVcsTUFBTSxNQUFNLE1BQU07RUFFakM7Q0FDRiJ9
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type WebSocketAudience } from "./ws-audience";
|
|
2
|
+
import { type WebSocketRegistryStats, type WebSocketRoomId, type WebSocketUserId } from "./ws-core";
|
|
3
|
+
import { type WebSocketPresenceRegistration, type WebSocketSessionPresence } from "./ws-presence-store";
|
|
4
|
+
/**
|
|
5
|
+
* 동기 presence 인덱스. registry의 노드 로컬 메모리와 in-memory store가 함께 쓴다.
|
|
6
|
+
* 외부로 내보내는 presence는 항상 복사본이다.
|
|
7
|
+
*/
|
|
8
|
+
export declare class WebSocketPresenceIndex {
|
|
9
|
+
private readonly sessions;
|
|
10
|
+
private readonly rooms;
|
|
11
|
+
private readonly users;
|
|
12
|
+
has(sessionId: string): boolean;
|
|
13
|
+
register(input: WebSocketPresenceRegistration): void;
|
|
14
|
+
activate(sessionId: string): void;
|
|
15
|
+
unregister(sessionId: string): WebSocketSessionPresence | undefined;
|
|
16
|
+
touch(sessionId: string): void;
|
|
17
|
+
setUserId(sessionId: string, userId: WebSocketUserId): void;
|
|
18
|
+
clearUserId(sessionId: string): void;
|
|
19
|
+
join(sessionId: string, roomId: WebSocketRoomId): void;
|
|
20
|
+
leave(sessionId: string, roomId: WebSocketRoomId): void;
|
|
21
|
+
getConnection(sessionId: string): WebSocketSessionPresence | undefined;
|
|
22
|
+
getConnectionCount(namespace?: string): number;
|
|
23
|
+
getRoomMembers(roomId: WebSocketRoomId, namespace?: string): WebSocketSessionPresence[];
|
|
24
|
+
getRoomMemberCount(roomId: WebSocketRoomId, namespace?: string): number;
|
|
25
|
+
getStats(): WebSocketRegistryStats;
|
|
26
|
+
/**
|
|
27
|
+
* audience에 해당하는 활성 세션을 중복 없이 반환한다. 반환값은 내부 상태를 노출하지 않도록 ID와 노드 ID만 담는다.
|
|
28
|
+
*/
|
|
29
|
+
queryAudience(audience: WebSocketAudience): Array<{
|
|
30
|
+
sessionId: string;
|
|
31
|
+
nodeId: string;
|
|
32
|
+
}>;
|
|
33
|
+
private collectAudience;
|
|
34
|
+
private getBoundSessions;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=ws-presence-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-presence-index.d.ts","sourceRoot":"","sources":["../../src/stream/ws-presence-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,sBAAsB,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACpG,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAC;AAM7B;;;GAGG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqC;IAC9D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4C;IAClE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4C;IAElE,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE9B;IAED,QAAQ,CAAC,KAAK,EAAE,6BAA6B,GAAG,IAAI,CAYnD;IAED,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAKhC;IAED,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAclE;IAED,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAK7B;IAED,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAW1D;IAED,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAQnC;IAED,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAQrD;IAED,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAQtD;IAED,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAGrE;IAED,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ7C;IAED,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,wBAAwB,EAAE,CAEtF;IAED,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtE;IAED,QAAQ,IAAI,sBAAsB,CAkBjC;IAED;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,iBAAiB,GAAG,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAavF;IAED,OAAO,CAAC,eAAe;IAyBvB,OAAO,CAAC,gBAAgB;CA0BzB"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { __esmMin } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/stream/ws-presence-index.ts
|
|
3
|
+
function matchesNamespace(record, namespace) {
|
|
4
|
+
return namespace === void 0 || namespace === "" || record.namespace === namespace;
|
|
5
|
+
}
|
|
6
|
+
function copyPresence(record) {
|
|
7
|
+
const copy = {
|
|
8
|
+
sessionId: record.sessionId,
|
|
9
|
+
nodeId: record.nodeId,
|
|
10
|
+
namespace: record.namespace,
|
|
11
|
+
active: record.active,
|
|
12
|
+
rooms: new Set(record.rooms),
|
|
13
|
+
connectedAt: new Date(record.connectedAt.getTime()),
|
|
14
|
+
lastSeenAt: new Date(record.lastSeenAt.getTime())
|
|
15
|
+
};
|
|
16
|
+
if (record.userId !== void 0) copy.userId = record.userId;
|
|
17
|
+
return copy;
|
|
18
|
+
}
|
|
19
|
+
function addBinding(index, namespace, id, sessionId) {
|
|
20
|
+
const bindings = index.get(namespace) ?? /* @__PURE__ */ new Map();
|
|
21
|
+
const members = bindings.get(id) ?? /* @__PURE__ */ new Set();
|
|
22
|
+
members.add(sessionId);
|
|
23
|
+
bindings.set(id, members);
|
|
24
|
+
index.set(namespace, bindings);
|
|
25
|
+
}
|
|
26
|
+
function removeBinding(index, namespace, id, sessionId) {
|
|
27
|
+
const bindings = index.get(namespace);
|
|
28
|
+
const members = bindings?.get(id);
|
|
29
|
+
if (!bindings || !members) return;
|
|
30
|
+
members.delete(sessionId);
|
|
31
|
+
if (members.size === 0) bindings.delete(id);
|
|
32
|
+
if (bindings.size === 0) index.delete(namespace);
|
|
33
|
+
}
|
|
34
|
+
var WebSocketPresenceIndex;
|
|
35
|
+
var init_ws_presence_index = __esmMin((() => {
|
|
36
|
+
WebSocketPresenceIndex = class {
|
|
37
|
+
sessions = /* @__PURE__ */ new Map();
|
|
38
|
+
rooms = /* @__PURE__ */ new Map();
|
|
39
|
+
users = /* @__PURE__ */ new Map();
|
|
40
|
+
has(sessionId) {
|
|
41
|
+
return this.sessions.has(sessionId);
|
|
42
|
+
}
|
|
43
|
+
register(input) {
|
|
44
|
+
this.unregister(input.sessionId);
|
|
45
|
+
this.sessions.set(input.sessionId, {
|
|
46
|
+
sessionId: input.sessionId,
|
|
47
|
+
nodeId: input.nodeId,
|
|
48
|
+
namespace: input.namespace,
|
|
49
|
+
active: input.active,
|
|
50
|
+
rooms: /* @__PURE__ */ new Set(),
|
|
51
|
+
connectedAt: new Date(input.connectedAt.getTime()),
|
|
52
|
+
lastSeenAt: new Date(input.connectedAt.getTime())
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
activate(sessionId) {
|
|
56
|
+
const record = this.sessions.get(sessionId);
|
|
57
|
+
if (record) record.active = true;
|
|
58
|
+
}
|
|
59
|
+
unregister(sessionId) {
|
|
60
|
+
const record = this.sessions.get(sessionId);
|
|
61
|
+
if (!record) return;
|
|
62
|
+
if (record.userId !== void 0) removeBinding(this.users, record.namespace, record.userId, sessionId);
|
|
63
|
+
for (const roomId of record.rooms) removeBinding(this.rooms, record.namespace, roomId, sessionId);
|
|
64
|
+
this.sessions.delete(sessionId);
|
|
65
|
+
return copyPresence(record);
|
|
66
|
+
}
|
|
67
|
+
touch(sessionId) {
|
|
68
|
+
const record = this.sessions.get(sessionId);
|
|
69
|
+
if (record) record.lastSeenAt = /* @__PURE__ */ new Date();
|
|
70
|
+
}
|
|
71
|
+
setUserId(sessionId, userId) {
|
|
72
|
+
const record = this.sessions.get(sessionId);
|
|
73
|
+
if (!record) return;
|
|
74
|
+
if (record.userId !== void 0) removeBinding(this.users, record.namespace, record.userId, sessionId);
|
|
75
|
+
record.userId = userId;
|
|
76
|
+
addBinding(this.users, record.namespace, userId, sessionId);
|
|
77
|
+
}
|
|
78
|
+
clearUserId(sessionId) {
|
|
79
|
+
const record = this.sessions.get(sessionId);
|
|
80
|
+
if (!record || record.userId === void 0) return;
|
|
81
|
+
removeBinding(this.users, record.namespace, record.userId, sessionId);
|
|
82
|
+
delete record.userId;
|
|
83
|
+
}
|
|
84
|
+
join(sessionId, roomId) {
|
|
85
|
+
const record = this.sessions.get(sessionId);
|
|
86
|
+
if (!record) return;
|
|
87
|
+
record.rooms.add(roomId);
|
|
88
|
+
addBinding(this.rooms, record.namespace, roomId, sessionId);
|
|
89
|
+
}
|
|
90
|
+
leave(sessionId, roomId) {
|
|
91
|
+
const record = this.sessions.get(sessionId);
|
|
92
|
+
if (!record) return;
|
|
93
|
+
record.rooms.delete(roomId);
|
|
94
|
+
removeBinding(this.rooms, record.namespace, roomId, sessionId);
|
|
95
|
+
}
|
|
96
|
+
getConnection(sessionId) {
|
|
97
|
+
const record = this.sessions.get(sessionId);
|
|
98
|
+
return record ? copyPresence(record) : void 0;
|
|
99
|
+
}
|
|
100
|
+
getConnectionCount(namespace) {
|
|
101
|
+
let count = 0;
|
|
102
|
+
for (const record of this.sessions.values()) if (record.active && matchesNamespace(record, namespace)) count += 1;
|
|
103
|
+
return count;
|
|
104
|
+
}
|
|
105
|
+
getRoomMembers(roomId, namespace) {
|
|
106
|
+
return this.getBoundSessions(this.rooms, roomId, namespace).map(copyPresence);
|
|
107
|
+
}
|
|
108
|
+
getRoomMemberCount(roomId, namespace) {
|
|
109
|
+
return this.getBoundSessions(this.rooms, roomId, namespace).length;
|
|
110
|
+
}
|
|
111
|
+
getStats() {
|
|
112
|
+
const byNamespace = {};
|
|
113
|
+
let totalConnections = 0;
|
|
114
|
+
for (const record of this.sessions.values()) {
|
|
115
|
+
if (!record.active) continue;
|
|
116
|
+
byNamespace[record.namespace] = (byNamespace[record.namespace] ?? 0) + 1;
|
|
117
|
+
totalConnections += 1;
|
|
118
|
+
}
|
|
119
|
+
let totalRooms = 0;
|
|
120
|
+
for (const bindings of this.rooms.values()) totalRooms += bindings.size;
|
|
121
|
+
return {
|
|
122
|
+
totalConnections,
|
|
123
|
+
totalRooms,
|
|
124
|
+
byNamespace
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* audience에 해당하는 활성 세션을 중복 없이 반환한다. 반환값은 내부 상태를 노출하지 않도록 ID와 노드 ID만 담는다.
|
|
129
|
+
*/
|
|
130
|
+
queryAudience(audience) {
|
|
131
|
+
const seen = /* @__PURE__ */ new Set();
|
|
132
|
+
const results = [];
|
|
133
|
+
for (const record of this.collectAudience(audience)) {
|
|
134
|
+
if (seen.has(record.sessionId)) continue;
|
|
135
|
+
seen.add(record.sessionId);
|
|
136
|
+
results.push({
|
|
137
|
+
sessionId: record.sessionId,
|
|
138
|
+
nodeId: record.nodeId
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return results;
|
|
142
|
+
}
|
|
143
|
+
collectAudience(audience) {
|
|
144
|
+
switch (audience.type) {
|
|
145
|
+
case "all": return [...this.sessions.values()].filter((record) => record.active && matchesNamespace(record, audience.namespace));
|
|
146
|
+
case "room": return this.getBoundSessions(this.rooms, audience.roomId, audience.namespace);
|
|
147
|
+
case "user": return this.getBoundSessions(this.users, audience.userId, audience.namespace);
|
|
148
|
+
case "connections": {
|
|
149
|
+
const records = [];
|
|
150
|
+
for (const sessionId of audience.connectionIds) {
|
|
151
|
+
const record = this.sessions.get(sessionId);
|
|
152
|
+
if (record?.active && matchesNamespace(record, audience.namespace)) records.push(record);
|
|
153
|
+
}
|
|
154
|
+
return records;
|
|
155
|
+
}
|
|
156
|
+
case "union": return audience.audiences.flatMap((item) => this.collectAudience(item));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
getBoundSessions(index, id, namespace) {
|
|
160
|
+
const namespaces = namespace ? [index.get(namespace)] : [...index.values()];
|
|
161
|
+
const seen = /* @__PURE__ */ new Set();
|
|
162
|
+
const records = [];
|
|
163
|
+
for (const bindings of namespaces) for (const sessionId of bindings?.get(id) ?? []) {
|
|
164
|
+
if (seen.has(sessionId)) continue;
|
|
165
|
+
seen.add(sessionId);
|
|
166
|
+
const record = this.sessions.get(sessionId);
|
|
167
|
+
if (record?.active) records.push(record);
|
|
168
|
+
}
|
|
169
|
+
return records;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}));
|
|
173
|
+
//#endregion
|
|
174
|
+
init_ws_presence_index();
|
|
175
|
+
export { WebSocketPresenceIndex, init_ws_presence_index };
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3MtcHJlc2VuY2UtaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0cmVhbS93cy1wcmVzZW5jZS1pbmRleC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB0eXBlIFdlYlNvY2tldEF1ZGllbmNlIH0gZnJvbSBcIi4vd3MtYXVkaWVuY2VcIjtcbmltcG9ydCB7IHR5cGUgV2ViU29ja2V0UmVnaXN0cnlTdGF0cywgdHlwZSBXZWJTb2NrZXRSb29tSWQsIHR5cGUgV2ViU29ja2V0VXNlcklkIH0gZnJvbSBcIi4vd3MtY29yZVwiO1xuaW1wb3J0IHtcbiAgdHlwZSBXZWJTb2NrZXRQcmVzZW5jZVJlZ2lzdHJhdGlvbixcbiAgdHlwZSBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2UsXG59IGZyb20gXCIuL3dzLXByZXNlbmNlLXN0b3JlXCI7XG5cbnR5cGUgUHJlc2VuY2VSZWNvcmQgPSBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2U7XG4vLyBuYW1lc3BhY2Ug4oaSIOuwlOyduOuUqSBpZCDihpIg7IS47IWYIElEIOynke2VqS4gdXNlcklk64qUIE1hcCDtgqTroZwg6re464yA66GcIOyNqOyEnCAx6rO8IFwiMVwi7J2EIOq1rOu2hO2VnOuLpFxudHlwZSBCaW5kaW5nSW5kZXg8SWQ+ID0gTWFwPHN0cmluZywgTWFwPElkLCBTZXQ8c3RyaW5nPj4+O1xuXG4vKipcbiAqIOuPmeq4sCBwcmVzZW5jZSDsnbjrjbHsiqQuIHJlZ2lzdHJ57J2YIOuFuOuTnCDroZzsu6wg66mU66qo66as7JmAIGluLW1lbW9yeSBzdG9yZeqwgCDtlajqu5gg7JO064ukLlxuICog7Jm467aA66GcIOuCtOuztOuCtOuKlCBwcmVzZW5jZeuKlCDtla3sg4Eg67O17IKs67O47J2064ukLlxuICovXG5leHBvcnQgY2xhc3MgV2ViU29ja2V0UHJlc2VuY2VJbmRleCB7XG4gIHByaXZhdGUgcmVhZG9ubHkgc2Vzc2lvbnMgPSBuZXcgTWFwPHN0cmluZywgUHJlc2VuY2VSZWNvcmQ+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgcm9vbXM6IEJpbmRpbmdJbmRleDxXZWJTb2NrZXRSb29tSWQ+ID0gbmV3IE1hcCgpO1xuICBwcml2YXRlIHJlYWRvbmx5IHVzZXJzOiBCaW5kaW5nSW5kZXg8V2ViU29ja2V0VXNlcklkPiA9IG5ldyBNYXAoKTtcblxuICBoYXMoc2Vzc2lvbklkOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5zZXNzaW9ucy5oYXMoc2Vzc2lvbklkKTtcbiAgfVxuXG4gIHJlZ2lzdGVyKGlucHV0OiBXZWJTb2NrZXRQcmVzZW5jZVJlZ2lzdHJhdGlvbik6IHZvaWQge1xuICAgIC8vIOqwmeydgCDshLjshZjsnYQg64uk7IucIOuTseuhne2VmOuptCDsnbTsoIQg67CU7J2465Sp7J2EIOuovOyggCDsoJXrpqztlbQg6rOg7JWEIOuwlOyduOuUqeydtCDrgqjsp4Ag7JWK6rKMIO2VnOuLpFxuICAgIHRoaXMudW5yZWdpc3RlcihpbnB1dC5zZXNzaW9uSWQpO1xuICAgIHRoaXMuc2Vzc2lvbnMuc2V0KGlucHV0LnNlc3Npb25JZCwge1xuICAgICAgc2Vzc2lvbklkOiBpbnB1dC5zZXNzaW9uSWQsXG4gICAgICBub2RlSWQ6IGlucHV0Lm5vZGVJZCxcbiAgICAgIG5hbWVzcGFjZTogaW5wdXQubmFtZXNwYWNlLFxuICAgICAgYWN0aXZlOiBpbnB1dC5hY3RpdmUsXG4gICAgICByb29tczogbmV3IFNldCgpLFxuICAgICAgY29ubmVjdGVkQXQ6IG5ldyBEYXRlKGlucHV0LmNvbm5lY3RlZEF0LmdldFRpbWUoKSksXG4gICAgICBsYXN0U2VlbkF0OiBuZXcgRGF0ZShpbnB1dC5jb25uZWN0ZWRBdC5nZXRUaW1lKCkpLFxuICAgIH0pO1xuICB9XG5cbiAgYWN0aXZhdGUoc2Vzc2lvbklkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCByZWNvcmQgPSB0aGlzLnNlc3Npb25zLmdldChzZXNzaW9uSWQpO1xuICAgIGlmIChyZWNvcmQpIHtcbiAgICAgIHJlY29yZC5hY3RpdmUgPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIHVucmVnaXN0ZXIoc2Vzc2lvbklkOiBzdHJpbmcpOiBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2UgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IHJlY29yZCA9IHRoaXMuc2Vzc2lvbnMuZ2V0KHNlc3Npb25JZCk7XG4gICAgaWYgKCFyZWNvcmQpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgaWYgKHJlY29yZC51c2VySWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmVtb3ZlQmluZGluZyh0aGlzLnVzZXJzLCByZWNvcmQubmFtZXNwYWNlLCByZWNvcmQudXNlcklkLCBzZXNzaW9uSWQpO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IHJvb21JZCBvZiByZWNvcmQucm9vbXMpIHtcbiAgICAgIHJlbW92ZUJpbmRpbmcodGhpcy5yb29tcywgcmVjb3JkLm5hbWVzcGFjZSwgcm9vbUlkLCBzZXNzaW9uSWQpO1xuICAgIH1cbiAgICB0aGlzLnNlc3Npb25zLmRlbGV0ZShzZXNzaW9uSWQpO1xuICAgIHJldHVybiBjb3B5UHJlc2VuY2UocmVjb3JkKTtcbiAgfVxuXG4gIHRvdWNoKHNlc3Npb25JZDogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgcmVjb3JkID0gdGhpcy5zZXNzaW9ucy5nZXQoc2Vzc2lvbklkKTtcbiAgICBpZiAocmVjb3JkKSB7XG4gICAgICByZWNvcmQubGFzdFNlZW5BdCA9IG5ldyBEYXRlKCk7XG4gICAgfVxuICB9XG5cbiAgc2V0VXNlcklkKHNlc3Npb25JZDogc3RyaW5nLCB1c2VySWQ6IFdlYlNvY2tldFVzZXJJZCk6IHZvaWQge1xuICAgIGNvbnN0IHJlY29yZCA9IHRoaXMuc2Vzc2lvbnMuZ2V0KHNlc3Npb25JZCk7XG4gICAgaWYgKCFyZWNvcmQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAocmVjb3JkLnVzZXJJZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZW1vdmVCaW5kaW5nKHRoaXMudXNlcnMsIHJlY29yZC5uYW1lc3BhY2UsIHJlY29yZC51c2VySWQsIHNlc3Npb25JZCk7XG4gICAgfVxuICAgIHJlY29yZC51c2VySWQgPSB1c2VySWQ7XG4gICAgYWRkQmluZGluZyh0aGlzLnVzZXJzLCByZWNvcmQubmFtZXNwYWNlLCB1c2VySWQsIHNlc3Npb25JZCk7XG4gIH1cblxuICBjbGVhclVzZXJJZChzZXNzaW9uSWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHJlY29yZCA9IHRoaXMuc2Vzc2lvbnMuZ2V0KHNlc3Npb25JZCk7XG4gICAgaWYgKCFyZWNvcmQgfHwgcmVjb3JkLnVzZXJJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgcmVtb3ZlQmluZGluZyh0aGlzLnVzZXJzLCByZWNvcmQubmFtZXNwYWNlLCByZWNvcmQudXNlcklkLCBzZXNzaW9uSWQpO1xuICAgIGRlbGV0ZSByZWNvcmQudXNlcklkO1xuICB9XG5cbiAgam9pbihzZXNzaW9uSWQ6IHN0cmluZywgcm9vbUlkOiBXZWJTb2NrZXRSb29tSWQpOiB2b2lkIHtcbiAgICBjb25zdCByZWNvcmQgPSB0aGlzLnNlc3Npb25zLmdldChzZXNzaW9uSWQpO1xuICAgIGlmICghcmVjb3JkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgcmVjb3JkLnJvb21zLmFkZChyb29tSWQpO1xuICAgIGFkZEJpbmRpbmcodGhpcy5yb29tcywgcmVjb3JkLm5hbWVzcGFjZSwgcm9vbUlkLCBzZXNzaW9uSWQpO1xuICB9XG5cbiAgbGVhdmUoc2Vzc2lvbklkOiBzdHJpbmcsIHJvb21JZDogV2ViU29ja2V0Um9vbUlkKTogdm9pZCB7XG4gICAgY29uc3QgcmVjb3JkID0gdGhpcy5zZXNzaW9ucy5nZXQoc2Vzc2lvbklkKTtcbiAgICBpZiAoIXJlY29yZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHJlY29yZC5yb29tcy5kZWxldGUocm9vbUlkKTtcbiAgICByZW1vdmVCaW5kaW5nKHRoaXMucm9vbXMsIHJlY29yZC5uYW1lc3BhY2UsIHJvb21JZCwgc2Vzc2lvbklkKTtcbiAgfVxuXG4gIGdldENvbm5lY3Rpb24oc2Vzc2lvbklkOiBzdHJpbmcpOiBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2UgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IHJlY29yZCA9IHRoaXMuc2Vzc2lvbnMuZ2V0KHNlc3Npb25JZCk7XG4gICAgcmV0dXJuIHJlY29yZCA/IGNvcHlQcmVzZW5jZShyZWNvcmQpIDogdW5kZWZpbmVkO1xuICB9XG5cbiAgZ2V0Q29ubmVjdGlvbkNvdW50KG5hbWVzcGFjZT86IHN0cmluZyk6IG51bWJlciB7XG4gICAgbGV0IGNvdW50ID0gMDtcbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiB0aGlzLnNlc3Npb25zLnZhbHVlcygpKSB7XG4gICAgICBpZiAocmVjb3JkLmFjdGl2ZSAmJiBtYXRjaGVzTmFtZXNwYWNlKHJlY29yZCwgbmFtZXNwYWNlKSkge1xuICAgICAgICBjb3VudCArPSAxO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gY291bnQ7XG4gIH1cblxuICBnZXRSb29tTWVtYmVycyhyb29tSWQ6IFdlYlNvY2tldFJvb21JZCwgbmFtZXNwYWNlPzogc3RyaW5nKTogV2ViU29ja2V0U2Vzc2lvblByZXNlbmNlW10ge1xuICAgIHJldHVybiB0aGlzLmdldEJvdW5kU2Vzc2lvbnModGhpcy5yb29tcywgcm9vbUlkLCBuYW1lc3BhY2UpLm1hcChjb3B5UHJlc2VuY2UpO1xuICB9XG5cbiAgZ2V0Um9vbU1lbWJlckNvdW50KHJvb21JZDogV2ViU29ja2V0Um9vbUlkLCBuYW1lc3BhY2U/OiBzdHJpbmcpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmdldEJvdW5kU2Vzc2lvbnModGhpcy5yb29tcywgcm9vbUlkLCBuYW1lc3BhY2UpLmxlbmd0aDtcbiAgfVxuXG4gIGdldFN0YXRzKCk6IFdlYlNvY2tldFJlZ2lzdHJ5U3RhdHMge1xuICAgIGNvbnN0IGJ5TmFtZXNwYWNlOiBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+ID0ge307XG4gICAgbGV0IHRvdGFsQ29ubmVjdGlvbnMgPSAwO1xuXG4gICAgZm9yIChjb25zdCByZWNvcmQgb2YgdGhpcy5zZXNzaW9ucy52YWx1ZXMoKSkge1xuICAgICAgaWYgKCFyZWNvcmQuYWN0aXZlKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgYnlOYW1lc3BhY2VbcmVjb3JkLm5hbWVzcGFjZV0gPSAoYnlOYW1lc3BhY2VbcmVjb3JkLm5hbWVzcGFjZV0gPz8gMCkgKyAxO1xuICAgICAgdG90YWxDb25uZWN0aW9ucyArPSAxO1xuICAgIH1cblxuICAgIGxldCB0b3RhbFJvb21zID0gMDtcbiAgICBmb3IgKGNvbnN0IGJpbmRpbmdzIG9mIHRoaXMucm9vbXMudmFsdWVzKCkpIHtcbiAgICAgIHRvdGFsUm9vbXMgKz0gYmluZGluZ3Muc2l6ZTtcbiAgICB9XG5cbiAgICByZXR1cm4geyB0b3RhbENvbm5lY3Rpb25zLCB0b3RhbFJvb21zLCBieU5hbWVzcGFjZSB9O1xuICB9XG5cbiAgLyoqXG4gICAqIGF1ZGllbmNl7JeQIO2VtOuLue2VmOuKlCDtmZzshLEg7IS47IWY7J2EIOykkeuztSDsl4bsnbQg67CY7ZmY7ZWc64ukLiDrsJjtmZjqsJLsnYAg64K067aAIOyDge2DnOulvCDrhbjstpztlZjsp4Ag7JWK64+E66GdIElE7JmAIOuFuOuTnCBJROunjCDri7TripTri6QuXG4gICAqL1xuICBxdWVyeUF1ZGllbmNlKGF1ZGllbmNlOiBXZWJTb2NrZXRBdWRpZW5jZSk6IEFycmF5PHsgc2Vzc2lvbklkOiBzdHJpbmc7IG5vZGVJZDogc3RyaW5nIH0+IHtcbiAgICBjb25zdCBzZWVuID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgY29uc3QgcmVzdWx0czogQXJyYXk8eyBzZXNzaW9uSWQ6IHN0cmluZzsgbm9kZUlkOiBzdHJpbmcgfT4gPSBbXTtcblxuICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIHRoaXMuY29sbGVjdEF1ZGllbmNlKGF1ZGllbmNlKSkge1xuICAgICAgaWYgKHNlZW4uaGFzKHJlY29yZC5zZXNzaW9uSWQpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgc2Vlbi5hZGQocmVjb3JkLnNlc3Npb25JZCk7XG4gICAgICByZXN1bHRzLnB1c2goeyBzZXNzaW9uSWQ6IHJlY29yZC5zZXNzaW9uSWQsIG5vZGVJZDogcmVjb3JkLm5vZGVJZCB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfVxuXG4gIHByaXZhdGUgY29sbGVjdEF1ZGllbmNlKGF1ZGllbmNlOiBXZWJTb2NrZXRBdWRpZW5jZSk6IFByZXNlbmNlUmVjb3JkW10ge1xuICAgIHN3aXRjaCAoYXVkaWVuY2UudHlwZSkge1xuICAgICAgY2FzZSBcImFsbFwiOlxuICAgICAgICByZXR1cm4gWy4uLnRoaXMuc2Vzc2lvbnMudmFsdWVzKCldLmZpbHRlcihcbiAgICAgICAgICAocmVjb3JkKSA9PiByZWNvcmQuYWN0aXZlICYmIG1hdGNoZXNOYW1lc3BhY2UocmVjb3JkLCBhdWRpZW5jZS5uYW1lc3BhY2UpLFxuICAgICAgICApO1xuICAgICAgY2FzZSBcInJvb21cIjpcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2V0Qm91bmRTZXNzaW9ucyh0aGlzLnJvb21zLCBhdWRpZW5jZS5yb29tSWQsIGF1ZGllbmNlLm5hbWVzcGFjZSk7XG4gICAgICBjYXNlIFwidXNlclwiOlxuICAgICAgICByZXR1cm4gdGhpcy5nZXRCb3VuZFNlc3Npb25zKHRoaXMudXNlcnMsIGF1ZGllbmNlLnVzZXJJZCwgYXVkaWVuY2UubmFtZXNwYWNlKTtcbiAgICAgIGNhc2UgXCJjb25uZWN0aW9uc1wiOiB7XG4gICAgICAgIGNvbnN0IHJlY29yZHM6IFByZXNlbmNlUmVjb3JkW10gPSBbXTtcbiAgICAgICAgZm9yIChjb25zdCBzZXNzaW9uSWQgb2YgYXVkaWVuY2UuY29ubmVjdGlvbklkcykge1xuICAgICAgICAgIGNvbnN0IHJlY29yZCA9IHRoaXMuc2Vzc2lvbnMuZ2V0KHNlc3Npb25JZCk7XG4gICAgICAgICAgaWYgKHJlY29yZD8uYWN0aXZlICYmIG1hdGNoZXNOYW1lc3BhY2UocmVjb3JkLCBhdWRpZW5jZS5uYW1lc3BhY2UpKSB7XG4gICAgICAgICAgICByZWNvcmRzLnB1c2gocmVjb3JkKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlY29yZHM7XG4gICAgICB9XG4gICAgICBjYXNlIFwidW5pb25cIjpcbiAgICAgICAgcmV0dXJuIGF1ZGllbmNlLmF1ZGllbmNlcy5mbGF0TWFwKChpdGVtKSA9PiB0aGlzLmNvbGxlY3RBdWRpZW5jZShpdGVtKSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBnZXRCb3VuZFNlc3Npb25zPElkPihcbiAgICBpbmRleDogQmluZGluZ0luZGV4PElkPixcbiAgICBpZDogSWQsXG4gICAgbmFtZXNwYWNlPzogc3RyaW5nLFxuICApOiBQcmVzZW5jZVJlY29yZFtdIHtcbiAgICAvLyBuYW1lc3BhY2XqsIAg7JeG7Jy866m0KOu5iCDrrLjsnpDsl7Qg7Y+s7ZWoKSDrqqjrk6AgbmFtZXNwYWNl7J2YIOuwlOyduOuUqeydhCDrs7jri6RcbiAgICBjb25zdCBuYW1lc3BhY2VzID0gbmFtZXNwYWNlID8gW2luZGV4LmdldChuYW1lc3BhY2UpXSA6IFsuLi5pbmRleC52YWx1ZXMoKV07XG4gICAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICAgIGNvbnN0IHJlY29yZHM6IFByZXNlbmNlUmVjb3JkW10gPSBbXTtcblxuICAgIGZvciAoY29uc3QgYmluZGluZ3Mgb2YgbmFtZXNwYWNlcykge1xuICAgICAgZm9yIChjb25zdCBzZXNzaW9uSWQgb2YgYmluZGluZ3M/LmdldChpZCkgPz8gW10pIHtcbiAgICAgICAgaWYgKHNlZW4uaGFzKHNlc3Npb25JZCkpIHtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBzZWVuLmFkZChzZXNzaW9uSWQpO1xuXG4gICAgICAgIGNvbnN0IHJlY29yZCA9IHRoaXMuc2Vzc2lvbnMuZ2V0KHNlc3Npb25JZCk7XG4gICAgICAgIGlmIChyZWNvcmQ/LmFjdGl2ZSkge1xuICAgICAgICAgIHJlY29yZHMucHVzaChyZWNvcmQpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlY29yZHM7XG4gIH1cbn1cblxuZnVuY3Rpb24gbWF0Y2hlc05hbWVzcGFjZShyZWNvcmQ6IFByZXNlbmNlUmVjb3JkLCBuYW1lc3BhY2U6IHN0cmluZyB8IHVuZGVmaW5lZCk6IGJvb2xlYW4ge1xuICByZXR1cm4gbmFtZXNwYWNlID09PSB1bmRlZmluZWQgfHwgbmFtZXNwYWNlID09PSBcIlwiIHx8IHJlY29yZC5uYW1lc3BhY2UgPT09IG5hbWVzcGFjZTtcbn1cblxuZnVuY3Rpb24gY29weVByZXNlbmNlKHJlY29yZDogUHJlc2VuY2VSZWNvcmQpOiBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2Uge1xuICBjb25zdCBjb3B5OiBXZWJTb2NrZXRTZXNzaW9uUHJlc2VuY2UgPSB7XG4gICAgc2Vzc2lvbklkOiByZWNvcmQuc2Vzc2lvbklkLFxuICAgIG5vZGVJZDogcmVjb3JkLm5vZGVJZCxcbiAgICBuYW1lc3BhY2U6IHJlY29yZC5uYW1lc3BhY2UsXG4gICAgYWN0aXZlOiByZWNvcmQuYWN0aXZlLFxuICAgIHJvb21zOiBuZXcgU2V0KHJlY29yZC5yb29tcyksXG4gICAgY29ubmVjdGVkQXQ6IG5ldyBEYXRlKHJlY29yZC5jb25uZWN0ZWRBdC5nZXRUaW1lKCkpLFxuICAgIGxhc3RTZWVuQXQ6IG5ldyBEYXRlKHJlY29yZC5sYXN0U2VlbkF0LmdldFRpbWUoKSksXG4gIH07XG4gIGlmIChyZWNvcmQudXNlcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICBjb3B5LnVzZXJJZCA9IHJlY29yZC51c2VySWQ7XG4gIH1cbiAgcmV0dXJuIGNvcHk7XG59XG5cbmZ1bmN0aW9uIGFkZEJpbmRpbmc8SWQ+KGluZGV4OiBCaW5kaW5nSW5kZXg8SWQ+LCBuYW1lc3BhY2U6IHN0cmluZywgaWQ6IElkLCBzZXNzaW9uSWQ6IHN0cmluZykge1xuICBjb25zdCBiaW5kaW5ncyA9IGluZGV4LmdldChuYW1lc3BhY2UpID8/IG5ldyBNYXA8SWQsIFNldDxzdHJpbmc+PigpO1xuICBjb25zdCBtZW1iZXJzID0gYmluZGluZ3MuZ2V0KGlkKSA/PyBuZXcgU2V0PHN0cmluZz4oKTtcbiAgbWVtYmVycy5hZGQoc2Vzc2lvbklkKTtcbiAgYmluZGluZ3Muc2V0KGlkLCBtZW1iZXJzKTtcbiAgaW5kZXguc2V0KG5hbWVzcGFjZSwgYmluZGluZ3MpO1xufVxuXG5mdW5jdGlvbiByZW1vdmVCaW5kaW5nPElkPihcbiAgaW5kZXg6IEJpbmRpbmdJbmRleDxJZD4sXG4gIG5hbWVzcGFjZTogc3RyaW5nLFxuICBpZDogSWQsXG4gIHNlc3Npb25JZDogc3RyaW5nLFxuKTogdm9pZCB7XG4gIGNvbnN0IGJpbmRpbmdzID0gaW5kZXguZ2V0KG5hbWVzcGFjZSk7XG4gIGNvbnN0IG1lbWJlcnMgPSBiaW5kaW5ncz8uZ2V0KGlkKTtcbiAgaWYgKCFiaW5kaW5ncyB8fCAhbWVtYmVycykge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIG1lbWJlcnMuZGVsZXRlKHNlc3Npb25JZCk7XG4gIGlmIChtZW1iZXJzLnNpemUgPT09IDApIHtcbiAgICBiaW5kaW5ncy5kZWxldGUoaWQpO1xuICB9XG4gIGlmIChiaW5kaW5ncy5zaXplID09PSAwKSB7XG4gICAgaW5kZXguZGVsZXRlKG5hbWVzcGFjZSk7XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7QUFpT0EsU0FBUyxpQkFBaUIsUUFBd0IsV0FBd0M7Q0FDeEYsT0FBTyxjQUFjLFVBQWEsY0FBYyxNQUFNLE9BQU8sY0FBYztBQUM3RTtBQUVBLFNBQVMsYUFBYSxRQUFrRDtDQUN0RSxNQUFNLE9BQWlDO0VBQ3JDLFdBQVcsT0FBTztFQUNsQixRQUFRLE9BQU87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
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type WebSocketAudience } from "./ws-audience";
|
|
2
|
-
import { type
|
|
2
|
+
import { type MaybePromise, type WebSocketRoomId, type WebSocketUserId } from "./ws-core";
|
|
3
|
+
/**
|
|
4
|
+
* 세션 presence snapshot. live 객체가 아니므로 값을 바꿔도 registry나 store 상태에 영향을 주지 않는다.
|
|
5
|
+
*/
|
|
3
6
|
export type WebSocketSessionPresence = {
|
|
4
7
|
sessionId: string;
|
|
5
8
|
nodeId: string;
|
|
@@ -10,52 +13,49 @@ export type WebSocketSessionPresence = {
|
|
|
10
13
|
lastSeenAt: Date;
|
|
11
14
|
userId?: WebSocketUserId;
|
|
12
15
|
};
|
|
16
|
+
export type WebSocketPresenceRegistration = {
|
|
17
|
+
sessionId: string;
|
|
18
|
+
nodeId: string;
|
|
19
|
+
namespace: string;
|
|
20
|
+
active: boolean;
|
|
21
|
+
connectedAt: Date;
|
|
22
|
+
};
|
|
23
|
+
export type WebSocketRoutingPlan = {
|
|
24
|
+
localSessionIds: string[];
|
|
25
|
+
remoteNodeIds: string[];
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 클러스터 presence replica 계약. 각 노드의 registry가 자기 세션의 mutation을 세션별 호출 순서대로 전달한다.
|
|
29
|
+
* 모든 메서드는 값이나 promise를 반환할 수 있고, registry는 동기 throw도 rejection으로 바꿔 처리한다.
|
|
30
|
+
* userId는 타입까지 일치해야 매칭된다(1과 "1"은 다르다).
|
|
31
|
+
*/
|
|
13
32
|
export interface WebSocketPresenceStore {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
join(sessionId: string, roomId: WebSocketRoomId): void;
|
|
26
|
-
leave(sessionId: string, roomId: WebSocketRoomId): void;
|
|
27
|
-
getConnection(sessionId: string): WebSocketSessionPresence | undefined;
|
|
28
|
-
getConnectionCount(namespace?: string): number;
|
|
29
|
-
getRoomMembers(roomId: WebSocketRoomId, namespace?: string): WebSocketSessionPresence[];
|
|
30
|
-
getStats(): WebSocketRegistryStats;
|
|
31
|
-
queryAudience(audience: WebSocketAudience): WebSocketSessionPresence[];
|
|
33
|
+
readonly nodeId?: string;
|
|
34
|
+
register(input: WebSocketPresenceRegistration): MaybePromise<void>;
|
|
35
|
+
activate(sessionId: string): MaybePromise<void>;
|
|
36
|
+
unregister(sessionId: string): MaybePromise<void>;
|
|
37
|
+
setUserId(sessionId: string, userId: WebSocketUserId): MaybePromise<void>;
|
|
38
|
+
clearUserId(sessionId: string): MaybePromise<void>;
|
|
39
|
+
join(sessionId: string, roomId: WebSocketRoomId): MaybePromise<void>;
|
|
40
|
+
leave(sessionId: string, roomId: WebSocketRoomId): MaybePromise<void>;
|
|
41
|
+
resolveRoute(audience: WebSocketAudience, nodeId: string): MaybePromise<WebSocketRoutingPlan>;
|
|
42
|
+
getRoomMemberCount(roomId: WebSocketRoomId, namespace?: string): MaybePromise<number>;
|
|
43
|
+
shutdown?(): MaybePromise<void>;
|
|
32
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* 프로세스 메모리에 presence를 두는 store. 단일 노드의 기본값이며, 같은 프로세스의 여러 runtime이 공유하면 클러스터 replica로도 동작한다.
|
|
47
|
+
*/
|
|
33
48
|
export declare class InMemoryWebSocketPresenceStore implements WebSocketPresenceStore {
|
|
34
|
-
private readonly
|
|
35
|
-
|
|
36
|
-
private readonly users;
|
|
37
|
-
register(input: {
|
|
38
|
-
sessionId: string;
|
|
39
|
-
nodeId: string;
|
|
40
|
-
namespace: string;
|
|
41
|
-
active?: boolean;
|
|
42
|
-
}): WebSocketSessionPresence;
|
|
49
|
+
private readonly index;
|
|
50
|
+
register(input: WebSocketPresenceRegistration): void;
|
|
43
51
|
activate(sessionId: string): void;
|
|
44
|
-
unregister(sessionId: string):
|
|
45
|
-
touch(sessionId: string): void;
|
|
52
|
+
unregister(sessionId: string): void;
|
|
46
53
|
setUserId(sessionId: string, userId: WebSocketUserId): void;
|
|
47
54
|
clearUserId(sessionId: string): void;
|
|
48
55
|
join(sessionId: string, roomId: WebSocketRoomId): void;
|
|
49
56
|
leave(sessionId: string, roomId: WebSocketRoomId): void;
|
|
57
|
+
resolveRoute(audience: WebSocketAudience, nodeId: string): WebSocketRoutingPlan;
|
|
58
|
+
getRoomMemberCount(roomId: WebSocketRoomId, namespace?: string): number;
|
|
50
59
|
getConnection(sessionId: string): WebSocketSessionPresence | undefined;
|
|
51
|
-
getConnectionCount(namespace?: string): number;
|
|
52
|
-
getRoomMembers(roomId: WebSocketRoomId, namespace?: string): WebSocketSessionPresence[];
|
|
53
|
-
getStats(): WebSocketRegistryStats;
|
|
54
|
-
queryAudience(audience: WebSocketAudience): WebSocketSessionPresence[];
|
|
55
|
-
private getActiveConnections;
|
|
56
|
-
private getBoundConnections;
|
|
57
|
-
private getScopedBindings;
|
|
58
|
-
private removeRoomBinding;
|
|
59
|
-
private removeUserBinding;
|
|
60
60
|
}
|
|
61
61
|
//# sourceMappingURL=ws-presence-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-presence-store.d.ts","sourceRoot":"","sources":["../../src/stream/ws-presence-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"ws-presence-store.d.ts","sourceRoot":"","sources":["../../src/stream/ws-presence-store.ts"],"names":[],"mappings":"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"}
|