@rljson/fs-agent 0.0.59 → 0.0.60

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.
@@ -190,6 +190,16 @@ export declare const RESTORED_BLOB_MEMORY_MAX = 50000;
190
190
  * high concurrency is how a client runs out of heap.
191
191
  */
192
192
  export declare const RESTORE_FETCH_CONCURRENCY = 16;
193
+ /**
194
+ * How many of its own past states a node remembers.
195
+ *
196
+ * Used to recognise a sender that is BEHIND this node — one that declares a
197
+ * state this node has held and left. A burst on a large folder produces a few
198
+ * dozen states, so this is generous; forgetting the oldest costs only the
199
+ * ability to spot a very stale sender, which the mass-delete guard still
200
+ * catches.
201
+ */
202
+ export declare const STATE_HISTORY_MAX = 1000;
193
203
  /**
194
204
  * What an agent concluded about an inbound ref.
195
205
  *
@@ -290,6 +300,15 @@ export declare class FsAgent {
290
300
  /** Files written vs left alone by the current {@link restore}. */
291
301
  private _restoreWritten;
292
302
  private _restoreSkipped;
303
+ /**
304
+ * The states this node has held, oldest first.
305
+ *
306
+ * A push declaring one of these — but not the current one — comes from a
307
+ * sender that is behind this node. Applying such a tree is what puts a
308
+ * deleted file back, so it is ignored outright rather than applied
309
+ * additively.
310
+ */
311
+ private readonly _stateHistory;
293
312
  /**
294
313
  * Absolute paths of the files this agent has actually TOLD anyone about.
295
314
  *
@@ -753,6 +772,17 @@ export declare class FsAgent {
753
772
  * its own pushes, and the states it adopts from theirs.
754
773
  * @param tree - The tree that just went out, or was adopted as ours.
755
774
  */
775
+ /**
776
+ * Records a state this node is now in.
777
+ * @param ref - The content ref of that state.
778
+ */
779
+ private _rememberState;
780
+ /**
781
+ * Whether a push comes from a sender this node has already moved past.
782
+ * @param predecessorRefs - What the push declares it descends from.
783
+ * @returns True when every declared parent is a state this node has left.
784
+ */
785
+ private _senderIsBehind;
756
786
  private _rememberAnnounced;
757
787
  private _adoptAppliedRef;
758
788
  /**
package/dist/fs-agent.js CHANGED
@@ -1080,6 +1080,7 @@ const REFUSAL_ANSWER_COOLDOWN_MS = 5e3;
1080
1080
  const TREE_FETCH_CONCURRENCY = 64;
1081
1081
  const RESTORED_BLOB_MEMORY_MAX = 5e4;
1082
1082
  const RESTORE_FETCH_CONCURRENCY = 16;
1083
+ const STATE_HISTORY_MAX = 1e3;
1083
1084
  const VERDICT_REASON = {
1084
1085
  "own-echo": "is this agent's own last advertisement echoed back",
1085
1086
  stale: "is not the newest its sender has advertised"
@@ -1153,6 +1154,15 @@ class FsAgent {
1153
1154
  /** Files written vs left alone by the current {@link restore}. */
1154
1155
  _restoreWritten = 0;
1155
1156
  _restoreSkipped = 0;
1157
+ /**
1158
+ * The states this node has held, oldest first.
1159
+ *
1160
+ * A push declaring one of these — but not the current one — comes from a
1161
+ * sender that is behind this node. Applying such a tree is what puts a
1162
+ * deleted file back, so it is ignored outright rather than applied
1163
+ * additively.
1164
+ */
1165
+ _stateHistory = [];
1156
1166
  /**
1157
1167
  * Absolute paths of the files this agent has actually TOLD anyone about.
1158
1168
  *
@@ -1268,6 +1278,7 @@ class FsAgent {
1268
1278
  }
1269
1279
  }
1270
1280
  _persistCurrentRef(ref) {
1281
+ this._rememberState(ref);
1271
1282
  try {
1272
1283
  writeFileSync(
1273
1284
  join(this._rootPath, AGENT_STATE_FILE),
@@ -2378,6 +2389,28 @@ ${err.stack}` : String(err);
2378
2389
  * its own pushes, and the states it adopts from theirs.
2379
2390
  * @param tree - The tree that just went out, or was adopted as ours.
2380
2391
  */
2392
+ /**
2393
+ * Records a state this node is now in.
2394
+ * @param ref - The content ref of that state.
2395
+ */
2396
+ _rememberState(ref) {
2397
+ if (this._stateHistory.includes(ref)) return;
2398
+ this._stateHistory.push(ref);
2399
+ if (this._stateHistory.length > STATE_HISTORY_MAX) {
2400
+ this._stateHistory.shift();
2401
+ }
2402
+ }
2403
+ /**
2404
+ * Whether a push comes from a sender this node has already moved past.
2405
+ * @param predecessorRefs - What the push declares it descends from.
2406
+ * @returns True when every declared parent is a state this node has left.
2407
+ */
2408
+ _senderIsBehind(predecessorRefs) {
2409
+ if (predecessorRefs.length === 0) return false;
2410
+ return predecessorRefs.every(
2411
+ (ref) => ref !== this._currentRef && ref !== this._lastAppliedRef && this._stateHistory.includes(ref)
2412
+ );
2413
+ }
2381
2414
  _rememberAnnounced(tree) {
2382
2415
  this._announcedFiles.clear();
2383
2416
  for (const path of this._getFileContentMap(tree).keys()) {
@@ -2549,6 +2582,12 @@ ${err.stack}` : String(err);
2549
2582
  (r) => r !== void 0
2550
2583
  );
2551
2584
  const senderSawMyState = !ancestryIsCarried || !declaresAncestry || statesIAmIn.some((r) => predecessorRefs.includes(r));
2585
+ if (ancestryIsCarried && declaresAncestry && this._senderIsBehind(predecessorRefs)) {
2586
+ console.warn(
2587
+ `[FsAgent] ref=${treeRef.slice(0, 8)}… descends from a state this node has already left — ignoring, the sender will catch up and re-push.`
2588
+ );
2589
+ return;
2590
+ }
2552
2591
  const withholdPrune = restoreOptions?.cleanTarget && (ancestryExpected && !declaresAncestry || !senderSawMyState);
2553
2592
  const applyOptions = withholdPrune ? { ...restoreOptions, cleanTarget: false } : restoreOptions;
2554
2593
  if (applyOptions !== restoreOptions) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rljson/fs-agent",
3
- "version": "0.0.59",
3
+ "version": "0.0.60",
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",