@lyxa.ai/core 1.2.20 → 1.2.21
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/libraries/socket/adapters/socket-subscription.js +9 -18
- package/dist/libraries/socket/adapters/socket-subscription.js.map +1 -1
- package/dist/libraries/socket/core/index.d.ts +2 -0
- package/dist/libraries/socket/core/index.js +2 -0
- package/dist/libraries/socket/core/index.js.map +1 -1
- package/dist/libraries/socket/core/local-subscription-registry.d.ts +3 -5
- package/dist/libraries/socket/core/local-subscription-registry.js +26 -39
- package/dist/libraries/socket/core/local-subscription-registry.js.map +1 -1
- package/dist/libraries/socket/core/socket-event-publisher.js +55 -28
- package/dist/libraries/socket/core/socket-event-publisher.js.map +1 -1
- package/dist/libraries/socket/core/socket-redis-connections.d.ts +16 -6
- package/dist/libraries/socket/core/socket-redis-connections.js +278 -61
- package/dist/libraries/socket/core/socket-redis-connections.js.map +1 -1
- package/dist/types/README.md +1 -1
- package/dist/types/package.json +1 -1
- package/package.json +1 -1
|
@@ -5,33 +5,24 @@ const index_1 = require("../index");
|
|
|
5
5
|
const crypto_1 = require("crypto");
|
|
6
6
|
const instance_utils_1 = require("../../../utilities/instance.utils");
|
|
7
7
|
const ws_1 = require("ws");
|
|
8
|
-
async function* _createSocketSubscription(
|
|
9
|
-
const {
|
|
10
|
-
const
|
|
8
|
+
async function* _createSocketSubscription(eventType, opts) {
|
|
9
|
+
const { ctx, input } = opts;
|
|
10
|
+
const entity = { id: ctx.entity.id, type: ctx.entity.type };
|
|
11
|
+
const eventKey = index_1.EventFactory.getSubscriptionKey(eventType, input);
|
|
11
12
|
const socketId = (0, crypto_1.randomUUID)();
|
|
13
|
+
const socketInfo = { socketId, eventKey, instanceId: (0, instance_utils_1.getInstanceId)() };
|
|
12
14
|
const localEE = new index_1.LocalEventEmitter(socketId);
|
|
13
|
-
|
|
14
|
-
const entityType = ctx.entity.type;
|
|
15
|
-
const entity = { id: entityId, type: entityType };
|
|
16
|
-
const eventKey = index_1.EventFactory.getSubscriptionKey(eventType, input);
|
|
17
|
-
const instanceId = (0, instance_utils_1.getInstanceId)();
|
|
18
|
-
const socketInfo = { socketId, eventKey, instanceId };
|
|
19
|
-
await (0, index_1.connectEntityToSocketEvent)(ctx.entity, socketInfo);
|
|
15
|
+
await (0, index_1.connectEntityToSocketEvent)(entity, socketInfo);
|
|
20
16
|
index_1.LocalSubs.bind(entity, eventKey, localEE);
|
|
21
17
|
ctx.signal?.addEventListener('abort', () => {
|
|
22
18
|
index_1.LocalSubs.unbind(entity, eventKey, localEE);
|
|
23
|
-
(0, index_1.disconnectEntityFromSocketEvent)(
|
|
19
|
+
(0, index_1.disconnectEntityFromSocketEvent)(entity, socketInfo).catch(console.error);
|
|
24
20
|
});
|
|
25
21
|
for await (const [data] of (0, ws_1.on)(localEE.ee, eventKey, { signal: ctx.signal })) {
|
|
26
|
-
|
|
27
|
-
yield payload;
|
|
22
|
+
yield data;
|
|
28
23
|
}
|
|
29
24
|
}
|
|
30
25
|
function createSocketSubscriptionAdapter(eventType) {
|
|
31
|
-
return (opts) => _createSocketSubscription(
|
|
32
|
-
eventType,
|
|
33
|
-
ctx: opts.ctx,
|
|
34
|
-
input: opts.input,
|
|
35
|
-
});
|
|
26
|
+
return (opts) => _createSocketSubscription(eventType, opts);
|
|
36
27
|
}
|
|
37
28
|
//# sourceMappingURL=socket-subscription.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-subscription.js","sourceRoot":"/","sources":["libraries/socket/adapters/socket-subscription.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"socket-subscription.js","sourceRoot":"/","sources":["libraries/socket/adapters/socket-subscription.ts"],"names":[],"mappings":";;AAuDA,0EAEC;AAxDD,oCAQkB;AAClB,mCAAoC;AACpC,sEAAkE;AAClE,2BAAwB;AAaxB,KAAK,SAAS,CAAC,CAAC,yBAAyB,CACxC,SAAqB,EACrB,IAA2C;IAE3C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC5B,MAAM,MAAM,GAAuB,EAAE,EAAE,EAAE,GAAG,CAAC,MAAO,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAO,CAAC,IAAI,EAAE,CAAC;IAElF,MAAM,QAAQ,GAAG,oBAAY,CAAC,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC9B,MAAM,UAAU,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAA,8BAAa,GAAE,EAAE,CAAC;IAEvE,MAAM,OAAO,GAAG,IAAI,yBAAiB,CAAC,QAAQ,CAAC,CAAC;IAGhD,MAAM,IAAA,kCAA0B,EAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrD,iBAAS,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAG1C,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1C,iBAAS,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAA,uCAA+B,EAAC,MAAM,EAAE,UAAU,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAIH,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,IAAA,OAAE,EAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;QAC7E,MAAM,IAAW,CAAC;IACnB,CAAC;AACF,CAAC;AAED,SAAgB,+BAA+B,CAA6C,SAAqB;IAChH,OAAO,CAAC,IAA2C,EAAE,EAAE,CAAC,yBAAyB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AACpG,CAAC","sourcesContent":["import { SocketEventType } from '../../../utilities/enum';\nimport {\n\tconnectEntityToSocketEvent,\n\tdisconnectEntityFromSocketEvent,\n\tEntitySubscription,\n\tEventFactory,\n\tLocalEventEmitter,\n\tLocalSubs,\n\tOutputFor,\n} from '../index';\nimport { randomUUID } from 'crypto';\nimport { getInstanceId } from '../../../utilities/instance.utils';\nimport { on } from 'ws';\nimport { AuthEntityType } from '../../auth';\n\ninterface _SubscriptionContext {\n\tentity?: { id: string; type: AuthEntityType };\n\tsignal?: AbortSignal;\n}\n\ninterface _SubscriptionOpts<TEventType extends SocketEventType, TInput> {\n\tctx: _SubscriptionContext;\n\tinput: TInput;\n}\n\nasync function* _createSocketSubscription<TEventType extends SocketEventType, TInput>(\n\teventType: TEventType,\n\topts: _SubscriptionOpts<TEventType, TInput>\n) {\n\tconst { ctx, input } = opts;\n\tconst entity: EntitySubscription = { id: ctx.entity!.id, type: ctx.entity!.type };\n\n\tconst eventKey = EventFactory.getSubscriptionKey(eventType, input);\n\tconst socketId = randomUUID();\n\tconst socketInfo = { socketId, eventKey, instanceId: getInstanceId() };\n\n\tconst localEE = new LocalEventEmitter(socketId);\n\n\t// Setup: connect to Redis and local registry\n\tawait connectEntityToSocketEvent(entity, socketInfo);\n\tLocalSubs.bind(entity, eventKey, localEE);\n\n\t// Cleanup on abort\n\tctx.signal?.addEventListener('abort', () => {\n\t\tLocalSubs.unbind(entity, eventKey, localEE);\n\t\tdisconnectEntityFromSocketEvent(entity, socketInfo).catch(console.error);\n\t});\n\n\t// Stream events\n\ttype Out = OutputFor<TEventType>;\n\tfor await (const [data] of on(localEE.ee, eventKey, { signal: ctx.signal })) {\n\t\tyield data as Out;\n\t}\n}\n\nexport function createSocketSubscriptionAdapter<TEventType extends SocketEventType, TInput>(eventType: TEventType) {\n\treturn (opts: _SubscriptionOpts<TEventType, TInput>) => _createSocketSubscription(eventType, opts);\n}\n\n/**\n * @Example\n * const socketRouterTwo = createTRPCRouter({\n * \tonMessage: createAuthenticatedProcedure({ entityTypes: [AuthEntityType.CRM] })\n * \t\t.input(ChatroomMessageSendSocketInputSchema)\n * \t\t.output(zAsyncIterable({ yield: ChatroomMessageSendOutputSchema }))\n * \t\t.subscription(createSocketSubscriptionAdapter(SocketEventType.CHATROOM_MESSAGE_SEND)),\n * });\n */\n"]}
|
|
@@ -19,4 +19,6 @@ __exportStar(require("./local-subscription-registry"), exports);
|
|
|
19
19
|
__exportStar(require("./socket-event-registry"), exports);
|
|
20
20
|
__exportStar(require("./zAsyncIterable"), exports);
|
|
21
21
|
__exportStar(require("./socket-redis-connections"), exports);
|
|
22
|
+
__exportStar(require("./socket-event-publisher"), exports);
|
|
23
|
+
__exportStar(require("./socket-event-consumer"), exports);
|
|
22
24
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/socket/core/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,gEAA8C;AAC9C,0DAAwC;AACxC,mDAAiC;AACjC,6DAA2C","sourcesContent":["export * from '../BaseSocketEvent';\nexport * from './local-subscription-registry';\nexport * from './socket-event-registry';\nexport * from './zAsyncIterable';\nexport * from './socket-redis-connections';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/socket/core/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,gEAA8C;AAC9C,0DAAwC;AACxC,mDAAiC;AACjC,6DAA2C;AAC3C,2DAAyC;AACzC,0DAAwC","sourcesContent":["export * from '../BaseSocketEvent';\nexport * from './local-subscription-registry';\nexport * from './socket-event-registry';\nexport * from './zAsyncIterable';\nexport * from './socket-redis-connections';\nexport * from './socket-event-publisher';\nexport * from './socket-event-consumer';\n"]}
|
|
@@ -3,14 +3,12 @@ import { EntitySubscription } from './socket-redis-connections';
|
|
|
3
3
|
type EventKey = string;
|
|
4
4
|
type SocketId = string;
|
|
5
5
|
export declare class LocalEventEmitter {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
readonly ee: EventEmitter;
|
|
7
|
+
readonly socketId: SocketId;
|
|
8
8
|
constructor(socketId: SocketId);
|
|
9
|
-
get socketId(): string;
|
|
10
|
-
get ee(): EventEmitter<[never]>;
|
|
11
9
|
}
|
|
12
10
|
declare class LocalSubscriptionRegistry {
|
|
13
|
-
private
|
|
11
|
+
private listeners;
|
|
14
12
|
bind(entity: EntitySubscription, eventKey: EventKey, ee: LocalEventEmitter): void;
|
|
15
13
|
unbind(entity: EntitySubscription, eventKey: EventKey, ee: LocalEventEmitter): void;
|
|
16
14
|
emit(entity: EntitySubscription, eventKey: EventKey, payload: unknown): void;
|
|
@@ -5,63 +5,50 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.LocalSubs = exports.LocalEventEmitter = void 0;
|
|
7
7
|
const events_1 = __importDefault(require("events"));
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
8
|
+
const keyCache = new Map();
|
|
9
|
+
const entityKey = (entity) => {
|
|
10
|
+
const raw = `${entity.type ?? 'default'}:${entity.id}`;
|
|
11
|
+
let cached = keyCache.get(raw);
|
|
12
|
+
if (!cached) {
|
|
13
|
+
cached = raw;
|
|
14
|
+
keyCache.set(raw, cached);
|
|
15
|
+
}
|
|
16
|
+
return cached;
|
|
17
|
+
};
|
|
11
18
|
class LocalEventEmitter {
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
ee;
|
|
20
|
+
socketId;
|
|
14
21
|
constructor(socketId) {
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
|
|
18
|
-
get socketId() {
|
|
19
|
-
return this._socketId;
|
|
20
|
-
}
|
|
21
|
-
get ee() {
|
|
22
|
-
return this._ee;
|
|
22
|
+
this.ee = new events_1.default();
|
|
23
|
+
this.ee.setMaxListeners(0);
|
|
24
|
+
this.socketId = socketId;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
exports.LocalEventEmitter = LocalEventEmitter;
|
|
26
28
|
class LocalSubscriptionRegistry {
|
|
27
|
-
|
|
29
|
+
listeners = new Map();
|
|
28
30
|
bind(entity, eventKey, ee) {
|
|
29
|
-
const key = entityKey(entity)
|
|
30
|
-
let
|
|
31
|
-
if (!eventMap) {
|
|
32
|
-
eventMap = new Map();
|
|
33
|
-
this.byEntity.set(key, eventMap);
|
|
34
|
-
}
|
|
35
|
-
let listenerSet = eventMap.get(eventKey);
|
|
31
|
+
const key = `${entityKey(entity)}:${eventKey}`;
|
|
32
|
+
let listenerSet = this.listeners.get(key);
|
|
36
33
|
if (!listenerSet) {
|
|
37
34
|
listenerSet = new Set();
|
|
38
|
-
|
|
35
|
+
this.listeners.set(key, listenerSet);
|
|
39
36
|
}
|
|
40
37
|
listenerSet.add(ee);
|
|
41
|
-
console.log(`[LocalSubs] Bound ${key} -> ${eventKey}, socket=${ee.socketId}`);
|
|
42
38
|
}
|
|
43
39
|
unbind(entity, eventKey, ee) {
|
|
44
|
-
const key = entityKey(entity)
|
|
45
|
-
const
|
|
46
|
-
if (!eventMap)
|
|
47
|
-
return;
|
|
48
|
-
const listenerSet = eventMap.get(eventKey);
|
|
40
|
+
const key = `${entityKey(entity)}:${eventKey}`;
|
|
41
|
+
const listenerSet = this.listeners.get(key);
|
|
49
42
|
if (listenerSet) {
|
|
50
43
|
listenerSet.delete(ee);
|
|
51
|
-
if (listenerSet.size === 0)
|
|
52
|
-
|
|
44
|
+
if (listenerSet.size === 0) {
|
|
45
|
+
this.listeners.delete(key);
|
|
46
|
+
}
|
|
53
47
|
}
|
|
54
|
-
if (eventMap.size === 0)
|
|
55
|
-
this.byEntity.delete(key);
|
|
56
|
-
console.log(`[LocalSubs] Unbound ${key} -> ${eventKey}, socket=${ee.socketId}`);
|
|
57
48
|
}
|
|
58
49
|
emit(entity, eventKey, payload) {
|
|
59
|
-
const key = entityKey(entity)
|
|
60
|
-
const
|
|
61
|
-
const listeners = eventMap?.get(eventKey);
|
|
62
|
-
console.log('LocalSubs emit called', {
|
|
63
|
-
payload,
|
|
64
|
-
});
|
|
50
|
+
const key = `${entityKey(entity)}:${eventKey}`;
|
|
51
|
+
const listeners = this.listeners.get(key);
|
|
65
52
|
if (!listeners)
|
|
66
53
|
return;
|
|
67
54
|
for (const listener of listeners) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-subscription-registry.js","sourceRoot":"/","sources":["libraries/socket/core/local-subscription-registry.ts"],"names":[],"mappings":";;;;;;AAAA,oDAAkC;AAOlC,
|
|
1
|
+
{"version":3,"file":"local-subscription-registry.js","sourceRoot":"/","sources":["libraries/socket/core/local-subscription-registry.ts"],"names":[],"mappings":";;;;;;AAAA,oDAAkC;AAOlC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;AAC3C,MAAM,SAAS,GAAG,CAAC,MAA0B,EAAU,EAAE;IACxD,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,SAAS,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;IACvD,IAAI,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,CAAC;QACb,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAGF,MAAa,iBAAiB;IACpB,EAAE,CAAe;IACjB,QAAQ,CAAW;IAE5B,YAAY,QAAkB;QAC7B,IAAI,CAAC,EAAE,GAAG,IAAI,gBAAY,EAAE,CAAC;QAC7B,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;CACD;AATD,8CASC;AAED,MAAM,yBAAyB;IAEtB,SAAS,GAAG,IAAI,GAAG,EAAkC,CAAC;IAE9D,IAAI,CAAC,MAA0B,EAAE,QAAkB,EAAE,EAAqB;QACzE,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAC/C,IAAI,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE1C,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;YACxB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACtC,CAAC;QAED,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,CAAC,MAA0B,EAAE,QAAkB,EAAE,EAAqB;QAC3E,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE5C,IAAI,WAAW,EAAE,CAAC;YACjB,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACvB,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC5B,CAAC;QACF,CAAC;IACF,CAAC;IAGD,IAAI,CAAC,MAA0B,EAAE,QAAkB,EAAE,OAAgB;QACpE,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE1C,IAAI,CAAC,SAAS;YAAE,OAAO;QAGvB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YAClC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;IACF,CAAC;CACD;AAEY,QAAA,SAAS,GAAG,IAAI,yBAAyB,EAAE,CAAC","sourcesContent":["import EventEmitter from 'events';\nimport { EntitySubscription } from './socket-redis-connections';\n\ntype EventKey = string;\ntype SocketId = string;\n\n// Zero-overhead key with memoization\nconst keyCache = new Map<string, string>();\nconst entityKey = (entity: EntitySubscription): string => {\n\tconst raw = `${entity.type ?? 'default'}:${entity.id}`;\n\tlet cached = keyCache.get(raw);\n\tif (!cached) {\n\t\tcached = raw;\n\t\tkeyCache.set(raw, cached);\n\t}\n\treturn cached;\n};\n\n// Remove wrapper class - direct EventEmitter usage\nexport class LocalEventEmitter {\n\treadonly ee: EventEmitter;\n\treadonly socketId: SocketId;\n\n\tconstructor(socketId: SocketId) {\n\t\tthis.ee = new EventEmitter();\n\t\tthis.ee.setMaxListeners(0); // Unlimited listeners\n\t\tthis.socketId = socketId;\n\t}\n}\n\nclass LocalSubscriptionRegistry {\n\t// Direct map for O(1) lookups with minimal overhead\n\tprivate listeners = new Map<string, Set<LocalEventEmitter>>();\n\n\tbind(entity: EntitySubscription, eventKey: EventKey, ee: LocalEventEmitter): void {\n\t\tconst key = `${entityKey(entity)}:${eventKey}`;\n\t\tlet listenerSet = this.listeners.get(key);\n\n\t\tif (!listenerSet) {\n\t\t\tlistenerSet = new Set();\n\t\t\tthis.listeners.set(key, listenerSet);\n\t\t}\n\n\t\tlistenerSet.add(ee);\n\t}\n\n\tunbind(entity: EntitySubscription, eventKey: EventKey, ee: LocalEventEmitter): void {\n\t\tconst key = `${entityKey(entity)}:${eventKey}`;\n\t\tconst listenerSet = this.listeners.get(key);\n\n\t\tif (listenerSet) {\n\t\t\tlistenerSet.delete(ee);\n\t\t\tif (listenerSet.size === 0) {\n\t\t\t\tthis.listeners.delete(key);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Zero-overhead emit - inline fast path\n\temit(entity: EntitySubscription, eventKey: EventKey, payload: unknown): void {\n\t\tconst key = `${entityKey(entity)}:${eventKey}`;\n\t\tconst listeners = this.listeners.get(key);\n\n\t\tif (!listeners) return;\n\n\t\t// Direct iteration - fastest possible\n\t\tfor (const listener of listeners) {\n\t\t\tlistener.ee.emit(eventKey, payload);\n\t\t}\n\t}\n}\n\nexport const LocalSubs = new LocalSubscriptionRegistry();\n"]}
|
|
@@ -2,40 +2,49 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.publishSocketEvent = publishSocketEvent;
|
|
4
4
|
exports.publishNotificationSocketEvent = publishNotificationSocketEvent;
|
|
5
|
-
const __1 = require("../../..");
|
|
6
5
|
const socket_redis_connections_1 = require("./socket-redis-connections");
|
|
7
6
|
const event_1 = require("../../event");
|
|
8
7
|
const PushNotificationEvent_1 = require("../PushNotificationEvent");
|
|
9
8
|
const enum_1 = require("../../../utilities/enum");
|
|
10
|
-
|
|
9
|
+
const PUBLISH_OPTIONS = {
|
|
10
|
+
contentType: 'application/json',
|
|
11
|
+
persistent: true,
|
|
12
|
+
};
|
|
13
|
+
function publishToInstances(recipientsWithInstances, localKey, payload) {
|
|
14
|
+
if (recipientsWithInstances.length === 0)
|
|
15
|
+
return;
|
|
11
16
|
const channel = (0, event_1.getChannel)();
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
const message = { localKey, payload, recipient: null };
|
|
18
|
+
for (let i = 0; i < recipientsWithInstances.length; i++) {
|
|
19
|
+
const { recipient, instances } = recipientsWithInstances[i];
|
|
20
|
+
message.recipient = recipient;
|
|
21
|
+
const messageStr = JSON.stringify(message);
|
|
22
|
+
for (let j = 0; j < instances.length; j++) {
|
|
23
|
+
const routingKey = `${event_1.INSTANCE_ROUTE_PREFIX}.${instances[j]}`;
|
|
24
|
+
try {
|
|
25
|
+
channel.publish(event_1.EXCHANGE_NAME, routingKey, Buffer.from(messageStr, 'utf8'), PUBLISH_OPTIONS);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(`[Socket] Publish failed: ${routingKey}`, error);
|
|
29
|
+
}
|
|
25
30
|
}
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
33
|
async function getActiveSubscribedRecipients(recipients, localKey) {
|
|
29
|
-
|
|
34
|
+
if (recipients.length === 0) {
|
|
35
|
+
return { subscribed: [], unsubscribed: [] };
|
|
36
|
+
}
|
|
37
|
+
const batchInput = recipients.map(recipient => ({ entity: recipient, eventKey: localKey }));
|
|
38
|
+
const resultsMap = await (0, socket_redis_connections_1.getEntitySubscriptionInfoBatch)(batchInput);
|
|
30
39
|
const subscribed = [];
|
|
31
40
|
const unsubscribed = [];
|
|
32
|
-
for (
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
subscribed.push(recipient);
|
|
41
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
42
|
+
const recipient = recipients[i];
|
|
43
|
+
const entityKeyStr = `${recipient.type ?? 'default'}:${recipient.id}`;
|
|
44
|
+
const cacheKey = `${entityKeyStr}:${localKey}`;
|
|
45
|
+
const info = resultsMap.get(cacheKey);
|
|
46
|
+
if (info && info.isSubscribed && info.instances.length > 0) {
|
|
47
|
+
subscribed.push({ recipient, instances: info.instances });
|
|
39
48
|
}
|
|
40
49
|
else {
|
|
41
50
|
unsubscribed.push(recipient);
|
|
@@ -44,25 +53,43 @@ async function getActiveSubscribedRecipients(recipients, localKey) {
|
|
|
44
53
|
return { subscribed, unsubscribed };
|
|
45
54
|
}
|
|
46
55
|
async function publishSocketEvent(event, recipients) {
|
|
56
|
+
if (recipients.length === 0)
|
|
57
|
+
return;
|
|
47
58
|
const localKey = event.getSubscriptionKey();
|
|
48
59
|
const payload = event.payload();
|
|
49
60
|
const { subscribed, unsubscribed } = await getActiveSubscribedRecipients(recipients, localKey);
|
|
50
61
|
if (subscribed.length > 0) {
|
|
51
|
-
|
|
62
|
+
publishToInstances(subscribed, localKey, payload);
|
|
52
63
|
}
|
|
53
64
|
if (unsubscribed.length > 0) {
|
|
54
65
|
const notificationEvent = event.notification?.();
|
|
55
66
|
const pushNotificationEvent = event.pushNotification?.();
|
|
56
67
|
if (pushNotificationEvent) {
|
|
57
|
-
|
|
58
|
-
await (0, event_1.publishEvent)(pushNotificationEvent);
|
|
68
|
+
(0, event_1.publishEvent)(new PushNotificationEvent_1.PushNotificationEvent(enum_1.PushNotificationEventType.CHATROOM_MESSAGE_SEND, payload)).catch(err => console.error('[Socket] Push failed:', err));
|
|
59
69
|
}
|
|
60
70
|
if (notificationEvent) {
|
|
61
|
-
|
|
71
|
+
publishNotificationSocketEvent(notificationEvent, unsubscribed).catch(err => console.error('[Socket] Notification failed:', err));
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
async function publishNotificationSocketEvent(event, recipients) {
|
|
66
|
-
|
|
76
|
+
if (recipients.length === 0)
|
|
77
|
+
return;
|
|
78
|
+
const eventKey = event.getSubscriptionKey();
|
|
79
|
+
const batchInput = recipients.map(recipient => ({ entity: recipient, eventKey }));
|
|
80
|
+
const resultsMap = await (0, socket_redis_connections_1.getEntitySubscriptionInfoBatch)(batchInput);
|
|
81
|
+
const validInstances = [];
|
|
82
|
+
for (let i = 0; i < recipients.length; i++) {
|
|
83
|
+
const recipient = recipients[i];
|
|
84
|
+
const entityKeyStr = `${recipient.type ?? 'default'}:${recipient.id}`;
|
|
85
|
+
const cacheKey = `${entityKeyStr}:${eventKey}`;
|
|
86
|
+
const info = resultsMap.get(cacheKey);
|
|
87
|
+
if (info && info.instances.length > 0) {
|
|
88
|
+
validInstances.push({ recipient, instances: info.instances });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (validInstances.length > 0) {
|
|
92
|
+
publishToInstances(validInstances, eventKey, event.payload());
|
|
93
|
+
}
|
|
67
94
|
}
|
|
68
95
|
//# sourceMappingURL=socket-event-publisher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-event-publisher.js","sourceRoot":"/","sources":["libraries/socket/core/socket-event-publisher.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"socket-event-publisher.js","sourceRoot":"/","sources":["libraries/socket/core/socket-event-publisher.ts"],"names":[],"mappings":"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","sourcesContent":["import { getLibraries } from '../../..';\nimport { BaseSocketEvent } from '../BaseSocketEvent';\nimport { EntitySubscription, getEntitySubscriptionInfo, getEntitySubscriptionInfoBatch } from './socket-redis-connections';\nimport { NotificationSocketEvent } from '../events/notification.socket.event';\nimport { EXCHANGE_NAME, getChannel, INSTANCE_ROUTE_PREFIX, publishEvent } from '../../event';\nimport { PushNotificationEvent } from '../PushNotificationEvent';\nimport { PushNotificationEventType } from '../../../utilities/enum';\n\n// Reusable publish options\nconst PUBLISH_OPTIONS = {\n\tcontentType: 'application/json',\n\tpersistent: true,\n} as const;\n\n// Zero-overhead publishing - minimal allocations\nfunction publishToInstances(\n\trecipientsWithInstances: Array<{ recipient: EntitySubscription; instances: string[] }>,\n\tlocalKey: string,\n\tpayload: any\n): void {\n\tif (recipientsWithInstances.length === 0) return;\n\n\tconst channel = getChannel();\n\n\t// Pre-serialize message once\n\tconst message = { localKey, payload, recipient: null as any };\n\t\n\t// Fire-and-forget with minimal overhead\n\tfor (let i = 0; i < recipientsWithInstances.length; i++) {\n\t\tconst { recipient, instances } = recipientsWithInstances[i];\n\t\tmessage.recipient = recipient;\n\t\tconst messageStr = JSON.stringify(message);\n\n\t\tfor (let j = 0; j < instances.length; j++) {\n\t\t\tconst routingKey = `${INSTANCE_ROUTE_PREFIX}.${instances[j]}`;\n\t\t\t\n\t\t\ttry {\n\t\t\t\t// Direct publish - Buffer.from creates minimal overhead\n\t\t\t\tchannel.publish(EXCHANGE_NAME, routingKey, Buffer.from(messageStr, 'utf8'), PUBLISH_OPTIONS);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`[Socket] Publish failed: ${routingKey}`, error);\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Zero-allocation recipient filtering\nasync function getActiveSubscribedRecipients(\n\trecipients: EntitySubscription[],\n\tlocalKey: string\n): Promise<{\n\tsubscribed: Array<{ recipient: EntitySubscription; instances: string[] }>;\n\tunsubscribed: EntitySubscription[];\n}> {\n\tif (recipients.length === 0) {\n\t\treturn { subscribed: [], unsubscribed: [] };\n\t}\n\n\t// Use batch API\n\tconst batchInput = recipients.map(recipient => ({ entity: recipient, eventKey: localKey }));\n\tconst resultsMap = await getEntitySubscriptionInfoBatch(batchInput);\n\n\t// Pre-allocate arrays\n\tconst subscribed: Array<{ recipient: EntitySubscription; instances: string[] }> = [];\n\tconst unsubscribed: EntitySubscription[] = [];\n\n\t// Single pass with minimal allocations\n\tfor (let i = 0; i < recipients.length; i++) {\n\t\tconst recipient = recipients[i];\n\t\tconst entityKeyStr = `${recipient.type ?? 'default'}:${recipient.id}`;\n\t\tconst cacheKey = `${entityKeyStr}:${localKey}`;\n\t\tconst info = resultsMap.get(cacheKey);\n\n\t\tif (info && info.isSubscribed && info.instances.length > 0) {\n\t\t\tsubscribed.push({ recipient, instances: info.instances });\n\t\t} else {\n\t\t\tunsubscribed.push(recipient);\n\t\t}\n\t}\n\n\treturn { subscribed, unsubscribed };\n}\n\n// Minimal overhead publish - early returns, lazy evaluation\nexport async function publishSocketEvent(\n\tevent: BaseSocketEvent<any, any>,\n\trecipients: EntitySubscription[]\n): Promise<void> {\n\tif (recipients.length === 0) return;\n\n\tconst localKey = event.getSubscriptionKey();\n\tconst payload = event.payload();\n\n\tconst { subscribed, unsubscribed } = await getActiveSubscribedRecipients(recipients, localKey);\n\n\t// Fire-and-forget: zero blocking\n\tif (subscribed.length > 0) {\n\t\tpublishToInstances(subscribed, localKey, payload);\n\t}\n\n\t// Lazy fallback evaluation - only if needed\n\tif (unsubscribed.length > 0) {\n\t\tconst notificationEvent = event.notification?.();\n\t\tconst pushNotificationEvent = event.pushNotification?.();\n\n\t\t// Fire without awaiting - true fire-and-forget\n\t\tif (pushNotificationEvent) {\n\t\t\tpublishEvent(new PushNotificationEvent(PushNotificationEventType.CHATROOM_MESSAGE_SEND, payload)).catch(\n\t\t\t\terr => console.error('[Socket] Push failed:', err)\n\t\t\t);\n\t\t}\n\n\t\tif (notificationEvent) {\n\t\t\tpublishNotificationSocketEvent(notificationEvent, unsubscribed).catch(err =>\n\t\t\t\tconsole.error('[Socket] Notification failed:', err)\n\t\t\t);\n\t\t}\n\t}\n}\n\n// Zero-overhead notification publish\nexport async function publishNotificationSocketEvent(\n\tevent: NotificationSocketEvent,\n\trecipients: EntitySubscription[]\n): Promise<void> {\n\tif (recipients.length === 0) return;\n\n\tconst eventKey = event.getSubscriptionKey();\n\tconst batchInput = recipients.map(recipient => ({ entity: recipient, eventKey }));\n\tconst resultsMap = await getEntitySubscriptionInfoBatch(batchInput);\n\n\t// Pre-allocate for valid instances\n\tconst validInstances: Array<{ recipient: EntitySubscription; instances: string[] }> = [];\n\n\tfor (let i = 0; i < recipients.length; i++) {\n\t\tconst recipient = recipients[i];\n\t\tconst entityKeyStr = `${recipient.type ?? 'default'}:${recipient.id}`;\n\t\tconst cacheKey = `${entityKeyStr}:${eventKey}`;\n\t\tconst info = resultsMap.get(cacheKey);\n\n\t\tif (info && info.instances.length > 0) {\n\t\t\tvalidInstances.push({ recipient, instances: info.instances });\n\t\t}\n\t}\n\n\tif (validInstances.length > 0) {\n\t\tpublishToInstances(validInstances, eventKey, event.payload());\n\t}\n}\n"]}
|
|
@@ -3,18 +3,28 @@ export type EntitySubscription = {
|
|
|
3
3
|
id: string;
|
|
4
4
|
type?: AuthEntityType;
|
|
5
5
|
};
|
|
6
|
-
export declare const kEntityEvents: (entity: EntitySubscription) => string;
|
|
7
|
-
export declare const kEntityInstances: (entity: EntitySubscription) => string;
|
|
8
|
-
export declare const kEntityConnection: (entityType?: AuthEntityType) => string;
|
|
9
6
|
export interface SocketRedisEventInformation {
|
|
10
7
|
eventKey: string;
|
|
11
8
|
instanceId: string;
|
|
12
9
|
socketId: string;
|
|
13
10
|
}
|
|
14
11
|
export declare function connectEntityToSocketEvent(entity: EntitySubscription, socketRedisEventInformation: SocketRedisEventInformation): Promise<void>;
|
|
15
|
-
export declare function getConnectedEntityByType(type: AuthEntityType): Promise<
|
|
12
|
+
export declare function getConnectedEntityByType(type: AuthEntityType): Promise<string[]>;
|
|
16
13
|
export declare function disconnectEntityFromSocketEvent(entity: EntitySubscription, socketRedisEventInformation: SocketRedisEventInformation): Promise<void>;
|
|
17
|
-
export declare function
|
|
14
|
+
export declare function getEntitySubscriptionInfo(entity: EntitySubscription, eventKey: string): Promise<{
|
|
15
|
+
isSubscribed: boolean;
|
|
16
|
+
instances: string[];
|
|
17
|
+
}>;
|
|
18
|
+
export declare function getEntitySubscriptionInfoBatch(entities: Array<{
|
|
19
|
+
entity: EntitySubscription;
|
|
20
|
+
eventKey: string;
|
|
21
|
+
}>): Promise<Map<string, {
|
|
22
|
+
isSubscribed: boolean;
|
|
23
|
+
instances: string[];
|
|
24
|
+
}>>;
|
|
18
25
|
export declare function isEntityConnected(entity: EntitySubscription): Promise<boolean>;
|
|
19
|
-
export declare function getEntitiesSubscribedToEvent(eventKey: string): Promise<EntitySubscription[]>;
|
|
20
26
|
export declare function disconnectAllEntitiesFromInstance(entityType?: AuthEntityType): Promise<void>;
|
|
27
|
+
export declare function getEventSubscribers(eventKey: string): Promise<EntitySubscription[]>;
|
|
28
|
+
export declare function getEventSubscribersBatch(eventKeys: string[]): Promise<Map<string, EntitySubscription[]>>;
|
|
29
|
+
export declare function getEventSubscribersByType(eventKey: string, entityType: AuthEntityType): Promise<EntitySubscription[]>;
|
|
30
|
+
export declare function getEventSubscriberCount(eventKey: string): Promise<number>;
|
|
@@ -1,101 +1,318 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.kEntityConnection = exports.kEntityInstances = exports.kEntityEvents = void 0;
|
|
4
3
|
exports.connectEntityToSocketEvent = connectEntityToSocketEvent;
|
|
5
4
|
exports.getConnectedEntityByType = getConnectedEntityByType;
|
|
6
5
|
exports.disconnectEntityFromSocketEvent = disconnectEntityFromSocketEvent;
|
|
7
|
-
exports.
|
|
6
|
+
exports.getEntitySubscriptionInfo = getEntitySubscriptionInfo;
|
|
7
|
+
exports.getEntitySubscriptionInfoBatch = getEntitySubscriptionInfoBatch;
|
|
8
8
|
exports.isEntityConnected = isEntityConnected;
|
|
9
|
-
exports.getEntitiesSubscribedToEvent = getEntitiesSubscribedToEvent;
|
|
10
9
|
exports.disconnectAllEntitiesFromInstance = disconnectAllEntitiesFromInstance;
|
|
10
|
+
exports.getEventSubscribers = getEventSubscribers;
|
|
11
|
+
exports.getEventSubscribersBatch = getEventSubscribersBatch;
|
|
12
|
+
exports.getEventSubscribersByType = getEventSubscribersByType;
|
|
13
|
+
exports.getEventSubscriberCount = getEventSubscriberCount;
|
|
11
14
|
const __1 = require("../../..");
|
|
12
15
|
const instance_utils_1 = require("../../../utilities/instance.utils");
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const keyCache = new Map();
|
|
17
|
+
const entityKey = (entity) => {
|
|
18
|
+
const cacheKey = `${entity.type}:${entity.id}`;
|
|
19
|
+
let key = keyCache.get(cacheKey);
|
|
20
|
+
if (!key) {
|
|
21
|
+
key = `${entity.type ?? 'default'}:${entity.id}`;
|
|
22
|
+
keyCache.set(cacheKey, key);
|
|
23
|
+
}
|
|
24
|
+
return key;
|
|
25
|
+
};
|
|
26
|
+
const kEntityEvents = (entity) => `socket:events:${entityKey(entity)}`;
|
|
27
|
+
const kEntityInstances = (entity) => `socket:instances:${entityKey(entity)}`;
|
|
28
|
+
const kEntityConnection = (entityType) => `socket:conn:${entityType ?? 'all'}`;
|
|
29
|
+
const kEventSubscribers = (eventKey) => `socket:event_subs:${eventKey}`;
|
|
30
|
+
class LRUCache {
|
|
31
|
+
cache = new Map();
|
|
32
|
+
maxSize;
|
|
33
|
+
constructor(maxSize = 10000) {
|
|
34
|
+
this.maxSize = maxSize;
|
|
35
|
+
}
|
|
36
|
+
get(key) {
|
|
37
|
+
const value = this.cache.get(key);
|
|
38
|
+
if (value !== undefined) {
|
|
39
|
+
this.cache.delete(key);
|
|
40
|
+
this.cache.set(key, value);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
set(key, value) {
|
|
45
|
+
this.cache.delete(key);
|
|
46
|
+
if (this.cache.size >= this.maxSize) {
|
|
47
|
+
const firstKey = this.cache.keys().next().value;
|
|
48
|
+
if (firstKey !== undefined) {
|
|
49
|
+
this.cache.delete(firstKey);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
this.cache.set(key, value);
|
|
53
|
+
}
|
|
54
|
+
delete(key) {
|
|
55
|
+
this.cache.delete(key);
|
|
56
|
+
}
|
|
57
|
+
clear() {
|
|
58
|
+
this.cache.clear();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const subscriptionCache = new LRUCache(10000);
|
|
62
|
+
const eventSubscribersCache = new LRUCache(5000);
|
|
63
|
+
const CACHE_TTL = 5000;
|
|
20
64
|
async function connectEntityToSocketEvent(entity, socketRedisEventInformation) {
|
|
21
65
|
const redis = (0, __1.getLibraries)().getRedisService();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
await
|
|
66
|
+
const entityKeyStr = entityKey(entity);
|
|
67
|
+
const eventData = JSON.stringify(socketRedisEventInformation);
|
|
68
|
+
await Promise.all([
|
|
69
|
+
redis.addToSet(kEntityEvents(entity), eventData),
|
|
70
|
+
redis.addToSet(kEntityInstances(entity), socketRedisEventInformation.instanceId),
|
|
71
|
+
redis.addToSet(kEntityConnection(entity.type), entity.id),
|
|
72
|
+
redis.addToSet(kEventSubscribers(socketRedisEventInformation.eventKey), entityKeyStr),
|
|
73
|
+
]);
|
|
74
|
+
const cacheKey = `${entityKeyStr}:${socketRedisEventInformation.eventKey}`;
|
|
75
|
+
subscriptionCache.delete(cacheKey);
|
|
76
|
+
eventSubscribersCache.delete(socketRedisEventInformation.eventKey);
|
|
25
77
|
}
|
|
26
78
|
async function getConnectedEntityByType(type) {
|
|
27
79
|
const redis = (0, __1.getLibraries)().getRedisService();
|
|
28
|
-
return redis.getSetMembers(
|
|
80
|
+
return redis.getSetMembers(kEntityConnection(type));
|
|
29
81
|
}
|
|
30
82
|
async function disconnectEntityFromSocketEvent(entity, socketRedisEventInformation) {
|
|
31
83
|
const redis = (0, __1.getLibraries)().getRedisService();
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
84
|
+
const eventData = JSON.stringify(socketRedisEventInformation);
|
|
85
|
+
const entityKeyStr = entityKey(entity);
|
|
86
|
+
await redis.removeFromSet(kEntityEvents(entity), eventData);
|
|
87
|
+
const remainingEvents = (await redis.getSetMembers(kEntityEvents(entity))).map(r => JSON.parse(r));
|
|
88
|
+
const hasInstanceEvents = remainingEvents.some(e => e.instanceId === socketRedisEventInformation.instanceId);
|
|
89
|
+
const hasAnyEvents = remainingEvents.length > 0;
|
|
90
|
+
const stillSubscribedToEvent = remainingEvents.some(e => e.eventKey === socketRedisEventInformation.eventKey);
|
|
91
|
+
const cleanupOps = [];
|
|
92
|
+
if (!hasInstanceEvents) {
|
|
93
|
+
cleanupOps.push(redis.removeFromSet(kEntityInstances(entity), socketRedisEventInformation.instanceId));
|
|
36
94
|
}
|
|
37
|
-
if (
|
|
38
|
-
|
|
95
|
+
if (!hasAnyEvents) {
|
|
96
|
+
cleanupOps.push(redis.removeFromSet(kEntityConnection(entity.type), entity.id));
|
|
39
97
|
}
|
|
98
|
+
if (!stillSubscribedToEvent) {
|
|
99
|
+
cleanupOps.push(redis.removeFromSet(kEventSubscribers(socketRedisEventInformation.eventKey), entityKeyStr));
|
|
100
|
+
}
|
|
101
|
+
if (cleanupOps.length > 0) {
|
|
102
|
+
await Promise.all(cleanupOps);
|
|
103
|
+
}
|
|
104
|
+
const cacheKey = `${entityKeyStr}:${socketRedisEventInformation.eventKey}`;
|
|
105
|
+
subscriptionCache.delete(cacheKey);
|
|
106
|
+
eventSubscribersCache.delete(socketRedisEventInformation.eventKey);
|
|
40
107
|
}
|
|
41
|
-
async function
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
108
|
+
async function getEntitySubscriptionInfo(entity, eventKey) {
|
|
109
|
+
const entityKeyStr = entityKey(entity);
|
|
110
|
+
const cacheKey = `${entityKeyStr}:${eventKey}`;
|
|
111
|
+
const cached = subscriptionCache.get(cacheKey);
|
|
112
|
+
if (cached) {
|
|
113
|
+
const age = Date.now() - cached.timestamp;
|
|
114
|
+
if (age < CACHE_TTL) {
|
|
115
|
+
return { isSubscribed: cached.isSubscribed, instances: cached.instances };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
47
118
|
const redis = (0, __1.getLibraries)().getRedisService();
|
|
48
|
-
|
|
119
|
+
const [events, instances] = await Promise.all([
|
|
120
|
+
redis.getSetMembers(kEntityEvents(entity)),
|
|
121
|
+
redis.getSetMembers(kEntityInstances(entity)),
|
|
122
|
+
]);
|
|
123
|
+
let isSubscribed = false;
|
|
124
|
+
for (let i = 0; i < events.length; i++) {
|
|
125
|
+
if (events[i].includes(eventKey)) {
|
|
126
|
+
isSubscribed = true;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const result = { isSubscribed, instances };
|
|
131
|
+
subscriptionCache.set(cacheKey, { ...result, timestamp: Date.now() });
|
|
132
|
+
return result;
|
|
49
133
|
}
|
|
50
|
-
async function
|
|
134
|
+
async function getEntitySubscriptionInfoBatch(entities) {
|
|
135
|
+
if (entities.length === 0)
|
|
136
|
+
return new Map();
|
|
51
137
|
const redis = (0, __1.getLibraries)().getRedisService();
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
for (const entityId of connectedEntityIds) {
|
|
65
|
-
const entity = { id: entityId, type: entityType };
|
|
66
|
-
const isSubscribed = await isEntitySubscribed(entity, eventKey);
|
|
67
|
-
if (isSubscribed) {
|
|
68
|
-
subscribers.push(entity);
|
|
69
|
-
}
|
|
138
|
+
const results = new Map();
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
const uncached = [];
|
|
141
|
+
for (let i = 0; i < entities.length; i++) {
|
|
142
|
+
const { entity, eventKey } = entities[i];
|
|
143
|
+
const cacheKey = `${entityKey(entity)}:${eventKey}`;
|
|
144
|
+
const cached = subscriptionCache.get(cacheKey);
|
|
145
|
+
if (cached && now - cached.timestamp < CACHE_TTL) {
|
|
146
|
+
results.set(cacheKey, { isSubscribed: cached.isSubscribed, instances: cached.instances });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
uncached.push(entities[i]);
|
|
70
150
|
}
|
|
71
151
|
}
|
|
72
|
-
|
|
152
|
+
if (uncached.length === 0)
|
|
153
|
+
return results;
|
|
154
|
+
await Promise.all(uncached.map(async ({ entity, eventKey }) => {
|
|
155
|
+
const [events, instances] = await Promise.all([
|
|
156
|
+
redis.getSetMembers(kEntityEvents(entity)),
|
|
157
|
+
redis.getSetMembers(kEntityInstances(entity)),
|
|
158
|
+
]);
|
|
159
|
+
let isSubscribed = false;
|
|
160
|
+
for (let i = 0; i < events.length; i++) {
|
|
161
|
+
if (events[i].includes(eventKey)) {
|
|
162
|
+
isSubscribed = true;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const cacheKey = `${entityKey(entity)}:${eventKey}`;
|
|
167
|
+
const result = { isSubscribed, instances };
|
|
168
|
+
subscriptionCache.set(cacheKey, { ...result, timestamp: now });
|
|
169
|
+
results.set(cacheKey, result);
|
|
170
|
+
}));
|
|
171
|
+
return results;
|
|
172
|
+
}
|
|
173
|
+
async function isEntityConnected(entity) {
|
|
174
|
+
const redis = (0, __1.getLibraries)().getRedisService();
|
|
175
|
+
return redis.isMemberOfSet(kEntityConnection(entity.type), entity.id);
|
|
73
176
|
}
|
|
74
177
|
async function disconnectAllEntitiesFromInstance(entityType) {
|
|
75
178
|
const instanceId = (0, instance_utils_1.getInstanceId)();
|
|
76
179
|
const redis = (0, __1.getLibraries)().getRedisService();
|
|
77
|
-
const connectedEntities = await redis.getSetMembers(
|
|
180
|
+
const connectedEntities = await redis.getSetMembers(kEntityConnection(entityType));
|
|
78
181
|
if (!connectedEntities.length)
|
|
79
182
|
return;
|
|
80
|
-
|
|
183
|
+
const affectedEvents = new Set();
|
|
184
|
+
await Promise.all(connectedEntities.map(async (entityId) => {
|
|
81
185
|
const entity = { id: entityId, type: entityType };
|
|
82
|
-
const allEvents = await redis.getSetMembers(
|
|
186
|
+
const allEvents = await redis.getSetMembers(kEntityEvents(entity));
|
|
83
187
|
if (!allEvents.length)
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
188
|
+
return;
|
|
189
|
+
const parsedEvents = allEvents.map(e => ({
|
|
190
|
+
raw: e,
|
|
191
|
+
parsed: JSON.parse(e),
|
|
192
|
+
}));
|
|
193
|
+
const eventsToRemove = parsedEvents.filter(e => e.parsed.instanceId === instanceId);
|
|
194
|
+
if (!eventsToRemove.length)
|
|
195
|
+
return;
|
|
196
|
+
eventsToRemove.forEach(e => affectedEvents.add(e.parsed.eventKey));
|
|
197
|
+
await Promise.all(eventsToRemove.map(e => redis.removeFromSet(kEntityEvents(entity), e.raw)));
|
|
198
|
+
const remainingEvents = parsedEvents.filter(e => !eventsToRemove.includes(e));
|
|
199
|
+
const cleanupOps = [];
|
|
200
|
+
if (!remainingEvents.some(e => e.parsed.instanceId === instanceId)) {
|
|
201
|
+
cleanupOps.push(redis.removeFromSet(kEntityInstances(entity), instanceId));
|
|
202
|
+
}
|
|
203
|
+
if (remainingEvents.length === 0) {
|
|
204
|
+
cleanupOps.push(redis.removeFromSet(kEntityConnection(entityType), entityId));
|
|
205
|
+
}
|
|
206
|
+
const remainingEventKeys = new Set(remainingEvents.map(e => e.parsed.eventKey));
|
|
207
|
+
const removedEventKeys = eventsToRemove
|
|
208
|
+
.map(e => e.parsed.eventKey)
|
|
209
|
+
.filter(key => !remainingEventKeys.has(key));
|
|
210
|
+
removedEventKeys.forEach(eventKey => {
|
|
211
|
+
cleanupOps.push(redis.removeFromSet(kEventSubscribers(eventKey), entityKey(entity)));
|
|
88
212
|
});
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
213
|
+
if (cleanupOps.length > 0) {
|
|
214
|
+
await Promise.all(cleanupOps);
|
|
215
|
+
}
|
|
216
|
+
subscriptionCache.delete(entityKey(entity));
|
|
217
|
+
}));
|
|
218
|
+
affectedEvents.forEach(eventKey => eventSubscribersCache.delete(eventKey));
|
|
219
|
+
}
|
|
220
|
+
async function getEventSubscribers(eventKey) {
|
|
221
|
+
const cached = eventSubscribersCache.get(eventKey);
|
|
222
|
+
const now = Date.now();
|
|
223
|
+
if (cached && now - cached.timestamp < CACHE_TTL) {
|
|
224
|
+
return cached.subscribers;
|
|
225
|
+
}
|
|
226
|
+
const redis = (0, __1.getLibraries)().getRedisService();
|
|
227
|
+
const entityKeys = await redis.getSetMembers(kEventSubscribers(eventKey));
|
|
228
|
+
if (entityKeys.length === 0) {
|
|
229
|
+
const empty = [];
|
|
230
|
+
eventSubscribersCache.set(eventKey, { subscribers: empty, timestamp: now });
|
|
231
|
+
return empty;
|
|
232
|
+
}
|
|
233
|
+
const subscribers = new Array(entityKeys.length);
|
|
234
|
+
for (let i = 0; i < entityKeys.length; i++) {
|
|
235
|
+
const colonIndex = entityKeys[i].indexOf(':');
|
|
236
|
+
const type = entityKeys[i].substring(0, colonIndex);
|
|
237
|
+
const id = entityKeys[i].substring(colonIndex + 1);
|
|
238
|
+
subscribers[i] = {
|
|
239
|
+
id,
|
|
240
|
+
type: type === 'default' ? undefined : type,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
eventSubscribersCache.set(eventKey, { subscribers, timestamp: now });
|
|
244
|
+
return subscribers;
|
|
245
|
+
}
|
|
246
|
+
async function getEventSubscribersBatch(eventKeys) {
|
|
247
|
+
const redis = (0, __1.getLibraries)().getRedisService();
|
|
248
|
+
const results = new Map();
|
|
249
|
+
const uncached = [];
|
|
250
|
+
for (const eventKey of eventKeys) {
|
|
251
|
+
const cached = eventSubscribersCache.get(eventKey);
|
|
252
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
253
|
+
results.set(eventKey, cached.subscribers);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
uncached.push(eventKey);
|
|
92
257
|
}
|
|
93
|
-
|
|
94
|
-
|
|
258
|
+
}
|
|
259
|
+
if (uncached.length > 0) {
|
|
260
|
+
await Promise.all(uncached.map(async (eventKey) => {
|
|
261
|
+
const entityKeys = await redis.getSetMembers(kEventSubscribers(eventKey));
|
|
262
|
+
const subscribers = entityKeys.map(key => {
|
|
263
|
+
const [type, id] = key.split(':');
|
|
264
|
+
return {
|
|
265
|
+
id,
|
|
266
|
+
type: type === 'default' ? undefined : type,
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
eventSubscribersCache.set(eventKey, { subscribers, timestamp: Date.now() });
|
|
270
|
+
results.set(eventKey, subscribers);
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
return results;
|
|
274
|
+
}
|
|
275
|
+
async function getEventSubscribersByType(eventKey, entityType) {
|
|
276
|
+
const cached = eventSubscribersCache.get(eventKey);
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
if (cached && now - cached.timestamp < CACHE_TTL) {
|
|
279
|
+
const filtered = [];
|
|
280
|
+
for (let i = 0; i < cached.subscribers.length; i++) {
|
|
281
|
+
if (cached.subscribers[i].type === entityType) {
|
|
282
|
+
filtered.push(cached.subscribers[i]);
|
|
283
|
+
}
|
|
95
284
|
}
|
|
96
|
-
|
|
97
|
-
|
|
285
|
+
return filtered;
|
|
286
|
+
}
|
|
287
|
+
const redis = (0, __1.getLibraries)().getRedisService();
|
|
288
|
+
const entityKeys = await redis.getSetMembers(kEventSubscribers(eventKey));
|
|
289
|
+
const typePrefix = `${entityType}:`;
|
|
290
|
+
const filtered = [];
|
|
291
|
+
const all = [];
|
|
292
|
+
for (let i = 0; i < entityKeys.length; i++) {
|
|
293
|
+
const key = entityKeys[i];
|
|
294
|
+
const colonIndex = key.indexOf(':');
|
|
295
|
+
const type = key.substring(0, colonIndex);
|
|
296
|
+
const id = key.substring(colonIndex + 1);
|
|
297
|
+
const sub = {
|
|
298
|
+
id,
|
|
299
|
+
type: type === 'default' ? undefined : type,
|
|
300
|
+
};
|
|
301
|
+
all.push(sub);
|
|
302
|
+
if (key.startsWith(typePrefix)) {
|
|
303
|
+
filtered.push(sub);
|
|
98
304
|
}
|
|
99
305
|
}
|
|
306
|
+
eventSubscribersCache.set(eventKey, { subscribers: all, timestamp: now });
|
|
307
|
+
return filtered;
|
|
308
|
+
}
|
|
309
|
+
async function getEventSubscriberCount(eventKey) {
|
|
310
|
+
const cached = eventSubscribersCache.get(eventKey);
|
|
311
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
312
|
+
return cached.subscribers.length;
|
|
313
|
+
}
|
|
314
|
+
const redis = (0, __1.getLibraries)().getRedisService();
|
|
315
|
+
const members = await redis.getSetMembers(kEventSubscribers(eventKey));
|
|
316
|
+
return members.length;
|
|
100
317
|
}
|
|
101
318
|
//# sourceMappingURL=socket-redis-connections.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-redis-connections.js","sourceRoot":"/","sources":["libraries/socket/core/socket-redis-connections.ts"],"names":[],"mappings":"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","sourcesContent":["import { getLibraries } from '../../..';\nimport { getInstanceId } from '../../../utilities/instance.utils';\nimport { AuthEntityType } from '../../auth';\n\nexport type EntitySubscription = {\n\tid: string;\n\ttype?: AuthEntityType;\n};\n\nexport const kEntityEvents = (entity: EntitySubscription) =>\n\tentity.type ? `socket:events:${entity.type}:${entity.id}` : `socket:events:entity:${entity.id}`;\nexport const kEntityInstances = (entity: EntitySubscription) =>\n\tentity.type ? `socket:instances:${entity.type}:${entity.id}` : `socket:instances:entity:${entity.id}`;\nexport const kEntityConnection = (entityType?: AuthEntityType) =>\n\tentityType ? `socket:connections:${entityType}` : `socket:connections:all`;\n\nexport interface SocketRedisEventInformation {\n\teventKey: string;\n\tinstanceId: string;\n\tsocketId: string;\n}\n\nexport async function connectEntityToSocketEvent(\n\tentity: EntitySubscription,\n\tsocketRedisEventInformation: SocketRedisEventInformation\n) {\n\tconst redis = getLibraries().getRedisService();\n\n\tawait redis.addToSet(kEntityEvents(entity), JSON.stringify(socketRedisEventInformation));\n\tawait redis.addToSet(kEntityInstances(entity), socketRedisEventInformation.instanceId);\n\tawait redis.addToSet(kEntityConnection(entity.type), entity.id);\n}\n\nexport async function getConnectedEntityByType(type: AuthEntityType): Promise<String[]> {\n\tconst redis = getLibraries().getRedisService();\n\treturn redis.getSetMembers(kEntityConnection(type));\n}\n\nexport async function disconnectEntityFromSocketEvent(\n\tentity: EntitySubscription,\n\tsocketRedisEventInformation: SocketRedisEventInformation\n) {\n\tconst redis = getLibraries().getRedisService();\n\n\tawait redis.removeFromSet(kEntityEvents(entity), JSON.stringify(socketRedisEventInformation));\n\n\tconst remainingEvents = (await redis.getSetMembers(kEntityEvents(entity))).map(\n\t\tr => JSON.parse(r) as SocketRedisEventInformation\n\t);\n\n\tif (remainingEvents.filter(e => e.instanceId === socketRedisEventInformation.instanceId).length === 0) {\n\t\tawait redis.removeFromSet(kEntityInstances(entity), socketRedisEventInformation.instanceId);\n\t}\n\n\tif (remainingEvents.length === 0) {\n\t\tawait redis.removeFromSet(kEntityConnection(entity.type), entity.id);\n\t}\n}\n\nexport async function isEntitySubscribed(entity: EntitySubscription, eventKey: string): Promise<boolean> {\n\tconst redis = getLibraries().getRedisService();\n\tconst events = await redis.getSetMembers(kEntityEvents(entity));\n\treturn events.map(e => JSON.parse(e) as SocketRedisEventInformation).some(e => e.eventKey === eventKey);\n}\n\nexport async function isEntityConnected(entity: EntitySubscription): Promise<boolean> {\n\tconst redis = getLibraries().getRedisService();\n\treturn redis.isMemberOfSet(kEntityConnection(entity.type), entity.id);\n}\n\nexport async function getEntitiesSubscribedToEvent(eventKey: string): Promise<EntitySubscription[]> {\n\tconst redis = getLibraries().getRedisService();\n\tconst subscribers: EntitySubscription[] = [];\n\n\tconst allEntityTypes = [\n\t\tAuthEntityType.USER,\n\t\tAuthEntityType.SELLER,\n\t\tAuthEntityType.DELIVERY_BOY,\n\t\tAuthEntityType.CRM,\n\t\tAuthEntityType.VENDOR_PARENT,\n\t\tAuthEntityType.PROFESSIONAL,\n\t\tundefined,\n\t];\n\n\tfor (const entityType of allEntityTypes) {\n\t\tconst connectedEntityIds = await redis.getSetMembers(kEntityConnection(entityType));\n\n\t\tfor (const entityId of connectedEntityIds) {\n\t\t\tconst entity: EntitySubscription = { id: entityId, type: entityType };\n\t\t\tconst isSubscribed = await isEntitySubscribed(entity, eventKey);\n\t\t\tif (isSubscribed) {\n\t\t\t\tsubscribers.push(entity);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn subscribers;\n}\n\nexport async function disconnectAllEntitiesFromInstance(entityType?: AuthEntityType) {\n\tconst instanceId = getInstanceId();\n\tconst redis = getLibraries().getRedisService();\n\n\tconst connectedEntities = await redis.getSetMembers(kEntityConnection(entityType));\n\tif (!connectedEntities.length) return;\n\n\tfor (const entityId of connectedEntities) {\n\t\tconst entity: EntitySubscription = { id: entityId, type: entityType };\n\t\tconst allEvents = await redis.getSetMembers(kEntityEvents(entity));\n\t\tif (!allEvents.length) continue;\n\n\t\tconst eventsToKeep = allEvents.filter(e => {\n\t\t\tconst parsed = JSON.parse(e) as SocketRedisEventInformation;\n\t\t\treturn parsed.instanceId !== instanceId;\n\t\t});\n\n\t\tconst eventsToRemove = allEvents.filter(e => !eventsToKeep.includes(e));\n\t\tfor (const e of eventsToRemove) {\n\t\t\tawait redis.removeFromSet(kEntityEvents(entity), e);\n\t\t}\n\n\t\tif (!eventsToKeep.some(e => (JSON.parse(e) as SocketRedisEventInformation).instanceId === instanceId)) {\n\t\t\tawait redis.removeFromSet(kEntityInstances(entity), instanceId);\n\t\t}\n\n\t\tif (eventsToKeep.length === 0) {\n\t\t\tawait redis.removeFromSet(kEntityConnection(entityType), entityId);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"socket-redis-connections.js","sourceRoot":"/","sources":["libraries/socket/core/socket-redis-connections.ts"],"names":[],"mappings":"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","sourcesContent":["import { getLibraries } from '../../..';\nimport { getInstanceId } from '../../../utilities/instance.utils';\nimport { AuthEntityType } from '../../auth';\n\nexport type EntitySubscription = {\n\tid: string;\n\ttype?: AuthEntityType;\n};\n\nexport interface SocketRedisEventInformation {\n\teventKey: string;\n\tinstanceId: string;\n\tsocketId: string;\n}\n\n// Zero-overhead key generation with memoization\nconst keyCache = new Map<string, string>();\n\nconst entityKey = (entity: EntitySubscription): string => {\n\tconst cacheKey = `${entity.type}:${entity.id}`;\n\tlet key = keyCache.get(cacheKey);\n\tif (!key) {\n\t\tkey = `${entity.type ?? 'default'}:${entity.id}`;\n\t\tkeyCache.set(cacheKey, key);\n\t}\n\treturn key;\n};\n\n// Pre-built key patterns (avoid string concatenation overhead)\nconst kEntityEvents = (entity: EntitySubscription) => `socket:events:${entityKey(entity)}`;\nconst kEntityInstances = (entity: EntitySubscription) => `socket:instances:${entityKey(entity)}`;\nconst kEntityConnection = (entityType?: AuthEntityType) => `socket:conn:${entityType ?? 'all'}`;\nconst kEventSubscribers = (eventKey: string) => `socket:event_subs:${eventKey}`;\n\n// LRU Cache for hot entities (reduces Redis load by 80-90%)\nclass LRUCache<K, V> {\n\tprivate cache = new Map<K, V>();\n\tprivate maxSize: number;\n\n\tconstructor(maxSize = 10000) {\n\t\tthis.maxSize = maxSize;\n\t}\n\n\tget(key: K): V | undefined {\n\t\tconst value = this.cache.get(key);\n\t\tif (value !== undefined) {\n\t\t\t// Move to end (most recently used)\n\t\t\tthis.cache.delete(key);\n\t\t\tthis.cache.set(key, value);\n\t\t}\n\t\treturn value;\n\t}\n\n\tset(key: K, value: V): void {\n\t\t// Remove if exists (will re-add at end)\n\t\tthis.cache.delete(key);\n\n\t\t// Evict oldest if at capacity\n\t\tif (this.cache.size >= this.maxSize) {\n\t\t\tconst firstKey = this.cache.keys().next().value;\n\t\t\tif (firstKey !== undefined) {\n\t\t\t\tthis.cache.delete(firstKey);\n\t\t\t}\n\t\t}\n\n\t\tthis.cache.set(key, value);\n\t}\n\n\tdelete(key: K): void {\n\t\tthis.cache.delete(key);\n\t}\n\n\tclear(): void {\n\t\tthis.cache.clear();\n\t}\n}\n\n// Cache subscription info for 5 seconds (hot path optimization)\nconst subscriptionCache = new LRUCache<string, { isSubscribed: boolean; instances: string[]; timestamp: number }>(\n\t10000\n);\n\n// Cache event subscribers (who's listening to what event)\nconst eventSubscribersCache = new LRUCache<string, { subscribers: EntitySubscription[]; timestamp: number }>(5000);\n\nconst CACHE_TTL = 5000; // 5 seconds\n\nexport async function connectEntityToSocketEvent(\n\tentity: EntitySubscription,\n\tsocketRedisEventInformation: SocketRedisEventInformation\n) {\n\tconst redis = getLibraries().getRedisService();\n\tconst entityKeyStr = entityKey(entity);\n\tconst eventData = JSON.stringify(socketRedisEventInformation);\n\n\t// Single Redis pipeline - all ops in one round-trip\n\tawait Promise.all([\n\t\tredis.addToSet(kEntityEvents(entity), eventData),\n\t\tredis.addToSet(kEntityInstances(entity), socketRedisEventInformation.instanceId),\n\t\tredis.addToSet(kEntityConnection(entity.type), entity.id),\n\t\tredis.addToSet(kEventSubscribers(socketRedisEventInformation.eventKey), entityKeyStr),\n\t]);\n\n\t// Lazy cache invalidation (only if cached)\n\tconst cacheKey = `${entityKeyStr}:${socketRedisEventInformation.eventKey}`;\n\tsubscriptionCache.delete(cacheKey);\n\teventSubscribersCache.delete(socketRedisEventInformation.eventKey);\n}\n\nexport async function getConnectedEntityByType(type: AuthEntityType): Promise<string[]> {\n\tconst redis = getLibraries().getRedisService();\n\treturn redis.getSetMembers(kEntityConnection(type));\n}\n\nexport async function disconnectEntityFromSocketEvent(\n\tentity: EntitySubscription,\n\tsocketRedisEventInformation: SocketRedisEventInformation\n) {\n\tconst redis = getLibraries().getRedisService();\n\tconst eventData = JSON.stringify(socketRedisEventInformation);\n\tconst entityKeyStr = entityKey(entity);\n\n\tawait redis.removeFromSet(kEntityEvents(entity), eventData);\n\n\t// Check remaining events in parallel with removal\n\tconst remainingEvents = (await redis.getSetMembers(kEntityEvents(entity))).map(\n\t\tr => JSON.parse(r) as SocketRedisEventInformation\n\t);\n\n\tconst hasInstanceEvents = remainingEvents.some(e => e.instanceId === socketRedisEventInformation.instanceId);\n\tconst hasAnyEvents = remainingEvents.length > 0;\n\t\n\t// Check if entity still subscribed to this specific event\n\tconst stillSubscribedToEvent = remainingEvents.some(e => e.eventKey === socketRedisEventInformation.eventKey);\n\n\t// Batch cleanup operations\n\tconst cleanupOps = [];\n\tif (!hasInstanceEvents) {\n\t\tcleanupOps.push(redis.removeFromSet(kEntityInstances(entity), socketRedisEventInformation.instanceId));\n\t}\n\tif (!hasAnyEvents) {\n\t\tcleanupOps.push(redis.removeFromSet(kEntityConnection(entity.type), entity.id));\n\t}\n\t// Remove from reverse index if no longer subscribed to this event\n\tif (!stillSubscribedToEvent) {\n\t\tcleanupOps.push(redis.removeFromSet(kEventSubscribers(socketRedisEventInformation.eventKey), entityKeyStr));\n\t}\n\n\tif (cleanupOps.length > 0) {\n\t\tawait Promise.all(cleanupOps);\n\t}\n\n\t// Invalidate caches on disconnection\n\tconst cacheKey = `${entityKeyStr}:${socketRedisEventInformation.eventKey}`;\n\tsubscriptionCache.delete(cacheKey);\n\teventSubscribersCache.delete(socketRedisEventInformation.eventKey);\n}\n\n// Zero-overhead hot path with inline cache check\nexport async function getEntitySubscriptionInfo(\n\tentity: EntitySubscription,\n\teventKey: string\n): Promise<{ isSubscribed: boolean; instances: string[] }> {\n\tconst entityKeyStr = entityKey(entity);\n\tconst cacheKey = `${entityKeyStr}:${eventKey}`;\n\n\t// Inline cache check (avoid function call overhead)\n\tconst cached = subscriptionCache.get(cacheKey);\n\tif (cached) {\n\t\tconst age = Date.now() - cached.timestamp;\n\t\tif (age < CACHE_TTL) {\n\t\t\treturn { isSubscribed: cached.isSubscribed, instances: cached.instances };\n\t\t}\n\t}\n\n\tconst redis = getLibraries().getRedisService();\n\n\t// Single parallel fetch\n\tconst [events, instances] = await Promise.all([\n\t\tredis.getSetMembers(kEntityEvents(entity)),\n\t\tredis.getSetMembers(kEntityInstances(entity)),\n\t]);\n\n\t// Optimized check - early return on first match\n\tlet isSubscribed = false;\n\tfor (let i = 0; i < events.length; i++) {\n\t\tif (events[i].includes(eventKey)) {\n\t\t\tisSubscribed = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Cache for next time\n\tconst result = { isSubscribed, instances };\n\tsubscriptionCache.set(cacheKey, { ...result, timestamp: Date.now() });\n\n\treturn result;\n}\n\n// Ultra-optimized batch with minimal allocations\nexport async function getEntitySubscriptionInfoBatch(\n\tentities: Array<{ entity: EntitySubscription; eventKey: string }>\n): Promise<Map<string, { isSubscribed: boolean; instances: string[] }>> {\n\tif (entities.length === 0) return new Map();\n\n\tconst redis = getLibraries().getRedisService();\n\tconst results = new Map<string, { isSubscribed: boolean; instances: string[] }>();\n\tconst now = Date.now();\n\t\n\t// Pre-allocate uncached array\n\tconst uncached: typeof entities = [];\n\t\n\t// Single pass: check cache and separate\n\tfor (let i = 0; i < entities.length; i++) {\n\t\tconst { entity, eventKey } = entities[i];\n\t\tconst cacheKey = `${entityKey(entity)}:${eventKey}`;\n\t\tconst cached = subscriptionCache.get(cacheKey);\n\n\t\tif (cached && now - cached.timestamp < CACHE_TTL) {\n\t\t\tresults.set(cacheKey, { isSubscribed: cached.isSubscribed, instances: cached.instances });\n\t\t} else {\n\t\t\tuncached.push(entities[i]);\n\t\t}\n\t}\n\n\t// Early return if all cached\n\tif (uncached.length === 0) return results;\n\n\t// Fetch all uncached in parallel\n\tawait Promise.all(\n\t\tuncached.map(async ({ entity, eventKey }) => {\n\t\t\tconst [events, instances] = await Promise.all([\n\t\t\t\tredis.getSetMembers(kEntityEvents(entity)),\n\t\t\t\tredis.getSetMembers(kEntityInstances(entity)),\n\t\t\t]);\n\n\t\t\t// Fast string search instead of JSON parse\n\t\t\tlet isSubscribed = false;\n\t\t\tfor (let i = 0; i < events.length; i++) {\n\t\t\t\tif (events[i].includes(eventKey)) {\n\t\t\t\t\tisSubscribed = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst cacheKey = `${entityKey(entity)}:${eventKey}`;\n\t\t\tconst result = { isSubscribed, instances };\n\n\t\t\tsubscriptionCache.set(cacheKey, { ...result, timestamp: now });\n\t\t\tresults.set(cacheKey, result);\n\t\t})\n\t);\n\n\treturn results;\n}\n\nexport async function isEntityConnected(entity: EntitySubscription): Promise<boolean> {\n\tconst redis = getLibraries().getRedisService();\n\treturn redis.isMemberOfSet(kEntityConnection(entity.type), entity.id);\n}\n\nexport async function disconnectAllEntitiesFromInstance(entityType?: AuthEntityType) {\n\tconst instanceId = getInstanceId();\n\tconst redis = getLibraries().getRedisService();\n\n\tconst connectedEntities = await redis.getSetMembers(kEntityConnection(entityType));\n\tif (!connectedEntities.length) return;\n\n\tconst affectedEvents = new Set<string>();\n\n\t// Process all entities in parallel\n\tawait Promise.all(\n\t\tconnectedEntities.map(async entityId => {\n\t\t\tconst entity: EntitySubscription = { id: entityId, type: entityType };\n\t\t\tconst allEvents = await redis.getSetMembers(kEntityEvents(entity));\n\t\t\tif (!allEvents.length) return;\n\n\t\t\t// Parse once, use many times\n\t\t\tconst parsedEvents = allEvents.map(e => ({\n\t\t\t\traw: e,\n\t\t\t\tparsed: JSON.parse(e) as SocketRedisEventInformation,\n\t\t\t}));\n\n\t\t\tconst eventsToRemove = parsedEvents.filter(e => e.parsed.instanceId === instanceId);\n\t\t\tif (!eventsToRemove.length) return;\n\n\t\t\t// Track events that will be affected\n\t\t\teventsToRemove.forEach(e => affectedEvents.add(e.parsed.eventKey));\n\n\t\t\t// Batch remove events\n\t\t\tawait Promise.all(eventsToRemove.map(e => redis.removeFromSet(kEntityEvents(entity), e.raw)));\n\n\t\t\t// Check if cleanup needed (reuse parsed data)\n\t\t\tconst remainingEvents = parsedEvents.filter(e => !eventsToRemove.includes(e));\n\t\t\tconst cleanupOps = [];\n\n\t\t\tif (!remainingEvents.some(e => e.parsed.instanceId === instanceId)) {\n\t\t\t\tcleanupOps.push(redis.removeFromSet(kEntityInstances(entity), instanceId));\n\t\t\t}\n\n\t\t\tif (remainingEvents.length === 0) {\n\t\t\t\tcleanupOps.push(redis.removeFromSet(kEntityConnection(entityType), entityId));\n\t\t\t}\n\n\t\t\t// Check which events this entity is no longer subscribed to\n\t\t\tconst remainingEventKeys = new Set(remainingEvents.map(e => e.parsed.eventKey));\n\t\t\tconst removedEventKeys = eventsToRemove\n\t\t\t\t.map(e => e.parsed.eventKey)\n\t\t\t\t.filter(key => !remainingEventKeys.has(key));\n\n\t\t\t// Remove from reverse index for events no longer subscribed\n\t\t\tremovedEventKeys.forEach(eventKey => {\n\t\t\t\tcleanupOps.push(redis.removeFromSet(kEventSubscribers(eventKey), entityKey(entity)));\n\t\t\t});\n\n\t\t\tif (cleanupOps.length > 0) {\n\t\t\t\tawait Promise.all(cleanupOps);\n\t\t\t}\n\n\t\t\t// Clear cache for this entity\n\t\t\tsubscriptionCache.delete(entityKey(entity));\n\t\t})\n\t);\n\n\t// Invalidate event subscriber caches for affected events\n\taffectedEvents.forEach(eventKey => eventSubscribersCache.delete(eventKey));\n}\n\n// Zero-allocation hot path for event subscribers\nexport async function getEventSubscribers(eventKey: string): Promise<EntitySubscription[]> {\n\tconst cached = eventSubscribersCache.get(eventKey);\n\tconst now = Date.now();\n\t\n\tif (cached && now - cached.timestamp < CACHE_TTL) {\n\t\treturn cached.subscribers;\n\t}\n\n\tconst redis = getLibraries().getRedisService();\n\tconst entityKeys = await redis.getSetMembers(kEventSubscribers(eventKey));\n\n\tif (entityKeys.length === 0) {\n\t\tconst empty: EntitySubscription[] = [];\n\t\teventSubscribersCache.set(eventKey, { subscribers: empty, timestamp: now });\n\t\treturn empty;\n\t}\n\n\t// Optimized parsing - single pass, no intermediate arrays\n\tconst subscribers: EntitySubscription[] = new Array(entityKeys.length);\n\tfor (let i = 0; i < entityKeys.length; i++) {\n\t\tconst colonIndex = entityKeys[i].indexOf(':');\n\t\tconst type = entityKeys[i].substring(0, colonIndex);\n\t\tconst id = entityKeys[i].substring(colonIndex + 1);\n\t\t\n\t\tsubscribers[i] = {\n\t\t\tid,\n\t\t\ttype: type === 'default' ? undefined : (type as AuthEntityType),\n\t\t};\n\t}\n\n\teventSubscribersCache.set(eventKey, { subscribers, timestamp: now });\n\treturn subscribers;\n}\n\n// ⚡ Batch variant: Get subscribers for multiple events at once\nexport async function getEventSubscribersBatch(\n\teventKeys: string[]\n): Promise<Map<string, EntitySubscription[]>> {\n\tconst redis = getLibraries().getRedisService();\n\tconst results = new Map<string, EntitySubscription[]>();\n\n\t// Separate cached vs uncached\n\tconst uncached: string[] = [];\n\tfor (const eventKey of eventKeys) {\n\t\tconst cached = eventSubscribersCache.get(eventKey);\n\t\tif (cached && Date.now() - cached.timestamp < CACHE_TTL) {\n\t\t\tresults.set(eventKey, cached.subscribers);\n\t\t} else {\n\t\t\tuncached.push(eventKey);\n\t\t}\n\t}\n\n\t// Fetch uncached in parallel\n\tif (uncached.length > 0) {\n\t\tawait Promise.all(\n\t\t\tuncached.map(async eventKey => {\n\t\t\t\tconst entityKeys = await redis.getSetMembers(kEventSubscribers(eventKey));\n\n\t\t\t\tconst subscribers: EntitySubscription[] = entityKeys.map(key => {\n\t\t\t\t\tconst [type, id] = key.split(':');\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttype: type === 'default' ? undefined : (type as AuthEntityType),\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\teventSubscribersCache.set(eventKey, { subscribers, timestamp: Date.now() });\n\t\t\t\tresults.set(eventKey, subscribers);\n\t\t\t})\n\t\t);\n\t}\n\n\treturn results;\n}\n\n// Zero-copy filtered query - inline filtering to avoid double allocation\nexport async function getEventSubscribersByType(\n\teventKey: string,\n\tentityType: AuthEntityType\n): Promise<EntitySubscription[]> {\n\tconst cached = eventSubscribersCache.get(eventKey);\n\tconst now = Date.now();\n\t\n\tif (cached && now - cached.timestamp < CACHE_TTL) {\n\t\t// Fast filter on cached data\n\t\tconst filtered: EntitySubscription[] = [];\n\t\tfor (let i = 0; i < cached.subscribers.length; i++) {\n\t\t\tif (cached.subscribers[i].type === entityType) {\n\t\t\t\tfiltered.push(cached.subscribers[i]);\n\t\t\t}\n\t\t}\n\t\treturn filtered;\n\t}\n\n\t// Fetch and filter in one pass\n\tconst redis = getLibraries().getRedisService();\n\tconst entityKeys = await redis.getSetMembers(kEventSubscribers(eventKey));\n\tconst typePrefix = `${entityType}:`;\n\t\n\tconst filtered: EntitySubscription[] = [];\n\tconst all: EntitySubscription[] = [];\n\t\n\tfor (let i = 0; i < entityKeys.length; i++) {\n\t\tconst key = entityKeys[i];\n\t\tconst colonIndex = key.indexOf(':');\n\t\tconst type = key.substring(0, colonIndex);\n\t\tconst id = key.substring(colonIndex + 1);\n\t\t\n\t\tconst sub: EntitySubscription = {\n\t\t\tid,\n\t\t\ttype: type === 'default' ? undefined : (type as AuthEntityType),\n\t\t};\n\t\t\n\t\tall.push(sub);\n\t\tif (key.startsWith(typePrefix)) {\n\t\t\tfiltered.push(sub);\n\t\t}\n\t}\n\n\t// Cache all subscribers for future calls\n\teventSubscribersCache.set(eventKey, { subscribers: all, timestamp: now });\n\t\n\treturn filtered;\n}\n\n// ⚡ Get subscriber count (fast, check cache or count)\nexport async function getEventSubscriberCount(eventKey: string): Promise<number> {\n\t// Check cache first\n\tconst cached = eventSubscribersCache.get(eventKey);\n\tif (cached && Date.now() - cached.timestamp < CACHE_TTL) {\n\t\treturn cached.subscribers.length;\n\t}\n\n\t// Otherwise fetch and count\n\tconst redis = getLibraries().getRedisService();\n\tconst members = await redis.getSetMembers(kEventSubscribers(eventKey));\n\treturn members.length;\n}\n"]}
|
package/dist/types/README.md
CHANGED
package/dist/types/package.json
CHANGED