@laboverwire/stitch 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +8 -5
  3. package/dist/index.d.ts +1 -8
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +0 -7
  6. package/dist/index.js.map +1 -1
  7. package/dist/react/provider.d.ts +2 -2
  8. package/dist/react/provider.d.ts.map +1 -1
  9. package/dist/store.d.ts +2 -2
  10. package/dist/store.d.ts.map +1 -1
  11. package/dist/store.js +190 -724
  12. package/dist/store.js.map +1 -1
  13. package/dist/types.d.ts +23 -161
  14. package/dist/types.d.ts.map +1 -1
  15. package/dist/types.js +1 -6
  16. package/dist/types.js.map +1 -1
  17. package/package.json +16 -7
  18. package/dist/internal-utils.d.ts +0 -4
  19. package/dist/internal-utils.d.ts.map +0 -1
  20. package/dist/internal-utils.js +0 -20
  21. package/dist/internal-utils.js.map +0 -1
  22. package/dist/internal-wasm-error.d.ts +0 -9
  23. package/dist/internal-wasm-error.d.ts.map +0 -1
  24. package/dist/internal-wasm-error.js +0 -48
  25. package/dist/internal-wasm-error.js.map +0 -1
  26. package/dist/memory-store.d.ts +0 -3
  27. package/dist/memory-store.d.ts.map +0 -1
  28. package/dist/memory-store.js +0 -572
  29. package/dist/memory-store.js.map +0 -1
  30. package/dist/offline-queue.d.ts +0 -6
  31. package/dist/offline-queue.d.ts.map +0 -1
  32. package/dist/offline-queue.js +0 -363
  33. package/dist/offline-queue.js.map +0 -1
  34. package/dist/persistence-layer.d.ts +0 -7
  35. package/dist/persistence-layer.d.ts.map +0 -1
  36. package/dist/persistence-layer.js +0 -409
  37. package/dist/persistence-layer.js.map +0 -1
  38. package/dist/remote-sync-layer.d.ts +0 -8
  39. package/dist/remote-sync-layer.d.ts.map +0 -1
  40. package/dist/remote-sync-layer.js +0 -439
  41. package/dist/remote-sync-layer.js.map +0 -1
  42. package/dist/sync-engine.d.ts +0 -3
  43. package/dist/sync-engine.d.ts.map +0 -1
  44. package/dist/sync-engine.js +0 -643
  45. package/dist/sync-engine.js.map +0 -1
@@ -1,439 +0,0 @@
1
- import { createSyncEngine } from "./sync-engine.js";
2
- import { stripNulls } from "./internal-utils.js";
3
- class RemoteSyncLayerImpl {
4
- sync = null;
5
- _connectionStatus = 'offline';
6
- statusListeners = new Set();
7
- sessionInvalidHandler = null;
8
- reconnectValidator = null;
9
- authenticatedUser = null;
10
- config;
11
- onRemoteMutation = null;
12
- onConnectionChange = null;
13
- onConnected = null;
14
- initialSyncDone = false;
15
- constructor(config) {
16
- this.config = config;
17
- }
18
- get connectionStatus() {
19
- return this._connectionStatus;
20
- }
21
- get isReconnecting() {
22
- return this.sync?.isReconnecting ?? false;
23
- }
24
- setMutationCallback(cb) {
25
- this.onRemoteMutation = cb;
26
- }
27
- setConnectionChangeCallback(cb) {
28
- this.onConnectionChange = cb;
29
- }
30
- setOnConnectedCallback(cb) {
31
- this.onConnected = cb;
32
- }
33
- async connect(serverUrl, getTicket) {
34
- const clientId = this.getOrCreateClientId();
35
- const syncEngine = createSyncEngine(clientId, this.config);
36
- this.sync = syncEngine;
37
- syncEngine.setMutationHandler((_scopeId, mutation) => {
38
- this.onRemoteMutation?.(mutation);
39
- });
40
- syncEngine.setSessionInvalidHandler(() => {
41
- this.handleSessionInvalid();
42
- });
43
- syncEngine.setConnectionStatusHandler((status) => {
44
- this.setConnectionStatus(status);
45
- this.onConnectionChange?.(status);
46
- if (status === 'connected') {
47
- this.onConnected?.();
48
- this.revalidateSessionOnReconnect();
49
- }
50
- });
51
- try {
52
- const wasmModule = await import('mqtt5-wasm');
53
- await syncEngine.connect(serverUrl, wasmModule, getTicket);
54
- }
55
- catch (err) {
56
- console.warn('[RemoteSyncLayer] Failed to connect, reconnecting in background:', err);
57
- this.initialSyncDone = true;
58
- }
59
- }
60
- disconnect() {
61
- this.sync?.disconnect();
62
- this.setConnectionStatus('offline');
63
- }
64
- async reconnect(serverUrl, getTicket) {
65
- if (!this.sync)
66
- return;
67
- if (this.sync.isReconnecting)
68
- return;
69
- try {
70
- await this.sync.reconnect(serverUrl, getTicket);
71
- }
72
- catch (err) {
73
- console.warn('[RemoteSyncLayer] Reconnect failed, working offline:', err);
74
- this.setConnectionStatus('offline');
75
- }
76
- }
77
- subscribeToConnectionStatus(cb) {
78
- this.statusListeners.add(cb);
79
- cb(this._connectionStatus);
80
- return () => this.statusListeners.delete(cb);
81
- }
82
- setAuthenticatedUser(userId) {
83
- this.authenticatedUser = userId;
84
- }
85
- setSessionInvalidHandler(handler) {
86
- this.sessionInvalidHandler = handler;
87
- }
88
- setReconnectValidator(validator) {
89
- this.reconnectValidator = validator;
90
- }
91
- async syncCreate(entity, scopeId, data) {
92
- if (!this.sync)
93
- throw new Error('Not connected');
94
- const { rootEntity, childEntities } = this.config.scope;
95
- const isChild = childEntities.includes(entity);
96
- const isTopLevel = this.config.topLevelEntities?.some((t) => t.entity === entity);
97
- if (entity === rootEntity || isTopLevel) {
98
- await this.sync.createEntity(entity, null, data);
99
- }
100
- else if (isChild) {
101
- await this.sync.createEntity(entity, scopeId, data);
102
- }
103
- }
104
- async syncUpdate(entity, scopeId, id, data) {
105
- if (!this.sync)
106
- throw new Error('Not connected');
107
- const { rootEntity, childEntities } = this.config.scope;
108
- const isChild = childEntities.includes(entity);
109
- if (entity === rootEntity) {
110
- await this.sync.updateEntity(entity, null, id, data);
111
- }
112
- else if (isChild) {
113
- await this.sync.updateEntity(entity, scopeId, id, data);
114
- }
115
- else {
116
- await this.sync.updateEntity(entity, null, id, data);
117
- }
118
- }
119
- async syncDelete(entity, scopeId, id) {
120
- if (!this.sync)
121
- throw new Error('Not connected');
122
- const { rootEntity, childEntities } = this.config.scope;
123
- const isChild = childEntities.includes(entity);
124
- if (entity === rootEntity) {
125
- await this.sync.deleteEntity(entity, null, id);
126
- }
127
- else if (isChild) {
128
- await this.sync.deleteEntity(entity, scopeId, id);
129
- }
130
- else {
131
- await this.sync.deleteEntity(entity, null, id);
132
- }
133
- }
134
- async openScope(scopeId) {
135
- if (!this.sync)
136
- throw new Error('Not connected');
137
- return this.sync.openScope(scopeId);
138
- }
139
- async closeScope(scopeId) {
140
- if (this.sync) {
141
- await this.sync.closeScope(scopeId);
142
- }
143
- }
144
- async fetchList(entity, scopeId, sort) {
145
- if (!this.sync)
146
- return null;
147
- return this.sync.fetchList(entity, scopeId, sort);
148
- }
149
- async syncRootEntityList(localAccessor, queue) {
150
- if (!this.sync)
151
- return;
152
- const { rootEntity, childEntities } = this.config.scope;
153
- const userScopeField = this.config.userScopeField;
154
- const versionField = this.config.versionField ?? 'version';
155
- this.initialSyncDone = false;
156
- const allEntities = await this.sync.fetchList(rootEntity);
157
- if (allEntities === null) {
158
- console.warn('[RemoteSyncLayer] fetchList returned null — server unreachable?');
159
- this.initialSyncDone = true;
160
- return;
161
- }
162
- const currentUser = this.authenticatedUser;
163
- const serverEntities = currentUser && userScopeField
164
- ? allEntities.filter((d) => d[userScopeField] === currentUser)
165
- : allEntities;
166
- const localEntities = await localAccessor.list(rootEntity, {});
167
- for (const local of localEntities) {
168
- const id = local.id;
169
- if (!id)
170
- continue;
171
- if (!currentUser)
172
- continue;
173
- const inServer = serverEntities.some((d) => d.id === id);
174
- if (inServer)
175
- continue;
176
- if (queue) {
177
- const hasPendingInsert = await queue.hasPendingInsert(rootEntity, id);
178
- if (hasPendingInsert) {
179
- console.log('[RemoteSyncLayer] keeping', id, '— has pending insert');
180
- continue;
181
- }
182
- }
183
- await localAccessor.delete(rootEntity, id);
184
- }
185
- for (const entity of serverEntities) {
186
- const id = entity.id;
187
- if (!id)
188
- continue;
189
- if (queue) {
190
- const pending = await queue.getPendingForScope(id);
191
- const hasPendingDelete = pending.some((p) => p.op === 'delete' && p.entity === rootEntity);
192
- if (hasPendingDelete)
193
- continue;
194
- }
195
- if (entity[versionField] != null && typeof entity[versionField] !== 'number') {
196
- entity[versionField] = Number(entity[versionField]) || 1;
197
- }
198
- try {
199
- await localAccessor.read(rootEntity, id);
200
- await localAccessor.update(rootEntity, id, entity);
201
- }
202
- catch {
203
- await localAccessor.create(rootEntity, entity);
204
- }
205
- const childResults = await Promise.all(childEntities.map((childEntity) => this.sync.fetchList(childEntity, id)));
206
- for (let i = 0; i < childEntities.length; i++) {
207
- const records = childResults[i];
208
- if (records === null)
209
- continue;
210
- await this.reconcileChildren(id, childEntities[i], records, localAccessor, queue);
211
- }
212
- }
213
- if (this.config.topLevelEntities) {
214
- for (const topLevel of this.config.topLevelEntities) {
215
- await this.reconcileTopLevelEntity(topLevel.entity, localAccessor);
216
- }
217
- }
218
- this.initialSyncDone = true;
219
- }
220
- get isInitialSyncDone() {
221
- return this.initialSyncDone;
222
- }
223
- async reconcileChildren(scopeId, entity, serverRecords, localAccessor, queue) {
224
- const { scopeField } = this.config.scope;
225
- const localRecords = await localAccessor.list(entity, {
226
- filters: [{ field: scopeField, op: 'eq', value: scopeId }],
227
- });
228
- let pendingRecords = [];
229
- if (queue) {
230
- const pending = await queue.getPendingForScope(scopeId);
231
- pendingRecords = pending
232
- .filter((p) => p.entity === entity)
233
- .map((p) => ({
234
- entityId: p.id,
235
- op: p.op,
236
- data: p.data,
237
- }));
238
- }
239
- const serverMap = new Map(serverRecords.map((r) => [r.id, r]));
240
- const localMap = new Map(localRecords.map((r) => [r.id, r]));
241
- const pendingByEntityId = new Map();
242
- for (const p of pendingRecords) {
243
- const eid = p.entityId;
244
- const arr = pendingByEntityId.get(eid) ?? [];
245
- arr.push(p);
246
- pendingByEntityId.set(eid, arr);
247
- }
248
- const hasPendingDelete = new Set();
249
- for (const p of pendingRecords) {
250
- if (p.op === 'delete')
251
- hasPendingDelete.add(p.entityId);
252
- }
253
- for (const record of serverRecords) {
254
- const id = record.id;
255
- if (!id)
256
- continue;
257
- if (hasPendingDelete.has(id))
258
- continue;
259
- const pendingOps = pendingByEntityId.get(id);
260
- if (pendingOps && pendingOps.some((p) => p.op === 'update'))
261
- continue;
262
- const cleaned = stripNulls(record);
263
- if (!cleaned[scopeField]) {
264
- cleaned[scopeField] = scopeId;
265
- }
266
- if (localMap.has(id)) {
267
- await localAccessor.update(entity, id, cleaned);
268
- }
269
- else {
270
- await localAccessor.create(entity, cleaned);
271
- }
272
- }
273
- for (const p of pendingRecords) {
274
- const eid = p.entityId;
275
- const op = p.op;
276
- if (op === 'delete')
277
- continue;
278
- if (serverMap.has(eid))
279
- continue;
280
- if (localMap.has(eid))
281
- continue;
282
- if (p.data) {
283
- try {
284
- await localAccessor.create(entity, p.data);
285
- }
286
- catch {
287
- // already exists from a race
288
- }
289
- }
290
- }
291
- for (const [id] of localMap) {
292
- if (serverMap.has(id))
293
- continue;
294
- if (pendingByEntityId.has(id))
295
- continue;
296
- try {
297
- await localAccessor.delete(entity, id);
298
- }
299
- catch {
300
- // already gone
301
- }
302
- }
303
- }
304
- async applyMutationToDb(mutation, localAccessor) {
305
- const { scopeField, rootEntity } = this.config.scope;
306
- const updatedAtField = this.config.updatedAtField ?? 'updatedAt';
307
- try {
308
- switch (mutation.op) {
309
- case 'insert':
310
- if (mutation.data) {
311
- const refScopeId = mutation.data[scopeField];
312
- if (refScopeId) {
313
- try {
314
- await localAccessor.read(rootEntity, refScopeId);
315
- }
316
- catch (readErr) {
317
- const msg = readErr instanceof Error ? readErr.message : String(readErr);
318
- if (/not found/i.test(msg))
319
- return;
320
- throw readErr;
321
- }
322
- }
323
- await localAccessor.create(mutation.entity, { ...mutation.data, id: mutation.id });
324
- }
325
- break;
326
- case 'update':
327
- if (mutation.data) {
328
- try {
329
- const existing = await localAccessor.read(mutation.entity, mutation.id);
330
- const remoteVersion = mutation.data._version;
331
- if (typeof remoteVersion === 'number') {
332
- const localVersion = typeof existing._version === 'number' ? existing._version : 0;
333
- if (remoteVersion < localVersion)
334
- return;
335
- }
336
- const remoteUpdatedAt = mutation.data[updatedAtField];
337
- const localUpdatedAt = existing[updatedAtField];
338
- if (typeof remoteUpdatedAt === 'number' &&
339
- typeof localUpdatedAt === 'number' &&
340
- remoteUpdatedAt < localUpdatedAt) {
341
- return;
342
- }
343
- }
344
- catch (readErr) {
345
- const msg = readErr instanceof Error ? readErr.message : String(readErr);
346
- if (/not found/i.test(msg))
347
- return;
348
- throw readErr;
349
- }
350
- await localAccessor.update(mutation.entity, mutation.id, mutation.data);
351
- }
352
- break;
353
- case 'delete':
354
- try {
355
- await localAccessor.delete(mutation.entity, mutation.id);
356
- }
357
- catch (delErr) {
358
- const msg = delErr instanceof Error ? delErr.message : String(delErr);
359
- if (!/not found/i.test(msg))
360
- throw delErr;
361
- }
362
- break;
363
- }
364
- }
365
- catch (err) {
366
- console.error('[RemoteSyncLayer] Failed to apply remote mutation:', err);
367
- }
368
- }
369
- async request(topic, payload) {
370
- if (!this.sync)
371
- throw new Error('Not connected');
372
- return this.sync.request(topic, payload);
373
- }
374
- resetForLogout() {
375
- this.disconnect();
376
- this.sync = null;
377
- this.authenticatedUser = null;
378
- this.sessionInvalidHandler = null;
379
- this.reconnectValidator = null;
380
- this.onConnected = null;
381
- this.initialSyncDone = false;
382
- this.setConnectionStatus('offline');
383
- sessionStorage.removeItem('stitch_client_id');
384
- }
385
- async reconcileTopLevelEntity(entity, localAccessor) {
386
- if (!this.sync)
387
- return;
388
- const serverRecords = await this.sync.fetchList(entity);
389
- if (serverRecords === null)
390
- return;
391
- const serverMap = new Map();
392
- for (const r of serverRecords) {
393
- serverMap.set(r.id, r);
394
- }
395
- const localRecords = await localAccessor.list(entity, {});
396
- for (const local of localRecords) {
397
- const id = local.id;
398
- if (serverMap.has(id))
399
- continue;
400
- await localAccessor.delete(entity, id);
401
- }
402
- for (const serverRecord of serverRecords) {
403
- const id = serverRecord.id;
404
- try {
405
- await localAccessor.read(entity, id);
406
- await localAccessor.update(entity, id, serverRecord);
407
- }
408
- catch {
409
- await localAccessor.create(entity, serverRecord);
410
- }
411
- }
412
- }
413
- setConnectionStatus(status) {
414
- this._connectionStatus = status;
415
- for (const listener of this.statusListeners) {
416
- listener(status);
417
- }
418
- }
419
- getOrCreateClientId() {
420
- const key = 'stitch_client_id';
421
- let id = sessionStorage.getItem(key);
422
- if (!id) {
423
- id = crypto.randomUUID();
424
- sessionStorage.setItem(key, id);
425
- }
426
- return id;
427
- }
428
- revalidateSessionOnReconnect() {
429
- this.reconnectValidator?.().catch(() => { });
430
- }
431
- handleSessionInvalid() {
432
- this.disconnect();
433
- this.sessionInvalidHandler?.();
434
- }
435
- }
436
- export function createRemoteSyncLayer(config) {
437
- return new RemoteSyncLayerImpl(config);
438
- }
439
- //# sourceMappingURL=remote-sync-layer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"remote-sync-layer.js","sourceRoot":"","sources":["../src/remote-sync-layer.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n StoreConfig,\n SyncMutation,\n ScopeState,\n ConnectionStatus,\n SortField,\n SyncEngine,\n RemoteSyncLayer,\n LocalAccessor,\n OfflineQueue,\n} from './types.ts';\nimport { createSyncEngine } from './sync-engine.ts';\nimport { stripNulls } from './internal-utils.ts';\n\ntype MutationCallback = (mutation: SyncMutation) => void;\ntype ConnectionCallback = (status: ConnectionStatus) => void;\n\nclass RemoteSyncLayerImpl implements RemoteSyncLayer {\n private sync: SyncEngine | null = null;\n private _connectionStatus: ConnectionStatus = 'offline';\n private statusListeners: Set<(status: ConnectionStatus) => void> = new Set();\n private sessionInvalidHandler: (() => void) | null = null;\n private reconnectValidator: (() => Promise<void>) | null = null;\n private authenticatedUser: string | null = null;\n private readonly config: StoreConfig;\n private onRemoteMutation: MutationCallback | null = null;\n private onConnectionChange: ConnectionCallback | null = null;\n private onConnected: (() => void) | null = null;\n private initialSyncDone = false;\n\n constructor(config: StoreConfig) {\n this.config = config;\n }\n\n get connectionStatus(): ConnectionStatus {\n return this._connectionStatus;\n }\n\n get isReconnecting(): boolean {\n return this.sync?.isReconnecting ?? false;\n }\n\n setMutationCallback(cb: MutationCallback): void {\n this.onRemoteMutation = cb;\n }\n\n setConnectionChangeCallback(cb: ConnectionCallback): void {\n this.onConnectionChange = cb;\n }\n\n setOnConnectedCallback(cb: () => void): void {\n this.onConnected = cb;\n }\n\n async connect(serverUrl: string, getTicket?: () => Promise<string>): Promise<void> {\n const clientId = this.getOrCreateClientId();\n const syncEngine = createSyncEngine(clientId, this.config);\n this.sync = syncEngine;\n\n syncEngine.setMutationHandler((_scopeId, mutation) => {\n this.onRemoteMutation?.(mutation);\n });\n\n syncEngine.setSessionInvalidHandler(() => {\n this.handleSessionInvalid();\n });\n\n syncEngine.setConnectionStatusHandler((status) => {\n this.setConnectionStatus(status);\n this.onConnectionChange?.(status);\n if (status === 'connected') {\n this.onConnected?.();\n this.revalidateSessionOnReconnect();\n }\n });\n\n try {\n const wasmModule = await import('mqtt5-wasm');\n await syncEngine.connect(serverUrl, wasmModule, getTicket);\n } catch (err) {\n console.warn('[RemoteSyncLayer] Failed to connect, reconnecting in background:', err);\n this.initialSyncDone = true;\n }\n }\n\n disconnect(): void {\n this.sync?.disconnect();\n this.setConnectionStatus('offline');\n }\n\n async reconnect(serverUrl: string, getTicket?: () => Promise<string>): Promise<void> {\n if (!this.sync) return;\n if (this.sync.isReconnecting) return;\n\n try {\n await this.sync.reconnect(serverUrl, getTicket);\n } catch (err) {\n console.warn('[RemoteSyncLayer] Reconnect failed, working offline:', err);\n this.setConnectionStatus('offline');\n }\n }\n\n subscribeToConnectionStatus(cb: (s: ConnectionStatus) => void): () => void {\n this.statusListeners.add(cb);\n cb(this._connectionStatus);\n return () => this.statusListeners.delete(cb);\n }\n\n setAuthenticatedUser(userId: string): void {\n this.authenticatedUser = userId;\n }\n\n setSessionInvalidHandler(handler: () => void): void {\n this.sessionInvalidHandler = handler;\n }\n\n setReconnectValidator(validator: () => Promise<void>): void {\n this.reconnectValidator = validator;\n }\n\n async syncCreate(entity: string, scopeId: string, data: Record<string, unknown>): Promise<void> {\n if (!this.sync) throw new Error('Not connected');\n const { rootEntity, childEntities } = this.config.scope;\n const isChild = childEntities.includes(entity);\n const isTopLevel = this.config.topLevelEntities?.some((t) => t.entity === entity);\n\n if (entity === rootEntity || isTopLevel) {\n await this.sync.createEntity(entity, null, data);\n } else if (isChild) {\n await this.sync.createEntity(entity, scopeId, data);\n }\n }\n\n async syncUpdate(\n entity: string,\n scopeId: string,\n id: string,\n data: Record<string, unknown>\n ): Promise<void> {\n if (!this.sync) throw new Error('Not connected');\n const { rootEntity, childEntities } = this.config.scope;\n const isChild = childEntities.includes(entity);\n\n if (entity === rootEntity) {\n await this.sync.updateEntity(entity, null, id, data);\n } else if (isChild) {\n await this.sync.updateEntity(entity, scopeId, id, data);\n } else {\n await this.sync.updateEntity(entity, null, id, data);\n }\n }\n\n async syncDelete(entity: string, scopeId: string, id: string): Promise<void> {\n if (!this.sync) throw new Error('Not connected');\n const { rootEntity, childEntities } = this.config.scope;\n const isChild = childEntities.includes(entity);\n\n if (entity === rootEntity) {\n await this.sync.deleteEntity(entity, null, id);\n } else if (isChild) {\n await this.sync.deleteEntity(entity, scopeId, id);\n } else {\n await this.sync.deleteEntity(entity, null, id);\n }\n }\n\n async openScope(scopeId: string): Promise<ScopeState> {\n if (!this.sync) throw new Error('Not connected');\n return this.sync.openScope(scopeId);\n }\n\n async closeScope(scopeId: string): Promise<void> {\n if (this.sync) {\n await this.sync.closeScope(scopeId);\n }\n }\n\n async fetchList(\n entity: string,\n scopeId?: string,\n sort?: SortField[]\n ): Promise<Record<string, unknown>[] | null> {\n if (!this.sync) return null;\n return this.sync.fetchList(entity, scopeId, sort);\n }\n\n async syncRootEntityList(\n localAccessor: LocalAccessor,\n queue: OfflineQueue | null\n ): Promise<void> {\n if (!this.sync) return;\n\n const { rootEntity, childEntities } = this.config.scope;\n const userScopeField = this.config.userScopeField;\n const versionField = this.config.versionField ?? 'version';\n\n this.initialSyncDone = false;\n\n const allEntities = await this.sync.fetchList(rootEntity);\n if (allEntities === null) {\n console.warn('[RemoteSyncLayer] fetchList returned null — server unreachable?');\n this.initialSyncDone = true;\n return;\n }\n\n const currentUser = this.authenticatedUser;\n const serverEntities =\n currentUser && userScopeField\n ? allEntities.filter((d: Record<string, unknown>) => d[userScopeField] === currentUser)\n : allEntities;\n\n const localEntities = await localAccessor.list(rootEntity, {});\n for (const local of localEntities) {\n const id = local.id as string;\n if (!id) continue;\n if (!currentUser) continue;\n\n const inServer = serverEntities.some((d: Record<string, unknown>) => (d.id as string) === id);\n if (inServer) continue;\n\n if (queue) {\n const hasPendingInsert = await queue.hasPendingInsert(rootEntity, id);\n if (hasPendingInsert) {\n console.log('[RemoteSyncLayer] keeping', id, '— has pending insert');\n continue;\n }\n }\n\n await localAccessor.delete(rootEntity, id);\n }\n\n for (const entity of serverEntities) {\n const id = entity.id as string;\n if (!id) continue;\n\n if (queue) {\n const pending = await queue.getPendingForScope(id);\n const hasPendingDelete = pending.some((p) => p.op === 'delete' && p.entity === rootEntity);\n if (hasPendingDelete) continue;\n }\n\n if (entity[versionField] != null && typeof entity[versionField] !== 'number') {\n entity[versionField] = Number(entity[versionField]) || 1;\n }\n\n try {\n await localAccessor.read(rootEntity, id);\n await localAccessor.update(rootEntity, id, entity);\n } catch {\n await localAccessor.create(rootEntity, entity);\n }\n\n const childResults = await Promise.all(\n childEntities.map((childEntity) => this.sync!.fetchList(childEntity, id))\n );\n\n for (let i = 0; i < childEntities.length; i++) {\n const records = childResults[i];\n if (records === null) continue;\n await this.reconcileChildren(id, childEntities[i], records, localAccessor, queue);\n }\n }\n\n if (this.config.topLevelEntities) {\n for (const topLevel of this.config.topLevelEntities) {\n await this.reconcileTopLevelEntity(topLevel.entity, localAccessor);\n }\n }\n\n this.initialSyncDone = true;\n }\n\n get isInitialSyncDone(): boolean {\n return this.initialSyncDone;\n }\n\n async reconcileChildren(\n scopeId: string,\n entity: string,\n serverRecords: Record<string, unknown>[],\n localAccessor: LocalAccessor,\n queue: OfflineQueue | null\n ): Promise<void> {\n const { scopeField } = this.config.scope;\n\n const localRecords = await localAccessor.list(entity, {\n filters: [{ field: scopeField, op: 'eq', value: scopeId }],\n });\n\n let pendingRecords: Array<Record<string, unknown>> = [];\n if (queue) {\n const pending = await queue.getPendingForScope(scopeId);\n pendingRecords = pending\n .filter((p) => p.entity === entity)\n .map((p) => ({\n entityId: p.id,\n op: p.op,\n data: p.data,\n }));\n }\n\n const serverMap = new Map(serverRecords.map((r) => [r.id as string, r]));\n const localMap = new Map(localRecords.map((r) => [r.id as string, r]));\n\n const pendingByEntityId = new Map<string, Array<Record<string, unknown>>>();\n for (const p of pendingRecords) {\n const eid = p.entityId as string;\n const arr = pendingByEntityId.get(eid) ?? [];\n arr.push(p);\n pendingByEntityId.set(eid, arr);\n }\n\n const hasPendingDelete = new Set<string>();\n for (const p of pendingRecords) {\n if (p.op === 'delete') hasPendingDelete.add(p.entityId as string);\n }\n\n for (const record of serverRecords) {\n const id = record.id as string;\n if (!id) continue;\n if (hasPendingDelete.has(id)) continue;\n const pendingOps = pendingByEntityId.get(id);\n if (pendingOps && pendingOps.some((p) => p.op === 'update')) continue;\n const cleaned = stripNulls(record);\n if (!cleaned[scopeField]) {\n cleaned[scopeField] = scopeId;\n }\n if (localMap.has(id)) {\n await localAccessor.update(entity, id, cleaned);\n } else {\n await localAccessor.create(entity, cleaned);\n }\n }\n\n for (const p of pendingRecords) {\n const eid = p.entityId as string;\n const op = p.op as string;\n if (op === 'delete') continue;\n if (serverMap.has(eid)) continue;\n if (localMap.has(eid)) continue;\n if (p.data) {\n try {\n await localAccessor.create(entity, p.data as Record<string, unknown>);\n } catch {\n // already exists from a race\n }\n }\n }\n\n for (const [id] of localMap) {\n if (serverMap.has(id)) continue;\n if (pendingByEntityId.has(id)) continue;\n try {\n await localAccessor.delete(entity, id);\n } catch {\n // already gone\n }\n }\n }\n\n async applyMutationToDb(mutation: SyncMutation, localAccessor: LocalAccessor): Promise<void> {\n const { scopeField, rootEntity } = this.config.scope;\n const updatedAtField = this.config.updatedAtField ?? 'updatedAt';\n\n try {\n switch (mutation.op) {\n case 'insert':\n if (mutation.data) {\n const refScopeId = mutation.data[scopeField] as string | undefined;\n if (refScopeId) {\n try {\n await localAccessor.read(rootEntity, refScopeId);\n } catch (readErr) {\n const msg = readErr instanceof Error ? readErr.message : String(readErr);\n if (/not found/i.test(msg)) return;\n throw readErr;\n }\n }\n await localAccessor.create(mutation.entity, { ...mutation.data, id: mutation.id });\n }\n break;\n case 'update':\n if (mutation.data) {\n try {\n const existing = await localAccessor.read(mutation.entity, mutation.id);\n const remoteVersion = mutation.data._version;\n if (typeof remoteVersion === 'number') {\n const localVersion = typeof existing._version === 'number' ? existing._version : 0;\n if (remoteVersion < localVersion) return;\n }\n const remoteUpdatedAt = mutation.data[updatedAtField];\n const localUpdatedAt = existing[updatedAtField];\n if (\n typeof remoteUpdatedAt === 'number' &&\n typeof localUpdatedAt === 'number' &&\n remoteUpdatedAt < localUpdatedAt\n ) {\n return;\n }\n } catch (readErr) {\n const msg = readErr instanceof Error ? readErr.message : String(readErr);\n if (/not found/i.test(msg)) return;\n throw readErr;\n }\n await localAccessor.update(mutation.entity, mutation.id, mutation.data);\n }\n break;\n case 'delete':\n try {\n await localAccessor.delete(mutation.entity, mutation.id);\n } catch (delErr) {\n const msg = delErr instanceof Error ? delErr.message : String(delErr);\n if (!/not found/i.test(msg)) throw delErr;\n }\n break;\n }\n } catch (err) {\n console.error('[RemoteSyncLayer] Failed to apply remote mutation:', err);\n }\n }\n\n async request(topic: string, payload: unknown): Promise<Record<string, unknown>> {\n if (!this.sync) throw new Error('Not connected');\n return this.sync.request(topic, payload);\n }\n\n resetForLogout(): void {\n this.disconnect();\n this.sync = null;\n this.authenticatedUser = null;\n this.sessionInvalidHandler = null;\n this.reconnectValidator = null;\n this.onConnected = null;\n this.initialSyncDone = false;\n this.setConnectionStatus('offline');\n sessionStorage.removeItem('stitch_client_id');\n }\n\n private async reconcileTopLevelEntity(\n entity: string,\n localAccessor: LocalAccessor\n ): Promise<void> {\n if (!this.sync) return;\n\n const serverRecords = await this.sync.fetchList(entity);\n if (serverRecords === null) return;\n\n const serverMap = new Map<string, Record<string, unknown>>();\n for (const r of serverRecords) {\n serverMap.set(r.id as string, r);\n }\n\n const localRecords = await localAccessor.list(entity, {});\n for (const local of localRecords) {\n const id = local.id as string;\n if (serverMap.has(id)) continue;\n await localAccessor.delete(entity, id);\n }\n\n for (const serverRecord of serverRecords) {\n const id = serverRecord.id as string;\n try {\n await localAccessor.read(entity, id);\n await localAccessor.update(entity, id, serverRecord);\n } catch {\n await localAccessor.create(entity, serverRecord);\n }\n }\n }\n\n private setConnectionStatus(status: ConnectionStatus): void {\n this._connectionStatus = status;\n for (const listener of this.statusListeners) {\n listener(status);\n }\n }\n\n private getOrCreateClientId(): string {\n const key = 'stitch_client_id';\n let id = sessionStorage.getItem(key);\n if (!id) {\n id = crypto.randomUUID();\n sessionStorage.setItem(key, id);\n }\n return id;\n }\n\n private revalidateSessionOnReconnect(): void {\n this.reconnectValidator?.().catch(() => {});\n }\n\n private handleSessionInvalid(): void {\n this.disconnect();\n this.sessionInvalidHandler?.();\n }\n}\n\nexport function createRemoteSyncLayer(config: StoreConfig): RemoteSyncLayer & {\n setMutationCallback(cb: (mutation: SyncMutation) => void): void;\n setConnectionChangeCallback(cb: (status: ConnectionStatus) => void): void;\n setOnConnectedCallback(cb: () => void): void;\n readonly isInitialSyncDone: boolean;\n} {\n return new RemoteSyncLayerImpl(config);\n}\n"]}
@@ -1,3 +0,0 @@
1
- import type { StoreConfig, SyncEngine } from './types.ts';
2
- export declare function createSyncEngine(clientId: string, config: StoreConfig): SyncEngine;
3
- //# sourceMappingURL=sync-engine.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync-engine.d.ts","sourceRoot":"","sources":["../src/sync-engine.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,WAAW,EAKX,UAAU,EACX,MAAM,YAAY,CAAC;AAgxBpB,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,UAAU,CAElF"}