@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.
Files changed (231) hide show
  1. package/README.md +5 -19
  2. package/dist/blob-ref.d.ts +21 -0
  3. package/dist/blob-ref.js +97 -0
  4. package/dist/bytes.d.ts +45 -0
  5. package/dist/bytes.js +242 -0
  6. package/dist/canonical-json.d.ts +10 -0
  7. package/dist/canonical-json.js +18 -0
  8. package/dist/constants.d.ts +5 -0
  9. package/dist/constants.js +5 -0
  10. package/dist/crypto.d.ts +60 -0
  11. package/dist/crypto.js +251 -0
  12. package/dist/errors.d.ts +14 -0
  13. package/dist/errors.js +17 -0
  14. package/dist/frames.d.ts +24 -0
  15. package/dist/frames.js +42 -0
  16. package/dist/index.d.ts +18 -26
  17. package/dist/index.js +18 -43
  18. package/dist/message.d.ts +157 -0
  19. package/dist/message.js +786 -0
  20. package/dist/realtime.d.ts +92 -0
  21. package/dist/realtime.js +222 -0
  22. package/dist/render.d.ts +22 -0
  23. package/dist/render.js +273 -0
  24. package/dist/row-codec.d.ts +39 -0
  25. package/dist/row-codec.js +176 -0
  26. package/dist/segment.d.ts +18 -0
  27. package/dist/segment.js +105 -0
  28. package/dist/stream.d.ts +20 -0
  29. package/dist/stream.js +102 -0
  30. package/package.json +7 -59
  31. package/src/blob-ref.ts +135 -0
  32. package/src/bytes.ts +301 -0
  33. package/src/canonical-json.ts +20 -0
  34. package/src/constants.ts +7 -0
  35. package/src/crypto.ts +335 -0
  36. package/src/errors.ts +18 -0
  37. package/src/frames.ts +47 -0
  38. package/src/index.ts +18 -43
  39. package/src/message.ts +1024 -0
  40. package/src/realtime.ts +340 -0
  41. package/src/render.ts +325 -0
  42. package/src/row-codec.ts +265 -0
  43. package/src/segment.ts +150 -0
  44. package/src/stream.ts +128 -0
  45. package/dist/blobs.d.ts +0 -153
  46. package/dist/blobs.d.ts.map +0 -1
  47. package/dist/blobs.js +0 -47
  48. package/dist/blobs.js.map +0 -1
  49. package/dist/column-codecs.d.ts +0 -59
  50. package/dist/column-codecs.d.ts.map +0 -1
  51. package/dist/column-codecs.js +0 -159
  52. package/dist/column-codecs.js.map +0 -1
  53. package/dist/conflict.d.ts +0 -22
  54. package/dist/conflict.d.ts.map +0 -1
  55. package/dist/conflict.js +0 -81
  56. package/dist/conflict.js.map +0 -1
  57. package/dist/database.d.ts +0 -7
  58. package/dist/database.d.ts.map +0 -1
  59. package/dist/database.js +0 -14
  60. package/dist/database.js.map +0 -1
  61. package/dist/dialect.d.ts +0 -7
  62. package/dist/dialect.d.ts.map +0 -1
  63. package/dist/dialect.js +0 -2
  64. package/dist/dialect.js.map +0 -1
  65. package/dist/error-responses.d.ts +0 -393
  66. package/dist/error-responses.d.ts.map +0 -1
  67. package/dist/error-responses.js +0 -393
  68. package/dist/error-responses.js.map +0 -1
  69. package/dist/http/api-client.d.ts +0 -11
  70. package/dist/http/api-client.d.ts.map +0 -1
  71. package/dist/http/api-client.js +0 -30
  72. package/dist/http/api-client.js.map +0 -1
  73. package/dist/http/blob.d.ts +0 -6
  74. package/dist/http/blob.d.ts.map +0 -1
  75. package/dist/http/blob.js +0 -46
  76. package/dist/http/blob.js.map +0 -1
  77. package/dist/http/generated/api.d.ts +0 -5241
  78. package/dist/http/generated/api.d.ts.map +0 -1
  79. package/dist/http/generated/api.js +0 -6
  80. package/dist/http/generated/api.js.map +0 -1
  81. package/dist/http/index.d.ts +0 -18
  82. package/dist/http/index.d.ts.map +0 -1
  83. package/dist/http/index.js +0 -212
  84. package/dist/http/index.js.map +0 -1
  85. package/dist/http/shared.d.ts +0 -45
  86. package/dist/http/shared.d.ts.map +0 -1
  87. package/dist/http/shared.js +0 -122
  88. package/dist/http/shared.js.map +0 -1
  89. package/dist/http/transport-client.d.ts +0 -31
  90. package/dist/http/transport-client.d.ts.map +0 -1
  91. package/dist/http/transport-client.js +0 -193
  92. package/dist/http/transport-client.js.map +0 -1
  93. package/dist/index.d.ts.map +0 -1
  94. package/dist/index.js.map +0 -1
  95. package/dist/kysely-column-codecs.d.ts +0 -31
  96. package/dist/kysely-column-codecs.d.ts.map +0 -1
  97. package/dist/kysely-column-codecs.js +0 -452
  98. package/dist/kysely-column-codecs.js.map +0 -1
  99. package/dist/logger.d.ts +0 -29
  100. package/dist/logger.d.ts.map +0 -1
  101. package/dist/logger.js +0 -26
  102. package/dist/logger.js.map +0 -1
  103. package/dist/proxy/index.d.ts +0 -54
  104. package/dist/proxy/index.d.ts.map +0 -1
  105. package/dist/proxy/index.js +0 -7
  106. package/dist/proxy/index.js.map +0 -1
  107. package/dist/schemas/blobs.d.ts +0 -82
  108. package/dist/schemas/blobs.d.ts.map +0 -1
  109. package/dist/schemas/blobs.js +0 -64
  110. package/dist/schemas/blobs.js.map +0 -1
  111. package/dist/schemas/common.d.ts +0 -100
  112. package/dist/schemas/common.d.ts.map +0 -1
  113. package/dist/schemas/common.js +0 -64
  114. package/dist/schemas/common.js.map +0 -1
  115. package/dist/schemas/index.d.ts +0 -7
  116. package/dist/schemas/index.d.ts.map +0 -1
  117. package/dist/schemas/index.js +0 -7
  118. package/dist/schemas/index.js.map +0 -1
  119. package/dist/schemas/sync.d.ts +0 -1065
  120. package/dist/schemas/sync.d.ts.map +0 -1
  121. package/dist/schemas/sync.js +0 -361
  122. package/dist/schemas/sync.js.map +0 -1
  123. package/dist/scopes/index.d.ts +0 -113
  124. package/dist/scopes/index.d.ts.map +0 -1
  125. package/dist/scopes/index.js +0 -103
  126. package/dist/scopes/index.js.map +0 -1
  127. package/dist/sentry.d.ts +0 -31
  128. package/dist/sentry.d.ts.map +0 -1
  129. package/dist/sentry.js +0 -147
  130. package/dist/sentry.js.map +0 -1
  131. package/dist/snapshot-chunks.d.ts +0 -110
  132. package/dist/snapshot-chunks.d.ts.map +0 -1
  133. package/dist/snapshot-chunks.js +0 -682
  134. package/dist/snapshot-chunks.js.map +0 -1
  135. package/dist/sync-packs.d.ts +0 -33
  136. package/dist/sync-packs.d.ts.map +0 -1
  137. package/dist/sync-packs.js +0 -821
  138. package/dist/sync-packs.js.map +0 -1
  139. package/dist/telemetry.d.ts +0 -114
  140. package/dist/telemetry.d.ts.map +0 -1
  141. package/dist/telemetry.js +0 -114
  142. package/dist/telemetry.js.map +0 -1
  143. package/dist/transforms.d.ts +0 -146
  144. package/dist/transforms.d.ts.map +0 -1
  145. package/dist/transforms.js +0 -155
  146. package/dist/transforms.js.map +0 -1
  147. package/dist/types.d.ts +0 -210
  148. package/dist/types.d.ts.map +0 -1
  149. package/dist/types.js +0 -20
  150. package/dist/types.js.map +0 -1
  151. package/dist/utils/bytes.d.ts +0 -4
  152. package/dist/utils/bytes.d.ts.map +0 -1
  153. package/dist/utils/bytes.js +0 -66
  154. package/dist/utils/bytes.js.map +0 -1
  155. package/dist/utils/compression.d.ts +0 -22
  156. package/dist/utils/compression.d.ts.map +0 -1
  157. package/dist/utils/compression.js +0 -129
  158. package/dist/utils/compression.js.map +0 -1
  159. package/dist/utils/crypto.d.ts +0 -13
  160. package/dist/utils/crypto.d.ts.map +0 -1
  161. package/dist/utils/crypto.js +0 -107
  162. package/dist/utils/crypto.js.map +0 -1
  163. package/dist/utils/id.d.ts +0 -3
  164. package/dist/utils/id.d.ts.map +0 -1
  165. package/dist/utils/id.js +0 -25
  166. package/dist/utils/id.js.map +0 -1
  167. package/dist/utils/index.d.ts +0 -10
  168. package/dist/utils/index.d.ts.map +0 -1
  169. package/dist/utils/index.js +0 -10
  170. package/dist/utils/index.js.map +0 -1
  171. package/dist/utils/internal-runtime.d.ts +0 -16
  172. package/dist/utils/internal-runtime.d.ts.map +0 -1
  173. package/dist/utils/internal-runtime.js +0 -14
  174. package/dist/utils/internal-runtime.js.map +0 -1
  175. package/dist/utils/object.d.ts +0 -2
  176. package/dist/utils/object.d.ts.map +0 -1
  177. package/dist/utils/object.js +0 -4
  178. package/dist/utils/object.js.map +0 -1
  179. package/dist/utils/realtime-change-index.d.ts +0 -9
  180. package/dist/utils/realtime-change-index.d.ts.map +0 -1
  181. package/dist/utils/realtime-change-index.js +0 -36
  182. package/dist/utils/realtime-change-index.js.map +0 -1
  183. package/dist/utils/realtime-connection-registry.d.ts +0 -40
  184. package/dist/utils/realtime-connection-registry.d.ts.map +0 -1
  185. package/dist/utils/realtime-connection-registry.js +0 -237
  186. package/dist/utils/realtime-connection-registry.js.map +0 -1
  187. package/dist/utils/table-registry.d.ts +0 -10
  188. package/dist/utils/table-registry.d.ts.map +0 -1
  189. package/dist/utils/table-registry.js +0 -54
  190. package/dist/utils/table-registry.js.map +0 -1
  191. package/dist/utils/url.d.ts +0 -3
  192. package/dist/utils/url.d.ts.map +0 -1
  193. package/dist/utils/url.js +0 -20
  194. package/dist/utils/url.js.map +0 -1
  195. package/src/blobs.ts +0 -219
  196. package/src/column-codecs.ts +0 -262
  197. package/src/conflict.ts +0 -92
  198. package/src/database.ts +0 -21
  199. package/src/dialect.ts +0 -8
  200. package/src/error-responses.ts +0 -423
  201. package/src/http/api-client.ts +0 -39
  202. package/src/http/blob.ts +0 -95
  203. package/src/http/generated/api.ts +0 -5241
  204. package/src/http/index.ts +0 -365
  205. package/src/http/shared.ts +0 -220
  206. package/src/http/transport-client.ts +0 -314
  207. package/src/kysely-column-codecs.ts +0 -585
  208. package/src/logger.ts +0 -38
  209. package/src/proxy/index.ts +0 -57
  210. package/src/schemas/blobs.ts +0 -110
  211. package/src/schemas/common.ts +0 -89
  212. package/src/schemas/index.ts +0 -7
  213. package/src/schemas/sync.ts +0 -517
  214. package/src/scopes/index.ts +0 -237
  215. package/src/sentry.ts +0 -200
  216. package/src/snapshot-chunks.ts +0 -964
  217. package/src/sync-packs.ts +0 -1096
  218. package/src/telemetry.ts +0 -243
  219. package/src/transforms.ts +0 -256
  220. package/src/types.ts +0 -267
  221. package/src/utils/bytes.ts +0 -75
  222. package/src/utils/compression.ts +0 -187
  223. package/src/utils/crypto.ts +0 -146
  224. package/src/utils/id.ts +0 -34
  225. package/src/utils/index.ts +0 -9
  226. package/src/utils/internal-runtime.ts +0 -39
  227. package/src/utils/object.ts +0 -3
  228. package/src/utils/realtime-change-index.ts +0 -44
  229. package/src/utils/realtime-connection-registry.ts +0 -286
  230. package/src/utils/table-registry.ts +0 -67
  231. package/src/utils/url.ts +0 -29
@@ -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
- }
@@ -1,3 +0,0 @@
1
- export function isRecord(value: unknown): value is Record<string, unknown> {
2
- return typeof value === 'object' && value !== null && !Array.isArray(value);
3
- }
@@ -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
- }