@rljson/fs-agent 0.0.26 → 0.0.28
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/fs-agent.d.ts +11 -0
- package/dist/fs-agent.js +39 -11
- package/dist/fs-scanner.d.ts +2 -0
- package/package.json +2 -2
package/dist/fs-agent.d.ts
CHANGED
|
@@ -274,6 +274,17 @@ export declare class FsAgent {
|
|
|
274
274
|
* must never be worth failing a sync over.
|
|
275
275
|
* @param ref - The ref the folder is now at.
|
|
276
276
|
*/
|
|
277
|
+
/**
|
|
278
|
+
* Starts the watcher unless it is already running.
|
|
279
|
+
*
|
|
280
|
+
* Both `syncToDb` and `syncFromDb` need a live watcher and either may be
|
|
281
|
+
* started first. `syncToDb` used to call `watch()` unconditionally, so
|
|
282
|
+
* starting them in the order `syncFromDb` then `syncToDb` threw "Already
|
|
283
|
+
* watching" — which quietly forced every caller into push-first, the order
|
|
284
|
+
* that lets a reconnecting client overwrite the network with a stale tree.
|
|
285
|
+
* A crash is a poor reason to choose an unsafe order.
|
|
286
|
+
*/
|
|
287
|
+
private _ensureWatching;
|
|
277
288
|
private _persistCurrentRef;
|
|
278
289
|
/**
|
|
279
290
|
* The ref this folder was last recorded at, from a previous run.
|
package/dist/fs-agent.js
CHANGED
|
@@ -458,6 +458,10 @@ class FsScanner {
|
|
|
458
458
|
get tree() {
|
|
459
459
|
return this._tree;
|
|
460
460
|
}
|
|
461
|
+
/** Whether a watcher is currently installed. */
|
|
462
|
+
get isWatching() {
|
|
463
|
+
return this._watcher !== null;
|
|
464
|
+
}
|
|
461
465
|
get rootPath() {
|
|
462
466
|
return this._rootPath;
|
|
463
467
|
}
|
|
@@ -1161,6 +1165,21 @@ class FsAgent {
|
|
|
1161
1165
|
* must never be worth failing a sync over.
|
|
1162
1166
|
* @param ref - The ref the folder is now at.
|
|
1163
1167
|
*/
|
|
1168
|
+
/**
|
|
1169
|
+
* Starts the watcher unless it is already running.
|
|
1170
|
+
*
|
|
1171
|
+
* Both `syncToDb` and `syncFromDb` need a live watcher and either may be
|
|
1172
|
+
* started first. `syncToDb` used to call `watch()` unconditionally, so
|
|
1173
|
+
* starting them in the order `syncFromDb` then `syncToDb` threw "Already
|
|
1174
|
+
* watching" — which quietly forced every caller into push-first, the order
|
|
1175
|
+
* that lets a reconnecting client overwrite the network with a stale tree.
|
|
1176
|
+
* A crash is a poor reason to choose an unsafe order.
|
|
1177
|
+
*/
|
|
1178
|
+
async _ensureWatching() {
|
|
1179
|
+
if (!this._scanner.isWatching) {
|
|
1180
|
+
await this._scanner.watch();
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1164
1183
|
_persistCurrentRef(ref) {
|
|
1165
1184
|
try {
|
|
1166
1185
|
writeFileSync(
|
|
@@ -1927,7 +1946,7 @@ ${err.stack}` : String(err);
|
|
|
1927
1946
|
}, this._timeouts.debounceMs);
|
|
1928
1947
|
};
|
|
1929
1948
|
this._scanner.onChange(debouncedSync);
|
|
1930
|
-
await this.
|
|
1949
|
+
await this._ensureWatching();
|
|
1931
1950
|
return () => {
|
|
1932
1951
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
1933
1952
|
this._scanner.offChange(debouncedSync);
|
|
@@ -2170,14 +2189,13 @@ ${err.stack}` : String(err);
|
|
|
2170
2189
|
* @returns Function to stop watching
|
|
2171
2190
|
*/
|
|
2172
2191
|
async syncFromDb(db, connector, treeKey, restoreOptions) {
|
|
2173
|
-
|
|
2174
|
-
await this._scanner.watch();
|
|
2175
|
-
}
|
|
2192
|
+
await this._ensureWatching();
|
|
2176
2193
|
let pendingRef = null;
|
|
2177
2194
|
let fromDbTimer = null;
|
|
2178
2195
|
let pendingRecoveryAttempt = 0;
|
|
2179
2196
|
let pendingPredecessorRefs;
|
|
2180
|
-
|
|
2197
|
+
let pendingIsNewest = true;
|
|
2198
|
+
const processRef = async (treeRef, recoveryAttempt = 0, predecessorRefs, isNewestFromSender = true) => {
|
|
2181
2199
|
const maxAttempts = this._timeouts.processRefRetries + 1;
|
|
2182
2200
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
2183
2201
|
this._scanner.pauseWatch();
|
|
@@ -2230,10 +2248,12 @@ ${err.stack}` : String(err);
|
|
|
2230
2248
|
}
|
|
2231
2249
|
const ancestryExpected = this._resolveConflicts;
|
|
2232
2250
|
const declaresAncestry = (predecessorRefs?.length ?? 0) > 0;
|
|
2233
|
-
const
|
|
2251
|
+
const staleAdvertisement = !isNewestFromSender;
|
|
2252
|
+
const withholdPrune = ancestryExpected && !declaresAncestry || staleAdvertisement;
|
|
2253
|
+
const applyOptions = restoreOptions?.cleanTarget && withholdPrune ? { ...restoreOptions, cleanTarget: false } : restoreOptions;
|
|
2234
2254
|
if (applyOptions !== restoreOptions) {
|
|
2235
2255
|
console.warn(
|
|
2236
|
-
`[FsAgent] ref=${treeRef.slice(0, 8)}…
|
|
2256
|
+
`[FsAgent] ref=${treeRef.slice(0, 8)}… ` + (staleAdvertisement ? "is not the newest its sender has advertised" : "declares no ancestry") + ` — applying additively, not pruning.`
|
|
2237
2257
|
);
|
|
2238
2258
|
}
|
|
2239
2259
|
await FsAgent._withTimeout(
|
|
@@ -2305,30 +2325,38 @@ ${err.stack}` : String(err);
|
|
|
2305
2325
|
);
|
|
2306
2326
|
}
|
|
2307
2327
|
};
|
|
2308
|
-
const scheduleProcess = (ref, delayMs, recoveryAttempt, predecessorRefs) => {
|
|
2328
|
+
const scheduleProcess = (ref, delayMs, recoveryAttempt, predecessorRefs, isNewestFromSender = true) => {
|
|
2309
2329
|
if (pendingRef && pendingRef !== ref) {
|
|
2310
2330
|
connector.invalidateReceived(pendingRef);
|
|
2311
2331
|
}
|
|
2312
2332
|
pendingRef = ref;
|
|
2313
2333
|
pendingRecoveryAttempt = recoveryAttempt;
|
|
2314
2334
|
pendingPredecessorRefs = predecessorRefs;
|
|
2335
|
+
pendingIsNewest = isNewestFromSender;
|
|
2315
2336
|
if (fromDbTimer) clearTimeout(fromDbTimer);
|
|
2316
2337
|
fromDbTimer = setTimeout(async () => {
|
|
2317
2338
|
fromDbTimer = null;
|
|
2318
2339
|
const r = pendingRef;
|
|
2319
2340
|
const ra = pendingRecoveryAttempt;
|
|
2320
2341
|
const pr = pendingPredecessorRefs;
|
|
2342
|
+
const newest = pendingIsNewest;
|
|
2321
2343
|
pendingRef = null;
|
|
2322
2344
|
if (r) {
|
|
2323
|
-
await processRef(r, ra, pr);
|
|
2345
|
+
await processRef(r, ra, pr, newest);
|
|
2324
2346
|
}
|
|
2325
2347
|
}, delayMs);
|
|
2326
2348
|
};
|
|
2327
|
-
const syncCallback = (treeRef, predecessorRefs) => {
|
|
2349
|
+
const syncCallback = (treeRef, predecessorRefs, info) => {
|
|
2328
2350
|
if (!treeRef || typeof treeRef !== "string") {
|
|
2329
2351
|
return Promise.resolve();
|
|
2330
2352
|
}
|
|
2331
|
-
scheduleProcess(
|
|
2353
|
+
scheduleProcess(
|
|
2354
|
+
treeRef,
|
|
2355
|
+
this._timeouts.debounceMs,
|
|
2356
|
+
0,
|
|
2357
|
+
predecessorRefs,
|
|
2358
|
+
info?.isNewestFromSender ?? true
|
|
2359
|
+
);
|
|
2332
2360
|
return Promise.resolve();
|
|
2333
2361
|
};
|
|
2334
2362
|
connector.listen(syncCallback);
|
package/dist/fs-scanner.d.ts
CHANGED
|
@@ -121,6 +121,8 @@ export declare class FsScanner {
|
|
|
121
121
|
private _vanishedDuringScan;
|
|
122
122
|
constructor(rootPath: string, options?: FsScanOptions);
|
|
123
123
|
get tree(): FsTree | null;
|
|
124
|
+
/** Whether a watcher is currently installed. */
|
|
125
|
+
get isWatching(): boolean;
|
|
124
126
|
get rootPath(): string;
|
|
125
127
|
get bs(): Bs;
|
|
126
128
|
scan(): Promise<FsTree>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rljson/fs-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
4
4
|
"description": "Rljson fs-agent description",
|
|
5
5
|
"homepage": "https://github.com/rljson/fs-agent",
|
|
6
6
|
"bugs": "https://github.com/rljson/fs-agent/issues",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@rljson/bs": "^0.0.21",
|
|
54
|
-
"@rljson/db": "^0.0.
|
|
54
|
+
"@rljson/db": "^0.0.32",
|
|
55
55
|
"@rljson/hash": "^0.0.18",
|
|
56
56
|
"@rljson/io": "^0.0.66",
|
|
57
57
|
"@rljson/json": "^0.0.23",
|