edmaxlabs-core 2.5.6 → 2.6.7
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 +337 -194
- package/dist/index.cjs +706 -338
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -54
- package/dist/index.d.ts +72 -54
- package/dist/index.mjs +706 -338
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
package/dist/index.mjs
CHANGED
|
@@ -359,20 +359,6 @@ var DocumentSnapshot = class _DocumentSnapshot {
|
|
|
359
359
|
}
|
|
360
360
|
};
|
|
361
361
|
|
|
362
|
-
// src/utils/documentNomalizer.ts
|
|
363
|
-
function normalizePayload(payload) {
|
|
364
|
-
const raw = payload?.document ?? payload?.data ?? payload;
|
|
365
|
-
if (!raw)
|
|
366
|
-
return null;
|
|
367
|
-
const doc = { ...raw };
|
|
368
|
-
doc.id = payload.id ?? payload._id;
|
|
369
|
-
delete doc._id;
|
|
370
|
-
return {
|
|
371
|
-
change: payload?.change ?? raw?.change ?? null,
|
|
372
|
-
data: doc
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
|
|
376
362
|
// src/database/DocumentRef.ts
|
|
377
363
|
function validateDocumentData(data, operation) {
|
|
378
364
|
if (data === null || data === void 0) {
|
|
@@ -398,40 +384,22 @@ var DocumentRef = class {
|
|
|
398
384
|
this.syncEngine = app.offline().syncEngine;
|
|
399
385
|
this.localStore = app.offline().localStore;
|
|
400
386
|
}
|
|
401
|
-
// ====================== GET ======================
|
|
402
387
|
async get() {
|
|
403
388
|
if (this.persistence) {
|
|
404
389
|
try {
|
|
405
390
|
const localSnap = await this.persistence.getDocSnapshot(this.collection, this.id);
|
|
391
|
+
if (typeof navigator === "undefined" || navigator.onLine) {
|
|
392
|
+
this.refreshFromRemote().catch(() => {
|
|
393
|
+
});
|
|
394
|
+
}
|
|
406
395
|
if (localSnap)
|
|
407
396
|
return localSnap;
|
|
408
397
|
} catch (error) {
|
|
409
398
|
console.error("[EdmaxLabs] Cache read error:", error);
|
|
410
399
|
}
|
|
411
400
|
}
|
|
412
|
-
|
|
413
|
-
const res = await new HttpsRequest({
|
|
414
|
-
method: "POST" /* POST */,
|
|
415
|
-
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
416
|
-
headers: {
|
|
417
|
-
authorization: this.app.getConfig().token,
|
|
418
|
-
"x-project": this.app.getConfig().project
|
|
419
|
-
},
|
|
420
|
-
body: {
|
|
421
|
-
collection: this.collection,
|
|
422
|
-
id: this.id,
|
|
423
|
-
single: true
|
|
424
|
-
}
|
|
425
|
-
}).sendRequest();
|
|
426
|
-
if (!res?.success || !res.document)
|
|
427
|
-
return null;
|
|
428
|
-
return DocumentSnapshot.fromMap(res.document);
|
|
429
|
-
} catch (error) {
|
|
430
|
-
console.error(`[DocumentRef] get(${this.collection}/${this.id}) failed:`, error);
|
|
431
|
-
return null;
|
|
432
|
-
}
|
|
401
|
+
return this.fetchRemoteSnapshot();
|
|
433
402
|
}
|
|
434
|
-
// ====================== UPDATE ======================
|
|
435
403
|
async update(data) {
|
|
436
404
|
if (this._isUpdating) {
|
|
437
405
|
console.warn(`[DocumentRef] update recursion blocked on ${this.collection}/${this.id}`);
|
|
@@ -477,15 +445,13 @@ var DocumentRef = class {
|
|
|
477
445
|
baseRevision: old.revision
|
|
478
446
|
});
|
|
479
447
|
const snap = DocumentSnapshot.fromMap(updated.data);
|
|
480
|
-
this.
|
|
481
|
-
this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
|
|
448
|
+
this.app.offline().realtimeBridge?.publishLocalChange(this.collection, this.id, "update").catch(console.error);
|
|
482
449
|
this.syncEngine?.flush().catch(console.error);
|
|
483
450
|
return snap;
|
|
484
451
|
} finally {
|
|
485
452
|
this._isUpdating = false;
|
|
486
453
|
}
|
|
487
454
|
}
|
|
488
|
-
// ====================== SET ======================
|
|
489
455
|
async set(data) {
|
|
490
456
|
validateDocumentData(data, "DocumentRef.set");
|
|
491
457
|
if (!this.persistence) {
|
|
@@ -500,6 +466,8 @@ var DocumentRef = class {
|
|
|
500
466
|
}).sendRequest();
|
|
501
467
|
return res?.success ? DocumentSnapshot.fromMap({ ...data, id: this.id }) : null;
|
|
502
468
|
}
|
|
469
|
+
const existing = await this.persistence.getDoc(this.collection, this.id);
|
|
470
|
+
const mutationType = existing?.exists && !existing.deleted ? "update" : "insert";
|
|
503
471
|
const updated = await this.persistence.upsertDoc({
|
|
504
472
|
collection: this.collection,
|
|
505
473
|
id: this.id,
|
|
@@ -508,22 +476,27 @@ var DocumentRef = class {
|
|
|
508
476
|
deleted: false,
|
|
509
477
|
pending: 1,
|
|
510
478
|
localOnly: false,
|
|
511
|
-
status: "pending"
|
|
479
|
+
status: "pending",
|
|
480
|
+
revision: existing?.revision,
|
|
481
|
+
lastSyncedAt: existing?.lastSyncedAt
|
|
512
482
|
});
|
|
513
483
|
await this.persistence.enqueueMutation({
|
|
514
484
|
mutationId: this.persistence.createMutationId(),
|
|
515
485
|
collection: this.collection,
|
|
516
486
|
documentId: this.id,
|
|
517
|
-
type:
|
|
518
|
-
payload: data
|
|
487
|
+
type: mutationType,
|
|
488
|
+
payload: data,
|
|
489
|
+
baseRevision: existing?.revision
|
|
519
490
|
});
|
|
520
491
|
const snap = DocumentSnapshot.fromMap(updated.data);
|
|
521
|
-
this.
|
|
522
|
-
|
|
492
|
+
this.app.offline().realtimeBridge?.publishLocalChange(
|
|
493
|
+
this.collection,
|
|
494
|
+
this.id,
|
|
495
|
+
mutationType === "insert" ? "insert" : "update"
|
|
496
|
+
).catch(console.error);
|
|
523
497
|
this.syncEngine?.flush().catch(console.error);
|
|
524
498
|
return snap;
|
|
525
499
|
}
|
|
526
|
-
// ====================== DELETE ======================
|
|
527
500
|
async delete() {
|
|
528
501
|
if (this.persistence) {
|
|
529
502
|
await this.persistence.markDeleted(this.collection, this.id, 1);
|
|
@@ -534,8 +507,7 @@ var DocumentRef = class {
|
|
|
534
507
|
type: "delete",
|
|
535
508
|
payload: null
|
|
536
509
|
});
|
|
537
|
-
this.
|
|
538
|
-
this.localStore?.notifyCollectionChanged(this.collection, this.id, "delete");
|
|
510
|
+
this.app.offline().realtimeBridge?.publishLocalChange(this.collection, this.id, "delete").catch(console.error);
|
|
539
511
|
this.syncEngine?.flush().catch(console.error);
|
|
540
512
|
return true;
|
|
541
513
|
}
|
|
@@ -550,20 +522,135 @@ var DocumentRef = class {
|
|
|
550
522
|
}).sendRequest();
|
|
551
523
|
return !!res?.success;
|
|
552
524
|
}
|
|
553
|
-
// ====================== SNAPSHOT ======================
|
|
554
525
|
onSnapshot(callback) {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
526
|
+
const realtimeBridge = this.app.offline().realtimeBridge;
|
|
527
|
+
if (this.persistence && this.localStore && realtimeBridge) {
|
|
528
|
+
const localUnsub = this.localStore.subscribeToDocument(this.collection, this.id, callback);
|
|
529
|
+
const remoteUnsub = realtimeBridge.watchDocument(this.collection, this.id);
|
|
530
|
+
this.persistence.getDocSnapshot(this.collection, this.id).then((snapshot) => callback(snapshot, "initial")).catch(console.error);
|
|
531
|
+
return () => {
|
|
532
|
+
localUnsub();
|
|
533
|
+
remoteUnsub();
|
|
534
|
+
};
|
|
559
535
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
536
|
+
this.fetchRemoteSnapshot().then((snapshot) => callback(snapshot, "initial")).catch(console.error);
|
|
537
|
+
return this.app.rtdb().subscribeToDocumentRaw(this.collection, this.id, (payload, change) => {
|
|
538
|
+
if (change === "delete") {
|
|
539
|
+
callback(null, "delete");
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
callback(DocumentSnapshot.fromMap(payload), change);
|
|
563
543
|
});
|
|
564
544
|
}
|
|
545
|
+
async fetchRemoteSnapshot() {
|
|
546
|
+
try {
|
|
547
|
+
const res = await new HttpsRequest({
|
|
548
|
+
method: "POST" /* POST */,
|
|
549
|
+
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
550
|
+
headers: {
|
|
551
|
+
authorization: this.app.getConfig().token,
|
|
552
|
+
"x-project": this.app.getConfig().project
|
|
553
|
+
},
|
|
554
|
+
body: {
|
|
555
|
+
collection: this.collection,
|
|
556
|
+
id: this.id,
|
|
557
|
+
single: true
|
|
558
|
+
}
|
|
559
|
+
}).sendRequest();
|
|
560
|
+
if (!res?.success || !res.document)
|
|
561
|
+
return null;
|
|
562
|
+
const snapshot = DocumentSnapshot.fromMap(res.document);
|
|
563
|
+
if (this.persistence) {
|
|
564
|
+
await this.persistence.applyRemoteDoc(this.collection, snapshot.data);
|
|
565
|
+
}
|
|
566
|
+
return snapshot;
|
|
567
|
+
} catch (error) {
|
|
568
|
+
console.error(`[DocumentRef] get(${this.collection}/${this.id}) failed:`, error);
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async refreshFromRemote() {
|
|
573
|
+
const snapshot = await this.fetchRemoteSnapshot();
|
|
574
|
+
if (this.persistence && snapshot) {
|
|
575
|
+
this.app.offline().realtimeBridge?.publishLocalChange(this.collection, this.id, "update").catch(console.error);
|
|
576
|
+
}
|
|
577
|
+
return snapshot;
|
|
578
|
+
}
|
|
565
579
|
};
|
|
566
580
|
|
|
581
|
+
// src/database/RealtimeListeners.ts
|
|
582
|
+
function buildTargetKey(collection, filter = {}) {
|
|
583
|
+
return `${collection}:${stableStringify(filter)}`;
|
|
584
|
+
}
|
|
585
|
+
function stableStringify(value) {
|
|
586
|
+
if (value === null || typeof value !== "object") {
|
|
587
|
+
return JSON.stringify(value);
|
|
588
|
+
}
|
|
589
|
+
if (Array.isArray(value)) {
|
|
590
|
+
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
591
|
+
}
|
|
592
|
+
const entries = Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`);
|
|
593
|
+
return `{${entries.join(",")}}`;
|
|
594
|
+
}
|
|
595
|
+
function diffSnapshots(previous, next) {
|
|
596
|
+
const previousById = new Map(previous.map((snapshot, index) => [snapshot.id, { snapshot, index }]));
|
|
597
|
+
const nextById = new Map(next.map((snapshot, index) => [snapshot.id, { snapshot, index }]));
|
|
598
|
+
const changes = [];
|
|
599
|
+
next.forEach((snapshot, index) => {
|
|
600
|
+
const before = previousById.get(snapshot.id);
|
|
601
|
+
if (!before) {
|
|
602
|
+
changes.push({
|
|
603
|
+
type: "added",
|
|
604
|
+
doc: snapshot,
|
|
605
|
+
previousDoc: null,
|
|
606
|
+
oldIndex: -1,
|
|
607
|
+
newIndex: index
|
|
608
|
+
});
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (before.index !== index || !areSnapshotsEqual(before.snapshot, snapshot)) {
|
|
612
|
+
changes.push({
|
|
613
|
+
type: "modified",
|
|
614
|
+
doc: snapshot,
|
|
615
|
+
previousDoc: before.snapshot,
|
|
616
|
+
oldIndex: before.index,
|
|
617
|
+
newIndex: index
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
previous.forEach((snapshot, index) => {
|
|
622
|
+
if (nextById.has(snapshot.id))
|
|
623
|
+
return;
|
|
624
|
+
changes.push({
|
|
625
|
+
type: "removed",
|
|
626
|
+
doc: snapshot,
|
|
627
|
+
previousDoc: snapshot,
|
|
628
|
+
oldIndex: index,
|
|
629
|
+
newIndex: -1
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
return changes;
|
|
633
|
+
}
|
|
634
|
+
function applySnapshotChange(current, incoming, source) {
|
|
635
|
+
if (!incoming) {
|
|
636
|
+
return current;
|
|
637
|
+
}
|
|
638
|
+
if (source === "delete") {
|
|
639
|
+
return current.filter((snapshot) => snapshot.id !== incoming.id);
|
|
640
|
+
}
|
|
641
|
+
const next = [...current];
|
|
642
|
+
const index = next.findIndex((snapshot) => snapshot.id === incoming.id);
|
|
643
|
+
if (index === -1) {
|
|
644
|
+
next.push(incoming);
|
|
645
|
+
return next;
|
|
646
|
+
}
|
|
647
|
+
next[index] = incoming;
|
|
648
|
+
return next;
|
|
649
|
+
}
|
|
650
|
+
function areSnapshotsEqual(left, right) {
|
|
651
|
+
return stableStringify(left.data) === stableStringify(right.data);
|
|
652
|
+
}
|
|
653
|
+
|
|
567
654
|
// src/database/Query.ts
|
|
568
655
|
var Query = class {
|
|
569
656
|
constructor(app, collection) {
|
|
@@ -576,6 +663,84 @@ var Query = class {
|
|
|
576
663
|
this.filter.push(expression);
|
|
577
664
|
return this;
|
|
578
665
|
}
|
|
666
|
+
async get() {
|
|
667
|
+
const persistence = this.app.offline().persistence;
|
|
668
|
+
if (this.filter.length > 0 && persistence) {
|
|
669
|
+
const local = await this.getLocalFilteredSnapshots();
|
|
670
|
+
if (typeof navigator === "undefined" || !navigator.onLine) {
|
|
671
|
+
return local;
|
|
672
|
+
}
|
|
673
|
+
return this.refreshFromRemote();
|
|
674
|
+
}
|
|
675
|
+
if (persistence) {
|
|
676
|
+
const local = await persistence.getCollectionSnapshots(this.collection);
|
|
677
|
+
if (typeof navigator === "undefined" || navigator.onLine) {
|
|
678
|
+
this.refreshFromRemote().catch(() => {
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
return local;
|
|
682
|
+
}
|
|
683
|
+
return this.refreshFromRemote();
|
|
684
|
+
}
|
|
685
|
+
async update(data) {
|
|
686
|
+
const genfilter = this.buildFilter();
|
|
687
|
+
return new HttpsRequest({
|
|
688
|
+
method: "POST" /* POST */,
|
|
689
|
+
endpoint: `${this.app.getBaseUrl()}/db/update`,
|
|
690
|
+
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
691
|
+
body: {
|
|
692
|
+
collection: this.collection,
|
|
693
|
+
filter: genfilter,
|
|
694
|
+
data
|
|
695
|
+
}
|
|
696
|
+
}).sendRequest();
|
|
697
|
+
}
|
|
698
|
+
onSnapshot(callback) {
|
|
699
|
+
const genfilter = this.buildFilter();
|
|
700
|
+
const persistence = this.app.offline().persistence;
|
|
701
|
+
const realtimeBridge = this.app.offline().realtimeBridge;
|
|
702
|
+
if (persistence && this.localStore && realtimeBridge) {
|
|
703
|
+
const targetKey = buildTargetKey(this.collection, genfilter);
|
|
704
|
+
const localUnsub = this.localStore.subscribeToCollection(targetKey, callback);
|
|
705
|
+
const remoteUnsub = realtimeBridge.watchCollection(
|
|
706
|
+
this.collection,
|
|
707
|
+
genfilter,
|
|
708
|
+
() => this.getLocalFilteredSnapshots()
|
|
709
|
+
);
|
|
710
|
+
this.getLocalFilteredSnapshots().then((snapshots) => {
|
|
711
|
+
realtimeBridge.primeCollectionTarget(this.collection, genfilter, snapshots);
|
|
712
|
+
callback(snapshots, "initial");
|
|
713
|
+
}).catch(console.error);
|
|
714
|
+
return () => {
|
|
715
|
+
localUnsub();
|
|
716
|
+
remoteUnsub();
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
return this.subscribeOnlineSnapshot(callback);
|
|
720
|
+
}
|
|
721
|
+
onChildListener(callbacks) {
|
|
722
|
+
const genfilter = this.buildFilter();
|
|
723
|
+
const persistence = this.app.offline().persistence;
|
|
724
|
+
const realtimeBridge = this.app.offline().realtimeBridge;
|
|
725
|
+
if (persistence && this.localStore && realtimeBridge) {
|
|
726
|
+
const targetKey = buildTargetKey(this.collection, genfilter);
|
|
727
|
+
const localUnsub = this.localStore.subscribeToChildEvents(targetKey, callbacks);
|
|
728
|
+
const remoteUnsub = realtimeBridge.watchCollection(
|
|
729
|
+
this.collection,
|
|
730
|
+
genfilter,
|
|
731
|
+
() => this.getLocalFilteredSnapshots()
|
|
732
|
+
);
|
|
733
|
+
this.getLocalFilteredSnapshots().then((snapshots) => {
|
|
734
|
+
realtimeBridge.primeCollectionTarget(this.collection, genfilter, snapshots);
|
|
735
|
+
this.emitInitialChildEvents(snapshots, callbacks);
|
|
736
|
+
}).catch(console.error);
|
|
737
|
+
return () => {
|
|
738
|
+
localUnsub();
|
|
739
|
+
remoteUnsub();
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
return this.subscribeOnlineChildListener(callbacks);
|
|
743
|
+
}
|
|
579
744
|
buildFilter() {
|
|
580
745
|
const mongoFilter = {};
|
|
581
746
|
this.filter.forEach(({ key, op, value }) => {
|
|
@@ -646,9 +811,7 @@ var Query = class {
|
|
|
646
811
|
if (this.filter.length === 0)
|
|
647
812
|
return docs;
|
|
648
813
|
return docs.filter(
|
|
649
|
-
(snapshot) => this.filter.every(
|
|
650
|
-
(expression) => this.matchesFilter(snapshot.data, expression)
|
|
651
|
-
)
|
|
814
|
+
(snapshot) => this.filter.every((expression) => this.matchesFilter(snapshot.data, expression))
|
|
652
815
|
);
|
|
653
816
|
}
|
|
654
817
|
async getLocalFilteredSnapshots() {
|
|
@@ -658,25 +821,6 @@ var Query = class {
|
|
|
658
821
|
const docs = await persistence.getCollectionSnapshots(this.collection);
|
|
659
822
|
return this.filterDocuments(docs);
|
|
660
823
|
}
|
|
661
|
-
async get() {
|
|
662
|
-
const persistence = this.app.offline().persistence;
|
|
663
|
-
if (this.filter.length > 0 && persistence) {
|
|
664
|
-
const local = await this.getLocalFilteredSnapshots();
|
|
665
|
-
if (typeof navigator === "undefined" || !navigator.onLine) {
|
|
666
|
-
return local;
|
|
667
|
-
}
|
|
668
|
-
return this.refreshFromRemote();
|
|
669
|
-
}
|
|
670
|
-
if (persistence) {
|
|
671
|
-
const local = await persistence.getCollectionSnapshots(this.collection);
|
|
672
|
-
if (typeof navigator === "undefined" || navigator.onLine) {
|
|
673
|
-
this.refreshFromRemote().catch(() => {
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
return local;
|
|
677
|
-
}
|
|
678
|
-
return this.refreshFromRemote();
|
|
679
|
-
}
|
|
680
824
|
async refreshFromRemote() {
|
|
681
825
|
try {
|
|
682
826
|
const genfilter = this.buildFilter();
|
|
@@ -692,69 +836,90 @@ var Query = class {
|
|
|
692
836
|
if (!res?.success || !Array.isArray(res.documents)) {
|
|
693
837
|
return [];
|
|
694
838
|
}
|
|
695
|
-
|
|
839
|
+
const snapshots = res.documents.map((d) => {
|
|
696
840
|
delete d.token;
|
|
697
841
|
d.id = d.id ?? d._id;
|
|
698
842
|
delete d._id;
|
|
699
843
|
return DocumentSnapshot.fromMap(d);
|
|
700
844
|
});
|
|
845
|
+
const persistence = this.app.offline().persistence;
|
|
846
|
+
if (!persistence) {
|
|
847
|
+
return snapshots;
|
|
848
|
+
}
|
|
849
|
+
for (const snapshot of snapshots) {
|
|
850
|
+
await persistence.applyRemoteDoc(this.collection, snapshot.data);
|
|
851
|
+
}
|
|
852
|
+
return this.getLocalFilteredSnapshots();
|
|
701
853
|
} catch {
|
|
702
854
|
return [];
|
|
703
855
|
}
|
|
704
856
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
857
|
+
subscribeOnlineSnapshot(callback) {
|
|
858
|
+
let current = [];
|
|
859
|
+
this.refreshFromRemote().then((snapshots) => {
|
|
860
|
+
current = snapshots;
|
|
861
|
+
callback(current, "initial");
|
|
862
|
+
}).catch(console.error);
|
|
863
|
+
return this.app.rtdb().subscribeToCollectionRaw(
|
|
864
|
+
this.collection,
|
|
865
|
+
(payload, change) => {
|
|
866
|
+
const source = change;
|
|
867
|
+
const incoming = source === "delete" ? DocumentSnapshot.fromMap({ id: payload?.id ?? payload?._id }) : DocumentSnapshot.fromMap(payload);
|
|
868
|
+
const next = applySnapshotChange(current, incoming, source);
|
|
869
|
+
current = this.filterDocuments(next);
|
|
870
|
+
callback(current, source, incoming.id);
|
|
871
|
+
},
|
|
872
|
+
this.buildFilter()
|
|
873
|
+
);
|
|
719
874
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
875
|
+
subscribeOnlineChildListener(callbacks) {
|
|
876
|
+
let current = [];
|
|
877
|
+
this.refreshFromRemote().then((snapshots) => {
|
|
878
|
+
current = snapshots;
|
|
879
|
+
this.emitInitialChildEvents(snapshots, callbacks);
|
|
880
|
+
}).catch(console.error);
|
|
881
|
+
return this.app.rtdb().subscribeToCollectionRaw(
|
|
882
|
+
this.collection,
|
|
883
|
+
(payload, change) => {
|
|
884
|
+
const source = change;
|
|
885
|
+
const incoming = source === "delete" ? DocumentSnapshot.fromMap({ id: payload?.id ?? payload?._id }) : DocumentSnapshot.fromMap(payload);
|
|
886
|
+
const next = this.filterDocuments(applySnapshotChange(current, incoming, source));
|
|
887
|
+
const childChanges = diffSnapshots(current, next);
|
|
888
|
+
current = next;
|
|
889
|
+
childChanges.forEach((childChange) => {
|
|
890
|
+
const context = {
|
|
891
|
+
snapshots: next,
|
|
892
|
+
source,
|
|
893
|
+
changedDocId: childChange.doc.id,
|
|
894
|
+
fromCache: false,
|
|
895
|
+
oldIndex: childChange.oldIndex,
|
|
896
|
+
newIndex: childChange.newIndex,
|
|
897
|
+
previousDoc: childChange.previousDoc
|
|
898
|
+
};
|
|
899
|
+
if (childChange.type === "added") {
|
|
900
|
+
callbacks.onChildAdded?.(childChange.doc, context);
|
|
901
|
+
} else if (childChange.type === "modified") {
|
|
902
|
+
callbacks.onChildUpdated?.(childChange.doc, context);
|
|
903
|
+
} else if (childChange.type === "removed") {
|
|
904
|
+
callbacks.onChildRemoved?.(childChange.doc, context);
|
|
739
905
|
}
|
|
740
|
-
) ?? (() => {
|
|
741
906
|
});
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
907
|
+
},
|
|
908
|
+
this.buildFilter()
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
emitInitialChildEvents(snapshots, callbacks) {
|
|
912
|
+
diffSnapshots([], snapshots).forEach((change) => {
|
|
913
|
+
callbacks.onChildAdded?.(change.doc, {
|
|
914
|
+
snapshots,
|
|
915
|
+
source: "initial",
|
|
916
|
+
changedDocId: change.doc.id,
|
|
917
|
+
fromCache: true,
|
|
918
|
+
oldIndex: change.oldIndex,
|
|
919
|
+
newIndex: change.newIndex,
|
|
920
|
+
previousDoc: change.previousDoc
|
|
748
921
|
});
|
|
749
|
-
|
|
750
|
-
localUnsub();
|
|
751
|
-
remoteUnsub?.();
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
return this.app.rtdb().subscribeToCollectionRaw(this.collection, (payload, changes) => {
|
|
755
|
-
const res = normalizePayload(payload);
|
|
756
|
-
callback([DocumentSnapshot.fromMap(res?.data)], changes);
|
|
757
|
-
}, genfilter);
|
|
922
|
+
});
|
|
758
923
|
}
|
|
759
924
|
};
|
|
760
925
|
|
|
@@ -786,6 +951,89 @@ var CollectionRef = class {
|
|
|
786
951
|
}
|
|
787
952
|
return local;
|
|
788
953
|
}
|
|
954
|
+
return this.fetchRemoteSnapshots();
|
|
955
|
+
}
|
|
956
|
+
async add(data) {
|
|
957
|
+
if (this.persistence) {
|
|
958
|
+
const localId = this.persistence.createLocalId();
|
|
959
|
+
const docRecord = await this.persistence.upsertDoc({
|
|
960
|
+
collection: this.collection,
|
|
961
|
+
id: localId,
|
|
962
|
+
data: { ...data, id: localId },
|
|
963
|
+
exists: true,
|
|
964
|
+
deleted: false,
|
|
965
|
+
pending: 1,
|
|
966
|
+
localOnly: true,
|
|
967
|
+
status: "pending"
|
|
968
|
+
});
|
|
969
|
+
await this.persistence.enqueueMutation({
|
|
970
|
+
mutationId: this.persistence.createMutationId(),
|
|
971
|
+
collection: this.collection,
|
|
972
|
+
documentId: localId,
|
|
973
|
+
type: "insert",
|
|
974
|
+
payload: docRecord.data
|
|
975
|
+
});
|
|
976
|
+
const snap = DocumentSnapshot.fromMap(docRecord.data);
|
|
977
|
+
this.app.offline().realtimeBridge?.publishLocalChange(this.collection, localId, "insert").catch(console.error);
|
|
978
|
+
this.syncEngine?.flush().catch(console.error);
|
|
979
|
+
return snap;
|
|
980
|
+
}
|
|
981
|
+
const res = await new HttpsRequest({
|
|
982
|
+
method: "POST" /* POST */,
|
|
983
|
+
endpoint: `${this.app.getBaseUrl()}/db/create`,
|
|
984
|
+
headers: {
|
|
985
|
+
authorization: this.app.getConfig().token,
|
|
986
|
+
"x-project": this.app.getConfig().project
|
|
987
|
+
},
|
|
988
|
+
body: { collection: this.collection, data: { ...data, id: "" } }
|
|
989
|
+
}).sendRequest();
|
|
990
|
+
if (!res?.success || !res.document)
|
|
991
|
+
return null;
|
|
992
|
+
return DocumentSnapshot.fromMap(res.document);
|
|
993
|
+
}
|
|
994
|
+
onSnapshot(callback) {
|
|
995
|
+
const realtimeBridge = this.app.offline().realtimeBridge;
|
|
996
|
+
if (this.persistence && this.localStore && realtimeBridge) {
|
|
997
|
+
const targetKey = buildTargetKey(this.collection, {});
|
|
998
|
+
const localUnsub = this.localStore.subscribeToCollection(targetKey, callback);
|
|
999
|
+
const remoteUnsub = realtimeBridge.watchCollection(
|
|
1000
|
+
this.collection,
|
|
1001
|
+
{},
|
|
1002
|
+
() => this.persistence.getCollectionSnapshots(this.collection)
|
|
1003
|
+
);
|
|
1004
|
+
this.persistence.getCollectionSnapshots(this.collection).then((snapshots) => {
|
|
1005
|
+
realtimeBridge.primeCollectionTarget(this.collection, {}, snapshots);
|
|
1006
|
+
callback(snapshots, "insert");
|
|
1007
|
+
}).catch(console.error);
|
|
1008
|
+
return () => {
|
|
1009
|
+
localUnsub();
|
|
1010
|
+
remoteUnsub();
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
return this.subscribeOnlineSnapshot(callback);
|
|
1014
|
+
}
|
|
1015
|
+
onChildListener(callbacks) {
|
|
1016
|
+
const realtimeBridge = this.app.offline().realtimeBridge;
|
|
1017
|
+
if (this.persistence && this.localStore && realtimeBridge) {
|
|
1018
|
+
const targetKey = buildTargetKey(this.collection, {});
|
|
1019
|
+
const localUnsub = this.localStore.subscribeToChildEvents(targetKey, callbacks);
|
|
1020
|
+
const remoteUnsub = realtimeBridge.watchCollection(
|
|
1021
|
+
this.collection,
|
|
1022
|
+
{},
|
|
1023
|
+
() => this.persistence.getCollectionSnapshots(this.collection)
|
|
1024
|
+
);
|
|
1025
|
+
this.persistence.getCollectionSnapshots(this.collection).then((snapshots) => {
|
|
1026
|
+
realtimeBridge.primeCollectionTarget(this.collection, {}, snapshots);
|
|
1027
|
+
this.emitInitialChildEvents(snapshots, callbacks);
|
|
1028
|
+
}).catch(console.error);
|
|
1029
|
+
return () => {
|
|
1030
|
+
localUnsub();
|
|
1031
|
+
remoteUnsub();
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
return this.subscribeOnlineChildListener(callbacks);
|
|
1035
|
+
}
|
|
1036
|
+
async refreshFromRemote() {
|
|
789
1037
|
try {
|
|
790
1038
|
const res = await new HttpsRequest({
|
|
791
1039
|
method: "POST" /* POST */,
|
|
@@ -802,18 +1050,25 @@ var CollectionRef = class {
|
|
|
802
1050
|
if (!res?.success || !Array.isArray(res.documents)) {
|
|
803
1051
|
return [];
|
|
804
1052
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
delete
|
|
809
|
-
return
|
|
1053
|
+
const normalized = res.documents.map((raw) => {
|
|
1054
|
+
const doc = { ...raw };
|
|
1055
|
+
doc.id = doc.id ?? doc._id;
|
|
1056
|
+
delete doc._id;
|
|
1057
|
+
return doc;
|
|
810
1058
|
});
|
|
1059
|
+
if (this.persistence) {
|
|
1060
|
+
await this.persistence.reconcileCollectionFromRemote(this.collection, normalized);
|
|
1061
|
+
const snapshots = await this.persistence.getCollectionSnapshots(this.collection);
|
|
1062
|
+
this.app.offline().realtimeBridge?.primeCollectionTarget(this.collection, {}, snapshots);
|
|
1063
|
+
return snapshots;
|
|
1064
|
+
}
|
|
1065
|
+
return normalized.map((doc) => DocumentSnapshot.fromMap(doc));
|
|
811
1066
|
} catch (error) {
|
|
812
|
-
console.error("[EdmaxLabs]
|
|
1067
|
+
console.error("[EdmaxLabs] refreshFromRemote failed:", error);
|
|
813
1068
|
return [];
|
|
814
1069
|
}
|
|
815
1070
|
}
|
|
816
|
-
async
|
|
1071
|
+
async fetchRemoteSnapshots() {
|
|
817
1072
|
try {
|
|
818
1073
|
const res = await new HttpsRequest({
|
|
819
1074
|
method: "POST" /* POST */,
|
|
@@ -830,17 +1085,6 @@ var CollectionRef = class {
|
|
|
830
1085
|
if (!res?.success || !Array.isArray(res.documents)) {
|
|
831
1086
|
return [];
|
|
832
1087
|
}
|
|
833
|
-
for (const raw of res.documents) {
|
|
834
|
-
const doc = { ...raw };
|
|
835
|
-
doc.id = doc.id ?? doc._id;
|
|
836
|
-
delete doc._id;
|
|
837
|
-
if (this.persistence) {
|
|
838
|
-
await this.persistence.applyRemoteDoc(this.collection, doc);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
if (this.persistence) {
|
|
842
|
-
return await this.persistence.getCollectionSnapshots(this.collection);
|
|
843
|
-
}
|
|
844
1088
|
return res.documents.map((d) => {
|
|
845
1089
|
delete d.token;
|
|
846
1090
|
d.id = d.id ?? d._id;
|
|
@@ -848,59 +1092,68 @@ var CollectionRef = class {
|
|
|
848
1092
|
return DocumentSnapshot.fromMap(d);
|
|
849
1093
|
});
|
|
850
1094
|
} catch (error) {
|
|
851
|
-
console.error("[EdmaxLabs]
|
|
1095
|
+
console.error("[EdmaxLabs] Collection get failed:", error);
|
|
852
1096
|
return [];
|
|
853
1097
|
}
|
|
854
1098
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
});
|
|
868
|
-
await this.persistence.enqueueMutation({
|
|
869
|
-
mutationId: this.persistence.createMutationId(),
|
|
870
|
-
collection: this.collection,
|
|
871
|
-
documentId: localId,
|
|
872
|
-
type: "insert",
|
|
873
|
-
payload: docRecord.data
|
|
874
|
-
});
|
|
875
|
-
const snap = DocumentSnapshot.fromMap(docRecord.data);
|
|
876
|
-
this.localStore?.emitDocument(this.collection, localId, snap, "insert");
|
|
877
|
-
this.localStore?.notifyCollectionChanged(this.collection, localId, "insert");
|
|
878
|
-
this.syncEngine?.flush().catch(console.error);
|
|
879
|
-
return snap;
|
|
880
|
-
}
|
|
881
|
-
const res = await new HttpsRequest({
|
|
882
|
-
method: "POST" /* POST */,
|
|
883
|
-
endpoint: `${this.app.getBaseUrl()}/db/create`,
|
|
884
|
-
headers: {
|
|
885
|
-
authorization: this.app.getConfig().token,
|
|
886
|
-
"x-project": this.app.getConfig().project
|
|
887
|
-
},
|
|
888
|
-
body: { collection: this.collection, data: { ...data, id: "" } }
|
|
889
|
-
// server will generate id
|
|
890
|
-
}).sendRequest();
|
|
891
|
-
if (!res?.success || !res.document)
|
|
892
|
-
return null;
|
|
893
|
-
return DocumentSnapshot.fromMap(res.document);
|
|
1099
|
+
subscribeOnlineSnapshot(callback) {
|
|
1100
|
+
let current = [];
|
|
1101
|
+
this.fetchRemoteSnapshots().then((snapshots) => {
|
|
1102
|
+
current = snapshots;
|
|
1103
|
+
callback(current, "initial");
|
|
1104
|
+
}).catch(console.error);
|
|
1105
|
+
return this.app.rtdb().subscribeToCollectionRaw(this.collection, (payload, change) => {
|
|
1106
|
+
const source = change;
|
|
1107
|
+
const incoming = source === "delete" ? DocumentSnapshot.fromMap({ id: payload?.id ?? payload?._id }) : DocumentSnapshot.fromMap(payload);
|
|
1108
|
+
current = applySnapshotChange(current, incoming, source);
|
|
1109
|
+
callback(current, source, incoming.id);
|
|
1110
|
+
});
|
|
894
1111
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1112
|
+
subscribeOnlineChildListener(callbacks) {
|
|
1113
|
+
let current = [];
|
|
1114
|
+
this.fetchRemoteSnapshots().then((snapshots) => {
|
|
1115
|
+
current = snapshots;
|
|
1116
|
+
this.emitInitialChildEvents(snapshots, callbacks);
|
|
1117
|
+
}).catch(console.error);
|
|
1118
|
+
return this.app.rtdb().subscribeToCollectionRaw(this.collection, (payload, change) => {
|
|
1119
|
+
const source = change;
|
|
1120
|
+
const incoming = source === "delete" ? DocumentSnapshot.fromMap({ id: payload?.id ?? payload?._id }) : DocumentSnapshot.fromMap(payload);
|
|
1121
|
+
const next = applySnapshotChange(current, incoming, source);
|
|
1122
|
+
const childChanges = diffSnapshots(current, next);
|
|
1123
|
+
current = next;
|
|
1124
|
+
childChanges.forEach((childChange) => {
|
|
1125
|
+
const context = {
|
|
1126
|
+
snapshots: next,
|
|
1127
|
+
source,
|
|
1128
|
+
changedDocId: childChange.doc.id,
|
|
1129
|
+
fromCache: false,
|
|
1130
|
+
oldIndex: childChange.oldIndex,
|
|
1131
|
+
newIndex: childChange.newIndex,
|
|
1132
|
+
previousDoc: childChange.previousDoc
|
|
1133
|
+
};
|
|
1134
|
+
if (childChange.type === "added") {
|
|
1135
|
+
callbacks.onChildAdded?.(childChange.doc, context);
|
|
1136
|
+
} else if (childChange.type === "modified") {
|
|
1137
|
+
callbacks.onChildUpdated?.(childChange.doc, context);
|
|
1138
|
+
} else if (childChange.type === "removed") {
|
|
1139
|
+
callbacks.onChildRemoved?.(childChange.doc, context);
|
|
1140
|
+
}
|
|
899
1141
|
});
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
emitInitialChildEvents(snapshots, callbacks) {
|
|
1145
|
+
const childChanges = diffSnapshots([], snapshots);
|
|
1146
|
+
childChanges.forEach((change) => {
|
|
1147
|
+
const context = {
|
|
1148
|
+
snapshots,
|
|
1149
|
+
source: "initial",
|
|
1150
|
+
changedDocId: change.doc.id,
|
|
1151
|
+
fromCache: true,
|
|
1152
|
+
oldIndex: change.oldIndex,
|
|
1153
|
+
newIndex: change.newIndex,
|
|
1154
|
+
previousDoc: change.previousDoc
|
|
1155
|
+
};
|
|
1156
|
+
callbacks.onChildAdded?.(change.doc, context);
|
|
904
1157
|
});
|
|
905
1158
|
}
|
|
906
1159
|
};
|
|
@@ -950,6 +1203,7 @@ var Batch = class {
|
|
|
950
1203
|
const persistence = this.app.offline().persistence;
|
|
951
1204
|
const localStore = this.app.offline().localStore;
|
|
952
1205
|
const syncEngine = this.app.offline().syncEngine;
|
|
1206
|
+
const realtimeBridge = this.app.offline().realtimeBridge;
|
|
953
1207
|
if (persistence && localStore) {
|
|
954
1208
|
const results = [];
|
|
955
1209
|
for (const op of this.ops) {
|
|
@@ -978,8 +1232,7 @@ var Batch = class {
|
|
|
978
1232
|
payload: op.data
|
|
979
1233
|
});
|
|
980
1234
|
const snap = DocumentSnapshot.fromMap(upserted.data);
|
|
981
|
-
|
|
982
|
-
localStore.notifyCollectionChanged(op.collection, op.id, "insert");
|
|
1235
|
+
realtimeBridge?.publishLocalChange(op.collection, op.id, "insert").catch(console.error);
|
|
983
1236
|
results.push(snap);
|
|
984
1237
|
} else if (op.op === "delete") {
|
|
985
1238
|
const docRef = new DocumentRef(this.app, op.collection, op.id);
|
|
@@ -1079,16 +1332,18 @@ var Database = class {
|
|
|
1079
1332
|
// src/database/Realtime.ts
|
|
1080
1333
|
import { io } from "socket.io-client";
|
|
1081
1334
|
|
|
1082
|
-
// src/utils/
|
|
1083
|
-
function
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1335
|
+
// src/utils/documentNomalizer.ts
|
|
1336
|
+
function normalizePayload(payload) {
|
|
1337
|
+
const raw = payload?.document ?? payload?.data ?? payload;
|
|
1338
|
+
if (!raw)
|
|
1339
|
+
return null;
|
|
1340
|
+
const doc = { ...raw };
|
|
1341
|
+
doc.id = doc.id ?? raw?.id ?? raw?._id ?? payload?.id ?? payload?._id;
|
|
1342
|
+
delete doc._id;
|
|
1343
|
+
return {
|
|
1344
|
+
change: payload?.change ?? raw?.change ?? null,
|
|
1345
|
+
data: doc
|
|
1346
|
+
};
|
|
1092
1347
|
}
|
|
1093
1348
|
|
|
1094
1349
|
// src/database/Realtime.ts
|
|
@@ -1152,15 +1407,19 @@ var Realtime = class {
|
|
|
1152
1407
|
*/
|
|
1153
1408
|
subscribeToCollectionRaw(collection, callback, filter = {}) {
|
|
1154
1409
|
this.connect();
|
|
1155
|
-
const lid =
|
|
1410
|
+
const lid = collection;
|
|
1156
1411
|
const handlers = [];
|
|
1157
|
-
this.socket.emit("subscribe", { collection, filter
|
|
1412
|
+
this.socket.emit("subscribe", { collection, filter });
|
|
1158
1413
|
this.events.forEach((event) => {
|
|
1159
1414
|
const channel = `${lid}-${event}`;
|
|
1160
1415
|
const fn = (payload) => {
|
|
1161
1416
|
const normalized = normalizePayload(payload);
|
|
1162
|
-
if (!normalized)
|
|
1417
|
+
if (!normalized) {
|
|
1418
|
+
if (event === "delete") {
|
|
1419
|
+
callback(payload, "delete");
|
|
1420
|
+
}
|
|
1163
1421
|
return;
|
|
1422
|
+
}
|
|
1164
1423
|
callback(normalized.data, event);
|
|
1165
1424
|
};
|
|
1166
1425
|
this.socket.on(channel, fn);
|
|
@@ -1174,10 +1433,10 @@ var Realtime = class {
|
|
|
1174
1433
|
*/
|
|
1175
1434
|
subscribeToDocumentRaw(collection, id, callback) {
|
|
1176
1435
|
this.connect();
|
|
1177
|
-
const lid =
|
|
1436
|
+
const lid = collection;
|
|
1178
1437
|
const filter = { _id: id };
|
|
1179
1438
|
const handlers = [];
|
|
1180
|
-
this.socket.emit("subscribe", { collection, filter
|
|
1439
|
+
this.socket.emit("subscribe", { collection, filter });
|
|
1181
1440
|
this.events.forEach((event) => {
|
|
1182
1441
|
const channel = `${lid}-${event}`;
|
|
1183
1442
|
const fn = (payload) => {
|
|
@@ -1284,11 +1543,11 @@ var LocalStore = class {
|
|
|
1284
1543
|
constructor() {
|
|
1285
1544
|
this.documentListeners = /* @__PURE__ */ new Map();
|
|
1286
1545
|
this.collectionListeners = /* @__PURE__ */ new Map();
|
|
1546
|
+
this.childListeners = /* @__PURE__ */ new Map();
|
|
1287
1547
|
}
|
|
1288
1548
|
docKey(collection, id) {
|
|
1289
1549
|
return `${collection}:${id}`;
|
|
1290
1550
|
}
|
|
1291
|
-
// ===================== DOCUMENT LISTENERS =====================
|
|
1292
1551
|
subscribeToDocument(collection, id, callback) {
|
|
1293
1552
|
const key = this.docKey(collection, id);
|
|
1294
1553
|
if (!this.documentListeners.has(key)) {
|
|
@@ -1303,24 +1562,33 @@ var LocalStore = class {
|
|
|
1303
1562
|
}
|
|
1304
1563
|
};
|
|
1305
1564
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
this.collectionListeners.set(collection, /* @__PURE__ */ new Set());
|
|
1565
|
+
subscribeToCollection(targetKey, callback) {
|
|
1566
|
+
if (!this.collectionListeners.has(targetKey)) {
|
|
1567
|
+
this.collectionListeners.set(targetKey, /* @__PURE__ */ new Set());
|
|
1310
1568
|
}
|
|
1311
|
-
const listeners = this.collectionListeners.get(
|
|
1569
|
+
const listeners = this.collectionListeners.get(targetKey);
|
|
1312
1570
|
listeners.add(callback);
|
|
1313
1571
|
return () => {
|
|
1314
1572
|
listeners.delete(callback);
|
|
1315
1573
|
if (listeners.size === 0) {
|
|
1316
|
-
this.collectionListeners.delete(
|
|
1574
|
+
this.collectionListeners.delete(targetKey);
|
|
1575
|
+
this.childListeners.delete(targetKey);
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
subscribeToChildEvents(targetKey, callbacks) {
|
|
1580
|
+
if (!this.childListeners.has(targetKey)) {
|
|
1581
|
+
this.childListeners.set(targetKey, /* @__PURE__ */ new Set());
|
|
1582
|
+
}
|
|
1583
|
+
const listeners = this.childListeners.get(targetKey);
|
|
1584
|
+
listeners.add(callbacks);
|
|
1585
|
+
return () => {
|
|
1586
|
+
listeners.delete(callbacks);
|
|
1587
|
+
if (listeners.size === 0) {
|
|
1588
|
+
this.childListeners.delete(targetKey);
|
|
1317
1589
|
}
|
|
1318
1590
|
};
|
|
1319
1591
|
}
|
|
1320
|
-
// ===================== EMITTERS =====================
|
|
1321
|
-
/**
|
|
1322
|
-
* Notify all listeners for a specific document
|
|
1323
|
-
*/
|
|
1324
1592
|
emitDocument(collection, id, snapshot, change) {
|
|
1325
1593
|
const key = this.docKey(collection, id);
|
|
1326
1594
|
this.documentListeners.get(key)?.forEach((cb) => {
|
|
@@ -1331,64 +1599,66 @@ var LocalStore = class {
|
|
|
1331
1599
|
}
|
|
1332
1600
|
});
|
|
1333
1601
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
* This is the most important fix — collection listeners now receive the full list.
|
|
1337
|
-
*/
|
|
1338
|
-
emitCollection(collection, snapshots, change, changedDocId) {
|
|
1339
|
-
this.collectionListeners.get(collection)?.forEach((cb) => {
|
|
1602
|
+
emitCollection(targetKey, emission) {
|
|
1603
|
+
this.collectionListeners.get(targetKey)?.forEach((cb) => {
|
|
1340
1604
|
try {
|
|
1341
|
-
cb(snapshots,
|
|
1605
|
+
cb(emission.snapshots, emission.source, emission.changedDocId);
|
|
1342
1606
|
} catch (err) {
|
|
1343
|
-
console.error(`[EdmaxLabs] Error in collection listener for ${
|
|
1607
|
+
console.error(`[EdmaxLabs] Error in collection listener for ${targetKey}:`, err);
|
|
1344
1608
|
}
|
|
1345
1609
|
});
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
this.collectionListeners.get(collection)?.forEach((cb) => {
|
|
1354
|
-
try {
|
|
1355
|
-
cb([], change, changedDocId);
|
|
1356
|
-
} catch (err) {
|
|
1357
|
-
console.error(`[EdmaxLabs] Error in collection listener for ${collection}:`, err);
|
|
1358
|
-
}
|
|
1610
|
+
const childChanges = emission.childChanges ?? [];
|
|
1611
|
+
if (childChanges.length === 0)
|
|
1612
|
+
return;
|
|
1613
|
+
this.childListeners.get(targetKey)?.forEach((callbacks) => {
|
|
1614
|
+
childChanges.forEach((change) => {
|
|
1615
|
+
this.dispatchChildEvent(callbacks, change, emission);
|
|
1616
|
+
});
|
|
1359
1617
|
});
|
|
1360
1618
|
}
|
|
1361
|
-
// ===================== UTILITY =====================
|
|
1362
|
-
/**
|
|
1363
|
-
* Clear all listeners (useful for testing or when persistence is disabled)
|
|
1364
|
-
*/
|
|
1365
1619
|
clearAllListeners() {
|
|
1366
1620
|
this.documentListeners.clear();
|
|
1367
1621
|
this.collectionListeners.clear();
|
|
1622
|
+
this.childListeners.clear();
|
|
1368
1623
|
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Get current listener count (for debugging / dev tools)
|
|
1371
|
-
*/
|
|
1372
1624
|
get listenerCount() {
|
|
1373
1625
|
let docCount = 0;
|
|
1374
1626
|
this.documentListeners.forEach((set) => docCount += set.size);
|
|
1375
1627
|
let collCount = 0;
|
|
1376
1628
|
this.collectionListeners.forEach((set) => collCount += set.size);
|
|
1629
|
+
this.childListeners.forEach((set) => collCount += set.size);
|
|
1377
1630
|
return { documents: docCount, collections: collCount };
|
|
1378
1631
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
removeCollectionListeners(collection) {
|
|
1383
|
-
this.collectionListeners.delete(collection);
|
|
1632
|
+
removeCollectionListeners(targetKey) {
|
|
1633
|
+
this.collectionListeners.delete(targetKey);
|
|
1634
|
+
this.childListeners.delete(targetKey);
|
|
1384
1635
|
}
|
|
1385
|
-
/**
|
|
1386
|
-
* Remove all listeners for a specific document (useful for cleanup)
|
|
1387
|
-
*/
|
|
1388
1636
|
removeDocumentListeners(collection, id) {
|
|
1389
1637
|
const key = this.docKey(collection, id);
|
|
1390
1638
|
this.documentListeners.delete(key);
|
|
1391
1639
|
}
|
|
1640
|
+
dispatchChildEvent(callbacks, change, emission) {
|
|
1641
|
+
const context = {
|
|
1642
|
+
snapshots: emission.snapshots,
|
|
1643
|
+
source: emission.source,
|
|
1644
|
+
changedDocId: emission.changedDocId,
|
|
1645
|
+
fromCache: emission.fromCache ?? true,
|
|
1646
|
+
oldIndex: change.oldIndex,
|
|
1647
|
+
newIndex: change.newIndex,
|
|
1648
|
+
previousDoc: change.previousDoc
|
|
1649
|
+
};
|
|
1650
|
+
try {
|
|
1651
|
+
if (change.type === "added") {
|
|
1652
|
+
callbacks.onChildAdded?.(change.doc, context);
|
|
1653
|
+
} else if (change.type === "modified") {
|
|
1654
|
+
callbacks.onChildUpdated?.(change.doc, context);
|
|
1655
|
+
} else if (change.type === "removed") {
|
|
1656
|
+
callbacks.onChildRemoved?.(change.doc, context);
|
|
1657
|
+
}
|
|
1658
|
+
} catch (err) {
|
|
1659
|
+
console.error("[EdmaxLabs] Error in child listener:", err);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1392
1662
|
};
|
|
1393
1663
|
|
|
1394
1664
|
// ../../../../node_modules/idb/build/wrap-idb-value.js
|
|
@@ -1759,11 +2029,25 @@ var Persistence = class {
|
|
|
1759
2029
|
}
|
|
1760
2030
|
async getPendingMutations() {
|
|
1761
2031
|
const app = await this.getDb();
|
|
1762
|
-
const
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
2032
|
+
const pending = await app.getAllFromIndex("mutations", "by-status", "pending");
|
|
2033
|
+
return pending.sort((a, b) => a.createdAt - b.createdAt);
|
|
2034
|
+
}
|
|
2035
|
+
async getFailedMutations() {
|
|
2036
|
+
const app = await this.getDb();
|
|
2037
|
+
const failed = await app.getAllFromIndex("mutations", "by-status", "failed");
|
|
2038
|
+
return failed.sort((a, b) => a.createdAt - b.createdAt);
|
|
2039
|
+
}
|
|
2040
|
+
async recoverSyncingMutations() {
|
|
2041
|
+
const app = await this.getDb();
|
|
2042
|
+
const syncing = await app.getAllFromIndex("mutations", "by-status", "syncing");
|
|
2043
|
+
for (const mutation of syncing) {
|
|
2044
|
+
await app.put("mutations", {
|
|
2045
|
+
...mutation,
|
|
2046
|
+
status: "pending",
|
|
2047
|
+
updatedAt: this.now()
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
return syncing.length;
|
|
1767
2051
|
}
|
|
1768
2052
|
async getMutation(mutationId) {
|
|
1769
2053
|
const app = await this.getDb();
|
|
@@ -1868,6 +2152,27 @@ var Persistence = class {
|
|
|
1868
2152
|
}
|
|
1869
2153
|
return this.markDeleted(collection, id, 0);
|
|
1870
2154
|
}
|
|
2155
|
+
async reconcileCollectionFromRemote(collection, documents) {
|
|
2156
|
+
const app = await this.getDb();
|
|
2157
|
+
const existing = await app.getAllFromIndex("docs", "by-collection", collection);
|
|
2158
|
+
const incomingIds = /* @__PURE__ */ new Set();
|
|
2159
|
+
for (const data of documents) {
|
|
2160
|
+
const id = data.id ?? data._id;
|
|
2161
|
+
if (!id)
|
|
2162
|
+
continue;
|
|
2163
|
+
incomingIds.add(String(id));
|
|
2164
|
+
await this.applyRemoteDoc(collection, { ...data, id: String(id) });
|
|
2165
|
+
}
|
|
2166
|
+
for (const doc of existing) {
|
|
2167
|
+
if (doc.pending && doc.pending > 0)
|
|
2168
|
+
continue;
|
|
2169
|
+
if (!doc.exists || doc.deleted)
|
|
2170
|
+
continue;
|
|
2171
|
+
if (incomingIds.has(doc.id))
|
|
2172
|
+
continue;
|
|
2173
|
+
await this.markDeleted(collection, doc.id, 0);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
1871
2176
|
// ==================== UTILITIES ====================
|
|
1872
2177
|
createLocalId() {
|
|
1873
2178
|
return `local_${crypto.randomUUID()}`;
|
|
@@ -1897,112 +2202,169 @@ var RealtimeBridge = class {
|
|
|
1897
2202
|
this.app = app;
|
|
1898
2203
|
this.persistence = persistence;
|
|
1899
2204
|
this.store = store;
|
|
1900
|
-
this.
|
|
1901
|
-
this.
|
|
2205
|
+
this.collectionTargets = /* @__PURE__ */ new Map();
|
|
2206
|
+
this.documentTargets = /* @__PURE__ */ new Map();
|
|
1902
2207
|
}
|
|
1903
2208
|
docKey(collection, id) {
|
|
1904
2209
|
return `${collection}:${id}`;
|
|
1905
2210
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
const
|
|
1909
|
-
if (
|
|
1910
|
-
|
|
2211
|
+
watchCollection(collection, filter = {}, read) {
|
|
2212
|
+
const targetKey = buildTargetKey(collection, filter);
|
|
2213
|
+
const existing = this.collectionTargets.get(targetKey);
|
|
2214
|
+
if (existing) {
|
|
2215
|
+
existing.refCount += 1;
|
|
2216
|
+
return () => this.releaseCollection(targetKey);
|
|
1911
2217
|
}
|
|
1912
|
-
this.emitCurrentCollection(collection);
|
|
1913
2218
|
const unsub = this.app.rtdb().subscribeToCollectionRaw(
|
|
1914
2219
|
collection,
|
|
1915
2220
|
async (payload, change) => {
|
|
1916
2221
|
if (change === "delete") {
|
|
1917
2222
|
const id = payload?.id || payload?._id;
|
|
1918
|
-
if (id)
|
|
2223
|
+
if (id) {
|
|
1919
2224
|
await this.handleRemoteDelete(collection, id);
|
|
1920
|
-
|
|
1921
|
-
|
|
2225
|
+
}
|
|
2226
|
+
return;
|
|
1922
2227
|
}
|
|
2228
|
+
await this.handleRemoteCreateOrUpdate(collection, payload, change);
|
|
1923
2229
|
},
|
|
1924
2230
|
filter
|
|
1925
2231
|
);
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
2232
|
+
this.collectionTargets.set(targetKey, {
|
|
2233
|
+
targetKey,
|
|
2234
|
+
collection,
|
|
2235
|
+
filter,
|
|
2236
|
+
read,
|
|
2237
|
+
refCount: 1,
|
|
2238
|
+
unsub,
|
|
2239
|
+
lastSnapshots: []
|
|
2240
|
+
});
|
|
2241
|
+
return () => this.releaseCollection(targetKey);
|
|
1932
2242
|
}
|
|
1933
2243
|
watchDocument(collection, id) {
|
|
1934
2244
|
const key = this.docKey(collection, id);
|
|
1935
|
-
|
|
1936
|
-
|
|
2245
|
+
const existing = this.documentTargets.get(key);
|
|
2246
|
+
if (existing) {
|
|
2247
|
+
existing.refCount += 1;
|
|
2248
|
+
return () => this.releaseDocument(key);
|
|
1937
2249
|
}
|
|
1938
|
-
this.emitCurrentDocument(collection, id);
|
|
1939
2250
|
const unsub = this.app.rtdb().subscribeToDocumentRaw(
|
|
1940
2251
|
collection,
|
|
1941
2252
|
id,
|
|
1942
2253
|
async (payload, change) => {
|
|
1943
2254
|
if (change === "delete") {
|
|
1944
2255
|
await this.handleRemoteDelete(collection, id);
|
|
1945
|
-
|
|
1946
|
-
await this.handleRemoteCreateOrUpdate(collection, payload, change);
|
|
2256
|
+
return;
|
|
1947
2257
|
}
|
|
2258
|
+
await this.handleRemoteCreateOrUpdate(collection, payload, change);
|
|
1948
2259
|
}
|
|
1949
2260
|
);
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
2261
|
+
this.documentTargets.set(key, {
|
|
2262
|
+
key,
|
|
2263
|
+
collection,
|
|
2264
|
+
id,
|
|
2265
|
+
refCount: 1,
|
|
2266
|
+
unsub
|
|
2267
|
+
});
|
|
2268
|
+
return () => this.releaseDocument(key);
|
|
2269
|
+
}
|
|
2270
|
+
async emitCurrentCollection(collection, filter = {}) {
|
|
2271
|
+
const targetKey = buildTargetKey(collection, filter);
|
|
2272
|
+
const target = this.collectionTargets.get(targetKey);
|
|
2273
|
+
if (!target)
|
|
2274
|
+
return [];
|
|
2275
|
+
return this.refreshCollectionTarget(target, "initial");
|
|
2276
|
+
}
|
|
2277
|
+
primeCollectionTarget(collection, filter, snapshots) {
|
|
2278
|
+
const targetKey = buildTargetKey(collection, filter);
|
|
2279
|
+
const target = this.collectionTargets.get(targetKey);
|
|
2280
|
+
if (!target)
|
|
2281
|
+
return;
|
|
2282
|
+
target.lastSnapshots = snapshots;
|
|
1956
2283
|
}
|
|
1957
|
-
// ===================== INTERNAL HANDLERS =====================
|
|
1958
2284
|
async emitCurrentDocument(collection, id) {
|
|
1959
|
-
const
|
|
1960
|
-
|
|
1961
|
-
|
|
2285
|
+
const snapshot = await this.getCurrentDocumentSnapshot(collection, id);
|
|
2286
|
+
this.store.emitDocument(collection, id, snapshot, "initial");
|
|
2287
|
+
return snapshot;
|
|
1962
2288
|
}
|
|
1963
|
-
async
|
|
1964
|
-
const
|
|
1965
|
-
this.store.
|
|
2289
|
+
async publishLocalChange(collection, id, source) {
|
|
2290
|
+
const snapshot = await this.getCurrentDocumentSnapshot(collection, id);
|
|
2291
|
+
this.store.emitDocument(collection, id, snapshot, source);
|
|
2292
|
+
await this.refreshCollectionTargets(collection, source, id);
|
|
2293
|
+
}
|
|
2294
|
+
async onReconnect() {
|
|
2295
|
+
for (const target of this.collectionTargets.values()) {
|
|
2296
|
+
await this.refreshCollectionTarget(target, "initial");
|
|
2297
|
+
}
|
|
2298
|
+
for (const target of this.documentTargets.values()) {
|
|
2299
|
+
await this.emitCurrentDocument(target.collection, target.id);
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
dispose() {
|
|
2303
|
+
this.collectionTargets.forEach((target) => target.unsub());
|
|
2304
|
+
this.documentTargets.forEach((target) => target.unsub());
|
|
2305
|
+
this.collectionTargets.clear();
|
|
2306
|
+
this.documentTargets.clear();
|
|
1966
2307
|
}
|
|
1967
|
-
async handleRemoteCreateOrUpdate(collection, raw,
|
|
2308
|
+
async handleRemoteCreateOrUpdate(collection, raw, source) {
|
|
1968
2309
|
const id = raw.id ?? raw._id;
|
|
1969
2310
|
if (!id)
|
|
1970
2311
|
return;
|
|
1971
2312
|
const saved = await this.persistence.applyRemoteDoc(collection, raw);
|
|
1972
|
-
if (
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
this.
|
|
2313
|
+
if (saved) {
|
|
2314
|
+
const snap = DocumentSnapshot.fromMap(saved.data);
|
|
2315
|
+
this.store.emitDocument(collection, id, snap, source);
|
|
2316
|
+
}
|
|
2317
|
+
await this.refreshCollectionTargets(collection, source, id, false);
|
|
1977
2318
|
}
|
|
1978
2319
|
async handleRemoteDelete(collection, id) {
|
|
1979
2320
|
await this.persistence.applyRemoteDelete(collection, id);
|
|
1980
2321
|
this.store.emitDocument(collection, id, null, "delete");
|
|
1981
|
-
this.
|
|
2322
|
+
await this.refreshCollectionTargets(collection, "delete", id, false);
|
|
1982
2323
|
}
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
* Replays pending mutations + refreshes all active subscriptions from cache
|
|
1987
|
-
*/
|
|
1988
|
-
async onReconnect() {
|
|
1989
|
-
for (const [key] of this.collectionUnsubs) {
|
|
1990
|
-
const collection = key.split(":")[0];
|
|
1991
|
-
await this.emitCurrentCollection(collection);
|
|
1992
|
-
}
|
|
1993
|
-
for (const [key] of this.documentUnsubs) {
|
|
1994
|
-
const [collection, id] = key.split(":");
|
|
1995
|
-
await this.emitCurrentDocument(collection, id);
|
|
1996
|
-
}
|
|
2324
|
+
async getCurrentDocumentSnapshot(collection, id) {
|
|
2325
|
+
const localDoc = await this.persistence.getDoc(collection, id);
|
|
2326
|
+
return localDoc && localDoc.exists && !localDoc.deleted ? DocumentSnapshot.fromMap(localDoc.data) : null;
|
|
1997
2327
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2328
|
+
async refreshCollectionTargets(collection, source, changedDocId, fromCache = true) {
|
|
2329
|
+
const targets = Array.from(this.collectionTargets.values()).filter(
|
|
2330
|
+
(target) => target.collection === collection
|
|
2331
|
+
);
|
|
2332
|
+
for (const target of targets) {
|
|
2333
|
+
await this.refreshCollectionTarget(target, source, changedDocId, fromCache);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
async refreshCollectionTarget(target, source, changedDocId, fromCache = true) {
|
|
2337
|
+
const snapshots = await target.read();
|
|
2338
|
+
const childChanges = diffSnapshots(target.lastSnapshots, snapshots);
|
|
2339
|
+
target.lastSnapshots = snapshots;
|
|
2340
|
+
this.store.emitCollection(target.targetKey, {
|
|
2341
|
+
snapshots,
|
|
2342
|
+
source,
|
|
2343
|
+
changedDocId,
|
|
2344
|
+
fromCache,
|
|
2345
|
+
childChanges
|
|
2346
|
+
});
|
|
2347
|
+
return snapshots;
|
|
2348
|
+
}
|
|
2349
|
+
releaseCollection(targetKey) {
|
|
2350
|
+
const target = this.collectionTargets.get(targetKey);
|
|
2351
|
+
if (!target)
|
|
2352
|
+
return;
|
|
2353
|
+
target.refCount -= 1;
|
|
2354
|
+
if (target.refCount > 0)
|
|
2355
|
+
return;
|
|
2356
|
+
target.unsub();
|
|
2357
|
+
this.collectionTargets.delete(targetKey);
|
|
2358
|
+
}
|
|
2359
|
+
releaseDocument(key) {
|
|
2360
|
+
const target = this.documentTargets.get(key);
|
|
2361
|
+
if (!target)
|
|
2362
|
+
return;
|
|
2363
|
+
target.refCount -= 1;
|
|
2364
|
+
if (target.refCount > 0)
|
|
2365
|
+
return;
|
|
2366
|
+
target.unsub();
|
|
2367
|
+
this.documentTargets.delete(key);
|
|
2006
2368
|
}
|
|
2007
2369
|
};
|
|
2008
2370
|
|
|
@@ -2014,16 +2376,19 @@ var SyncEngine = class {
|
|
|
2014
2376
|
this.retryTimeout = null;
|
|
2015
2377
|
this.MAX_RETRIES = 5;
|
|
2016
2378
|
this.BASE_RETRY_DELAY = 2e3;
|
|
2379
|
+
this.handleOnline = () => this.onNetworkOnline();
|
|
2380
|
+
this.handleVisibilityChange = () => {
|
|
2381
|
+
if (document.visibilityState === "visible")
|
|
2382
|
+
this.onNetworkOnline();
|
|
2383
|
+
};
|
|
2017
2384
|
this.app = app;
|
|
2018
2385
|
this.persistence = persistence;
|
|
2019
2386
|
this.store = store;
|
|
2020
2387
|
this.realtimeBridge = realtimeBridge || null;
|
|
2388
|
+
this.persistence.recoverSyncingMutations().catch(console.error);
|
|
2021
2389
|
if (typeof window !== "undefined") {
|
|
2022
|
-
window.addEventListener("online",
|
|
2023
|
-
document.addEventListener("visibilitychange",
|
|
2024
|
-
if (document.visibilityState === "visible")
|
|
2025
|
-
this.onNetworkOnline();
|
|
2026
|
-
});
|
|
2390
|
+
window.addEventListener("online", this.handleOnline);
|
|
2391
|
+
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
2027
2392
|
}
|
|
2028
2393
|
}
|
|
2029
2394
|
onNetworkOnline() {
|
|
@@ -2040,6 +2405,7 @@ var SyncEngine = class {
|
|
|
2040
2405
|
return;
|
|
2041
2406
|
this.syncing = true;
|
|
2042
2407
|
try {
|
|
2408
|
+
await this.persistence.recoverSyncingMutations();
|
|
2043
2409
|
const pending = await this.persistence.getPendingMutations();
|
|
2044
2410
|
for (const mutation of pending) {
|
|
2045
2411
|
if (mutation.retryCount >= this.MAX_RETRIES) {
|
|
@@ -2116,8 +2482,7 @@ var SyncEngine = class {
|
|
|
2116
2482
|
if (replaced) {
|
|
2117
2483
|
const snap = DocumentSnapshot.fromMap(replaced.data);
|
|
2118
2484
|
this.store.emitDocument(mutation.collection, oldId, snap, "insert");
|
|
2119
|
-
this.
|
|
2120
|
-
this.store.notifyCollectionChanged(mutation.collection, newId, "insert");
|
|
2485
|
+
await this.realtimeBridge?.publishLocalChange(mutation.collection, newId, "insert");
|
|
2121
2486
|
}
|
|
2122
2487
|
return true;
|
|
2123
2488
|
}
|
|
@@ -2150,7 +2515,7 @@ var SyncEngine = class {
|
|
|
2150
2515
|
});
|
|
2151
2516
|
const snap = DocumentSnapshot.fromMap(local.data);
|
|
2152
2517
|
this.store.emitDocument(mutation.collection, mutation.documentId, snap, "update");
|
|
2153
|
-
this.
|
|
2518
|
+
await this.realtimeBridge?.publishLocalChange(mutation.collection, mutation.documentId, "update");
|
|
2154
2519
|
return true;
|
|
2155
2520
|
}
|
|
2156
2521
|
async syncDelete(mutation) {
|
|
@@ -2167,7 +2532,7 @@ var SyncEngine = class {
|
|
|
2167
2532
|
return false;
|
|
2168
2533
|
await this.persistence.markDeleted(mutation.collection, mutation.documentId, 0);
|
|
2169
2534
|
this.store.emitDocument(mutation.collection, mutation.documentId, null, "delete");
|
|
2170
|
-
this.
|
|
2535
|
+
await this.realtimeBridge?.publishLocalChange(mutation.collection, mutation.documentId, "delete");
|
|
2171
2536
|
return true;
|
|
2172
2537
|
}
|
|
2173
2538
|
scheduleRetry() {
|
|
@@ -2187,8 +2552,7 @@ var SyncEngine = class {
|
|
|
2187
2552
|
* Returns mutations that exceeded MAX_RETRIES
|
|
2188
2553
|
*/
|
|
2189
2554
|
async getFailedMutations() {
|
|
2190
|
-
|
|
2191
|
-
return all.filter((m) => m.status === "failed");
|
|
2555
|
+
return this.persistence.getFailedMutations();
|
|
2192
2556
|
}
|
|
2193
2557
|
/**
|
|
2194
2558
|
* Retry a specific failed mutation by resetting its retry count
|
|
@@ -2230,6 +2594,10 @@ var SyncEngine = class {
|
|
|
2230
2594
|
if (this.retryTimeout) {
|
|
2231
2595
|
clearTimeout(this.retryTimeout);
|
|
2232
2596
|
}
|
|
2597
|
+
if (typeof window !== "undefined") {
|
|
2598
|
+
window.removeEventListener("online", this.handleOnline);
|
|
2599
|
+
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
2600
|
+
}
|
|
2233
2601
|
}
|
|
2234
2602
|
};
|
|
2235
2603
|
|