@syncular/core 0.0.6-171 → 0.0.6-178
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/utils/realtime-connection-registry.d.ts +10 -5
- package/dist/utils/realtime-connection-registry.d.ts.map +1 -1
- package/dist/utils/realtime-connection-registry.js +63 -48
- package/dist/utils/realtime-connection-registry.js.map +1 -1
- package/package.json +1 -1
- package/src/utils/realtime-connection-registry.ts +76 -48
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
export interface RealtimeConnection {
|
|
2
2
|
readonly clientId: string;
|
|
3
|
+
readonly ownerKey: string;
|
|
3
4
|
readonly isOpen: boolean;
|
|
4
5
|
sendHeartbeat(): void;
|
|
5
6
|
close(code?: number, reason?: string): void;
|
|
6
7
|
}
|
|
7
8
|
export declare class RealtimeConnectionRegistry<TConnection extends RealtimeConnection> {
|
|
9
|
+
private connectionsByOwnerKey;
|
|
8
10
|
private connectionsByClientId;
|
|
9
|
-
private
|
|
11
|
+
private scopeKeysByOwnerKey;
|
|
10
12
|
private connectionsByScopeKey;
|
|
11
13
|
private readonly heartbeatIntervalMs;
|
|
12
14
|
private heartbeatTimer;
|
|
13
|
-
private readonly
|
|
15
|
+
private readonly onOwnerDisconnected?;
|
|
14
16
|
constructor(options?: {
|
|
15
17
|
heartbeatIntervalMs?: number;
|
|
16
|
-
|
|
18
|
+
onOwnerDisconnected?: (ownerKey: string) => void;
|
|
17
19
|
});
|
|
18
20
|
register(connection: TConnection, initialScopeKeys?: string[]): () => void;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
updateOwnerScopeKeys(ownerKey: string, scopeKeys: string[]): void;
|
|
22
|
+
isOwnerSubscribedToScopeKey(ownerKey: string, scopeKey: string): boolean;
|
|
23
|
+
getConnectionsForOwner(ownerKey: string): ReadonlySet<TConnection> | undefined;
|
|
24
|
+
getScopedConnectionCount(ownerKey: string): number;
|
|
21
25
|
getConnectionsForClient(clientId: string): ReadonlySet<TConnection> | undefined;
|
|
22
26
|
getConnectionCount(clientId: string): number;
|
|
23
27
|
getTotalConnections(): number;
|
|
@@ -25,6 +29,7 @@ export declare class RealtimeConnectionRegistry<TConnection extends RealtimeConn
|
|
|
25
29
|
excludeClientIds?: readonly string[];
|
|
26
30
|
}): void;
|
|
27
31
|
forEachConnection(visitor: (connection: TConnection) => void): void;
|
|
32
|
+
closeOwnerConnections(ownerKey: string, code?: number, reason?: string): void;
|
|
28
33
|
closeClientConnections(clientId: string, code?: number, reason?: string): void;
|
|
29
34
|
closeAll(code?: number, reason?: string): void;
|
|
30
35
|
private ensureHeartbeat;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-connection-registry.d.ts","sourceRoot":"","sources":["../../src/utils/realtime-connection-registry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,aAAa,IAAI,IAAI,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,qBAAa,0BAA0B,CACrC,WAAW,SAAS,kBAAkB;IAEtC,OAAO,CAAC,qBAAqB,CAAuC;IACpE,OAAO,CAAC,mBAAmB,CAAkC;IAC7D,OAAO,CAAC,qBAAqB,CAAuC;IAEpE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"realtime-connection-registry.d.ts","sourceRoot":"","sources":["../../src/utils/realtime-connection-registry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,aAAa,IAAI,IAAI,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,qBAAa,0BAA0B,CACrC,WAAW,SAAS,kBAAkB;IAEtC,OAAO,CAAC,qBAAqB,CAAuC;IACpE,OAAO,CAAC,qBAAqB,CAAuC;IACpE,OAAO,CAAC,mBAAmB,CAAkC;IAC7D,OAAO,CAAC,qBAAqB,CAAuC;IAEpE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAA6B;IAElE,YAAY,OAAO,CAAC,EAAE;QACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;KAClD,EAGA;IAED,QAAQ,CACN,UAAU,EAAE,WAAW,EACvB,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,IAAI,CAqCZ;IAED,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAqChE;IAED,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIvE;IAED,sBAAsB,CACpB,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,WAAW,CAAC,GAAG,SAAS,CAEtC;IAED,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjD;IAED,uBAAuB,CACrB,QAAQ,EAAE,MAAM,GACf,WAAW,CAAC,WAAW,CAAC,GAAG,SAAS,CAEtC;IAED,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3C;IAED,mBAAmB,IAAI,MAAM,CAM5B;IAED,4BAA4B,CAC1B,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,IAAI,EAC1C,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GACjD,IAAI,CAcN;IAED,iBAAiB,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAOlE;IAED,qBAAqB,CACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,IAAI,CAUN;IAED,sBAAsB,CACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,IAAI,CASN;IAED,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAQ7C;IAED,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,UAAU;CA8BnB"}
|
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
export class RealtimeConnectionRegistry {
|
|
2
|
+
connectionsByOwnerKey = new Map();
|
|
2
3
|
connectionsByClientId = new Map();
|
|
3
|
-
|
|
4
|
+
scopeKeysByOwnerKey = new Map();
|
|
4
5
|
connectionsByScopeKey = new Map();
|
|
5
6
|
heartbeatIntervalMs;
|
|
6
7
|
heartbeatTimer = null;
|
|
7
|
-
|
|
8
|
+
onOwnerDisconnected;
|
|
8
9
|
constructor(options) {
|
|
9
10
|
this.heartbeatIntervalMs = options?.heartbeatIntervalMs ?? 30_000;
|
|
10
|
-
this.
|
|
11
|
+
this.onOwnerDisconnected = options?.onOwnerDisconnected;
|
|
11
12
|
}
|
|
12
13
|
register(connection, initialScopeKeys = []) {
|
|
13
|
-
const
|
|
14
|
-
let
|
|
14
|
+
const ownerKey = connection.ownerKey;
|
|
15
|
+
let ownerConns = this.connectionsByOwnerKey.get(ownerKey);
|
|
16
|
+
if (!ownerConns) {
|
|
17
|
+
ownerConns = new Set();
|
|
18
|
+
this.connectionsByOwnerKey.set(ownerKey, ownerConns);
|
|
19
|
+
}
|
|
20
|
+
ownerConns.add(connection);
|
|
21
|
+
let clientConns = this.connectionsByClientId.get(connection.clientId);
|
|
15
22
|
if (!clientConns) {
|
|
16
23
|
clientConns = new Set();
|
|
17
|
-
this.connectionsByClientId.set(clientId, clientConns);
|
|
24
|
+
this.connectionsByClientId.set(connection.clientId, clientConns);
|
|
18
25
|
}
|
|
19
26
|
clientConns.add(connection);
|
|
20
|
-
if (!this.
|
|
21
|
-
this.
|
|
27
|
+
if (!this.scopeKeysByOwnerKey.has(ownerKey)) {
|
|
28
|
+
this.scopeKeysByOwnerKey.set(ownerKey, new Set(initialScopeKeys));
|
|
22
29
|
}
|
|
23
|
-
const scopeKeys = this.
|
|
30
|
+
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
24
31
|
if (scopeKeys) {
|
|
25
32
|
for (const key of scopeKeys) {
|
|
26
33
|
let scopedConns = this.connectionsByScopeKey.get(key);
|
|
@@ -37,12 +44,12 @@ export class RealtimeConnectionRegistry {
|
|
|
37
44
|
this.ensureHeartbeat();
|
|
38
45
|
};
|
|
39
46
|
}
|
|
40
|
-
|
|
41
|
-
const conns = this.
|
|
47
|
+
updateOwnerScopeKeys(ownerKey, scopeKeys) {
|
|
48
|
+
const conns = this.connectionsByOwnerKey.get(ownerKey);
|
|
42
49
|
if (!conns || conns.size === 0)
|
|
43
50
|
return;
|
|
44
51
|
const next = new Set(scopeKeys);
|
|
45
|
-
const prev = this.
|
|
52
|
+
const prev = this.scopeKeysByOwnerKey.get(ownerKey) ?? new Set();
|
|
46
53
|
if (prev.size === next.size) {
|
|
47
54
|
let unchanged = true;
|
|
48
55
|
for (const key of prev) {
|
|
@@ -54,7 +61,7 @@ export class RealtimeConnectionRegistry {
|
|
|
54
61
|
if (unchanged)
|
|
55
62
|
return;
|
|
56
63
|
}
|
|
57
|
-
this.
|
|
64
|
+
this.scopeKeysByOwnerKey.set(ownerKey, next);
|
|
58
65
|
for (const key of prev) {
|
|
59
66
|
if (next.has(key))
|
|
60
67
|
continue;
|
|
@@ -78,12 +85,18 @@ export class RealtimeConnectionRegistry {
|
|
|
78
85
|
scopedConns.add(conn);
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
|
-
|
|
82
|
-
const scopeKeys = this.
|
|
88
|
+
isOwnerSubscribedToScopeKey(ownerKey, scopeKey) {
|
|
89
|
+
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
83
90
|
if (!scopeKeys || scopeKeys.size === 0)
|
|
84
91
|
return false;
|
|
85
92
|
return scopeKeys.has(scopeKey);
|
|
86
93
|
}
|
|
94
|
+
getConnectionsForOwner(ownerKey) {
|
|
95
|
+
return this.connectionsByOwnerKey.get(ownerKey);
|
|
96
|
+
}
|
|
97
|
+
getScopedConnectionCount(ownerKey) {
|
|
98
|
+
return this.connectionsByOwnerKey.get(ownerKey)?.size ?? 0;
|
|
99
|
+
}
|
|
87
100
|
getConnectionsForClient(clientId) {
|
|
88
101
|
return this.connectionsByClientId.get(clientId);
|
|
89
102
|
}
|
|
@@ -92,7 +105,7 @@ export class RealtimeConnectionRegistry {
|
|
|
92
105
|
}
|
|
93
106
|
getTotalConnections() {
|
|
94
107
|
let total = 0;
|
|
95
|
-
for (const conns of this.
|
|
108
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
96
109
|
total += conns.size;
|
|
97
110
|
}
|
|
98
111
|
return total;
|
|
@@ -116,7 +129,7 @@ export class RealtimeConnectionRegistry {
|
|
|
116
129
|
}
|
|
117
130
|
}
|
|
118
131
|
forEachConnection(visitor) {
|
|
119
|
-
for (const conns of this.
|
|
132
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
120
133
|
for (const conn of conns) {
|
|
121
134
|
if (!conn.isOpen)
|
|
122
135
|
continue;
|
|
@@ -124,38 +137,33 @@ export class RealtimeConnectionRegistry {
|
|
|
124
137
|
}
|
|
125
138
|
}
|
|
126
139
|
}
|
|
140
|
+
closeOwnerConnections(ownerKey, code, reason) {
|
|
141
|
+
const conns = this.connectionsByOwnerKey.get(ownerKey);
|
|
142
|
+
if (!conns)
|
|
143
|
+
return;
|
|
144
|
+
for (const conn of Array.from(conns)) {
|
|
145
|
+
conn.close(code, reason);
|
|
146
|
+
this.unregister(conn);
|
|
147
|
+
}
|
|
148
|
+
this.ensureHeartbeat();
|
|
149
|
+
}
|
|
127
150
|
closeClientConnections(clientId, code, reason) {
|
|
128
151
|
const conns = this.connectionsByClientId.get(clientId);
|
|
129
152
|
if (!conns)
|
|
130
153
|
return;
|
|
131
|
-
const
|
|
132
|
-
if (scopeKeys) {
|
|
133
|
-
for (const key of scopeKeys) {
|
|
134
|
-
const scopedConns = this.connectionsByScopeKey.get(key);
|
|
135
|
-
if (!scopedConns)
|
|
136
|
-
continue;
|
|
137
|
-
for (const conn of conns)
|
|
138
|
-
scopedConns.delete(conn);
|
|
139
|
-
if (scopedConns.size === 0)
|
|
140
|
-
this.connectionsByScopeKey.delete(key);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
for (const conn of conns) {
|
|
154
|
+
for (const conn of Array.from(conns)) {
|
|
144
155
|
conn.close(code, reason);
|
|
156
|
+
this.unregister(conn);
|
|
145
157
|
}
|
|
146
|
-
this.connectionsByClientId.delete(clientId);
|
|
147
|
-
this.scopeKeysByClientId.delete(clientId);
|
|
148
158
|
this.ensureHeartbeat();
|
|
149
159
|
}
|
|
150
160
|
closeAll(code, reason) {
|
|
151
|
-
for (const conns of this.
|
|
152
|
-
for (const conn of conns) {
|
|
161
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
162
|
+
for (const conn of Array.from(conns)) {
|
|
153
163
|
conn.close(code, reason);
|
|
164
|
+
this.unregister(conn);
|
|
154
165
|
}
|
|
155
166
|
}
|
|
156
|
-
this.connectionsByClientId.clear();
|
|
157
|
-
this.scopeKeysByClientId.clear();
|
|
158
|
-
this.connectionsByScopeKey.clear();
|
|
159
167
|
this.ensureHeartbeat();
|
|
160
168
|
}
|
|
161
169
|
ensureHeartbeat() {
|
|
@@ -177,7 +185,7 @@ export class RealtimeConnectionRegistry {
|
|
|
177
185
|
}
|
|
178
186
|
sendHeartbeats() {
|
|
179
187
|
const closedConnections = [];
|
|
180
|
-
for (const conns of this.
|
|
188
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
181
189
|
for (const conn of conns) {
|
|
182
190
|
if (!conn.isOpen) {
|
|
183
191
|
closedConnections.push(conn);
|
|
@@ -192,8 +200,8 @@ export class RealtimeConnectionRegistry {
|
|
|
192
200
|
this.ensureHeartbeat();
|
|
193
201
|
}
|
|
194
202
|
unregister(connection) {
|
|
195
|
-
const
|
|
196
|
-
const scopeKeys = this.
|
|
203
|
+
const ownerKey = connection.ownerKey;
|
|
204
|
+
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
197
205
|
if (scopeKeys) {
|
|
198
206
|
for (const key of scopeKeys) {
|
|
199
207
|
const scopedConns = this.connectionsByScopeKey.get(key);
|
|
@@ -204,15 +212,22 @@ export class RealtimeConnectionRegistry {
|
|
|
204
212
|
this.connectionsByScopeKey.delete(key);
|
|
205
213
|
}
|
|
206
214
|
}
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
const ownerConns = this.connectionsByOwnerKey.get(ownerKey);
|
|
216
|
+
if (ownerConns) {
|
|
217
|
+
ownerConns.delete(connection);
|
|
218
|
+
if (ownerConns.size === 0) {
|
|
219
|
+
this.connectionsByOwnerKey.delete(ownerKey);
|
|
220
|
+
this.scopeKeysByOwnerKey.delete(ownerKey);
|
|
221
|
+
this.onOwnerDisconnected?.(ownerKey);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const clientConns = this.connectionsByClientId.get(connection.clientId);
|
|
225
|
+
if (!clientConns)
|
|
212
226
|
return;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
227
|
+
clientConns.delete(connection);
|
|
228
|
+
if (clientConns.size === 0) {
|
|
229
|
+
this.connectionsByClientId.delete(connection.clientId);
|
|
230
|
+
}
|
|
216
231
|
}
|
|
217
232
|
}
|
|
218
233
|
//# sourceMappingURL=realtime-connection-registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime-connection-registry.js","sourceRoot":"","sources":["../../src/utils/realtime-connection-registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"realtime-connection-registry.js","sourceRoot":"","sources":["../../src/utils/realtime-connection-registry.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export interface RealtimeConnection {
|
|
2
2
|
readonly clientId: string;
|
|
3
|
+
readonly ownerKey: string;
|
|
3
4
|
readonly isOpen: boolean;
|
|
4
5
|
sendHeartbeat(): void;
|
|
5
6
|
close(code?: number, reason?: string): void;
|
|
@@ -8,39 +9,47 @@ export interface RealtimeConnection {
|
|
|
8
9
|
export class RealtimeConnectionRegistry<
|
|
9
10
|
TConnection extends RealtimeConnection,
|
|
10
11
|
> {
|
|
12
|
+
private connectionsByOwnerKey = new Map<string, Set<TConnection>>();
|
|
11
13
|
private connectionsByClientId = new Map<string, Set<TConnection>>();
|
|
12
|
-
private
|
|
14
|
+
private scopeKeysByOwnerKey = new Map<string, Set<string>>();
|
|
13
15
|
private connectionsByScopeKey = new Map<string, Set<TConnection>>();
|
|
14
16
|
|
|
15
17
|
private readonly heartbeatIntervalMs: number;
|
|
16
18
|
private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
17
|
-
private readonly
|
|
19
|
+
private readonly onOwnerDisconnected?: (ownerKey: string) => void;
|
|
18
20
|
|
|
19
21
|
constructor(options?: {
|
|
20
22
|
heartbeatIntervalMs?: number;
|
|
21
|
-
|
|
23
|
+
onOwnerDisconnected?: (ownerKey: string) => void;
|
|
22
24
|
}) {
|
|
23
25
|
this.heartbeatIntervalMs = options?.heartbeatIntervalMs ?? 30_000;
|
|
24
|
-
this.
|
|
26
|
+
this.onOwnerDisconnected = options?.onOwnerDisconnected;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
register(
|
|
28
30
|
connection: TConnection,
|
|
29
31
|
initialScopeKeys: string[] = []
|
|
30
32
|
): () => void {
|
|
31
|
-
const
|
|
32
|
-
let
|
|
33
|
+
const ownerKey = connection.ownerKey;
|
|
34
|
+
let ownerConns = this.connectionsByOwnerKey.get(ownerKey);
|
|
35
|
+
if (!ownerConns) {
|
|
36
|
+
ownerConns = new Set();
|
|
37
|
+
this.connectionsByOwnerKey.set(ownerKey, ownerConns);
|
|
38
|
+
}
|
|
39
|
+
ownerConns.add(connection);
|
|
40
|
+
|
|
41
|
+
let clientConns = this.connectionsByClientId.get(connection.clientId);
|
|
33
42
|
if (!clientConns) {
|
|
34
43
|
clientConns = new Set();
|
|
35
|
-
this.connectionsByClientId.set(clientId, clientConns);
|
|
44
|
+
this.connectionsByClientId.set(connection.clientId, clientConns);
|
|
36
45
|
}
|
|
37
46
|
clientConns.add(connection);
|
|
38
47
|
|
|
39
|
-
if (!this.
|
|
40
|
-
this.
|
|
48
|
+
if (!this.scopeKeysByOwnerKey.has(ownerKey)) {
|
|
49
|
+
this.scopeKeysByOwnerKey.set(ownerKey, new Set(initialScopeKeys));
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
const scopeKeys = this.
|
|
52
|
+
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
44
53
|
if (scopeKeys) {
|
|
45
54
|
for (const key of scopeKeys) {
|
|
46
55
|
let scopedConns = this.connectionsByScopeKey.get(key);
|
|
@@ -59,12 +68,12 @@ export class RealtimeConnectionRegistry<
|
|
|
59
68
|
};
|
|
60
69
|
}
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
const conns = this.
|
|
71
|
+
updateOwnerScopeKeys(ownerKey: string, scopeKeys: string[]): void {
|
|
72
|
+
const conns = this.connectionsByOwnerKey.get(ownerKey);
|
|
64
73
|
if (!conns || conns.size === 0) return;
|
|
65
74
|
|
|
66
75
|
const next = new Set(scopeKeys);
|
|
67
|
-
const prev = this.
|
|
76
|
+
const prev = this.scopeKeysByOwnerKey.get(ownerKey) ?? new Set<string>();
|
|
68
77
|
|
|
69
78
|
if (prev.size === next.size) {
|
|
70
79
|
let unchanged = true;
|
|
@@ -77,7 +86,7 @@ export class RealtimeConnectionRegistry<
|
|
|
77
86
|
if (unchanged) return;
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
this.
|
|
89
|
+
this.scopeKeysByOwnerKey.set(ownerKey, next);
|
|
81
90
|
|
|
82
91
|
for (const key of prev) {
|
|
83
92
|
if (next.has(key)) continue;
|
|
@@ -98,12 +107,22 @@ export class RealtimeConnectionRegistry<
|
|
|
98
107
|
}
|
|
99
108
|
}
|
|
100
109
|
|
|
101
|
-
|
|
102
|
-
const scopeKeys = this.
|
|
110
|
+
isOwnerSubscribedToScopeKey(ownerKey: string, scopeKey: string): boolean {
|
|
111
|
+
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
103
112
|
if (!scopeKeys || scopeKeys.size === 0) return false;
|
|
104
113
|
return scopeKeys.has(scopeKey);
|
|
105
114
|
}
|
|
106
115
|
|
|
116
|
+
getConnectionsForOwner(
|
|
117
|
+
ownerKey: string
|
|
118
|
+
): ReadonlySet<TConnection> | undefined {
|
|
119
|
+
return this.connectionsByOwnerKey.get(ownerKey);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getScopedConnectionCount(ownerKey: string): number {
|
|
123
|
+
return this.connectionsByOwnerKey.get(ownerKey)?.size ?? 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
107
126
|
getConnectionsForClient(
|
|
108
127
|
clientId: string
|
|
109
128
|
): ReadonlySet<TConnection> | undefined {
|
|
@@ -116,7 +135,7 @@ export class RealtimeConnectionRegistry<
|
|
|
116
135
|
|
|
117
136
|
getTotalConnections(): number {
|
|
118
137
|
let total = 0;
|
|
119
|
-
for (const conns of this.
|
|
138
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
120
139
|
total += conns.size;
|
|
121
140
|
}
|
|
122
141
|
return total;
|
|
@@ -143,7 +162,7 @@ export class RealtimeConnectionRegistry<
|
|
|
143
162
|
}
|
|
144
163
|
|
|
145
164
|
forEachConnection(visitor: (connection: TConnection) => void): void {
|
|
146
|
-
for (const conns of this.
|
|
165
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
147
166
|
for (const conn of conns) {
|
|
148
167
|
if (!conn.isOpen) continue;
|
|
149
168
|
visitor(conn);
|
|
@@ -151,6 +170,22 @@ export class RealtimeConnectionRegistry<
|
|
|
151
170
|
}
|
|
152
171
|
}
|
|
153
172
|
|
|
173
|
+
closeOwnerConnections(
|
|
174
|
+
ownerKey: string,
|
|
175
|
+
code?: number,
|
|
176
|
+
reason?: string
|
|
177
|
+
): void {
|
|
178
|
+
const conns = this.connectionsByOwnerKey.get(ownerKey);
|
|
179
|
+
if (!conns) return;
|
|
180
|
+
|
|
181
|
+
for (const conn of Array.from(conns)) {
|
|
182
|
+
conn.close(code, reason);
|
|
183
|
+
this.unregister(conn);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.ensureHeartbeat();
|
|
187
|
+
}
|
|
188
|
+
|
|
154
189
|
closeClientConnections(
|
|
155
190
|
clientId: string,
|
|
156
191
|
code?: number,
|
|
@@ -159,35 +194,20 @@ export class RealtimeConnectionRegistry<
|
|
|
159
194
|
const conns = this.connectionsByClientId.get(clientId);
|
|
160
195
|
if (!conns) return;
|
|
161
196
|
|
|
162
|
-
const
|
|
163
|
-
if (scopeKeys) {
|
|
164
|
-
for (const key of scopeKeys) {
|
|
165
|
-
const scopedConns = this.connectionsByScopeKey.get(key);
|
|
166
|
-
if (!scopedConns) continue;
|
|
167
|
-
for (const conn of conns) scopedConns.delete(conn);
|
|
168
|
-
if (scopedConns.size === 0) this.connectionsByScopeKey.delete(key);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
for (const conn of conns) {
|
|
197
|
+
for (const conn of Array.from(conns)) {
|
|
173
198
|
conn.close(code, reason);
|
|
199
|
+
this.unregister(conn);
|
|
174
200
|
}
|
|
175
|
-
|
|
176
|
-
this.connectionsByClientId.delete(clientId);
|
|
177
|
-
this.scopeKeysByClientId.delete(clientId);
|
|
178
201
|
this.ensureHeartbeat();
|
|
179
202
|
}
|
|
180
203
|
|
|
181
204
|
closeAll(code?: number, reason?: string): void {
|
|
182
|
-
for (const conns of this.
|
|
183
|
-
for (const conn of conns) {
|
|
205
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
206
|
+
for (const conn of Array.from(conns)) {
|
|
184
207
|
conn.close(code, reason);
|
|
208
|
+
this.unregister(conn);
|
|
185
209
|
}
|
|
186
210
|
}
|
|
187
|
-
|
|
188
|
-
this.connectionsByClientId.clear();
|
|
189
|
-
this.scopeKeysByClientId.clear();
|
|
190
|
-
this.connectionsByScopeKey.clear();
|
|
191
211
|
this.ensureHeartbeat();
|
|
192
212
|
}
|
|
193
213
|
|
|
@@ -211,7 +231,7 @@ export class RealtimeConnectionRegistry<
|
|
|
211
231
|
|
|
212
232
|
private sendHeartbeats(): void {
|
|
213
233
|
const closedConnections: TConnection[] = [];
|
|
214
|
-
for (const conns of this.
|
|
234
|
+
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
215
235
|
for (const conn of conns) {
|
|
216
236
|
if (!conn.isOpen) {
|
|
217
237
|
closedConnections.push(conn);
|
|
@@ -229,9 +249,9 @@ export class RealtimeConnectionRegistry<
|
|
|
229
249
|
}
|
|
230
250
|
|
|
231
251
|
private unregister(connection: TConnection): void {
|
|
232
|
-
const
|
|
252
|
+
const ownerKey = connection.ownerKey;
|
|
233
253
|
|
|
234
|
-
const scopeKeys = this.
|
|
254
|
+
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
235
255
|
if (scopeKeys) {
|
|
236
256
|
for (const key of scopeKeys) {
|
|
237
257
|
const scopedConns = this.connectionsByScopeKey.get(key);
|
|
@@ -241,13 +261,21 @@ export class RealtimeConnectionRegistry<
|
|
|
241
261
|
}
|
|
242
262
|
}
|
|
243
263
|
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
264
|
+
const ownerConns = this.connectionsByOwnerKey.get(ownerKey);
|
|
265
|
+
if (ownerConns) {
|
|
266
|
+
ownerConns.delete(connection);
|
|
267
|
+
if (ownerConns.size === 0) {
|
|
268
|
+
this.connectionsByOwnerKey.delete(ownerKey);
|
|
269
|
+
this.scopeKeysByOwnerKey.delete(ownerKey);
|
|
270
|
+
this.onOwnerDisconnected?.(ownerKey);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
248
273
|
|
|
249
|
-
this.connectionsByClientId.
|
|
250
|
-
|
|
251
|
-
|
|
274
|
+
const clientConns = this.connectionsByClientId.get(connection.clientId);
|
|
275
|
+
if (!clientConns) return;
|
|
276
|
+
clientConns.delete(connection);
|
|
277
|
+
if (clientConns.size === 0) {
|
|
278
|
+
this.connectionsByClientId.delete(connection.clientId);
|
|
279
|
+
}
|
|
252
280
|
}
|
|
253
281
|
}
|