@vrtmrz/livesync-commonlib 0.1.19 → 0.1.20
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 +5 -1
- package/dist/common/models/setting.p2p.d.ts +2 -0
- package/dist/common/models/setting.p2p.js +5 -0
- package/dist/interfaces/ServiceModule.d.ts +13 -4
- package/dist/managers/ChunkFetcher.js +10 -0
- package/dist/p2p/P2PService.d.ts +123 -0
- package/dist/p2p/P2PService.js +271 -0
- package/dist/p2p/index.d.ts +8 -0
- package/dist/p2p/index.js +8 -0
- package/dist/pouchdb/LiveSyncLocalDB.d.ts +3 -0
- package/dist/pouchdb/LiveSyncLocalDB.js +23 -3
- package/dist/pouchdb/ReplicatorShim.d.ts +43 -4
- package/dist/pouchdb/ReplicatorShim.js +78 -5
- package/dist/replication/CentralCompatibility.d.ts +38 -0
- package/dist/replication/CentralCompatibility.js +39 -0
- package/dist/replication/CentralRemoteAdministration.d.ts +84 -0
- package/dist/replication/CentralRemoteAdministration.js +71 -0
- package/dist/replication/LiveSyncAbstractReplicator.d.ts +19 -6
- package/dist/replication/ProviderCapability.d.ts +32 -0
- package/dist/replication/ProviderCapability.js +27 -0
- package/dist/replication/RemoteResource.d.ts +85 -0
- package/dist/replication/RemoteResource.js +17 -0
- package/dist/replication/ReplicatorInstance.d.ts +23 -0
- package/dist/replication/ReplicatorProvider.d.ts +249 -0
- package/dist/replication/ReplicatorProvider.js +145 -0
- package/dist/replication/couchdb/LiveSyncReplicator.d.ts +54 -8
- package/dist/replication/couchdb/LiveSyncReplicator.js +411 -158
- package/dist/replication/index.d.ts +10 -0
- package/dist/replication/index.js +70 -0
- package/dist/replication/journal/JournalSyncCore.d.ts +2 -0
- package/dist/replication/journal/JournalSyncCore.js +18 -5
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +38 -10
- package/dist/replication/journal/LiveSyncJournalReplicator.js +271 -63
- package/dist/replication/journal/LiveSyncJournalReplicatorEnv.d.ts +8 -0
- package/dist/replication/journal/objectstore/JournalStorageAdapter.d.ts +2 -0
- package/dist/replication/journal/objectstore/MinioStorageAdapter.d.ts +2 -0
- package/dist/replication/journal/objectstore/MinioStorageAdapter.js +10 -1
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.d.ts +12 -40
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.js +52 -152
- package/dist/replication/trystero/P2PAutomationCoordinator.d.ts +20 -0
- package/dist/replication/trystero/P2PAutomationCoordinator.js +43 -0
- package/dist/replication/trystero/P2PConnectionProbeAdmission.d.ts +32 -0
- package/dist/replication/trystero/P2PConnectionProbeAdmission.js +10 -0
- package/dist/replication/trystero/P2PReplicationWire.d.ts +31 -0
- package/dist/replication/trystero/P2PReplicationWire.js +50 -0
- package/dist/replication/trystero/P2PRoomSession.d.ts +32 -0
- package/dist/replication/trystero/P2PRoomSession.js +101 -0
- package/dist/replication/trystero/P2PRoomSessionOwner.d.ts +61 -0
- package/dist/replication/trystero/P2PRoomSessionOwner.js +249 -0
- package/dist/replication/trystero/TrysteroReplicator.d.ts +88 -36
- package/dist/replication/trystero/TrysteroReplicator.js +273 -113
- package/dist/replication/trystero/TrysteroReplicatorP2PClient.d.ts +7 -4
- package/dist/replication/trystero/TrysteroReplicatorP2PClient.js +21 -14
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +26 -3
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +84 -39
- package/dist/replication/trystero/UseP2PReplicatorResult.d.ts +10 -6
- package/dist/replication/trystero/addP2PEventHandlers.d.ts +8 -12
- package/dist/replication/trystero/addP2PEventHandlers.js +19 -19
- package/dist/replication/trystero/p2pReplicatorConfigurationIdentity.d.ts +9 -0
- package/dist/replication/trystero/p2pReplicatorConfigurationIdentity.js +30 -0
- package/dist/replication/trystero/types.d.ts +18 -0
- package/dist/replication/trystero/types.js +5 -1
- package/dist/replication/trystero/useP2PReplicatorCommands.d.ts +3 -3
- package/dist/replication/trystero/useP2PReplicatorCommands.js +6 -8
- package/dist/replication/trystero/useP2PReplicatorFeature.d.ts +16 -7
- package/dist/replication/trystero/useP2PReplicatorFeature.js +72 -55
- package/dist/rpc/RpcRoom.d.ts +15 -1
- package/dist/rpc/RpcRoom.js +176 -41
- package/dist/rpc/RpcSession.d.ts +3 -1
- package/dist/rpc/RpcSession.js +2 -2
- package/dist/rpc/index.d.ts +1 -1
- package/dist/rpc/transports/trysteroUtils.js +2 -1
- package/dist/rpc/types.d.ts +26 -1
- package/dist/serviceFeatures/checkRemoteSize.js +5 -2
- package/dist/serviceModules/Rebuilder.js +66 -56
- package/dist/services/base/AppLifecycleService.d.ts +4 -0
- package/dist/services/base/AppLifecycleService.js +4 -0
- package/dist/services/base/ControlService.js +0 -4
- package/dist/services/base/DatabaseEventService.js +1 -1
- package/dist/services/base/IService.d.ts +105 -8
- package/dist/services/base/ReadinessEvaluator.d.ts +40 -0
- package/dist/services/base/ReadinessEvaluator.js +16 -0
- package/dist/services/base/RemoteConnection.d.ts +3 -0
- package/dist/services/base/RemoteService.js +2 -2
- package/dist/services/base/ReplicationService.d.ts +45 -10
- package/dist/services/base/ReplicationService.js +248 -91
- package/dist/services/base/ReplicationService.readiness.d.ts +52 -0
- package/dist/services/base/ReplicationService.readiness.js +87 -0
- package/dist/services/base/ReplicationService.typedReplication.d.ts +83 -0
- package/dist/services/base/ReplicationService.typedReplication.js +191 -0
- package/dist/services/base/ReplicatorService.activeReplicatorState.d.ts +47 -0
- package/dist/services/base/ReplicatorService.activeReplicatorState.js +107 -0
- package/dist/services/base/ReplicatorService.centralRemoteAdministration.d.ts +5 -0
- package/dist/services/base/ReplicatorService.centralRemoteAdministration.js +44 -0
- package/dist/services/base/ReplicatorService.d.ts +75 -14
- package/dist/services/base/ReplicatorService.js +232 -53
- package/dist/services/base/ReplicatorService.remoteResourceResolver.d.ts +4 -0
- package/dist/services/base/ReplicatorService.remoteResourceResolver.js +11 -0
- package/dist/services/base/TweakValueService.d.ts +3 -2
- package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +1 -1
- package/dist/services/lib/HandlerUtils.d.ts +24 -0
- package/dist/services/lib/HandlerUtils.js +40 -0
- package/docs/development.md +40 -7
- package/docs/p2p-transport-lifecycle.md +135 -11
- package/docs/proven-in-use.md +5 -1
- package/docs/remote-configurations.md +4 -0
- package/docs/service-feature-composition.md +250 -0
- package/package.json +17 -1
- package/dist/replication/trystero/P2PReplicatorBase.d.ts +0 -19
- package/dist/replication/trystero/P2PReplicatorCore.d.ts +0 -8
- package/dist/replication/trystero/P2PReplicatorCore.js +0 -56
- package/dist/replication/trystero/TrysteroReplicatorP2PConnection.d.ts +0 -2
- package/dist/replication/trystero/TrysteroReplicatorP2PConnection.js +0 -4
- /package/dist/replication/{trystero/P2PReplicatorBase.js → ReplicatorInstance.js} +0 -0
package/dist/rpc/types.d.ts
CHANGED
|
@@ -9,6 +9,29 @@ export type RpcErrorShape = {
|
|
|
9
9
|
message: string;
|
|
10
10
|
details?: JsonLike;
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Options which control one outbound RPC invocation.
|
|
14
|
+
*
|
|
15
|
+
* The numeric timeout overload remains supported by {@link RpcSession.call}
|
|
16
|
+
* and {@link RpcRoom.invoke}; use this object when a caller also needs an
|
|
17
|
+
* abort signal.
|
|
18
|
+
*/
|
|
19
|
+
export type RpcCallOptions = {
|
|
20
|
+
readonly timeoutMs?: number;
|
|
21
|
+
readonly signal?: AbortSignal;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Context supplied to handlers registered through the cancellation-aware
|
|
25
|
+
* registration API.
|
|
26
|
+
*
|
|
27
|
+
* Cancellation is cooperative: the handler must observe `signal.aborted` or
|
|
28
|
+
* otherwise pass the signal to an abort-capable operation. The RPC layer still
|
|
29
|
+
* suppresses a response after cancellation when a handler does not stop
|
|
30
|
+
* immediately.
|
|
31
|
+
*/
|
|
32
|
+
export type RpcRequestContext = {
|
|
33
|
+
readonly signal: AbortSignal;
|
|
34
|
+
};
|
|
12
35
|
export type RpcRequestEnvelope = {
|
|
13
36
|
kind: "request";
|
|
14
37
|
requestId: string;
|
|
@@ -66,10 +89,12 @@ export type RpcRoomOptions = {
|
|
|
66
89
|
transport: TransportAdapter;
|
|
67
90
|
maxWirePayloadBytes?: number;
|
|
68
91
|
chunkMissingRetryMs?: number;
|
|
69
|
-
canAcceptRequest?: (peerId: string, method: string) => boolean | Promise<boolean>;
|
|
92
|
+
canAcceptRequest?: (peerId: string, method: string, context: RpcRequestContext) => boolean | Promise<boolean>;
|
|
70
93
|
onProtocolWarning?: (message: string, peerId?: string) => void;
|
|
71
94
|
};
|
|
72
95
|
export type RpcMethodHandler<T extends JsonLike[], U> = (peerId: string, ...args: T) => U | Promise<U>;
|
|
96
|
+
/** Handler which can stop cooperative work when its request is cancelled. */
|
|
97
|
+
export type RpcCancellationAwareMethodHandler<T extends JsonLike[], U> = (context: RpcRequestContext, peerId: string, ...args: T) => U | Promise<U>;
|
|
73
98
|
export type RpcRegisterOptions = {
|
|
74
99
|
serial?: boolean;
|
|
75
100
|
};
|
|
@@ -4,6 +4,9 @@ import { createInstanceLogFunction } from "../services/lib/logUtils.js";
|
|
|
4
4
|
import { EVENT_REQUEST_CHECK_REMOTE_SIZE } from "../events/coreEvents.js";
|
|
5
5
|
import { fireAndForget } from "../common/utils.js";
|
|
6
6
|
const REMOTE_SIZE_NOTICE_DURATION_MS = 5 * 60 * 1e3;
|
|
7
|
+
function canReadRemoteStatus(replicator) {
|
|
8
|
+
return replicator !== void 0 && "getRemoteStatus" in replicator && typeof replicator.getRemoteStatus === "function";
|
|
9
|
+
}
|
|
7
10
|
function configureReviewAnchor(anchor, label, review) {
|
|
8
11
|
anchor.href = "#";
|
|
9
12
|
anchor.textContent = label;
|
|
@@ -84,13 +87,13 @@ function onNotifyRemoteSizeExceedFactory(host, log, mode = "notice") {
|
|
|
84
87
|
return true;
|
|
85
88
|
}
|
|
86
89
|
const replicator = host.services.replicator.getActiveReplicator();
|
|
87
|
-
const remoteStat = await replicator
|
|
90
|
+
const remoteStat = canReadRemoteStatus(replicator) ? await replicator.getRemoteStatus(settings) : false;
|
|
88
91
|
if (!remoteStat) {
|
|
89
92
|
log("Failed to get remote status, skipping remote size exceed check.", LOG_LEVEL_INFO);
|
|
90
93
|
return true;
|
|
91
94
|
}
|
|
92
95
|
const estimatedSize = remoteStat.estimatedSize;
|
|
93
|
-
if (estimatedSize) {
|
|
96
|
+
if (estimatedSize !== void 0) {
|
|
94
97
|
const maxSize = settings.notifyThresholdOfRemoteStorageSize * 1024 * 1024;
|
|
95
98
|
if (estimatedSize <= maxSize) {
|
|
96
99
|
log(
|
|
@@ -10,8 +10,12 @@ import { getConfiguredFunctionsForEncryption } from "../pouchdb/encryption.js";
|
|
|
10
10
|
import { AuthorizationHeaderGenerator, generateCredentialObject } from "../replication/httplib.js";
|
|
11
11
|
import { parseHeaderValues } from "../common/utils.js";
|
|
12
12
|
import { sizeToHumanReadable } from "octagonal-wheels/number";
|
|
13
|
+
import { REMOTE_RESOURCE_KINDS } from "../replication/index.js";
|
|
13
14
|
const FAST_FETCH_CHECKPOINT_KEY = "fast-fetch-checkpoint";
|
|
14
15
|
const FAST_FETCH_RETRY_DELAYS = [2e3, 5e3, 1e4, 2e4];
|
|
16
|
+
function canResetCentralRemote(replicator) {
|
|
17
|
+
return "tryResetRemoteDatabase" in replicator && typeof replicator.tryResetRemoteDatabase === "function";
|
|
18
|
+
}
|
|
15
19
|
class ServiceRebuilder extends ServiceModuleBase {
|
|
16
20
|
events;
|
|
17
21
|
appLifecycle;
|
|
@@ -180,6 +184,9 @@ Please enable them from the settings screen after setup is complete.`,
|
|
|
180
184
|
this._log("No active replicator found when trying to reset remote database.", LOG_LEVEL_NOTICE);
|
|
181
185
|
return;
|
|
182
186
|
}
|
|
187
|
+
if (!canResetCentralRemote(currentReplicator)) {
|
|
188
|
+
throw new Error("The active replicator does not support resetting a central remote database.");
|
|
189
|
+
}
|
|
183
190
|
await currentReplicator.tryResetRemoteDatabase(settings);
|
|
184
191
|
}
|
|
185
192
|
// private async _tryCreateRemoteDatabase(): Promise<void> {
|
|
@@ -397,67 +404,70 @@ Are you sure you wish to proceed?`;
|
|
|
397
404
|
localDB = this.database.localDatabase.localDatabase;
|
|
398
405
|
since = "0";
|
|
399
406
|
}
|
|
400
|
-
const
|
|
401
|
-
if (!
|
|
402
|
-
throw new Error("
|
|
407
|
+
const securitySeed = await this.replicator.createRemoteResource(REMOTE_RESOURCE_KINDS.SECURITY_SEED, settings);
|
|
408
|
+
if (!securitySeed) {
|
|
409
|
+
throw new Error("The selected provider cannot supply a Security Seed for Fast Fetch.");
|
|
403
410
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
411
|
+
try {
|
|
412
|
+
const enc = getConfiguredFunctionsForEncryption(
|
|
413
|
+
settings.passphrase,
|
|
414
|
+
false,
|
|
415
|
+
false,
|
|
416
|
+
() => securitySeed.read(),
|
|
417
|
+
settings.E2EEAlgorithm
|
|
418
|
+
);
|
|
419
|
+
const authHeader = await new AuthorizationHeaderGenerator().getAuthorizationHeader(
|
|
420
|
+
generateCredentialObject(settings)
|
|
421
|
+
);
|
|
422
|
+
const customHeaders = parseHeaderValues(settings.couchDB_CustomHeaders);
|
|
423
|
+
for (let attempt = 0; ; attempt++) {
|
|
424
|
+
try {
|
|
425
|
+
await fetchChangesForInitialSync(
|
|
426
|
+
localDB,
|
|
427
|
+
remote,
|
|
428
|
+
authHeader,
|
|
429
|
+
enc.outgoing,
|
|
430
|
+
since,
|
|
431
|
+
(progress) => {
|
|
432
|
+
this._log(
|
|
433
|
+
`Fast fetch progress: ${progress.totalValidFetched} / ${progress.docsToFetch}
|
|
427
434
|
Total bytes fetched: ${sizeToHumanReadable(progress.totalBytes)}`,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
435
|
+
LOG_LEVEL_NOTICE,
|
|
436
|
+
"fetch-init-progress"
|
|
437
|
+
);
|
|
438
|
+
},
|
|
439
|
+
(sequence) => this.saveFastFetchCheckpoint(remote, sequence),
|
|
440
|
+
customHeaders
|
|
441
|
+
);
|
|
442
|
+
break;
|
|
443
|
+
} catch (ex) {
|
|
444
|
+
if (!isRetryableStreamingFetchFailure(ex) || attempt >= FAST_FETCH_RETRY_DELAYS.length) throw ex;
|
|
445
|
+
checkpoint = this.getFastFetchCheckpoint(remote);
|
|
446
|
+
since = checkpoint?.sequence ?? since;
|
|
447
|
+
this._log(
|
|
448
|
+
`Fast fetch interrupted. Retrying from sequence: ${since}`,
|
|
449
|
+
LOG_LEVEL_NOTICE,
|
|
450
|
+
"fetch-init-resume"
|
|
451
|
+
);
|
|
452
|
+
await delay(FAST_FETCH_RETRY_DELAYS[attempt]);
|
|
453
|
+
}
|
|
446
454
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
455
|
+
const allDocs = await localDB.allDocs({ include_docs: false });
|
|
456
|
+
this._log(
|
|
457
|
+
`Fast database fetch completed. Total documents in local database: ${allDocs.total_rows}`,
|
|
458
|
+
LOG_LEVEL_NOTICE,
|
|
459
|
+
"fetch-init-complete"
|
|
460
|
+
);
|
|
461
|
+
await this.replication.markResolved();
|
|
462
|
+
if (autoResume) {
|
|
463
|
+
if (!await this.finishRebuild()) {
|
|
464
|
+
throw new Error("Fast Fetch could not be finalised.");
|
|
465
|
+
}
|
|
458
466
|
}
|
|
467
|
+
this.clearFastFetchCheckpoint();
|
|
468
|
+
} finally {
|
|
469
|
+
await securitySeed.dispose();
|
|
459
470
|
}
|
|
460
|
-
this.clearFastFetchCheckpoint();
|
|
461
471
|
}
|
|
462
472
|
/**
|
|
463
473
|
* Complete the final rebuild phases and establish application readiness.
|
|
@@ -65,6 +65,9 @@ export declare abstract class AppLifecycleService<T extends ServiceContext = Ser
|
|
|
65
65
|
readonly onBeforeUnload: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<() => Promise<boolean>>;
|
|
66
66
|
/**
|
|
67
67
|
* Event triggered when the plug-in is being unloaded.
|
|
68
|
+
*
|
|
69
|
+
* Resource owners complete terminal retirement here. `ControlService`
|
|
70
|
+
* awaits every handler before it closes the local database.
|
|
68
71
|
*/
|
|
69
72
|
readonly onUnload: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<() => Promise<boolean>>;
|
|
70
73
|
/**
|
|
@@ -85,6 +88,7 @@ export declare abstract class AppLifecycleService<T extends ServiceContext = Ser
|
|
|
85
88
|
abstract scheduleRestart(): void;
|
|
86
89
|
/**
|
|
87
90
|
* Event triggered when the application is being suspended (e.g., system sleep).
|
|
91
|
+
* Suspension is reversible and is not equivalent to terminal unload.
|
|
88
92
|
*/
|
|
89
93
|
readonly onSuspending: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<() => Promise<boolean>>;
|
|
90
94
|
/**
|
|
@@ -62,10 +62,14 @@ class AppLifecycleService extends ServiceBase {
|
|
|
62
62
|
onBeforeUnload = handlers().all("onBeforeUnload");
|
|
63
63
|
/**
|
|
64
64
|
* Event triggered when the plug-in is being unloaded.
|
|
65
|
+
*
|
|
66
|
+
* Resource owners complete terminal retirement here. `ControlService`
|
|
67
|
+
* awaits every handler before it closes the local database.
|
|
65
68
|
*/
|
|
66
69
|
onUnload = handlers().all("onUnload");
|
|
67
70
|
/**
|
|
68
71
|
* Event triggered when the application is being suspended (e.g., system sleep).
|
|
72
|
+
* Suspension is reversible and is not equivalent to terminal unload.
|
|
69
73
|
*/
|
|
70
74
|
onSuspending = handlers().bailFirstFailure("onSuspending");
|
|
71
75
|
/**
|
|
@@ -58,10 +58,6 @@ class ControlService extends ServiceBase {
|
|
|
58
58
|
this._unloaded = true;
|
|
59
59
|
const localDatabase = this.services.databaseService.localDatabaseDirect;
|
|
60
60
|
if (localDatabase != null) {
|
|
61
|
-
const activeReplicator = this.services.replicatorService.getActiveReplicator();
|
|
62
|
-
if (activeReplicator) {
|
|
63
|
-
activeReplicator.closeReplication();
|
|
64
|
-
}
|
|
65
61
|
localDatabase.onunload();
|
|
66
62
|
await localDatabase.close();
|
|
67
63
|
}
|
|
@@ -8,7 +8,7 @@ class DatabaseEventService extends ServiceBase {
|
|
|
8
8
|
/**
|
|
9
9
|
* Event triggered when the database is about to be closed.
|
|
10
10
|
*/
|
|
11
|
-
onCloseDatabase = handlers().
|
|
11
|
+
onCloseDatabase = handlers().allSettled("onCloseDatabase");
|
|
12
12
|
/**
|
|
13
13
|
* Event triggered when the database is being initialized.
|
|
14
14
|
*/
|
|
@@ -2,7 +2,7 @@ import type { FetchHttpHandler } from "@smithy/fetch-http-handler";
|
|
|
2
2
|
import { type LOG_LEVEL } from "octagonal-wheels/common/logger";
|
|
3
3
|
import type { AnyEntry, AUTO_MERGED, CouchDBCredentials, diff_result, DocumentID, EntryDoc, EntryHasPath, FileEventItem, FilePath, FilePathWithPrefix, LoadedEntry, MetaEntry, MISSING_OR_ERROR, ObsidianLiveSyncSettings, RemoteDBSettings, RemotePreferredTweakResult, SettingsMigrationState, TweakValues, UXFileInfo, UXFileInfoStub } from "../../common/types.js";
|
|
4
4
|
import type { LiveSyncLocalDB } from "../../pouchdb/LiveSyncLocalDB.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { ReplicatorInstance } from "../../replication/ReplicatorInstance.js";
|
|
6
6
|
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
|
|
7
7
|
import type { Confirm } from "../../interfaces/Confirm.js";
|
|
8
8
|
import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive";
|
|
@@ -12,6 +12,10 @@ import type { DatabaseEventService } from "./DatabaseEventService.js";
|
|
|
12
12
|
import type { BASE_IS_NEW, EVEN, TARGET_IS_NEW } from "../../common/models/shared.const.symbols.js";
|
|
13
13
|
import type { AsyncActivityOptions } from "../../interfaces/AsyncActivityRunner.js";
|
|
14
14
|
import type { OwnedCouchDBConnection, RemoteConnectionOpenOptions } from "./RemoteConnection.js";
|
|
15
|
+
import type { ActiveReplicatorContext, ContinuousReplicationRequest, ReplicationAttemptFailure, ReplicationFailureRequest, ReplicatorProviderDefinitionMap, ReplicationOutcome, ReplicationReadinessRequirements, UnattendedOneShotRequest, UserInitiatedOneShotRequest } from "../../replication/ReplicatorProvider.js";
|
|
16
|
+
import type { RemoteResourceKind, RemoteResourceMap } from "../../replication/RemoteResource.js";
|
|
17
|
+
import type { CentralRemoteAdministrationRequest, CentralRemoteAdministrationResult } from "../../replication/CentralRemoteAdministration.js";
|
|
18
|
+
import type { MultipleHandlerFunction } from "../lib/HandlerUtils.js";
|
|
15
19
|
declare global {
|
|
16
20
|
interface OPTIONAL_SYNC_FEATURES {
|
|
17
21
|
DISABLE: "DISABLE";
|
|
@@ -99,10 +103,63 @@ export interface IFileProcessingService {
|
|
|
99
103
|
onStorageFileEvent(): void;
|
|
100
104
|
}
|
|
101
105
|
export interface IReplicatorService {
|
|
106
|
+
/**
|
|
107
|
+
* Fence new work, drain admitted work, and close the active Replicator.
|
|
108
|
+
*
|
|
109
|
+
* Do not await this command from an admitted active-context callback: the
|
|
110
|
+
* retirement command waits for that callback to settle.
|
|
111
|
+
*/
|
|
102
112
|
onCloseActiveReplication(): Promise<boolean>;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Run host preparation after candidate initialisation and before active publication.
|
|
115
|
+
*
|
|
116
|
+
* This hook runs inside the ownership transition. A handler must not call or await
|
|
117
|
+
* `acquireActiveReplicatorContext()` or `runWithActiveReplicatorContext()`, because
|
|
118
|
+
* both wait for that same transition. Work which requires the new active context
|
|
119
|
+
* belongs after publication.
|
|
120
|
+
*/
|
|
121
|
+
onBeforeReplicatorPublication(): Promise<boolean>;
|
|
122
|
+
getNewReplicator: ReplicatorFactoryHandler;
|
|
123
|
+
/** Add the fixed provider definitions composed by the current host. */
|
|
124
|
+
registerReplicatorProviderDefinitions(definitions: ReplicatorProviderDefinitionMap): void;
|
|
125
|
+
/** Resolve one provider-owned finite resource without changing active publication. */
|
|
126
|
+
createRemoteResource<TKind extends RemoteResourceKind>(kind: TKind, setting: RemoteDBSettings): Promise<RemoteResourceMap[TKind] | undefined>;
|
|
127
|
+
/** Apply and verify one typed provider-specific central-remote administration action. */
|
|
128
|
+
runCentralRemoteAdministration(request: CentralRemoteAdministrationRequest): Promise<CentralRemoteAdministrationResult>;
|
|
129
|
+
/** Wait for queued ownership transitions, then return the published active context. */
|
|
130
|
+
acquireActiveReplicatorContext(): Promise<ActiveReplicatorContext | undefined>;
|
|
131
|
+
/**
|
|
132
|
+
* Run one task against an exact active publication admitted before later transitions.
|
|
133
|
+
*
|
|
134
|
+
* Invocation is ordered with replacement and disposal. A later
|
|
135
|
+
* transition fences new admission, requests transfer cancellation, waits for
|
|
136
|
+
* admitted tasks, and only then closes the old Replicator. The
|
|
137
|
+
* callback must not initiate or await settings realisation, database
|
|
138
|
+
* replacement, Replicator retirement, or another operation which queues that
|
|
139
|
+
* same lifecycle transition. Stage such recovery after this promise settles.
|
|
140
|
+
* Terminal plug-in unload uses the same retirement drain before the local
|
|
141
|
+
* database closes. Reversible host suspension only requests transfer
|
|
142
|
+
* cancellation and retains the publication, so it is not a retirement fence.
|
|
143
|
+
*
|
|
144
|
+
* The callback is not invoked when no typed publication can be admitted.
|
|
145
|
+
*/
|
|
146
|
+
runWithActiveReplicatorContext<TResult>(task: (context: ActiveReplicatorContext) => TResult | PromiseLike<TResult>): Promise<TResult | undefined>;
|
|
147
|
+
/**
|
|
148
|
+
* Return the legacy unreserved view of the active Replicator.
|
|
149
|
+
*
|
|
150
|
+
* Missing active state retains its established diagnostic. New work must
|
|
151
|
+
* acquire or admit an `ActiveReplicatorContext` instead.
|
|
152
|
+
*/
|
|
153
|
+
getActiveReplicator(): ReplicatorInstance | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* Return whether a compatibility active Replicator exists without
|
|
156
|
+
* acquiring or returning it.
|
|
157
|
+
*
|
|
158
|
+
* This side-effect-free, non-owning predicate exists only for
|
|
159
|
+
* compatibility-state classification. Work must acquire or admit an
|
|
160
|
+
* `ActiveReplicatorContext` before using a Replicator.
|
|
161
|
+
*/
|
|
162
|
+
hasActiveReplicator(): boolean;
|
|
106
163
|
replicationStatics: ReactiveSource<ReplicationStatics>;
|
|
107
164
|
/** Number of finite remote operations currently in progress. */
|
|
108
165
|
boundedRemoteActivityCount: ReactiveSource<number>;
|
|
@@ -113,19 +170,59 @@ export interface IReplicatorService {
|
|
|
113
170
|
/** Runs finite replication which may place documents in the local database. */
|
|
114
171
|
runFiniteReplicationActivity<T>(task: () => T | PromiseLike<T>, options?: AsyncActivityOptions): Promise<T>;
|
|
115
172
|
}
|
|
173
|
+
export type ReplicatorFactoryCallback = (settingOverride?: Partial<ObsidianLiveSyncSettings>) => Promise<ReplicatorInstance | undefined | false>;
|
|
174
|
+
export type ReplicatorFactoryHandler = MultipleHandlerFunction<ReplicatorFactoryCallback>;
|
|
116
175
|
export interface IReplicationService {
|
|
117
176
|
processSynchroniseResult(doc: MetaEntry): Promise<boolean>;
|
|
118
177
|
processOptionalSynchroniseResult(doc: LoadedEntry): Promise<boolean>;
|
|
119
178
|
processVirtualDocument(docs: PouchDB.Core.ExistingDocument<EntryDoc>): Promise<boolean>;
|
|
120
179
|
onBeforeReplicate(showMessage: boolean): Promise<boolean>;
|
|
121
|
-
|
|
180
|
+
/** Prepare provider state which exists only for the central remote. */
|
|
181
|
+
onPrepareCentralRemoteReplication(showMessage: boolean): Promise<boolean>;
|
|
182
|
+
checkConnectionFailure(failure: ReplicationAttemptFailure): Promise<boolean | "CHECKAGAIN" | undefined>;
|
|
122
183
|
/** Lightweight, idempotent policy checks which every replication entry point may invoke. */
|
|
123
184
|
onCheckReplicationReady(showMessage: boolean): Promise<boolean>;
|
|
124
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Evaluate ordered host and provider readiness without acquiring a Replicator.
|
|
187
|
+
*
|
|
188
|
+
* The provider requirements determine whether central-remote preparation
|
|
189
|
+
* applies. A false result means no remote work has been admitted.
|
|
190
|
+
*/
|
|
191
|
+
isReplicationReady(showMessage: boolean, readiness?: ReplicationReadinessRequirements): Promise<boolean>;
|
|
192
|
+
/**
|
|
193
|
+
* Run one user-initiated typed capability against an atomically acquired context.
|
|
194
|
+
*
|
|
195
|
+
* Readiness precedes finite activity, and failure recovery receives only
|
|
196
|
+
* the interaction authority carried by the request.
|
|
197
|
+
*/
|
|
198
|
+
replicateUserInitiated(request?: UserInitiatedOneShotRequest): Promise<ReplicationOutcome>;
|
|
199
|
+
/**
|
|
200
|
+
* Run one unattended typed capability without granting interaction authority.
|
|
201
|
+
*
|
|
202
|
+
* Readiness precedes finite activity, and failed outcomes reach unattended
|
|
203
|
+
* recovery only after activity accounting has settled.
|
|
204
|
+
*/
|
|
205
|
+
replicateUnattended(request: UnattendedOneShotRequest): Promise<ReplicationOutcome>;
|
|
206
|
+
/** Serialise and rate-limit an unattended event-triggered finite capability. */
|
|
207
|
+
replicateUnattendedByEvent(request: UnattendedOneShotRequest): Promise<ReplicationOutcome>;
|
|
208
|
+
/**
|
|
209
|
+
* Start a supported continuous capability after its provider-specific readiness.
|
|
210
|
+
*
|
|
211
|
+
* Applicability is checked before readiness. Continuous work is not wrapped
|
|
212
|
+
* as finite activity and does not use the finite-operation recovery handler.
|
|
213
|
+
*/
|
|
214
|
+
startContinuous(request: ContinuousReplicationRequest): Promise<ReplicationOutcome>;
|
|
215
|
+
/**
|
|
216
|
+
* Stop finite transfer work on the current typed active Replicator.
|
|
217
|
+
*
|
|
218
|
+
* This operator action bypasses readiness, finite activity accounting, and
|
|
219
|
+
* failure-recovery handlers.
|
|
220
|
+
*/
|
|
221
|
+
stopActiveTransfer(): Promise<ReplicationOutcome>;
|
|
125
222
|
performReplication(showMessage?: boolean): Promise<boolean | void>;
|
|
126
223
|
replicate(showMessage?: boolean): Promise<boolean | void>;
|
|
127
224
|
replicateByEvent(showMessage?: boolean): Promise<boolean | void>;
|
|
128
|
-
onReplicationFailed(
|
|
225
|
+
onReplicationFailed(request: ReplicationFailureRequest): Promise<boolean>;
|
|
129
226
|
parseSynchroniseResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<boolean>;
|
|
130
227
|
databaseQueueCount: ReactiveSource<number>;
|
|
131
228
|
storageApplyingCount: ReactiveSource<number>;
|
|
@@ -238,7 +335,7 @@ export interface ISettingService {
|
|
|
238
335
|
export interface ITweakValueService {
|
|
239
336
|
fetchRemotePreferred(trialSetting: RemoteDBSettings): Promise<RemotePreferredTweakResult>;
|
|
240
337
|
checkAndAskResolvingMismatched(preferred: Partial<TweakValues>): Promise<[TweakValues | boolean, boolean]>;
|
|
241
|
-
askResolvingMismatched(preferredSource: TweakValues): Promise<"OK" | "CHECKAGAIN" | "IGNORE">;
|
|
338
|
+
askResolvingMismatched(preferredSource: TweakValues, updatePreferredRemote?: (setting: ObsidianLiveSyncSettings) => Promise<boolean>): Promise<"OK" | "CHECKAGAIN" | "IGNORE">;
|
|
242
339
|
checkAndAskUseRemoteConfiguration(settings: RemoteDBSettings): Promise<{
|
|
243
340
|
result: false | TweakValues;
|
|
244
341
|
requireFetch: boolean;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** One ordered condition in a readiness evaluation. */
|
|
2
|
+
export interface ReadinessCondition<TContext> {
|
|
3
|
+
/** Stable diagnostic name for the condition; it is not user-facing text. */
|
|
4
|
+
readonly name: string;
|
|
5
|
+
/**
|
|
6
|
+
* Return whether this condition applies to the current evaluation.
|
|
7
|
+
*
|
|
8
|
+
* Applicability checks must be side-effect-free. Conditions without this
|
|
9
|
+
* callback always apply.
|
|
10
|
+
*/
|
|
11
|
+
readonly appliesTo?: (context: TContext) => boolean;
|
|
12
|
+
/** Return whether the condition admits the requested operation. */
|
|
13
|
+
readonly evaluate: (context: TContext) => boolean | Promise<boolean>;
|
|
14
|
+
/** Perform condition-specific reporting after a false result. */
|
|
15
|
+
readonly onRejected?: (context: TContext) => void | Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/** A purpose and its ordered readiness conditions. */
|
|
18
|
+
export interface ReadinessEvaluationRequest<TContext> {
|
|
19
|
+
/** Stable diagnostic purpose; it is not user-facing text. */
|
|
20
|
+
readonly purpose: string;
|
|
21
|
+
readonly context: TContext;
|
|
22
|
+
readonly conditions: readonly ReadinessCondition<TContext>[];
|
|
23
|
+
}
|
|
24
|
+
/** Structured result from an ordered readiness evaluation. */
|
|
25
|
+
export type ReadinessEvaluationResult = {
|
|
26
|
+
readonly ready: true;
|
|
27
|
+
readonly purpose: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly ready: false;
|
|
30
|
+
readonly purpose: string;
|
|
31
|
+
readonly rejectedCondition: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Evaluate applicable conditions in declaration order until one rejects.
|
|
35
|
+
*
|
|
36
|
+
* Rejection reporting completes before the result is returned. Exceptions
|
|
37
|
+
* from applicability, evaluation, or reporting callbacks propagate unchanged;
|
|
38
|
+
* the evaluator does not reinterpret operational failures as policy results.
|
|
39
|
+
*/
|
|
40
|
+
export declare function evaluateReadiness<TContext>(request: ReadinessEvaluationRequest<TContext>): Promise<ReadinessEvaluationResult>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
async function evaluateReadiness(request) {
|
|
2
|
+
for (const condition of request.conditions) {
|
|
3
|
+
if (condition.appliesTo && !condition.appliesTo(request.context)) continue;
|
|
4
|
+
if (await condition.evaluate(request.context)) continue;
|
|
5
|
+
await condition.onRejected?.(request.context);
|
|
6
|
+
return {
|
|
7
|
+
ready: false,
|
|
8
|
+
purpose: request.purpose,
|
|
9
|
+
rejectedCondition: condition.name
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return { ready: true, purpose: request.purpose };
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
evaluateReadiness
|
|
16
|
+
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { E2EEAlgorithm } from "../../common/types.js";
|
|
1
2
|
/**
|
|
2
3
|
* Options which bind requests made through a remote CouchDB connection to the
|
|
3
4
|
* operation which owns it.
|
|
4
5
|
*/
|
|
5
6
|
export interface RemoteConnectionOpenOptions {
|
|
7
|
+
/** Encryption algorithm captured with the settings which own this connection. */
|
|
8
|
+
readonly encryptionAlgorithm?: E2EEAlgorithm;
|
|
6
9
|
/**
|
|
7
10
|
* Cancels abort-capable requests made through the connection when the
|
|
8
11
|
* owning operation is cancelled.
|
|
@@ -275,7 +275,7 @@ class RemoteService extends ServiceBase {
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
};
|
|
278
|
-
const
|
|
278
|
+
const encryptionAlgorithm = connectionOptions.encryptionAlgorithm ?? this._settingService.currentSettings().E2EEAlgorithm;
|
|
279
279
|
let db;
|
|
280
280
|
try {
|
|
281
281
|
db = new this._pouchDB(uri, conf);
|
|
@@ -297,7 +297,7 @@ class RemoteService extends ServiceBase {
|
|
|
297
297
|
replicationFilter(db, compression);
|
|
298
298
|
disableEncryption();
|
|
299
299
|
if (passphrase !== "false" && typeof passphrase === "string") {
|
|
300
|
-
enableEncryption(db, passphrase, useDynamicIterationCount, false, getPBKDF2Salt,
|
|
300
|
+
enableEncryption(db, passphrase, useDynamicIterationCount, false, getPBKDF2Salt, encryptionAlgorithm);
|
|
301
301
|
}
|
|
302
302
|
} catch (ex) {
|
|
303
303
|
await closeAfterConnectionFailure();
|
|
@@ -2,7 +2,7 @@ import { type LOG_LEVEL } from "../../common/types.js";
|
|
|
2
2
|
import type { IAPIService, IDatabaseService, IFileProcessingService, IReplicationService, IReplicatorService, ISettingService } from "./IService.js";
|
|
3
3
|
import { ServiceBase, type ServiceContext } from "./ServiceBase.js";
|
|
4
4
|
import { type LogFunction } from "../lib/logUtils.js";
|
|
5
|
-
import type
|
|
5
|
+
import { type ContinuousReplicationRequest, type ReplicationAttemptFailure, type ReplicationOutcome, type ReplicationReadinessRequirements, type UnattendedOneShotRequest, type UserInitiatedOneShotRequest } from "../../replication/ReplicatorProvider.js";
|
|
6
6
|
import type { AppLifecycleService } from "./AppLifecycleService.js";
|
|
7
7
|
export interface ReplicationServiceDependencies {
|
|
8
8
|
APIService: IAPIService;
|
|
@@ -13,10 +13,17 @@ export interface ReplicationServiceDependencies {
|
|
|
13
13
|
fileProcessingService: IFileProcessingService;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Host-facing replication façade for handlers, legacy entry points, and counters.
|
|
17
|
+
*
|
|
18
|
+
* Ordered readiness evaluation and typed capability dispatch are delegated to
|
|
19
|
+
* focused collaborators. Handler objects remain on this service so existing
|
|
20
|
+
* hosts retain their registration identity, priority ordering, and failure
|
|
21
|
+
* semantics. Active Replicator ownership remains with `ReplicatorService`.
|
|
17
22
|
*/
|
|
18
23
|
export declare abstract class ReplicationService<T extends ServiceContext = ServiceContext> extends ServiceBase<T> implements IReplicationService {
|
|
19
24
|
private _unresolvedErrorManager;
|
|
25
|
+
private readonly _evaluateReadiness;
|
|
26
|
+
private readonly _typedReplication;
|
|
20
27
|
showError(msg: string, max_log_level?: LOG_LEVEL): void;
|
|
21
28
|
clearErrors(): void;
|
|
22
29
|
_log: LogFunction;
|
|
@@ -48,6 +55,8 @@ export declare abstract class ReplicationService<T extends ServiceContext = Serv
|
|
|
48
55
|
* An event triggered before starting replication.
|
|
49
56
|
*/
|
|
50
57
|
readonly onBeforeReplicate: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(showMessage: boolean) => Promise<boolean>>;
|
|
58
|
+
/** Provider preparation which applies only to the central remote. */
|
|
59
|
+
readonly onPrepareCentralRemoteReplication: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(showMessage: boolean) => Promise<boolean>>;
|
|
51
60
|
/**
|
|
52
61
|
* Lightweight, repeatable policy checks shared by every replication entry point.
|
|
53
62
|
* Handlers must remain idempotent because a high-level replication may cross
|
|
@@ -55,11 +64,30 @@ export declare abstract class ReplicationService<T extends ServiceContext = Serv
|
|
|
55
64
|
*/
|
|
56
65
|
readonly onCheckReplicationReady: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(showMessage: boolean) => Promise<boolean>>;
|
|
57
66
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
67
|
+
* Evaluate the ordered readiness conditions for an operation.
|
|
68
|
+
*
|
|
69
|
+
* This method does not acquire a Replicator or begin activity accounting.
|
|
70
|
+
* Provider requirements select whether central-remote preparation applies.
|
|
71
|
+
*
|
|
72
|
+
* @param showMessage Whether condition-specific diagnostics may be prominent.
|
|
73
|
+
* @param readiness Provider-owned preparation requirements.
|
|
60
74
|
*/
|
|
61
|
-
isReplicationReady(showMessage?: boolean): Promise<boolean>;
|
|
62
|
-
onReplicationFailed: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(
|
|
75
|
+
isReplicationReady(showMessage?: boolean, readiness?: ReplicationReadinessRequirements): Promise<boolean>;
|
|
76
|
+
onReplicationFailed: import("../lib/HandlerUtils.js").BooleanMultipleHandlerFunction<(request: import("../../replication/ReplicatorProvider.js").ReplicationFailureRequest) => Promise<boolean>>;
|
|
77
|
+
replicateUserInitiated(request?: UserInitiatedOneShotRequest): Promise<ReplicationOutcome>;
|
|
78
|
+
replicateUnattended(request: UnattendedOneShotRequest): Promise<ReplicationOutcome>;
|
|
79
|
+
replicateUnattendedByEvent(request: UnattendedOneShotRequest): Promise<ReplicationOutcome>;
|
|
80
|
+
startContinuous(request: ContinuousReplicationRequest): Promise<ReplicationOutcome>;
|
|
81
|
+
/**
|
|
82
|
+
* Stop finite transfer work on the active provider without entering the
|
|
83
|
+
* replication readiness or activity accounting paths.
|
|
84
|
+
*
|
|
85
|
+
* The provider and replicator are obtained together so that a concurrent
|
|
86
|
+
* replacement cannot pair a capability from one provider with a
|
|
87
|
+
* replicator from another. Stopping is an operator action, not a failed
|
|
88
|
+
* replication, so it does not invoke failure-recovery handlers.
|
|
89
|
+
*/
|
|
90
|
+
stopActiveTransfer(): Promise<ReplicationOutcome>;
|
|
63
91
|
private performReplicationRequest;
|
|
64
92
|
/**
|
|
65
93
|
* Perform replication and handle a failed result.
|
|
@@ -80,13 +108,19 @@ export declare abstract class ReplicationService<T extends ServiceContext = Serv
|
|
|
80
108
|
/**
|
|
81
109
|
* Check if there is a connection failure with the remote database.
|
|
82
110
|
*/
|
|
83
|
-
readonly checkConnectionFailure: import("../lib/HandlerUtils.js").MultipleHandlerFunction<() => Promise<boolean | "CHECKAGAIN" | undefined>, unknown>;
|
|
111
|
+
readonly checkConnectionFailure: import("../lib/HandlerUtils.js").MultipleHandlerFunction<(failure: ReplicationAttemptFailure) => Promise<boolean | "CHECKAGAIN" | undefined>, unknown>;
|
|
84
112
|
databaseQueueCount: import("octagonal-wheels/dataobject/reactive_v2").ReactiveSource<number>;
|
|
85
113
|
storageApplyingCount: import("octagonal-wheels/dataobject/reactive_v2").ReactiveSource<number>;
|
|
86
114
|
replicationResultCount: import("octagonal-wheels/dataobject/reactive_v2").ReactiveSource<number>;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Dispatch one directional full transfer through one admitted publication.
|
|
117
|
+
*
|
|
118
|
+
* Each attempt owns a separate reservation. Compatibility recovery runs
|
|
119
|
+
* between those reservations, and a retry is admitted only when the exact
|
|
120
|
+
* first-attempt context remains active. Both attempts use one detached
|
|
121
|
+
* settings snapshot.
|
|
122
|
+
*/
|
|
123
|
+
private performDirectionalReplication;
|
|
90
124
|
replicateAllToRemote(showingNotice?: boolean): Promise<boolean>;
|
|
91
125
|
replicateAllFromRemote(showingNotice?: boolean): Promise<boolean>;
|
|
92
126
|
/**
|
|
@@ -103,6 +137,7 @@ export declare abstract class ReplicationService<T extends ServiceContext = Serv
|
|
|
103
137
|
* it is not a general application-readiness bypass.
|
|
104
138
|
*/
|
|
105
139
|
replicateAllFromRemoteForRebuild(showingNotice?: boolean): Promise<boolean>;
|
|
140
|
+
private getActiveReplicatorFor;
|
|
106
141
|
private _getReplicatorAndPerform;
|
|
107
142
|
markLocked(lockByClean?: boolean): Promise<void>;
|
|
108
143
|
markUnlocked(): Promise<void>;
|