@vrtmrz/livesync-commonlib 0.1.1 → 0.1.2-rc.0
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.
|
@@ -979,20 +979,17 @@ ${sendingDocs}/(${sentDocsCount} done)`,
|
|
|
979
979
|
return false;
|
|
980
980
|
}
|
|
981
981
|
const remoteChunks = await ret.db.allDocs({ keys: missingChunks, include_docs: true });
|
|
982
|
-
|
|
982
|
+
const errorRows = remoteChunks.rows.filter((e) => "error" in e);
|
|
983
|
+
if (errorRows.length > 0) {
|
|
983
984
|
Logger(
|
|
984
|
-
`Some chunks
|
|
985
|
+
`Some requested chunks were not found in the remote database.`,
|
|
985
986
|
showResult ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO,
|
|
986
987
|
"fetch"
|
|
987
988
|
);
|
|
988
|
-
Logger(`
|
|
989
|
-
Logger(
|
|
990
|
-
`Error chunks: ${remoteChunks.rows.filter((e) => "error" in e).map((e) => e.key).join(",")}`,
|
|
991
|
-
LOG_LEVEL_VERBOSE
|
|
992
|
-
);
|
|
993
|
-
return false;
|
|
989
|
+
Logger(`Requested chunks: ${missingChunks.join(",")}`, LOG_LEVEL_VERBOSE);
|
|
990
|
+
Logger(`Error chunks: ${errorRows.map((e) => e.key).join(",")}`, LOG_LEVEL_VERBOSE);
|
|
994
991
|
}
|
|
995
|
-
const remoteChunkItems = remoteChunks.rows.map((e) => e.doc);
|
|
992
|
+
const remoteChunkItems = remoteChunks.rows.filter((e) => !("error" in e)).map((e) => e.doc).filter((e) => e !== void 0);
|
|
996
993
|
return remoteChunkItems;
|
|
997
994
|
}
|
|
998
995
|
async tryConnectRemote(setting, showResult = true) {
|
|
@@ -208,7 +208,7 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
208
208
|
return false;
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
-
async findContentRevisionsInternal(file, content, currentRev) {
|
|
211
|
+
async findContentRevisionsInternal(file, content, currentRev, stopAfterFirstMatch = false) {
|
|
212
212
|
const filename = getDatabasePathFromUXFileInfo(file);
|
|
213
213
|
try {
|
|
214
214
|
const doc = await this.database.localDatabase.getDBEntryMeta(
|
|
@@ -255,6 +255,9 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
255
255
|
const entry = await this.database.localDatabase.getDBEntry(filename, { rev }, false, true, true);
|
|
256
256
|
if (entry !== false && !entry._deleted && await isDocContentSame(readContent(entry), content)) {
|
|
257
257
|
matchingRevisions.push(rev);
|
|
258
|
+
if (stopAfterFirstMatch) {
|
|
259
|
+
return matchingRevisions;
|
|
260
|
+
}
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
return matchingRevisions;
|
|
@@ -274,7 +277,7 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
274
277
|
if (!await this.checkIsTargetFile(file)) {
|
|
275
278
|
return true;
|
|
276
279
|
}
|
|
277
|
-
return (await this.findContentRevisionsInternal(file, content, currentRev)).length > 0;
|
|
280
|
+
return (await this.findContentRevisionsInternal(file, content, currentRev, true)).length > 0;
|
|
278
281
|
}
|
|
279
282
|
async getConflictedRevs(file) {
|
|
280
283
|
if (!await this.checkIsTargetFile(file)) {
|
|
@@ -35,7 +35,9 @@ Commonlib follows these rules:
|
|
|
35
35
|
- preserve storage content as a conflict when it is absent from all available branches; and
|
|
36
36
|
- never select the newest modification time as a package-level default.
|
|
37
37
|
|
|
38
|
-
The
|
|
38
|
+
The history check gathers available revisions from every live branch, then compares revision bodies until it finds an exact byte match. Its boolean existence form stops at that first match because older bodies cannot change the answer. This avoids reading obsolete historical bodies whose unique chunks were collected after they stopped being reachable.
|
|
39
|
+
|
|
40
|
+
This check matters after a resolution has propagated. If a receiving device still displays the deleted losing revision, that exact content is known synchronised content. The resolved winner may replace it without recreating the conflict. If the device has edited that content again, the bytes no longer match the old revision, so the storage-protection guard preserves the new edit.
|
|
39
41
|
|
|
40
42
|
## Resolution classes
|
|
41
43
|
|
package/docs/development.md
CHANGED
|
@@ -59,7 +59,9 @@ The multiple-remote profile contract has focused unit coverage in `src/serviceFe
|
|
|
59
59
|
|
|
60
60
|
The full offline scanner applies both the host's target-file policy and Commonlib's built-in `shouldBeIgnored` policy before constructing storage/database pairs. This keeps legacy LiveSync log files and flag files outside ordinary create, restore, and delete decisions even when a host accepts their file extension. Lower-level write guards remain a defence for direct calls; they are not the normal full-scan filter. Extend both storage-collection and database-collection tests whenever the built-in exclusions change.
|
|
61
61
|
|
|
62
|
-
The revision-tree safety rules are documented in [Conflict resolution and file provenance](conflict-resolution.md). `ConflictManager.unit.spec.ts` owns common-ancestor and conservative merge behaviour, deterministic pair selection for multiple live leaves, and reconstruction from the committed tree between stages. `ServiceDatabaseFileAccessBase.unit.spec.ts` owns all-branch content discovery and exact-revision logical deletion. `ServiceFileHandlerBase.unit.spec.ts` owns the injected provenance policy for edit, deletion, and rename operations, together with user-selected reflection, child creation, strict matching, matching-provenance cleanup after exact branch deletion, and stale-revision refusal. `LiveSyncLocalDB.unit.spec.ts` owns chunk reachability across current winners, shared chunks, live conflicts, available merge ancestry, conflict resolution, and PouchDB replication of collection and recreation. Extend the relevant sides when changing conflict resolution, compaction handling, provenance, chunk collection, or database-to-storage overwrite protection.
|
|
62
|
+
The revision-tree safety rules are documented in [Conflict resolution and file provenance](conflict-resolution.md). `ConflictManager.unit.spec.ts` owns common-ancestor and conservative merge behaviour, deterministic pair selection for multiple live leaves, and reconstruction from the committed tree between stages. `ServiceDatabaseFileAccessBase.unit.spec.ts` owns all-branch content discovery, short-circuiting history existence checks after an exact match, and exact-revision logical deletion. `ServiceFileHandlerBase.unit.spec.ts` owns the injected provenance policy for edit, deletion, and rename operations, together with user-selected reflection, child creation, strict matching, matching-provenance cleanup after exact branch deletion, and stale-revision refusal. `LiveSyncLocalDB.unit.spec.ts` owns chunk reachability across current winners, shared chunks, live conflicts, available merge ancestry, conflict resolution, and PouchDB replication of collection and recreation. Extend the relevant sides when changing conflict resolution, compaction handling, provenance, chunk collection, or database-to-storage overwrite protection.
|
|
63
|
+
|
|
64
|
+
`LiveSyncReplicator.unit.spec.ts` owns the CouchDB remote-chunk batch contract: a mixed `_all_docs` result must return each available document even when another keyed row reports an error. `ChunkFetcher.unit.spec.ts` owns the consumer behaviour: it writes valid returned chunks and emits `missingChunkRemote` only for requested IDs which are absent from the returned set. Extend both sides when changing remote chunk acquisition or missing-chunk delivery.
|
|
63
65
|
|
|
64
66
|
`openSimpleStore()` may create an inert namespace handle during composition, before the backing key-value database has opened. Maintained hosts must complete their sequential settings lifecycle before scans, watchers, or replication invoke the handle. Operations fail on a lifecycle violation rather than waiting for readiness; do not introduce an implicit wait which can hang after failed initialisation or become self-referential from an initialisation handler. Treat database reset as a transient unavailable boundary and reconstruct derived local state after reopening.
|
|
65
67
|
|
package/docs/proven-in-use.md
CHANGED
|
@@ -12,10 +12,12 @@ The plug-in extends the neutral `ServiceContext` with Obsidian-owned capabilitie
|
|
|
12
12
|
|
|
13
13
|
The consumer contract in `test/contracts/serviceContext.ts` checks shared result semantics and exact context identity across the composed services. The real-Obsidian suite documented in `test/e2e-obsidian/README.md` then covers plug-in loading, representative Svelte dialogue mounts, Vault reflection, CouchDB and Object Storage synchronisation, a two-device P2P Setup URI round-trip, two-Vault behaviour, and other Obsidian-owned boundaries.
|
|
14
14
|
|
|
15
|
-
Commonlib's real-PouchDB and injected-boundary unit tests prove that conservative merge uses the nearest revision actually shared by both conflict leaves, multiple remaining leaves are selected in a deterministic order, each completed pair is reconstructed from the committed tree, storage content from a deleted losing branch remains recognisable after resolution, and conflict-time operations retain the exact displayed branch or preserve every branch when that identity cannot be proved. They also verify that a local edit remains a readable sibling branch when the winning body is unavailable but its exact revision and parent metadata remain,
|
|
15
|
+
Commonlib's real-PouchDB and injected-boundary unit tests prove that conservative merge uses the nearest revision actually shared by both conflict leaves, multiple remaining leaves are selected in a deterministic order, each completed pair is reconstructed from the committed tree, storage content from a deleted losing branch remains recognisable after resolution, and conflict-time operations retain the exact displayed branch or preserve every branch when that identity cannot be proved. They also verify that a boolean history check stops after its first exact content match without reading an older unavailable revision, while revision enumeration still returns every match. A local edit remains a readable sibling branch when the winning body is unavailable but its exact revision and parent metadata remain, a generation-one revision without a parent is left unresolved, and a separately confirmed exact deletion of that unreadable live leaf permits a replacement to be stored without reading the missing chunk. Exact reconciliation tests cover reflecting a selected live leaf, storing Vault content as its child, recording a matching leaf without creating a revision, clearing matching provenance after exact branch deletion, and refusing to record a stale revision or a revision whose content differs. Self-hosted LiveSync supplies the host composition, persistent device-local provenance, and the per-revision repair interface. Its optional two-Vault scopes cover post-resolution propagation and edit, logical deletion, case-only rename, and cross-path rename while conflicts remain active. The conflict-operation scope passes in real Obsidian against the exact locally packed Commonlib artefact, including replicated parent revisions and preservation of the other live branches.
|
|
16
16
|
|
|
17
17
|
Commonlib's chunk-reachability tests use real PouchDB revision trees. They verify that an ordinary superseded revision releases its unique chunks, a chunk shared by current files remains reachable, live conflict branches and their nearest available shared ancestor remain protected, and losing-branch ancestry becomes eligible after resolution. They also replicate a collected chunk deletion to another PouchDB database and verify that writing the same content can recreate and replicate the content-addressed chunk. Self-hosted LiveSync owns the CouchDB-specific Garbage Collection V3 workflow, including device checks, confirmation, deletion, push-only replication, and remote compaction.
|
|
18
18
|
|
|
19
|
+
Commonlib's injected CouchDB tests verify that a mixed remote chunk batch preserves available documents when another requested ID is absent. Its chunk-delivery tests separately verify that available chunks reach local storage and only absent IDs are marked unavailable. Self-hosted LiveSync supplies the real CouchDB connection and start-up composition, so device-specific replication timing remains downstream integration evidence rather than Commonlib unit coverage.
|
|
20
|
+
|
|
19
21
|
Most replication and storage services used by the plug-in still enter through explicit `compat/*` paths. Those imports prove that the published compatibility boundary supports the current migration; they are not examples of a finished high-level client API.
|
|
20
22
|
|
|
21
23
|
The plug-in's manual onboarding and Remote Databases pane use the focused `/remote-configurations` entry to create opaque profile IDs, retain user-visible names, and select main and P2P remotes. Commonlib owns the in-memory profile result contract; Self-hosted LiveSync adds dialogue, Setup URI classification, Fetch or Rebuild scheduling, persistence, restart ordering, and real-Obsidian presentation checks.
|