@vrtmrz/livesync-commonlib 0.2.2 → 0.3.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.
- package/README.md +86 -1
- package/dist/API/DirectFileManipulatorV2.d.ts +7 -1
- package/dist/API/DirectFileManipulatorV2.js +46 -67
- package/dist/API/DirectFileWatchOwner.d.ts +24 -0
- package/dist/API/DirectFileWatchOwner.js +256 -0
- package/dist/API/processSetting.js +3 -3
- package/dist/ContentSplitter/ContentSplitterRabinKarp.js +2 -17
- package/dist/ContentSplitter/ContentSplitterV1.js +9 -19
- package/dist/ContentSplitter/ContentSplitterV2.js +9 -19
- package/dist/common/models/setting.type.d.ts +1 -1
- package/dist/common/models/shared.definition.configNames.js +1 -2
- package/dist/common/models/tweak.definition.d.ts +1 -1
- package/dist/common/pathNormalization.d.ts +2 -0
- package/dist/common/pathNormalization.js +7 -2
- package/dist/common/utils.d.ts +33 -3
- package/dist/common/utils.js +12 -44
- package/dist/common/utils.object.js +2 -1
- package/dist/concurrency/keyedPendingWork.d.ts +13 -0
- package/dist/concurrency/keyedPendingWork.js +27 -0
- package/dist/conflicts/conflictCheckQueue.d.ts +45 -0
- package/dist/conflicts/conflictCheckQueue.js +108 -0
- package/dist/conflicts.d.ts +3 -0
- package/dist/conflicts.js +4 -0
- package/dist/diagnostics/integrity.js +11 -12
- package/dist/interfaces/ServiceModule.d.ts +42 -24
- package/dist/interfaces/ServiceModule.js +4 -3
- package/dist/managers/ChunkFetcher.d.ts +3 -0
- package/dist/managers/ChunkFetcher.js +20 -13
- package/dist/managers/LiveSyncManagers.d.ts +2 -0
- package/dist/managers/LiveSyncManagers.js +1 -0
- package/dist/managers/StorageEventManager.d.ts +3 -3
- package/dist/managers/StorageEventManager.js +15 -6
- package/dist/managers/chunkFetchSource.d.ts +14 -0
- package/dist/managers/chunkFetchSource.js +12 -0
- package/dist/pouchdb/LiveSyncDBFunctions.js +5 -0
- package/dist/pouchdb/LiveSyncLocalDB.d.ts +7 -1
- package/dist/pouchdb/LiveSyncLocalDB.js +14 -10
- package/dist/pouchdb/chunks.d.ts +16 -0
- package/dist/pouchdb/pathIdentityCompatibility.d.ts +8 -0
- package/dist/pouchdb/pathIdentityCompatibility.js +19 -1
- package/dist/pouchdb/pouchdb-browser.js +2 -84
- package/dist/pouchdb/pouchdb-http.js +2 -84
- package/dist/pouchdb/purgeMulti.d.ts +4 -0
- package/dist/pouchdb/purgeMulti.js +89 -0
- package/dist/replication/RemoteResource.d.ts +3 -0
- package/dist/replication/index.d.ts +2 -0
- package/dist/replication/index.js +2 -0
- package/dist/replication/journal/JournalSyncCore.d.ts +1 -0
- package/dist/replication/journal/JournalSyncCore.js +41 -20
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +11 -3
- package/dist/replication/journal/LiveSyncJournalReplicator.js +123 -45
- package/dist/replication/trystero/P2PRoomSessionOwner.js +14 -13
- package/dist/replication/trystero/TrysteroReplicator.d.ts +7 -5
- package/dist/replication/trystero/TrysteroReplicator.js +101 -74
- package/dist/replication/trystero/TrysteroReplicator.pathIdentity.d.ts +11 -0
- package/dist/replication/trystero/TrysteroReplicator.pathIdentity.js +72 -0
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +3 -2
- package/dist/replication/trystero/p2pReplicatorConfigurationIdentity.js +2 -0
- package/dist/replication/trystero/peerPatterns.d.ts +1 -0
- package/dist/replication/trystero/peerPatterns.js +6 -0
- package/dist/replication/trystero/useP2PReplicatorFeature.js +6 -2
- package/dist/rpc/RpcRoom.d.ts +5 -1
- package/dist/rpc/RpcRoom.js +80 -105
- package/dist/serviceFeatures/offlineScanner.d.ts +4 -4
- package/dist/serviceFeatures/offlineScanner.js +14 -15
- package/dist/serviceFeatures/prepareDatabaseForUse.d.ts +2 -2
- package/dist/serviceFeatures/remoteConfig.d.ts +10 -5
- package/dist/serviceModules/Rebuilder.d.ts +1 -0
- package/dist/serviceModules/Rebuilder.js +33 -5
- package/dist/serviceModules/ServiceFileHandlerBase.js +2 -7
- package/dist/services/implements/injectable/InjectableConflictService.d.ts +5 -5
- package/dist/services/lib/HandlerUtils.d.ts +8 -6
- package/dist/services/lib/HandlerUtils.js +14 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/conflict-resolution.md +30 -0
- package/docs/development.md +50 -0
- package/docs/proven-in-use.md +80 -0
- package/docs/releasing.md +4 -0
- package/docs/service-feature-composition.md +63 -10
- package/docs/unicode-path-identity.md +64 -3
- package/package.json +11 -3
|
@@ -72,6 +72,36 @@ Each duplicate collapse, conservative merge, or host-directed manual choice is c
|
|
|
72
72
|
|
|
73
73
|
A host must not apply a dialogue result after either compared revision has ceased to be the current pair. It should discard that stale result, refresh its warning or dialogue state, and queue the path again when conflicts remain.
|
|
74
74
|
|
|
75
|
+
## Scheduling conflict checks
|
|
76
|
+
|
|
77
|
+
The `@vrtmrz/livesync-commonlib/conflicts` entry, published in Commonlib 0.2.3, exports
|
|
78
|
+
`createConflictCheckQueue`. The host supplies a `process(path, scope)` callback
|
|
79
|
+
and may observe pending plus active checks through `onProgress`. Concurrency
|
|
80
|
+
defaults to ten and includes each item's deferred cleanup. The host continues
|
|
81
|
+
to own active-file filtering, optional conflict handlers, revision decisions,
|
|
82
|
+
and serialisation of shared user interactions.
|
|
83
|
+
|
|
84
|
+
`enqueue(path)` accepts work synchronously. A duplicate pending path replaces
|
|
85
|
+
its earlier entry and moves to the tail; an executing check is unchanged.
|
|
86
|
+
An executing check may enqueue its path again, including while every execution
|
|
87
|
+
slot is occupied. Pending paths have no fixed capacity, and there is no delay
|
|
88
|
+
or batching policy.
|
|
89
|
+
|
|
90
|
+
`waitForIdle()` waits until accepted work, new arrivals, requeued checks, and
|
|
91
|
+
cleanup have finished. It leaves admission open for later checks and returns
|
|
92
|
+
`true` on quiescence. `close()` stops admission and drains accepted work.
|
|
93
|
+
`abort()` stops admission and further dispatch, waits for started work and
|
|
94
|
+
cleanup, and returns a snapshot of the unstarted paths. It does not forcibly
|
|
95
|
+
cancel a running callback; `waitForIdle()` returns `false` after abort.
|
|
96
|
+
A processor must not await its own queue's completion, closure, or abort.
|
|
97
|
+
|
|
98
|
+
An unhandled processing, cleanup, or progress-observer error stops dispatch.
|
|
99
|
+
Completion methods reject only after started work and cleanup settle, while
|
|
100
|
+
`pending` retains the unstarted paths for inspection. Catch and report an item
|
|
101
|
+
error inside `process` when the host deliberately intends to continue with
|
|
102
|
+
later items. The queue performs no implicit retry, logging, persistence, or
|
|
103
|
+
recovery. Enqueueing after close, abort, or failure throws.
|
|
104
|
+
|
|
75
105
|
## File-reflection provenance
|
|
76
106
|
|
|
77
107
|
The compatibility implementation accepts an injected, device-local `FileReflectionProvenance` capability owned by the database-to-storage composition rather than by a filesystem adapter. Maintained hosts persist:
|
package/docs/development.md
CHANGED
|
@@ -7,6 +7,7 @@ This document is for Commonlib developers. Package consumers should begin with t
|
|
|
7
7
|
The source tree is larger than the supported package surface. Consumers may import only paths in the generated package export map:
|
|
8
8
|
|
|
9
9
|
- `src/index.ts`, `src/context.ts`, `src/settings.ts`, `src/remoteConfigurations.ts`, `src/p2p/index.ts`, `src/platform/browser/index.ts`, and `src/platform/node/index.ts` define the focused entries;
|
|
10
|
+
- `src/conflicts.ts` defines the host-owned conflict-check queue contract; `src/concurrency/keyedPendingWork.ts` remains package-private;
|
|
10
11
|
- `src/replication/index.ts` defines the focused provider-capability and interaction contract for hosts which compose replication;
|
|
11
12
|
- `src/diagnostics/index.ts` defines read-only Usage and integrity inspection, report projection, and formatting; hosts own the run dialogue and output destination;
|
|
12
13
|
- `src/rpc/index.ts` defines a transitional entry for the existing LiveSync P2P composition, not a stable Commonlib 1.0 contract;
|
|
@@ -37,6 +38,10 @@ Commonlib uses four related forms of composition. Choose between them by ownersh
|
|
|
37
38
|
- A **ServiceModule** is a long-lived stateful or resource-owning object constructed by the host and shared through the typed `ServiceModules` record. Use one when several consumers need the same operational capability or lifecycle, such as storage access, file handling, or database rebuilding. State alone does not make a ServiceModule necessary.
|
|
38
39
|
- An **AbstractModule** is the legacy application module layer. Existing modules may continue to own application behaviour, but new composition should not acquire the broad core merely to gain lifecycle access or shared dependencies.
|
|
39
40
|
|
|
41
|
+
`NecessaryServices<T, U, THub>` selects dependencies from a chosen Service Hub source, defaulting to `ServiceHub`, and retains `context` only when that source exposes it. Declare additional collaborators in the feature's own dependency type. `createServiceFeature()` preserves the supplied function's complete signature and result, but does not infer dependency keys from its body; annotate the host parameter when a narrow dependency contract is intended. Unannotated legacy callbacks retain the default `ServiceHub` contract for compatibility.
|
|
42
|
+
|
|
43
|
+
Keep operation host types focused on the dependencies the operation uses, and let the composer add the Services required for logging, lifecycle access, or handler registration. Where an exported compatibility host must remain broad, use narrower private host types for the implementation. The examples in [Service feature composition](service-feature-composition.md) show these separate boundaries.
|
|
44
|
+
|
|
40
45
|
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.
|
|
41
46
|
|
|
42
47
|
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.
|
|
@@ -106,6 +111,10 @@ The revision-tree safety rules are documented in [Conflict resolution and file p
|
|
|
106
111
|
|
|
107
112
|
`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.
|
|
108
113
|
|
|
114
|
+
Direct CouchDB hosts supply a database-bound `ChunkFetchSource` through the optional `LiveSyncLocalDB` runtime factory. `LiveSyncManagers` passes it to the shared `ChunkFetcher`; application hosts omit it and retain active-replicator retrieval and local persistence. The direct source reads the prepared database and accepts results without writing them back. Recreate it with each database instance so reopening cannot retain a closed connection. `ChunkFetcher.source.unit.spec.ts` covers direct retrieval, delayed availability without change notifications, bounded failure, and teardown. `DirectFileManipulatorV2.integration.spec.ts` covers encrypted arrival before and during an outstanding read, with assertions that the reader does not write Chunks back.
|
|
115
|
+
|
|
116
|
+
`DirectFileWatchOwner` owns the direct watch lifecycle. Its per-client state retains unfinished reads, same-document delivery ownership, and pending checkpoint persistence across stop and restart. Each watch session separately owns its pending documents, connection generation, and timers. Named operations receive explicit contexts through Octagonal Wheels `bindContext`; per-file and per-delivery scopes release their bookkeeping after the operation settles. Feed cancellation and timer cancellation remain synchronous in `endWatch()`, independently of unfinished reads or host callbacks. The owner tests cover retries, safe checkpoint positions, replacement sessions, and failure handling; `DirectFileManipulatorV2.unit.spec.ts` retains the public state, database binding, and close-order checks.
|
|
117
|
+
|
|
109
118
|
`RemoteService.connect()` returns an owned CouchDB connection as the existing flat `{ db, info }` result with an idempotent `close()` method. `close()` first aborts outstanding abort-capable requests in that connection, then closes the PouchDB handle. The owner must transfer or close the complete connection; passing only `db` loses the cancellation boundary. A skipped information request retains the established placeholder in `info` for compatibility. `OwnedCouchDBConnection`, its opening options, and the checked `CouchDBReplicationConnection` are Commonlib-owned types exported by the existing CouchDB replicator module. The legacy `_ensureConnection()` helper continues to return a raw PouchDB handle for source compatibility; new Commonlib code must retain and close the complete owned connection instead.
|
|
110
119
|
|
|
111
120
|
The one-shot CouchDB connectivity preflight is the first bounded consumer. It applies an internal 60-second safety fuse to the complete preflight on the web-compatible fetch path, disables fallback into the non-abortable native request adapter, and clears the fuse before transferring the connection to replication. The fuse is a last-resort wall-clock bound on an owner which would otherwise remain pending indefinitely; it is not the expected completion time, a per-request inactivity timeout, a user setting, or the `useTimeouts` changes-feed setting. The explicitly selected native Request API retains its previous unbounded behaviour because its current host adapter cannot honour `AbortSignal`. Extend `RemoteService.unit.spec.ts` and the one-shot ownership table in `LiveSyncReplicator.unit.spec.ts` when changing this boundary. Do not use `Promise.race()` to report a mutating native request as cancelled while it can still complete remotely.
|
|
@@ -149,6 +158,37 @@ Commonlib publishes the typed English messages which it owns and uses them when
|
|
|
149
158
|
|
|
150
159
|
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.
|
|
151
160
|
|
|
161
|
+
### Testing an unpublished Octagonal Wheels dependency
|
|
162
|
+
|
|
163
|
+
The current integration pins the published Octagonal Wheels 0.1.56 release and
|
|
164
|
+
uses the ordinary package gate. The override below is for future unpublished
|
|
165
|
+
dependency changes.
|
|
166
|
+
|
|
167
|
+
Keep the repository dependency declaration and lockfile separate from a local
|
|
168
|
+
candidate check. After building and packing Octagonal Wheels, install that
|
|
169
|
+
exact tarball for source checks without saving it to either manifest:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm ci
|
|
173
|
+
npm install --no-save --ignore-scripts /path/to/octagonal-wheels-candidate.tgz
|
|
174
|
+
npm run check:types
|
|
175
|
+
npm test -- --maxWorkers=1 --fileParallelism=false
|
|
176
|
+
npm run test:package -- --octagonal-wheels-tarball /path/to/octagonal-wheels-candidate.tgz
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The package checker adds the selected tarball and an explicit npm override
|
|
180
|
+
only to its disposable consumer fixture. It reports the tarball's SHA-256 and
|
|
181
|
+
checks that Commonlib resolves that same installed package. The generated
|
|
182
|
+
Commonlib package retains its original dependency declaration. The option
|
|
183
|
+
does not publish a package or select a future release version.
|
|
184
|
+
|
|
185
|
+
The Blob migration's source tests cover legacy construction and Commonlib's
|
|
186
|
+
document interpretation. Its packed fixtures compile the old imports and
|
|
187
|
+
exercise the generated runtime. A passing candidate check qualifies that
|
|
188
|
+
combination of artefacts; before publication, update the declared dependency
|
|
189
|
+
to a released version containing the new entry, regenerate the lockfile, and
|
|
190
|
+
run the ordinary package gate without the candidate override.
|
|
191
|
+
|
|
152
192
|
## Integration tests
|
|
153
193
|
|
|
154
194
|
The integration suite owns Commonlib's direct CouchDB and S3-compatible storage checks. Run it with managed, disposable CouchDB and RustFS containers:
|
|
@@ -200,6 +240,8 @@ The platform and RPC ownership decisions are accepted:
|
|
|
200
240
|
|
|
201
241
|
The package build records each compatibility path explicitly. Do not add a wildcard export. A new compatibility path requires a real downstream import, a packed-consumer or downstream test, and a migration plan towards a focused public entry or the owning package.
|
|
202
242
|
|
|
243
|
+
The existing `compat/services/lib` helpers `bindableFunction` and `lazyBindableFunction` return an `UnregisterFunction` from `setHandler()`. It removes only the assignment made by that registration while it remains current; a stale remover, including after an override with the same callback, and repeated removals are no-ops. Existing callers may ignore the return value, but implementations and test doubles of the handler interfaces must return a remover. Keep this contract within the existing compatibility boundary rather than presenting it as a general SDK pattern.
|
|
244
|
+
|
|
203
245
|
After the reviewed Self-hosted LiveSync revision changes, inspect its current compatibility imports with:
|
|
204
246
|
|
|
205
247
|
```bash
|
|
@@ -211,3 +253,11 @@ The command recognises both the former `@lib/*` source aliases and current `@vrt
|
|
|
211
253
|
The classification in `docs/migration/compatibility-classification.json` is accepted for LiveSync 1.0 planning. A destination still requires its own result and lifecycle contract before it becomes a stable public entry. Remove obsolete paths rather than documenting accidental layout as API.
|
|
212
254
|
|
|
213
255
|
Release preparation and staged-publication checks are documented in [the maintainer runbook](releasing.md).
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
The shared value extraction uses the declared Octagonal Wheels dependency. Its
|
|
259
|
+
`utils.runtime-tools.unit.spec.ts` tests preserve the legacy adapter contracts;
|
|
260
|
+
`runtime-tools-types.ts` and `runtime-tools-runtime.mjs` check the installed
|
|
261
|
+
package's public duration and delimiter helpers; source tests cover the
|
|
262
|
+
internal JSON copy adapter. When testing a future unpublished dependency, keep
|
|
263
|
+
these checks tied to the same Octagonal Wheels tarball used by the downstream consumer.
|
package/docs/proven-in-use.md
CHANGED
|
@@ -6,6 +6,52 @@ The useful evidence is host- and contract-specific. Focused Commonlib tests esta
|
|
|
6
6
|
|
|
7
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
|
+
## Published shared-runtime integration
|
|
10
|
+
|
|
11
|
+
Commonlib [0.2.3](https://github.com/vrtmrz/livesync-commonlib/releases/tag/0.2.3)
|
|
12
|
+
is published on npm through `latest` and `next`, with an exact dependency on
|
|
13
|
+
Octagonal Wheels 0.1.56. Self-hosted LiveSync
|
|
14
|
+
[1.0.44](https://github.com/vrtmrz/obsidian-livesync/releases/tag/1.0.44)
|
|
15
|
+
adopts the published Commonlib package. Its
|
|
16
|
+
[integration record](https://github.com/vrtmrz/obsidian-livesync/pull/1307)
|
|
17
|
+
separates the final source checks from the focused browser, CLI, and real-Obsidian
|
|
18
|
+
results for unchanged consumer boundaries. The earlier candidate measurements
|
|
19
|
+
below remain historical evidence at their recorded versions.
|
|
20
|
+
|
|
21
|
+
<a id="blob-construction-and-comparison-candidate-integration"></a>
|
|
22
|
+
|
|
23
|
+
## Blob construction and comparison: published integration
|
|
24
|
+
|
|
25
|
+
Commonlib 0.2.3 delegates Blob construction to
|
|
26
|
+
`octagonal-wheels/binary/blob`. Commonlib retains the old creation imports and
|
|
27
|
+
wraps `toBlob` as `createBlob`, while document classification, binary decoding,
|
|
28
|
+
and content-comparison policy remain in Commonlib. The legacy
|
|
29
|
+
`isDocContentSame` delegates its sliced fallback to `isBlobContentEqual`,
|
|
30
|
+
passing the existing Commonlib encoder as the equality predicate. Its native
|
|
31
|
+
IndexedDB path remains in Commonlib.
|
|
32
|
+
|
|
33
|
+
The [legacy result tests](../src/common/utils.blob.unit.spec.ts) establish the
|
|
34
|
+
same behaviour before and after delegation: Blob identity and MIME, text
|
|
35
|
+
parts and line endings, binary view bounds and snapshots, stored-document
|
|
36
|
+
decoding, and representation-independent byte comparison. The
|
|
37
|
+
[comparison contract tests](../src/common/utils.blob-comparison.unit.spec.ts)
|
|
38
|
+
cover size and MIME handling, sequential bounded reads, early termination,
|
|
39
|
+
IndexedDB availability timing, method and receiver selection, Buffer fallback,
|
|
40
|
+
and unchanged failures. [Composition tests](../src/common/utils.blob-delegation.unit.spec.ts)
|
|
41
|
+
spy on the real shared comparison to verify fallback delegation and its
|
|
42
|
+
absence on the native path. The
|
|
43
|
+
[packed type fixture](../_tools/fixtures/blob-compatibility-types.ts) and
|
|
44
|
+
[runtime fixture](../_tools/fixtures/blob-compatibility-runtime.mjs) exercise
|
|
45
|
+
the existing Commonlib imports after packaging.
|
|
46
|
+
|
|
47
|
+
Earlier candidate checks explicitly installed the selected unpublished Octagonal
|
|
48
|
+
Wheels tarball in the disposable consumer. Commonlib 0.2.3 pins the published
|
|
49
|
+
Octagonal Wheels 0.1.56 dependency and uses the ordinary package gate without a
|
|
50
|
+
candidate override. The subsequent Self-hosted LiveSync integration uses the
|
|
51
|
+
exact published Commonlib package. Its recorded application builds and focused
|
|
52
|
+
real-Obsidian conflict and catalogue checks complement the Commonlib-owned
|
|
53
|
+
compatibility contracts; they do not establish physical mobile-device behaviour.
|
|
54
|
+
|
|
9
55
|
## Obsidian plug-in
|
|
10
56
|
|
|
11
57
|
The unreleased V3 Rabin–Karp correction uses proportional binary thresholds,
|
|
@@ -219,3 +265,37 @@ Use the named consumer paths as integration examples, while retaining the owners
|
|
|
219
265
|
## Scope and maintenance
|
|
220
266
|
|
|
221
267
|
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.
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
<a id="shared-value-helpers-candidate-integration"></a>
|
|
271
|
+
|
|
272
|
+
## Shared value helpers: published integration
|
|
273
|
+
|
|
274
|
+
The [runtime helper tests](../src/common/utils.runtime-tools.unit.spec.ts)
|
|
275
|
+
characterise duration thresholds, delimiter empty parts, and JSON conversion
|
|
276
|
+
before and after delegation. Commonlib owns the tagged list adapter and
|
|
277
|
+
internal `asCopy<T>` signature. Replication services use that internal adapter
|
|
278
|
+
to detach settings before an operation. The shared package owns the value
|
|
279
|
+
operations. The packed [type fixture](../_tools/fixtures/runtime-tools-types.ts) and
|
|
280
|
+
[runtime fixture](../_tools/fixtures/runtime-tools-runtime.mjs) exercise those
|
|
281
|
+
public duration and delimiter compatibility entries. The original checks used an
|
|
282
|
+
unpublished dependency candidate; Commonlib 0.2.3 pins the published
|
|
283
|
+
Octagonal Wheels 0.1.56 release. Source tests cover the internal copy adapter.
|
|
284
|
+
LiveSync continues to use the existing compatibility entry points, including
|
|
285
|
+
duration labels in ConfigSync and tagged lists in settings. Source and packed
|
|
286
|
+
checks do not establish real-Obsidian rendering or device behaviour.
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
<a id="shared-multi-key-locking-candidate-integration"></a>
|
|
290
|
+
|
|
291
|
+
## Shared multi-key locking: published integration
|
|
292
|
+
|
|
293
|
+
Commonlib 0.2.3 imports `withLocks` directly from
|
|
294
|
+
`octagonal-wheels/concurrency/lock` for offline Metadata repair and file-event
|
|
295
|
+
processing. Commonlib retains the `processFileEvent-` namespace, unique sorted
|
|
296
|
+
document IDs, and conflict hooks deferred until every lock has been released.
|
|
297
|
+
The [file-handler tests](../src/serviceModules/ServiceFileHandlerBase.unit.spec.ts)
|
|
298
|
+
exercise a rename alongside Metadata repair, including independent keys,
|
|
299
|
+
reversed and repeated input IDs, and callback failure. These are Commonlib-owned
|
|
300
|
+
composition checks using the selected dependency; Octagonal Wheels owns the
|
|
301
|
+
primitive's lock acquisition, result propagation, and release contracts.
|
package/docs/releasing.md
CHANGED
|
@@ -38,6 +38,10 @@ When a named host-specific risk is not established by the Commonlib owner checks
|
|
|
38
38
|
|
|
39
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.
|
|
40
40
|
|
|
41
|
+
For 0.2.3, the documented exceptions are the `HandlerFunction.setHandler` and `LazyHandlerFunction.setHandler` return types changing from `void` to `UnregisterFunction`, and `timeDeltaToHumanReadable(Infinity)` returning `∞y` instead of `Infinityy`. The setter types now describe the existing runtime return. Custom implementations and test doubles whose setters return `void` must provide an unregister function; ordinary callers may continue to ignore the result. These are the specific 0.2.3 exceptions to the policy.
|
|
42
|
+
|
|
43
|
+
Version 0.3.0 introduces a source compatibility change: the custom client passed to `LiveSyncJournalReplicator.ensureBucketIsCompatible` must provide `hasRemoteJournals(): Promise<boolean>`. The operation distinguishes an empty bucket from Journal files left without an identifying milestone. Keeping it required makes missing client capability a type error. The standard `JournalSyncCore` client already supplies it. This change uses the next minor version under the policy above.
|
|
44
|
+
|
|
41
45
|
## Preparing a release
|
|
42
46
|
|
|
43
47
|
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.
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
-
date: 2026-
|
|
3
|
-
commonlib-version: "0.
|
|
4
|
-
self-hosted-livesync-version: "1.0.
|
|
5
|
-
status:
|
|
2
|
+
date: 2026-10-25
|
|
3
|
+
commonlib-version: "0.2.0"
|
|
4
|
+
self-hosted-livesync-version: "1.0.40"
|
|
5
|
+
status: published
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Service feature composition
|
|
9
9
|
|
|
10
|
+
Publication update, 2nd November, 2026: the Service Hub source selection, narrowed
|
|
11
|
+
host dependencies, and signature-preserving composition helpers are published
|
|
12
|
+
in Commonlib [0.2.3](https://github.com/vrtmrz/livesync-commonlib/releases/tag/0.2.3)
|
|
13
|
+
and used by Self-hosted LiveSync 1.0.44. The front matter retains the original
|
|
14
|
+
review date and version baseline.
|
|
15
|
+
|
|
10
16
|
This guide is for Commonlib and maintained-host developers who are adding behaviour to the Service Hub composition. It explains when to extend a Service, create a serviceFeature, share a ServiceModule, or introduce a class which owns a resource.
|
|
11
17
|
|
|
12
18
|
The choice is primarily about dependency direction and ownership. Mutable state alone does not require a class or a ServiceModule.
|
|
@@ -24,7 +30,7 @@ The choice is primarily about dependency direction and ownership. Mutable state
|
|
|
24
30
|
|
|
25
31
|
## Declare dependencies at the boundary
|
|
26
32
|
|
|
27
|
-
Use `NecessaryServices` to state the exact Services and ServiceModules which a feature consumes
|
|
33
|
+
Use `NecessaryServices` to state the exact Services and ServiceModules which a feature consumes. Its type parameters select service keys, module keys, and the Service Hub source; the source defaults to `ServiceHub`. The selected Services retain `context` when that source exposes it. The `NecessaryServicesInterfaces` alias continues to use `IServiceHub` without adding an implicit context to that interface contract.
|
|
28
34
|
|
|
29
35
|
```typescript
|
|
30
36
|
type IndexFeatureHost = NecessaryServices<"appLifecycle" | "API", "storageAccess">;
|
|
@@ -41,6 +47,20 @@ export const useIndexFeature = createServiceFeature((host: IndexFeatureHost) =>
|
|
|
41
47
|
});
|
|
42
48
|
```
|
|
43
49
|
|
|
50
|
+
Select a more specific host when a feature needs an injectable handler type or host-extended context. Collaborators outside the Service Hub and ServiceModules, such as a borrowed logger, belong in the feature's own dependency type:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
type FeatureHost = NecessaryServices<
|
|
54
|
+
"appLifecycle" | "conflict",
|
|
55
|
+
never,
|
|
56
|
+
InjectableServiceHub
|
|
57
|
+
> & {
|
|
58
|
+
readonly log: LogFunction;
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`createServiceFeature()` returns the supplied function unchanged. Its declared host type, optional or additional arguments, generic or overloaded signature, and result type, including a `Promise` or focused view, remain available to callers. Dependency keys are not inferred from the callback body. An unannotated legacy callback retains the full default `ServiceHub` contract for compatibility, so annotate the host parameter when a narrow contract is intended. Existing `createServiceFeature<T, U, Result>(...)` calls remain supported. The `serviceFeature<T, U, THub>().create(...)` builder also selects a source and preserves the supplied function type. Its existing `.create<Result>(...)` call shape and `withContext()` behaviour are unchanged.
|
|
63
|
+
|
|
44
64
|
This signature is both documentation and a compile-time boundary. Adding an unrelated Service to the host object does not make it available to the feature. If the operation needs another collaborator, add it deliberately to the declared contract.
|
|
45
65
|
|
|
46
66
|
A serviceFeature is a composition function, not a runtime registry entry. Call it from the host composition after its dependencies exist:
|
|
@@ -65,13 +85,17 @@ When an operation has meaningful sequencing or failure behaviour, keep it indepe
|
|
|
65
85
|
`prepareDatabaseForUse.ts` follows this split. Its operation accepts explicit collaborators and returns the application result:
|
|
66
86
|
|
|
67
87
|
```typescript
|
|
68
|
-
type
|
|
69
|
-
"appLifecycle" | "
|
|
88
|
+
type PrepareDatabaseOperationHost = NecessaryServices<
|
|
89
|
+
"appLifecycle" | "vault" | "database" | "databaseEvents" | "fileProcessing" | "replicator",
|
|
90
|
+
never
|
|
91
|
+
>;
|
|
92
|
+
type PrepareDatabaseFeatureHost = NecessaryServices<
|
|
93
|
+
"API" | "appLifecycle" | "vault" | "database" | "databaseEvents" | "fileProcessing" | "replicator",
|
|
70
94
|
never
|
|
71
95
|
>;
|
|
72
96
|
|
|
73
97
|
export async function prepareDatabaseForUse(
|
|
74
|
-
host:
|
|
98
|
+
host: PrepareDatabaseOperationHost,
|
|
75
99
|
log: LogFunction,
|
|
76
100
|
errorManager: UnresolvedErrorManager,
|
|
77
101
|
showingNotice = false,
|
|
@@ -83,10 +107,10 @@ export async function prepareDatabaseForUse(
|
|
|
83
107
|
}
|
|
84
108
|
```
|
|
85
109
|
|
|
86
|
-
The corresponding composition constructs the owned error manager and registers one adapter function:
|
|
110
|
+
The operation does not need `setting` or `path`. The corresponding composition adds `API` to create the instance logger, constructs the owned error manager, and registers one adapter function:
|
|
87
111
|
|
|
88
112
|
```typescript
|
|
89
|
-
export function usePrepareDatabaseForUse(host:
|
|
113
|
+
export function usePrepareDatabaseForUse(host: PrepareDatabaseFeatureHost): void {
|
|
90
114
|
const log = createInstanceLogFunction("SF:prepareDatabaseForUse", host.services.API);
|
|
91
115
|
const errorManager = new UnresolvedErrorManager(host.services.appLifecycle, host.services.context.events);
|
|
92
116
|
|
|
@@ -107,6 +131,35 @@ export function usePrepareDatabaseForUse(host: PrepareDatabaseHost): void {
|
|
|
107
131
|
|
|
108
132
|
Test the operation's sequencing and failure short-circuiting separately from the handler registration. This keeps a lifecycle rename from forcing every domain test through an application-shaped fixture.
|
|
109
133
|
|
|
134
|
+
The offline scanner follows the same split. A full-scan operation uses only the file and database dependencies, while the composer adds `API` for its logger and `appLifecycle` for its error manager and handler registration:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
type FullScanHost = NecessaryServices<
|
|
138
|
+
"setting" | "vault" | "path" | "database" | "keyValueDB",
|
|
139
|
+
"storageAccess" | "fileHandler"
|
|
140
|
+
>;
|
|
141
|
+
|
|
142
|
+
type OfflineScannerFeatureHost = NecessaryServices<
|
|
143
|
+
"API" | "appLifecycle" | "setting" | "vault" | "path" | "database" | "keyValueDB",
|
|
144
|
+
"storageAccess" | "fileHandler"
|
|
145
|
+
>;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`performFullScan` uses the full-scan host, and its scan operations declare their own narrower service sets; none requires `fileProcessing`. `useOfflineScanner` binds the `vault.scanVault` handler with the composer host. The scanner does not require `databaseEvents` or `replicator`.
|
|
149
|
+
|
|
150
|
+
### Separate remote-configuration operation and composition hosts
|
|
151
|
+
|
|
152
|
+
`RemoteConfigHost` remains the broad exported compatibility type for existing callers. The implementation uses private host types so each operation or composer declares only its required Services:
|
|
153
|
+
|
|
154
|
+
| Implementation boundary | Private host type | Required Services |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| Remote configuration migration operation | `RemoteConfigMigrationHost` | `setting`, `API` |
|
|
157
|
+
| Migration feature composer | `RemoteConfigMigrationFeatureHost` | `setting`, `API`, `appLifecycle` |
|
|
158
|
+
| Switch-connection command | `RemoteConfigSwitchHost` | `setting`, `UI`, `control`, `API` |
|
|
159
|
+
| Replication command and feature composer | `RemoteConfigReplicationHost` | `setting`, `UI`, `replication`, `control`, `API` |
|
|
160
|
+
|
|
161
|
+
Keep the broad export for compatibility and use the focused private types inside the implementation.
|
|
162
|
+
|
|
110
163
|
Database preparation and full scanning remain strict by default. A host may set `continueOnFileFailure` only when its ordinary start-up policy deliberately permits readiness after individual file-pair failures. In that case, the operation returns `completed-with-file-failures` after all later initialisation steps succeed; it does not collapse the result into `true`. That option does not admit a failed scan precondition or a later initialisation step, and explicit recovery workflows retain the default.
|
|
111
164
|
|
|
112
165
|
The `scanVault` and `initialiseDatabase` services use a failure-veto result aggregator for this contract. It preserves the first non-boolean continuation result but still invokes later handlers, so a later `false` result or exception rejects the operation. `firstResult` is not suitable here because it would return the partial result before those handlers can veto it.
|
|
@@ -14,7 +14,8 @@ does not alter the Metadata path or require physical files to be renamed.
|
|
|
14
14
|
Unsupported values are rejected.
|
|
15
15
|
|
|
16
16
|
NFC identity shipped in Commonlib `0.1.42` and Self-hosted LiveSync `1.0.38`.
|
|
17
|
-
The
|
|
17
|
+
The Journal and P2P extension described below is unreleased. The original
|
|
18
|
+
CouchDB release remains the published baseline for this document.
|
|
18
19
|
|
|
19
20
|
## Package contract
|
|
20
21
|
|
|
@@ -25,6 +26,24 @@ implementation for Metadata IDs rather than add a host-specific normalisation
|
|
|
25
26
|
rule. Host storage adapters remain responsible for mapping a logical path back
|
|
26
27
|
to its existing physical entry.
|
|
27
28
|
|
|
29
|
+
## Initial host settings
|
|
30
|
+
|
|
31
|
+
Commonlib's shared new-Vault and stored-setting defaults retain version `0`.
|
|
32
|
+
The maintained CLI and Obsidian hosts use version `1` for newly created
|
|
33
|
+
synchronisation settings in the unreleased extension, while retaining `0` for
|
|
34
|
+
old settings and Setup URIs which omit the field. This policy does not enable
|
|
35
|
+
NFC for WebApp or WebPeer, and does not migrate existing databases.
|
|
36
|
+
|
|
37
|
+
The focused replication entry exports `inspectDatabasePathIdentity` and
|
|
38
|
+
`PathIdentityInspection`. The existing `RemoteConnectionProbe` may expose
|
|
39
|
+
`inspectPathIdentity` for read-only initial setup. Inspection distinguishes a
|
|
40
|
+
confirmed empty remote from an existing declaration; unavailable or unsupported
|
|
41
|
+
state rejects. Hosts use the existing declaration before first participation,
|
|
42
|
+
and keep source Rebuild and configured startup explicit. Journal inspection
|
|
43
|
+
reads milestone evidence without writing a node, Security Seed, or checkpoint.
|
|
44
|
+
A P2P signalling probe does not establish peer database identity; Setup URIs
|
|
45
|
+
carry the selected mode, and manual configuration must match the source.
|
|
46
|
+
|
|
28
47
|
## Database compatibility
|
|
29
48
|
|
|
30
49
|
Version `1` uses `nfc-path-identity-v1` in the existing generation-13
|
|
@@ -78,14 +97,56 @@ does not itself perform the required reconstruction.
|
|
|
78
97
|
|
|
79
98
|
## Supported hosts
|
|
80
99
|
|
|
81
|
-
The
|
|
100
|
+
The maintained CLI and Obsidian support NFC identity with CouchDB, Journal,
|
|
101
|
+
and P2P in this unreleased extension.
|
|
82
102
|
`PathService` owns logical ID derivation. The CLI resolves NFC-equivalent names
|
|
83
103
|
against directory entries while preserving the actual entry spelling; its
|
|
84
104
|
generic Node storage contract remains exact-path access. Obsidian supplies its
|
|
85
105
|
normalised storage paths and preserves an existing physical spelling when it
|
|
86
106
|
is NFC-equivalent to the received path.
|
|
87
107
|
|
|
88
|
-
Journal
|
|
108
|
+
Journal uses compatibility range `4` for NFC identity, independently of
|
|
109
|
+
encryption and path obfuscation. Legacy ranges remain unchanged. The existing
|
|
110
|
+
stored node ranges and required Tweak reject a different identity even when
|
|
111
|
+
ordinary mismatch warnings are disabled. Removing preferred Tweaks does not
|
|
112
|
+
remove the range check or an existing document-ID agreement proof. The existing
|
|
113
|
+
ID check is also reused before Fetch and ordinary milestone updates. A missing
|
|
114
|
+
milestone over existing journals requires Rebuild in both legacy and NFC modes;
|
|
115
|
+
it is not permission to declare a new identity. Older clients must be updated
|
|
116
|
+
before migration; their explicit version-check override is outside this
|
|
117
|
+
compatibility guarantee.
|
|
118
|
+
|
|
119
|
+
The same read-only identity assessment runs before a Journal Fetch resets the
|
|
120
|
+
local database and before ordinary milestone updates. It does not publish a
|
|
121
|
+
node, create a Security Seed, or change a checkpoint. Rebuild first locks the
|
|
122
|
+
existing milestone for the rebuilding node, retaining an existing compatibility
|
|
123
|
+
range for that node and preserving the old identity evidence and Tweaks until
|
|
124
|
+
the journals are deleted. Deletion lists the keys once, retaining skipped control
|
|
125
|
+
document keys until all Journal pages have been processed. It then removes those
|
|
126
|
+
control documents, including the milestone and Security Seed, in finite batches.
|
|
127
|
+
A repeated or backwards listing cursor stops the reset before those control
|
|
128
|
+
documents are removed. A partial failure or interrupted request during Journal
|
|
129
|
+
deletion retains those control documents and the transfer checkpoint for a retry. Checkpoints reset only after control
|
|
130
|
+
document deletion also succeeds. A missing milestone stays absent during this
|
|
131
|
+
step. The replacement identity is declared after deletion succeeds, so a failed
|
|
132
|
+
or interrupted deletion cannot label the old journals with the new identity.
|
|
133
|
+
|
|
134
|
+
P2P uses its existing Tweak exchange and version document to check both peers
|
|
135
|
+
before ordinary data replication. Missing path settings mean version `0`.
|
|
136
|
+
Local Metadata without the matching feature requires Rebuild. A fresh database
|
|
137
|
+
with only control documents may declare the existing NFC feature. Connection
|
|
138
|
+
state is renewed when the selected identity changes. This adds no
|
|
139
|
+
authentication protocol or persistent admission store; all participating peers
|
|
140
|
+
must be updated and migrated.
|
|
141
|
+
|
|
142
|
+
Journal migration rebuilds the source local database and remote journals. P2P
|
|
143
|
+
Rebuild prepares only the source local database. Other devices replace their
|
|
144
|
+
local databases through Fetch. The existing local reset destroys Journal KV
|
|
145
|
+
checkpoints; the P2P source mark and target checkpoint handle database
|
|
146
|
+
replacement. These operations do not rewrite IDs or merge histories in place.
|
|
147
|
+
P2P retains its existing source selection during Fetch.
|
|
148
|
+
|
|
149
|
+
Direct file access refuses a remote which
|
|
89
150
|
declares the NFC feature. Other host adapters must establish physical path
|
|
90
151
|
resolution before offering this setting. The regression tests cover ASCII,
|
|
91
152
|
NFC, NFD, and mixed paths through in-memory PouchDB Metadata lookup, with and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vrtmrz/livesync-commonlib",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared data, storage, and replication primitives for Self-hosted LiveSync clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
"import": "./dist/platform/browser/index.js",
|
|
43
43
|
"default": "./dist/platform/browser/index.js"
|
|
44
44
|
},
|
|
45
|
+
"./conflicts": {
|
|
46
|
+
"types": "./dist/conflicts.d.ts",
|
|
47
|
+
"import": "./dist/conflicts.js",
|
|
48
|
+
"default": "./dist/conflicts.js"
|
|
49
|
+
},
|
|
45
50
|
"./context": {
|
|
46
51
|
"types": "./dist/context.d.ts",
|
|
47
52
|
"import": "./dist/context.js",
|
|
@@ -640,6 +645,9 @@
|
|
|
640
645
|
"browser": [
|
|
641
646
|
"dist/platform/browser/index.d.ts"
|
|
642
647
|
],
|
|
648
|
+
"conflicts": [
|
|
649
|
+
"dist/conflicts.d.ts"
|
|
650
|
+
],
|
|
643
651
|
"context": [
|
|
644
652
|
"dist/context.d.ts"
|
|
645
653
|
],
|
|
@@ -1011,8 +1019,8 @@
|
|
|
1011
1019
|
"fflate": "^0.8.2",
|
|
1012
1020
|
"idb": "^8.0.3",
|
|
1013
1021
|
"markdown-it": "^14.3.3",
|
|
1014
|
-
"minimatch": "^
|
|
1015
|
-
"octagonal-wheels": "
|
|
1022
|
+
"minimatch": "^11.0.3",
|
|
1023
|
+
"octagonal-wheels": "0.1.56",
|
|
1016
1024
|
"pouchdb-adapter-http": "^9.0.0",
|
|
1017
1025
|
"pouchdb-adapter-idb": "^9.0.0",
|
|
1018
1026
|
"pouchdb-adapter-indexeddb": "^9.0.0",
|