zen-fs-config 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +39 -4
- package/dist/index.mjs +39 -4
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -465,6 +465,8 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
465
465
|
constructor(appId: string, nodeId: string, primaryBackendId: string, cachedFS: MinimalAsyncFS, serializer: PathAwareSerializer, onConflict?: (conflict: ConflictInfo) => Promise<unknown | null>, pollIntervalMs?: number);
|
|
466
466
|
/** Full path to this node's directory on the primary backend. */
|
|
467
467
|
get nodePath(): string;
|
|
468
|
+
/** Number of replica backends registered (excludes the local primary). */
|
|
469
|
+
get replicaCount(): number;
|
|
468
470
|
load(rawConfig?: string): Promise<void>;
|
|
469
471
|
getConfig<T = unknown>(path: string): T;
|
|
470
472
|
setConfig(path: string, data: unknown): void;
|
|
@@ -489,6 +491,14 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
489
491
|
* This prevents bi-directional sync from copying the file back.
|
|
490
492
|
*/
|
|
491
493
|
private processTombstones;
|
|
494
|
+
/** Public wrapper for processTombstones — used by createConfigRepo. */
|
|
495
|
+
processTombstonesPublic(): Promise<void>;
|
|
496
|
+
/**
|
|
497
|
+
* Perform a full sync + dedup cycle without the watch snapshot cache.
|
|
498
|
+
* Used by createConfigRepo to pull remote-only files (like duplicate
|
|
499
|
+
* backend descriptors) that watch()'s initial snapshot would skip.
|
|
500
|
+
*/
|
|
501
|
+
initialSyncAndDedup(): Promise<void>;
|
|
492
502
|
/**
|
|
493
503
|
* After sync: mark each tombstone as confirmed by all replica backends.
|
|
494
504
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -465,6 +465,8 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
465
465
|
constructor(appId: string, nodeId: string, primaryBackendId: string, cachedFS: MinimalAsyncFS, serializer: PathAwareSerializer, onConflict?: (conflict: ConflictInfo) => Promise<unknown | null>, pollIntervalMs?: number);
|
|
466
466
|
/** Full path to this node's directory on the primary backend. */
|
|
467
467
|
get nodePath(): string;
|
|
468
|
+
/** Number of replica backends registered (excludes the local primary). */
|
|
469
|
+
get replicaCount(): number;
|
|
468
470
|
load(rawConfig?: string): Promise<void>;
|
|
469
471
|
getConfig<T = unknown>(path: string): T;
|
|
470
472
|
setConfig(path: string, data: unknown): void;
|
|
@@ -489,6 +491,14 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
489
491
|
* This prevents bi-directional sync from copying the file back.
|
|
490
492
|
*/
|
|
491
493
|
private processTombstones;
|
|
494
|
+
/** Public wrapper for processTombstones — used by createConfigRepo. */
|
|
495
|
+
processTombstonesPublic(): Promise<void>;
|
|
496
|
+
/**
|
|
497
|
+
* Perform a full sync + dedup cycle without the watch snapshot cache.
|
|
498
|
+
* Used by createConfigRepo to pull remote-only files (like duplicate
|
|
499
|
+
* backend descriptors) that watch()'s initial snapshot would skip.
|
|
500
|
+
*/
|
|
501
|
+
initialSyncAndDedup(): Promise<void>;
|
|
492
502
|
/**
|
|
493
503
|
* After sync: mark each tombstone as confirmed by all replica backends.
|
|
494
504
|
*/
|
package/dist/index.js
CHANGED
|
@@ -585,6 +585,10 @@ var ConfigRepo = class {
|
|
|
585
585
|
get nodePath() {
|
|
586
586
|
return `/nodes/${this.nodeId}`;
|
|
587
587
|
}
|
|
588
|
+
/** Number of replica backends registered (excludes the local primary). */
|
|
589
|
+
get replicaCount() {
|
|
590
|
+
return this.replicaBackends.size;
|
|
591
|
+
}
|
|
588
592
|
// -----------------------------------------------------------------------
|
|
589
593
|
// IConfigRepo — Load
|
|
590
594
|
// -----------------------------------------------------------------------
|
|
@@ -723,6 +727,8 @@ var ConfigRepo = class {
|
|
|
723
727
|
this.assertNotDisposed();
|
|
724
728
|
await this.processTombstones();
|
|
725
729
|
const resultsMap = await this.syncEngine.syncAll();
|
|
730
|
+
await this.readAllBackendDescriptors();
|
|
731
|
+
await this.processTombstones();
|
|
726
732
|
await this.updateTombstoneConfirmations();
|
|
727
733
|
await this.gcTombstones();
|
|
728
734
|
return Array.from(resultsMap.values());
|
|
@@ -811,6 +817,22 @@ var ConfigRepo = class {
|
|
|
811
817
|
}
|
|
812
818
|
}
|
|
813
819
|
}
|
|
820
|
+
/** Public wrapper for processTombstones — used by createConfigRepo. */
|
|
821
|
+
async processTombstonesPublic() {
|
|
822
|
+
await this.processTombstones();
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Perform a full sync + dedup cycle without the watch snapshot cache.
|
|
826
|
+
* Used by createConfigRepo to pull remote-only files (like duplicate
|
|
827
|
+
* backend descriptors) that watch()'s initial snapshot would skip.
|
|
828
|
+
*/
|
|
829
|
+
async initialSyncAndDedup() {
|
|
830
|
+
this.syncEngine.unwatchAll();
|
|
831
|
+
await this.syncEngine.syncAll();
|
|
832
|
+
await this.readAllBackendDescriptors();
|
|
833
|
+
await this.processTombstones();
|
|
834
|
+
this.syncEngine.watchAll();
|
|
835
|
+
}
|
|
814
836
|
/**
|
|
815
837
|
* After sync: mark each tombstone as confirmed by all replica backends.
|
|
816
838
|
*/
|
|
@@ -988,7 +1010,6 @@ var ConfigRepo = class {
|
|
|
988
1010
|
this.handleConflict(event);
|
|
989
1011
|
};
|
|
990
1012
|
this.syncEngine.on(pair.pairId, "conflict", conflictHandler);
|
|
991
|
-
this.syncEngine.watch(pair.pairId);
|
|
992
1013
|
console.log(`[ConfigRepo] Replica ${desc.id} created, sync pair=${pair.pairId}`);
|
|
993
1014
|
} catch (err) {
|
|
994
1015
|
console.error(`[ConfigRepo] Failed to create replica ${desc.id} (${desc.type}):`, err);
|
|
@@ -1198,6 +1219,16 @@ var ConfigRepo = class {
|
|
|
1198
1219
|
);
|
|
1199
1220
|
for (const dupId of duplicates) {
|
|
1200
1221
|
const descPath = this.backendFilePath(dupId);
|
|
1222
|
+
for (const [replicaId, replica] of this.replicaBackends) {
|
|
1223
|
+
try {
|
|
1224
|
+
await replica.instance.unlink(descPath);
|
|
1225
|
+
} catch {
|
|
1226
|
+
}
|
|
1227
|
+
try {
|
|
1228
|
+
await replica.instance.unlink(versionPathFor(descPath));
|
|
1229
|
+
} catch {
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1201
1232
|
try {
|
|
1202
1233
|
await this.deleteFile(descPath);
|
|
1203
1234
|
} catch {
|
|
@@ -1312,9 +1343,9 @@ var ConfigRepo = class {
|
|
|
1312
1343
|
this.handleConflict(event);
|
|
1313
1344
|
};
|
|
1314
1345
|
this.syncEngine.on(pair.pairId, "conflict", conflictHandler);
|
|
1315
|
-
this.syncEngine.watch(pair.pairId);
|
|
1316
1346
|
console.log(`[ConfigRepo] addBackend: ${id} (${type}) added, sync pair=${pair.pairId}`);
|
|
1317
1347
|
await this.syncMetaToReplicas();
|
|
1348
|
+
this.syncEngine.watch(pair.pairId);
|
|
1318
1349
|
}
|
|
1319
1350
|
async removeBackend(id) {
|
|
1320
1351
|
this.assertNotDisposed();
|
|
@@ -1559,7 +1590,6 @@ var AppDataGroupImpl = class {
|
|
|
1559
1590
|
"/"
|
|
1560
1591
|
);
|
|
1561
1592
|
this.dataBackends.set(desc.id, { instance, syncable, pairId: pair.pairId, desc });
|
|
1562
|
-
this.syncEngine.watch(pair.pairId);
|
|
1563
1593
|
console.log(`[AppDataGroup:${this.groupId}] backend ${desc.id} (${desc.type}) connected, pair=${pair.pairId}`);
|
|
1564
1594
|
} catch (err) {
|
|
1565
1595
|
console.error(`[AppDataGroup:${this.groupId}] Failed to create backend ${desc.id} (${desc.type}):`, err);
|
|
@@ -1570,6 +1600,7 @@ var AppDataGroupImpl = class {
|
|
|
1570
1600
|
} catch (err) {
|
|
1571
1601
|
console.warn(`[AppDataGroup:${this.groupId}] Initial sync failed:`, err);
|
|
1572
1602
|
}
|
|
1603
|
+
this.syncEngine.watchAll();
|
|
1573
1604
|
}
|
|
1574
1605
|
getSyncStatuses() {
|
|
1575
1606
|
return this.syncEngine.getStatusAll();
|
|
@@ -1599,13 +1630,13 @@ var AppDataGroupImpl = class {
|
|
|
1599
1630
|
);
|
|
1600
1631
|
const desc = { id, type, options, description };
|
|
1601
1632
|
this.dataBackends.set(id, { instance, syncable, pairId: pair.pairId, desc });
|
|
1602
|
-
this.syncEngine.watch(pair.pairId);
|
|
1603
1633
|
console.log(`[AppDataGroup:${this.groupId}] addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
|
|
1604
1634
|
try {
|
|
1605
1635
|
await this.syncEngine.sync(pair.pairId);
|
|
1606
1636
|
} catch (err) {
|
|
1607
1637
|
console.warn(`[AppDataGroup:${this.groupId}] addBackend: initial sync failed for ${id}:`, err);
|
|
1608
1638
|
}
|
|
1639
|
+
this.syncEngine.watch(pair.pairId);
|
|
1609
1640
|
}
|
|
1610
1641
|
async removeBackend(id) {
|
|
1611
1642
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
@@ -1739,6 +1770,10 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1739
1770
|
);
|
|
1740
1771
|
await repo.setupSync(allBackends, LOCAL_IDB_BACKEND_ID, options.syncPollIntervalMs);
|
|
1741
1772
|
await repo.load();
|
|
1773
|
+
if (repo.replicaCount > 0) {
|
|
1774
|
+
console.log("[createConfigRepo] Initial sync + dedup cycle...");
|
|
1775
|
+
await repo.initialSyncAndDedup();
|
|
1776
|
+
}
|
|
1742
1777
|
repo.syncMetaToReplicas().catch((err) => {
|
|
1743
1778
|
console.error("[createConfigRepo] background syncMetaToReplicas failed:", err);
|
|
1744
1779
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -528,6 +528,10 @@ var ConfigRepo = class {
|
|
|
528
528
|
get nodePath() {
|
|
529
529
|
return `/nodes/${this.nodeId}`;
|
|
530
530
|
}
|
|
531
|
+
/** Number of replica backends registered (excludes the local primary). */
|
|
532
|
+
get replicaCount() {
|
|
533
|
+
return this.replicaBackends.size;
|
|
534
|
+
}
|
|
531
535
|
// -----------------------------------------------------------------------
|
|
532
536
|
// IConfigRepo — Load
|
|
533
537
|
// -----------------------------------------------------------------------
|
|
@@ -666,6 +670,8 @@ var ConfigRepo = class {
|
|
|
666
670
|
this.assertNotDisposed();
|
|
667
671
|
await this.processTombstones();
|
|
668
672
|
const resultsMap = await this.syncEngine.syncAll();
|
|
673
|
+
await this.readAllBackendDescriptors();
|
|
674
|
+
await this.processTombstones();
|
|
669
675
|
await this.updateTombstoneConfirmations();
|
|
670
676
|
await this.gcTombstones();
|
|
671
677
|
return Array.from(resultsMap.values());
|
|
@@ -754,6 +760,22 @@ var ConfigRepo = class {
|
|
|
754
760
|
}
|
|
755
761
|
}
|
|
756
762
|
}
|
|
763
|
+
/** Public wrapper for processTombstones — used by createConfigRepo. */
|
|
764
|
+
async processTombstonesPublic() {
|
|
765
|
+
await this.processTombstones();
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Perform a full sync + dedup cycle without the watch snapshot cache.
|
|
769
|
+
* Used by createConfigRepo to pull remote-only files (like duplicate
|
|
770
|
+
* backend descriptors) that watch()'s initial snapshot would skip.
|
|
771
|
+
*/
|
|
772
|
+
async initialSyncAndDedup() {
|
|
773
|
+
this.syncEngine.unwatchAll();
|
|
774
|
+
await this.syncEngine.syncAll();
|
|
775
|
+
await this.readAllBackendDescriptors();
|
|
776
|
+
await this.processTombstones();
|
|
777
|
+
this.syncEngine.watchAll();
|
|
778
|
+
}
|
|
757
779
|
/**
|
|
758
780
|
* After sync: mark each tombstone as confirmed by all replica backends.
|
|
759
781
|
*/
|
|
@@ -931,7 +953,6 @@ var ConfigRepo = class {
|
|
|
931
953
|
this.handleConflict(event);
|
|
932
954
|
};
|
|
933
955
|
this.syncEngine.on(pair.pairId, "conflict", conflictHandler);
|
|
934
|
-
this.syncEngine.watch(pair.pairId);
|
|
935
956
|
console.log(`[ConfigRepo] Replica ${desc.id} created, sync pair=${pair.pairId}`);
|
|
936
957
|
} catch (err) {
|
|
937
958
|
console.error(`[ConfigRepo] Failed to create replica ${desc.id} (${desc.type}):`, err);
|
|
@@ -1141,6 +1162,16 @@ var ConfigRepo = class {
|
|
|
1141
1162
|
);
|
|
1142
1163
|
for (const dupId of duplicates) {
|
|
1143
1164
|
const descPath = this.backendFilePath(dupId);
|
|
1165
|
+
for (const [replicaId, replica] of this.replicaBackends) {
|
|
1166
|
+
try {
|
|
1167
|
+
await replica.instance.unlink(descPath);
|
|
1168
|
+
} catch {
|
|
1169
|
+
}
|
|
1170
|
+
try {
|
|
1171
|
+
await replica.instance.unlink(versionPathFor(descPath));
|
|
1172
|
+
} catch {
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1144
1175
|
try {
|
|
1145
1176
|
await this.deleteFile(descPath);
|
|
1146
1177
|
} catch {
|
|
@@ -1255,9 +1286,9 @@ var ConfigRepo = class {
|
|
|
1255
1286
|
this.handleConflict(event);
|
|
1256
1287
|
};
|
|
1257
1288
|
this.syncEngine.on(pair.pairId, "conflict", conflictHandler);
|
|
1258
|
-
this.syncEngine.watch(pair.pairId);
|
|
1259
1289
|
console.log(`[ConfigRepo] addBackend: ${id} (${type}) added, sync pair=${pair.pairId}`);
|
|
1260
1290
|
await this.syncMetaToReplicas();
|
|
1291
|
+
this.syncEngine.watch(pair.pairId);
|
|
1261
1292
|
}
|
|
1262
1293
|
async removeBackend(id) {
|
|
1263
1294
|
this.assertNotDisposed();
|
|
@@ -1502,7 +1533,6 @@ var AppDataGroupImpl = class {
|
|
|
1502
1533
|
"/"
|
|
1503
1534
|
);
|
|
1504
1535
|
this.dataBackends.set(desc.id, { instance, syncable, pairId: pair.pairId, desc });
|
|
1505
|
-
this.syncEngine.watch(pair.pairId);
|
|
1506
1536
|
console.log(`[AppDataGroup:${this.groupId}] backend ${desc.id} (${desc.type}) connected, pair=${pair.pairId}`);
|
|
1507
1537
|
} catch (err) {
|
|
1508
1538
|
console.error(`[AppDataGroup:${this.groupId}] Failed to create backend ${desc.id} (${desc.type}):`, err);
|
|
@@ -1513,6 +1543,7 @@ var AppDataGroupImpl = class {
|
|
|
1513
1543
|
} catch (err) {
|
|
1514
1544
|
console.warn(`[AppDataGroup:${this.groupId}] Initial sync failed:`, err);
|
|
1515
1545
|
}
|
|
1546
|
+
this.syncEngine.watchAll();
|
|
1516
1547
|
}
|
|
1517
1548
|
getSyncStatuses() {
|
|
1518
1549
|
return this.syncEngine.getStatusAll();
|
|
@@ -1542,13 +1573,13 @@ var AppDataGroupImpl = class {
|
|
|
1542
1573
|
);
|
|
1543
1574
|
const desc = { id, type, options, description };
|
|
1544
1575
|
this.dataBackends.set(id, { instance, syncable, pairId: pair.pairId, desc });
|
|
1545
|
-
this.syncEngine.watch(pair.pairId);
|
|
1546
1576
|
console.log(`[AppDataGroup:${this.groupId}] addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
|
|
1547
1577
|
try {
|
|
1548
1578
|
await this.syncEngine.sync(pair.pairId);
|
|
1549
1579
|
} catch (err) {
|
|
1550
1580
|
console.warn(`[AppDataGroup:${this.groupId}] addBackend: initial sync failed for ${id}:`, err);
|
|
1551
1581
|
}
|
|
1582
|
+
this.syncEngine.watch(pair.pairId);
|
|
1552
1583
|
}
|
|
1553
1584
|
async removeBackend(id) {
|
|
1554
1585
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
@@ -1682,6 +1713,10 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1682
1713
|
);
|
|
1683
1714
|
await repo.setupSync(allBackends, LOCAL_IDB_BACKEND_ID, options.syncPollIntervalMs);
|
|
1684
1715
|
await repo.load();
|
|
1716
|
+
if (repo.replicaCount > 0) {
|
|
1717
|
+
console.log("[createConfigRepo] Initial sync + dedup cycle...");
|
|
1718
|
+
await repo.initialSyncAndDedup();
|
|
1719
|
+
}
|
|
1685
1720
|
repo.syncMetaToReplicas().catch((err) => {
|
|
1686
1721
|
console.error("[createConfigRepo] background syncMetaToReplicas failed:", err);
|
|
1687
1722
|
});
|