@syncular/core 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -19
- package/dist/blob-ref.d.ts +21 -0
- package/dist/blob-ref.js +97 -0
- package/dist/bytes.d.ts +45 -0
- package/dist/bytes.js +242 -0
- package/dist/canonical-json.d.ts +10 -0
- package/dist/canonical-json.js +18 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.js +5 -0
- package/dist/crypto.d.ts +60 -0
- package/dist/crypto.js +251 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +17 -0
- package/dist/frames.d.ts +24 -0
- package/dist/frames.js +42 -0
- package/dist/index.d.ts +18 -26
- package/dist/index.js +18 -43
- package/dist/message.d.ts +157 -0
- package/dist/message.js +786 -0
- package/dist/realtime.d.ts +92 -0
- package/dist/realtime.js +222 -0
- package/dist/render.d.ts +22 -0
- package/dist/render.js +273 -0
- package/dist/row-codec.d.ts +39 -0
- package/dist/row-codec.js +176 -0
- package/dist/segment.d.ts +18 -0
- package/dist/segment.js +105 -0
- package/dist/stream.d.ts +20 -0
- package/dist/stream.js +102 -0
- package/package.json +7 -59
- package/src/blob-ref.ts +135 -0
- package/src/bytes.ts +301 -0
- package/src/canonical-json.ts +20 -0
- package/src/constants.ts +7 -0
- package/src/crypto.ts +335 -0
- package/src/errors.ts +18 -0
- package/src/frames.ts +47 -0
- package/src/index.ts +18 -43
- package/src/message.ts +1024 -0
- package/src/realtime.ts +340 -0
- package/src/render.ts +325 -0
- package/src/row-codec.ts +265 -0
- package/src/segment.ts +150 -0
- package/src/stream.ts +128 -0
- package/dist/blobs.d.ts +0 -153
- package/dist/blobs.d.ts.map +0 -1
- package/dist/blobs.js +0 -47
- package/dist/blobs.js.map +0 -1
- package/dist/column-codecs.d.ts +0 -59
- package/dist/column-codecs.d.ts.map +0 -1
- package/dist/column-codecs.js +0 -159
- package/dist/column-codecs.js.map +0 -1
- package/dist/conflict.d.ts +0 -22
- package/dist/conflict.d.ts.map +0 -1
- package/dist/conflict.js +0 -81
- package/dist/conflict.js.map +0 -1
- package/dist/database.d.ts +0 -7
- package/dist/database.d.ts.map +0 -1
- package/dist/database.js +0 -14
- package/dist/database.js.map +0 -1
- package/dist/dialect.d.ts +0 -7
- package/dist/dialect.d.ts.map +0 -1
- package/dist/dialect.js +0 -2
- package/dist/dialect.js.map +0 -1
- package/dist/error-responses.d.ts +0 -393
- package/dist/error-responses.d.ts.map +0 -1
- package/dist/error-responses.js +0 -393
- package/dist/error-responses.js.map +0 -1
- package/dist/http/api-client.d.ts +0 -11
- package/dist/http/api-client.d.ts.map +0 -1
- package/dist/http/api-client.js +0 -30
- package/dist/http/api-client.js.map +0 -1
- package/dist/http/blob.d.ts +0 -6
- package/dist/http/blob.d.ts.map +0 -1
- package/dist/http/blob.js +0 -46
- package/dist/http/blob.js.map +0 -1
- package/dist/http/generated/api.d.ts +0 -5241
- package/dist/http/generated/api.d.ts.map +0 -1
- package/dist/http/generated/api.js +0 -6
- package/dist/http/generated/api.js.map +0 -1
- package/dist/http/index.d.ts +0 -18
- package/dist/http/index.d.ts.map +0 -1
- package/dist/http/index.js +0 -212
- package/dist/http/index.js.map +0 -1
- package/dist/http/shared.d.ts +0 -45
- package/dist/http/shared.d.ts.map +0 -1
- package/dist/http/shared.js +0 -122
- package/dist/http/shared.js.map +0 -1
- package/dist/http/transport-client.d.ts +0 -31
- package/dist/http/transport-client.d.ts.map +0 -1
- package/dist/http/transport-client.js +0 -193
- package/dist/http/transport-client.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/kysely-column-codecs.d.ts +0 -31
- package/dist/kysely-column-codecs.d.ts.map +0 -1
- package/dist/kysely-column-codecs.js +0 -452
- package/dist/kysely-column-codecs.js.map +0 -1
- package/dist/logger.d.ts +0 -29
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -26
- package/dist/logger.js.map +0 -1
- package/dist/proxy/index.d.ts +0 -54
- package/dist/proxy/index.d.ts.map +0 -1
- package/dist/proxy/index.js +0 -7
- package/dist/proxy/index.js.map +0 -1
- package/dist/schemas/blobs.d.ts +0 -82
- package/dist/schemas/blobs.d.ts.map +0 -1
- package/dist/schemas/blobs.js +0 -64
- package/dist/schemas/blobs.js.map +0 -1
- package/dist/schemas/common.d.ts +0 -100
- package/dist/schemas/common.d.ts.map +0 -1
- package/dist/schemas/common.js +0 -64
- package/dist/schemas/common.js.map +0 -1
- package/dist/schemas/index.d.ts +0 -7
- package/dist/schemas/index.d.ts.map +0 -1
- package/dist/schemas/index.js +0 -7
- package/dist/schemas/index.js.map +0 -1
- package/dist/schemas/sync.d.ts +0 -1065
- package/dist/schemas/sync.d.ts.map +0 -1
- package/dist/schemas/sync.js +0 -361
- package/dist/schemas/sync.js.map +0 -1
- package/dist/scopes/index.d.ts +0 -113
- package/dist/scopes/index.d.ts.map +0 -1
- package/dist/scopes/index.js +0 -103
- package/dist/scopes/index.js.map +0 -1
- package/dist/sentry.d.ts +0 -31
- package/dist/sentry.d.ts.map +0 -1
- package/dist/sentry.js +0 -147
- package/dist/sentry.js.map +0 -1
- package/dist/snapshot-chunks.d.ts +0 -110
- package/dist/snapshot-chunks.d.ts.map +0 -1
- package/dist/snapshot-chunks.js +0 -682
- package/dist/snapshot-chunks.js.map +0 -1
- package/dist/sync-packs.d.ts +0 -33
- package/dist/sync-packs.d.ts.map +0 -1
- package/dist/sync-packs.js +0 -821
- package/dist/sync-packs.js.map +0 -1
- package/dist/telemetry.d.ts +0 -114
- package/dist/telemetry.d.ts.map +0 -1
- package/dist/telemetry.js +0 -114
- package/dist/telemetry.js.map +0 -1
- package/dist/transforms.d.ts +0 -146
- package/dist/transforms.d.ts.map +0 -1
- package/dist/transforms.js +0 -155
- package/dist/transforms.js.map +0 -1
- package/dist/types.d.ts +0 -210
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -20
- package/dist/types.js.map +0 -1
- package/dist/utils/bytes.d.ts +0 -4
- package/dist/utils/bytes.d.ts.map +0 -1
- package/dist/utils/bytes.js +0 -66
- package/dist/utils/bytes.js.map +0 -1
- package/dist/utils/compression.d.ts +0 -22
- package/dist/utils/compression.d.ts.map +0 -1
- package/dist/utils/compression.js +0 -129
- package/dist/utils/compression.js.map +0 -1
- package/dist/utils/crypto.d.ts +0 -13
- package/dist/utils/crypto.d.ts.map +0 -1
- package/dist/utils/crypto.js +0 -107
- package/dist/utils/crypto.js.map +0 -1
- package/dist/utils/id.d.ts +0 -3
- package/dist/utils/id.d.ts.map +0 -1
- package/dist/utils/id.js +0 -25
- package/dist/utils/id.js.map +0 -1
- package/dist/utils/index.d.ts +0 -10
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -10
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/internal-runtime.d.ts +0 -16
- package/dist/utils/internal-runtime.d.ts.map +0 -1
- package/dist/utils/internal-runtime.js +0 -14
- package/dist/utils/internal-runtime.js.map +0 -1
- package/dist/utils/object.d.ts +0 -2
- package/dist/utils/object.d.ts.map +0 -1
- package/dist/utils/object.js +0 -4
- package/dist/utils/object.js.map +0 -1
- package/dist/utils/realtime-change-index.d.ts +0 -9
- package/dist/utils/realtime-change-index.d.ts.map +0 -1
- package/dist/utils/realtime-change-index.js +0 -36
- package/dist/utils/realtime-change-index.js.map +0 -1
- package/dist/utils/realtime-connection-registry.d.ts +0 -40
- package/dist/utils/realtime-connection-registry.d.ts.map +0 -1
- package/dist/utils/realtime-connection-registry.js +0 -237
- package/dist/utils/realtime-connection-registry.js.map +0 -1
- package/dist/utils/table-registry.d.ts +0 -10
- package/dist/utils/table-registry.d.ts.map +0 -1
- package/dist/utils/table-registry.js +0 -54
- package/dist/utils/table-registry.js.map +0 -1
- package/dist/utils/url.d.ts +0 -3
- package/dist/utils/url.d.ts.map +0 -1
- package/dist/utils/url.js +0 -20
- package/dist/utils/url.js.map +0 -1
- package/src/blobs.ts +0 -219
- package/src/column-codecs.ts +0 -262
- package/src/conflict.ts +0 -92
- package/src/database.ts +0 -21
- package/src/dialect.ts +0 -8
- package/src/error-responses.ts +0 -423
- package/src/http/api-client.ts +0 -39
- package/src/http/blob.ts +0 -95
- package/src/http/generated/api.ts +0 -5241
- package/src/http/index.ts +0 -365
- package/src/http/shared.ts +0 -220
- package/src/http/transport-client.ts +0 -314
- package/src/kysely-column-codecs.ts +0 -585
- package/src/logger.ts +0 -38
- package/src/proxy/index.ts +0 -57
- package/src/schemas/blobs.ts +0 -110
- package/src/schemas/common.ts +0 -89
- package/src/schemas/index.ts +0 -7
- package/src/schemas/sync.ts +0 -517
- package/src/scopes/index.ts +0 -237
- package/src/sentry.ts +0 -200
- package/src/snapshot-chunks.ts +0 -964
- package/src/sync-packs.ts +0 -1096
- package/src/telemetry.ts +0 -243
- package/src/transforms.ts +0 -256
- package/src/types.ts +0 -267
- package/src/utils/bytes.ts +0 -75
- package/src/utils/compression.ts +0 -187
- package/src/utils/crypto.ts +0 -146
- package/src/utils/id.ts +0 -34
- package/src/utils/index.ts +0 -9
- package/src/utils/internal-runtime.ts +0 -39
- package/src/utils/object.ts +0 -3
- package/src/utils/realtime-change-index.ts +0 -44
- package/src/utils/realtime-connection-registry.ts +0 -286
- package/src/utils/table-registry.ts +0 -67
- package/src/utils/url.ts +0 -29
package/src/utils/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export * from './bytes';
|
|
2
|
-
export * from './compression';
|
|
3
|
-
export * from './crypto';
|
|
4
|
-
export * from './id';
|
|
5
|
-
export * from './object';
|
|
6
|
-
export * from './realtime-change-index';
|
|
7
|
-
export * from './realtime-connection-registry';
|
|
8
|
-
export * from './table-registry';
|
|
9
|
-
export * from './url';
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
interface BunCryptoHasher {
|
|
2
|
-
update(data: string | Uint8Array): void;
|
|
3
|
-
digest(encoding: 'hex'): string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
interface BunRuntime {
|
|
7
|
-
CryptoHasher?: new (algorithm: string) => BunCryptoHasher;
|
|
8
|
-
gzipSync?: (data: Uint8Array, options?: { level?: number }) => Uint8Array;
|
|
9
|
-
gunzipSync?: (data: Uint8Array) => Uint8Array;
|
|
10
|
-
readableStreamToBytes?: (
|
|
11
|
-
stream: ReadableStream<Uint8Array>
|
|
12
|
-
) => Promise<Uint8Array | ArrayBuffer>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
type RuntimeGlobals = typeof globalThis & {
|
|
16
|
-
Bun?: BunRuntime;
|
|
17
|
-
Deno?: object;
|
|
18
|
-
process?: {
|
|
19
|
-
versions?: {
|
|
20
|
-
node?: string;
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
function getRuntimeGlobals(): RuntimeGlobals {
|
|
26
|
-
return globalThis as RuntimeGlobals;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function getBunRuntime(): BunRuntime | null {
|
|
30
|
-
return getRuntimeGlobals().Bun ?? null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function usesNodeRuntimeModules(): boolean {
|
|
34
|
-
const globals = getRuntimeGlobals();
|
|
35
|
-
if (globals.Deno !== undefined) {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
return typeof globals.process?.versions?.node === 'string';
|
|
39
|
-
}
|
package/src/utils/object.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export interface RealtimeChangeScopeEntry<T> {
|
|
2
|
-
item: T;
|
|
3
|
-
scopeKeys: readonly string[];
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface RealtimeChangeScopeIndex<T> {
|
|
7
|
-
selectForScopeKeys(scopeKeys: Iterable<string>): T[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function createRealtimeChangeScopeIndex<T>(
|
|
11
|
-
entries: readonly RealtimeChangeScopeEntry<T>[]
|
|
12
|
-
): RealtimeChangeScopeIndex<T> {
|
|
13
|
-
const items = entries.map((entry) => entry.item);
|
|
14
|
-
const indexesByScopeKey = new Map<string, number[]>();
|
|
15
|
-
|
|
16
|
-
for (let index = 0; index < entries.length; index += 1) {
|
|
17
|
-
const seenScopeKeys = new Set<string>();
|
|
18
|
-
for (const scopeKey of entries[index]!.scopeKeys) {
|
|
19
|
-
if (!scopeKey || seenScopeKeys.has(scopeKey)) continue;
|
|
20
|
-
seenScopeKeys.add(scopeKey);
|
|
21
|
-
let indexes = indexesByScopeKey.get(scopeKey);
|
|
22
|
-
if (!indexes) {
|
|
23
|
-
indexes = [];
|
|
24
|
-
indexesByScopeKey.set(scopeKey, indexes);
|
|
25
|
-
}
|
|
26
|
-
indexes.push(index);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
selectForScopeKeys(scopeKeys: Iterable<string>): T[] {
|
|
32
|
-
const selectedIndexes = new Set<number>();
|
|
33
|
-
for (const scopeKey of scopeKeys) {
|
|
34
|
-
const indexes = indexesByScopeKey.get(scopeKey);
|
|
35
|
-
if (!indexes) continue;
|
|
36
|
-
for (const index of indexes) selectedIndexes.add(index);
|
|
37
|
-
}
|
|
38
|
-
if (selectedIndexes.size === 0) return [];
|
|
39
|
-
return Array.from(selectedIndexes)
|
|
40
|
-
.sort((a, b) => a - b)
|
|
41
|
-
.map((index) => items[index]!);
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
export interface RealtimeConnection {
|
|
2
|
-
readonly clientId: string;
|
|
3
|
-
readonly ownerKey: string;
|
|
4
|
-
readonly isOpen: boolean;
|
|
5
|
-
sendHeartbeat(): void;
|
|
6
|
-
close(code?: number, reason?: string): void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class RealtimeConnectionRegistry<
|
|
10
|
-
TConnection extends RealtimeConnection,
|
|
11
|
-
> {
|
|
12
|
-
private connectionsByOwnerKey = new Map<string, Set<TConnection>>();
|
|
13
|
-
private connectionsByClientId = new Map<string, Set<TConnection>>();
|
|
14
|
-
private scopeKeysByOwnerKey = new Map<string, Set<string>>();
|
|
15
|
-
private connectionsByScopeKey = new Map<string, Set<TConnection>>();
|
|
16
|
-
|
|
17
|
-
private readonly heartbeatIntervalMs: number;
|
|
18
|
-
private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
19
|
-
private readonly onOwnerDisconnected?: (ownerKey: string) => void;
|
|
20
|
-
|
|
21
|
-
constructor(options?: {
|
|
22
|
-
heartbeatIntervalMs?: number;
|
|
23
|
-
onOwnerDisconnected?: (ownerKey: string) => void;
|
|
24
|
-
}) {
|
|
25
|
-
this.heartbeatIntervalMs = options?.heartbeatIntervalMs ?? 30_000;
|
|
26
|
-
this.onOwnerDisconnected = options?.onOwnerDisconnected;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
register(
|
|
30
|
-
connection: TConnection,
|
|
31
|
-
initialScopeKeys: string[] = []
|
|
32
|
-
): () => void {
|
|
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);
|
|
42
|
-
if (!clientConns) {
|
|
43
|
-
clientConns = new Set();
|
|
44
|
-
this.connectionsByClientId.set(connection.clientId, clientConns);
|
|
45
|
-
}
|
|
46
|
-
clientConns.add(connection);
|
|
47
|
-
|
|
48
|
-
if (!this.scopeKeysByOwnerKey.has(ownerKey)) {
|
|
49
|
-
this.scopeKeysByOwnerKey.set(ownerKey, new Set(initialScopeKeys));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
53
|
-
if (scopeKeys) {
|
|
54
|
-
for (const key of scopeKeys) {
|
|
55
|
-
let scopedConns = this.connectionsByScopeKey.get(key);
|
|
56
|
-
if (!scopedConns) {
|
|
57
|
-
scopedConns = new Set();
|
|
58
|
-
this.connectionsByScopeKey.set(key, scopedConns);
|
|
59
|
-
}
|
|
60
|
-
scopedConns.add(connection);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
this.ensureHeartbeat();
|
|
65
|
-
return () => {
|
|
66
|
-
this.unregister(connection);
|
|
67
|
-
this.ensureHeartbeat();
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
updateOwnerScopeKeys(ownerKey: string, scopeKeys: string[]): void {
|
|
72
|
-
const conns = this.connectionsByOwnerKey.get(ownerKey);
|
|
73
|
-
if (!conns || conns.size === 0) return;
|
|
74
|
-
|
|
75
|
-
const next = new Set(scopeKeys);
|
|
76
|
-
const prev = this.scopeKeysByOwnerKey.get(ownerKey) ?? new Set<string>();
|
|
77
|
-
|
|
78
|
-
if (prev.size === next.size) {
|
|
79
|
-
let unchanged = true;
|
|
80
|
-
for (const key of prev) {
|
|
81
|
-
if (!next.has(key)) {
|
|
82
|
-
unchanged = false;
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (unchanged) return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
this.scopeKeysByOwnerKey.set(ownerKey, next);
|
|
90
|
-
|
|
91
|
-
for (const key of prev) {
|
|
92
|
-
if (next.has(key)) continue;
|
|
93
|
-
const scopedConns = this.connectionsByScopeKey.get(key);
|
|
94
|
-
if (!scopedConns) continue;
|
|
95
|
-
for (const conn of conns) scopedConns.delete(conn);
|
|
96
|
-
if (scopedConns.size === 0) this.connectionsByScopeKey.delete(key);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
for (const key of next) {
|
|
100
|
-
if (prev.has(key)) continue;
|
|
101
|
-
let scopedConns = this.connectionsByScopeKey.get(key);
|
|
102
|
-
if (!scopedConns) {
|
|
103
|
-
scopedConns = new Set();
|
|
104
|
-
this.connectionsByScopeKey.set(key, scopedConns);
|
|
105
|
-
}
|
|
106
|
-
for (const conn of conns) scopedConns.add(conn);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
isOwnerSubscribedToScopeKey(ownerKey: string, scopeKey: string): boolean {
|
|
111
|
-
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
112
|
-
if (!scopeKeys || scopeKeys.size === 0) return false;
|
|
113
|
-
return scopeKeys.has(scopeKey);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
getScopeKeysForOwner(ownerKey: string): readonly string[] {
|
|
117
|
-
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
118
|
-
return scopeKeys ? Array.from(scopeKeys) : [];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
getConnectionsForOwner(
|
|
122
|
-
ownerKey: string
|
|
123
|
-
): ReadonlySet<TConnection> | undefined {
|
|
124
|
-
return this.connectionsByOwnerKey.get(ownerKey);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
getScopedConnectionCount(ownerKey: string): number {
|
|
128
|
-
return this.connectionsByOwnerKey.get(ownerKey)?.size ?? 0;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
getConnectionsForClient(
|
|
132
|
-
clientId: string
|
|
133
|
-
): ReadonlySet<TConnection> | undefined {
|
|
134
|
-
return this.connectionsByClientId.get(clientId);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
getConnectionCount(clientId: string): number {
|
|
138
|
-
return this.connectionsByClientId.get(clientId)?.size ?? 0;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
getTotalConnections(): number {
|
|
142
|
-
let total = 0;
|
|
143
|
-
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
144
|
-
total += conns.size;
|
|
145
|
-
}
|
|
146
|
-
return total;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
forEachConnectionInScopeKeys(
|
|
150
|
-
scopeKeys: Iterable<string>,
|
|
151
|
-
visitor: (connection: TConnection) => void,
|
|
152
|
-
options?: { excludeClientIds?: readonly string[] }
|
|
153
|
-
): void {
|
|
154
|
-
const targets = new Set<TConnection>();
|
|
155
|
-
for (const key of scopeKeys) {
|
|
156
|
-
const conns = this.connectionsByScopeKey.get(key);
|
|
157
|
-
if (!conns) continue;
|
|
158
|
-
for (const conn of conns) targets.add(conn);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const excludedClientIds = new Set(options?.excludeClientIds ?? []);
|
|
162
|
-
for (const conn of targets) {
|
|
163
|
-
if (!conn.isOpen) continue;
|
|
164
|
-
if (excludedClientIds.has(conn.clientId)) continue;
|
|
165
|
-
visitor(conn);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
forEachConnection(visitor: (connection: TConnection) => void): void {
|
|
170
|
-
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
171
|
-
for (const conn of conns) {
|
|
172
|
-
if (!conn.isOpen) continue;
|
|
173
|
-
visitor(conn);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
closeOwnerConnections(
|
|
179
|
-
ownerKey: string,
|
|
180
|
-
code?: number,
|
|
181
|
-
reason?: string
|
|
182
|
-
): void {
|
|
183
|
-
const conns = this.connectionsByOwnerKey.get(ownerKey);
|
|
184
|
-
if (!conns) return;
|
|
185
|
-
|
|
186
|
-
for (const conn of Array.from(conns)) {
|
|
187
|
-
conn.close(code, reason);
|
|
188
|
-
this.unregister(conn);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.ensureHeartbeat();
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
closeClientConnections(
|
|
195
|
-
clientId: string,
|
|
196
|
-
code?: number,
|
|
197
|
-
reason?: string
|
|
198
|
-
): void {
|
|
199
|
-
const conns = this.connectionsByClientId.get(clientId);
|
|
200
|
-
if (!conns) return;
|
|
201
|
-
|
|
202
|
-
for (const conn of Array.from(conns)) {
|
|
203
|
-
conn.close(code, reason);
|
|
204
|
-
this.unregister(conn);
|
|
205
|
-
}
|
|
206
|
-
this.ensureHeartbeat();
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
closeAll(code?: number, reason?: string): void {
|
|
210
|
-
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
211
|
-
for (const conn of Array.from(conns)) {
|
|
212
|
-
conn.close(code, reason);
|
|
213
|
-
this.unregister(conn);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
this.ensureHeartbeat();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
private ensureHeartbeat(): void {
|
|
220
|
-
if (this.heartbeatIntervalMs <= 0) return;
|
|
221
|
-
|
|
222
|
-
const total = this.getTotalConnections();
|
|
223
|
-
if (total === 0) {
|
|
224
|
-
if (this.heartbeatTimer) {
|
|
225
|
-
clearInterval(this.heartbeatTimer);
|
|
226
|
-
this.heartbeatTimer = null;
|
|
227
|
-
}
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (this.heartbeatTimer) return;
|
|
232
|
-
this.heartbeatTimer = setInterval(() => {
|
|
233
|
-
this.sendHeartbeats();
|
|
234
|
-
}, this.heartbeatIntervalMs);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private sendHeartbeats(): void {
|
|
238
|
-
const closedConnections: TConnection[] = [];
|
|
239
|
-
for (const conns of this.connectionsByOwnerKey.values()) {
|
|
240
|
-
for (const conn of conns) {
|
|
241
|
-
if (!conn.isOpen) {
|
|
242
|
-
closedConnections.push(conn);
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
conn.sendHeartbeat();
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
for (const conn of closedConnections) {
|
|
250
|
-
this.unregister(conn);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
this.ensureHeartbeat();
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private unregister(connection: TConnection): void {
|
|
257
|
-
const ownerKey = connection.ownerKey;
|
|
258
|
-
|
|
259
|
-
const scopeKeys = this.scopeKeysByOwnerKey.get(ownerKey);
|
|
260
|
-
if (scopeKeys) {
|
|
261
|
-
for (const key of scopeKeys) {
|
|
262
|
-
const scopedConns = this.connectionsByScopeKey.get(key);
|
|
263
|
-
if (!scopedConns) continue;
|
|
264
|
-
scopedConns.delete(connection);
|
|
265
|
-
if (scopedConns.size === 0) this.connectionsByScopeKey.delete(key);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const ownerConns = this.connectionsByOwnerKey.get(ownerKey);
|
|
270
|
-
if (ownerConns) {
|
|
271
|
-
ownerConns.delete(connection);
|
|
272
|
-
if (ownerConns.size === 0) {
|
|
273
|
-
this.connectionsByOwnerKey.delete(ownerKey);
|
|
274
|
-
this.scopeKeysByOwnerKey.delete(ownerKey);
|
|
275
|
-
this.onOwnerDisconnected?.(ownerKey);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const clientConns = this.connectionsByClientId.get(connection.clientId);
|
|
280
|
-
if (!clientConns) return;
|
|
281
|
-
clientConns.delete(connection);
|
|
282
|
-
if (clientConns.size === 0) {
|
|
283
|
-
this.connectionsByClientId.delete(connection.clientId);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
type TableEntry = {
|
|
2
|
-
table: string;
|
|
3
|
-
dependsOn?: readonly string[];
|
|
4
|
-
};
|
|
5
|
-
export function registerTableOrThrow(
|
|
6
|
-
registeredTables: Set<string>,
|
|
7
|
-
table: string,
|
|
8
|
-
duplicateTableError: (table: string) => string
|
|
9
|
-
): void {
|
|
10
|
-
if (registeredTables.has(table)) throw new Error(duplicateTableError(table));
|
|
11
|
-
registeredTables.add(table);
|
|
12
|
-
}
|
|
13
|
-
export function createTableLookup<T extends TableEntry>(
|
|
14
|
-
handlers: readonly T[],
|
|
15
|
-
duplicateTableError: (table: string) => string
|
|
16
|
-
): ReadonlyMap<string, T> {
|
|
17
|
-
const byTable = new Map<string, T>();
|
|
18
|
-
for (const handler of handlers) {
|
|
19
|
-
if (byTable.has(handler.table)) {
|
|
20
|
-
throw new Error(duplicateTableError(handler.table));
|
|
21
|
-
}
|
|
22
|
-
byTable.set(handler.table, handler);
|
|
23
|
-
}
|
|
24
|
-
return byTable;
|
|
25
|
-
}
|
|
26
|
-
export function assertKnownTableDependencies<T extends TableEntry>(
|
|
27
|
-
handlers: readonly T[],
|
|
28
|
-
byTable: ReadonlyMap<string, T>,
|
|
29
|
-
unknownDependencyError: (table: string, dependency: string) => string
|
|
30
|
-
): void {
|
|
31
|
-
for (const handler of handlers) {
|
|
32
|
-
for (const dependency of handler.dependsOn ?? []) {
|
|
33
|
-
if (!byTable.has(dependency)) {
|
|
34
|
-
throw new Error(unknownDependencyError(handler.table, dependency));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export function topologicallySortTablesByDependencies<T extends TableEntry>(
|
|
40
|
-
byTable: ReadonlyMap<string, T>,
|
|
41
|
-
targetTable?: string
|
|
42
|
-
): T[] {
|
|
43
|
-
const visited = new Set<string>();
|
|
44
|
-
const visiting = new Set<string>();
|
|
45
|
-
const sorted: T[] = [];
|
|
46
|
-
const visit = (table: string) => {
|
|
47
|
-
if (visited.has(table)) return;
|
|
48
|
-
if (visiting.has(table)) {
|
|
49
|
-
throw new Error(
|
|
50
|
-
`Circular dependency detected involving table "${table}"`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
const handler = byTable.get(table);
|
|
54
|
-
if (!handler) throw new Error(`Unknown table: ${table}`);
|
|
55
|
-
visiting.add(table);
|
|
56
|
-
for (const dependency of handler.dependsOn ?? []) visit(dependency);
|
|
57
|
-
visiting.delete(table);
|
|
58
|
-
visited.add(table);
|
|
59
|
-
sorted.push(handler);
|
|
60
|
-
};
|
|
61
|
-
if (targetTable) {
|
|
62
|
-
visit(targetTable);
|
|
63
|
-
} else {
|
|
64
|
-
for (const table of byTable.keys()) visit(table);
|
|
65
|
-
}
|
|
66
|
-
return sorted;
|
|
67
|
-
}
|
package/src/utils/url.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const ABSOLUTE_URL_SCHEME_REGEX = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
|
|
2
|
-
|
|
3
|
-
const trimTrailingSlashes = (value: string): string =>
|
|
4
|
-
value.replace(/\/+$/, '');
|
|
5
|
-
|
|
6
|
-
export function normalizeSyncBaseUrl(url: string): string {
|
|
7
|
-
const trimmed = trimTrailingSlashes(url.trim());
|
|
8
|
-
if (!trimmed.endsWith('/sync')) return trimmed;
|
|
9
|
-
const baseUrl = trimmed.slice(0, -'/sync'.length);
|
|
10
|
-
return baseUrl.length > 0 ? baseUrl : '/';
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function resolveUrlFromBase(
|
|
14
|
-
baseUrl: string,
|
|
15
|
-
path: string,
|
|
16
|
-
origin?: string
|
|
17
|
-
): string {
|
|
18
|
-
const normalizedPath = path.replace(/^\/+/, '');
|
|
19
|
-
const normalizedBaseUrl = trimTrailingSlashes(baseUrl);
|
|
20
|
-
if (ABSOLUTE_URL_SCHEME_REGEX.test(baseUrl)) {
|
|
21
|
-
return new URL(
|
|
22
|
-
normalizedPath,
|
|
23
|
-
baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`
|
|
24
|
-
).toString();
|
|
25
|
-
}
|
|
26
|
-
if (origin)
|
|
27
|
-
return new URL(`${normalizedBaseUrl}/${normalizedPath}`, origin).toString();
|
|
28
|
-
return `${normalizedBaseUrl}/${normalizedPath}`;
|
|
29
|
-
}
|