zen-fs-config 0.5.44 → 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 +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +107 -6
- package/dist/index.mjs +107 -6
- package/dist/zen-fs-config.js +107 -6
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -345,8 +345,30 @@ 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
|
+
}
|
|
358
|
+
/** Per-backend sync state of a data group. */
|
|
359
|
+
interface BackendSyncState {
|
|
360
|
+
backendId: string;
|
|
361
|
+
/** Pair id when the backend instance was created and paired successfully. */
|
|
362
|
+
pairId?: string;
|
|
363
|
+
/**
|
|
364
|
+
* Present when this descriptor could not be connected (e.g. a required
|
|
365
|
+
* option is missing), so no sync pair exists for it.
|
|
366
|
+
*/
|
|
367
|
+
error?: {
|
|
368
|
+
message: string;
|
|
369
|
+
timestamp: number;
|
|
370
|
+
};
|
|
371
|
+
}
|
|
350
372
|
/** Handle to a data-sync group, providing direct fs access. */
|
|
351
373
|
interface AppDataGroup {
|
|
352
374
|
readonly groupId: string;
|
|
@@ -355,6 +377,11 @@ interface AppDataGroup {
|
|
|
355
377
|
readonly fs: typeof node_fs;
|
|
356
378
|
/** Get sync status for this data group's sync pairs. */
|
|
357
379
|
getSyncStatuses(): Map<string, SyncPairStatus>;
|
|
380
|
+
/**
|
|
381
|
+
* Per-backend sync state, including descriptors that failed to connect and
|
|
382
|
+
* therefore have no pair (they do not appear in getSyncStatuses()).
|
|
383
|
+
*/
|
|
384
|
+
getBackendSyncStates(): Map<string, BackendSyncState>;
|
|
358
385
|
/** Manually flush pending sync. */
|
|
359
386
|
flush(): Promise<SyncResult[]>;
|
|
360
387
|
/** Stop sync and release resources. */
|
|
@@ -536,6 +563,20 @@ interface IConfigRepo {
|
|
|
536
563
|
getAppDataGroup(id: string): Promise<AppDataGroup>;
|
|
537
564
|
/** List all data-sync group descriptors for this app. */
|
|
538
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>;
|
|
539
580
|
/** Remove a data-sync group (stops sync, removes descriptor). */
|
|
540
581
|
removeAppDataGroup(id: string): Promise<void>;
|
|
541
582
|
/**
|
|
@@ -986,6 +1027,20 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
986
1027
|
* authoritative source of the data group's backend topology (FR2.3).
|
|
987
1028
|
*/
|
|
988
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;
|
|
989
1044
|
getAppDataGroup(id: string): Promise<AppDataGroup>;
|
|
990
1045
|
listAppDataGroups(): Promise<AppDataGroupDescriptor[]>;
|
|
991
1046
|
removeAppDataGroup(id: string): Promise<void>;
|
|
@@ -1141,4 +1196,4 @@ declare function incrementVersion(fs: SyncableFS, configFilePath: string, newCon
|
|
|
1141
1196
|
*/
|
|
1142
1197
|
declare function verifyOrRepairVersion(fs: SyncableFS, configFilePath: string, author: string): Promise<VersionMeta | null>;
|
|
1143
1198
|
|
|
1144
|
-
export { type AppDataBackendDescriptor, type AppDataGroup, type AppDataGroupDescriptor, type BackendDescriptor, type BackendFactory, type BackendInstance, type BackendMetadata, type BackendParamDef, 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,30 @@ 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
|
+
}
|
|
358
|
+
/** Per-backend sync state of a data group. */
|
|
359
|
+
interface BackendSyncState {
|
|
360
|
+
backendId: string;
|
|
361
|
+
/** Pair id when the backend instance was created and paired successfully. */
|
|
362
|
+
pairId?: string;
|
|
363
|
+
/**
|
|
364
|
+
* Present when this descriptor could not be connected (e.g. a required
|
|
365
|
+
* option is missing), so no sync pair exists for it.
|
|
366
|
+
*/
|
|
367
|
+
error?: {
|
|
368
|
+
message: string;
|
|
369
|
+
timestamp: number;
|
|
370
|
+
};
|
|
371
|
+
}
|
|
350
372
|
/** Handle to a data-sync group, providing direct fs access. */
|
|
351
373
|
interface AppDataGroup {
|
|
352
374
|
readonly groupId: string;
|
|
@@ -355,6 +377,11 @@ interface AppDataGroup {
|
|
|
355
377
|
readonly fs: typeof node_fs;
|
|
356
378
|
/** Get sync status for this data group's sync pairs. */
|
|
357
379
|
getSyncStatuses(): Map<string, SyncPairStatus>;
|
|
380
|
+
/**
|
|
381
|
+
* Per-backend sync state, including descriptors that failed to connect and
|
|
382
|
+
* therefore have no pair (they do not appear in getSyncStatuses()).
|
|
383
|
+
*/
|
|
384
|
+
getBackendSyncStates(): Map<string, BackendSyncState>;
|
|
358
385
|
/** Manually flush pending sync. */
|
|
359
386
|
flush(): Promise<SyncResult[]>;
|
|
360
387
|
/** Stop sync and release resources. */
|
|
@@ -536,6 +563,20 @@ interface IConfigRepo {
|
|
|
536
563
|
getAppDataGroup(id: string): Promise<AppDataGroup>;
|
|
537
564
|
/** List all data-sync group descriptors for this app. */
|
|
538
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>;
|
|
539
580
|
/** Remove a data-sync group (stops sync, removes descriptor). */
|
|
540
581
|
removeAppDataGroup(id: string): Promise<void>;
|
|
541
582
|
/**
|
|
@@ -986,6 +1027,20 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
986
1027
|
* authoritative source of the data group's backend topology (FR2.3).
|
|
987
1028
|
*/
|
|
988
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;
|
|
989
1044
|
getAppDataGroup(id: string): Promise<AppDataGroup>;
|
|
990
1045
|
listAppDataGroups(): Promise<AppDataGroupDescriptor[]>;
|
|
991
1046
|
removeAppDataGroup(id: string): Promise<void>;
|
|
@@ -1141,4 +1196,4 @@ declare function incrementVersion(fs: SyncableFS, configFilePath: string, newCon
|
|
|
1141
1196
|
*/
|
|
1142
1197
|
declare function verifyOrRepairVersion(fs: SyncableFS, configFilePath: string, author: string): Promise<VersionMeta | null>;
|
|
1143
1198
|
|
|
1144
|
-
export { type AppDataBackendDescriptor, type AppDataGroup, type AppDataGroupDescriptor, type BackendDescriptor, type BackendFactory, type BackendInstance, type BackendMetadata, type BackendParamDef, 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);
|
|
@@ -2885,6 +2969,7 @@ var AppDataGroupImpl = class {
|
|
|
2885
2969
|
syncEngine;
|
|
2886
2970
|
localFS;
|
|
2887
2971
|
dataBackends = /* @__PURE__ */ new Map();
|
|
2972
|
+
backendStates = /* @__PURE__ */ new Map();
|
|
2888
2973
|
disposed = false;
|
|
2889
2974
|
constructor(groupId, appId, backends, pollIntervalMs, parent) {
|
|
2890
2975
|
this.groupId = groupId;
|
|
@@ -2957,8 +3042,14 @@ var AppDataGroupImpl = class {
|
|
|
2957
3042
|
"/"
|
|
2958
3043
|
);
|
|
2959
3044
|
this.dataBackends.set(desc.id, { instance, syncable, pairId: pair.pairId, desc });
|
|
3045
|
+
this.backendStates.set(desc.id, { backendId: desc.id, pairId: pair.pairId });
|
|
2960
3046
|
log2.log(`[AppDataGroup:${this.groupId}] backend ${desc.id} (${desc.type}) connected, pair=${pair.pairId}`);
|
|
2961
3047
|
} catch (err) {
|
|
3048
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3049
|
+
this.backendStates.set(desc.id, {
|
|
3050
|
+
backendId: desc.id,
|
|
3051
|
+
error: { message, timestamp: Date.now() }
|
|
3052
|
+
});
|
|
2962
3053
|
log2.error(`[AppDataGroup:${this.groupId}] Failed to create backend ${desc.id} (${desc.type}):`, err);
|
|
2963
3054
|
}
|
|
2964
3055
|
}
|
|
@@ -2983,6 +3074,9 @@ var AppDataGroupImpl = class {
|
|
|
2983
3074
|
const results = await this.syncEngine.syncAll();
|
|
2984
3075
|
return Array.from(results.values());
|
|
2985
3076
|
}
|
|
3077
|
+
getBackendSyncStates() {
|
|
3078
|
+
return new Map(this.backendStates);
|
|
3079
|
+
}
|
|
2986
3080
|
async addBackend(id, type, options, description) {
|
|
2987
3081
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
2988
3082
|
if (this.dataBackends.has(id)) {
|
|
@@ -3005,6 +3099,8 @@ var AppDataGroupImpl = class {
|
|
|
3005
3099
|
);
|
|
3006
3100
|
const desc = { id, type, options, description, createdAt: Date.now() };
|
|
3007
3101
|
this.dataBackends.set(id, { instance, syncable, pairId: pair.pairId, desc });
|
|
3102
|
+
this.backendStates.set(id, { backendId: id, pairId: pair.pairId });
|
|
3103
|
+
this._backends = [...this._backends.filter((b) => b.id !== id), desc];
|
|
3008
3104
|
log2.log(`[AppDataGroup:${this.groupId}] addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
|
|
3009
3105
|
try {
|
|
3010
3106
|
await this.syncEngine.sync(pair.pairId);
|
|
@@ -3012,18 +3108,23 @@ var AppDataGroupImpl = class {
|
|
|
3012
3108
|
log2.warn(`[AppDataGroup:${this.groupId}] addBackend: initial sync failed for ${id}:`, err);
|
|
3013
3109
|
}
|
|
3014
3110
|
this.syncEngine.watch(pair.pairId);
|
|
3015
|
-
await this.parent.updateAppDataGroupDescriptor(this.groupId, this.
|
|
3111
|
+
await this.parent.updateAppDataGroupDescriptor(this.groupId, this._backends);
|
|
3016
3112
|
}
|
|
3017
3113
|
async removeBackend(id) {
|
|
3018
3114
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
3019
3115
|
const backend = this.dataBackends.get(id);
|
|
3020
|
-
|
|
3116
|
+
const desc = this._backends.find((b) => b.id === id);
|
|
3117
|
+
if (!backend && !desc) {
|
|
3021
3118
|
throw new Error(`Backend "${id}" not found in data group "${this.groupId}"`);
|
|
3022
3119
|
}
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3120
|
+
if (backend) {
|
|
3121
|
+
this.syncEngine.removePair(backend.pairId);
|
|
3122
|
+
this.dataBackends.delete(id);
|
|
3123
|
+
}
|
|
3124
|
+
this.backendStates.delete(id);
|
|
3125
|
+
this._backends = this._backends.filter((b) => b.id !== id);
|
|
3126
|
+
await this.parent.updateAppDataGroupDescriptor(this.groupId, this._backends);
|
|
3127
|
+
if (backend?.instance?.dispose) {
|
|
3027
3128
|
await backend.instance.dispose();
|
|
3028
3129
|
}
|
|
3029
3130
|
log2.log(`[AppDataGroup:${this.groupId}] removeBackend: ${id} removed`);
|
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);
|
|
@@ -2345,6 +2429,7 @@ var AppDataGroupImpl = class {
|
|
|
2345
2429
|
syncEngine;
|
|
2346
2430
|
localFS;
|
|
2347
2431
|
dataBackends = /* @__PURE__ */ new Map();
|
|
2432
|
+
backendStates = /* @__PURE__ */ new Map();
|
|
2348
2433
|
disposed = false;
|
|
2349
2434
|
constructor(groupId, appId, backends, pollIntervalMs, parent) {
|
|
2350
2435
|
this.groupId = groupId;
|
|
@@ -2417,8 +2502,14 @@ var AppDataGroupImpl = class {
|
|
|
2417
2502
|
"/"
|
|
2418
2503
|
);
|
|
2419
2504
|
this.dataBackends.set(desc.id, { instance, syncable, pairId: pair.pairId, desc });
|
|
2505
|
+
this.backendStates.set(desc.id, { backendId: desc.id, pairId: pair.pairId });
|
|
2420
2506
|
log2.log(`[AppDataGroup:${this.groupId}] backend ${desc.id} (${desc.type}) connected, pair=${pair.pairId}`);
|
|
2421
2507
|
} catch (err) {
|
|
2508
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2509
|
+
this.backendStates.set(desc.id, {
|
|
2510
|
+
backendId: desc.id,
|
|
2511
|
+
error: { message, timestamp: Date.now() }
|
|
2512
|
+
});
|
|
2422
2513
|
log2.error(`[AppDataGroup:${this.groupId}] Failed to create backend ${desc.id} (${desc.type}):`, err);
|
|
2423
2514
|
}
|
|
2424
2515
|
}
|
|
@@ -2443,6 +2534,9 @@ var AppDataGroupImpl = class {
|
|
|
2443
2534
|
const results = await this.syncEngine.syncAll();
|
|
2444
2535
|
return Array.from(results.values());
|
|
2445
2536
|
}
|
|
2537
|
+
getBackendSyncStates() {
|
|
2538
|
+
return new Map(this.backendStates);
|
|
2539
|
+
}
|
|
2446
2540
|
async addBackend(id, type, options, description) {
|
|
2447
2541
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
2448
2542
|
if (this.dataBackends.has(id)) {
|
|
@@ -2465,6 +2559,8 @@ var AppDataGroupImpl = class {
|
|
|
2465
2559
|
);
|
|
2466
2560
|
const desc = { id, type, options, description, createdAt: Date.now() };
|
|
2467
2561
|
this.dataBackends.set(id, { instance, syncable, pairId: pair.pairId, desc });
|
|
2562
|
+
this.backendStates.set(id, { backendId: id, pairId: pair.pairId });
|
|
2563
|
+
this._backends = [...this._backends.filter((b) => b.id !== id), desc];
|
|
2468
2564
|
log2.log(`[AppDataGroup:${this.groupId}] addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
|
|
2469
2565
|
try {
|
|
2470
2566
|
await this.syncEngine.sync(pair.pairId);
|
|
@@ -2472,18 +2568,23 @@ var AppDataGroupImpl = class {
|
|
|
2472
2568
|
log2.warn(`[AppDataGroup:${this.groupId}] addBackend: initial sync failed for ${id}:`, err);
|
|
2473
2569
|
}
|
|
2474
2570
|
this.syncEngine.watch(pair.pairId);
|
|
2475
|
-
await this.parent.updateAppDataGroupDescriptor(this.groupId, this.
|
|
2571
|
+
await this.parent.updateAppDataGroupDescriptor(this.groupId, this._backends);
|
|
2476
2572
|
}
|
|
2477
2573
|
async removeBackend(id) {
|
|
2478
2574
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
2479
2575
|
const backend = this.dataBackends.get(id);
|
|
2480
|
-
|
|
2576
|
+
const desc = this._backends.find((b) => b.id === id);
|
|
2577
|
+
if (!backend && !desc) {
|
|
2481
2578
|
throw new Error(`Backend "${id}" not found in data group "${this.groupId}"`);
|
|
2482
2579
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2580
|
+
if (backend) {
|
|
2581
|
+
this.syncEngine.removePair(backend.pairId);
|
|
2582
|
+
this.dataBackends.delete(id);
|
|
2583
|
+
}
|
|
2584
|
+
this.backendStates.delete(id);
|
|
2585
|
+
this._backends = this._backends.filter((b) => b.id !== id);
|
|
2586
|
+
await this.parent.updateAppDataGroupDescriptor(this.groupId, this._backends);
|
|
2587
|
+
if (backend?.instance?.dispose) {
|
|
2487
2588
|
await backend.instance.dispose();
|
|
2488
2589
|
}
|
|
2489
2590
|
log2.log(`[AppDataGroup:${this.groupId}] removeBackend: ${id} removed`);
|
package/dist/zen-fs-config.js
CHANGED
|
@@ -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);
|
|
@@ -28911,6 +28995,7 @@ var ZenFSConfig = (() => {
|
|
|
28911
28995
|
__publicField(this, "syncEngine");
|
|
28912
28996
|
__publicField(this, "localFS");
|
|
28913
28997
|
__publicField(this, "dataBackends", /* @__PURE__ */ new Map());
|
|
28998
|
+
__publicField(this, "backendStates", /* @__PURE__ */ new Map());
|
|
28914
28999
|
__publicField(this, "disposed", false);
|
|
28915
29000
|
__publicField(this, "parent");
|
|
28916
29001
|
__publicField(this, "_backends");
|
|
@@ -28982,8 +29067,14 @@ var ZenFSConfig = (() => {
|
|
|
28982
29067
|
"/"
|
|
28983
29068
|
);
|
|
28984
29069
|
this.dataBackends.set(desc.id, { instance, syncable, pairId: pair.pairId, desc });
|
|
29070
|
+
this.backendStates.set(desc.id, { backendId: desc.id, pairId: pair.pairId });
|
|
28985
29071
|
log6.log(`[AppDataGroup:${this.groupId}] backend ${desc.id} (${desc.type}) connected, pair=${pair.pairId}`);
|
|
28986
29072
|
} catch (err2) {
|
|
29073
|
+
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
29074
|
+
this.backendStates.set(desc.id, {
|
|
29075
|
+
backendId: desc.id,
|
|
29076
|
+
error: { message, timestamp: Date.now() }
|
|
29077
|
+
});
|
|
28987
29078
|
log6.error(`[AppDataGroup:${this.groupId}] Failed to create backend ${desc.id} (${desc.type}):`, err2);
|
|
28988
29079
|
}
|
|
28989
29080
|
}
|
|
@@ -29008,6 +29099,9 @@ var ZenFSConfig = (() => {
|
|
|
29008
29099
|
const results = await this.syncEngine.syncAll();
|
|
29009
29100
|
return Array.from(results.values());
|
|
29010
29101
|
}
|
|
29102
|
+
getBackendSyncStates() {
|
|
29103
|
+
return new Map(this.backendStates);
|
|
29104
|
+
}
|
|
29011
29105
|
async addBackend(id, type, options, description) {
|
|
29012
29106
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
29013
29107
|
if (this.dataBackends.has(id)) {
|
|
@@ -29030,6 +29124,8 @@ var ZenFSConfig = (() => {
|
|
|
29030
29124
|
);
|
|
29031
29125
|
const desc = { id, type, options, description, createdAt: Date.now() };
|
|
29032
29126
|
this.dataBackends.set(id, { instance, syncable, pairId: pair.pairId, desc });
|
|
29127
|
+
this.backendStates.set(id, { backendId: id, pairId: pair.pairId });
|
|
29128
|
+
this._backends = [...this._backends.filter((b) => b.id !== id), desc];
|
|
29033
29129
|
log6.log(`[AppDataGroup:${this.groupId}] addBackend: ${id} (${type}) connected, pair=${pair.pairId}`);
|
|
29034
29130
|
try {
|
|
29035
29131
|
await this.syncEngine.sync(pair.pairId);
|
|
@@ -29037,18 +29133,23 @@ var ZenFSConfig = (() => {
|
|
|
29037
29133
|
log6.warn(`[AppDataGroup:${this.groupId}] addBackend: initial sync failed for ${id}:`, err2);
|
|
29038
29134
|
}
|
|
29039
29135
|
this.syncEngine.watch(pair.pairId);
|
|
29040
|
-
await this.parent.updateAppDataGroupDescriptor(this.groupId, this.
|
|
29136
|
+
await this.parent.updateAppDataGroupDescriptor(this.groupId, this._backends);
|
|
29041
29137
|
}
|
|
29042
29138
|
async removeBackend(id) {
|
|
29043
29139
|
if (this.disposed) throw new Error("DataSyncGroup has been disposed");
|
|
29044
29140
|
const backend = this.dataBackends.get(id);
|
|
29045
|
-
|
|
29141
|
+
const desc = this._backends.find((b) => b.id === id);
|
|
29142
|
+
if (!backend && !desc) {
|
|
29046
29143
|
throw new Error(`Backend "${id}" not found in data group "${this.groupId}"`);
|
|
29047
29144
|
}
|
|
29048
|
-
|
|
29049
|
-
|
|
29050
|
-
|
|
29051
|
-
|
|
29145
|
+
if (backend) {
|
|
29146
|
+
this.syncEngine.removePair(backend.pairId);
|
|
29147
|
+
this.dataBackends.delete(id);
|
|
29148
|
+
}
|
|
29149
|
+
this.backendStates.delete(id);
|
|
29150
|
+
this._backends = this._backends.filter((b) => b.id !== id);
|
|
29151
|
+
await this.parent.updateAppDataGroupDescriptor(this.groupId, this._backends);
|
|
29152
|
+
if (backend?.instance?.dispose) {
|
|
29052
29153
|
await backend.instance.dispose();
|
|
29053
29154
|
}
|
|
29054
29155
|
log6.log(`[AppDataGroup:${this.groupId}] removeBackend: ${id} removed`);
|