@vrtmrz/livesync-commonlib 0.1.0-rc.0 → 0.1.0-rc.2
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 +98 -16
- package/dist/common/i18n.js +7 -7099
- package/dist/common/messages/combinedMessages.prod.d.ts +3 -0
- package/dist/common/messages/combinedMessages.prod.js +3 -0
- package/dist/common/messages/def.d.ts +1 -0
- package/dist/common/messagesJson/en.json +1 -0
- package/dist/common/models/setting.const.defaults.d.ts +19 -0
- package/dist/common/models/setting.const.defaults.js +20 -2
- package/dist/common/models/setting.lifecycle.d.ts +31 -0
- package/dist/common/models/setting.lifecycle.js +109 -0
- package/dist/common/types.d.ts +4 -2
- package/dist/common/types.js +16 -1
- package/dist/context.d.ts +1 -0
- package/dist/interfaces/Confirm.d.ts +8 -1
- package/dist/interfaces/DatabaseRebuilder.d.ts +12 -2
- package/dist/platform/node/index.d.ts +1 -0
- package/dist/platform/node/index.js +4 -0
- package/dist/platform/node/standardIo.d.ts +24 -0
- package/dist/platform/node/standardIo.js +39 -0
- package/dist/platform/standardIo.d.ts +19 -0
- package/dist/platform/standardIo.js +0 -0
- package/dist/remoteConfigurations.d.ts +9 -0
- package/dist/remoteConfigurations.js +14 -0
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.d.ts +4 -1
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.js +41 -26
- package/dist/replication/trystero/P2PReplicatorCore.d.ts +2 -6
- package/dist/replication/trystero/P2PReplicatorCore.js +16 -86
- package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -1
- package/dist/replication/trystero/TrysteroReplicator.js +10 -4
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +4 -5
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +22 -30
- package/dist/replication/trystero/UseP2PReplicatorResult.d.ts +10 -2
- package/dist/replication/trystero/addP2PEventHandlers.d.ts +4 -2
- package/dist/replication/trystero/addP2PEventHandlers.js +8 -6
- package/dist/replication/trystero/types.d.ts +2 -0
- package/dist/replication/trystero/useP2PReplicatorCommands.d.ts +1 -1
- package/dist/replication/trystero/useP2PReplicatorCommands.js +3 -1
- package/dist/replication/trystero/useP2PReplicatorFeature.js +1 -1
- package/dist/rpc/RpcRoom.d.ts +1 -1
- package/dist/rpc/RpcRoom.js +22 -16
- package/dist/rpc/transports/TrysteroTransport.d.ts +2 -0
- package/dist/rpc/transports/TrysteroTransport.js +24 -17
- package/dist/rpc/transports/trysteroRoomEvents.d.ts +14 -0
- package/dist/rpc/transports/trysteroRoomEvents.js +34 -0
- package/dist/serviceFeatures/remoteConfig.d.ts +37 -1
- package/dist/serviceFeatures/remoteConfig.js +90 -0
- package/dist/serviceModules/Rebuilder.d.ts +3 -2
- package/dist/serviceModules/Rebuilder.js +21 -9
- package/dist/services/base/IService.d.ts +12 -1
- package/dist/services/base/ReplicationService.d.ts +3 -1
- package/dist/services/base/ReplicationService.js +6 -1
- package/dist/services/base/SettingService.d.ts +14 -1
- package/dist/services/base/SettingService.js +35 -12
- package/dist/services/implements/headless/HeadlessAPIService.d.ts +1 -1
- package/dist/services/implements/headless/HeadlessAPIService.js +1 -1
- package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +3 -3
- package/dist/settings.d.ts +10 -0
- package/dist/settings.js +32 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/development.md +77 -2
- package/docs/p2p-transport-lifecycle.md +52 -0
- package/docs/platform-standard-io.md +44 -0
- package/docs/proven-in-use.md +55 -0
- package/docs/releasing.md +9 -5
- package/docs/remote-configurations.md +63 -0
- package/docs/settings-lifecycle.md +60 -0
- package/package.json +12 -2
package/docs/development.md
CHANGED
|
@@ -2,10 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
This document is for Commonlib developers. Package consumers should begin with the root README and the focused platform guide.
|
|
4
4
|
|
|
5
|
+
## Repository and package boundary
|
|
6
|
+
|
|
7
|
+
The source tree is larger than the supported package surface. Consumers may import only paths in the generated package export map:
|
|
8
|
+
|
|
9
|
+
- `src/index.ts`, `src/context.ts`, `src/settings.ts`, `src/remoteConfigurations.ts`, `src/platform/browser/index.ts`, `src/platform/node/index.ts`, and `src/rpc/index.ts` define the focused entries;
|
|
10
|
+
- `_tools/build-package.mjs` compiles those entries and creates the publishable manifest under `.package`;
|
|
11
|
+
- `docs/migration/downstream-imports.json` is the reviewed inventory from which explicit `compat/*` exports are generated;
|
|
12
|
+
- colocated `*.unit.spec.ts` and `*.unit.test.ts` files exercise implementation and result contracts; and
|
|
13
|
+
- colocated `*.integration.spec.ts` files, `test/integration`, and the integration runner own checks against disposable CouchDB and S3-compatible services.
|
|
14
|
+
|
|
15
|
+
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.
|
|
16
|
+
|
|
5
17
|
## Local validation
|
|
6
18
|
|
|
19
|
+
For a complete local package gate, run:
|
|
20
|
+
|
|
7
21
|
```bash
|
|
8
22
|
npm ci
|
|
23
|
+
npm run verify:package
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`verify:package` type-checks the source, runs the unit suite and package-boundary tests, validates release selection, builds the compiled package, and checks the exact packed consumer boundary.
|
|
27
|
+
|
|
28
|
+
Run the managed integration suite as a separate gate when CouchDB streaming fetches, S3-compatible storage, or their dependencies could be affected:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run test:integration:managed
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
During focused work, the component scripts remain available independently:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
9
37
|
npm run check:types
|
|
10
38
|
npm test
|
|
11
39
|
npm run test:contracts
|
|
@@ -17,13 +45,50 @@ npm run test:package
|
|
|
17
45
|
|
|
18
46
|
`test:contracts` runs the host-neutral result contracts which are intended to be reusable while implementations move:
|
|
19
47
|
|
|
20
|
-
- `test:contract:context` verifies event delivery, default instance isolation, translation results, and host extension of `ServiceContextContract`;
|
|
21
|
-
- `test:contract:platform-storage` runs one result table against Node storage and browser File System Access storage, including missing paths, text and binary round trips, append, listing, removal, root handling, and traversal rejection
|
|
48
|
+
- `test:contract:context` verifies event delivery, default instance isolation, translation results, and host extension of `ServiceContextContract`;
|
|
49
|
+
- `test:contract:platform-storage` runs one result table against Node storage and browser File System Access storage, including missing paths, text and binary round trips, append, listing, removal, root handling, and traversal rejection; and
|
|
50
|
+
- `test:contract:standard-io` verifies the injectable contract, UTF-8 decoding across chunks, text and binary output, and line-oriented prompting through injected Node streams.
|
|
22
51
|
|
|
23
52
|
These scripts are deliberately available independently of CI. Add a focused result contract before moving another platform API; do not infer compatibility from matching TypeScript shapes alone. Platform-specific behaviour, such as timestamp fidelity, must remain documented outside the shared result set.
|
|
24
53
|
|
|
54
|
+
The settings lifecycle has an additional focused unit table in `src/common/models/setting.lifecycle.unit.spec.ts`. Extend it whenever a schema migration, a new-Vault recommendation, or downgrade handling changes. A stored setting must keep its explicit value unless the migration itself documents a deliberate transformation.
|
|
55
|
+
|
|
56
|
+
The multiple-remote profile contract has focused unit coverage in `src/serviceFeatures/remoteConfig.unit.spec.ts`. Extend it whenever profile identity, generated display names, activation, P2P selection, URI serialisation, or legacy migration changes. New hosts should import the reviewed primitives through `/remote-configurations`; `compat/serviceFeatures/remoteConfig` remains the wider migration surface used by existing clients.
|
|
57
|
+
|
|
58
|
+
### P2P composition ownership
|
|
59
|
+
|
|
60
|
+
`useP2PReplicatorFeature` is the sole owner of the active `LiveSyncTrysteroReplicator` and its lifecycle bindings. It creates or replaces the outer replicator when `ReplicatorService` requests one, closes the previous instance before replacement, and returns a stable result object whose `replicator` property resolves the current instance.
|
|
61
|
+
|
|
62
|
+
Consumers must preserve that result object and read `result.replicator` at the point of use. Do not destructure the property into a command, event handler, view, or other long-lived closure: a database reinitialisation or remote-type transition can close and replace the captured instance, and calling `open()` on that obsolete outer object can create a second connection outside the active service state.
|
|
63
|
+
|
|
64
|
+
The similarly named compositions have narrower roles:
|
|
65
|
+
|
|
66
|
+
- `useP2PReplicatorCommands` adds host commands and resolves the current instance when a command is checked or invoked;
|
|
67
|
+
- host UI features add views, status presentation, and injected peer-selection callbacks without owning the replicator lifecycle; and
|
|
68
|
+
- the deprecated `useP2PReplicator` entry is a compatibility composition which delegates ownership to `useP2PReplicatorFeature`.
|
|
69
|
+
|
|
70
|
+
When registering P2P event handlers for a replaceable instance, pass a provider to `addP2PEventHandlers`. Passing a fixed instance remains supported only for compositions whose instance cannot change. Extend the focused feature, command, event-hub, and compatibility-wrapper tests whenever this ownership boundary changes.
|
|
71
|
+
|
|
72
|
+
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.
|
|
73
|
+
|
|
25
74
|
Self-hosted LiveSync remains the principal downstream consumer. A Commonlib change is not ready for publication until the exact packed artefact has also passed the applicable LiveSync type checks, unit and integration tests, application builds, CLI E2E, and focused real-Obsidian E2E.
|
|
26
75
|
|
|
76
|
+
The package publishes the generated production language catalogue as an ordinary ESM module. It does not duplicate that catalogue inside `dist/common/i18n.js`; the final application bundler remains responsible for composing the catalogue for environments such as Obsidian. Source development continues to use the JSON-derived development catalogue.
|
|
77
|
+
|
|
78
|
+
Keep [the maintained-host evidence](proven-in-use.md) aligned with the downstream source and gates. If a host no longer consumes a focused entry, a test is retired, or a compatibility path receives a reviewed replacement, update the consumer-facing account as part of the same maintenance rather than leaving historical adoption as a current support claim.
|
|
79
|
+
|
|
80
|
+
## Integration tests
|
|
81
|
+
|
|
82
|
+
The integration suite owns Commonlib's direct CouchDB and S3-compatible storage checks. Run it with managed, disposable CouchDB and MinIO containers:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run test:integration:managed
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This command requires Docker Compose, creates only test credentials and data, and removes the containers and their volumes after the run. It is the same entry point used by package CI.
|
|
89
|
+
|
|
90
|
+
To test services that are already running, use `npm run test:integration`. The runner accepts `hostname`, `username`, and `password` for CouchDB, and `minioEndpoint`, `accessKey`, `secretKey`, and `bucketName` for S3-compatible storage. Without overrides, it uses the endpoints and test credentials from the managed Compose environment. The selected MinIO bucket must already exist when services are not managed by the runner.
|
|
91
|
+
|
|
27
92
|
## Translation catalogue
|
|
28
93
|
|
|
29
94
|
Human-edited translations live under `src/common/messagesYAML`. Regenerate derived JSON and TypeScript resources with:
|
|
@@ -42,6 +107,16 @@ Generated API reference is deferred until the compatibility export surface has b
|
|
|
42
107
|
|
|
43
108
|
TODO: introduce generated API reference for a reviewed allow-list of public entry points once the high-level client façade and its lifecycle contract have been accepted.
|
|
44
109
|
|
|
110
|
+
## Open 1.0 documentation decisions
|
|
111
|
+
|
|
112
|
+
Resolve these questions before describing the corresponding behaviour as stable:
|
|
113
|
+
|
|
114
|
+
- define the high-level client façade and the readiness, enumeration, watch, failure, conflict, concurrency, and disposal semantics currently left open by `DirectFileManipulator`;
|
|
115
|
+
- classify the selected Node built-in convenience exports as either supported platform contracts or migration-only compatibility exports;
|
|
116
|
+
- decide whether the RPC entry and its draft wire protocol belong to the Commonlib 1.0 contract, remain experimental, or move to a separately versioned package;
|
|
117
|
+
- classify compatibility paths whose owner or migration destination is still unclear, and remove obsolete paths rather than preserving the source layout; and
|
|
118
|
+
- introduce generated API reference only for the reviewed public allow-list, with TSDoc coverage for ownership, lifecycle, errors, platform constraints, and disposal.
|
|
119
|
+
|
|
45
120
|
## Compatibility exports
|
|
46
121
|
|
|
47
122
|
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.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# P2P transport lifecycle
|
|
2
|
+
|
|
3
|
+
This guide is for developers composing Commonlib's Trystero P2P service. It describes resource ownership and shutdown semantics; it is not a general Trystero API reference.
|
|
4
|
+
|
|
5
|
+
## Ownership layers
|
|
6
|
+
|
|
7
|
+
The P2P composition has three distinct owners:
|
|
8
|
+
|
|
9
|
+
- `useP2PReplicatorFeature` owns the current `LiveSyncTrysteroReplicator` exposed to a host;
|
|
10
|
+
- the Commonlib P2P host owns LiveSync RPC, advertisements, client proxies, diagnostics, and one Trystero room binding; and
|
|
11
|
+
- Trystero owns the underlying shared WebRTC peers and Nostr relay clients.
|
|
12
|
+
|
|
13
|
+
Consumers must retain the service-feature result and resolve `result.replicator` at the point of use. A settings or database lifecycle transition can replace the replicator. A command, event listener, or view which captured the former instance could otherwise reopen a transport which the current service no longer owns.
|
|
14
|
+
|
|
15
|
+
## Normal close
|
|
16
|
+
|
|
17
|
+
`LiveSyncTrysteroReplicator.close()` serialises the close with any in-flight open operation. It stops LiveSync-owned work, closes the RPC room, calls `room.leave()`, clears instance references, pauses relay reconnection, and closes the current Nostr relay WebSockets.
|
|
18
|
+
|
|
19
|
+
Normal close deliberately does not call `close()` on the `RTCPeerConnection` values returned by `room.getPeers()`. Trystero 0.25 may share a physical peer across rooms. Leaving a room detaches its callbacks and action namespace, while Trystero may retain the idle physical peer for approximately 123 seconds for reuse. The retained peer cannot carry traffic for the departed room.
|
|
20
|
+
|
|
21
|
+
Likewise, an incoming peer-leave notification removes LiveSync advertisement and client state without directly closing the underlying peer. Trystero decides whether that peer is shared, stale, or ready for destruction.
|
|
22
|
+
|
|
23
|
+
The host command named 'Disconnect from the Signalling Server' means:
|
|
24
|
+
|
|
25
|
+
- the LiveSync P2P and RPC service stops immediately;
|
|
26
|
+
- room membership and room-scoped actions are removed;
|
|
27
|
+
- relay WebSockets close and automatic reconnection is paused; and
|
|
28
|
+
- an idle WebRTC object may remain under Trystero ownership until its reuse interval ends.
|
|
29
|
+
|
|
30
|
+
It is therefore a logical LiveSync disconnection and a physical signalling-server disconnection, not a synchronous destruction promise for every browser-owned WebRTC object.
|
|
31
|
+
|
|
32
|
+
## Reconnection and replacement
|
|
33
|
+
|
|
34
|
+
An explicit open resumes Trystero relay reconnection before joining the configured room. Settings application may change the relay URLs, room ID, passphrase, or TURN configuration, so the owning service closes and discards the former LiveSync instance before constructing the replacement. No fixed close-to-open delay is required: lifecycle operations are serialised, and peer readiness is observed through discovery.
|
|
35
|
+
|
|
36
|
+
Do not replace a relay socket's `onclose` handler. Trystero 0.25 shares relay clients by URL and uses its own handler to retire and recreate them. Use the exported pause and resume functions around explicit disconnection instead.
|
|
37
|
+
|
|
38
|
+
## Unsupported forced close
|
|
39
|
+
|
|
40
|
+
Do not close the raw values returned by `room.getPeers()`. Both closing them before room departure and closing captured values after departure have prevented the same peer from being rediscovered within 60 seconds in the real-transport replacement test.
|
|
41
|
+
|
|
42
|
+
Commonlib consequently does not expose a forced physical-disconnection command. Such an operation requires a future Trystero API which removes and destroys peers through its shared-peer manager, followed by proof that immediate reconnection remains possible.
|
|
43
|
+
|
|
44
|
+
## Verification
|
|
45
|
+
|
|
46
|
+
Maintain all three boundaries when this lifecycle changes:
|
|
47
|
+
|
|
48
|
+
- Commonlib unit tests must prove that normal close leaves the room without directly closing Trystero-owned peers, and that overlapping open and close requests leave one current owner;
|
|
49
|
+
- the Self-hosted LiveSync Compose P2P lifecycle test must replace a current replicator, rediscover the same real peer, perform bidirectional RPC, and verify transferred content from a separate process; and
|
|
50
|
+
- the relay-disconnect test must observe the original WebSocket reach `CLOSED`, remain closed while reconnection is paused, and be replaced after reconnection resumes.
|
|
51
|
+
|
|
52
|
+
The corresponding product decision and rejected alternatives are recorded in Self-hosted LiveSync's P2P room and transport lifecycle ADR.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Standard input and output
|
|
2
|
+
|
|
3
|
+
Commonlib defines a narrow `StandardIo` contract for command-line hosts. The contract covers standard input, line-oriented prompting, standard output, and standard error. It does not own process arguments, exit codes, signals, logging levels, or log persistence.
|
|
4
|
+
|
|
5
|
+
## Host composition
|
|
6
|
+
|
|
7
|
+
The host chooses and constructs the implementation, then places it on its host-specific Context or passes it to the command composition which owns it. The base `ServiceContext` does not require standard I/O because Obsidian, browser, and headless service compositions do not all have terminal streams.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import type { StandardIo } from '@vrtmrz/livesync-commonlib/context';
|
|
11
|
+
import { createNodeStandardIo } from '@vrtmrz/livesync-commonlib/node';
|
|
12
|
+
|
|
13
|
+
class CliContext {
|
|
14
|
+
constructor(readonly standardIo: StandardIo) {}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const context = new CliContext(createNodeStandardIo());
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`createNodeStandardIo()` binds to `process.stdin`, `process.stdout`, and `process.stderr` by default. An embedding host may supply alternative Node streams. The host remains responsible for those streams and their lifecycle.
|
|
21
|
+
|
|
22
|
+
## Output contract
|
|
23
|
+
|
|
24
|
+
`writeStdout()` and `writeStderr()` accept text or `Uint8Array` chunks and do not add a newline. Commands must add delimiters required by their own text protocol. Binary command output must use `writeStdout()` directly without conversion through a logger.
|
|
25
|
+
|
|
26
|
+
Diagnostic logging remains a separate capability. A host can render selected logs to standard error, but Commonlib does not make a Logger part of `StandardIo` and does not interpret log levels in this adapter.
|
|
27
|
+
|
|
28
|
+
## Tests and embedded hosts
|
|
29
|
+
|
|
30
|
+
Consumers can inject a small memory implementation without replacing process globals:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import type { StandardIo, StandardIoChunk } from '@vrtmrz/livesync-commonlib/context';
|
|
34
|
+
|
|
35
|
+
const stdout: StandardIoChunk[] = [];
|
|
36
|
+
const io: StandardIo = {
|
|
37
|
+
readStdin: async () => 'input',
|
|
38
|
+
prompt: async () => 'answer',
|
|
39
|
+
writeStdout: (chunk) => stdout.push(chunk),
|
|
40
|
+
writeStderr: () => undefined,
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This boundary lets a command test assert exact protocol bytes, prompts, and errors while the real Node adapter is verified separately against injected streams.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Proven in maintained hosts
|
|
2
|
+
|
|
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
|
+
|
|
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 and CLI. The Webapp has focused adapter tests, and both the Webapp and WebPeer are type-checked and built, but they do not have an equally current end-to-end gate.
|
|
6
|
+
|
|
7
|
+
The source links below identify the exact reviewed Self-hosted LiveSync revision used to establish this package boundary. The integration descriptions must still be reviewed when the maintained downstream changes.
|
|
8
|
+
|
|
9
|
+
## Obsidian plug-in
|
|
10
|
+
|
|
11
|
+
The plug-in extends the neutral `ServiceContext` with Obsidian-owned capabilities in [`ObsidianServiceContext.ts`](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/src/modules/services/ObsidianServiceContext.ts). One context instance carries the host translator and event hub through the Service Hub rather than placing those dependencies in Commonlib globals.
|
|
12
|
+
|
|
13
|
+
The consumer's [service-context contract](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/test/contracts/serviceContext.ts) checks shared result semantics and exact context identity across the composed services. The [real-Obsidian suite](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/test/e2e-obsidian/README.md) then covers plug-in loading, representative Svelte dialogue mounts, Vault reflection, CouchDB and Object Storage synchronisation, two-Vault behaviour, and other Obsidian-owned boundaries.
|
|
14
|
+
|
|
15
|
+
Most replication and storage services used by the plug-in still enter through explicit `compat/*` paths. Those imports prove that the published compatibility boundary supports the current migration; they are not examples of a finished high-level client API.
|
|
16
|
+
|
|
17
|
+
The plug-in's manual onboarding and Remote Databases pane use the focused `/remote-configurations` entry to create opaque profile IDs, retain user-visible names, and select main and P2P remotes. Commonlib owns the in-memory profile result contract; Self-hosted LiveSync adds dialogue, Setup URI classification, Fetch or Rebuild scheduling, persistence, restart ordering, and real-Obsidian presentation checks.
|
|
18
|
+
|
|
19
|
+
## CLI
|
|
20
|
+
|
|
21
|
+
The CLI extends the same neutral context with its database path and injected `StandardIo` in [`NodeServiceContext.ts`](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/src/apps/cli/services/NodeServiceContext.ts). Its entry point creates the Node standard-I/O adapter once and passes it to command composition, while unit tests provide stream-independent fakes for parsing, prompts, standard output, and standard error.
|
|
22
|
+
|
|
23
|
+
The CLI filesystem composition receives a configured base directory and constructs the rooted Node storage adapter in [`NodeFileSystemAdapter.ts`](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/src/apps/cli/adapters/NodeFileSystemAdapter.ts). Commonlib's shared storage result table checks the focused root contract; the consumer adds its file cache, Vault-shaped operations, case handling, and command policy.
|
|
24
|
+
|
|
25
|
+
The maintained CLI checks include unit tests, Deno-driven CLI E2E, Compose-based compatibility runs, and the [CLI-to-real-Obsidian synchronisation scenario](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/test/e2e-obsidian/scripts/cli-to-obsidian-sync.ts). The latter is the strongest current evidence that the CLI and plug-in compose compatible Commonlib behaviour across two hosts.
|
|
26
|
+
|
|
27
|
+
## Webapp
|
|
28
|
+
|
|
29
|
+
The Webapp receives an already authorised `FileSystemDirectoryHandle` from its host and constructs `FileSystemAccessStorageAdapter` inside [`FSAPIFileSystemAdapter.ts`](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/src/apps/webapp/adapters/FSAPIFileSystemAdapter.ts). The adapter does not call `showDirectoryPicker()`, choose a directory, or persist the handle. This is the maintained application example for the browser entry's root-ownership rule.
|
|
30
|
+
|
|
31
|
+
The browser and Node storage implementations run through the same Commonlib result contract, and the Webapp adapter has focused unit coverage. The Webapp is also type-checked and built by downstream verification. Its browser E2E has not been kept at the same level as the Obsidian and CLI suites, so the documentation does not treat Webapp integration as equivalent real-runtime evidence.
|
|
32
|
+
|
|
33
|
+
## WebPeer and browser service composition
|
|
34
|
+
|
|
35
|
+
The browser service composition in [`createLiveSyncBrowserServiceHub.ts`](https://github.com/vrtmrz/obsidian-livesync/blob/6cc06bf20fc044c70408ef3d92fe0bbc0874479d/src/apps/browser/createLiveSyncBrowserServiceHub.ts) supplies browser-owned API and UI services around a caller-supplied context. WebPeer and the browser applications exercise this compatibility surface through type checks and builds.
|
|
36
|
+
|
|
37
|
+
The Obsidian plug-in, CLI, and Webapp compose P2P ownership through `useP2PReplicatorFeature`. Commands and Obsidian views retain its live result object and resolve the current replicator when an action occurs. WebPeer still enters through the deprecated combined compatibility function, which now delegates the same ownership and lifecycle to the focused feature instead of maintaining a second implementation. Downstream Deno and Compose P2P E2E cover peer discovery and synchronisation; the Webapp and WebPeer UI surfaces themselves remain build- and type-check evidence rather than equivalent real-runtime gates.
|
|
38
|
+
|
|
39
|
+
This is evidence that the current package boundary can be consumed without the Obsidian host, but it is not yet a recommended standalone Commonlib client factory. The high-level lifecycle, readiness, error, and disposal contract remains a documented 1.0 decision.
|
|
40
|
+
|
|
41
|
+
## What a new consumer should copy
|
|
42
|
+
|
|
43
|
+
A new integration should start with the focused entries, not by reproducing Self-hosted LiveSync's compatibility imports:
|
|
44
|
+
|
|
45
|
+
- create one `ServiceContext` per independently composed client, inject its translator and events, and extend it only with capabilities owned by that host;
|
|
46
|
+
- let the host select and authorise a storage root, then pass the root once to the Node or browser storage factory;
|
|
47
|
+
- inject `StandardIo` into CLI policy so that protocol output and prompts remain testable; and
|
|
48
|
+
- use `/remote-configurations` when a host needs to create or select saved remote profiles, while keeping naming prompts, persistence, and restart policy in the host; and
|
|
49
|
+
- run the relevant Commonlib result contract against another implementation before describing matching TypeScript shapes as compatible behaviour.
|
|
50
|
+
|
|
51
|
+
Use the linked consumer code as an integration example, while retaining the ownership and limitations stated in the [root README](https://github.com/vrtmrz/livesync-commonlib/blob/main/readme.md), [storage guide](platform-storage.md), [standard-I/O guide](platform-standard-io.md), and [remote configuration profile guide](remote-configurations.md).
|
|
52
|
+
|
|
53
|
+
## Maintenance rule
|
|
54
|
+
|
|
55
|
+
This page records current evidence rather than historical adoption. When a host stops using an entry point, a verification path is retired, or a compatibility import moves to a focused contract, update the description and links instead of retaining a stronger claim than the maintained consumer establishes.
|
package/docs/releasing.md
CHANGED
|
@@ -19,7 +19,7 @@ Before publication, run the downstream workflow against an exact Self-hosted Liv
|
|
|
19
19
|
|
|
20
20
|
Choose the version explicitly. Use a prerelease such as `0.1.0-rc.0` when registry installation must be validated before the first stable version. Package-proof versions are local artefacts and cannot be staged.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
For a stable release, prepare the version on a release branch based on `main`, review it, and merge the exact release commit into `main` before dispatch. For a pre-release which must be validated before its feature pull request is merged, prepare the version commit on that reviewed draft branch and keep the pull request in draft:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
npm ci
|
|
@@ -53,17 +53,21 @@ After bootstrap, configure the npm Trusted Publisher for:
|
|
|
53
53
|
- environment: `npm`; and
|
|
54
54
|
- allowed action: staged publishing only.
|
|
55
55
|
|
|
56
|
-
Protect the GitHub `npm` environment with a required reviewer and permit only
|
|
56
|
+
Protect the GitHub `npm` environment with a required reviewer and permit only `main`. The trusted workflow definition always runs from `main`, but a pre-release may package an exact commit from a reviewed draft branch. The workflow confirms that the named source branch still points to the requested full commit SHA, permits stable versions only when the source branch is `main`, runs `verify:package`, validates the source ref, requested version, full commit SHA, and confirmation text, packs that reviewed output, records its checksum, and passes the same tarball to the protected staging job.
|
|
57
57
|
|
|
58
|
-
Dispatch
|
|
58
|
+
Dispatch a pre-release from its reviewed draft branch with the exact version and full commit SHA:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
|
|
61
|
+
source_ref=separate-setting-lifecycle
|
|
62
|
+
sha=$(git rev-parse "origin/$source_ref")
|
|
62
63
|
gh workflow run publish-npm.yml \
|
|
63
64
|
--ref main \
|
|
64
65
|
-f version=<version> \
|
|
66
|
+
-f source_ref="$source_ref" \
|
|
65
67
|
-f expected_sha="$sha" \
|
|
66
68
|
-f confirmation="stage @vrtmrz/livesync-commonlib@<version> from $sha"
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
For a stable version, use `source_ref=main`; the workflow rejects a stable version selected from any other source branch. The workflow dispatch itself still uses `--ref main` in both cases so that an unmerged branch cannot change the trusted publication job or acquire access to the `npm` environment.
|
|
72
|
+
|
|
73
|
+
The workflow always stages to `next`. Inspect the staged package name, version, access, dist-tag, provenance, checksum, files, source branch, and source commit before approving it through npm. Approval and later promotion to `latest` are separate user-authorised operations. Keep a pre-release pull request in draft while validating the exact registry version in Self-hosted LiveSync, then merge the validated change if it succeeds. Validate a stable release in Self-hosted LiveSync before promoting it to `latest`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Remote configuration profiles
|
|
2
|
+
|
|
3
|
+
Commonlib represents saved remote connections as independently named profiles. The profile map is the persisted source for multiple-remote selection. Existing CouchDB, Object Storage, and P2P fields remain the runtime projection consumed by compatibility services.
|
|
4
|
+
|
|
5
|
+
Use the focused entry point for new profile-management code:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
upsertRemoteConfigurationInPlace,
|
|
10
|
+
type RemoteConfiguration,
|
|
11
|
+
} from "@vrtmrz/livesync-commonlib/remote-configurations";
|
|
12
|
+
import { createNewVaultSettings } from "@vrtmrz/livesync-commonlib/settings";
|
|
13
|
+
|
|
14
|
+
const settings = createNewVaultSettings();
|
|
15
|
+
Object.assign(settings, {
|
|
16
|
+
couchDB_URI: "https://couch.example",
|
|
17
|
+
couchDB_USER: "alice",
|
|
18
|
+
couchDB_PASSWORD: "secret",
|
|
19
|
+
couchDB_DBNAME: "notes",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const profile: RemoteConfiguration = upsertRemoteConfigurationInPlace(settings, "couchdb", {
|
|
23
|
+
activate: true,
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The helper serialises the connection fields into the profile URI. When `activate` is true, it selects that profile through `activeConfigurationId` and projects the profile back onto the compatibility fields. The host still owns persistence, restart policy, credential acquisition, and user confirmation.
|
|
28
|
+
|
|
29
|
+
## Identity and display names
|
|
30
|
+
|
|
31
|
+
Profile IDs are opaque identity. Omit `id` to allocate a new one and preserve every existing profile. Pass a known `id` only when deliberately updating that profile.
|
|
32
|
+
|
|
33
|
+
Display names are presentation, not identity. When `name` is omitted, Commonlib proposes a concise type-specific name such as `CouchDB couch.example`, `S3 notes`, or `P2P team-room`. It adds a numeric suffix when that display name is already present. A host may let users rename profiles without changing their IDs or selections.
|
|
34
|
+
|
|
35
|
+
There is no special profile named or identified as `default`. The selected main remote is represented only by `activeConfigurationId`.
|
|
36
|
+
|
|
37
|
+
## Main and P2P selections
|
|
38
|
+
|
|
39
|
+
`activeConfigurationId` selects the main remote used by ordinary replication. `P2P_ActiveRemoteConfigurationId` independently selects the profile used by P2P features.
|
|
40
|
+
|
|
41
|
+
- Use `{ activate: true }` for a CouchDB or Object Storage profile selected as the main remote.
|
|
42
|
+
- Use `{ activate: true, activateForP2P: true }` when P2P is the main remote.
|
|
43
|
+
- Use `{ activateForP2P: true }` to select a P2P profile without changing the main remote.
|
|
44
|
+
|
|
45
|
+
Selecting a profile updates the compatibility fields immediately. Existing replication services may therefore continue to consume those fields while profile-aware hosts treat the profile map and selection IDs as authoritative persisted state.
|
|
46
|
+
|
|
47
|
+
## Import and migration boundary
|
|
48
|
+
|
|
49
|
+
A modern Setup URI or another settings transport should preserve `remoteConfigurations`, profile IDs, display names, `activeConfigurationId`, and `P2P_ActiveRemoteConfigurationId` exactly.
|
|
50
|
+
|
|
51
|
+
An older settings payload may contain only the flat remote fields. The standard `SettingService` recognises that shape when the profile map is empty and migrates the configured connections into explicitly labelled `legacy-*` profiles. This is a compatibility path, not the recommended way to create a new profile. New setup flows should call the focused API directly.
|
|
52
|
+
|
|
53
|
+
If a settings payload already contains profiles, legacy migration does not create or replace any entry. A host which writes only flat fields in that state would therefore fail to register a new connection; profile-aware setup code must upsert the profile explicitly.
|
|
54
|
+
|
|
55
|
+
## Persistence and encryption
|
|
56
|
+
|
|
57
|
+
The helper stores a plaintext connection URI in memory and marks the changed profile as unencrypted. The standard `SettingService` applies configured at-rest encryption when persisting settings. A host using a different persistence service must provide equivalent encryption and decryption policy itself.
|
|
58
|
+
|
|
59
|
+
## Verification ownership
|
|
60
|
+
|
|
61
|
+
Commonlib unit tests cover preserving existing profiles, generated-name collision handling, main-remote activation, and independent P2P selection. The packed-package test imports the focused entry from a clean consumer and checks its declarations and runtime exports.
|
|
62
|
+
|
|
63
|
+
Hosts remain responsible for testing their dialogues, import classification, confirmation, Fetch or Rebuild scheduling, persistence, restart ordering, and real-runtime presentation.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Settings lifecycle
|
|
2
|
+
|
|
3
|
+
Commonlib separates three values which older integrations commonly treated as one object:
|
|
4
|
+
|
|
5
|
+
- `SETTINGS_SCHEMA_DEFAULTS` completes keys which are absent from stored settings. Its values remain conservative so that an upgrade does not silently change an existing choice.
|
|
6
|
+
- `NEW_VAULT_SETTINGS` initialises a store which has never contained Self-hosted LiveSync settings. It includes the current recommended base values.
|
|
7
|
+
- the `PREFERRED_SETTING_*` objects add remote-specific recommendations when a user explicitly selects Cloudant, self-hosted CouchDB, or Object Storage during setup.
|
|
8
|
+
|
|
9
|
+
`DEFAULT_SETTINGS` remains an alias of `SETTINGS_SCHEMA_DEFAULTS` for compatibility. New code should choose the named contract which matches its operation.
|
|
10
|
+
|
|
11
|
+
## Starting a new Vault
|
|
12
|
+
|
|
13
|
+
Use `createNewVaultSettings()` only when the settings store is absent or empty, or when the user explicitly requests a factory reset. `NEW_VAULT_SETTINGS` exposes the values for inspection, while the factory prevents mutable nested settings from being shared between clients:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createNewVaultSettings } from "@vrtmrz/livesync-commonlib/settings";
|
|
17
|
+
|
|
18
|
+
const initialSettings = createNewVaultSettings();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The recommended base currently selects a 50 MB maximum synchronised file size, Rabin–Karp chunk splitting, fixed chunk revisions, Plug-in Sync V2, case-insensitive file-name handling, and E2EE V2. It does not enable synchronisation or encryption on behalf of the user.
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
## Loading existing settings
|
|
26
|
+
|
|
27
|
+
`prepareSettingsForLoad` detects a blank store, completes stored settings with conservative schema fallbacks, runs ordered schema migrations, and reports the result separately from the settings value:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { prepareSettingsForLoad } from "@vrtmrz/livesync-commonlib/settings";
|
|
31
|
+
|
|
32
|
+
const prepared = prepareSettingsForLoad(await host.loadSettings());
|
|
33
|
+
const settings = prepared.settings;
|
|
34
|
+
|
|
35
|
+
if (prepared.changed && !prepared.isFromFutureSchema) {
|
|
36
|
+
await host.saveSettings(settings);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (prepared.requiresSyncReview) {
|
|
40
|
+
host.requestLocalSyncReview(prepared.reviewReasons);
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
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.
|
|
45
|
+
|
|
46
|
+
The standard Commonlib `SettingService` performs this preparation automatically. Hosts which use that service can inspect `getSettingsMigrationState()` after `loadSettings()` instead of calling the function themselves.
|
|
47
|
+
|
|
48
|
+
## Review and downgrade boundary
|
|
49
|
+
|
|
50
|
+
A migration may report `requiresSyncReview` when automatic synchronisation should wait for a human decision. Commonlib reports that requirement but does not disable synchronisation settings, persist an acknowledgement, display a dialogue, or decide whether replication may proceed. Those are host-local responsibilities because one device must not acknowledge a safety review for another device through synchronised settings.
|
|
51
|
+
|
|
52
|
+
Settings written by a schema newer than the running Commonlib version are completed for in-memory compatibility but are not assigned an older schema version. The result has `isFromFutureSchema: true`, `changed: false`, and a review reason. A host must not persist that completed object as a downgrade migration.
|
|
53
|
+
|
|
54
|
+
The settings schema version is independent of the package version and of any remote data or protocol compatibility version. Increment it only when the stored settings shape requires an ordered migration.
|
|
55
|
+
|
|
56
|
+
## Tests owned by Commonlib
|
|
57
|
+
|
|
58
|
+
The settings lifecycle unit tests cover blank stores, representative legacy choices, migration idempotence, legacy review state, future-schema downgrade protection, and the distinction between new-Vault recommendations and stored-setting fallbacks. The packed-package test imports the focused `/settings` entry from a clean consumer and checks its declarations and runtime exports.
|
|
59
|
+
|
|
60
|
+
Hosts remain responsible for testing when setup is classified as new, how local review acknowledgement is stored, how replication is gated, and how their settings interface presents the result.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vrtmrz/livesync-commonlib",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.2",
|
|
4
4
|
"description": "Shared data, storage, and replication primitives for Self-hosted LiveSync clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,6 +56,16 @@
|
|
|
56
56
|
"import": "./dist/rpc/index.js",
|
|
57
57
|
"default": "./dist/rpc/index.js"
|
|
58
58
|
},
|
|
59
|
+
"./remote-configurations": {
|
|
60
|
+
"types": "./dist/remoteConfigurations.d.ts",
|
|
61
|
+
"import": "./dist/remoteConfigurations.js",
|
|
62
|
+
"default": "./dist/remoteConfigurations.js"
|
|
63
|
+
},
|
|
64
|
+
"./settings": {
|
|
65
|
+
"types": "./dist/settings.d.ts",
|
|
66
|
+
"import": "./dist/settings.js",
|
|
67
|
+
"default": "./dist/settings.js"
|
|
68
|
+
},
|
|
59
69
|
"./compat/API/processSetting": {
|
|
60
70
|
"types": "./dist/API/processSetting.d.ts",
|
|
61
71
|
"import": "./dist/API/processSetting.js",
|
|
@@ -656,7 +666,7 @@
|
|
|
656
666
|
"@smithy/middleware-apply-body-checksum": "^4.3.9",
|
|
657
667
|
"@smithy/types": "^4.14.3",
|
|
658
668
|
"@smithy/util-retry": "^4.4.5",
|
|
659
|
-
"@trystero-p2p/nostr": "
|
|
669
|
+
"@trystero-p2p/nostr": "0.25.3",
|
|
660
670
|
"diff-match-patch": "^1.0.5",
|
|
661
671
|
"events": "^3.3.0",
|
|
662
672
|
"fflate": "^0.8.2",
|