zen-fs-config 0.5.18 → 0.5.20

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 (3) hide show
  1. package/dist/index.js +106 -118
  2. package/dist/index.mjs +106 -118
  3. package/package.json +5 -2
package/dist/index.js CHANGED
@@ -485,6 +485,9 @@ registerBackend("IndexedDB", async (options) => {
485
485
  defaultOptions: { storeName: "" }
486
486
  });
487
487
 
488
+ // src/config-repo.ts
489
+ var import_localstorage_logger = require("@richard432/localstorage-logger");
490
+
488
491
  // src/version.ts
489
492
  function versionPathFor(configFilePath) {
490
493
  const lastSlash = configFilePath.lastIndexOf("/");
@@ -573,6 +576,7 @@ async function verifyOrRepairVersion(fs, configFilePath, author) {
573
576
  }
574
577
 
575
578
  // src/config-repo.ts
579
+ var log = (0, import_localstorage_logger.createLogger)("zen-fs-config:config-repo");
576
580
  var META_DIR = "/.meta";
577
581
  var GROUP_TYPE_FILE = `${META_DIR}/group-type`;
578
582
  var BACKENDS_FILE = `${META_DIR}/backends.json`;
@@ -688,7 +692,7 @@ var ConfigRepo = class {
688
692
  const bytes = this.serializer.serialize(data, fullPath);
689
693
  this.configCache.set(fullPath, data);
690
694
  this.persistConfig(fullPath, bytes).catch((err) => {
691
- console.error(`[zen-fs-config] Failed to persist ${fullPath}:`, err);
695
+ log.error(`[zen-fs-config] Failed to persist ${fullPath}:`, err);
692
696
  });
693
697
  }
694
698
  // -----------------------------------------------------------------------
@@ -834,7 +838,7 @@ var ConfigRepo = class {
834
838
  } catch {
835
839
  }
836
840
  }
837
- console.log(`[ConfigRepo] deleteFile: ${normalizedPath} (tombstone at ${tombstonePath})`);
841
+ log.log(`[ConfigRepo] deleteFile: ${normalizedPath} (tombstone at ${tombstonePath})`);
838
842
  this.invalidateTombstoneCache();
839
843
  this.schedulePostDeleteSync();
840
844
  }
@@ -852,7 +856,7 @@ var ConfigRepo = class {
852
856
  this.postDeleteSyncTimer = void 0;
853
857
  if (this.disposed) return;
854
858
  this.syncEngine.syncAll().catch((err) => {
855
- console.warn("[ConfigRepo] post-delete sync failed:", err);
859
+ log.warn("[ConfigRepo] post-delete sync failed:", err);
856
860
  });
857
861
  }, 500);
858
862
  }
@@ -904,11 +908,11 @@ var ConfigRepo = class {
904
908
  let alreadyDeleted = 0;
905
909
  for (const tombstone of tombstones) {
906
910
  const tVersionPath = versionPathFor(tombstone.path);
907
- console.log(`[TOMB-TRACE] processing tombstone: path=${tombstone.path} versionPath=${tVersionPath ?? "null"}`);
911
+ log.log(`[TOMB-TRACE] processing tombstone: path=${tombstone.path} versionPath=${tVersionPath ?? "null"}`);
908
912
  const existedOnPrimary = await this.safeExists(this.cachedFS, tombstone.path);
909
- console.log(`[TOMB-TRACE] primary safeExists(${tombstone.path}) \u2192 ${existedOnPrimary}`);
913
+ log.log(`[TOMB-TRACE] primary safeExists(${tombstone.path}) \u2192 ${existedOnPrimary}`);
910
914
  if (existedOnPrimary) {
911
- console.log(`[TOMB-TRACE] tombstone check: ${tombstone.path} on primary \u2192 EXISTS`);
915
+ log.log(`[TOMB-TRACE] tombstone check: ${tombstone.path} on primary \u2192 EXISTS`);
912
916
  }
913
917
  if (existedOnPrimary) {
914
918
  try {
@@ -919,7 +923,7 @@ var ConfigRepo = class {
919
923
  }
920
924
  if (tVersionPath) {
921
925
  const vExistedOnPrimary = await this.safeExists(this.cachedFS, tVersionPath);
922
- console.log(`[TOMB-TRACE] primary safeExists(${tVersionPath}) \u2192 ${vExistedOnPrimary}`);
926
+ log.log(`[TOMB-TRACE] primary safeExists(${tVersionPath}) \u2192 ${vExistedOnPrimary}`);
923
927
  if (vExistedOnPrimary) {
924
928
  try {
925
929
  await this.cachedFS.unlink(tVersionPath);
@@ -928,40 +932,40 @@ var ConfigRepo = class {
928
932
  }
929
933
  }
930
934
  for (const [replicaId, replica] of this.replicaBackends) {
931
- console.log(`[TOMB-TRACE] checking replica: ${replicaId} for ${tombstone.path}`);
935
+ log.log(`[TOMB-TRACE] checking replica: ${replicaId} for ${tombstone.path}`);
932
936
  const existed = await this.safeExists(replica.instance, tombstone.path);
933
- console.log(`[TOMB-TRACE] replica ${replicaId} safeExists(${tombstone.path}) \u2192 ${existed}`);
937
+ log.log(`[TOMB-TRACE] replica ${replicaId} safeExists(${tombstone.path}) \u2192 ${existed}`);
934
938
  if (existed) {
935
- console.log(`[TOMB-TRACE] tombstone check: ${tombstone.path} on ${replicaId} \u2192 EXISTS`);
939
+ log.log(`[TOMB-TRACE] tombstone check: ${tombstone.path} on ${replicaId} \u2192 EXISTS`);
936
940
  try {
937
- console.log(`[TOMB-TRACE] calling unlink(${tombstone.path}) on ${replicaId}`);
941
+ log.log(`[TOMB-TRACE] calling unlink(${tombstone.path}) on ${replicaId}`);
938
942
  await replica.instance.unlink(tombstone.path);
939
- console.log(`[TOMB-TRACE] tombstone ${tombstone.path}: deleted on ${replicaId}`);
943
+ log.log(`[TOMB-TRACE] tombstone ${tombstone.path}: deleted on ${replicaId}`);
940
944
  processed++;
941
945
  } catch (err) {
942
- console.log(`[TOMB-TRACE] unlink(${tombstone.path}) on ${replicaId} FAILED: ${err}`);
946
+ log.log(`[TOMB-TRACE] unlink(${tombstone.path}) on ${replicaId} FAILED: ${err}`);
943
947
  alreadyDeleted++;
944
948
  }
945
949
  } else {
946
- console.log(`[TOMB-TRACE] replica ${replicaId}: already gone, skipping unlink`);
950
+ log.log(`[TOMB-TRACE] replica ${replicaId}: already gone, skipping unlink`);
947
951
  alreadyDeleted++;
948
952
  }
949
953
  if (tVersionPath) {
950
954
  const vExisted = await this.safeExists(replica.instance, tVersionPath);
951
- console.log(`[TOMB-TRACE] replica ${replicaId} safeExists(${tVersionPath}) \u2192 ${vExisted}`);
955
+ log.log(`[TOMB-TRACE] replica ${replicaId} safeExists(${tVersionPath}) \u2192 ${vExisted}`);
952
956
  if (vExisted) {
953
957
  try {
954
- console.log(`[TOMB-TRACE] calling unlink(${tVersionPath}) on ${replicaId}`);
958
+ log.log(`[TOMB-TRACE] calling unlink(${tVersionPath}) on ${replicaId}`);
955
959
  await replica.instance.unlink(tVersionPath);
956
960
  } catch (err) {
957
- console.log(`[TOMB-TRACE] unlink(${tVersionPath}) on ${replicaId} FAILED: ${err}`);
961
+ log.log(`[TOMB-TRACE] unlink(${tVersionPath}) on ${replicaId} FAILED: ${err}`);
958
962
  }
959
963
  }
960
964
  }
961
965
  }
962
966
  }
963
967
  if (processed > 0 || alreadyDeleted > 0) {
964
- console.log(`[TOMB-TRACE] processTombstones: ${tombstones.length} tombstone(s), ${processed} deleted, ${alreadyDeleted} already gone`);
968
+ log.log(`[TOMB-TRACE] processTombstones: ${tombstones.length} tombstone(s), ${processed} deleted, ${alreadyDeleted} already gone`);
965
969
  }
966
970
  }
967
971
  /**
@@ -977,21 +981,21 @@ var ConfigRepo = class {
977
981
  const innerBackendName = fs?.inner?.backendName;
978
982
  const directBackendName = fs?.backendName;
979
983
  const backendLabel = innerBackendName ?? directBackendName ?? innerBackend ?? ctorName;
980
- console.log(`[TOMB-TRACE] safeExists(${path}): fs type=${ctorName} backend=${backendLabel} hasExists=${hasExists} hasStat=${hasStat} inner=${innerBackend ?? "N/A"}`);
984
+ log.log(`[TOMB-TRACE] safeExists(${path}): fs type=${ctorName} backend=${backendLabel} hasExists=${hasExists} hasStat=${hasStat} inner=${innerBackend ?? "N/A"}`);
981
985
  if (hasExists) {
982
- console.log(`[TOMB-TRACE] safeExists(${path}): \u2192 calling fs.exists() [backend=${backendLabel}]`);
986
+ log.log(`[TOMB-TRACE] safeExists(${path}): \u2192 calling fs.exists() [backend=${backendLabel}]`);
983
987
  const result = await fs.exists(path);
984
- console.log(`[TOMB-TRACE] safeExists(${path}): \u2190 fs.exists() [backend=${backendLabel}] \u2192 ${result}`);
988
+ log.log(`[TOMB-TRACE] safeExists(${path}): \u2190 fs.exists() [backend=${backendLabel}] \u2192 ${result}`);
985
989
  return result;
986
990
  }
987
- console.log(`[TOMB-TRACE] safeExists(${path}): no exists(), calling fs.stat() [backend=${backendLabel}]`);
991
+ log.log(`[TOMB-TRACE] safeExists(${path}): no exists(), calling fs.stat() [backend=${backendLabel}]`);
988
992
  await fs.stat(path);
989
- console.log(`[TOMB-TRACE] safeExists(${path}): \u2190 fs.stat() [backend=${backendLabel}] \u2192 OK (exists)`);
993
+ log.log(`[TOMB-TRACE] safeExists(${path}): \u2190 fs.stat() [backend=${backendLabel}] \u2192 OK (exists)`);
990
994
  return true;
991
995
  } catch (err) {
992
996
  const errCode = err?.code ?? err?.status ?? "";
993
997
  const errName = err?.constructor?.name ?? "";
994
- console.log(`[TOMB-TRACE] safeExists(${path}): \u2190 ERROR ${errName} ${errCode} ${err?.message ?? err}`);
998
+ log.log(`[TOMB-TRACE] safeExists(${path}): \u2190 ERROR ${errName} ${errCode} ${err?.message ?? err}`);
995
999
  return false;
996
1000
  }
997
1001
  }
@@ -1018,9 +1022,9 @@ var ConfigRepo = class {
1018
1022
  */
1019
1023
  startBackgroundSync() {
1020
1024
  this.initialSyncPromise = this.initialSyncAndDedup().then(() => {
1021
- console.log("[ConfigRepo] Background initial sync complete");
1025
+ log.log("[ConfigRepo] Background initial sync complete");
1022
1026
  }).catch((err) => {
1023
- console.error("[ConfigRepo] Background initial sync failed:", err);
1027
+ log.error("[ConfigRepo] Background initial sync failed:", err);
1024
1028
  }).finally(() => {
1025
1029
  this.initialSyncPromise = null;
1026
1030
  });
@@ -1048,7 +1052,7 @@ var ConfigRepo = class {
1048
1052
  } catch {
1049
1053
  }
1050
1054
  }
1051
- console.log(`[ConfigRepo] updateTombstoneConfirmations: ${tombstones.length} tombstone(s) updated`);
1055
+ log.log(`[ConfigRepo] updateTombstoneConfirmations: ${tombstones.length} tombstone(s) updated`);
1052
1056
  this.invalidateTombstoneCache();
1053
1057
  }
1054
1058
  /**
@@ -1080,7 +1084,7 @@ var ConfigRepo = class {
1080
1084
  }
1081
1085
  }
1082
1086
  }
1083
- console.log(`[ConfigRepo] gcTombstones: removed ${tombstonePath} (all ${allBackendIds.length} backends confirmed)`);
1087
+ log.log(`[ConfigRepo] gcTombstones: removed ${tombstonePath} (all ${allBackendIds.length} backends confirmed)`);
1084
1088
  }
1085
1089
  }
1086
1090
  }
@@ -1096,7 +1100,7 @@ var ConfigRepo = class {
1096
1100
  this.assertNotDisposed();
1097
1101
  const results = await this.flush();
1098
1102
  for (const result of results) {
1099
- console.log(
1103
+ log.log(
1100
1104
  `[ConfigRepo] syncMetaToReplicas: ${result.pairId} +${result.filesCreated}/~${result.filesUpdated}/-${result.filesDeleted} skip:${result.filesSkipped} ${result.durationMs}ms`
1101
1105
  );
1102
1106
  }
@@ -1198,21 +1202,21 @@ var ConfigRepo = class {
1198
1202
  // Internal — Setup
1199
1203
  // -----------------------------------------------------------------------
1200
1204
  async setupSync(backends, primaryBackendId, pollIntervalMs) {
1201
- console.log(`[ConfigRepo] setupSync: ${backends.length} backends, primary=${primaryBackendId} pollInterval=${pollIntervalMs ?? "default"}ms`);
1205
+ log.log(`[ConfigRepo] setupSync: ${backends.length} backends, primary=${primaryBackendId} pollInterval=${pollIntervalMs ?? "default"}ms`);
1202
1206
  for (const desc of backends) {
1203
1207
  if (desc.id === primaryBackendId) continue;
1204
1208
  if (desc.enabled === false) {
1205
- console.log(`[ConfigRepo] Skipping disabled replica: ${desc.id}`);
1209
+ log.log(`[ConfigRepo] Skipping disabled replica: ${desc.id}`);
1206
1210
  continue;
1207
1211
  }
1208
- console.log(`[ConfigRepo] Creating replica backend: id=${desc.id}, type=${desc.type}`);
1212
+ log.log(`[ConfigRepo] Creating replica backend: id=${desc.id}, type=${desc.type}`);
1209
1213
  try {
1210
1214
  const instance = await createBackend(desc);
1211
1215
  let fsInstance = instance;
1212
1216
  if (this.cacheOptions) {
1213
1217
  const { wrapWithCache: wrapWithCache2 } = await Promise.resolve().then(() => (init_cache_wrapper(), cache_wrapper_exports));
1214
1218
  fsInstance = wrapWithCache2(instance, desc.id, this.cacheOptions);
1215
- console.log(`[ConfigRepo] Replica ${desc.id} wrapped with CachedFileSystem (store=${this.cacheOptions.storeType ?? "IdbCacheStore"})`);
1219
+ log.log(`[ConfigRepo] Replica ${desc.id} wrapped with CachedFileSystem (store=${this.cacheOptions.storeType ?? "IdbCacheStore"})`);
1216
1220
  }
1217
1221
  const syncable = backendToSyncableFS(fsInstance, `${desc.type}(${desc.id})`);
1218
1222
  const pair = this.syncEngine.addPair(
@@ -1227,7 +1231,7 @@ var ConfigRepo = class {
1227
1231
  this.invalidateTombstoneCache();
1228
1232
  await this.processTombstones();
1229
1233
  } catch (err) {
1230
- console.warn("[ConfigRepo] preSyncHook processTombstones failed:", err);
1234
+ log.warn("[ConfigRepo] preSyncHook processTombstones failed:", err);
1231
1235
  }
1232
1236
  },
1233
1237
  postSyncHook: async () => {
@@ -1236,7 +1240,7 @@ var ConfigRepo = class {
1236
1240
  await this.processTombstones();
1237
1241
  await this.updateTombstoneConfirmations();
1238
1242
  } catch (err) {
1239
- console.warn("[ConfigRepo] postSyncHook tombstone processing failed:", err);
1243
+ log.warn("[ConfigRepo] postSyncHook tombstone processing failed:", err);
1240
1244
  }
1241
1245
  }
1242
1246
  },
@@ -1247,13 +1251,13 @@ var ConfigRepo = class {
1247
1251
  this.handleConflict(event);
1248
1252
  };
1249
1253
  this.syncEngine.on(pair.pairId, "conflict", conflictHandler);
1250
- console.log(`[ConfigRepo] Replica ${desc.id} created, sync pair=${pair.pairId}`);
1254
+ log.log(`[ConfigRepo] Replica ${desc.id} created, sync pair=${pair.pairId}`);
1251
1255
  } catch (err) {
1252
- console.error(`[ConfigRepo] Failed to create replica ${desc.id} (${desc.type}):`, err);
1256
+ log.error(`[ConfigRepo] Failed to create replica ${desc.id} (${desc.type}):`, err);
1253
1257
  }
1254
1258
  }
1255
- console.log(`[ConfigRepo] setupSync complete. Replicas:`, Array.from(this.replicaBackends.keys()));
1256
- console.log(`[ConfigRepo] Sync statuses:`, this.getSyncStatuses());
1259
+ log.log(`[ConfigRepo] setupSync complete. Replicas:`, Array.from(this.replicaBackends.keys()));
1260
+ log.log(`[ConfigRepo] Sync statuses:`, this.getSyncStatuses());
1257
1261
  }
1258
1262
  // -----------------------------------------------------------------------
1259
1263
  // Internal — Persistence
@@ -1365,7 +1369,7 @@ var ConfigRepo = class {
1365
1369
  await this.resolveConflict(`${conflictId}/meta.json`, customMerge);
1366
1370
  }
1367
1371
  } catch (err) {
1368
- console.error("[zen-fs-config] Conflict handler error:", err);
1372
+ log.error("[zen-fs-config] Conflict handler error:", err);
1369
1373
  }
1370
1374
  }
1371
1375
  }
@@ -1464,11 +1468,11 @@ var ConfigRepo = class {
1464
1468
  }
1465
1469
  return { kind: "ok", desc, mtime };
1466
1470
  } else {
1467
- console.warn(`[ConfigRepo] Backend descriptor ${entry} is missing id/type fields, marking for cleanup`);
1471
+ log.warn(`[ConfigRepo] Backend descriptor ${entry} is missing id/type fields, marking for cleanup`);
1468
1472
  return { kind: "corrupt", filePath };
1469
1473
  }
1470
1474
  } catch (parseErr) {
1471
- console.warn(`[ConfigRepo] Backend descriptor ${entry} has corrupted JSON: ${parseErr}. Marking for cleanup.`);
1475
+ log.warn(`[ConfigRepo] Backend descriptor ${entry} has corrupted JSON: ${parseErr}. Marking for cleanup.`);
1472
1476
  return { kind: "corrupt", filePath };
1473
1477
  }
1474
1478
  })
@@ -1517,7 +1521,7 @@ var ConfigRepo = class {
1517
1521
  }
1518
1522
  }
1519
1523
  if (duplicates.length > 0) {
1520
- console.log(
1524
+ log.log(
1521
1525
  `[ConfigRepo] readAllBackendDescriptors: removing ${duplicates.length} duplicate(s): ${duplicates.join(", ")}`
1522
1526
  );
1523
1527
  for (const dupId of duplicates) {
@@ -1620,7 +1624,7 @@ var ConfigRepo = class {
1620
1624
  `Backend "${id}" has the same configuration as existing backend "${dup.id}" (type=${type}). Use removeBackend("${dup.id}") first, or connect with the existing backend's ID.`
1621
1625
  );
1622
1626
  }
1623
- console.log(`[ConfigRepo] addBackend: creating ${id} (${type})...`);
1627
+ log.log(`[ConfigRepo] addBackend: creating ${id} (${type})...`);
1624
1628
  const instance = await createBackend({ type, options });
1625
1629
  const syncable = backendToSyncableFS(instance, `${type}(${id})`);
1626
1630
  const desc = { id, type, options, description };
@@ -1636,7 +1640,7 @@ var ConfigRepo = class {
1636
1640
  this.invalidateTombstoneCache();
1637
1641
  await this.processTombstones();
1638
1642
  } catch (err) {
1639
- console.warn("[ConfigRepo] preSyncHook processTombstones failed:", err);
1643
+ log.warn("[ConfigRepo] preSyncHook processTombstones failed:", err);
1640
1644
  }
1641
1645
  },
1642
1646
  postSyncHook: async () => {
@@ -1645,7 +1649,7 @@ var ConfigRepo = class {
1645
1649
  await this.processTombstones();
1646
1650
  await this.updateTombstoneConfirmations();
1647
1651
  } catch (err) {
1648
- console.warn("[ConfigRepo] postSyncHook tombstone processing failed:", err);
1652
+ log.warn("[ConfigRepo] postSyncHook tombstone processing failed:", err);
1649
1653
  }
1650
1654
  }
1651
1655
  },
@@ -1656,7 +1660,7 @@ var ConfigRepo = class {
1656
1660
  this.handleConflict(event);
1657
1661
  };
1658
1662
  this.syncEngine.on(pair.pairId, "conflict", conflictHandler);
1659
- console.log(`[ConfigRepo] addBackend: ${id} (${type}) added, sync pair=${pair.pairId}`);
1663
+ log.log(`[ConfigRepo] addBackend: ${id} (${type}) added, sync pair=${pair.pairId}`);
1660
1664
  await this.syncMetaToReplicas();
1661
1665
  this.syncEngine.watch(pair.pairId);
1662
1666
  }
@@ -1679,13 +1683,13 @@ var ConfigRepo = class {
1679
1683
  await this.removeBackendDescriptor(id);
1680
1684
  }
1681
1685
  this.syncEngine.removePair(replica.pairId);
1682
- console.log(`[ConfigRepo] removeBackend: sync pair ${replica.pairId} removed`);
1686
+ log.log(`[ConfigRepo] removeBackend: sync pair ${replica.pairId} removed`);
1683
1687
  this.replicaBackends.delete(id);
1684
1688
  if (replica.instance?.dispose) {
1685
1689
  await replica.instance.dispose();
1686
1690
  }
1687
1691
  } else {
1688
- console.log(`[ConfigRepo] removeBackend: "${id}" not in replicaBackends, cleaning up descriptor only`);
1692
+ log.log(`[ConfigRepo] removeBackend: "${id}" not in replicaBackends, cleaning up descriptor only`);
1689
1693
  try {
1690
1694
  await this.deleteFile(descPath);
1691
1695
  } catch {
@@ -1693,7 +1697,7 @@ var ConfigRepo = class {
1693
1697
  }
1694
1698
  }
1695
1699
  this.schedulePostDeleteSync();
1696
- console.log(`[ConfigRepo] removeBackend: ${id} removed (tombstone written, remote cleaned)`);
1700
+ log.log(`[ConfigRepo] removeBackend: ${id} removed (tombstone written, remote cleaned)`);
1697
1701
  }
1698
1702
  // -----------------------------------------------------------------------
1699
1703
  // IConfigRepo — Group Type
@@ -1705,14 +1709,14 @@ var ConfigRepo = class {
1705
1709
  const existing = await this.cachedFS.readFile(GROUP_TYPE_FILE, "utf-8");
1706
1710
  const current = existing.trim();
1707
1711
  if (current && current !== type) {
1708
- console.warn(`[ConfigRepo] group-type already set to "${current}", ignoring request to set "${type}"`);
1712
+ log.warn(`[ConfigRepo] group-type already set to "${current}", ignoring request to set "${type}"`);
1709
1713
  return;
1710
1714
  }
1711
1715
  } catch {
1712
1716
  }
1713
1717
  await this.ensureDir(GROUP_TYPE_FILE);
1714
1718
  await this.cachedFS.writeFile(GROUP_TYPE_FILE, new TextEncoder().encode(type));
1715
- console.log(`[ConfigRepo] group-type set to "${type}"`);
1719
+ log.log(`[ConfigRepo] group-type set to "${type}"`);
1716
1720
  }
1717
1721
  /** Read the group-type marker. Returns null if not set. */
1718
1722
  async getGroupType() {
@@ -1753,7 +1757,7 @@ var ConfigRepo = class {
1753
1757
  if (this.appDataGroups.has(id)) {
1754
1758
  throw new Error(`App data group "${id}" already exists. Use removeAppDataGroup() first.`);
1755
1759
  }
1756
- console.log(`[ConfigRepo] createAppDataGroup: creating "${id}" with ${backends.length} backend(s)`);
1760
+ log.log(`[ConfigRepo] createAppDataGroup: creating "${id}" with ${backends.length} backend(s)`);
1757
1761
  const resolvedBackends = [];
1758
1762
  for (const desc of backends) {
1759
1763
  const mergedOptions = await this.resolveAppDataBackendOptions(desc);
@@ -1779,7 +1783,7 @@ var ConfigRepo = class {
1779
1783
  const version = await incrementVersion(this.fullFS, descPath, bytes, author);
1780
1784
  await this.writeVersionSidecar(descPath, version);
1781
1785
  this.appDataGroups.set(id, group);
1782
- console.log(`[ConfigRepo] createAppDataGroup: "${id}" created`);
1786
+ log.log(`[ConfigRepo] createAppDataGroup: "${id}" created`);
1783
1787
  return group;
1784
1788
  }
1785
1789
  async getAppDataGroup(id) {
@@ -1838,7 +1842,7 @@ var ConfigRepo = class {
1838
1842
  } catch {
1839
1843
  }
1840
1844
  await this.unlinkVersionSidecar(this.cachedFS, descPath);
1841
- console.log(`[ConfigRepo] removeAppDataGroup: "${id}" removed`);
1845
+ log.log(`[ConfigRepo] removeAppDataGroup: "${id}" removed`);
1842
1846
  }
1843
1847
  async listAccountBackends() {
1844
1848
  this.assertNotDisposed();
@@ -1903,15 +1907,15 @@ var AppDataGroupImpl = class {
1903
1907
  "/"
1904
1908
  );
1905
1909
  this.dataBackends.set(desc.id, { instance, syncable, pairId: pair.pairId, desc });
1906
- console.log(`[AppDataGroup:${this.groupId}] backend ${desc.id} (${desc.type}) connected, pair=${pair.pairId}`);
1910
+ log.log(`[AppDataGroup:${this.groupId}] backend ${desc.id} (${desc.type}) connected, pair=${pair.pairId}`);
1907
1911
  } catch (err) {
1908
- console.error(`[AppDataGroup:${this.groupId}] Failed to create backend ${desc.id} (${desc.type}):`, err);
1912
+ log.error(`[AppDataGroup:${this.groupId}] Failed to create backend ${desc.id} (${desc.type}):`, err);
1909
1913
  }
1910
1914
  }
1911
1915
  try {
1912
1916
  await this.syncEngine.syncAll();
1913
1917
  } catch (err) {
1914
- console.warn(`[AppDataGroup:${this.groupId}] Initial sync failed:`, err);
1918
+ log.warn(`[AppDataGroup:${this.groupId}] Initial sync failed:`, err);
1915
1919
  }
1916
1920
  this.syncEngine.watchAll();
1917
1921
  }
@@ -1927,7 +1931,7 @@ var AppDataGroupImpl = class {
1927
1931
  if (this.dataBackends.has(id)) {
1928
1932
  throw new Error(`Backend "${id}" already exists in data group "${this.groupId}"`);
1929
1933
  }
1930
- console.log(`[AppDataGroup:${this.groupId}] addBackend: creating ${id} (${type})...`);
1934
+ log.log(`[AppDataGroup:${this.groupId}] addBackend: creating ${id} (${type})...`);
1931
1935
  const instance = await createBackend({ type, options });
1932
1936
  const syncable = backendToSyncableFS(instance, `${type}(${id})`);
1933
1937
  const localSyncable = backendToSyncableFS(this.localFS, `local(${this.groupId})`);
@@ -1943,11 +1947,11 @@ var AppDataGroupImpl = class {
1943
1947
  );
1944
1948
  const desc = { id, type, options, description };
1945
1949
  this.dataBackends.set(id, { instance, syncable, pairId: pair.pairId, desc });
1946
- console.log(`[AppDataGroup:${this.groupId}] addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
1950
+ log.log(`[AppDataGroup:${this.groupId}] addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
1947
1951
  try {
1948
1952
  await this.syncEngine.sync(pair.pairId);
1949
1953
  } catch (err) {
1950
- console.warn(`[AppDataGroup:${this.groupId}] addBackend: initial sync failed for ${id}:`, err);
1954
+ log.warn(`[AppDataGroup:${this.groupId}] addBackend: initial sync failed for ${id}:`, err);
1951
1955
  }
1952
1956
  this.syncEngine.watch(pair.pairId);
1953
1957
  }
@@ -1962,7 +1966,7 @@ var AppDataGroupImpl = class {
1962
1966
  if (backend.instance?.dispose) {
1963
1967
  await backend.instance.dispose();
1964
1968
  }
1965
- console.log(`[AppDataGroup:${this.groupId}] removeBackend: ${id} removed`);
1969
+ log.log(`[AppDataGroup:${this.groupId}] removeBackend: ${id} removed`);
1966
1970
  }
1967
1971
  listBackends() {
1968
1972
  return Array.from(this.dataBackends.values()).map((b) => b.desc);
@@ -1988,7 +1992,7 @@ function toUint8Array(raw) {
1988
1992
  }
1989
1993
  async function createConfigRepo(appId, options = {}) {
1990
1994
  const idbStoreName = options.idbStoreName || `zen-fs-config-${appId}`;
1991
- console.log(`[createConfigRepo] Creating IndexedDB primary (store: ${idbStoreName})...`);
1995
+ log.log(`[createConfigRepo] Creating IndexedDB primary (store: ${idbStoreName})...`);
1992
1996
  const primaryInstance = await createBackend({
1993
1997
  type: "IndexedDB",
1994
1998
  options: { storeName: idbStoreName }
@@ -1997,12 +2001,12 @@ async function createConfigRepo(appId, options = {}) {
1997
2001
  const cacheOptions = options.cache === false ? void 0 : options.cache ?? {};
1998
2002
  try {
1999
2003
  await primaryInstance.mkdir(META_DIR);
2000
- console.log(`[createConfigRepo] /.meta/ ready`);
2004
+ log.log(`[createConfigRepo] /.meta/ ready`);
2001
2005
  } catch (err) {
2002
2006
  const msg = err.message || "";
2003
2007
  if (msg.includes("File exists") || msg.includes("EEXIST") || err.code === "EEXIST") {
2004
2008
  } else {
2005
- console.error(`[createConfigRepo] Failed to ensure /.meta/:`, err.message);
2009
+ log.error(`[createConfigRepo] Failed to ensure /.meta/:`, err.message);
2006
2010
  }
2007
2011
  }
2008
2012
  try {
@@ -2011,10 +2015,10 @@ async function createConfigRepo(appId, options = {}) {
2011
2015
  await primaryInstance.readFile(`${META_DIR}/group-type`);
2012
2016
  } catch {
2013
2017
  await primaryInstance.writeFile(`${META_DIR}/group-type`, groupTypeBytes);
2014
- console.log(`[createConfigRepo] group-type set to "config-sync"`);
2018
+ log.log(`[createConfigRepo] group-type set to "config-sync"`);
2015
2019
  }
2016
2020
  } catch (err) {
2017
- console.warn(`[createConfigRepo] Failed to write group-type:`, err.message);
2021
+ log.warn(`[createConfigRepo] Failed to write group-type:`, err.message);
2018
2022
  }
2019
2023
  const tempRepo = new ConfigRepo(
2020
2024
  appId,
@@ -2028,11 +2032,11 @@ async function createConfigRepo(appId, options = {}) {
2028
2032
  );
2029
2033
  const oldBackendsMeta = await tempRepo.readMetaFile(BACKENDS_FILE);
2030
2034
  if (oldBackendsMeta && oldBackendsMeta.backends?.length > 0) {
2031
- console.log(`[createConfigRepo] Migrating ${oldBackendsMeta.backends.length} backend(s) from backends.json to individual files...`);
2035
+ log.log(`[createConfigRepo] Migrating ${oldBackendsMeta.backends.length} backend(s) from backends.json to individual files...`);
2032
2036
  await tempRepo.ensureDir(`${BACKENDS_DIR}/.keep`);
2033
2037
  for (const desc of oldBackendsMeta.backends) {
2034
2038
  if (desc.id === LOCAL_IDB_BACKEND_ID || desc.type === "IndexedDB") {
2035
- console.log(`[createConfigRepo] Skipping local backend ${desc.id} during migration`);
2039
+ log.log(`[createConfigRepo] Skipping local backend ${desc.id} during migration`);
2036
2040
  continue;
2037
2041
  }
2038
2042
  await tempRepo.writeBackendDescriptor(desc);
@@ -2050,7 +2054,7 @@ async function createConfigRepo(appId, options = {}) {
2050
2054
  }
2051
2055
  }
2052
2056
  }
2053
- console.log(`[createConfigRepo] Migration complete`);
2057
+ log.log(`[createConfigRepo] Migration complete`);
2054
2058
  }
2055
2059
  let allBackends = await tempRepo.readAllBackendDescriptors();
2056
2060
  if (options.backendInfo) {
@@ -2068,19 +2072,19 @@ async function createConfigRepo(appId, options = {}) {
2068
2072
  type: options.backendInfo.type,
2069
2073
  options: options.backendInfo.options
2070
2074
  });
2071
- console.log(`[createConfigRepo] Added replica backend: ${replicaId} (${options.backendInfo.type})`);
2075
+ log.log(`[createConfigRepo] Added replica backend: ${replicaId} (${options.backendInfo.type})`);
2072
2076
  allBackends = [...allBackends, { id: replicaId, type: options.backendInfo.type, options: options.backendInfo.options }];
2073
2077
  } else if (dupConfig) {
2074
- console.log(`[createConfigRepo] Replica with same config already registered as "${dupConfig.id}", skipping`);
2078
+ log.log(`[createConfigRepo] Replica with same config already registered as "${dupConfig.id}", skipping`);
2075
2079
  } else {
2076
- console.log(`[createConfigRepo] Replica ${replicaId} already registered`);
2080
+ log.log(`[createConfigRepo] Replica ${replicaId} already registered`);
2077
2081
  }
2078
2082
  }
2079
- console.log(`[createConfigRepo] Replica backends: ${allBackends.map((b) => b.id).join(", ") || "(none)"}`);
2083
+ log.log(`[createConfigRepo] Replica backends: ${allBackends.map((b) => b.id).join(", ") || "(none)"}`);
2080
2084
  let nodeId = options.nodeId;
2081
2085
  if (!nodeId) {
2082
2086
  nodeId = `node-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
2083
- console.log(`[createConfigRepo] Generated nodeId: ${nodeId}`);
2087
+ log.log(`[createConfigRepo] Generated nodeId: ${nodeId}`);
2084
2088
  }
2085
2089
  const serializer = createSerializerChain(options.serializer);
2086
2090
  const repo = new ConfigRepo(
@@ -2096,11 +2100,11 @@ async function createConfigRepo(appId, options = {}) {
2096
2100
  await repo.setupSync(allBackends, LOCAL_IDB_BACKEND_ID, options.syncPollIntervalMs);
2097
2101
  await repo.load();
2098
2102
  if (repo.replicaCount > 0) {
2099
- console.log("[createConfigRepo] Starting background initial sync + dedup...");
2103
+ log.log("[createConfigRepo] Starting background initial sync + dedup...");
2100
2104
  repo.startBackgroundSync();
2101
2105
  }
2102
2106
  repo.syncMetaToReplicas().catch((err) => {
2103
- console.error("[createConfigRepo] background syncMetaToReplicas failed:", err);
2107
+ log.error("[createConfigRepo] background syncMetaToReplicas failed:", err);
2104
2108
  });
2105
2109
  return repo;
2106
2110
  }
@@ -2109,31 +2113,15 @@ async function createConfigRepo(appId, options = {}) {
2109
2113
  var import_zen_fs_sync2 = require("zen-fs-sync");
2110
2114
 
2111
2115
  // src/logger.ts
2112
- var isDebug = (() => {
2113
- if (typeof process !== "undefined" && process.env?.ZEN_FS_CONFIG_DEBUG) {
2114
- return true;
2115
- }
2116
- if (typeof localStorage !== "undefined") {
2117
- try {
2118
- return localStorage.getItem("ZEN_FS_CONFIG_DEBUG") === "1";
2119
- } catch {
2120
- return false;
2121
- }
2122
- }
2123
- return false;
2124
- })();
2125
- function createLogger(prefix) {
2126
- if (!isDebug) {
2127
- return () => {
2128
- };
2129
- }
2130
- return (...args) => {
2131
- console.log(`[${prefix}]`, ...args);
2132
- };
2116
+ var import_localstorage_logger2 = require("@richard432/localstorage-logger");
2117
+ var MODULE_PREFIX = "zen-fs-config";
2118
+ function createLogger2(prefix) {
2119
+ const logger = (0, import_localstorage_logger2.createLogger)(`${MODULE_PREFIX}:${prefix}`);
2120
+ return (...args) => logger.log(...args);
2133
2121
  }
2134
2122
 
2135
2123
  // src/data-sync-group.ts
2136
- var log = createLogger("data-sync-group");
2124
+ var log2 = createLogger2("data-sync-group");
2137
2125
  var META_DIR2 = "/.meta";
2138
2126
  var GROUP_TYPE_FILE2 = `${META_DIR2}/group-type`;
2139
2127
  var BACKENDS_DIR2 = `${META_DIR2}/backends`;
@@ -2160,7 +2148,7 @@ var DataSyncGroup = class {
2160
2148
  if (this.dataBackends.has(id)) {
2161
2149
  throw new Error(`Backend "${id}" already exists in data group "${this.groupId}"`);
2162
2150
  }
2163
- log(`addBackend: creating ${id} (${type})...`);
2151
+ log2(`addBackend: creating ${id} (${type})...`);
2164
2152
  const instance = await createBackend({ type, options });
2165
2153
  const syncable = backendToSyncableFS(instance, `${type}(${id})`);
2166
2154
  const localSyncable = backendToSyncableFS(this.localFS, `local(${this.groupId})`);
@@ -2175,12 +2163,12 @@ var DataSyncGroup = class {
2175
2163
  );
2176
2164
  this.dataBackends.set(id, { instance, syncable, pairId: pair.pairId, desc: { id, type, options, description } });
2177
2165
  this.syncEngine.watch(pair.pairId);
2178
- log(`addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
2166
+ log2(`addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
2179
2167
  await this.saveBackendDescriptor(id, type, options, description);
2180
2168
  try {
2181
2169
  await this.syncEngine.sync(pair.pairId);
2182
2170
  } catch (err) {
2183
- log(`addBackend: initial sync failed for ${id}:`, err);
2171
+ log2(`addBackend: initial sync failed for ${id}:`, err);
2184
2172
  }
2185
2173
  }
2186
2174
  /**
@@ -2201,7 +2189,7 @@ var DataSyncGroup = class {
2201
2189
  await this.localFS.unlink(`${BACKENDS_DIR2}/${id}.json`);
2202
2190
  } catch {
2203
2191
  }
2204
- log(`removeBackend: ${id} removed`);
2192
+ log2(`removeBackend: ${id} removed`);
2205
2193
  }
2206
2194
  getSyncStatuses() {
2207
2195
  return this.syncEngine.getStatusAll();
@@ -2223,7 +2211,7 @@ var DataSyncGroup = class {
2223
2211
  }
2224
2212
  }
2225
2213
  this.dataBackends.clear();
2226
- log(`disposed`);
2214
+ log2(`disposed`);
2227
2215
  }
2228
2216
  // -----------------------------------------------------------------------
2229
2217
  // Internal — used by the factory function
@@ -2281,7 +2269,7 @@ var DataSyncGroup = class {
2281
2269
  };
2282
2270
  async function createDataSyncGroup(appId, options = {}) {
2283
2271
  const groupId = `data-${appId}-${Date.now().toString(36)}`;
2284
- log(`createDataSyncGroup: appId=${appId} groupId=${groupId}`);
2272
+ log2(`createDataSyncGroup: appId=${appId} groupId=${groupId}`);
2285
2273
  const localFS = await createBackend({
2286
2274
  type: "InMemory",
2287
2275
  options: { label: `data-sync-${appId}-${Date.now()}` }
@@ -2311,7 +2299,7 @@ async function createDataSyncGroup(appId, options = {}) {
2311
2299
  new TextEncoder().encode("data-sync")
2312
2300
  );
2313
2301
  } catch (writeErr) {
2314
- log(`Could not write group-type on remote: ${writeErr.message}`);
2302
+ log2(`Could not write group-type on remote: ${writeErr.message}`);
2315
2303
  }
2316
2304
  }
2317
2305
  let remoteBackendDescs = [];
@@ -2356,15 +2344,15 @@ async function createDataSyncGroup(appId, options = {}) {
2356
2344
  const syncable = backendToSyncableFS(instance, `${rdesc.type}(${rdesc.id})`);
2357
2345
  group2._registerBackend(rdesc.id, instance, syncable, options.pollIntervalMs);
2358
2346
  } catch (err) {
2359
- log(`Failed to add remote backend ${rdesc.id}:`, err);
2347
+ log2(`Failed to add remote backend ${rdesc.id}:`, err);
2360
2348
  }
2361
2349
  }
2362
2350
  try {
2363
2351
  await group2._syncAll();
2364
2352
  } catch (err) {
2365
- log(`Initial sync failed:`, err);
2353
+ log2(`Initial sync failed:`, err);
2366
2354
  }
2367
- log(`createDataSyncGroup: ready with ${group2._backendCount} backend(s)`);
2355
+ log2(`createDataSyncGroup: ready with ${group2._backendCount} backend(s)`);
2368
2356
  return group2;
2369
2357
  }
2370
2358
  const group = new DataSyncGroup(appId, groupId, localFS);
@@ -2372,28 +2360,28 @@ async function createDataSyncGroup(appId, options = {}) {
2372
2360
  await localFS.writeFile(GROUP_TYPE_FILE2, new TextEncoder().encode("data-sync"));
2373
2361
  } catch {
2374
2362
  }
2375
- log(`createDataSyncGroup: local-only group created`);
2363
+ log2(`createDataSyncGroup: local-only group created`);
2376
2364
  return group;
2377
2365
  }
2378
2366
 
2379
2367
  // src/connect.ts
2380
- var log2 = createLogger("connect");
2368
+ var log3 = createLogger2("connect");
2381
2369
  var META_DIR3 = "/.meta";
2382
2370
  var GROUP_TYPE_FILE3 = `${META_DIR3}/group-type`;
2383
2371
  async function detectGroupType(type, options) {
2384
- log2(`detectGroupType: connecting to ${type}...`);
2372
+ log3(`detectGroupType: connecting to ${type}...`);
2385
2373
  const tempBackend = await createBackend({ type, options });
2386
2374
  try {
2387
2375
  const raw = await tempBackend.readFile(GROUP_TYPE_FILE3, "utf-8");
2388
2376
  const groupType = raw.trim();
2389
2377
  if (groupType === "config-sync" || groupType === "data-sync") {
2390
- log2(`detectGroupType: detected "${groupType}"`);
2378
+ log3(`detectGroupType: detected "${groupType}"`);
2391
2379
  return groupType;
2392
2380
  }
2393
- log2(`detectGroupType: unknown group-type value "${groupType}", treating as null`);
2381
+ log3(`detectGroupType: unknown group-type value "${groupType}", treating as null`);
2394
2382
  return null;
2395
2383
  } catch {
2396
- log2(`detectGroupType: no group-type file found (new backend)`);
2384
+ log3(`detectGroupType: no group-type file found (new backend)`);
2397
2385
  return null;
2398
2386
  } finally {
2399
2387
  if (tempBackend?.dispose) {
@@ -2402,10 +2390,10 @@ async function detectGroupType(type, options) {
2402
2390
  }
2403
2391
  }
2404
2392
  async function connect(appId, options = {}) {
2405
- log2(`connect: appId=${appId}`);
2393
+ log3(`connect: appId=${appId}`);
2406
2394
  if (!options.backendInfo) {
2407
2395
  const groupType2 = options.groupType ?? "config-sync";
2408
- log2(`connect: no backendInfo, using groupType="${groupType2}"`);
2396
+ log3(`connect: no backendInfo, using groupType="${groupType2}"`);
2409
2397
  if (groupType2 === "data-sync") {
2410
2398
  const dataGroup = await createDataSyncGroup(appId, {});
2411
2399
  return { groupType: "data-sync", dataGroup };
@@ -2429,7 +2417,7 @@ async function connect(appId, options = {}) {
2429
2417
  groupType = detectedType;
2430
2418
  } else {
2431
2419
  groupType = options.groupType ?? "config-sync";
2432
- log2(`connect: new backend, using groupType="${groupType}"`);
2420
+ log3(`connect: new backend, using groupType="${groupType}"`);
2433
2421
  }
2434
2422
  if (groupType === "data-sync") {
2435
2423
  const dataGroup = await createDataSyncGroup(appId, {