@vrtmrz/livesync-commonlib 0.1.26 → 0.1.27

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.
@@ -131,12 +131,16 @@ export type ReplicationFailed = {
131
131
  export type ReplicationOutcome = ReplicationCompleted | ReplicationCancelled | ReplicationBlocked | ReplicationPartial | ReplicationFailed;
132
132
  /** Preparation required before an ordinary operation may use a provider. */
133
133
  export interface ReplicationReadinessRequirements {
134
- readonly centralRemotePreparation: "required" | "not-applicable";
134
+ readonly centralRemotePreparation: "required" | "provider-owned" | "not-applicable";
135
135
  }
136
136
  /** Shared readiness contract for providers backed by the central remote. */
137
137
  export declare const CENTRAL_REMOTE_REPLICATION_READINESS: Readonly<{
138
138
  readonly centralRemotePreparation: "required";
139
139
  }>;
140
+ /** Central provider which prepares its Security Seed within each transfer. */
141
+ export declare const PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS: Readonly<{
142
+ readonly centralRemotePreparation: "provider-owned";
143
+ }>;
140
144
  /** Shared readiness contract for peer-to-peer providers without a central remote. */
141
145
  export declare const PEER_REPLICATION_READINESS: Readonly<{
142
146
  readonly centralRemotePreparation: "not-applicable";
@@ -25,6 +25,9 @@ const REPLICATION_CANCELLED = Object.freeze({ status: "cancelled" });
25
25
  const CENTRAL_REMOTE_REPLICATION_READINESS = Object.freeze({
26
26
  centralRemotePreparation: "required"
27
27
  });
28
+ const PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS = Object.freeze({
29
+ centralRemotePreparation: "provider-owned"
30
+ });
28
31
  const PEER_REPLICATION_READINESS = Object.freeze({
29
32
  centralRemotePreparation: "not-applicable"
30
33
  });
@@ -131,6 +134,7 @@ export {
131
134
  CENTRAL_REMOTE_REPLICATION_READINESS,
132
135
  NO_INTERACTION,
133
136
  PEER_REPLICATION_READINESS,
137
+ PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS,
134
138
  REPLICATION_CANCELLED,
135
139
  REPLICATION_COMPLETED,
136
140
  REPLICATION_PROGRESS_PRESENTATIONS,
@@ -32,4 +32,6 @@ export declare class SyncParamsNotFoundError extends SyncParamsHandlerError {
32
32
  }
33
33
  export declare class SyncParamsUpdateError extends SyncParamsHandlerError {
34
34
  }
35
+ /** Create a handler owned by one operation rather than the process-wide server cache. */
36
+ export declare function createSyncParamsHandler({ put, get, create }: CreateSyncParamsHanderOptions): SyncParamsHandler;
35
37
  export {};
@@ -114,5 +114,6 @@ export {
114
114
  SyncParamsNotFoundError,
115
115
  SyncParamsUpdateError,
116
116
  clearHandlers,
117
- createSyncParamsHanderForServer
117
+ createSyncParamsHanderForServer,
118
+ createSyncParamsHandler
118
119
  };
@@ -1,4 +1,4 @@
1
- export { CAPABILITY_NOT_APPLICABLE, CAPABILITY_NOT_IMPLEMENTED, CENTRAL_REMOTE_REPLICATION_READINESS, NO_INTERACTION, PEER_REPLICATION_READINESS, REPLICATION_CANCELLED, REPLICATION_COMPLETED, REPLICATION_PROGRESS_PRESENTATIONS, USER_INITIATED_REPLICATION_AUTHORITY, defineReplicatorProviderDefinitions, isReplicationCompleted, outcomeFromContinuousOpenReplication, outcomeFromFiniteOpenReplication, replicationBlocked, replicationFailed, supportedOpenReplicationContinuous, supportedOpenReplicationOneShot, supportedOpenReplicationUnattended, supportedStopActiveTransfer, supportedCapability, } from "./ReplicatorProvider.js";
1
+ export { CAPABILITY_NOT_APPLICABLE, CAPABILITY_NOT_IMPLEMENTED, CENTRAL_REMOTE_REPLICATION_READINESS, NO_INTERACTION, PEER_REPLICATION_READINESS, PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS, REPLICATION_CANCELLED, REPLICATION_COMPLETED, REPLICATION_PROGRESS_PRESENTATIONS, USER_INITIATED_REPLICATION_AUTHORITY, defineReplicatorProviderDefinitions, isReplicationCompleted, outcomeFromContinuousOpenReplication, outcomeFromFiniteOpenReplication, replicationBlocked, replicationFailed, supportedOpenReplicationContinuous, supportedOpenReplicationOneShot, supportedOpenReplicationUnattended, supportedStopActiveTransfer, supportedCapability, } from "./ReplicatorProvider.js";
2
2
  export type { ReplicatorInstance } from "./ReplicatorInstance.js";
3
3
  export type { ActiveReplicatorContext, CapabilitySupport, ContinuousReplicationRequest, ContinuousReplicationTrigger, ContinuousRunner, InteractionAuthority, InteractionPermissions, NotApplicableCapability, NotImplementedCapability, NoInteraction, PermittedInteractionPermissions, ReplicationBlockReason, ReplicationAttemptFailure, ReplicationCancelled, ReplicationCompleted, ReplicationFailed, ReplicationInteraction, ReplicationOutcome, ReplicationProgressPresentation, ReplicationReadinessRequirements, ReplicationPartial, ReplicationFailureRequest, ReplicatorConfigurationIdentity, ReplicatorProviderDefinition, ReplicatorProviderDefinitionMap, StopActiveTransferRunner, SupportedCapability, UnattendedOneShotRequest, UnattendedReplicationTrigger, UnattendedOneShotRunner, UserInitiatedOneShotRequest, UserInitiatedReplicationAuthority, UserInitiatedOneShotRunner, } from "./ReplicatorProvider.js";
4
4
  export { CAPABILITY_SUPPORT_KINDS, CAPABILITY_UNAVAILABLE_REASONS } from "./ProviderCapability.js";
@@ -4,6 +4,7 @@ import {
4
4
  CENTRAL_REMOTE_REPLICATION_READINESS,
5
5
  NO_INTERACTION,
6
6
  PEER_REPLICATION_READINESS,
7
+ PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS,
7
8
  REPLICATION_CANCELLED,
8
9
  REPLICATION_COMPLETED,
9
10
  REPLICATION_PROGRESS_PRESENTATIONS,
@@ -48,6 +49,7 @@ export {
48
49
  NO_INTERACTION,
49
50
  NO_REMOTE_RESOURCE_CAPABILITIES,
50
51
  PEER_REPLICATION_READINESS,
52
+ PROVIDER_OWNED_CENTRAL_REMOTE_REPLICATION_READINESS,
51
53
  REMOTE_RESOURCE_KINDS,
52
54
  REPLICATION_CANCELLED,
53
55
  REPLICATION_COMPLETED,
@@ -16,6 +16,7 @@ export declare class JournalSyncCore {
16
16
  env: LiveSyncJournalReplicatorEnv;
17
17
  store: SimpleStore<CheckPointInfo>;
18
18
  requestedStop: boolean;
19
+ private syncParamsHandler?;
19
20
  getInitialSyncParameters(): Promise<SyncParameters>;
20
21
  getSyncParameters(): Promise<SyncParameters>;
21
22
  putSyncParameters(params: SyncParameters): Promise<boolean>;
@@ -44,6 +45,7 @@ export declare class JournalSyncCore {
44
45
  resetBucket(): Promise<boolean>;
45
46
  getRemoteKey(): string;
46
47
  getReplicationPBKDF2Salt(refresh?: boolean): Promise<Uint8Array<ArrayBuffer>>;
48
+ private getSyncParamsHandler;
47
49
  isEncryptionPrevented(fileName: string): boolean;
48
50
  private decryptDataV2;
49
51
  private decryptDataV1;
@@ -22,8 +22,7 @@ import {
22
22
  JournalStorageReadStatuses
23
23
  } from "./objectstore/JournalStorageAdapter.js";
24
24
  import {
25
- clearHandlers,
26
- createSyncParamsHanderForServer,
25
+ createSyncParamsHandler,
27
26
  SyncParamsFetchError,
28
27
  SyncParamsNotFoundError,
29
28
  SyncParamsUpdateError
@@ -61,6 +60,7 @@ class JournalSyncCore {
61
60
  env;
62
61
  store;
63
62
  requestedStop = false;
63
+ syncParamsHandler;
64
64
  getInitialSyncParameters() {
65
65
  return Promise.resolve({
66
66
  ...DEFAULT_SYNC_PARAMETERS,
@@ -109,7 +109,6 @@ class JournalSyncCore {
109
109
  this.store = store;
110
110
  this.hash = this.getHash(settings);
111
111
  this.storage = storage;
112
- clearHandlers();
113
112
  }
114
113
  async downloadJson(key) {
115
114
  try {
@@ -156,7 +155,7 @@ class JournalSyncCore {
156
155
  this.store = store;
157
156
  this.hash = this.getHash(settings);
158
157
  this.storage.applyNewConfig(settings);
159
- clearHandlers();
158
+ this.resetAllCaches();
160
159
  }
161
160
  updateInfo(info) {
162
161
  const old = this.env.services.replicator.replicationStatics.value;
@@ -205,23 +204,18 @@ class JournalSyncCore {
205
204
  return this._currentCheckPointInfo;
206
205
  }
207
206
  resetAllCaches() {
208
- clearHandlers();
207
+ this.syncParamsHandler = void 0;
209
208
  }
210
209
  async resetCheckpointInfo() {
211
210
  await this.updateCheckPointInfo((info) => ({ ...CheckPointInfoDefault }));
212
- clearHandlers();
213
211
  }
214
212
  getJournalEpochFromSyncParams(params) {
215
213
  return `${params.protocolVersion}:${params.pbkdf2salt}`;
216
214
  }
217
215
  async ensureCheckpointCachesAreFresh() {
218
- let journalEpoch = "";
219
- try {
220
- const params = await this.getSyncParameters();
221
- journalEpoch = this.getJournalEpochFromSyncParams(params);
222
- } catch {
223
- return;
224
- }
216
+ const params = await this.getSyncParamsHandler().fetch(true);
217
+ if (!params) throw new SyncParamsFetchError("Could not prepare Journal synchronisation parameters");
218
+ const journalEpoch = this.getJournalEpochFromSyncParams(params);
225
219
  const current = await this.getCheckpointInfo();
226
220
  if (current.journalEpoch === journalEpoch) {
227
221
  return;
@@ -252,12 +246,12 @@ class JournalSyncCore {
252
246
  receivedFiles: /* @__PURE__ */ new Set(),
253
247
  sentFiles: /* @__PURE__ */ new Set()
254
248
  }));
255
- clearHandlers();
256
249
  }
257
250
  async isAvailable() {
258
251
  return await this.storage.isAvailable();
259
252
  }
260
253
  async resetBucket() {
254
+ this.resetAllCaches();
261
255
  let files = [];
262
256
  try {
263
257
  do {
@@ -269,7 +263,6 @@ class JournalSyncCore {
269
263
  return false;
270
264
  }
271
265
  } while (files.length != 0);
272
- clearHandlers();
273
266
  } catch (ex) {
274
267
  Logger(`WARNING! Could not delete files.`, LOG_LEVEL_NOTICE, "reset-bucket");
275
268
  Logger(ex, LOG_LEVEL_VERBOSE);
@@ -291,13 +284,14 @@ class JournalSyncCore {
291
284
  return this.getHash(this._settings);
292
285
  }
293
286
  async getReplicationPBKDF2Salt(refresh) {
294
- const server = this.getRemoteKey();
295
- const manager = createSyncParamsHanderForServer(server, {
287
+ return await this.getSyncParamsHandler().getPBKDF2Salt(refresh);
288
+ }
289
+ getSyncParamsHandler() {
290
+ return this.syncParamsHandler ??= createSyncParamsHandler({
296
291
  put: (params) => this.putSyncParameters(params),
297
292
  get: () => this.getSyncParameters(),
298
293
  create: () => this.getInitialSyncParameters()
299
294
  });
300
- return await manager.getPBKDF2Salt(refresh);
301
295
  }
302
296
  isEncryptionPrevented(fileName) {
303
297
  if (fileName.endsWith(DOCID_JOURNAL_SYNC_PARAMETERS)) return true;
@@ -762,6 +756,7 @@ class JournalSyncCore {
762
756
  /** Stop this Journal operation owner and release its storage transport. */
763
757
  dispose() {
764
758
  this.requestStop();
759
+ this.resetAllCaches();
765
760
  this.storage.dispose?.();
766
761
  }
767
762
  }
@@ -12,7 +12,6 @@ import { MinioStorageAdapter } from "./objectstore/MinioStorageAdapter.js";
12
12
  import { LiveSyncAbstractReplicator } from "../LiveSyncAbstractReplicator.js";
13
13
  import { ensureRemoteIsCompatible } from "../../pouchdb/LiveSyncDBFunctions.js";
14
14
  import { extractObject } from "../../common/utils.js";
15
- import { clearHandlers } from "../SyncParamsHandler.js";
16
15
  import { JournalStorageReadStatuses } from "./objectstore/JournalStorageAdapter.js";
17
16
  import {
18
17
  CENTRAL_COMPATIBILITY_ACCEPTED,
@@ -154,20 +153,24 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
154
153
  async openReplication(setting, _, showResult, ignoreCleanLock = false, recordCompatibilityDecision) {
155
154
  return await this.runJournalTransfer(async (stopGeneration) => {
156
155
  const client = this.setupJournalSyncClient(setting);
157
- stopGeneration = this.journalTransferStopGeneration;
158
- if (!await this.checkReplicationConnectivity(
159
- false,
160
- ignoreCleanLock,
161
- showResult,
162
- setting,
163
- client,
164
- recordCompatibilityDecision
165
- )) {
166
- return false;
156
+ try {
157
+ stopGeneration = this.journalTransferStopGeneration;
158
+ if (!await this.checkReplicationConnectivity(
159
+ false,
160
+ ignoreCleanLock,
161
+ showResult,
162
+ setting,
163
+ client,
164
+ recordCompatibilityDecision
165
+ )) {
166
+ return false;
167
+ }
168
+ if (stopGeneration !== this.journalTransferStopGeneration) return false;
169
+ this.hasEnteredReplication = true;
170
+ return await client.sync(showResult);
171
+ } finally {
172
+ client.resetAllCaches();
167
173
  }
168
- if (stopGeneration !== this.journalTransferStopGeneration) return false;
169
- this.hasEnteredReplication = true;
170
- return await client.sync(showResult);
171
174
  });
172
175
  }
173
176
  /**
@@ -191,20 +194,24 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
191
194
  async runDirectionalReplication(setting, showingNotice, transfer, recordCompatibilityDecision) {
192
195
  return await this.runJournalTransfer(async (stopGeneration) => {
193
196
  const client = this.setupJournalSyncClient(setting);
194
- stopGeneration = this.journalTransferStopGeneration;
195
- if (!await this.checkReplicationConnectivity(
196
- false,
197
- false,
198
- !!showingNotice,
199
- setting,
200
- client,
201
- recordCompatibilityDecision
202
- )) {
203
- return false;
197
+ try {
198
+ stopGeneration = this.journalTransferStopGeneration;
199
+ if (!await this.checkReplicationConnectivity(
200
+ false,
201
+ false,
202
+ !!showingNotice,
203
+ setting,
204
+ client,
205
+ recordCompatibilityDecision
206
+ )) {
207
+ return false;
208
+ }
209
+ if (stopGeneration !== this.journalTransferStopGeneration) return false;
210
+ this.hasEnteredReplication = true;
211
+ return await transfer(client);
212
+ } finally {
213
+ client.resetAllCaches();
204
214
  }
205
- if (stopGeneration !== this.journalTransferStopGeneration) return false;
206
- this.hasEnteredReplication = true;
207
- return await transfer(client);
208
215
  });
209
216
  }
210
217
  /** Capture only the compatibility decision made by this exact borrowed client. */
@@ -344,7 +351,6 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
344
351
  if (!await this.client.resetBucket()) {
345
352
  throw new Error("Could not reset remote bucket");
346
353
  }
347
- clearHandlers();
348
354
  Logger("Remote Bucket Cleared", LOG_LEVEL_NOTICE);
349
355
  await this.tryCreateRemoteDatabase(setting);
350
356
  } catch (ex) {
@@ -356,7 +362,6 @@ class LiveSyncJournalReplicator extends LiveSyncAbstractReplicator {
356
362
  async tryCreateRemoteDatabase(setting) {
357
363
  this.closeReplication();
358
364
  Logger("Remote Database Created or Connected", LOG_LEVEL_NOTICE);
359
- clearHandlers();
360
365
  if (!await this.ensurePBKDF2Salt(setting, true, false)) {
361
366
  throw new Error("Could not ensure PBKDF2 salt (Security Seed)");
362
367
  }
@@ -34,6 +34,8 @@ Commonlib's local-database lifecycle tests distinguish the settings-selected dat
34
34
 
35
35
  The focused `/replication` entry defines the provider capability, configuration identity, same-kind reconciliation, interaction authority, and truthful outcome contracts used when a host composes replication. A host selects its fixed provider subset and registers each definition during composition; an omitted role remains an explicit unsupported result rather than falling back to another provider. Commonlib serialises active replacement, rejects a candidate whose configuration changes before publication, and provides a transition-aware context acquisition boundary. The entry does not own user dialogue, P2P room policy, or selection of unknown providers.
36
36
 
37
+ The same entry distinguishes a central provider whose host prepares the Security Seed from one which prepares it within each transfer. Object Storage uses the latter: Journal reads fresh synchronisation parameters before compatibility checks, shares that result with its checkpoint epoch and encryption work, and releases it when the transfer settles. CouchDB retains host preparation. Focused Commonlib tests cover read reuse, later refresh, unavailable storage, and checkpoint reset; the maintained plug-in's provider tests and packed-package checks cover the declared readiness. Behaviour on the reporter's Android device remains a downstream runtime check.
38
+
37
39
  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.
38
40
 
39
41
  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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vrtmrz/livesync-commonlib",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
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",