zen-fs-config 0.4.5 → 0.5.0
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/DESIGN.md +357 -12
- package/dist/index.d.mts +234 -3
- package/dist/index.d.ts +234 -3
- package/dist/index.js +691 -7
- package/dist/index.mjs +689 -7
- package/package.json +1 -1
package/DESIGN.md
CHANGED
|
@@ -9,6 +9,13 @@ zen-fs-config is a distributed configuration management library built on top of:
|
|
|
9
9
|
|
|
10
10
|
It allows multiple application instances (programs) running on different nodes to share configuration through a network of ZenFS backends, with per-app isolation, shared config spaces, node-local config, and conflict safety.
|
|
11
11
|
|
|
12
|
+
zen-fs-config supports two types of **sync groups**, each backed by a multi-backend sync network but serving different purposes:
|
|
13
|
+
|
|
14
|
+
- **Config-sync group** — Synchronizes the configuration repository itself: backend topology, app configs, shared configs, node-local configs. This is the "meta layer."
|
|
15
|
+
- **Data-sync group** — Synchronizes application data only. A data-sync group can be used standalone, or referenced by a config-sync group as an app's data storage layer.
|
|
16
|
+
|
|
17
|
+
A config-sync group can reference one or more data-sync groups per app, allowing apps to store bulk data on separate backends (e.g., a different repo/branch under the same account) while keeping configuration management unified.
|
|
18
|
+
|
|
12
19
|
## 2. Architecture
|
|
13
20
|
|
|
14
21
|
### 2.1 Three-Layer Stack
|
|
@@ -47,18 +54,67 @@ Backend topology and sync rules are stored **inside** the configuration reposito
|
|
|
47
54
|
|
|
48
55
|
External input at startup is limited to: **which backend to connect to** and optionally **bootstrap data** (if the repo doesn't exist yet).
|
|
49
56
|
|
|
57
|
+
### 2.4 Sync Group Types
|
|
58
|
+
|
|
59
|
+
A **sync group** is a set of backends that synchronize with each other. There are two types:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
63
|
+
│ Config-Sync Group │
|
|
64
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
65
|
+
│ │ .meta/backends/ ← config-sync backends │ │
|
|
66
|
+
│ │ .meta/app-data-groups/ ← references to data-sync │ │
|
|
67
|
+
│ │ /{appId}/ ← app config data │ │
|
|
68
|
+
│ │ /shared/ ← shared config │ │
|
|
69
|
+
│ │ /nodes/ ← node-local config │ │
|
|
70
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
71
|
+
│ │ references │
|
|
72
|
+
│ ▼ │
|
|
73
|
+
│ ┌──────────────────────────────────────────────────────┐ │
|
|
74
|
+
│ │ Data-Sync Group (per-app) │ │
|
|
75
|
+
│ │ .meta/backends/ ← data-sync backends │ │
|
|
76
|
+
│ │ / ← app data files │ │
|
|
77
|
+
│ └──────────────────────────────────────────────────────┘ │
|
|
78
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Config-sync group**:
|
|
82
|
+
- Synced content: `.meta/` (topology), `/{appId}/` (app config), `/shared/`, `/nodes/`
|
|
83
|
+
- Backends: full credentials + storage location (e.g., Gitee: token + owner + repo + branch)
|
|
84
|
+
- Can reference data-sync groups via `.meta/app-data-groups/{appId}/`
|
|
85
|
+
|
|
86
|
+
**Data-sync group**:
|
|
87
|
+
- Synced content: application data files only (no config meta layer)
|
|
88
|
+
- Backends: full credentials + storage location, but typically reusing the same account as a config-sync backend with a different storage target (e.g., same token/owner, different repo/branch)
|
|
89
|
+
- Can be used standalone (no config-sync group needed)
|
|
90
|
+
- Can be referenced by a config-sync group as an app's data storage
|
|
91
|
+
|
|
92
|
+
**Group type detection**: When connecting to a backend, the library reads `.meta/group-type` to determine the group type. If the file is absent, the backend is treated as a new empty group and the caller decides which type to create.
|
|
93
|
+
|
|
94
|
+
| `.meta/group-type` | Behavior |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `config-sync` | Full system: IndexedDB primary + config replicas + optional data-sync groups |
|
|
97
|
+
| `data-sync` | Lightweight: data backends only, direct read/write, no config meta layer |
|
|
98
|
+
| absent | New empty backend; caller decides group type at creation time |
|
|
99
|
+
|
|
50
100
|
## 3. File System Structure
|
|
51
101
|
|
|
102
|
+
### 3.1 Config-Sync Group
|
|
103
|
+
|
|
52
104
|
```
|
|
53
105
|
/
|
|
54
106
|
├─ .meta/ [synced to replicas]
|
|
55
|
-
│ ├─
|
|
56
|
-
│
|
|
107
|
+
│ ├─ group-type Group type marker: "config-sync"
|
|
108
|
+
│ ├─ backends/ Backend topology (one file per backend)
|
|
109
|
+
│ │ ├─ local-idb.json { id, type, options, description }
|
|
57
110
|
│ │ ├─ gitee-prod.json
|
|
58
111
|
│ │ └─ ...
|
|
59
|
-
│ ├─
|
|
112
|
+
│ ├─ app-data-groups/ References to data-sync groups (per app)
|
|
113
|
+
│ │ └─ {appId}/
|
|
114
|
+
│ │ └─ {dataGroupId}.json { groupType: "data-sync", backends: [...] }
|
|
115
|
+
│ ├─ .deleted/ Tombstones for deletion propagation
|
|
60
116
|
│ │ └─ {encoded-path}.json
|
|
61
|
-
│ └─ .conflicts/
|
|
117
|
+
│ └─ .conflicts/ Conflict archives (safekeeping)
|
|
62
118
|
│ └─ {timestamp}_{path}/
|
|
63
119
|
│ ├─ meta.json
|
|
64
120
|
│ ├─ source
|
|
@@ -81,7 +137,27 @@ External input at startup is limited to: **which backend to connect to** and opt
|
|
|
81
137
|
└─ .node-id Current node's ID (auto-generated)
|
|
82
138
|
```
|
|
83
139
|
|
|
84
|
-
### 3.
|
|
140
|
+
### 3.2 Data-Sync Group
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
/
|
|
144
|
+
├─ .meta/ [synced to replicas]
|
|
145
|
+
│ ├─ group-type Group type marker: "data-sync"
|
|
146
|
+
│ └─ backends/ Data backend topology (one file per backend)
|
|
147
|
+
│ ├─ gitee-data-1.json { id, type, options, description }
|
|
148
|
+
│ └─ gitee-data-2.json
|
|
149
|
+
│
|
|
150
|
+
└─ (application data files) [synced: bi-directional]
|
|
151
|
+
├─ documents/
|
|
152
|
+
│ ├─ note-1.json
|
|
153
|
+
│ └─ note-2.json
|
|
154
|
+
└─ media/
|
|
155
|
+
└─ config.json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
A data-sync group has a much simpler structure: no version sidecars, no tombstones, no conflict archives — just raw data files and a minimal `.meta/` for backend topology and group type identification.
|
|
159
|
+
|
|
160
|
+
### 3.3 Directory Semantics (Config-Sync Group)
|
|
85
161
|
|
|
86
162
|
| Directory | Sync Direction | Conflict Risk | Purpose |
|
|
87
163
|
|---|---|---|---|
|
|
@@ -90,7 +166,7 @@ External input at startup is limited to: **which backend to connect to** and opt
|
|
|
90
166
|
| `/nodes/` | None (by default) | None | Per-node local config |
|
|
91
167
|
| `/.meta/` | Bi-directional | None (topology files) | Backend topology, tombstones, conflict archives |
|
|
92
168
|
|
|
93
|
-
### 3.
|
|
169
|
+
### 3.4 Config-to-File Mapping
|
|
94
170
|
|
|
95
171
|
Each config key maps to one file. The mapping is straightforward:
|
|
96
172
|
|
|
@@ -99,7 +175,7 @@ Each config key maps to one file. The mapping is straightforward:
|
|
|
99
175
|
- Path is relative to the app's root (`/{appId}/`), with `.json` extension appended automatically
|
|
100
176
|
- If path already has an extension (e.g., `/readme.md`), the extension is preserved
|
|
101
177
|
|
|
102
|
-
### 3.
|
|
178
|
+
### 3.5 Serialization
|
|
103
179
|
|
|
104
180
|
The serializer is determined by file extension:
|
|
105
181
|
|
|
@@ -140,6 +216,53 @@ The local IndexedDB backend (`local-idb`) is always the primary — all config o
|
|
|
140
216
|
|
|
141
217
|
**Migration**: If a legacy `.meta/backends.json` file exists (pre-0.4.0), it is automatically migrated to individual files on startup.
|
|
142
218
|
|
|
219
|
+
## 4.1 App Data Groups (`.meta/app-data-groups/{appId}/`)
|
|
220
|
+
|
|
221
|
+
A config-sync group can reference data-sync groups on behalf of specific apps. Each reference is stored as a JSON file under `.meta/app-data-groups/{appId}/`.
|
|
222
|
+
|
|
223
|
+
**`.meta/app-data-groups/my-app/data-store-1.json`**:
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"id": "data-store-1",
|
|
227
|
+
"groupType": "data-sync",
|
|
228
|
+
"backends": [
|
|
229
|
+
{
|
|
230
|
+
"id": "gitee-data",
|
|
231
|
+
"type": "Gitee",
|
|
232
|
+
"options": { "token": "...", "owner": "...", "repo": "my-app-data", "branch": "main" },
|
|
233
|
+
"accountBackendId": "gitee-prod",
|
|
234
|
+
"description": "App data on Gitee (reuses gitee-prod account)"
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Account Reuse
|
|
241
|
+
|
|
242
|
+
The `accountBackendId` field (optional) references a config-sync backend whose account fields (e.g., `token`, `owner`, `baseUrl` for Gitee/GitHub) are reused. When present, the data-sync backend's `options` only need to specify the **storage location** fields (e.g., `repo`, `branch`); account fields are merged in from the referenced config-sync backend at creation time.
|
|
243
|
+
|
|
244
|
+
When `accountBackendId` is absent or null, the data-sync backend must provide a complete set of options (including credentials).
|
|
245
|
+
|
|
246
|
+
The `accountFields` metadata for each backend type (registered via `BackendMetadata.accountFields`) determines which fields are "account" vs "storage location":
|
|
247
|
+
|
|
248
|
+
| Backend Type | Account Fields | Storage Location Fields |
|
|
249
|
+
|---|---|---|
|
|
250
|
+
| Gitee | `token`, `owner`, `baseUrl` | `repo`, `branch` |
|
|
251
|
+
| GitHub | `token`, `owner`, `baseUrl` | `repo`, `branch` |
|
|
252
|
+
| WebDAV | `url`, `username`, `password` | `rootPath` |
|
|
253
|
+
| RemoteStorage | `userAddress`, `token` | (none) |
|
|
254
|
+
|
|
255
|
+
### Standalone Data-Sync Group
|
|
256
|
+
|
|
257
|
+
A data-sync group can also be used **without** a config-sync group. In this case:
|
|
258
|
+
|
|
259
|
+
1. The user provides a single backend configuration (e.g., Gitee: token + owner + repo + branch)
|
|
260
|
+
2. The library connects and reads `.meta/group-type` → `"data-sync"`
|
|
261
|
+
3. The app directly reads/writes data files on these backends
|
|
262
|
+
4. No config meta layer, no version sidecars, no tombstones — just raw data sync
|
|
263
|
+
|
|
264
|
+
Multiple data-sync backends can be registered within a single data-sync group, providing redundancy and multi-device sync for app data.
|
|
265
|
+
|
|
143
266
|
## 5. Sync Rules (`.meta/sync-rules.json`)
|
|
144
267
|
|
|
145
268
|
```json
|
|
@@ -376,13 +499,88 @@ interface ConfigRepo {
|
|
|
376
499
|
/** Sync .meta/ files to all replicas */
|
|
377
500
|
syncMetaToReplicas(): Promise<void>;
|
|
378
501
|
|
|
502
|
+
// --- App Data Storage (data-sync groups) ---
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Create a data-sync group for this app, referencing a config-sync backend's account.
|
|
506
|
+
* The data-sync group gets its own set of backends (typically reusing an account
|
|
507
|
+
* from a config-sync backend but with different storage location like repo/branch).
|
|
508
|
+
*
|
|
509
|
+
* @param id Data group ID (e.g., "data-store-1")
|
|
510
|
+
* @param backends Array of backend descriptors for the data-sync group.
|
|
511
|
+
* Each can optionally specify `accountBackendId` to reuse credentials.
|
|
512
|
+
*/
|
|
513
|
+
createAppDataGroup(
|
|
514
|
+
id: string,
|
|
515
|
+
backends: AppDataBackendDescriptor[],
|
|
516
|
+
): Promise<AppDataGroup>;
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Get an existing data-sync group for this app.
|
|
520
|
+
* Returns a handle with its own fs for reading/writing data files.
|
|
521
|
+
*/
|
|
522
|
+
getAppDataGroup(id: string): Promise<AppDataGroup>;
|
|
523
|
+
|
|
524
|
+
/** List all data-sync groups registered for this app. */
|
|
525
|
+
listAppDataGroups(): Promise<AppDataGroupDescriptor[]>;
|
|
526
|
+
|
|
527
|
+
/** Remove a data-sync group (stops sync, removes descriptor). */
|
|
528
|
+
removeAppDataGroup(id: string): Promise<void>;
|
|
529
|
+
|
|
379
530
|
/** Dispose: stop sync, release resources */
|
|
380
531
|
dispose(): Promise<void>;
|
|
381
532
|
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* A data-sync group handle. Provides direct file system access
|
|
536
|
+
* to the app's data storage, independent of the config-sync layer.
|
|
537
|
+
*/
|
|
538
|
+
interface AppDataGroup {
|
|
539
|
+
readonly groupId: string;
|
|
540
|
+
readonly appId: string;
|
|
541
|
+
/** Direct fs for reading/writing data files (chroot to this group's root) */
|
|
542
|
+
readonly fs: typeof import('node:fs');
|
|
543
|
+
/** Get sync status for this data group's sync pairs */
|
|
544
|
+
getSyncStatuses(): Map<string, SyncPairStatus>;
|
|
545
|
+
/** Manually flush pending sync */
|
|
546
|
+
flush(): Promise<SyncResult[]>;
|
|
547
|
+
/** Stop sync and release resources */
|
|
548
|
+
dispose(): Promise<void>;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** Descriptor for a backend within a data-sync group. */
|
|
552
|
+
interface AppDataBackendDescriptor {
|
|
553
|
+
id: string;
|
|
554
|
+
type: string;
|
|
555
|
+
options: Record<string, unknown>;
|
|
556
|
+
/** Optional: reuse account fields from a config-sync backend */
|
|
557
|
+
accountBackendId?: string;
|
|
558
|
+
description?: string;
|
|
559
|
+
}
|
|
382
560
|
```
|
|
383
561
|
|
|
384
562
|
## 10. Initialization
|
|
385
563
|
|
|
564
|
+
The recommended entry point is `connect`, which auto-detects the group type. The lower-level `createConfigRepo` and `createDataSyncGroup` are also available for explicit control.
|
|
565
|
+
|
|
566
|
+
### `connect` (recommended — auto-detect)
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
import { connect } from 'zen-fs-config';
|
|
570
|
+
|
|
571
|
+
// User provides a backend — connect auto-detects config-sync vs data-sync
|
|
572
|
+
const result = await connect('my-app', {
|
|
573
|
+
backendInfo: {
|
|
574
|
+
type: 'Gitee',
|
|
575
|
+
options: { token: '...', owner: '...', repo: '...', branch: 'main' },
|
|
576
|
+
},
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// result.groupType → "config-sync" or "data-sync"
|
|
580
|
+
// result.repo → ConfigRepo (if config-sync)
|
|
581
|
+
// result.dataGroup → DataSyncGroup (if data-sync)
|
|
582
|
+
```
|
|
583
|
+
|
|
386
584
|
### Zero-parameter (offline-first)
|
|
387
585
|
|
|
388
586
|
```typescript
|
|
@@ -436,8 +634,62 @@ const backends = await repo.getBackends();
|
|
|
436
634
|
// backends.backends = [{ id: 'local-idb', ... }, { id: 's3-backup', ... }]
|
|
437
635
|
```
|
|
438
636
|
|
|
637
|
+
### Standalone Data-Sync Group (no config layer)
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
import { createDataSyncGroup } from 'zen-fs-config';
|
|
641
|
+
|
|
642
|
+
// User provides a data backend directly — no config-sync layer needed
|
|
643
|
+
const dataGroup = await createDataSyncGroup('my-app', {
|
|
644
|
+
backendInfo: {
|
|
645
|
+
type: 'Gitee',
|
|
646
|
+
options: { token: '...', owner: '...', repo: 'my-app-data', branch: 'main' },
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// Read/write data files directly
|
|
651
|
+
await dataGroup.fs.promises.writeFile('/notes/todo.json', JSON.stringify({ task: 'buy milk' }));
|
|
652
|
+
const data = JSON.parse(await dataGroup.fs.promises.readFile('/notes/todo.json', 'utf-8'));
|
|
653
|
+
|
|
654
|
+
// Add more data backends later (multi-backend sync)
|
|
655
|
+
await dataGroup.addBackend('gitee-backup', 'Gitee', {
|
|
656
|
+
token: '...', owner: '...', repo: 'my-app-data-backup', branch: 'main',
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
// Cleanup
|
|
660
|
+
await dataGroup.dispose();
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Config-Sync with App Data Group (account reuse)
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
const repo = await createConfigRepo('my-app', {
|
|
667
|
+
backendInfo: {
|
|
668
|
+
type: 'Gitee',
|
|
669
|
+
options: { token: '...', owner: '...', repo: 'configs', branch: 'main' },
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
// Create a data-sync group that reuses the config backend's account
|
|
674
|
+
// but stores data in a different repo
|
|
675
|
+
await repo.createAppDataGroup('data-store-1', [
|
|
676
|
+
{
|
|
677
|
+
id: 'gitee-data',
|
|
678
|
+
type: 'Gitee',
|
|
679
|
+
accountBackendId: 'gitee-prod', // reuse token + owner from this config backend
|
|
680
|
+
options: { repo: 'my-app-data', branch: 'main' }, // only storage location
|
|
681
|
+
},
|
|
682
|
+
]);
|
|
683
|
+
|
|
684
|
+
// Get the data group handle for direct file access
|
|
685
|
+
const dataGroup = await repo.getAppDataGroup('data-store-1');
|
|
686
|
+
await dataGroup.fs.promises.writeFile('/cache.json', '{"key":"value"}');
|
|
687
|
+
```
|
|
688
|
+
|
|
439
689
|
## 11. Initialization Flow
|
|
440
690
|
|
|
691
|
+
### 11.1 Config-Sync Group (`createConfigRepo`)
|
|
692
|
+
|
|
441
693
|
```
|
|
442
694
|
createConfigRepo('my-app', options?)
|
|
443
695
|
│
|
|
@@ -446,9 +698,9 @@ createConfigRepo('my-app', options?)
|
|
|
446
698
|
│
|
|
447
699
|
├─ 2. Ensure /.meta/ directory exists
|
|
448
700
|
│
|
|
449
|
-
├─ 3.
|
|
701
|
+
├─ 3. Write /.meta/group-type = "config-sync" (if not exists)
|
|
450
702
|
│
|
|
451
|
-
├─ 4.
|
|
703
|
+
├─ 4. Migrate legacy .meta/backends.json → .meta/backends/*.json (if exists)
|
|
452
704
|
│
|
|
453
705
|
├─ 5. If options.backendInfo provided:
|
|
454
706
|
│ ├─ Generate replica ID (options.primaryBackendId or auto)
|
|
@@ -456,7 +708,7 @@ createConfigRepo('my-app', options?)
|
|
|
456
708
|
│
|
|
457
709
|
├─ 6. Read all backend descriptors from .meta/backends/
|
|
458
710
|
│
|
|
459
|
-
├─ 7. Determine nodeId (explicit >
|
|
711
|
+
├─ 7. Determine nodeId (explicit > localStorage > auto-generated)
|
|
460
712
|
│
|
|
461
713
|
├─ 8. Create ConfigRepo with primary = 'local-idb'
|
|
462
714
|
│
|
|
@@ -465,11 +717,104 @@ createConfigRepo('my-app', options?)
|
|
|
465
717
|
│ ├─ Create SyncPair(IndexedDB, replica, bi-directional)
|
|
466
718
|
│ └─ syncEngine.watch(pairId)
|
|
467
719
|
│
|
|
468
|
-
├─ 10.
|
|
720
|
+
├─ 10. Load app data group references from .meta/app-data-groups/{appId}/
|
|
721
|
+
│ For each referenced data-sync group:
|
|
722
|
+
│ ├─ Create data-sync backends (merge account fields if accountBackendId set)
|
|
723
|
+
│ ├─ Create SyncPair for each data backend
|
|
724
|
+
│ └─ syncEngine.watch(pairId)
|
|
725
|
+
│
|
|
726
|
+
├─ 11. syncMetaToReplicas: push .meta/ changes to all replicas (background)
|
|
727
|
+
│
|
|
728
|
+
└─ 12. Load config cache from IndexedDB
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
### 11.2 Standalone Data-Sync Group (`createDataSyncGroup`)
|
|
732
|
+
|
|
733
|
+
```
|
|
734
|
+
createDataSyncGroup('my-app', options?)
|
|
735
|
+
│
|
|
736
|
+
├─ 1. Connect to user-provided backend (options.backendInfo)
|
|
469
737
|
│
|
|
470
|
-
|
|
738
|
+
├─ 2. Read /.meta/group-type
|
|
739
|
+
│ ├─ "data-sync" → existing group, read .meta/backends/ for all data backends
|
|
740
|
+
│ ├─ absent → new group, write /.meta/group-type = "data-sync"
|
|
741
|
+
│ └─ "config-sync" → error: this is a config-sync backend, use createConfigRepo()
|
|
742
|
+
│
|
|
743
|
+
├─ 3. Create IndexedDB as local primary (for offline access)
|
|
744
|
+
│
|
|
745
|
+
├─ 4. Setup sync: IndexedDB ↔ each data backend (bi-directional)
|
|
746
|
+
│
|
|
747
|
+
└─ 5. Return DataSyncGroup handle with direct fs access
|
|
471
748
|
```
|
|
472
749
|
|
|
750
|
+
### 11.3 Unified Entry Point (`connect`)
|
|
751
|
+
|
|
752
|
+
`createConfigRepo` and `createDataSyncGroup` are lower-level factory functions. The recommended entry point is `connect`, which auto-detects the group type and dispatches to the appropriate factory:
|
|
753
|
+
|
|
754
|
+
```
|
|
755
|
+
connect('my-app', options?)
|
|
756
|
+
│
|
|
757
|
+
├─ 1. Connect to user-provided backend (options.backendInfo)
|
|
758
|
+
│
|
|
759
|
+
├─ 2. Read /.meta/group-type
|
|
760
|
+
│
|
|
761
|
+
├─ "config-sync" → dispatch to createConfigRepo()
|
|
762
|
+
│ return { groupType: "config-sync", repo }
|
|
763
|
+
│
|
|
764
|
+
├─ "data-sync" → dispatch to createDataSyncGroup()
|
|
765
|
+
│ return { groupType: "data-sync", dataGroup }
|
|
766
|
+
│
|
|
767
|
+
└─ absent → new empty backend
|
|
768
|
+
├─ options.groupType === "data-sync" → dispatch to createDataSyncGroup()
|
|
769
|
+
├─ options.groupType === "config-sync" (or omitted) → dispatch to createConfigRepo()
|
|
770
|
+
└─ default: config-sync
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Usage**:
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
import { connect } from 'zen-fs-config';
|
|
777
|
+
|
|
778
|
+
// Auto-detect: connects to backend, reads group-type, dispatches accordingly
|
|
779
|
+
const result = await connect('my-app', {
|
|
780
|
+
backendInfo: {
|
|
781
|
+
type: 'Gitee',
|
|
782
|
+
options: { token: '...', owner: '...', repo: '...', branch: 'main' },
|
|
783
|
+
},
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
if (result.groupType === 'config-sync') {
|
|
787
|
+
// result.repo is a ConfigRepo — full config system
|
|
788
|
+
const repo = result.repo;
|
|
789
|
+
repo.setConfig('/db/host', { hostname: 'localhost' });
|
|
790
|
+
} else {
|
|
791
|
+
// result.dataGroup is a DataSyncGroup — lightweight data-only system
|
|
792
|
+
const dataGroup = result.dataGroup;
|
|
793
|
+
await dataGroup.fs.promises.writeFile('/data.json', '{"key":"value"}');
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Explicit override (skip detection, force a specific group type)
|
|
797
|
+
const result = await connect('my-app', {
|
|
798
|
+
backendInfo: { type: 'Gitee', options: {...} },
|
|
799
|
+
groupType: 'data-sync', // force data-sync even if backend has no group-type yet
|
|
800
|
+
});
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
**Return type**:
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
interface ConnectResult {
|
|
807
|
+
/** Detected or forced group type */
|
|
808
|
+
groupType: 'config-sync' | 'data-sync';
|
|
809
|
+
/** Present when groupType === "config-sync" */
|
|
810
|
+
repo?: ConfigRepo;
|
|
811
|
+
/** Present when groupType === "data-sync" */
|
|
812
|
+
dataGroup?: DataSyncGroup;
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
**Offline / zero-parameter mode**: When no `backendInfo` is provided, `connect` defaults to `config-sync` and creates an IndexedDB-only repo (same as `createConfigRepo` with no options).
|
|
817
|
+
|
|
473
818
|
## 12. Data Flow
|
|
474
819
|
|
|
475
820
|
### Read Path
|