@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.
- package/dist/replication/ReplicatorProvider.d.ts +5 -1
- package/dist/replication/ReplicatorProvider.js +4 -0
- package/dist/replication/SyncParamsHandler.d.ts +2 -0
- package/dist/replication/SyncParamsHandler.js +2 -1
- package/dist/replication/index.d.ts +1 -1
- package/dist/replication/index.js +2 -0
- package/dist/replication/journal/JournalSyncCore.d.ts +2 -0
- package/dist/replication/journal/JournalSyncCore.js +13 -18
- package/dist/replication/journal/LiveSyncJournalReplicator.js +34 -29
- package/docs/proven-in-use.md +2 -0
- package/package.json +1 -1
|
@@ -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 {};
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
295
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
}
|
package/docs/proven-in-use.md
CHANGED
|
@@ -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