zen-fs-config 0.5.45 → 0.5.46

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
@@ -345,8 +345,16 @@ interface AppDataBackendDescriptor {
345
345
  interface AppDataGroupDescriptor {
346
346
  id: string;
347
347
  groupType: 'data-sync';
348
+ /** User-editable display name. `id` is the stable identifier. */
349
+ name?: string;
348
350
  backends: AppDataBackendDescriptor[];
349
351
  }
352
+ /** Descriptor for the config-sync group itself (display metadata only). */
353
+ interface ConfigSyncGroupDescriptor {
354
+ groupType: 'config-sync';
355
+ /** User-editable display name. The group has no separate id. */
356
+ name?: string;
357
+ }
350
358
  /** Per-backend sync state of a data group. */
351
359
  interface BackendSyncState {
352
360
  backendId: string;
@@ -555,6 +563,20 @@ interface IConfigRepo {
555
563
  getAppDataGroup(id: string): Promise<AppDataGroup>;
556
564
  /** List all data-sync group descriptors for this app. */
557
565
  listAppDataGroups(): Promise<AppDataGroupDescriptor[]>;
566
+ /**
567
+ * Update a data-sync group's display name (descriptor-only; id, backends,
568
+ * local and remote data are untouched). Empty/oversized names are rejected.
569
+ */
570
+ updateAppDataGroupName(id: string, name: string): Promise<void>;
571
+ /** Read the config-sync group's display descriptor (may be absent). */
572
+ getConfigSyncGroupDescriptor(): Promise<ConfigSyncGroupDescriptor | null>;
573
+ /**
574
+ * Update the config-sync group's display name (descriptor-only; backends,
575
+ * local store and remote data are untouched).
576
+ */
577
+ updateConfigSyncGroupDescriptor(patch: {
578
+ name: string;
579
+ }): Promise<ConfigSyncGroupDescriptor>;
558
580
  /** Remove a data-sync group (stops sync, removes descriptor). */
559
581
  removeAppDataGroup(id: string): Promise<void>;
560
582
  /**
@@ -1005,6 +1027,20 @@ declare class ConfigRepo implements IConfigRepo {
1005
1027
  * authoritative source of the data group's backend topology (FR2.3).
1006
1028
  */
1007
1029
  updateAppDataGroupDescriptor(id: string, backends: AppDataBackendDescriptor[]): Promise<void>;
1030
+ /**
1031
+ * Change a data-sync group's display name only. `id`, backends, local stores
1032
+ * and remote data are untouched, so existing sync pairs keep working.
1033
+ */
1034
+ updateAppDataGroupName(id: string, name: string): Promise<void>;
1035
+ getConfigSyncGroupDescriptor(): Promise<ConfigSyncGroupDescriptor | null>;
1036
+ /**
1037
+ * Change the config-sync group's display name only. Backends, the local
1038
+ * store and remote data are untouched.
1039
+ */
1040
+ updateConfigSyncGroupDescriptor(patch: {
1041
+ name: string;
1042
+ }): Promise<ConfigSyncGroupDescriptor>;
1043
+ private readAppDataGroupDescriptor;
1008
1044
  getAppDataGroup(id: string): Promise<AppDataGroup>;
1009
1045
  listAppDataGroups(): Promise<AppDataGroupDescriptor[]>;
1010
1046
  removeAppDataGroup(id: string): Promise<void>;
@@ -1160,4 +1196,4 @@ declare function incrementVersion(fs: SyncableFS, configFilePath: string, newCon
1160
1196
  */
1161
1197
  declare function verifyOrRepairVersion(fs: SyncableFS, configFilePath: string, author: string): Promise<VersionMeta | null>;
1162
1198
 
1163
- export { type AppDataBackendDescriptor, type AppDataGroup, type AppDataGroupDescriptor, type BackendDescriptor, type BackendFactory, type BackendInstance, type BackendMetadata, type BackendParamDef, type BackendSyncState, type BackendsMeta, type CacheOptions, ConfigRepo, type ConfigRepoOptions, type ConfigSerializer, type ConflictArchive, type ConflictInfo, type ConnectOptions, type ConnectResult, FolderStore, type IConfigRepo, type KeepPurgeResult, LOCAL_IDB_BACKEND_ID, type MigrateLocalFS, type MigrateReplicaFS, type MtimePurgeResult, type PurgeKeepOptions, type PurgeMtimeOptions, type PurgeableFS$1 as PurgeableFS, type SyncGroupType, type TombstoneMeta, type VersionMeta, type VersionMigrationOptions, type VersionMigrationResult, configKeyToFilePath, connect, createBackend, createConfigRepo, createSerializerChain, getAccountFields, getBackendMetadata, getExtension, hasBackend, incrementVersion, isBrowserEnv, isKeepFile, isMtimeSidecar, legacyVersionPathFor, listBackendMetadata, listBackends, localPrimaryType, mergeAccountFields, migrateVersionSidecars, purgeKeepFiles, purgeMtimeSidecars, readVersion, registerBackend, registerFolderBackend, resolveLocalPrimary, sha256, unregisterBackend, verifyOrRepairVersion, versionPathFor, wrapZenFSFileSystem, writeVersion };
1199
+ export { type AppDataBackendDescriptor, type AppDataGroup, type AppDataGroupDescriptor, type BackendDescriptor, type BackendFactory, type BackendInstance, type BackendMetadata, type BackendParamDef, type BackendSyncState, type BackendsMeta, type CacheOptions, ConfigRepo, type ConfigRepoOptions, type ConfigSerializer, type ConfigSyncGroupDescriptor, type ConflictArchive, type ConflictInfo, type ConnectOptions, type ConnectResult, FolderStore, type IConfigRepo, type KeepPurgeResult, LOCAL_IDB_BACKEND_ID, type MigrateLocalFS, type MigrateReplicaFS, type MtimePurgeResult, type PurgeKeepOptions, type PurgeMtimeOptions, type PurgeableFS$1 as PurgeableFS, type SyncGroupType, type TombstoneMeta, type VersionMeta, type VersionMigrationOptions, type VersionMigrationResult, configKeyToFilePath, connect, createBackend, createConfigRepo, createSerializerChain, getAccountFields, getBackendMetadata, getExtension, hasBackend, incrementVersion, isBrowserEnv, isKeepFile, isMtimeSidecar, legacyVersionPathFor, listBackendMetadata, listBackends, localPrimaryType, mergeAccountFields, migrateVersionSidecars, purgeKeepFiles, purgeMtimeSidecars, readVersion, registerBackend, registerFolderBackend, resolveLocalPrimary, sha256, unregisterBackend, verifyOrRepairVersion, versionPathFor, wrapZenFSFileSystem, writeVersion };
package/dist/index.d.ts CHANGED
@@ -345,8 +345,16 @@ interface AppDataBackendDescriptor {
345
345
  interface AppDataGroupDescriptor {
346
346
  id: string;
347
347
  groupType: 'data-sync';
348
+ /** User-editable display name. `id` is the stable identifier. */
349
+ name?: string;
348
350
  backends: AppDataBackendDescriptor[];
349
351
  }
352
+ /** Descriptor for the config-sync group itself (display metadata only). */
353
+ interface ConfigSyncGroupDescriptor {
354
+ groupType: 'config-sync';
355
+ /** User-editable display name. The group has no separate id. */
356
+ name?: string;
357
+ }
350
358
  /** Per-backend sync state of a data group. */
351
359
  interface BackendSyncState {
352
360
  backendId: string;
@@ -555,6 +563,20 @@ interface IConfigRepo {
555
563
  getAppDataGroup(id: string): Promise<AppDataGroup>;
556
564
  /** List all data-sync group descriptors for this app. */
557
565
  listAppDataGroups(): Promise<AppDataGroupDescriptor[]>;
566
+ /**
567
+ * Update a data-sync group's display name (descriptor-only; id, backends,
568
+ * local and remote data are untouched). Empty/oversized names are rejected.
569
+ */
570
+ updateAppDataGroupName(id: string, name: string): Promise<void>;
571
+ /** Read the config-sync group's display descriptor (may be absent). */
572
+ getConfigSyncGroupDescriptor(): Promise<ConfigSyncGroupDescriptor | null>;
573
+ /**
574
+ * Update the config-sync group's display name (descriptor-only; backends,
575
+ * local store and remote data are untouched).
576
+ */
577
+ updateConfigSyncGroupDescriptor(patch: {
578
+ name: string;
579
+ }): Promise<ConfigSyncGroupDescriptor>;
558
580
  /** Remove a data-sync group (stops sync, removes descriptor). */
559
581
  removeAppDataGroup(id: string): Promise<void>;
560
582
  /**
@@ -1005,6 +1027,20 @@ declare class ConfigRepo implements IConfigRepo {
1005
1027
  * authoritative source of the data group's backend topology (FR2.3).
1006
1028
  */
1007
1029
  updateAppDataGroupDescriptor(id: string, backends: AppDataBackendDescriptor[]): Promise<void>;
1030
+ /**
1031
+ * Change a data-sync group's display name only. `id`, backends, local stores
1032
+ * and remote data are untouched, so existing sync pairs keep working.
1033
+ */
1034
+ updateAppDataGroupName(id: string, name: string): Promise<void>;
1035
+ getConfigSyncGroupDescriptor(): Promise<ConfigSyncGroupDescriptor | null>;
1036
+ /**
1037
+ * Change the config-sync group's display name only. Backends, the local
1038
+ * store and remote data are untouched.
1039
+ */
1040
+ updateConfigSyncGroupDescriptor(patch: {
1041
+ name: string;
1042
+ }): Promise<ConfigSyncGroupDescriptor>;
1043
+ private readAppDataGroupDescriptor;
1008
1044
  getAppDataGroup(id: string): Promise<AppDataGroup>;
1009
1045
  listAppDataGroups(): Promise<AppDataGroupDescriptor[]>;
1010
1046
  removeAppDataGroup(id: string): Promise<void>;
@@ -1160,4 +1196,4 @@ declare function incrementVersion(fs: SyncableFS, configFilePath: string, newCon
1160
1196
  */
1161
1197
  declare function verifyOrRepairVersion(fs: SyncableFS, configFilePath: string, author: string): Promise<VersionMeta | null>;
1162
1198
 
1163
- export { type AppDataBackendDescriptor, type AppDataGroup, type AppDataGroupDescriptor, type BackendDescriptor, type BackendFactory, type BackendInstance, type BackendMetadata, type BackendParamDef, type BackendSyncState, type BackendsMeta, type CacheOptions, ConfigRepo, type ConfigRepoOptions, type ConfigSerializer, type ConflictArchive, type ConflictInfo, type ConnectOptions, type ConnectResult, FolderStore, type IConfigRepo, type KeepPurgeResult, LOCAL_IDB_BACKEND_ID, type MigrateLocalFS, type MigrateReplicaFS, type MtimePurgeResult, type PurgeKeepOptions, type PurgeMtimeOptions, type PurgeableFS$1 as PurgeableFS, type SyncGroupType, type TombstoneMeta, type VersionMeta, type VersionMigrationOptions, type VersionMigrationResult, configKeyToFilePath, connect, createBackend, createConfigRepo, createSerializerChain, getAccountFields, getBackendMetadata, getExtension, hasBackend, incrementVersion, isBrowserEnv, isKeepFile, isMtimeSidecar, legacyVersionPathFor, listBackendMetadata, listBackends, localPrimaryType, mergeAccountFields, migrateVersionSidecars, purgeKeepFiles, purgeMtimeSidecars, readVersion, registerBackend, registerFolderBackend, resolveLocalPrimary, sha256, unregisterBackend, verifyOrRepairVersion, versionPathFor, wrapZenFSFileSystem, writeVersion };
1199
+ export { type AppDataBackendDescriptor, type AppDataGroup, type AppDataGroupDescriptor, type BackendDescriptor, type BackendFactory, type BackendInstance, type BackendMetadata, type BackendParamDef, type BackendSyncState, type BackendsMeta, type CacheOptions, ConfigRepo, type ConfigRepoOptions, type ConfigSerializer, type ConfigSyncGroupDescriptor, type ConflictArchive, type ConflictInfo, type ConnectOptions, type ConnectResult, FolderStore, type IConfigRepo, type KeepPurgeResult, LOCAL_IDB_BACKEND_ID, type MigrateLocalFS, type MigrateReplicaFS, type MtimePurgeResult, type PurgeKeepOptions, type PurgeMtimeOptions, type PurgeableFS$1 as PurgeableFS, type SyncGroupType, type TombstoneMeta, type VersionMeta, type VersionMigrationOptions, type VersionMigrationResult, configKeyToFilePath, connect, createBackend, createConfigRepo, createSerializerChain, getAccountFields, getBackendMetadata, getExtension, hasBackend, incrementVersion, isBrowserEnv, isKeepFile, isMtimeSidecar, legacyVersionPathFor, listBackendMetadata, listBackends, localPrimaryType, mergeAccountFields, migrateVersionSidecars, purgeKeepFiles, purgeMtimeSidecars, readVersion, registerBackend, registerFolderBackend, resolveLocalPrimary, sha256, unregisterBackend, verifyOrRepairVersion, versionPathFor, wrapZenFSFileSystem, writeVersion };
package/dist/index.js CHANGED
@@ -1424,10 +1424,25 @@ var GROUP_TYPE_FILE = `${META_DIR}/group-type`;
1424
1424
  var BACKENDS_FILE = `${META_DIR}/backends.json`;
1425
1425
  var BACKENDS_DIR = `${META_DIR}/backends`;
1426
1426
  var APP_DATA_GROUPS_DIR = `${META_DIR}/app-data-groups`;
1427
+ var CONFIG_SYNC_GROUP_FILE = `${META_DIR}/config-sync-group.json`;
1428
+ var GROUP_NAME_MAX_LENGTH = 40;
1427
1429
  var CONFLICTS_DIR = `${META_DIR}/.conflicts`;
1428
1430
  var DELETIONS_DIR = `${META_DIR}/.deleted`;
1429
1431
  var NODES_DIR = "/nodes";
1430
1432
  var SHARED_DIR = "/shared";
1433
+ function normalizeGroupName(value) {
1434
+ if (typeof value !== "string") {
1435
+ throw new Error("Group name must be a string");
1436
+ }
1437
+ const name = value.trim();
1438
+ if (!name) {
1439
+ throw new Error("Group name must not be empty");
1440
+ }
1441
+ if (name.length > GROUP_NAME_MAX_LENGTH) {
1442
+ throw new Error(`Group name must be at most ${GROUP_NAME_MAX_LENGTH} characters`);
1443
+ }
1444
+ return name;
1445
+ }
1431
1446
  var CONFIG_ONLY_PREFIXES = [META_DIR, NODES_DIR, SHARED_DIR];
1432
1447
  var LOCAL_IDB_BACKEND_ID = "local-idb";
1433
1448
  function tombstoneFileName(filePath) {
@@ -2783,9 +2798,11 @@ var ConfigRepo = class {
2783
2798
  */
2784
2799
  async updateAppDataGroupDescriptor(id, backends) {
2785
2800
  this.assertNotDisposed();
2801
+ const existing = await this.readAppDataGroupDescriptor(id);
2786
2802
  const descriptor = {
2787
2803
  id,
2788
2804
  groupType: "data-sync",
2805
+ ...existing?.name ? { name: existing.name } : {},
2789
2806
  backends
2790
2807
  };
2791
2808
  const descPath = this.appDataGroupFilePath(id);
@@ -2796,6 +2813,73 @@ var ConfigRepo = class {
2796
2813
  const version = await incrementVersion(this.fullFS, descPath, bytes, author);
2797
2814
  await this.writeVersionSidecar(descPath, version);
2798
2815
  }
2816
+ /**
2817
+ * Change a data-sync group's display name only. `id`, backends, local stores
2818
+ * and remote data are untouched, so existing sync pairs keep working.
2819
+ */
2820
+ async updateAppDataGroupName(id, name) {
2821
+ this.assertNotDisposed();
2822
+ const normalized = normalizeGroupName(name);
2823
+ const existing = await this.readAppDataGroupDescriptor(id);
2824
+ if (!existing) {
2825
+ throw new Error(`App data group "${id}" not found`);
2826
+ }
2827
+ const descriptor = {
2828
+ ...existing,
2829
+ id,
2830
+ groupType: "data-sync",
2831
+ name: normalized
2832
+ };
2833
+ const descPath = this.appDataGroupFilePath(id);
2834
+ await this.ensureDir(descPath);
2835
+ const bytes = new TextEncoder().encode(JSON.stringify(descriptor, null, 2));
2836
+ await this.cachedFS.writeFile(descPath, bytes);
2837
+ const author = `${this.appId}/${this.nodeId}`;
2838
+ const version = await incrementVersion(this.fullFS, descPath, bytes, author);
2839
+ await this.writeVersionSidecar(descPath, version);
2840
+ }
2841
+ async getConfigSyncGroupDescriptor() {
2842
+ this.assertNotDisposed();
2843
+ try {
2844
+ const raw = await this.cachedFS.readFile(CONFIG_SYNC_GROUP_FILE);
2845
+ const parsed = JSON.parse(new TextDecoder().decode(toUint8Array2(raw)));
2846
+ if (parsed && parsed.groupType === "config-sync") {
2847
+ return parsed;
2848
+ }
2849
+ } catch {
2850
+ }
2851
+ return null;
2852
+ }
2853
+ /**
2854
+ * Change the config-sync group's display name only. Backends, the local
2855
+ * store and remote data are untouched.
2856
+ */
2857
+ async updateConfigSyncGroupDescriptor(patch) {
2858
+ this.assertNotDisposed();
2859
+ const normalized = normalizeGroupName(patch?.name);
2860
+ const descriptor = {
2861
+ groupType: "config-sync",
2862
+ name: normalized
2863
+ };
2864
+ await this.ensureDir(CONFIG_SYNC_GROUP_FILE);
2865
+ const bytes = new TextEncoder().encode(JSON.stringify(descriptor, null, 2));
2866
+ await this.cachedFS.writeFile(CONFIG_SYNC_GROUP_FILE, bytes);
2867
+ const author = `${this.appId}/${this.nodeId}`;
2868
+ const version = await incrementVersion(this.fullFS, CONFIG_SYNC_GROUP_FILE, bytes, author);
2869
+ await this.writeVersionSidecar(CONFIG_SYNC_GROUP_FILE, version);
2870
+ return descriptor;
2871
+ }
2872
+ async readAppDataGroupDescriptor(id) {
2873
+ try {
2874
+ const raw = await this.cachedFS.readFile(this.appDataGroupFilePath(id));
2875
+ const parsed = JSON.parse(new TextDecoder().decode(toUint8Array2(raw)));
2876
+ if (parsed && parsed.id === id && parsed.groupType === "data-sync") {
2877
+ return parsed;
2878
+ }
2879
+ } catch {
2880
+ }
2881
+ return null;
2882
+ }
2799
2883
  async getAppDataGroup(id) {
2800
2884
  this.assertNotDisposed();
2801
2885
  const cached = this.appDataGroups.get(id);
package/dist/index.mjs CHANGED
@@ -884,10 +884,25 @@ var GROUP_TYPE_FILE = `${META_DIR}/group-type`;
884
884
  var BACKENDS_FILE = `${META_DIR}/backends.json`;
885
885
  var BACKENDS_DIR = `${META_DIR}/backends`;
886
886
  var APP_DATA_GROUPS_DIR = `${META_DIR}/app-data-groups`;
887
+ var CONFIG_SYNC_GROUP_FILE = `${META_DIR}/config-sync-group.json`;
888
+ var GROUP_NAME_MAX_LENGTH = 40;
887
889
  var CONFLICTS_DIR = `${META_DIR}/.conflicts`;
888
890
  var DELETIONS_DIR = `${META_DIR}/.deleted`;
889
891
  var NODES_DIR = "/nodes";
890
892
  var SHARED_DIR = "/shared";
893
+ function normalizeGroupName(value) {
894
+ if (typeof value !== "string") {
895
+ throw new Error("Group name must be a string");
896
+ }
897
+ const name = value.trim();
898
+ if (!name) {
899
+ throw new Error("Group name must not be empty");
900
+ }
901
+ if (name.length > GROUP_NAME_MAX_LENGTH) {
902
+ throw new Error(`Group name must be at most ${GROUP_NAME_MAX_LENGTH} characters`);
903
+ }
904
+ return name;
905
+ }
891
906
  var CONFIG_ONLY_PREFIXES = [META_DIR, NODES_DIR, SHARED_DIR];
892
907
  var LOCAL_IDB_BACKEND_ID = "local-idb";
893
908
  function tombstoneFileName(filePath) {
@@ -2243,9 +2258,11 @@ var ConfigRepo = class {
2243
2258
  */
2244
2259
  async updateAppDataGroupDescriptor(id, backends) {
2245
2260
  this.assertNotDisposed();
2261
+ const existing = await this.readAppDataGroupDescriptor(id);
2246
2262
  const descriptor = {
2247
2263
  id,
2248
2264
  groupType: "data-sync",
2265
+ ...existing?.name ? { name: existing.name } : {},
2249
2266
  backends
2250
2267
  };
2251
2268
  const descPath = this.appDataGroupFilePath(id);
@@ -2256,6 +2273,73 @@ var ConfigRepo = class {
2256
2273
  const version = await incrementVersion(this.fullFS, descPath, bytes, author);
2257
2274
  await this.writeVersionSidecar(descPath, version);
2258
2275
  }
2276
+ /**
2277
+ * Change a data-sync group's display name only. `id`, backends, local stores
2278
+ * and remote data are untouched, so existing sync pairs keep working.
2279
+ */
2280
+ async updateAppDataGroupName(id, name) {
2281
+ this.assertNotDisposed();
2282
+ const normalized = normalizeGroupName(name);
2283
+ const existing = await this.readAppDataGroupDescriptor(id);
2284
+ if (!existing) {
2285
+ throw new Error(`App data group "${id}" not found`);
2286
+ }
2287
+ const descriptor = {
2288
+ ...existing,
2289
+ id,
2290
+ groupType: "data-sync",
2291
+ name: normalized
2292
+ };
2293
+ const descPath = this.appDataGroupFilePath(id);
2294
+ await this.ensureDir(descPath);
2295
+ const bytes = new TextEncoder().encode(JSON.stringify(descriptor, null, 2));
2296
+ await this.cachedFS.writeFile(descPath, bytes);
2297
+ const author = `${this.appId}/${this.nodeId}`;
2298
+ const version = await incrementVersion(this.fullFS, descPath, bytes, author);
2299
+ await this.writeVersionSidecar(descPath, version);
2300
+ }
2301
+ async getConfigSyncGroupDescriptor() {
2302
+ this.assertNotDisposed();
2303
+ try {
2304
+ const raw = await this.cachedFS.readFile(CONFIG_SYNC_GROUP_FILE);
2305
+ const parsed = JSON.parse(new TextDecoder().decode(toUint8Array2(raw)));
2306
+ if (parsed && parsed.groupType === "config-sync") {
2307
+ return parsed;
2308
+ }
2309
+ } catch {
2310
+ }
2311
+ return null;
2312
+ }
2313
+ /**
2314
+ * Change the config-sync group's display name only. Backends, the local
2315
+ * store and remote data are untouched.
2316
+ */
2317
+ async updateConfigSyncGroupDescriptor(patch) {
2318
+ this.assertNotDisposed();
2319
+ const normalized = normalizeGroupName(patch?.name);
2320
+ const descriptor = {
2321
+ groupType: "config-sync",
2322
+ name: normalized
2323
+ };
2324
+ await this.ensureDir(CONFIG_SYNC_GROUP_FILE);
2325
+ const bytes = new TextEncoder().encode(JSON.stringify(descriptor, null, 2));
2326
+ await this.cachedFS.writeFile(CONFIG_SYNC_GROUP_FILE, bytes);
2327
+ const author = `${this.appId}/${this.nodeId}`;
2328
+ const version = await incrementVersion(this.fullFS, CONFIG_SYNC_GROUP_FILE, bytes, author);
2329
+ await this.writeVersionSidecar(CONFIG_SYNC_GROUP_FILE, version);
2330
+ return descriptor;
2331
+ }
2332
+ async readAppDataGroupDescriptor(id) {
2333
+ try {
2334
+ const raw = await this.cachedFS.readFile(this.appDataGroupFilePath(id));
2335
+ const parsed = JSON.parse(new TextDecoder().decode(toUint8Array2(raw)));
2336
+ if (parsed && parsed.id === id && parsed.groupType === "data-sync") {
2337
+ return parsed;
2338
+ }
2339
+ } catch {
2340
+ }
2341
+ return null;
2342
+ }
2259
2343
  async getAppDataGroup(id) {
2260
2344
  this.assertNotDisposed();
2261
2345
  const cached = this.appDataGroups.get(id);
@@ -27449,10 +27449,25 @@ var ZenFSConfig = (() => {
27449
27449
  var BACKENDS_FILE = `${META_DIR}/backends.json`;
27450
27450
  var BACKENDS_DIR = `${META_DIR}/backends`;
27451
27451
  var APP_DATA_GROUPS_DIR = `${META_DIR}/app-data-groups`;
27452
+ var CONFIG_SYNC_GROUP_FILE = `${META_DIR}/config-sync-group.json`;
27453
+ var GROUP_NAME_MAX_LENGTH = 40;
27452
27454
  var CONFLICTS_DIR = `${META_DIR}/.conflicts`;
27453
27455
  var DELETIONS_DIR = `${META_DIR}/.deleted`;
27454
27456
  var NODES_DIR = "/nodes";
27455
27457
  var SHARED_DIR = "/shared";
27458
+ function normalizeGroupName(value) {
27459
+ if (typeof value !== "string") {
27460
+ throw new Error("Group name must be a string");
27461
+ }
27462
+ const name = value.trim();
27463
+ if (!name) {
27464
+ throw new Error("Group name must not be empty");
27465
+ }
27466
+ if (name.length > GROUP_NAME_MAX_LENGTH) {
27467
+ throw new Error(`Group name must be at most ${GROUP_NAME_MAX_LENGTH} characters`);
27468
+ }
27469
+ return name;
27470
+ }
27456
27471
  var CONFIG_ONLY_PREFIXES = [META_DIR, NODES_DIR, SHARED_DIR];
27457
27472
  var LOCAL_IDB_BACKEND_ID = "local-idb";
27458
27473
  function tombstoneFileName(filePath) {
@@ -28808,9 +28823,11 @@ var ZenFSConfig = (() => {
28808
28823
  */
28809
28824
  async updateAppDataGroupDescriptor(id, backends) {
28810
28825
  this.assertNotDisposed();
28826
+ const existing = await this.readAppDataGroupDescriptor(id);
28811
28827
  const descriptor = {
28812
28828
  id,
28813
28829
  groupType: "data-sync",
28830
+ ...existing?.name ? { name: existing.name } : {},
28814
28831
  backends
28815
28832
  };
28816
28833
  const descPath = this.appDataGroupFilePath(id);
@@ -28821,6 +28838,73 @@ var ZenFSConfig = (() => {
28821
28838
  const version2 = await incrementVersion(this.fullFS, descPath, bytes, author);
28822
28839
  await this.writeVersionSidecar(descPath, version2);
28823
28840
  }
28841
+ /**
28842
+ * Change a data-sync group's display name only. `id`, backends, local stores
28843
+ * and remote data are untouched, so existing sync pairs keep working.
28844
+ */
28845
+ async updateAppDataGroupName(id, name) {
28846
+ this.assertNotDisposed();
28847
+ const normalized = normalizeGroupName(name);
28848
+ const existing = await this.readAppDataGroupDescriptor(id);
28849
+ if (!existing) {
28850
+ throw new Error(`App data group "${id}" not found`);
28851
+ }
28852
+ const descriptor = {
28853
+ ...existing,
28854
+ id,
28855
+ groupType: "data-sync",
28856
+ name: normalized
28857
+ };
28858
+ const descPath = this.appDataGroupFilePath(id);
28859
+ await this.ensureDir(descPath);
28860
+ const bytes = new TextEncoder().encode(JSON.stringify(descriptor, null, 2));
28861
+ await this.cachedFS.writeFile(descPath, bytes);
28862
+ const author = `${this.appId}/${this.nodeId}`;
28863
+ const version2 = await incrementVersion(this.fullFS, descPath, bytes, author);
28864
+ await this.writeVersionSidecar(descPath, version2);
28865
+ }
28866
+ async getConfigSyncGroupDescriptor() {
28867
+ this.assertNotDisposed();
28868
+ try {
28869
+ const raw = await this.cachedFS.readFile(CONFIG_SYNC_GROUP_FILE);
28870
+ const parsed = JSON.parse(new TextDecoder().decode(toUint8Array2(raw)));
28871
+ if (parsed && parsed.groupType === "config-sync") {
28872
+ return parsed;
28873
+ }
28874
+ } catch {
28875
+ }
28876
+ return null;
28877
+ }
28878
+ /**
28879
+ * Change the config-sync group's display name only. Backends, the local
28880
+ * store and remote data are untouched.
28881
+ */
28882
+ async updateConfigSyncGroupDescriptor(patch) {
28883
+ this.assertNotDisposed();
28884
+ const normalized = normalizeGroupName(patch?.name);
28885
+ const descriptor = {
28886
+ groupType: "config-sync",
28887
+ name: normalized
28888
+ };
28889
+ await this.ensureDir(CONFIG_SYNC_GROUP_FILE);
28890
+ const bytes = new TextEncoder().encode(JSON.stringify(descriptor, null, 2));
28891
+ await this.cachedFS.writeFile(CONFIG_SYNC_GROUP_FILE, bytes);
28892
+ const author = `${this.appId}/${this.nodeId}`;
28893
+ const version2 = await incrementVersion(this.fullFS, CONFIG_SYNC_GROUP_FILE, bytes, author);
28894
+ await this.writeVersionSidecar(CONFIG_SYNC_GROUP_FILE, version2);
28895
+ return descriptor;
28896
+ }
28897
+ async readAppDataGroupDescriptor(id) {
28898
+ try {
28899
+ const raw = await this.cachedFS.readFile(this.appDataGroupFilePath(id));
28900
+ const parsed = JSON.parse(new TextDecoder().decode(toUint8Array2(raw)));
28901
+ if (parsed && parsed.id === id && parsed.groupType === "data-sync") {
28902
+ return parsed;
28903
+ }
28904
+ } catch {
28905
+ }
28906
+ return null;
28907
+ }
28824
28908
  async getAppDataGroup(id) {
28825
28909
  this.assertNotDisposed();
28826
28910
  const cached = this.appDataGroups.get(id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zen-fs-config",
3
- "version": "0.5.45",
3
+ "version": "0.5.46",
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",