@vrtmrz/livesync-commonlib 0.1.25 → 0.1.26

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.
@@ -44,6 +44,7 @@ export declare abstract class ServiceFileHandlerBase extends ServiceModuleBase<S
44
44
  private setting;
45
45
  private vault;
46
46
  private fileReflectionProvenance?;
47
+ private readonly deferredConflictChecks;
47
48
  constructor(services: ServiceFileHandlerDependencies);
48
49
  get db(): DatabaseFileAccess;
49
50
  get storage(): StorageAccess;
@@ -52,31 +53,148 @@ export declare abstract class ServiceFileHandlerBase extends ServiceModuleBase<S
52
53
  private deleteProvenance;
53
54
  private findUniqueContentRevision;
54
55
  private getProvenBaseRevision;
56
+ /**
57
+ * Classify a storage read against its displayed database branch.
58
+ *
59
+ * A recorded revision identifies the branch and therefore takes precedence
60
+ * over byte matches. Without a readable base, only current live leaves are
61
+ * considered. Any match avoids a duplicate write, but only a unique match
62
+ * identifies provenance; no match is `unknown`.
63
+ * `preferredPath` selects the provenance key during a rename, and
64
+ * `currentLoaded` supplies an already decoded candidate body when available.
65
+ * Missing chunks do not clear provenance unless their metadata is also
66
+ * missing or deleted.
67
+ */
68
+ private classifyStorageContent;
69
+ /** Record provenance only when storage matches one unambiguous live leaf. */
70
+ private rememberMatchingLeaf;
71
+ /**
72
+ * Read the raw provenance value used by an optimistic storage snapshot.
73
+ * Unlike `getProvenance`, this does not validate or remove the referenced
74
+ * database revision; an unreadable value is represented as `undefined`.
75
+ */
76
+ private readProvenanceSnapshot;
77
+ /**
78
+ * Check that storage still has the same path, bytes, timestamp, and
79
+ * provenance as a previously captured snapshot.
80
+ */
81
+ private currentStorageSnapshotMatches;
82
+ /**
83
+ * Publish provenance after a durable database write if storage is unchanged.
84
+ * A failed revalidation leaves the database revision stored and asks the
85
+ * caller to reread storage before proceeding.
86
+ */
87
+ private finaliseStoredRevision;
88
+ /**
89
+ * Reread storage after a restored startup snapshot changed and retry a save.
90
+ * The bounded counter prevents an unstable file from causing an endless
91
+ * startup loop; the next event or scan can reconcile a final refusal.
92
+ */
93
+ private retryCurrentStorage;
55
94
  getPath(entry: AnyEntry): FilePathWithPrefix;
56
95
  getPathWithoutPrefix(entry: AnyEntry): FilePathWithPrefix;
57
96
  readFileFromStub(file: UXFileInfoStub | UXFileInfo): Promise<UXFileInfo>;
58
97
  private infoToStub;
59
98
  storeFileToDB(info: UXFileInfoStub | UXFileInfo | UXInternalFileInfoStub | FilePathWithPrefix, force?: boolean, onlyChunks?: boolean): Promise<boolean>;
60
99
  storeFileToDBWithBaseRevision(info: UXFileInfoStub | UXFileInfo | FilePathWithPrefix, baseRevision: string, createIfDifferent?: boolean): Promise<boolean>;
100
+ /**
101
+ * Store current storage content below an exact live revision. A differing
102
+ * body is rejected when `createIfDifferent` is false; callers hold the
103
+ * document lock for this core operation.
104
+ */
105
+ private storeFileToDBWithBaseRevisionCore;
106
+ /**
107
+ * Save one storage snapshot while the caller holds its document lock.
108
+ * Ordinary saves read once inside the lock and leave retries disabled.
109
+ * Restored startup events retain bounded rereads because file watching has
110
+ * not yet started and cannot supply a new event for a concurrent edit.
111
+ */
61
112
  private storeFileToDBFromRevision;
62
113
  deleteFileFromDB(info: UXFileInfoStub | UXInternalFileInfoStub | FilePath): Promise<boolean>;
114
+ /**
115
+ * Record a storage deletion under the caller's document lock, preserving
116
+ * the displayed conflict branch when its provenance is known.
117
+ */
118
+ private deleteFileFromDBCore;
63
119
  renameFileInDB(info: UXFileInfoStub | UXFileInfo, oldPath: FilePath | FilePathWithPrefix): Promise<boolean>;
120
+ /**
121
+ * Record a rename under the caller's source and target locks. Case-only
122
+ * renames retain the source provenance while other renames handle the
123
+ * source conflict before deleting its old path.
124
+ */
125
+ private renameFileInDBCore;
64
126
  deleteRevisionFromDB(info: UXFileInfoStub | FilePath | FilePathWithPrefix, rev: string): Promise<boolean | undefined>;
127
+ /** Delete one selected revision and clear matching provenance under the caller's document lock. */
128
+ private deleteRevisionFromDBCore;
65
129
  resolveConflictedByDeletingRevision(info: UXFileInfoStub | FilePath, rev: string): Promise<boolean | undefined>;
130
+ /**
131
+ * Apply the legacy resolve-by-delete sequence while holding one document
132
+ * lock; metadata is captured before the selected revision is removed.
133
+ */
134
+ private resolveConflictedByDeletingRevisionCore;
66
135
  dbToStorageWithSpecificRev(info: UXFileInfoStub | UXFileInfo | FilePath | FilePathWithPrefix | null, rev: string, force?: boolean): Promise<boolean>;
136
+ /**
137
+ * Reflect a selected live revision under the caller's document lock.
138
+ * The public wrapper performs the same lock acquisition before entering here.
139
+ */
140
+ private dbToStorageWithSpecificRevCore;
67
141
  dbToStorage(entryInfo: MetaEntry | FilePathWithPrefix, info: UXFileInfoStub | UXFileInfo | FilePath | null, force?: boolean): Promise<boolean>;
142
+ /** Fetch the current metadata and reflect it while the caller holds its lock. */
143
+ private dbToStorageCore;
144
+ /**
145
+ * Reflect one database revision while protecting concurrent storage edits.
146
+ * Overwrite and deletion paths preserve changed bytes first, then revalidate
147
+ * their snapshot and the current database revision immediately before the
148
+ * destructive operation.
149
+ */
68
150
  private applyDatabaseEntryToStorage;
151
+ /**
152
+ * Preserve storage bytes before an incoming reflection can replace them.
153
+ *
154
+ * The initial read and provenance form one candidate snapshot. Classification
155
+ * gives a recorded branch priority, deduplicates matching live leaves, or
156
+ * creates an independent root for unknown origin. Optional `incomingContent` avoids
157
+ * re-reading an already loaded incoming body. Storage is revalidated before
158
+ * and after the database write; `preserved: true` stops the incoming write,
159
+ * while a returned `snapshot` lets the caller continue only after revalidation.
160
+ */
69
161
  private preserveUnsyncedStorageAsConflict;
162
+ /**
163
+ * Repeat overwrite protection from a fresh storage read after a snapshot
164
+ * becomes stale. Existing conflicts may defer reflection and request an
165
+ * open-only check while leaving the current storage untouched.
166
+ */
167
+ private recheckIncomingStorage;
168
+ /**
169
+ * Re-read database metadata immediately before reflecting an incoming entry.
170
+ * If the current revision advanced, recurse with fresh storage; force and
171
+ * explicitly allowed conflict writes retain the caller's selected entry.
172
+ * `incomingRetry` bounds repeated database churn.
173
+ */
174
+ private reconcileAdvancedIncomingRevision;
70
175
  private _anyHandlerProcessesFileEvent;
71
176
  private canRecordStorageDeletion;
177
+ /**
178
+ * Serialise work by every document ID represented by the supplied paths.
179
+ * IDs are deduplicated and sorted so aliases take one lock and cross-document renames have one order.
180
+ * The callback runs under those locks and must use core methods; conflict
181
+ * hooks requested inside it are deferred until all locks have been released.
182
+ */
72
183
  private serializedByFileEventPaths;
184
+ /**
185
+ * Queue a conflict hook while its document lock is held, or run it directly
186
+ * when no matching lock is active. Preserve `ifOpen` across deferral.
187
+ */
188
+ private queueConflictCheckFor;
73
189
  /**
74
190
  * Revalidate a persisted storage operation against the current storage state.
75
191
  *
76
192
  * Snapshot entries preserve operation intent and ordering only. Their file
77
193
  * stub, timestamps, and existence assumptions can be stale after a restart.
78
194
  * Current inclusion is therefore read from storage, while destructive work
79
- * is allowed only after current absence has been observed.
195
+ * is allowed only after current absence has been observed. Store and rename
196
+ * actions use the fresh reread with bounded retry because no later watcher
197
+ * event is guaranteed after startup reconciliation.
80
198
  */
81
199
  private _processRestoredFileEvent;
82
200
  private _planRestoredFileEvent;