zen-fs-config 0.3.19 → 0.3.21

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 CHANGED
@@ -170,6 +170,11 @@ interface IConfigRepo {
170
170
  getSyncRules(): Promise<SyncRulesMeta | null>;
171
171
  /** Write .meta/sync-rules.json. */
172
172
  updateSyncRules(meta: SyncRulesMeta): Promise<void>;
173
+ /**
174
+ * Sync .meta/ files (backends.json, sync-rules.json) to all replica backends.
175
+ * Called automatically by createConfigRepo() after setupSync().
176
+ */
177
+ syncMetaToReplicas(): Promise<void>;
173
178
  /** Dispose: stop all sync, release cache FS and resources. */
174
179
  dispose(): Promise<void>;
175
180
  }
@@ -254,6 +259,16 @@ declare class ConfigRepo implements IConfigRepo {
254
259
  }): Promise<SyncResult>;
255
260
  peekNodeConfig<T = unknown>(nodeId: string, path: string): Promise<T>;
256
261
  flush(): Promise<SyncResult[]>;
262
+ /**
263
+ * Sync .meta/ files (backends.json, sync-rules.json) to all replica backends.
264
+ *
265
+ * This ensures the backend topology is available on every replica, enabling
266
+ * any program that connects to any backend to discover the full topology.
267
+ *
268
+ * Called automatically by createConfigRepo() after setupSync().
269
+ * Can also be called manually after updateBackends() / updateSyncRules().
270
+ */
271
+ syncMetaToReplicas(): Promise<void>;
257
272
  getSyncStatuses(): Map<string, SyncPairStatus>;
258
273
  resolveConflict(conflictId: string, mergedContent: unknown): Promise<void>;
259
274
  listConflicts(): Promise<ConflictArchive[]>;
package/dist/index.d.ts CHANGED
@@ -170,6 +170,11 @@ interface IConfigRepo {
170
170
  getSyncRules(): Promise<SyncRulesMeta | null>;
171
171
  /** Write .meta/sync-rules.json. */
172
172
  updateSyncRules(meta: SyncRulesMeta): Promise<void>;
173
+ /**
174
+ * Sync .meta/ files (backends.json, sync-rules.json) to all replica backends.
175
+ * Called automatically by createConfigRepo() after setupSync().
176
+ */
177
+ syncMetaToReplicas(): Promise<void>;
173
178
  /** Dispose: stop all sync, release cache FS and resources. */
174
179
  dispose(): Promise<void>;
175
180
  }
@@ -254,6 +259,16 @@ declare class ConfigRepo implements IConfigRepo {
254
259
  }): Promise<SyncResult>;
255
260
  peekNodeConfig<T = unknown>(nodeId: string, path: string): Promise<T>;
256
261
  flush(): Promise<SyncResult[]>;
262
+ /**
263
+ * Sync .meta/ files (backends.json, sync-rules.json) to all replica backends.
264
+ *
265
+ * This ensures the backend topology is available on every replica, enabling
266
+ * any program that connects to any backend to discover the full topology.
267
+ *
268
+ * Called automatically by createConfigRepo() after setupSync().
269
+ * Can also be called manually after updateBackends() / updateSyncRules().
270
+ */
271
+ syncMetaToReplicas(): Promise<void>;
257
272
  getSyncStatuses(): Map<string, SyncPairStatus>;
258
273
  resolveConflict(conflictId: string, mergedContent: unknown): Promise<void>;
259
274
  listConflicts(): Promise<ConflictArchive[]>;
package/dist/index.js CHANGED
@@ -665,6 +665,35 @@ var ConfigRepo = class {
665
665
  const resultsMap = await this.syncEngine.syncAll();
666
666
  return Array.from(resultsMap.values());
667
667
  }
668
+ /**
669
+ * Sync .meta/ files (backends.json, sync-rules.json) to all replica backends.
670
+ *
671
+ * This ensures the backend topology is available on every replica, enabling
672
+ * any program that connects to any backend to discover the full topology.
673
+ *
674
+ * Called automatically by createConfigRepo() after setupSync().
675
+ * Can also be called manually after updateBackends() / updateSyncRules().
676
+ */
677
+ async syncMetaToReplicas() {
678
+ this.assertNotDisposed();
679
+ for (const metaFile of [BACKENDS_FILE, SYNC_RULES_FILE]) {
680
+ try {
681
+ const content = await this.cachedFS.readFile(metaFile);
682
+ const vPath = versionPathFor(metaFile);
683
+ const vContent = await this.cachedFS.readFile(vPath);
684
+ for (const [id, replica] of this.replicaBackends) {
685
+ try {
686
+ await replica.syncable.writeFile(metaFile, content);
687
+ await replica.syncable.writeFile(vPath, vContent);
688
+ console.log(`[ConfigRepo] Synced ${metaFile} + .version to replica ${id}`);
689
+ } catch (err) {
690
+ console.error(`[ConfigRepo] Failed to sync ${metaFile} to ${id}:`, err.message);
691
+ }
692
+ }
693
+ } catch {
694
+ }
695
+ }
696
+ }
668
697
  getSyncStatuses() {
669
698
  this.assertNotDisposed();
670
699
  return this.syncEngine.getStatusAll();
@@ -901,12 +930,14 @@ var ConfigRepo = class {
901
930
  async writeMetaFile(path, data) {
902
931
  console.log(`[writeMetaFile] ${path}, ensuring dir...`);
903
932
  await this.ensureDir(path);
904
- console.log(`[writeMetaFile] ${path}, writing ${JSON.stringify(data).length} bytes...`);
905
- await this.cachedFS.writeFile(
906
- path,
907
- new TextEncoder().encode(JSON.stringify(data, null, 2))
908
- );
909
- console.log(`[writeMetaFile] ${path} done`);
933
+ const bytes = new TextEncoder().encode(JSON.stringify(data, null, 2));
934
+ console.log(`[writeMetaFile] ${path}, writing ${bytes.length} bytes...`);
935
+ await this.cachedFS.writeFile(path, bytes);
936
+ const author = `${this.appId}/${this.nodeId}`;
937
+ const version = await incrementVersion(this.fullFS, path, bytes, author);
938
+ await this.ensureDir(versionPathFor(path));
939
+ await writeVersion(this.fullFS, versionPathFor(path), version);
940
+ console.log(`[writeMetaFile] ${path} done (version=${version.version})`);
910
941
  }
911
942
  async readMetaFile(path) {
912
943
  try {
@@ -1052,7 +1083,12 @@ async function createConfigRepo(appId, options) {
1052
1083
  replicas: backendsMeta.backends.map((b) => b.id)
1053
1084
  },
1054
1085
  { prefix: `${NODES_DIR}/`, direction: "none" },
1055
- { prefix: `${META_DIR}/`, direction: "none" }
1086
+ {
1087
+ prefix: `${META_DIR}/`,
1088
+ direction: "one-way",
1089
+ conflictStrategy: "source-wins",
1090
+ replicas: backendsMeta.backends.map((b) => b.id)
1091
+ }
1056
1092
  ]
1057
1093
  };
1058
1094
  }
@@ -1094,6 +1130,7 @@ async function createConfigRepo(appId, options) {
1094
1130
  backendsMeta.backends,
1095
1131
  options.primaryBackendId
1096
1132
  );
1133
+ await repo.syncMetaToReplicas();
1097
1134
  await repo.load();
1098
1135
  return repo;
1099
1136
  }
package/dist/index.mjs CHANGED
@@ -616,6 +616,35 @@ var ConfigRepo = class {
616
616
  const resultsMap = await this.syncEngine.syncAll();
617
617
  return Array.from(resultsMap.values());
618
618
  }
619
+ /**
620
+ * Sync .meta/ files (backends.json, sync-rules.json) to all replica backends.
621
+ *
622
+ * This ensures the backend topology is available on every replica, enabling
623
+ * any program that connects to any backend to discover the full topology.
624
+ *
625
+ * Called automatically by createConfigRepo() after setupSync().
626
+ * Can also be called manually after updateBackends() / updateSyncRules().
627
+ */
628
+ async syncMetaToReplicas() {
629
+ this.assertNotDisposed();
630
+ for (const metaFile of [BACKENDS_FILE, SYNC_RULES_FILE]) {
631
+ try {
632
+ const content = await this.cachedFS.readFile(metaFile);
633
+ const vPath = versionPathFor(metaFile);
634
+ const vContent = await this.cachedFS.readFile(vPath);
635
+ for (const [id, replica] of this.replicaBackends) {
636
+ try {
637
+ await replica.syncable.writeFile(metaFile, content);
638
+ await replica.syncable.writeFile(vPath, vContent);
639
+ console.log(`[ConfigRepo] Synced ${metaFile} + .version to replica ${id}`);
640
+ } catch (err) {
641
+ console.error(`[ConfigRepo] Failed to sync ${metaFile} to ${id}:`, err.message);
642
+ }
643
+ }
644
+ } catch {
645
+ }
646
+ }
647
+ }
619
648
  getSyncStatuses() {
620
649
  this.assertNotDisposed();
621
650
  return this.syncEngine.getStatusAll();
@@ -852,12 +881,14 @@ var ConfigRepo = class {
852
881
  async writeMetaFile(path, data) {
853
882
  console.log(`[writeMetaFile] ${path}, ensuring dir...`);
854
883
  await this.ensureDir(path);
855
- console.log(`[writeMetaFile] ${path}, writing ${JSON.stringify(data).length} bytes...`);
856
- await this.cachedFS.writeFile(
857
- path,
858
- new TextEncoder().encode(JSON.stringify(data, null, 2))
859
- );
860
- console.log(`[writeMetaFile] ${path} done`);
884
+ const bytes = new TextEncoder().encode(JSON.stringify(data, null, 2));
885
+ console.log(`[writeMetaFile] ${path}, writing ${bytes.length} bytes...`);
886
+ await this.cachedFS.writeFile(path, bytes);
887
+ const author = `${this.appId}/${this.nodeId}`;
888
+ const version = await incrementVersion(this.fullFS, path, bytes, author);
889
+ await this.ensureDir(versionPathFor(path));
890
+ await writeVersion(this.fullFS, versionPathFor(path), version);
891
+ console.log(`[writeMetaFile] ${path} done (version=${version.version})`);
861
892
  }
862
893
  async readMetaFile(path) {
863
894
  try {
@@ -1003,7 +1034,12 @@ async function createConfigRepo(appId, options) {
1003
1034
  replicas: backendsMeta.backends.map((b) => b.id)
1004
1035
  },
1005
1036
  { prefix: `${NODES_DIR}/`, direction: "none" },
1006
- { prefix: `${META_DIR}/`, direction: "none" }
1037
+ {
1038
+ prefix: `${META_DIR}/`,
1039
+ direction: "one-way",
1040
+ conflictStrategy: "source-wins",
1041
+ replicas: backendsMeta.backends.map((b) => b.id)
1042
+ }
1007
1043
  ]
1008
1044
  };
1009
1045
  }
@@ -1045,6 +1081,7 @@ async function createConfigRepo(appId, options) {
1045
1081
  backendsMeta.backends,
1046
1082
  options.primaryBackendId
1047
1083
  );
1084
+ await repo.syncMetaToReplicas();
1048
1085
  await repo.load();
1049
1086
  return repo;
1050
1087
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zen-fs-config",
3
- "version": "0.3.19",
3
+ "version": "0.3.21",
4
4
  "description": "Distributed config management library built on ZenFS, zen-fs-cache, and zen-fs-sync",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",