@rljson/fs-agent 0.0.56 → 0.0.58
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 +16 -0
- package/dist/fs-agent.js +59 -1
- package/package.json +1 -1
package/dist/fs-agent.d.ts
CHANGED
|
@@ -276,6 +276,14 @@ export declare class FsAgent {
|
|
|
276
276
|
/** Files written vs left alone by the current {@link restore}. */
|
|
277
277
|
private _restoreWritten;
|
|
278
278
|
private _restoreSkipped;
|
|
279
|
+
/**
|
|
280
|
+
* Absolute paths of the files this agent has actually TOLD anyone about.
|
|
281
|
+
*
|
|
282
|
+
* A prune may only remove a file that peers could know exists. Anything
|
|
283
|
+
* written since the last announcement is invisible to every sender, so a
|
|
284
|
+
* tree that lacks it is not deleting it — it simply predates it.
|
|
285
|
+
*/
|
|
286
|
+
private readonly _announcedFiles;
|
|
279
287
|
/**
|
|
280
288
|
* Files this restore DELETED.
|
|
281
289
|
*
|
|
@@ -724,6 +732,14 @@ export declare class FsAgent {
|
|
|
724
732
|
* @returns `apply`, or the reason it is not news.
|
|
725
733
|
*/
|
|
726
734
|
private _inboundRefVerdict;
|
|
735
|
+
/**
|
|
736
|
+
* Records the files an announcement carried.
|
|
737
|
+
*
|
|
738
|
+
* Called wherever this agent commits to a state as "what peers know" —
|
|
739
|
+
* its own pushes, and the states it adopts from theirs.
|
|
740
|
+
* @param tree - The tree that just went out, or was adopted as ours.
|
|
741
|
+
*/
|
|
742
|
+
private _rememberAnnounced;
|
|
727
743
|
private _adoptAppliedRef;
|
|
728
744
|
/**
|
|
729
745
|
* Derives a deterministic content key from an FsTree.
|
package/dist/fs-agent.js
CHANGED
|
@@ -1152,6 +1152,14 @@ class FsAgent {
|
|
|
1152
1152
|
/** Files written vs left alone by the current {@link restore}. */
|
|
1153
1153
|
_restoreWritten = 0;
|
|
1154
1154
|
_restoreSkipped = 0;
|
|
1155
|
+
/**
|
|
1156
|
+
* Absolute paths of the files this agent has actually TOLD anyone about.
|
|
1157
|
+
*
|
|
1158
|
+
* A prune may only remove a file that peers could know exists. Anything
|
|
1159
|
+
* written since the last announcement is invisible to every sender, so a
|
|
1160
|
+
* tree that lacks it is not deleting it — it simply predates it.
|
|
1161
|
+
*/
|
|
1162
|
+
_announcedFiles = /* @__PURE__ */ new Set();
|
|
1155
1163
|
/**
|
|
1156
1164
|
* Files this restore DELETED.
|
|
1157
1165
|
*
|
|
@@ -1939,6 +1947,9 @@ ${err.stack}` : String(err);
|
|
|
1939
1947
|
}
|
|
1940
1948
|
}
|
|
1941
1949
|
} else if (!expectedFiles.has(fullPath) && preRestore.has(fullPath)) {
|
|
1950
|
+
if (this._announcedFiles.size > 0 && !this._announcedFiles.has(fullPath)) {
|
|
1951
|
+
continue;
|
|
1952
|
+
}
|
|
1942
1953
|
await rm(fullPath, { force: true });
|
|
1943
1954
|
this._restorePruned++;
|
|
1944
1955
|
}
|
|
@@ -1988,12 +1999,14 @@ ${err.stack}` : String(err);
|
|
|
1988
1999
|
this._currentRef = initialRef;
|
|
1989
2000
|
this._persistCurrentRef(initialRef);
|
|
1990
2001
|
this._lastSentContentKey = this._contentKeyFromTree(initialTree);
|
|
2002
|
+
this._rememberAnnounced(initialTree);
|
|
1991
2003
|
}
|
|
1992
2004
|
if (initialRef && !isSilentJoiner) {
|
|
1993
2005
|
this._lastSentRef = initialRef;
|
|
1994
2006
|
this._currentRef = initialRef;
|
|
1995
2007
|
this._persistCurrentRef(initialRef);
|
|
1996
2008
|
this._lastSentContentKey = this._contentKeyFromTree(initialTree);
|
|
2009
|
+
this._rememberAnnounced(initialTree);
|
|
1997
2010
|
await this._sendRef(
|
|
1998
2011
|
connector,
|
|
1999
2012
|
initialRef,
|
|
@@ -2030,7 +2043,35 @@ ${err.stack}` : String(err);
|
|
|
2030
2043
|
);
|
|
2031
2044
|
const ref = await FsAgent._withRetry(
|
|
2032
2045
|
() => FsAgent._withTimeout(
|
|
2033
|
-
|
|
2046
|
+
// skipNotification, because THIS AGENT broadcasts the ref
|
|
2047
|
+
// itself a few lines below, with the ancestry attached.
|
|
2048
|
+
//
|
|
2049
|
+
// Letting the insert notify sends the ref twice, and the
|
|
2050
|
+
// first copy is the wrong one: the connector attaches
|
|
2051
|
+
// whatever predecessors it currently holds, and this push has
|
|
2052
|
+
// not set its own yet — so the ref goes out carrying the
|
|
2053
|
+
// PREVIOUS push's parent. `_sendRef` then sets the right
|
|
2054
|
+
// ancestry and sends again, and every receiver drops that
|
|
2055
|
+
// second copy as already-received.
|
|
2056
|
+
//
|
|
2057
|
+
// Measured on the lab, sender against receivers:
|
|
2058
|
+
//
|
|
2059
|
+
// sent 6guj63Ox parent yNAJN-wC | seen parent CtAgdd1w
|
|
2060
|
+
// sent UBl35ZQQ parent 6guj63Ox | seen parent yNAJN-wC
|
|
2061
|
+
//
|
|
2062
|
+
// Every push arriving one parent behind. Harmless while
|
|
2063
|
+
// nothing read the ancestry; now that a receiver prunes only
|
|
2064
|
+
// for a sender that names a state it is in, it means a
|
|
2065
|
+
// correctly-formed deletion is refused by everyone — which is
|
|
2066
|
+
// exactly what the large-folder recipe has been reporting.
|
|
2067
|
+
//
|
|
2068
|
+
// The doubled `[sync:out]` line in every log was this, in
|
|
2069
|
+
// plain sight, for the whole investigation.
|
|
2070
|
+
dbAdapter.storeFsTree(tree, {
|
|
2071
|
+
...options,
|
|
2072
|
+
previous,
|
|
2073
|
+
skipNotification: true
|
|
2074
|
+
}),
|
|
2034
2075
|
this._timeouts.fetchTree,
|
|
2035
2076
|
`syncToDb → storeFsTree(${treeKey})`
|
|
2036
2077
|
),
|
|
@@ -2045,6 +2086,7 @@ ${err.stack}` : String(err);
|
|
|
2045
2086
|
}
|
|
2046
2087
|
this._lastSentRef = ref;
|
|
2047
2088
|
this._lastSentContentKey = contentKey;
|
|
2089
|
+
this._rememberAnnounced(tree);
|
|
2048
2090
|
if (parentRef && parentRef !== ref) {
|
|
2049
2091
|
connector.invalidateReceived(parentRef);
|
|
2050
2092
|
}
|
|
@@ -2311,6 +2353,19 @@ ${err.stack}` : String(err);
|
|
|
2311
2353
|
if (!isNewestFromSender) return "stale";
|
|
2312
2354
|
return "apply";
|
|
2313
2355
|
}
|
|
2356
|
+
/**
|
|
2357
|
+
* Records the files an announcement carried.
|
|
2358
|
+
*
|
|
2359
|
+
* Called wherever this agent commits to a state as "what peers know" —
|
|
2360
|
+
* its own pushes, and the states it adopts from theirs.
|
|
2361
|
+
* @param tree - The tree that just went out, or was adopted as ours.
|
|
2362
|
+
*/
|
|
2363
|
+
_rememberAnnounced(tree) {
|
|
2364
|
+
this._announcedFiles.clear();
|
|
2365
|
+
for (const path of this._getFileContentMap(tree).keys()) {
|
|
2366
|
+
this._announcedFiles.add(join(this._rootPath, path));
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2314
2369
|
_adoptAppliedRef(connector, treeRef) {
|
|
2315
2370
|
if (this._lastAppliedRef && this._lastAppliedRef !== treeRef) {
|
|
2316
2371
|
connector.invalidateSent?.(this._lastAppliedRef);
|
|
@@ -2380,6 +2435,7 @@ ${err.stack}` : String(err);
|
|
|
2380
2435
|
const ref = await dbAdapter.storeFsTree(tree, { previous });
|
|
2381
2436
|
this._lastSentRef = ref;
|
|
2382
2437
|
this._lastSentContentKey = this._contentKeyFromTree(tree);
|
|
2438
|
+
this._rememberAnnounced(tree);
|
|
2383
2439
|
this._currentRef = ref;
|
|
2384
2440
|
return ref;
|
|
2385
2441
|
}
|
|
@@ -2443,6 +2499,7 @@ ${err.stack}` : String(err);
|
|
|
2443
2499
|
this._currentRef = treeRef;
|
|
2444
2500
|
this._persistCurrentRef(treeRef);
|
|
2445
2501
|
this._lastSentContentKey = this._contentKeyFromTree(currentTree);
|
|
2502
|
+
this._rememberAnnounced(currentTree);
|
|
2446
2503
|
return;
|
|
2447
2504
|
}
|
|
2448
2505
|
if (this._resolveConflicts && this._currentRef && predecessorRefs && predecessorRefs.length > 0) {
|
|
@@ -2517,6 +2574,7 @@ ${err.stack}` : String(err);
|
|
|
2517
2574
|
if (!hasNewsOfOurOwn) {
|
|
2518
2575
|
this._lastSentRef = postRestoreRef;
|
|
2519
2576
|
this._lastSentContentKey = this._contentKeyFromTree(postRestoreTree);
|
|
2577
|
+
this._rememberAnnounced(postRestoreTree);
|
|
2520
2578
|
if (postRestoreFiles.size < incomingFiles.size) {
|
|
2521
2579
|
console.log(
|
|
2522
2580
|
`[FsAgent] ref=${treeRef.slice(0, 8)}… left this node behind (${postRestoreFiles.size} of ${incomingFiles.size} files) — not announcing a state that is catching up.`
|