@rljson/fs-agent 0.0.12 → 0.0.15
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 +130 -1
- package/dist/fs-agent.js +810 -90
- package/dist/fs-conflict-resolver.d.ts +181 -0
- package/dist/fs-db-adapter.d.ts +8 -0
- package/dist/fs-scanner.d.ts +40 -6
- package/dist/index.d.ts +1 -0
- package/package.json +20 -10
package/dist/fs-agent.d.ts
CHANGED
|
@@ -44,6 +44,14 @@ export interface FsAgentOptions {
|
|
|
44
44
|
* auto-generates its own identity.
|
|
45
45
|
*/
|
|
46
46
|
clientIdentity?: ClientId;
|
|
47
|
+
/**
|
|
48
|
+
* Enable Nextcloud-style conflict resolution. When true, {@link syncFromDb}
|
|
49
|
+
* registers a DAG-branch conflict observer that resolves forks into a single
|
|
50
|
+
* merge revision (winner keeps the path, loser is renamed). This is a
|
|
51
|
+
* **client-only** behaviour — hubs are dumb relays and must leave it off
|
|
52
|
+
* (the default). See `doc/conflict-resolution-design.md`.
|
|
53
|
+
*/
|
|
54
|
+
resolveConflicts?: boolean;
|
|
47
55
|
}
|
|
48
56
|
/** Restore options */
|
|
49
57
|
export interface RestoreOptions {
|
|
@@ -85,9 +93,26 @@ export interface TimeoutConfig {
|
|
|
85
93
|
* Each retry waits `attempt * processRefRetryDelayMs` (i.e. 5s, 10s, 15s).
|
|
86
94
|
*/
|
|
87
95
|
processRefRetryDelayMs?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Number of **recovery re-queues** for a ref whose per-cycle retries were
|
|
98
|
+
* all exhausted (e.g. `db.get` kept timing out because the transport was
|
|
99
|
+
* disconnected/contended during a hub crash, reconnect or restart). Instead
|
|
100
|
+
* of permanently dropping the ref — which loses the file written in that
|
|
101
|
+
* window — it is re-queued up to this many times so it is eventually applied
|
|
102
|
+
* once the transport recovers. A newer incoming ref supersedes a pending
|
|
103
|
+
* recovery; `tearDown()` stops it. Default: 10. Set 0 to restore the old
|
|
104
|
+
* drop-on-exhaustion behaviour.
|
|
105
|
+
*/
|
|
106
|
+
recoveryRetries?: number;
|
|
88
107
|
}
|
|
89
108
|
/** Filename for sync error log written to the sync folder */
|
|
90
109
|
export declare const SYNC_ERROR_FILE = ".sync-errors.log";
|
|
110
|
+
/**
|
|
111
|
+
* Filename prefix for the staging files used by atomic writes. The scanner
|
|
112
|
+
* ignores anything starting with this so the transient temp + rename never
|
|
113
|
+
* pollutes the tree or churns the watcher.
|
|
114
|
+
*/
|
|
115
|
+
export declare const ATOMIC_TMP_PREFIX = ".fsagent-tmp-";
|
|
91
116
|
/**
|
|
92
117
|
* Orchestrates filesystem operations with tree structures and blob storage
|
|
93
118
|
*/
|
|
@@ -104,6 +129,15 @@ export declare class FsAgent {
|
|
|
104
129
|
/** Content fingerprint of the last tree we broadcasted (paths+blobIds) */
|
|
105
130
|
private _lastSentContentKey?;
|
|
106
131
|
private _timeouts;
|
|
132
|
+
/** Client-only: resolve DAG-branch conflicts into merge revisions. */
|
|
133
|
+
private _resolveConflicts;
|
|
134
|
+
/**
|
|
135
|
+
* Ancestry head: the content ref of the revision currently representing the
|
|
136
|
+
* filesystem state. New local revisions descend from it; received revisions
|
|
137
|
+
* advance it. Only tracked when `resolveConflicts` is enabled, so the
|
|
138
|
+
* InsertHistory predecessor DAG forms only where conflict resolution is on.
|
|
139
|
+
*/
|
|
140
|
+
private _currentRef?;
|
|
107
141
|
constructor(rootPath: string, bs?: Bs, options?: FsAgentOptions);
|
|
108
142
|
/**
|
|
109
143
|
* Gets the root path
|
|
@@ -132,6 +166,38 @@ export declare class FsAgent {
|
|
|
132
166
|
* @param err - The error value caught
|
|
133
167
|
*/
|
|
134
168
|
_writeSyncError(context: string, err: unknown): void;
|
|
169
|
+
/**
|
|
170
|
+
* Extracts a human-readable message from a thrown value. The non-`Error`
|
|
171
|
+
* branch is defensive (the DB/transport always throw `Error`s).
|
|
172
|
+
* @param err - The caught value.
|
|
173
|
+
* @returns A message string.
|
|
174
|
+
*/
|
|
175
|
+
private static _errMessage;
|
|
176
|
+
/**
|
|
177
|
+
* Retries an async operation up to `attempts` times with exponential backoff
|
|
178
|
+
* (each delay doubles from `baseDelayMs`). For transient failures — a file
|
|
179
|
+
* briefly locked by antivirus or a save-and-rename editor, a peer briefly
|
|
180
|
+
* unreachable. Non-final failures are logged once at warn level so retry
|
|
181
|
+
* pressure is visible without log-spam.
|
|
182
|
+
* @param fn - The operation to run
|
|
183
|
+
* @param attempts - Maximum number of attempts
|
|
184
|
+
* @param baseDelayMs - Initial backoff delay (doubles each retry)
|
|
185
|
+
* @param label - Human-readable label for log messages
|
|
186
|
+
* @returns The operation's resolved value
|
|
187
|
+
*/
|
|
188
|
+
private static _withRetry;
|
|
189
|
+
/**
|
|
190
|
+
* Atomically writes a file: stages the content in a sibling `.<rand>.tmp`,
|
|
191
|
+
* then renames over the target. The rename is atomic, so a crash mid-write
|
|
192
|
+
* leaves only the temp behind — never a half-written target file. (We do not
|
|
193
|
+
* `fsync` the temp: it adds significant per-file latency under bursty
|
|
194
|
+
* restores, and durability-on-power-loss is secondary here since the content
|
|
195
|
+
* is replicated and re-synced.) The random suffix keeps concurrent restores
|
|
196
|
+
* of the same path from trampling each other.
|
|
197
|
+
* @param filePath - Destination path
|
|
198
|
+
* @param content - Bytes to write
|
|
199
|
+
*/
|
|
200
|
+
private static _atomicWriteFile;
|
|
135
201
|
/**
|
|
136
202
|
* Wraps a promise with a timeout.
|
|
137
203
|
* Rejects with a descriptive error if the promise does not settle
|
|
@@ -147,6 +213,9 @@ export declare class FsAgent {
|
|
|
147
213
|
* otherwise falls back to fire-and-forget `send()`.
|
|
148
214
|
* @param connector - The Connector to send through
|
|
149
215
|
* @param ref - The ref to broadcast
|
|
216
|
+
* @param predecessorRefs - Causal predecessor content refs to attach (for
|
|
217
|
+
* conflict ancestry); set explicitly here because the FsAgent broadcasts
|
|
218
|
+
* via an explicit send, which pre-empts the Connector's db-observer path.
|
|
150
219
|
*/
|
|
151
220
|
private _sendRef;
|
|
152
221
|
/**
|
|
@@ -179,6 +248,14 @@ export declare class FsAgent {
|
|
|
179
248
|
* @param options - Restore options
|
|
180
249
|
*/
|
|
181
250
|
restore(tree: FsTree, targetPath?: string, options?: RestoreOptions): Promise<void>;
|
|
251
|
+
/**
|
|
252
|
+
* Recursively collects the absolute paths of all files under `currentDir`.
|
|
253
|
+
* Used to snapshot the pre-restore file set for prune race-protection.
|
|
254
|
+
* @param currentDir - Directory to walk
|
|
255
|
+
* @param out - Accumulator set (created if omitted)
|
|
256
|
+
* @returns The set of absolute file paths
|
|
257
|
+
*/
|
|
258
|
+
private _collectAllFiles;
|
|
182
259
|
/**
|
|
183
260
|
* Recursively restores a tree node and its children
|
|
184
261
|
* @param treeHash - Hash of the tree node to restore
|
|
@@ -246,10 +323,13 @@ export declare class FsAgent {
|
|
|
246
323
|
*/
|
|
247
324
|
private _collectExpectedPaths;
|
|
248
325
|
/**
|
|
249
|
-
* Remove files/dirs not present in the expected sets
|
|
326
|
+
* Remove files/dirs not present in the expected sets, preserving any file
|
|
327
|
+
* that appeared *during* the restore (not in `preRestore`) — a fresh user
|
|
328
|
+
* write that must not be clobbered.
|
|
250
329
|
* @param currentDir - Directory currently being inspected
|
|
251
330
|
* @param expectedDirs - Allowed directory paths
|
|
252
331
|
* @param expectedFiles - Allowed file paths
|
|
332
|
+
* @param preRestore - Files present before the restore (prune candidates)
|
|
253
333
|
*/
|
|
254
334
|
private _pruneExtraneous;
|
|
255
335
|
/**
|
|
@@ -262,6 +342,44 @@ export declare class FsAgent {
|
|
|
262
342
|
* @returns Function to stop watching
|
|
263
343
|
*/
|
|
264
344
|
syncToDb(db: Db, connector: Connector, treeKey: string, options?: StoreFsTreeOptions): Promise<() => void>;
|
|
345
|
+
/**
|
|
346
|
+
* Resolves the `previous` (InsertHistory predecessor timeIds) for a new
|
|
347
|
+
* revision from the parent's shared content refs. timeIds are per-db, so we
|
|
348
|
+
* map each shared parent ref to *this* db's local timeId(s). Returns undefined
|
|
349
|
+
* when ancestry tracking is off (default) or no parent is known — in which
|
|
350
|
+
* case the store behaves exactly as before.
|
|
351
|
+
* @param db - Database instance
|
|
352
|
+
* @param treeKey - Tree table key
|
|
353
|
+
* @param parentRefs - Parent content refs (local head, or received predecessors)
|
|
354
|
+
*/
|
|
355
|
+
private _ancestryPrevious;
|
|
356
|
+
/**
|
|
357
|
+
* Classifies an incoming revision relative to our current head using the
|
|
358
|
+
* local InsertHistory DAG (keyed on shared content refs):
|
|
359
|
+
* - `behind` → incoming descends from our head → fast-forward (restore).
|
|
360
|
+
* - `ahead` → our head descends from incoming (e.g. a reconnect bootstrap
|
|
361
|
+
* re-sending an older ancestor) → ignore; we are newer.
|
|
362
|
+
* - `diverged` → siblings produced by concurrent edits → resolve the fork.
|
|
363
|
+
* @param db - Database instance
|
|
364
|
+
* @param treeKey - Tree table key
|
|
365
|
+
* @param currentRef - Our current head's content ref
|
|
366
|
+
* @param incomingRef - The incoming revision's content ref
|
|
367
|
+
* @param incomingPredecessorRefs - The incoming revision's predecessor refs
|
|
368
|
+
*/
|
|
369
|
+
private _ancestryRelation;
|
|
370
|
+
/**
|
|
371
|
+
* Resolves a divergent incoming revision inline (called from `processRef`
|
|
372
|
+
* with the watcher paused, so resolution cannot race the sync loop). Records
|
|
373
|
+
* the incoming revision as a fork tip without clobbering local content, then
|
|
374
|
+
* merges our head and the incoming tip into a single merge revision D that is
|
|
375
|
+
* materialised to disk and broadcast.
|
|
376
|
+
* @param db - Database instance
|
|
377
|
+
* @param treeKey - Tree table key
|
|
378
|
+
* @param incomingRef - The incoming revision's content ref
|
|
379
|
+
* @param incomingTree - The fetched incoming tree
|
|
380
|
+
* @param predecessorRefs - The incoming revision's predecessor content refs
|
|
381
|
+
*/
|
|
382
|
+
private _resolveConflictInline;
|
|
265
383
|
/**
|
|
266
384
|
* Builds a map of relativePath → blobId for all files in a tree.
|
|
267
385
|
* Used to compare trees by content rather than by hash (which includes mtime).
|
|
@@ -289,6 +407,17 @@ export declare class FsAgent {
|
|
|
289
407
|
* @param b - Second tree to compare
|
|
290
408
|
*/
|
|
291
409
|
private _treesHaveEquivalentContent;
|
|
410
|
+
/**
|
|
411
|
+
* Builds the dependency surface a {@link FsConflictResolver} needs, wiring it
|
|
412
|
+
* to this agent's db, blob store, scanner, and working directory.
|
|
413
|
+
*
|
|
414
|
+
* The merge store records the merged ref/content key as the last-sent state,
|
|
415
|
+
* so the watcher-driven re-scan that follows the on-disk materialisation
|
|
416
|
+
* settles to a no-op instead of re-broadcasting.
|
|
417
|
+
* @param db - Database instance
|
|
418
|
+
* @param treeKey - Tree table key
|
|
419
|
+
*/
|
|
420
|
+
private _buildConflictResolverDeps;
|
|
292
421
|
/**
|
|
293
422
|
* Watches database for tree changes and syncs to filesystem
|
|
294
423
|
* Uses Connector for socket-based notifications
|