@vrtmrz/livesync-commonlib 0.1.44 → 0.2.1
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/README.md +80 -4
- package/dist/API/DirectFileManipulatorV2.d.ts +2 -0
- package/dist/API/DirectFileManipulatorV2.js +3 -0
- package/dist/API/processSetting.d.ts +1 -0
- package/dist/common/configForDoc.d.ts +1 -0
- package/dist/common/configForDoc.js +11 -1
- package/dist/common/models/db.definition.d.ts +1 -0
- package/dist/common/models/setting.const.defaults.js +1 -0
- package/dist/common/models/setting.const.preferred.js +1 -3
- package/dist/common/models/setting.const.qr.js +1 -0
- package/dist/common/models/setting.policy.d.ts +4 -0
- package/dist/common/models/setting.policy.js +1 -0
- package/dist/common/models/setting.type.d.ts +2 -0
- package/dist/common/models/shared.definition.d.ts +20 -0
- package/dist/common/models/tweak.definition.d.ts +1 -0
- package/dist/common/settingConstants.d.ts +1 -0
- package/dist/common/settingConstants.js +6 -2
- package/dist/interfaces/FileHandler.d.ts +5 -1
- package/dist/interfaces/StorageEventManager.d.ts +5 -0
- package/dist/interfaces/StorageEventManager.js +7 -0
- package/dist/managers/EntryManager/EntryManagerImpls.js +3 -2
- package/dist/managers/StorageEventManager.d.ts +19 -10
- package/dist/managers/StorageEventManager.js +259 -189
- package/dist/pouchdb/LiveSyncDBFunctions.js +4 -0
- package/dist/pouchdb/StreamingFetch.d.ts +5 -1
- package/dist/pouchdb/StreamingFetch.js +529 -65
- package/dist/replication/LiveSyncAbstractReplicator.d.ts +2 -0
- package/dist/replication/LiveSyncAbstractReplicator.js +5 -2
- package/dist/replication/RemoteResource.d.ts +23 -2
- package/dist/replication/RemoteResource.js +6 -2
- package/dist/replication/couchdb/LiveSyncReplicator.d.ts +14 -0
- package/dist/replication/couchdb/LiveSyncReplicator.js +171 -3
- package/dist/replication/couchdb/fastForward.d.ts +19 -0
- package/dist/replication/couchdb/fastForward.js +119 -0
- package/dist/replication/couchdb/replicationProgress.d.ts +6 -0
- package/dist/replication/couchdb/replicationProgress.js +105 -0
- package/dist/replication/index.d.ts +4 -1
- package/dist/replication/joinedDevices.d.ts +25 -0
- package/dist/replication/joinedDevices.js +123 -0
- package/dist/replication/journal/JournalSyncCore.d.ts +2 -1
- package/dist/replication/journal/JournalSyncCore.js +11 -1
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +4 -0
- package/dist/replication/journal/LiveSyncJournalReplicator.js +56 -1
- package/dist/replication/journal/objectstore/JournalStorageAdapter.d.ts +3 -0
- package/dist/replication/journal/objectstore/MinioStorageAdapter.d.ts +2 -1
- package/dist/replication/journal/objectstore/MinioStorageAdapter.js +27 -1
- package/dist/replication/trystero/P2PRoomSessionOwner.d.ts +1 -1
- package/dist/replication/trystero/P2PRoomSessionOwner.js +27 -14
- package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -0
- package/dist/serviceFeatures/offlineScanner.js +19 -7
- package/dist/serviceModules/Rebuilder.js +41 -15
- package/dist/serviceModules/ServiceFileAccessBase.js +5 -0
- package/dist/serviceModules/ServiceFileHandlerBase.d.ts +8 -5
- package/dist/serviceModules/ServiceFileHandlerBase.js +75 -38
- package/dist/services/BrowserServices.d.ts +4 -2
- package/dist/services/BrowserServices.js +2 -5
- package/dist/services/HeadlessServices.d.ts +3 -0
- package/dist/services/HeadlessServices.js +2 -5
- package/dist/services/ServiceHub.d.ts +4 -4
- package/dist/services/base/AppLifecycleService.js +1 -1
- package/dist/services/base/ControlService.js +6 -2
- package/dist/services/base/FileProcessingService.d.ts +7 -1
- package/dist/services/base/FileProcessingService.js +9 -1
- package/dist/services/base/IService.d.ts +19 -19
- package/dist/services/base/ReplicatorService.d.ts +1 -0
- package/dist/services/base/ReplicatorService.remoteResourceResolver.js +1 -1
- package/dist/services/base/SettingService.d.ts +8 -18
- package/dist/services/base/SettingService.js +28 -46
- package/dist/services/implements/base/SvelteDialog.d.ts +2 -2
- package/dist/services/implements/base/UIService.d.ts +2 -0
- package/dist/services/implements/base/UIService.js +5 -0
- package/dist/services/implements/injectable/InjectableServiceHub.d.ts +4 -4
- package/dist/services/implements/injectable/InjectableServices.d.ts +2 -2
- package/dist/services/implements/injectable/InjectableSettingService.d.ts +0 -3
- package/dist/services/implements/injectable/InjectableSettingService.js +0 -11
- package/dist/services/lib/HandlerUtils.d.ts +60 -0
- package/dist/services/lib/HandlerUtils.js +46 -0
- package/dist/string_and_binary/chunks.js +7 -5
- package/dist/uiCommands.d.ts +26 -0
- package/dist/uiCommands.js +57 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/development.md +15 -3
- package/docs/offline-scanner.md +15 -13
- package/docs/proven-in-use.md +38 -4
- package/docs/releasing.md +20 -13
- package/docs/settings-lifecycle.md +39 -5
- package/docs/storage-events-and-reflection.md +109 -10
- package/package.json +13 -18
- package/dist/services/base/ConfigService.d.ts +0 -7
- package/dist/services/base/ConfigService.js +0 -6
- package/dist/services/implements/browser/ConfigServiceBrowserCompat.d.ts +0 -16
- package/dist/services/implements/browser/ConfigServiceBrowserCompat.js +0 -23
package/docs/development.md
CHANGED
|
@@ -18,6 +18,16 @@ The source tree is larger than the supported package surface. Consumers may impo
|
|
|
18
18
|
|
|
19
19
|
Files elsewhere under `src` are implementation details unless a focused entry re-exports them or the compatibility inventory names them. Do not use the source layout, TypeScript path aliases, or the presence of generated declarations as evidence of a supported consumer import.
|
|
20
20
|
|
|
21
|
+
## Module filenames
|
|
22
|
+
|
|
23
|
+
For new or renamed TypeScript and JavaScript source modules, start the filename
|
|
24
|
+
with an upper-case letter when the file contains a class with the same name,
|
|
25
|
+
such as `ServiceHub.ts`. Use a lower-case initial when the module primarily
|
|
26
|
+
exports functions, such as `fastForward.ts`. Follow the surrounding filename
|
|
27
|
+
style for the rest of the basename. Corresponding test files use the same
|
|
28
|
+
basename, such as `fastForward.unit.spec.ts`. Retain established names for
|
|
29
|
+
unrelated files and separate conventions for components or other file types.
|
|
30
|
+
|
|
21
31
|
## Service composition and state ownership
|
|
22
32
|
|
|
23
33
|
Commonlib uses four related forms of composition. Choose between them by ownership and dependency direction, not merely by whether an implementation has mutable state.
|
|
@@ -29,6 +39,8 @@ Commonlib uses four related forms of composition. Choose between them by ownersh
|
|
|
29
39
|
|
|
30
40
|
When a feature needs state, separate the component which owns that state, its transitions, and its invariants from the code which connects it to application lifecycle events and downstream effects. Give the stateful component narrow collaborators or callbacks rather than the complete Service Hub or application core. Keep application-level handler registration and host-specific presentation in the surrounding serviceFeature. A handler or effect which is part of the state contract, such as querying unresolved errors or reporting their first occurrence, may remain inside the stateful component when its input and output ports are explicit and bounded.
|
|
31
41
|
|
|
42
|
+
Settings storage follows the same ownership boundary. Each host selects one exact-key asynchronous `ValueStore & ValueDeleter` at its composition root and supplies it as the setting service's required `deviceValues` dependency. `setting.deviceValues` is that root; `setting.config` wraps it with the existing Vault and database-suffix scope, and the Service Hub `config` property aliases the same view. Commonlib does not discover browser globals or provide a module-global fallback. Keep existing local keys on the exact-key root, preserve configuration key names, and await Value Store operations. See [Settings lifecycle](settings-lifecycle.md#host-storage-for-device-values-and-configuration) for the key mapping and host example.
|
|
43
|
+
|
|
32
44
|
Existing features illustrate this direction:
|
|
33
45
|
|
|
34
46
|
- `targetFilter.ts` keeps each cache or readiness gate inside the factory which owns the corresponding predicate. `useTargetFilters` composes those predicates and registers them in the required order.
|
|
@@ -127,15 +139,15 @@ The compatibility event bridge delegates lifecycle and status requests to the st
|
|
|
127
139
|
|
|
128
140
|
The physical room, WebRTC peer, and relay-socket boundaries are documented in [P2P transport lifecycle](p2p-transport-lifecycle.md). In particular, normal shutdown leaves the Trystero room without closing raw peer connections directly.
|
|
129
141
|
|
|
130
|
-
Self-hosted LiveSync remains
|
|
142
|
+
Self-hosted LiveSync remains a principal maintained consumer. Commonlib's normal publication gate is its own type, unit, integration, and packed-boundary checks, as described in [the release runbook](releasing.md). Run targeted downstream or real-runtime checks against the exact packed artefact when a named host-specific risk is not established by those owner tests; consumer adoption and downstream test completion are not general release prerequisites.
|
|
131
143
|
|
|
132
144
|
Commonlib owner tests exercise the real implementation behind each focused result and lifecycle contract. A consumer-owned unit test may inject the smallest structural capability it uses and provide its own fake or spy when that current test needs isolation. Do not publish Vitest-specific mocks, expose private service constructors, or refactor a production boundary merely to create possible future test infrastructure. Mock and spy helpers are not a prerequisite for the LiveSync 1.0 package-boundary work; add them only with the consumer test which requires them, or after that release work.
|
|
133
145
|
|
|
134
|
-
Prefer an object or callback injected at the host composition boundary over replacing an ESM module namespace. If a proposed focused API would require global mutation, import-order coupling, hidden construction, or private nominal types to substitute later, stop and review the production design before stabilising it.
|
|
146
|
+
Prefer an object or callback injected at the host composition boundary over replacing an ESM module namespace. If a proposed focused API would require global mutation, import-order coupling, hidden construction, or private nominal types to substitute later, stop and review the production design before stabilising it. Use exact-package downstream checks and focused real-runtime E2E for named host-specific risks which the owner tests cannot establish.
|
|
135
147
|
|
|
136
148
|
Commonlib publishes the typed English messages which it owns and uses them when a host does not provide a translator. A host may inject a translator through `ServiceContext`; the full multilingual catalogue and language-selection state belong to that host and are not part of the Commonlib package.
|
|
137
149
|
|
|
138
|
-
|
|
150
|
+
The [maintained-host examples and evidence](proven-in-use.md) are optional context, not a support entitlement or release or adoption gate. When that page is updated, distinguish current use, historical use, Commonlib-owned results, downstream tests, and real-runtime observations, and keep each claim within the evidence.
|
|
139
151
|
|
|
140
152
|
## Integration tests
|
|
141
153
|
|
package/docs/offline-scanner.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
date: 2026-
|
|
3
|
-
commonlib-version: "0.
|
|
4
|
-
self-hosted-livesync-version: "1.0.
|
|
2
|
+
date: 2026-10-25
|
|
3
|
+
commonlib-version: "0.2.0"
|
|
4
|
+
self-hosted-livesync-version: "1.0.40"
|
|
5
5
|
status: unreleased
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -31,15 +31,15 @@ The tables below cover only paths which remain after identity validation, the ho
|
|
|
31
31
|
|
|
32
32
|
The `initialized` marker records that a full-scan invocation has reached its aggregate-result boundary and that later invocations may restore stored storage-event operations. It is not a successful-scan marker: the first invocation records it after an aggregate `false` result, `true`, or an accepted partial result. An exception before the aggregate result is produced does not reach that write.
|
|
33
33
|
|
|
34
|
-
Storage-event restoration is a bounded start-up recovery step. Restored events bypass their former batch delay, but `restoreState()` waits for their actual
|
|
34
|
+
Storage-event restoration is a bounded start-up recovery step. Restored events bypass their former batch delay, but `restoreState()` waits for their actual handler outcomes and snapshot ordering before the file collections are compared. Saved events run first because a deletion or rename can carry operation intent which is no longer visible in the current storage listing. An individual restored operation failure remains in the pending snapshot and is logged; it does not prevent the scan from examining current state or independent paths. The following full scan then makes the final reconciliation decision.
|
|
35
35
|
|
|
36
|
-
This ordering does not mean that storage unconditionally overwrites the replay result. The action matrix remains authoritative: when a replayed operation leaves an ambiguous pair, such as a storage file alongside a deleted database entry, the selected policy may deliberately return `skipped` rather than guess which side is intended.
|
|
36
|
+
This ordering does not mean that storage unconditionally overwrites the replay result. The action matrix remains authoritative: when a replayed operation leaves an ambiguous pair, such as a storage file alongside a deleted database entry, the selected policy may deliberately return `skipped` rather than guess which side is intended. A failed replay does not become a completed operation; a later scan may still reconcile its current pair. This start-up recovery boundary is separate from later watcher work and a host's replication-result queue.
|
|
37
37
|
|
|
38
38
|
### Stored event interpretation
|
|
39
39
|
|
|
40
|
-
The storage-event snapshot
|
|
40
|
+
The storage-event snapshot retains every accepted operation while it is buffered, waiting for a batch boundary, or in a handler. It remains there until the operation has a durable local result, is proved already represented or safely covered by a successful successor, or ends in a skip explicitly allowed by policy. A `false` handler result or exception leaves the saved operation pending. A failed initial snapshot prevents the handler from starting; a later snapshot failure is reported and cannot be treated as completion. Ordered snapshot writes prevent an older asynchronous save from replacing a newer pending view. The snapshot records operation intent, not file content, and cannot recover bytes or events which the host never persisted or delivered; the full scan continues to observe current storage state.
|
|
41
41
|
|
|
42
|
-
A restored event is an operation intent which must be revalidated against the current exact storage path before it can cause a database side effect. Its saved file stub, timestamps, and optional editor cache describe the earlier observation; they are not authoritative file content after a restart. Whenever an inclusion is still applicable, the handler reads a fresh stub and content
|
|
42
|
+
A restored event is an operation intent which must be revalidated against the current exact storage path before it can cause a database side effect. Its saved file stub, timestamps, and optional editor cache describe the earlier observation; they are not authoritative file content after a restart. Whenever an inclusion is still applicable, the handler reads a fresh stub and current content when the operation runs. Whenever an ordinary deletion's expected absence is contradicted by a current storage item, that deletion is suppressed and the full scan evaluates that current item instead.
|
|
43
43
|
|
|
44
44
|
For this check, an exact path means that the canonical path returned by storage has the saved spelling after ordinary separator normalisation. A file returned only through a case-insensitive lookup does not prove the saved inclusion path. A same-ID rename remains the explicit case-change path described below.
|
|
45
45
|
|
|
@@ -58,7 +58,7 @@ The standard-file revalidation contract is:
|
|
|
58
58
|
| Case-only or otherwise same-ID `RENAME` | A current selected file resolves to the renamed canonical path | Update that single document's path and current content without a separate deletion |
|
|
59
59
|
| Case-only or otherwise same-ID `RENAME` | The current identity does not support the saved rename | Do not replay the rename; let the full scan evaluate the current path |
|
|
60
60
|
|
|
61
|
-
An inability to inspect the current path is not proof of absence. Revalidation
|
|
61
|
+
An inability to inspect the current path is not proof of absence. Revalidation suppresses a destructive half when storage inspection fails, records the failure, and leaves the operation pending. The full scan can still examine the current state and reports any selected-pair failure under its strict or explicitly partial completion policy. Target-file policy and file-size limits are evaluated from current settings rather than inherited from the saved event.
|
|
62
62
|
|
|
63
63
|
The unreleased rename-exclusion guard retains `renameTarget` when a rename is converted to deletion during target filtering. Unlike an ordinary deletion, this intent can remove a file from synchronisation without removing it from storage. The guard distinguishes deliberate exclusion from transient case-collision rejection, preserves a recreated source, and permits a deliberately excluded same-ID destination without requiring that destination to disappear. Older snapshots without this optional field retain the absence-only rule. See [Storage events and database-to-storage reflection](storage-events-and-reflection.md) for the exact admission conditions and the separate ordinary watcher boundary.
|
|
64
64
|
|
|
@@ -138,11 +138,13 @@ By default, the complete scan returns `false` when any pair is `failed`, and ret
|
|
|
138
138
|
|
|
139
139
|
Self-hosted LiveSync enables this policy only for ordinary Obsidian start-up, where one unavailable path must not prevent unaffected files from synchronising. Fast Setup, Fetch, Rebuild, direct scans, and CLI mirror or daemon paths retain the strict default.
|
|
140
140
|
|
|
141
|
-
##
|
|
141
|
+
## Files in the same timestamp bucket
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
For a `both` pair which selects `NEWER_WINS` (`sync-newer`), equal raw modification times and distinct times within the platform tolerance do not establish that the storage file is unchanged. The scanner first asks the file handler to record provenance for the current database revision only when current storage and one unambiguous live revision have matching content. The handler checks the storage bytes and database revision under the document lock, then revalidates the storage snapshot, current revision, and absent provenance before writing the record. This check writes neither storage content nor a database revision. The existing selection, file-size, and conflict policies still apply, and the `DB_APPLY` action remains unchanged.
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
The result of `tryRecordUntrackedFileRevision()` is not an edit predicate. Its `false` can mean that provenance already exists, content differs, history conflicts, required content or provenance is unavailable, or state changed during verification. When it returns `false`, the scanner uses the ordinary, non-forced `storeFileToDB` path with bounded storage revalidation; it does not force the Vault file into the current database winner. The file handler retains a known branch when readable provenance identifies it, applies newer database state when the storage bytes match that recorded older revision, and saves a genuine edit on its recorded branch.
|
|
146
|
+
|
|
147
|
+
A valid saved content hash can prove that unchanged bytes still represent a recorded revision whose database content has been pruned. A hash cannot reconstruct ancestry for edited bytes. If the recorded base is pruned and the current bytes do not match its saved hash, the handler may preserve readable storage as an independent branch when it matches no current live leaf; it must not attach those bytes to an arbitrary database winner. This unknown-origin case is distinct from an unavailable comparison: if required storage, provenance, database content, or revalidation cannot be read, the operation remains unresolved and the pair fails for retry. Repeated storage changes are bounded and also remain failed rather than being reported as reconciled.
|
|
146
148
|
|
|
147
149
|
## Result-state verification
|
|
148
150
|
|
|
@@ -156,12 +158,12 @@ Focused two-pass coverage starts with a `DB_APPLY` reflection whose file handler
|
|
|
156
158
|
|
|
157
159
|
Full-scan coverage also verifies that the strict default returns `false`, an ordinary-start-up scan which explicitly continues returns `completed-with-file-failures` while logging the affected path at verbose level, and a later clean scan returns `true`.
|
|
158
160
|
|
|
159
|
-
The equal-time regression uses real in-memory PouchDB and the file handler. It checks missing and recorded provenance with unchanged files and genuine local edits, then advances the database and
|
|
161
|
+
The equal-time regression uses real in-memory PouchDB and the file handler. It checks missing and recorded provenance with unchanged files and genuine local edits at both identical raw timestamps and distinct raw timestamps within one comparison bucket. It verifies that edits are preserved during the scan, then advances the database and checks that an incoming revision retains the local branch. It also verifies that a matching saved hash remains useful after its database revision is pruned, while edited bytes with an unhashed pruned base and no matching live leaf are preserved independently. Focused scanner tests verify that a failed ordinary save returns a failed pair. Focused handler tests cover provenance read and write failures, unavailable recorded-base content, an existing conflict, and storage or database changes during verification.
|
|
160
162
|
|
|
161
163
|
A second two-pass case starts with a `db-only` entry skipped by the size limit. It then removes that limit and verifies that the entry is reflected to storage rather than misclassified as an offline local deletion. Coverage for an existing storage file verifies that a failed `sync-newer` reflection retains the observed storage mtime, and full-scan coverage verifies that the aggregate failure reaches its caller after scanner initialisation completes.
|
|
162
164
|
|
|
163
165
|
Identity coverage verifies early quarantine when no consistent target exists, continued processing when consistent Metadata represents the same logical path, expired-deletion retention, special-namespace routing, actual-ID-first inspection, stale and ambiguous repair rejection, target-first ordering, exact-target retry, source preservation after failure, and last-seen clearing.
|
|
164
166
|
|
|
165
|
-
Stored-event coverage verifies that current storage content replaces saved observations, a recreated path suppresses a stale deletion, distinct-ID rename halves are admitted independently from current path state, and a same-ID rename never performs a separate deletion. It also verifies that a storage-inspection failure does not become evidence for a destructive operation and that an individual replay failure does not prevent the full scan from running.
|
|
167
|
+
Stored-event coverage verifies that current storage content replaces saved observations, a recreated path suppresses a stale deletion, distinct-ID rename halves are admitted independently from current path state, and a same-ID rename never performs a separate deletion. It also verifies that a storage-inspection failure does not become evidence for a destructive operation, that failed work remains pending, and that an individual replay failure does not prevent the full scan from running.
|
|
166
168
|
|
|
167
169
|
Maintain that interaction boundary when changing file-handler results, last-seen persistence, Fast Setup reflection, or offline deletion detection.
|
package/docs/proven-in-use.md
CHANGED
|
@@ -1,13 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Maintained-host examples and evidence
|
|
2
2
|
|
|
3
3
|
Commonlib is extracted from the implementation shared by [Self-hosted LiveSync](https://github.com/vrtmrz/obsidian-livesync) and its host applications. The package is therefore exercised by a substantial maintained consumer, but it does not yet claim a separate ecosystem of independent applications or a stable end-to-end SDK.
|
|
4
4
|
|
|
5
5
|
The useful evidence is host- and contract-specific. Focused Commonlib tests establish reusable result semantics; Self-hosted LiveSync tests establish how those contracts compose into the Obsidian plug-in, CLI, WebApp, and WebPeer. The Obsidian plug-in and CLI retain the strongest device and long-running synchronisation evidence. The browser applications add focused adapter and runtime tests, browser smoke tests, a cross-application P2P scenario ending at the production CLI, and public Pages smoke validation.
|
|
6
6
|
|
|
7
|
-
The code paths below identify
|
|
7
|
+
The code paths below identify maintained Self-hosted LiveSync composition points as examples. The reviewed downstream revision and compatibility imports are recorded in [`migration/downstream-imports.json`](migration/downstream-imports.json), which is regenerated by the package-boundary tooling rather than copied into prose links. These snapshots may become historical as the maintained downstream changes; this page is optional context, not a support entitlement or release or adoption gate.
|
|
8
8
|
|
|
9
9
|
## Obsidian plug-in
|
|
10
10
|
|
|
11
|
+
The unreleased V3 Rabin–Karp correction uses proportional binary thresholds,
|
|
12
|
+
including at values 0 and 1. Regression tests cover natural boundaries, suffix
|
|
13
|
+
reuse, forced caps, explicit minima, EOF tails, and large UTF-8 text. The packed
|
|
14
|
+
old/new CLI matrix verifies 810 file-stage digests over CouchDB, Journal, and
|
|
15
|
+
P2P in both directions. Separate revision tests retain conflict coverage.
|
|
16
|
+
The Linux Obsidian manual-setup workflow verifies value 0, eight 32 KiB binary
|
|
17
|
+
pieces, unchanged Metadata revision and complete local Chunk inventory after
|
|
18
|
+
restart, exact binary reception through Fast Fetch, and a bidirectional note
|
|
19
|
+
round-trip. These checks use the unpublished local candidate with LiveSync
|
|
20
|
+
1.0.39; they do not establish mobile or Cloudant-service behaviour.
|
|
21
|
+
|
|
22
|
+
Unreleased Fast Fetch tests verify all current conflict leaves, including deleted
|
|
23
|
+
leaves, before advancing the durable stream cursor. CouchDB 3.5.0 integration
|
|
24
|
+
tests exercise revision ancestry retrieval and a fresh-import handoff to the
|
|
25
|
+
ordinary PouchDB checkpoints, followed by a batch-size change and receipt of a
|
|
26
|
+
new revision in LiveSync mode. Unit tests cover unimported local writes, missing
|
|
27
|
+
or replaced checkpoint guards, partial publication, and exclusion of persisted
|
|
28
|
+
resume cursors. A controlled connection delay also verifies that disposing the
|
|
29
|
+
resource during preparation closes the connection when it arrives. Rebuilder
|
|
30
|
+
tests also delay and reject device-local checkpoint
|
|
31
|
+
writes, verifying that handoff, completion, and checkpoint removal wait for
|
|
32
|
+
successful persistence. The handoff requires an empty local database, suspended local
|
|
33
|
+
writers, and a remote database which is not replaced during the operation.
|
|
34
|
+
These package-owned tests do not establish arbitrary-time fast-forwarding,
|
|
35
|
+
On Demand repair, or real Obsidian runtime behaviour.
|
|
36
|
+
|
|
37
|
+
The unreleased joined-device resource and UI-command work adds instance-owned typed UI handlers, legacy-compatible device projections, and device-record removal. Focused unit tests cover missing records, preservation of other devices and unknown fields, self-removal rejection, conflict retries, unsupported writes, verification failures, and handler disposal. CouchDB 3.5.0 integration tests verify that reading or forgetting never creates a missing database, and verify removal and preservation while retaining a fixture which demonstrates that local documents accept stale revisions. Object Storage integration tests against RustFS verify conditional removal and rejection of stale ETags. These checks do not make concurrent CouchDB updates atomic.
|
|
38
|
+
|
|
39
|
+
Before integration with Commonlib 0.2.0, a local packed candidate based on 0.1.44 passed Self-hosted LiveSync 1.0.39 checks, unit tests, and production build. Its focused real-Obsidian 1.12.7 scenario on Linux verifies the settings entry, self-first and locked-out records, current metadata, manual refresh, confirmed removal, and preservation of other records and remote state. Mobile emulation verifies the command-palette entry, scrolling, viewport and safe-area bounds, and touch targets. Self-hosted LiveSync owns the dialogue and advisory version Notices. These results establish the tested build's behaviour; publication and native iOS and Android behaviour remain unverified.
|
|
40
|
+
|
|
11
41
|
Unreleased remote-inspection tests compare individual and bulk reads against CouchDB 3.5.0. A fixture with 65 Metadata documents and 130 Chunks produces identical integrity reports while reducing HTTP requests from 198 to 8. Encrypted bulk reads preserve current conflicts, exact revision access, missing-document evidence, and unchanged database and Security Seed state. Unit tests cover bounded GET batches, encoded URL limits, per-document failures, cancellation, and explicitly excluded remote coverage. These counts describe the fixture; they do not establish large-Vault elapsed-time improvements.
|
|
12
42
|
|
|
13
43
|
Unreleased [database diagnostics](database-diagnostics.md) tests cover preserved Usage accounting, normal-file identity classification, current conflict branches, byte comparisons, embedded Chunks, missing and unreadable content, cancellation, concurrent changes, and consistent pseudonyms. Real in-memory PouchDB tests verify pagination and conflict revision reads while retaining the original database handle. A CouchDB 3.5.0 integration test verifies GET-only access, encrypted Metadata and Chunk reads with an existing Security Seed, missing-seed failure without initialisation, and unchanged database update sequences and Seed documents. Focused tests also reject ciphertext which remains undecoded after an ID mismatch. Commonlib owns the result contract and compact Integrity Markdown formatter; Self-hosted LiveSync owns the serviceFeature, run-time name choice, dialogue lifecycle, and report copy and download controls. Its real Obsidian 1.12.7 test on Linux verifies cancellation before a run, both name modes, the nine-column Usage report, copy and download payloads, local integrity findings when CouchDB is unavailable, and unchanged settings, database update sequences, and Vault file names. Mobile emulation verifies the choice dialogue's viewport, safe-area, and touch-target layout; native iOS and Android behaviour remains unverified.
|
|
@@ -28,10 +58,14 @@ The CouchDB connection fix bypasses browser HTTP caching so encrypted replicatio
|
|
|
28
58
|
|
|
29
59
|
Unreleased settings tests verify that Object Storage-only credentials, JWT values, and authentication headers reach the host persistence handler only after connection encryption, and that unavailable or failed required encryption preserves the previous persisted settings. The focused `/settings` entry also supplies conditional whole-profile Markdown preparation and import preservation. A clean packed consumer checks the exhaustive property policy and prepared output types with an ES2018 compile fixture. Commonlib owns these result and type contracts; Self-hosted LiveSync owns Markdown file writes, its settings interface, and restoration through a real Obsidian restart.
|
|
30
60
|
|
|
61
|
+
The setting-service tests also cover the injected device-value boundary: exact local keys remain unchanged, the existing Vault and database-suffix configuration key layout is preserved, and separately injected stores remain isolated. These are Commonlib-owned asynchronous store contracts. Each host owns selecting its persistent adapter and verifying host storage across restart; this evidence does not claim additional real-runtime coverage.
|
|
62
|
+
|
|
31
63
|
Unreleased colon-path regression tests establish that Commonlib preserves ordinary file paths through Metadata storage, Chunk retrieval, and storage reflection, with path obfuscation enabled or disabled. They use real PouchDB with injected storage. This result does not establish which filenames a host's file-creation API accepts; that boundary requires host runtime verification.
|
|
32
64
|
|
|
33
65
|
Commonlib's encryption tests verify that an obfuscated Metadata document retains its document ID while its HKDF-encrypted path and file properties round-trip, that V1-obfuscated paths remain readable through the fallback, and that PouchDB replication presents a decoded path to both local storage and change events. The Fast Fetch test verifies decryption before storage and checkpointing. On 26th September 2026, a local packed candidate of this fix passed LiveSync 1.0.31's `npm run check` and unit tests, as well as real Obsidian two-Vault synchronisation, encrypted CLI-to-Obsidian transfer, and encrypted Metadata retrieval through Fast Fetch during manual CouchDB onboarding.
|
|
34
66
|
|
|
67
|
+
The EntryManager selection tests distinguish Hidden File Sync documents by their original `i:` prefix. They check that the normal-file allow-list applies to ordinary paths while prefixed hidden documents remain eligible, that the normal-file ignore-list still excludes matching hidden documents, and that hidden-file disablement and the Customisation Sync exceptions retain their behaviour. Hidden File Sync owns its additional target and ignore patterns in Self-hosted LiveSync; storage reflection and native Obsidian behaviour remain downstream verification boundaries.
|
|
68
|
+
|
|
35
69
|
The unreleased 0.1.30 packed candidate adds encryption of Hidden File Sync and Customisation Sync Metadata when CouchDB, E2EE V2, and Property Encryption are active. Focused Commonlib tests cover the `i:f:`, `ix:f:`, and legacy `ps:f:` representations, legacy and new setting defaults, feature declaration, concurrent version updates, and unknown-feature rejection. With that packed candidate in LiveSync, two real-Obsidian scenarios checked the raw CouchDB Metadata marker, zeroed times and size, empty public Chunk-reference list, and generation 13 feature declaration before restoring the files in a second Vault. The Hidden File Sync scenario also rebuilt the remote and verified the accepted writer's feature declaration and encrypted Metadata before the second Vault fetched them. The CLI-to-Obsidian encrypted note scenario also passed with matching effective settings. A separate real-Obsidian scenario kept continuous CouchDB replication active whilst the remote version document acquired an unknown feature; the local document was observed, the active Replicator retired, another replication was refused, and previously accepted Vault content remained intact. After restarting the Vault, both finite replication and the actual continuous connection attempt were refused by the remote declaration, without a KV feature history. These runs establish the tested local package and host build; they do not validate a published registry artefact or recovery under a future compatible client.
|
|
36
70
|
|
|
37
71
|
Additional real-CouchDB integration tests exercise Fast Fetch admission before local reset, generation 12 acceptance, and generation 13 retrieval with plaintext and encrypted internal Metadata together. A real-Obsidian migration scenario enables the preference through the settings UI without Rebuild, verifies that unchanged Metadata retains its original revision while rewritten entries are encrypted with stable IDs, and checks a second device's mismatch rejection and restoration after alignment. After turning the preference OFF, Fast Fetch still retrieves both representations and their Chunks with matching content; the remote feature declaration remains. These checks use the local packed candidate with LiveSync 1.0.32.
|
|
@@ -182,6 +216,6 @@ A new integration should start with the focused entries, not by reproducing Self
|
|
|
182
216
|
|
|
183
217
|
Use the named consumer paths as integration examples, while retaining the ownership and limitations stated in the [root README](../readme.md), [storage guide](platform-storage.md), [standard-I/O guide](platform-standard-io.md), and [remote configuration profile guide](remote-configurations.md).
|
|
184
218
|
|
|
185
|
-
##
|
|
219
|
+
## Scope and maintenance
|
|
186
220
|
|
|
187
|
-
This page
|
|
221
|
+
This optional page preserves examples, current integration evidence, and history. It does not define supported behaviour or create a release or adoption gate; package guarantees are described by the published contracts and release policy. When this page is updated, distinguish current use, historical use, Commonlib-owned tests, downstream tests, and real-runtime observations, and keep each claim within its evidence.
|
package/docs/releasing.md
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
This document is the developer and maintainer runbook for preparing and publishing `@vrtmrz/livesync-commonlib`. Package-consumer guidance belongs in the root README.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Commonlib-owned release checks
|
|
6
6
|
|
|
7
|
-
Run the
|
|
7
|
+
Run the normal owner checks from a clean checkout:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npm ci
|
|
11
11
|
npm run verify:package
|
|
12
|
+
npm run test:integration:managed
|
|
12
13
|
```
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Together these checks type-check Commonlib, run its unit and integration suites, verify the source boundary, build the distributable package, install its exact tarball into a clean consumer, and bundle representative browser and Node entry points. These Commonlib-owned checks are the normal publication criteria. The source `package.json` remains private to prevent publishing the repository root. Only the generated `.package` directory is publishable, and its manifest defaults to public publication on the `next` dist-tag.
|
|
16
|
+
|
|
17
|
+
Downstream or real-runtime checks are appropriate when a named host-specific risk is not established by the owner tests; run those checks against the exact packed artefact and record the risk and result. LiveSync adoption and downstream validation are not general publication prerequisites. The [maintained-host examples and evidence](proven-in-use.md) page is optional context, not a release or update gate.
|
|
15
18
|
|
|
16
19
|
The package builder normalises generated directories to `0755`, ordinary files to `0644`, and declared `bin` targets to `0755` before validation. `test:package` uses a restrictive umask and verifies the packed modes, so local and hosted builds do not differ only because their caller environments use different umasks.
|
|
17
20
|
|
|
@@ -29,13 +32,17 @@ npm run verify:package
|
|
|
29
32
|
|
|
30
33
|
Inspect the lockfile diff before committing it. A dependency-only maintenance change must not alter the source package version, and lockfile normalisation must not be described as a package upgrade when resolved versions and integrity values are unchanged. When the workflow selects a newer reviewed npm CLI, update this example in the same change.
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
When a named host-specific risk is not established by the Commonlib owner checks, run the relevant downstream workflow against an exact Self-hosted LiveSync ref which consumes the package. It installs the tarball produced from the selected Commonlib commit and runs LiveSync type checks, unit tests, plug-in and application builds, and CLI E2E. Real Obsidian E2E remains local-only and is appropriate when the named risk concerns actual plug-in composition, storage, UI, or platform behaviour. These checks are conditional on the risk; LiveSync adoption is not a general release prerequisite.
|
|
36
|
+
|
|
37
|
+
## Version compatibility policy
|
|
38
|
+
|
|
39
|
+
The Commonlib 0.2 policy begins with 0.2.0 and does not retroactively change compatibility expectations for published 0.1.x versions. Within 0.2.x, preserve published APIs, types, behaviour, supported persisted data, and exported `compat/*` paths. Intentional breaking changes belong in 0.3.0. Consumers may declare a range such as `^0.2.0`; lockfiles and `npm ci` remain the reproducible install path.
|
|
33
40
|
|
|
34
41
|
## Preparing a release
|
|
35
42
|
|
|
36
|
-
Choose the version explicitly.
|
|
43
|
+
Choose the version explicitly. For the 0.2 line, start at 0.2.0. A stable candidate can use its final version on the `next` dist-tag, then be checked by exact source revision and tarball SHA-256 before the same version is promoted to `latest`. No `-next.N` or `-rc.N` version is required; use a separate prerelease version only when it is useful for a distinct candidate. Package-proof versions are local artefacts and cannot be staged.
|
|
37
44
|
|
|
38
|
-
Prepare
|
|
45
|
+
Prepare stable releases and any optional prereleases on a reviewed pull request based on `main`. An optional prerelease may be staged from the exact reviewed pull-request branch commit. A stable release must be staged only after its exact reviewed release commit is present on `main`.
|
|
39
46
|
|
|
40
47
|
```bash
|
|
41
48
|
npm ci
|
|
@@ -44,7 +51,7 @@ npm run verify:package
|
|
|
44
51
|
npm publish --dry-run .package --tag next --access public
|
|
45
52
|
```
|
|
46
53
|
|
|
47
|
-
Review `package.json`, `package-lock.json`, the generated manifest, the tarball contents, the test results
|
|
54
|
+
Review `package.json`, `package-lock.json`, the generated manifest, the tarball contents, and the Commonlib-owned test results. Add downstream evidence when a named host-specific risk requires it. Commit only the source manifest and lockfile for the version change; `.package`, `.package-consumer`, and `artifacts` are generated and ignored. Push and open a release pull request only after the usual user checkpoint. Keep a pull request in draft while a specifically planned downstream check is in progress; that check is not required when no such risk remains. Merge and publication remain separate later checkpoints.
|
|
48
55
|
|
|
49
56
|
## Historical initial npm bootstrap
|
|
50
57
|
|
|
@@ -58,9 +65,9 @@ npm run verify:package
|
|
|
58
65
|
npm publish .package --tag next --access public
|
|
59
66
|
```
|
|
60
67
|
|
|
61
|
-
Confirm the authenticated npm account, `@vrtmrz` scope ownership, package name, version, tarball checksum, packed contents, source commit, and target tag immediately before publication. Treat bootstrap publication as a separate user-authorised operation. npm may assign `latest` to the first published version even when `next` is requested; leave the immutable version in place and replace `latest` only after a stable release has passed
|
|
68
|
+
Confirm the authenticated npm account, `@vrtmrz` scope ownership, package name, version, tarball checksum, packed contents, source commit, and target tag immediately before publication. Treat bootstrap publication as a separate user-authorised operation. npm may assign `latest` to the first published version even when `next` is requested; leave the immutable version in place and replace `latest` only after a stable release has passed the Commonlib owner checks and any applicable targeted host checks.
|
|
62
69
|
|
|
63
|
-
Bootstrap publication was the one historical exception which used direct `npm publish`. Do not repeat that operation for routine releases.
|
|
70
|
+
Bootstrap publication was the one historical exception which used direct `npm publish`. Do not repeat that operation for routine releases. Optional prereleases use protected staged publishing and may select an exact reviewed branch commit; stable releases use an exact `main` commit. If the owner checks or an applicable targeted host check fails, leave the published version immutable and prepare a new version.
|
|
64
71
|
|
|
65
72
|
## Trusted staged publishing
|
|
66
73
|
|
|
@@ -73,9 +80,9 @@ After bootstrap, configure the npm Trusted Publisher for:
|
|
|
73
80
|
|
|
74
81
|
Protect the GitHub `npm` environment with a required reviewer and permit only `main`. The trusted workflow definition always runs from `main`. A pre-release may package an exact commit from a reviewed draft branch, while a stable release also requires the package source and workflow-triggering commit to be the exact current `main` commit. The workflow confirms that the selected source branch still points to the requested full commit SHA, checks out that SHA, runs `verify:package`, validates the requested version and confirmation text, packs the reviewed output, records its checksum, and passes the same tarball to the protected staging job.
|
|
75
82
|
|
|
76
|
-
npm's automatic provenance identifies the `main` commit which triggered the trusted workflow. For
|
|
83
|
+
npm's automatic provenance identifies the `main` commit which triggered the trusted workflow. For an optional prerelease selected from a draft branch, this identifies the trusted workflow source rather than the packaged source commit. Record and verify the separate `source_ref`, exact package SHA, and tarball checksum before approving the staged version. This provenance distinction is accepted for an immutable prerelease when a consumer check is planned before merge. A stable release requires the workflow and package source to be the same exact `main` commit.
|
|
77
84
|
|
|
78
|
-
|
|
85
|
+
When using an optional pre-release, dispatch it from its exact reviewed draft branch commit:
|
|
79
86
|
|
|
80
87
|
```bash
|
|
81
88
|
source_ref=separate-setting-lifecycle
|
|
@@ -91,11 +98,11 @@ gh workflow run publish-npm.yml \
|
|
|
91
98
|
|
|
92
99
|
For a stable release, use `source_ref=main`. The workflow rejects a stable release selected from another branch, rejects a stable release without its trusted workflow commit, and requires the workflow-triggering SHA to equal the packaged `main` SHA. The workflow dispatch itself uses `--ref main` for every release, so an unmerged branch cannot change the trusted publication job or acquire access to the `npm` environment.
|
|
93
100
|
|
|
94
|
-
The workflow always stages to `next`. Inspect the staged package name, version, access, dist-tag, provenance, checksum, files, selected source branch, and source commit before approving
|
|
101
|
+
The workflow always stages to `next`. Inspect the staged package name, version, access, dist-tag, provenance, checksum, files, selected source branch, and source commit before approving publication to npm. After approval publishes the version, fetch that exact registry version and compare its SHA-256, package name and version, and provenance with the reviewed staged artefact and source revision; confirm that `next` points to that version. Install the fetched registry artefact in a clean consumer and run the relevant package checks before requesting promotion. Stage approval and later promotion to `latest` are separate user-authorised operations. Promote the same version only after those registry checks pass and any applicable targeted host checks are complete. If a check fails, leave the published version immutable and prepare a new version. Keep consumer pull requests in draft only while a specifically planned host check is in progress; consumer adoption is not a precondition for Commonlib merge or release.
|
|
95
102
|
|
|
96
103
|
## Publishing a GitHub Release
|
|
97
104
|
|
|
98
|
-
Create a GitHub Release for each stable Commonlib release after the exact registry artefact has passed
|
|
105
|
+
Create a GitHub Release for each stable Commonlib release after the exact registry artefact has passed the Commonlib owner checks and the version has been promoted to `latest`. Complete any applicable targeted host checks before release. Target the exact `main` commit used to build the published package.
|
|
99
106
|
|
|
100
107
|
Protect the GitHub `github-release` environment with a required reviewer and permit only `main`. The manually dispatched workflow first verifies the release with read-only repository access. Only its protected publication job receives `contents: write` access.
|
|
101
108
|
|
|
@@ -18,7 +18,7 @@ import { createNewVaultSettings } from "@vrtmrz/livesync-commonlib/settings";
|
|
|
18
18
|
const initialSettings = createNewVaultSettings();
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
The recommended base currently selects a 50 MB maximum synchronised file size, Rabin–Karp chunk splitting, Plug-in Sync V2, case-insensitive file-name handling, and E2EE V2. Chunk revisions are always derived from their content; the obsolete `doNotUseFixedRevisionForChunks` stored key is retained only for compatibility and is not a recommendation or tweak. The recommended base does not enable synchronisation or encryption on behalf of the user.
|
|
21
|
+
The recommended base currently selects a 50 MB maximum synchronised file size, a `customChunkSize` value of 0, Rabin–Karp chunk splitting, Plug-in Sync V2, case-insensitive file-name handling, and E2EE V2. Chunk revisions are always derived from their content; the obsolete `doNotUseFixedRevisionForChunks` stored key is retained only for compatibility and is not a recommendation or tweak. The recommended base does not enable synchronisation or encryption on behalf of the user.
|
|
22
22
|
|
|
23
23
|
Compared with the conservative stored-setting fallbacks, the current new-Vault object has exactly two semantic differences:
|
|
24
24
|
|
|
@@ -27,15 +27,23 @@ Compared with the conservative stored-setting fallbacks, the current new-Vault o
|
|
|
27
27
|
| `usePluginSyncV2` | `false` | `true` |
|
|
28
28
|
| `handleFilenameCaseSensitive` | normalised from the legacy absent value to `false` | `false` |
|
|
29
29
|
|
|
30
|
-
The other headline recommendations are already the conservative fallbacks: `syncMaxSizeInMB` is 50, `chunkSplitterVersion` is `v3-rabin-karp`, and `E2EEAlgorithm` is `v2`. Data Compression, Eden, V1 dynamic iteration, the legacy IndexedDB adapter, Hidden File Sync, and automatic synchronisation remain disabled when their stored values are absent. Explicit stored values take precedence.
|
|
30
|
+
The other headline recommendations are already the conservative fallbacks: `syncMaxSizeInMB` is 50, `customChunkSize` is 0, `chunkSplitterVersion` is `v3-rabin-karp`, and `E2EEAlgorithm` is `v2`. Data Compression, Eden, V1 dynamic iteration, the legacy IndexedDB adapter, Hidden File Sync, and automatic synchronisation remain disabled when their stored values are absent. Explicit stored values take precedence.
|
|
31
31
|
|
|
32
|
-
Apply a remote-specific preferred object only while configuring that remote. Do not merge it into an existing stored configuration merely because the remote type can be inferred.
|
|
32
|
+
Apply a remote-specific preferred object only while configuring that remote. Do not merge it into an existing stored configuration merely because the remote type can be inferred. Cloudant, self-hosted CouchDB, and Journal Sync setup all use the common `customChunkSize` value of 0. Their unrelated request and bulk settings remain service-specific. The current Doctor regulation no longer recommends changing the value to 60 universally, and existing explicit values remain valid.
|
|
33
|
+
|
|
34
|
+
The new Doctor rule set inherits 1.0.33 and withdraws the Chunk-size rule with
|
|
35
|
+
`customChunkSize: undefined`. Historical regulations keep their original rules.
|
|
36
|
+
This removes the recommendation, rather than deleting a stored setting or
|
|
37
|
+
forcing it to 0. Its consultation version deliberately remains 1.0.33: removing
|
|
38
|
+
a recommendation does not require users to review unchanged recommendations
|
|
39
|
+
again. Existing dismissals remain effective; an explicit forced rescan still
|
|
40
|
+
checks the remaining rules.
|
|
33
41
|
|
|
34
42
|
| Explicitly selected remote | `customChunkSize` | `concurrencyOfReadChunksOnline` | `minimumIntervalOfReadChunksOnline` |
|
|
35
43
|
| --- | ---: | ---: | ---: |
|
|
36
44
|
| Cloudant | 0 | 100 | 333 |
|
|
37
|
-
| Self-hosted CouchDB |
|
|
38
|
-
| Object Storage |
|
|
45
|
+
| Self-hosted CouchDB | 0 | 30 | 25 |
|
|
46
|
+
| Journal Sync (Object Storage) | 0 | 30 | 25 |
|
|
39
47
|
|
|
40
48
|
These are setup-time recommendations, not upgrade migrations. Selecting or detecting a remote does not apply them to an existing configuration unless the person is actively configuring that remote.
|
|
41
49
|
|
|
@@ -60,6 +68,8 @@ if (prepared.requiresSyncReview) {
|
|
|
60
68
|
|
|
61
69
|
Explicit stored values take precedence over every fallback. A migration therefore preserves synchronisation switches and other user choices unless a migration step documents a deliberate transformation.
|
|
62
70
|
|
|
71
|
+
`batchSaveInLiveSync` is a separate explicit choice for bounded local database batching in LiveSync mode. Both new and existing settings default to `false`; the legacy pair `liveSync: true, batchSave: true` does not enable it. The public setting property is optional for existing host settings shapes, and a missing value remains disabled. Other modes continue to use `batchSave`. Markdown settings and QR data preserve the explicit choice, while older imports without the property do not infer it from `batchSave`. Hosts should preserve both choices during ordinary mode changes and use the shared minimum and maximum delays.
|
|
72
|
+
|
|
63
73
|
The sleep preferences are included in Setup URI encoding. When an older Setup URI or stored setting does not contain either key, `allowSleepDuringSynchronisation` defaults to `false`, while `allowSleepDuringSynchronisationOnDesktop` defaults to `true`. The maintained Obsidian host therefore retains best-effort screen-awake protection on mobile but allows normal automatic sleep on desktop unless the user selects otherwise.
|
|
64
74
|
|
|
65
75
|
An existing settings store without an explicit `handleFilenameCaseSensitive` value is normalised to `false` and saved. This preserves the effective behaviour of earlier releases, where an absent value followed the case-insensitive branch. An explicit `true` or `false` value is preserved. New Vaults also select case-insensitive handling (`false`).
|
|
@@ -72,6 +82,30 @@ Two other values deliberately remain unset until their owning lifecycle supplies
|
|
|
72
82
|
|
|
73
83
|
The standard Commonlib `SettingService` performs this preparation automatically. Hosts which use that service can inspect `getSettingsMigrationState()` after `loadSettings()` instead of calling the function themselves.
|
|
74
84
|
|
|
85
|
+
## Host storage for device values and configuration
|
|
86
|
+
|
|
87
|
+
The host selects an asynchronous `ValueStore & ValueDeleter` for each application instance and injects it into the setting service as `deviceValues`. Commonlib does not discover `globalThis.localStorage`, create module-global storage, or fall back to another store. `setting.deviceValues` exposes the injected exact-key root. `setting.config` is an asynchronous view over the same root, scoped to the current Vault name and `additionalSuffixOfDatabaseName`; the Service Hub `config` property refers to that same view.
|
|
88
|
+
|
|
89
|
+
For an existing Web Storage layout, pass the selected storage object explicitly and use an empty adapter prefix so the service can address existing raw keys:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { createWebStorageValueStore } from "@vrtmrz/value-store/web-storage";
|
|
93
|
+
|
|
94
|
+
const deviceValues = createWebStorageValueStore(window.localStorage, { prefix: "" });
|
|
95
|
+
// Supply deviceValues as the setting service's required composition dependency.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Use `deviceValues` for exact device-local keys, including existing keys such as `ls-setting-passphrase`. Use `config` for logical configuration keys; the service retains the established Vault and database-suffix key format. For example, the `checkpoint` key for Vault `notes` remains `notes--checkpoint` with an empty suffix and `notes-team-checkpoint` with suffix `team`. Keep the existing key names and await every read, write, and deletion:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const passphrase = await setting.deviceValues.getValue("ls-setting-passphrase");
|
|
102
|
+
const checkpoint = await setting.config.getValue("checkpoint");
|
|
103
|
+
await setting.config.setValue("checkpoint", "revision-42");
|
|
104
|
+
await setting.config.delete("checkpoint");
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`getValue` returns `null` when a key is absent. Use `getObject` and `setObject` for values stored as JSON; Commonlib does not automatically reinterpret old strings or migrate entries between the exact-key root and the scoped view.
|
|
108
|
+
|
|
75
109
|
## Preparing settings for persistence and Markdown
|
|
76
110
|
|
|
77
111
|
The existing `SettingService` provides `changeConfigurationEncryption(store, passphrase?)` for hosts which edit the configuration encryption mode or device-local custom key. Await it before marking those edits as saved. It prepares one wrapper key for connection credentials, the E2EE passphrase, remote profile URIs, and the independent ID key, and serialises the change with ordinary settings saves. It rejects empty keys and unreadable existing protected items. A failed write attempts to restore the previous device key and does not send a success event. The device key and settings document use separate host writes, so this is not a guarantee of rollback after process termination or an uncertain persistence error.
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
date: 2026-
|
|
3
|
-
commonlib-version: "0.
|
|
4
|
-
self-hosted-livesync-version: "1.0.
|
|
2
|
+
date: 2026-10-25
|
|
3
|
+
commonlib-version: "0.2.0"
|
|
4
|
+
self-hosted-livesync-version: "1.0.40"
|
|
5
|
+
status: unreleased
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Storage events and database-to-storage reflection
|
|
8
9
|
|
|
9
10
|
This note separates storage-to-database event handling from
|
|
10
|
-
database-to-storage reflection. It
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
versions
|
|
11
|
+
database-to-storage reflection. It documents the current guard against a stale
|
|
12
|
+
`DELETE` notification logically deleting Metadata while the same file is still
|
|
13
|
+
present in storage, and the accepted local completion contract for queued file
|
|
14
|
+
operations. The version fields identify the reviewed source versions; these
|
|
15
|
+
changes remain unreleased.
|
|
14
16
|
|
|
15
17
|
## Processing directions
|
|
16
18
|
|
|
@@ -46,6 +48,101 @@ and unsynchronised storage content retain their existing reflection rules.
|
|
|
46
48
|
The watcher guard does not intercept this path or general explicit database
|
|
47
49
|
deletion operations.
|
|
48
50
|
|
|
51
|
+
## Pending operations and local completion
|
|
52
|
+
|
|
53
|
+
Once admitted, a file operation remains pending while buffered, waiting for its
|
|
54
|
+
batch boundary, or running in a handler. The manager includes all three states
|
|
55
|
+
in its existing event snapshot. It saves snapshots in order, so an older write
|
|
56
|
+
cannot replace a newer view of pending work. The manager acknowledges an
|
|
57
|
+
ordinary file operation only after its effect is durable locally, current state proves the
|
|
58
|
+
effect is already represented or safely covered by a successful successor, or
|
|
59
|
+
the handler returns a skip explicitly allowed by policy. A handler
|
|
60
|
+
failure or exception leaves the saved operation pending. A failed initial
|
|
61
|
+
snapshot prevents the handler from starting; a later snapshot failure is
|
|
62
|
+
reported and cannot be treated as completion. Completion of an older operation
|
|
63
|
+
cannot remove a newer operation for the same path.
|
|
64
|
+
|
|
65
|
+
The event snapshot records operation intent, not authoritative current file
|
|
66
|
+
content. Admission does not read a file body merely to wait. When a `CREATE` or
|
|
67
|
+
`CHANGED` operation becomes eligible, the handler rechecks the current path and
|
|
68
|
+
obtains its current content for processing. Restored events are revalidated
|
|
69
|
+
against current storage and selection before they can affect the database. The
|
|
70
|
+
saved stub and any optional cache are not used as a historical file version.
|
|
71
|
+
The current-storage deletion checks and rename-exclusion rules below continue to
|
|
72
|
+
apply.
|
|
73
|
+
|
|
74
|
+
A restored deletion whose database entry is already absent or deleted completes
|
|
75
|
+
without another revision. A restored rename whose source deletion is already
|
|
76
|
+
represented reconciles the current target through the ordinary non-forced
|
|
77
|
+
handler. The same applies to a case-only rename when Metadata already records
|
|
78
|
+
the target spelling. Replaying either completed operation preserves later local
|
|
79
|
+
edits. If the source is still live and a target already exists, the existing
|
|
80
|
+
collision refusal remains in force: the snapshot does not prove that this
|
|
81
|
+
rename created that target, so the unresolved operation remains pending.
|
|
82
|
+
|
|
83
|
+
`commitPendingFileEvents()` requests immediate processing of the finite set of
|
|
84
|
+
operations accepted before the call, including work which is buffered,
|
|
85
|
+
waiting, or already in a handler. It waits for each actual handler result and
|
|
86
|
+
for the ordered snapshot update which records that result; releasing a timer is
|
|
87
|
+
not completion. Later events do not extend that captured boundary. A newer
|
|
88
|
+
coalesced operation satisfies an earlier one only when its successful result
|
|
89
|
+
covers the earlier intent. A failed handler or snapshot update is reported to
|
|
90
|
+
the caller and leaves unresolved work in the snapshot. This boundary covers
|
|
91
|
+
local completion under the selected handler contract only; it does not prove replication or receipt by another
|
|
92
|
+
Vault.
|
|
93
|
+
|
|
94
|
+
A `DELETE` or `RENAME` ordering sentinel waits for its predecessors only. It
|
|
95
|
+
must not wait for itself or events which follow it in the dispatch queue. Batch
|
|
96
|
+
timers make work eligible to run; they do not perform it. The quiet period
|
|
97
|
+
follows the latest eligible content update, while the maximum intentional delay
|
|
98
|
+
is measured from the first uncommitted update, starting when the queue call is
|
|
99
|
+
accepted and before asynchronous filtering or the touched-file check. Waiting
|
|
100
|
+
timers do not occupy a processing slot, and time spent waiting for a slot does
|
|
101
|
+
not restart the maximum delay. Once due, the operation acquires a slot and reads
|
|
102
|
+
current content for processing.
|
|
103
|
+
|
|
104
|
+
## Optional file handlers
|
|
105
|
+
|
|
106
|
+
`INTERNAL` events and files marked `isInternal` use their owning feature's
|
|
107
|
+
contract. Hosts can register `processOptionalFileEventWithResult` handlers with
|
|
108
|
+
`firstHandled`. `HandlerOutcome<TValue = void, TVetoReason = void>` carries a
|
|
109
|
+
success value and a veto reason independently; a non-void payload is required
|
|
110
|
+
in its branch. Dispatch follows priority and registration order:
|
|
111
|
+
|
|
112
|
+
| Outcome | Dispatch and completion |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `not-applicable` | Try the next provider. If none applies, complete the policy-approved skip. |
|
|
115
|
+
| `completed` | Stop and complete the event after the provider's actual save or verified no-op. |
|
|
116
|
+
| `veto` | Stop and retain the event with a reported refusal reason. |
|
|
117
|
+
| Exception or rejection | Propagate the original error and retain the event. |
|
|
118
|
+
|
|
119
|
+
A completed result stops dispatch immediately; a later provider cannot veto
|
|
120
|
+
it. Empty registration returns `not-applicable`. False or zero payloads remain
|
|
121
|
+
valid values. Providers must resolve their promise only after the operation
|
|
122
|
+
finishes. Snapshot acknowledgement still follows the handler result, and a
|
|
123
|
+
snapshot failure keeps the event recoverable.
|
|
124
|
+
|
|
125
|
+
The optional `whenFlushRequested` promise resolves when a finite completion
|
|
126
|
+
boundary includes this event. A provider which debounces saves uses it to
|
|
127
|
+
release that event's captured delay. It must wait for the corresponding save,
|
|
128
|
+
settle all notifications coalesced into that save, and keep later groups outside
|
|
129
|
+
the captured boundary. This signal is runtime state; it does not change the
|
|
130
|
+
persisted snapshot format.
|
|
131
|
+
|
|
132
|
+
Migrate all optional providers in a host together. Once the first typed provider
|
|
133
|
+
is registered, the manager uses the typed chain exclusively, even when every
|
|
134
|
+
provider returns `not-applicable`. A host without a registered typed provider
|
|
135
|
+
retains `processOptionalFileEvent` as a legacy notification contract. Its
|
|
136
|
+
boolean does not confirm persistence: a resolved notification completes only
|
|
137
|
+
that dispatch, while a rejection which reaches the manager is still retained.
|
|
138
|
+
The existing boolean aggregators and ordinary file contract are unchanged.
|
|
139
|
+
|
|
140
|
+
During start-up replay and settings or unload flushes, providers use actual
|
|
141
|
+
database readiness and initialise their required local state before saving.
|
|
142
|
+
They must not wait for an application-ready or resume event which occurs only
|
|
143
|
+
after that same flush. An unavailable prerequisite returns a veto; the manager
|
|
144
|
+
does not introduce an automatic retry loop for a failed operation.
|
|
145
|
+
|
|
49
146
|
## Event intent and current storage state
|
|
50
147
|
|
|
51
148
|
| Event intent | Current storage observation | Admitted operation |
|
|
@@ -92,9 +189,11 @@ incoming logical deletion can still remove unchanged storage content.
|
|
|
92
189
|
|
|
93
190
|
These tests do not establish the native watcher event sequence reported in
|
|
94
191
|
Issue 1168. Real-runtime verification belongs to the host and must distinguish
|
|
95
|
-
observed events from inferred causes.
|
|
96
|
-
convergence or full folder-rename support.
|
|
97
|
-
|
|
192
|
+
observed events from inferred causes. The watcher guard does not promise
|
|
193
|
+
path-case convergence or full folder-rename support. Focused manager tests cover
|
|
194
|
+
finite completion boundaries, ordered snapshots, retained failures, replay, and
|
|
195
|
+
batch timing. This work adds no second journal, unbounded retry loop, or
|
|
196
|
+
database-schema change.
|
|
98
197
|
|
|
99
198
|
The maintained host's real-runtime comparison is recorded in
|
|
100
199
|
[Proven in maintained hosts](proven-in-use.md#obsidian-plug-in), separately
|