zen-fs-config 0.5.7 → 0.5.9
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/cache-wrapper-XQCPZJCD.mjs +22 -0
- package/dist/index.d.mts +27 -6
- package/dist/index.d.ts +27 -6
- package/dist/index.js +50 -6
- package/dist/index.mjs +17 -6
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/cache-wrapper.ts
|
|
2
|
+
import { CachedFileSystem, IdbCacheStore, MemoryCacheStore } from "zen-fs-cache";
|
|
3
|
+
function wrapWithCache(backend, backendId, options) {
|
|
4
|
+
const storeType = options.storeType ?? "IdbCacheStore";
|
|
5
|
+
const prefix = options.storePrefix ?? `zen-fs-config:${backendId}:`;
|
|
6
|
+
let store;
|
|
7
|
+
if (storeType === "IdbCacheStore") {
|
|
8
|
+
store = new IdbCacheStore(prefix);
|
|
9
|
+
} else {
|
|
10
|
+
store = new MemoryCacheStore();
|
|
11
|
+
}
|
|
12
|
+
const wrapped = new CachedFileSystem(backend, store, {
|
|
13
|
+
ttlMs: options.ttlMs ?? 0
|
|
14
|
+
});
|
|
15
|
+
if (typeof backend.shouldSync === "function") {
|
|
16
|
+
wrapped.shouldSync = (...args) => backend.shouldSync(...args);
|
|
17
|
+
}
|
|
18
|
+
return wrapped;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
wrapWithCache
|
|
22
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -89,11 +89,20 @@ interface ConfigSerializer {
|
|
|
89
89
|
}
|
|
90
90
|
/** Cache configuration. */
|
|
91
91
|
interface CacheOptions {
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Type of cache store for replica backends.
|
|
94
|
+
* Default: `'IdbCacheStore'` — persists across page reloads so cached
|
|
95
|
+
* content + revision tokens survive. Use `'MemoryCacheStore'` for
|
|
96
|
+
* session-only caching (lost on reload).
|
|
97
|
+
*/
|
|
93
98
|
storeType?: 'MemoryCacheStore' | 'IdbCacheStore';
|
|
94
|
-
/** Cache store prefix (for IdbCacheStore). */
|
|
99
|
+
/** Cache store prefix (for IdbCacheStore). Default: `'zen-fs-config:'`. */
|
|
95
100
|
storePrefix?: string;
|
|
96
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* TTL in milliseconds for cache hits without revalidation.
|
|
103
|
+
* Default: 0 (always revalidate via `getRevision` — exact, no stale data).
|
|
104
|
+
* Ignored when the backend implements `getRevision`.
|
|
105
|
+
*/
|
|
97
106
|
ttlMs?: number;
|
|
98
107
|
}
|
|
99
108
|
/** Options for creating a ConfigRepo. */
|
|
@@ -119,8 +128,19 @@ interface ConfigRepoOptions {
|
|
|
119
128
|
idbStoreName?: string;
|
|
120
129
|
/** Node identifier. Auto-detected if not provided (see DESIGN.md §8.2). */
|
|
121
130
|
nodeId?: string;
|
|
122
|
-
/**
|
|
123
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Cache configuration for replica backends.
|
|
133
|
+
*
|
|
134
|
+
* - **Default** (omitted): Caching is enabled with `IdbCacheStore`
|
|
135
|
+
* (persists across page reloads). Replica backends (Gitee,
|
|
136
|
+
* RemoteStorage, etc.) are wrapped with `CachedFileSystem` to avoid
|
|
137
|
+
* redundant network reads via `getRevision` revalidation.
|
|
138
|
+
* - Pass a `CacheOptions` object to customize the store type, prefix,
|
|
139
|
+
* or TTL.
|
|
140
|
+
* - Pass `false` to disable caching entirely (all reads go directly
|
|
141
|
+
* to the backend).
|
|
142
|
+
*/
|
|
143
|
+
cache?: CacheOptions | false;
|
|
124
144
|
/** Custom serializer. */
|
|
125
145
|
serializer?: ConfigSerializer;
|
|
126
146
|
/** Custom conflict handler. Called before auto-resolution. */
|
|
@@ -461,10 +481,11 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
461
481
|
private disposed;
|
|
462
482
|
private configCache;
|
|
463
483
|
private readonly primaryBackendId;
|
|
484
|
+
private readonly cacheOptions?;
|
|
464
485
|
private readonly pollIntervalMs?;
|
|
465
486
|
/** Tombstone cache — avoids redundant reads within a single flush() cycle. */
|
|
466
487
|
private tombstoneCache;
|
|
467
|
-
constructor(appId: string, nodeId: string, primaryBackendId: string, cachedFS: MinimalAsyncFS, serializer: PathAwareSerializer, onConflict?: (conflict: ConflictInfo) => Promise<unknown | null>, pollIntervalMs?: number);
|
|
488
|
+
constructor(appId: string, nodeId: string, primaryBackendId: string, cachedFS: MinimalAsyncFS, serializer: PathAwareSerializer, onConflict?: (conflict: ConflictInfo) => Promise<unknown | null>, pollIntervalMs?: number, cacheOptions?: CacheOptions);
|
|
468
489
|
/** Full path to this node's directory on the primary backend. */
|
|
469
490
|
get nodePath(): string;
|
|
470
491
|
/** Number of replica backends registered (excludes the local primary). */
|
package/dist/index.d.ts
CHANGED
|
@@ -89,11 +89,20 @@ interface ConfigSerializer {
|
|
|
89
89
|
}
|
|
90
90
|
/** Cache configuration. */
|
|
91
91
|
interface CacheOptions {
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Type of cache store for replica backends.
|
|
94
|
+
* Default: `'IdbCacheStore'` — persists across page reloads so cached
|
|
95
|
+
* content + revision tokens survive. Use `'MemoryCacheStore'` for
|
|
96
|
+
* session-only caching (lost on reload).
|
|
97
|
+
*/
|
|
93
98
|
storeType?: 'MemoryCacheStore' | 'IdbCacheStore';
|
|
94
|
-
/** Cache store prefix (for IdbCacheStore). */
|
|
99
|
+
/** Cache store prefix (for IdbCacheStore). Default: `'zen-fs-config:'`. */
|
|
95
100
|
storePrefix?: string;
|
|
96
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* TTL in milliseconds for cache hits without revalidation.
|
|
103
|
+
* Default: 0 (always revalidate via `getRevision` — exact, no stale data).
|
|
104
|
+
* Ignored when the backend implements `getRevision`.
|
|
105
|
+
*/
|
|
97
106
|
ttlMs?: number;
|
|
98
107
|
}
|
|
99
108
|
/** Options for creating a ConfigRepo. */
|
|
@@ -119,8 +128,19 @@ interface ConfigRepoOptions {
|
|
|
119
128
|
idbStoreName?: string;
|
|
120
129
|
/** Node identifier. Auto-detected if not provided (see DESIGN.md §8.2). */
|
|
121
130
|
nodeId?: string;
|
|
122
|
-
/**
|
|
123
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Cache configuration for replica backends.
|
|
133
|
+
*
|
|
134
|
+
* - **Default** (omitted): Caching is enabled with `IdbCacheStore`
|
|
135
|
+
* (persists across page reloads). Replica backends (Gitee,
|
|
136
|
+
* RemoteStorage, etc.) are wrapped with `CachedFileSystem` to avoid
|
|
137
|
+
* redundant network reads via `getRevision` revalidation.
|
|
138
|
+
* - Pass a `CacheOptions` object to customize the store type, prefix,
|
|
139
|
+
* or TTL.
|
|
140
|
+
* - Pass `false` to disable caching entirely (all reads go directly
|
|
141
|
+
* to the backend).
|
|
142
|
+
*/
|
|
143
|
+
cache?: CacheOptions | false;
|
|
124
144
|
/** Custom serializer. */
|
|
125
145
|
serializer?: ConfigSerializer;
|
|
126
146
|
/** Custom conflict handler. Called before auto-resolution. */
|
|
@@ -461,10 +481,11 @@ declare class ConfigRepo implements IConfigRepo {
|
|
|
461
481
|
private disposed;
|
|
462
482
|
private configCache;
|
|
463
483
|
private readonly primaryBackendId;
|
|
484
|
+
private readonly cacheOptions?;
|
|
464
485
|
private readonly pollIntervalMs?;
|
|
465
486
|
/** Tombstone cache — avoids redundant reads within a single flush() cycle. */
|
|
466
487
|
private tombstoneCache;
|
|
467
|
-
constructor(appId: string, nodeId: string, primaryBackendId: string, cachedFS: MinimalAsyncFS, serializer: PathAwareSerializer, onConflict?: (conflict: ConflictInfo) => Promise<unknown | null>, pollIntervalMs?: number);
|
|
488
|
+
constructor(appId: string, nodeId: string, primaryBackendId: string, cachedFS: MinimalAsyncFS, serializer: PathAwareSerializer, onConflict?: (conflict: ConflictInfo) => Promise<unknown | null>, pollIntervalMs?: number, cacheOptions?: CacheOptions);
|
|
468
489
|
/** Full path to this node's directory on the primary backend. */
|
|
469
490
|
get nodePath(): string;
|
|
470
491
|
/** Number of replica backends registered (excludes the local primary). */
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,6 +30,36 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
33
|
+
// src/cache-wrapper.ts
|
|
34
|
+
var cache_wrapper_exports = {};
|
|
35
|
+
__export(cache_wrapper_exports, {
|
|
36
|
+
wrapWithCache: () => wrapWithCache
|
|
37
|
+
});
|
|
38
|
+
function wrapWithCache(backend, backendId, options) {
|
|
39
|
+
const storeType = options.storeType ?? "IdbCacheStore";
|
|
40
|
+
const prefix = options.storePrefix ?? `zen-fs-config:${backendId}:`;
|
|
41
|
+
let store;
|
|
42
|
+
if (storeType === "IdbCacheStore") {
|
|
43
|
+
store = new import_zen_fs_cache.IdbCacheStore(prefix);
|
|
44
|
+
} else {
|
|
45
|
+
store = new import_zen_fs_cache.MemoryCacheStore();
|
|
46
|
+
}
|
|
47
|
+
const wrapped = new import_zen_fs_cache.CachedFileSystem(backend, store, {
|
|
48
|
+
ttlMs: options.ttlMs ?? 0
|
|
49
|
+
});
|
|
50
|
+
if (typeof backend.shouldSync === "function") {
|
|
51
|
+
wrapped.shouldSync = (...args) => backend.shouldSync(...args);
|
|
52
|
+
}
|
|
53
|
+
return wrapped;
|
|
54
|
+
}
|
|
55
|
+
var import_zen_fs_cache;
|
|
56
|
+
var init_cache_wrapper = __esm({
|
|
57
|
+
"src/cache-wrapper.ts"() {
|
|
58
|
+
"use strict";
|
|
59
|
+
import_zen_fs_cache = require("zen-fs-cache");
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
30
63
|
// src/index.ts
|
|
31
64
|
var index_exports = {};
|
|
32
65
|
__export(index_exports, {
|
|
@@ -460,7 +493,7 @@ function versionPathFor(configFilePath) {
|
|
|
460
493
|
if (fileName.endsWith(".version")) {
|
|
461
494
|
return null;
|
|
462
495
|
}
|
|
463
|
-
const versionFileName = `.${fileName}.version`;
|
|
496
|
+
const versionFileName = fileName.startsWith(".") ? `${fileName}.version` : `.${fileName}.version`;
|
|
464
497
|
return dir ? `${dir}/${versionFileName}` : versionFileName;
|
|
465
498
|
}
|
|
466
499
|
async function sha256(data) {
|
|
@@ -585,10 +618,11 @@ var ConfigRepo = class {
|
|
|
585
618
|
disposed = false;
|
|
586
619
|
configCache = /* @__PURE__ */ new Map();
|
|
587
620
|
primaryBackendId;
|
|
621
|
+
cacheOptions;
|
|
588
622
|
pollIntervalMs;
|
|
589
623
|
/** Tombstone cache — avoids redundant reads within a single flush() cycle. */
|
|
590
624
|
tombstoneCache = null;
|
|
591
|
-
constructor(appId, nodeId, primaryBackendId, cachedFS, serializer, onConflict, pollIntervalMs) {
|
|
625
|
+
constructor(appId, nodeId, primaryBackendId, cachedFS, serializer, onConflict, pollIntervalMs, cacheOptions) {
|
|
592
626
|
this.appId = appId;
|
|
593
627
|
this.nodeId = nodeId;
|
|
594
628
|
this.primaryBackendId = primaryBackendId;
|
|
@@ -598,6 +632,7 @@ var ConfigRepo = class {
|
|
|
598
632
|
this.replicaBackends = /* @__PURE__ */ new Map();
|
|
599
633
|
this.onConflictCallback = onConflict;
|
|
600
634
|
this.pollIntervalMs = pollIntervalMs;
|
|
635
|
+
this.cacheOptions = cacheOptions;
|
|
601
636
|
this.fullFS = backendToSyncableFS(cachedFS, primaryBackendId);
|
|
602
637
|
this.fs = createChrootFS(cachedFS, `/${appId}`);
|
|
603
638
|
this.rootFS = createChrootFS(cachedFS, "/");
|
|
@@ -1036,7 +1071,13 @@ var ConfigRepo = class {
|
|
|
1036
1071
|
console.log(`[ConfigRepo] Creating replica backend: id=${desc.id}, type=${desc.type}`);
|
|
1037
1072
|
try {
|
|
1038
1073
|
const instance = await createBackend(desc);
|
|
1039
|
-
|
|
1074
|
+
let fsInstance = instance;
|
|
1075
|
+
if (this.cacheOptions) {
|
|
1076
|
+
const { wrapWithCache: wrapWithCache2 } = await Promise.resolve().then(() => (init_cache_wrapper(), cache_wrapper_exports));
|
|
1077
|
+
fsInstance = wrapWithCache2(instance, desc.id, this.cacheOptions);
|
|
1078
|
+
console.log(`[ConfigRepo] Replica ${desc.id} wrapped with CachedFileSystem (store=${this.cacheOptions.storeType ?? "IdbCacheStore"})`);
|
|
1079
|
+
}
|
|
1080
|
+
const syncable = backendToSyncableFS(fsInstance, `${desc.type}(${desc.id})`);
|
|
1040
1081
|
const pair = this.syncEngine.addPair(
|
|
1041
1082
|
this.fullFS,
|
|
1042
1083
|
syncable,
|
|
@@ -1047,7 +1088,7 @@ var ConfigRepo = class {
|
|
|
1047
1088
|
},
|
|
1048
1089
|
"/"
|
|
1049
1090
|
);
|
|
1050
|
-
this.replicaBackends.set(desc.id, { instance, syncable, pairId: pair.pairId });
|
|
1091
|
+
this.replicaBackends.set(desc.id, { instance: fsInstance, syncable, pairId: pair.pairId });
|
|
1051
1092
|
const conflictHandler = (event) => {
|
|
1052
1093
|
this.handleConflict(event);
|
|
1053
1094
|
};
|
|
@@ -1753,6 +1794,7 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1753
1794
|
options: { storeName: idbStoreName }
|
|
1754
1795
|
});
|
|
1755
1796
|
const cachedFS = primaryInstance;
|
|
1797
|
+
const cacheOptions = options.cache === false ? void 0 : options.cache ?? {};
|
|
1756
1798
|
try {
|
|
1757
1799
|
await primaryInstance.mkdir(META_DIR);
|
|
1758
1800
|
console.log(`[createConfigRepo] /.meta/ ready`);
|
|
@@ -1777,7 +1819,8 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1777
1819
|
cachedFS,
|
|
1778
1820
|
createSerializerChain(),
|
|
1779
1821
|
void 0,
|
|
1780
|
-
options.syncPollIntervalMs
|
|
1822
|
+
options.syncPollIntervalMs,
|
|
1823
|
+
cacheOptions
|
|
1781
1824
|
);
|
|
1782
1825
|
const oldBackendsMeta = await tempRepo.readMetaFile(BACKENDS_FILE);
|
|
1783
1826
|
if (oldBackendsMeta && oldBackendsMeta.backends?.length > 0) {
|
|
@@ -1843,7 +1886,8 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1843
1886
|
cachedFS,
|
|
1844
1887
|
serializer,
|
|
1845
1888
|
options.onConflict,
|
|
1846
|
-
options.syncPollIntervalMs
|
|
1889
|
+
options.syncPollIntervalMs,
|
|
1890
|
+
cacheOptions
|
|
1847
1891
|
);
|
|
1848
1892
|
await repo.setupSync(allBackends, LOCAL_IDB_BACKEND_ID, options.syncPollIntervalMs);
|
|
1849
1893
|
await repo.load();
|
package/dist/index.mjs
CHANGED
|
@@ -403,7 +403,7 @@ function versionPathFor(configFilePath) {
|
|
|
403
403
|
if (fileName.endsWith(".version")) {
|
|
404
404
|
return null;
|
|
405
405
|
}
|
|
406
|
-
const versionFileName = `.${fileName}.version`;
|
|
406
|
+
const versionFileName = fileName.startsWith(".") ? `${fileName}.version` : `.${fileName}.version`;
|
|
407
407
|
return dir ? `${dir}/${versionFileName}` : versionFileName;
|
|
408
408
|
}
|
|
409
409
|
async function sha256(data) {
|
|
@@ -528,10 +528,11 @@ var ConfigRepo = class {
|
|
|
528
528
|
disposed = false;
|
|
529
529
|
configCache = /* @__PURE__ */ new Map();
|
|
530
530
|
primaryBackendId;
|
|
531
|
+
cacheOptions;
|
|
531
532
|
pollIntervalMs;
|
|
532
533
|
/** Tombstone cache — avoids redundant reads within a single flush() cycle. */
|
|
533
534
|
tombstoneCache = null;
|
|
534
|
-
constructor(appId, nodeId, primaryBackendId, cachedFS, serializer, onConflict, pollIntervalMs) {
|
|
535
|
+
constructor(appId, nodeId, primaryBackendId, cachedFS, serializer, onConflict, pollIntervalMs, cacheOptions) {
|
|
535
536
|
this.appId = appId;
|
|
536
537
|
this.nodeId = nodeId;
|
|
537
538
|
this.primaryBackendId = primaryBackendId;
|
|
@@ -541,6 +542,7 @@ var ConfigRepo = class {
|
|
|
541
542
|
this.replicaBackends = /* @__PURE__ */ new Map();
|
|
542
543
|
this.onConflictCallback = onConflict;
|
|
543
544
|
this.pollIntervalMs = pollIntervalMs;
|
|
545
|
+
this.cacheOptions = cacheOptions;
|
|
544
546
|
this.fullFS = backendToSyncableFS(cachedFS, primaryBackendId);
|
|
545
547
|
this.fs = createChrootFS(cachedFS, `/${appId}`);
|
|
546
548
|
this.rootFS = createChrootFS(cachedFS, "/");
|
|
@@ -979,7 +981,13 @@ var ConfigRepo = class {
|
|
|
979
981
|
console.log(`[ConfigRepo] Creating replica backend: id=${desc.id}, type=${desc.type}`);
|
|
980
982
|
try {
|
|
981
983
|
const instance = await createBackend(desc);
|
|
982
|
-
|
|
984
|
+
let fsInstance = instance;
|
|
985
|
+
if (this.cacheOptions) {
|
|
986
|
+
const { wrapWithCache } = await import("./cache-wrapper-XQCPZJCD.mjs");
|
|
987
|
+
fsInstance = wrapWithCache(instance, desc.id, this.cacheOptions);
|
|
988
|
+
console.log(`[ConfigRepo] Replica ${desc.id} wrapped with CachedFileSystem (store=${this.cacheOptions.storeType ?? "IdbCacheStore"})`);
|
|
989
|
+
}
|
|
990
|
+
const syncable = backendToSyncableFS(fsInstance, `${desc.type}(${desc.id})`);
|
|
983
991
|
const pair = this.syncEngine.addPair(
|
|
984
992
|
this.fullFS,
|
|
985
993
|
syncable,
|
|
@@ -990,7 +998,7 @@ var ConfigRepo = class {
|
|
|
990
998
|
},
|
|
991
999
|
"/"
|
|
992
1000
|
);
|
|
993
|
-
this.replicaBackends.set(desc.id, { instance, syncable, pairId: pair.pairId });
|
|
1001
|
+
this.replicaBackends.set(desc.id, { instance: fsInstance, syncable, pairId: pair.pairId });
|
|
994
1002
|
const conflictHandler = (event) => {
|
|
995
1003
|
this.handleConflict(event);
|
|
996
1004
|
};
|
|
@@ -1696,6 +1704,7 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1696
1704
|
options: { storeName: idbStoreName }
|
|
1697
1705
|
});
|
|
1698
1706
|
const cachedFS = primaryInstance;
|
|
1707
|
+
const cacheOptions = options.cache === false ? void 0 : options.cache ?? {};
|
|
1699
1708
|
try {
|
|
1700
1709
|
await primaryInstance.mkdir(META_DIR);
|
|
1701
1710
|
console.log(`[createConfigRepo] /.meta/ ready`);
|
|
@@ -1720,7 +1729,8 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1720
1729
|
cachedFS,
|
|
1721
1730
|
createSerializerChain(),
|
|
1722
1731
|
void 0,
|
|
1723
|
-
options.syncPollIntervalMs
|
|
1732
|
+
options.syncPollIntervalMs,
|
|
1733
|
+
cacheOptions
|
|
1724
1734
|
);
|
|
1725
1735
|
const oldBackendsMeta = await tempRepo.readMetaFile(BACKENDS_FILE);
|
|
1726
1736
|
if (oldBackendsMeta && oldBackendsMeta.backends?.length > 0) {
|
|
@@ -1786,7 +1796,8 @@ async function createConfigRepo(appId, options = {}) {
|
|
|
1786
1796
|
cachedFS,
|
|
1787
1797
|
serializer,
|
|
1788
1798
|
options.onConflict,
|
|
1789
|
-
options.syncPollIntervalMs
|
|
1799
|
+
options.syncPollIntervalMs,
|
|
1800
|
+
cacheOptions
|
|
1790
1801
|
);
|
|
1791
1802
|
await repo.setupSync(allBackends, LOCAL_IDB_BACKEND_ID, options.syncPollIntervalMs);
|
|
1792
1803
|
await repo.load();
|