@vrtmrz/livesync-commonlib 0.1.19-rc.0 → 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/README.md
CHANGED
|
@@ -28,6 +28,8 @@ The package is ESM-only and declares Node 20 or later. Browser entry points are
|
|
|
28
28
|
| `@vrtmrz/livesync-commonlib/context` | Instance-owned events, translation injection, and host-neutral standard-I/O types | Focused, package-tested pre-1.0 contract |
|
|
29
29
|
| `@vrtmrz/livesync-commonlib/browser` | Rooted File System Access API storage | Focused, package-tested pre-1.0 contract |
|
|
30
30
|
| `@vrtmrz/livesync-commonlib/node` | Rooted Node storage, Node standard I/O, and selected Node capabilities | Supported platform façade; keeps Node-only dependencies behind an explicit boundary |
|
|
31
|
+
| `@vrtmrz/livesync-commonlib/p2p` | Host-composed P2P service views and composition helpers | Focused, package-tested pre-1.0 contract with a temporary compatibility façade |
|
|
32
|
+
| `@vrtmrz/livesync-commonlib/replication` | Provider capabilities, interaction authority, and typed replication outcomes | Focused, package-tested pre-1.0 host-composition contract |
|
|
31
33
|
| `@vrtmrz/livesync-commonlib/rpc` | Existing LiveSync RPC and PouchDB bridge migration | Transitional only; not part of the stable Commonlib 1.0 contract |
|
|
32
34
|
| `@vrtmrz/livesync-commonlib/remote-configurations` | Multiple-remote profile creation, naming, and selection | Focused, package-tested pre-1.0 contract |
|
|
33
35
|
| `@vrtmrz/livesync-commonlib/settings` | New-Vault defaults, stored-setting fallbacks, and settings migration results | Focused, package-tested pre-1.0 contract |
|
|
@@ -40,6 +42,8 @@ The Node and browser entries are platform façades. They keep platform-specific
|
|
|
40
42
|
|
|
41
43
|
The RPC entry exists only while the maintained LiveSync P2P composition depends on the current implementation. Its draft wire protocol and PouchDB bridge are not stable Commonlib 1.0 contracts. RPC is planned to move to Fancy Kit as an independently owned rewrite; new Commonlib consumers should not adopt the transitional entry.
|
|
42
44
|
|
|
45
|
+
The P2P entry exposes eight focused views over one service owner, together with the maintained host-composition helpers. New consumers select only the lifecycle, peer-directory, peer-admission, targeted-transfer, change-relay, configuration-exchange, diagnostic, or connection-probe-admission view which they need. The composition result still carries a deprecated concrete Replicator façade while existing panes migrate; new consumers must not depend on that property or reach through it to a raw room or host.
|
|
46
|
+
|
|
43
47
|
## Host context and initialisation
|
|
44
48
|
|
|
45
49
|
One service context belongs to one independently composed Commonlib client. It owns that client's event hub and translator. Creating a context without options creates a fresh event hub and renders Commonlib's typed canonical English messages. A host may inject a translator to provide another language:
|
|
@@ -123,7 +127,7 @@ The package is currently an infrastructure and compatibility boundary. The conte
|
|
|
123
127
|
|
|
124
128
|
The accepted replacement direction is an asynchronously created file client with stable `list`, `get`, `put`, `delete`, `watch`, and `close` operations. The operation result, conflict, concurrency, watch checkpoint, retry, and error contracts still require focused decisions and tests before that client can be published.
|
|
125
129
|
|
|
126
|
-
Package developers should read [the developer guide](docs/development.md). The focused contracts are described in [the storage guide](docs/platform-storage.md), [the standard-I/O guide](docs/platform-standard-io.md), [the settings lifecycle guide](docs/settings-lifecycle.md), [the remote configuration profile guide](docs/remote-configurations.md),
|
|
130
|
+
Package developers should read [the developer guide](docs/development.md). The focused contracts are described in [the storage guide](docs/platform-storage.md), [the standard-I/O guide](docs/platform-standard-io.md), [the settings lifecycle guide](docs/settings-lifecycle.md), [the remote configuration profile guide](docs/remote-configurations.md), [the conflict-resolution guide](docs/conflict-resolution.md), and [the P2P transport lifecycle guide](docs/p2p-transport-lifecycle.md).
|
|
127
131
|
|
|
128
132
|
## Proven in maintained hosts
|
|
129
133
|
|
|
@@ -8,6 +8,8 @@ import { type P2PConnectionPath } from "./setting.const.js";
|
|
|
8
8
|
export declare function normaliseP2PMaxWirePayloadBytes(value: unknown): number;
|
|
9
9
|
/** Return a recognised P2P connection path, defaulting legacy values to automatic selection. */
|
|
10
10
|
export declare function normaliseP2PConnectionPath(value: unknown): P2PConnectionPath;
|
|
11
|
+
/** Split the stored comma-separated signalling relay list without changing its entries. */
|
|
12
|
+
export declare function splitP2PRelayUrls(value: string | undefined): string[];
|
|
11
13
|
/** Split the stored comma-separated TURN server list without changing its entries. */
|
|
12
14
|
export declare function splitP2PTurnServerUrls(value: string): string[];
|
|
13
15
|
/**
|
|
@@ -8,6 +8,10 @@ function normaliseP2PMaxWirePayloadBytes(value) {
|
|
|
8
8
|
function normaliseP2PConnectionPath(value) {
|
|
9
9
|
return value === P2PConnectionPaths.Relay ? P2PConnectionPaths.Relay : P2PConnectionPaths.Automatic;
|
|
10
10
|
}
|
|
11
|
+
function splitP2PRelayUrls(value) {
|
|
12
|
+
if (!value) return [];
|
|
13
|
+
return value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
14
|
+
}
|
|
11
15
|
function splitP2PTurnServerUrls(value) {
|
|
12
16
|
return value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
13
17
|
}
|
|
@@ -31,5 +35,6 @@ export {
|
|
|
31
35
|
isValidP2PTurnServerUrl,
|
|
32
36
|
normaliseP2PConnectionPath,
|
|
33
37
|
normaliseP2PMaxWirePayloadBytes,
|
|
38
|
+
splitP2PRelayUrls,
|
|
34
39
|
splitP2PTurnServerUrls
|
|
35
40
|
};
|
|
@@ -42,11 +42,20 @@ export type ServiceFeatureFunctionWithContext<T extends keyof ServiceHub, U exte
|
|
|
42
42
|
* @param featureFunction The feature function to be wrapped.
|
|
43
43
|
* @returns The same feature function with proper typing.
|
|
44
44
|
* @example
|
|
45
|
-
* const
|
|
46
|
-
*
|
|
45
|
+
* const useIndexFeature = createServiceFeature((host: NecessaryServices<"appLifecycle", "storageAccess">) => {
|
|
46
|
+
* const { appLifecycle } = host.services;
|
|
47
|
+
* const { storageAccess } = host.serviceModules;
|
|
48
|
+
*
|
|
49
|
+
* appLifecycle.onLoaded.addHandler(async () => {
|
|
50
|
+
* await storageAccess.getFileNames();
|
|
51
|
+
* return true;
|
|
52
|
+
* });
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* useIndexFeature({
|
|
56
|
+
* services: { context: serviceHub.context, appLifecycle: serviceHub.appLifecycle },
|
|
57
|
+
* serviceModules: { storageAccess },
|
|
47
58
|
* });
|
|
48
|
-
* const myFeature = myFeatureDef.bind(null, this); // <- `this` may `ObsidianLiveSyncPlugin` or a custom context object
|
|
49
|
-
* appLifecycle.onLayoutReady(myFeature);
|
|
50
59
|
*/
|
|
51
60
|
export declare function createServiceFeature<T extends keyof ServiceHub, U extends keyof ServiceModules, TR>(featureFunction: ServiceFeatureFunction<T, U, TR>): ServiceFeatureFunction<T, U, TR>;
|
|
52
61
|
type ContextFactory<T extends keyof ServiceHub, U extends keyof ServiceModules, C> = (host: NecessaryServices<T, U>) => ServiceFeatureContext<C>;
|
|
@@ -9,6 +9,9 @@ const EVENT_MISSING_CHUNKS = "missingChunks";
|
|
|
9
9
|
const EVENT_MISSING_CHUNK_REMOTE = "missingChunkRemote";
|
|
10
10
|
const EVENT_CHUNK_FETCHED = "chunkFetched";
|
|
11
11
|
const BATCH_SIZE = 100;
|
|
12
|
+
function canFetchRemoteChunks(replicator) {
|
|
13
|
+
return "fetchRemoteChunks" in replicator && typeof replicator.fetchRemoteChunks === "function";
|
|
14
|
+
}
|
|
12
15
|
class ChunkFetcher {
|
|
13
16
|
options;
|
|
14
17
|
get chunkManager() {
|
|
@@ -169,6 +172,13 @@ class ChunkFetcher {
|
|
|
169
172
|
Logger("No active replicator was found to request missing chunks.");
|
|
170
173
|
return;
|
|
171
174
|
}
|
|
175
|
+
if (!canFetchRemoteChunks(replicator)) {
|
|
176
|
+
Logger("The active replicator does not support fetching individual remote chunks.");
|
|
177
|
+
for (const chunkID of requestIDs) {
|
|
178
|
+
this.chunkManager.emitEvent(EVENT_MISSING_CHUNK_REMOTE, chunkID);
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
172
182
|
const fetched = await replicator.fetchRemoteChunks(requestIDs, false);
|
|
173
183
|
this.touchClaims(requestIDs);
|
|
174
184
|
if (!fetched) {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { ObsidianLiveSyncSettings } from "../common/types.js";
|
|
2
|
+
import type { ReplicatorInstance } from "../replication/ReplicatorInstance.js";
|
|
3
|
+
import type { DiagRTCPeerConnectionMetrics } from "../rpc/transports/DiagRTCPeerConnections.types.js";
|
|
4
|
+
import { LiveSyncTrysteroReplicator, type LiveSyncTrysteroReplicatorEnv } from "../replication/trystero/LiveSyncTrysteroReplicator.js";
|
|
5
|
+
import type { P2PReplicationResult } from "../replication/trystero/TrysteroReplicator.js";
|
|
6
|
+
import type { AcceptanceDecision, RevokeAcceptanceDecision } from "../replication/trystero/TrysteroReplicatorP2PServer.js";
|
|
7
|
+
import type { Advertisement } from "../replication/trystero/types.js";
|
|
8
|
+
import { type ReplicationOutcome } from "../replication/ReplicatorProvider.js";
|
|
9
|
+
import type { P2PConnectionProbeAdmission } from "../replication/trystero/P2PConnectionProbeAdmission.js";
|
|
10
|
+
export { ACTIVE_P2P_RELAY_BINDING_CONFLICT } from "../replication/trystero/P2PConnectionProbeAdmission.js";
|
|
11
|
+
export type { P2PConnectionProbeAdmission, P2PConnectionProbeAdmissionResult, P2PConnectionProbeSettings, } from "../replication/trystero/P2PConnectionProbeAdmission.js";
|
|
12
|
+
/** Candidate details projected from browser RTC statistics. */
|
|
13
|
+
export interface P2PCandidateSummary {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly candidateType: string;
|
|
16
|
+
readonly protocol: string;
|
|
17
|
+
readonly relayProtocol: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Current peer-connection metrics without a raw RTC report or peer handle.
|
|
21
|
+
*
|
|
22
|
+
* Missing numeric values use the existing `unknown` sentinel so maintained
|
|
23
|
+
* diagnostic output can distinguish absence from zero.
|
|
24
|
+
*/
|
|
25
|
+
export interface P2PPeerConnectionMetrics {
|
|
26
|
+
readonly selectedPairPresent: boolean;
|
|
27
|
+
readonly selectedPairId: string;
|
|
28
|
+
readonly state: string;
|
|
29
|
+
readonly currentRoundTripTime: number | "unknown";
|
|
30
|
+
readonly totalRoundTripTime: number | "unknown";
|
|
31
|
+
readonly requestsSent: number | "unknown";
|
|
32
|
+
readonly responsesReceived: number | "unknown";
|
|
33
|
+
readonly packetsDiscardedOnSend: number | "unknown";
|
|
34
|
+
readonly bytesSent: number | "unknown";
|
|
35
|
+
readonly bytesReceived: number | "unknown";
|
|
36
|
+
readonly localCandidate?: P2PCandidateSummary;
|
|
37
|
+
readonly remoteCandidate?: P2PCandidateSummary;
|
|
38
|
+
}
|
|
39
|
+
/** Project low-level RTC statistics into the focused P2P diagnostic contract. */
|
|
40
|
+
export declare function projectP2PPeerConnectionMetrics(metrics: DiagRTCPeerConnectionMetrics): P2PPeerConnectionMetrics;
|
|
41
|
+
/** Observes and explicitly controls the room transport owned by the P2P service. */
|
|
42
|
+
export interface P2PTransportLifecycle {
|
|
43
|
+
readonly isConnected: boolean;
|
|
44
|
+
connect(): Promise<void>;
|
|
45
|
+
disconnect(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/** Supplies a current peer snapshot without exposing the underlying room. */
|
|
48
|
+
export interface P2PPeerDirectory {
|
|
49
|
+
getPeers(): readonly Advertisement[];
|
|
50
|
+
}
|
|
51
|
+
/** Administers temporary and persisted peer acceptance decisions. */
|
|
52
|
+
export interface P2PPeerAdmission {
|
|
53
|
+
makeDecision(decision: AcceptanceDecision): Promise<void>;
|
|
54
|
+
revokeDecision(decision: RevokeAcceptanceDecision): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/** Performs finite transfer operations against an explicitly selected peer. */
|
|
57
|
+
export interface P2PTargetedTransfer {
|
|
58
|
+
pullFromPeer(peerId: string, options?: {
|
|
59
|
+
readonly showNotice?: boolean;
|
|
60
|
+
readonly skipOrdinaryReplicationPolicy?: boolean;
|
|
61
|
+
}): Promise<P2PReplicationResult>;
|
|
62
|
+
requestPushToPeer(peerId: string): Promise<P2PReplicationResult>;
|
|
63
|
+
synchroniseWithPeer(peerId: string, showNotice?: boolean): Promise<P2PReplicationResult>;
|
|
64
|
+
/** Synchronise the persisted target set without interactive peer selection. */
|
|
65
|
+
synchroniseConfiguredTargets(): Promise<ReplicationOutcome>;
|
|
66
|
+
}
|
|
67
|
+
/** Controls watch and broadcast behaviour attached to the current room session. */
|
|
68
|
+
export interface P2PChangeRelay {
|
|
69
|
+
watchPeer(peerId: string): void;
|
|
70
|
+
unwatchPeer(peerId: string): void;
|
|
71
|
+
enableBroadcastChanges(): void;
|
|
72
|
+
disableBroadcastChanges(): void;
|
|
73
|
+
}
|
|
74
|
+
/** Exchanges configuration with one explicitly selected peer. */
|
|
75
|
+
export interface P2PConfigurationExchange {
|
|
76
|
+
getRemoteConfiguration(peerId: string): Promise<ObsidianLiveSyncSettings | false>;
|
|
77
|
+
}
|
|
78
|
+
/** Supplies transport diagnostics without exposing a room or peer connection. */
|
|
79
|
+
export interface P2PDiagnostics {
|
|
80
|
+
requestStatus(): void;
|
|
81
|
+
getPeerConnectionMetrics(peerId: string): Promise<P2PPeerConnectionMetrics | undefined>;
|
|
82
|
+
}
|
|
83
|
+
/** Focused contract views backed by one private P2P service context. */
|
|
84
|
+
export interface P2PServiceViews {
|
|
85
|
+
readonly transportLifecycle: P2PTransportLifecycle;
|
|
86
|
+
readonly connectionProbe: P2PConnectionProbeAdmission;
|
|
87
|
+
readonly peerDirectory: P2PPeerDirectory;
|
|
88
|
+
readonly peerAdmission: P2PPeerAdmission;
|
|
89
|
+
readonly targetedTransfer: P2PTargetedTransfer;
|
|
90
|
+
readonly changeRelay: P2PChangeRelay;
|
|
91
|
+
readonly configurationExchange: P2PConfigurationExchange;
|
|
92
|
+
readonly diagnostics: P2PDiagnostics;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Host-only lifecycle operations which are deliberately absent from ordinary
|
|
96
|
+
* P2P capability views.
|
|
97
|
+
*/
|
|
98
|
+
export interface P2PServiceLifecycle {
|
|
99
|
+
requestStatus(): void;
|
|
100
|
+
openAfterDatabaseRebuild(): Promise<void>;
|
|
101
|
+
closeForLifecycle(): Promise<void>;
|
|
102
|
+
/** Reconcile the automatic room demand; the room owner also reapplies current room-local policy. */
|
|
103
|
+
reconcileAutoStart(settings: Pick<ObsidianLiveSyncSettings, "P2P_Enabled" | "P2P_AutoStart">): Promise<void>;
|
|
104
|
+
scheduleAutoStart(delayMs?: number): void;
|
|
105
|
+
}
|
|
106
|
+
/** Host composition result. Ordinary consumers receive only `views`. */
|
|
107
|
+
export interface P2PServiceComposition {
|
|
108
|
+
/** Deprecated compatibility surface for consumers which still require it. */
|
|
109
|
+
readonly compatibilityReplicator: LiveSyncTrysteroReplicator;
|
|
110
|
+
/** Stable capability projections over the private service context. */
|
|
111
|
+
readonly views: P2PServiceViews;
|
|
112
|
+
/** Host lifecycle operations which are not ordinary P2P capabilities. */
|
|
113
|
+
readonly lifecycle: P2PServiceLifecycle;
|
|
114
|
+
/** Create a non-owning adapter for the active Replicator selection. */
|
|
115
|
+
createActiveReplicator(): ReplicatorInstance;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compose stable capability views over one private P2P service context.
|
|
119
|
+
*
|
|
120
|
+
* `P2PRoomSessionOwner` remains the resource owner. The returned composition
|
|
121
|
+
* object is host wiring, not a capability façade for ordinary consumers.
|
|
122
|
+
*/
|
|
123
|
+
export declare function createP2PService(env: LiveSyncTrysteroReplicatorEnv): P2PServiceComposition;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { getPeerConnectionStats } from "../rpc/transports/DiagRTCPeerConnections.utils.js";
|
|
2
|
+
import {
|
|
3
|
+
LiveSyncTrysteroReplicator
|
|
4
|
+
} from "../replication/trystero/LiveSyncTrysteroReplicator.js";
|
|
5
|
+
import { P2PRoomSessionOwner } from "../replication/trystero/P2PRoomSessionOwner.js";
|
|
6
|
+
import { compatGlobal } from "../common/coreEnvFunctions.js";
|
|
7
|
+
import {
|
|
8
|
+
REPLICATION_CANCELLED,
|
|
9
|
+
REPLICATION_COMPLETED,
|
|
10
|
+
replicationBlocked,
|
|
11
|
+
replicationFailed
|
|
12
|
+
} from "../replication/ReplicatorProvider.js";
|
|
13
|
+
import { ACTIVE_P2P_RELAY_BINDING_CONFLICT } from "../replication/trystero/P2PConnectionProbeAdmission.js";
|
|
14
|
+
function getReportValue(report, key) {
|
|
15
|
+
const value = report?.[key];
|
|
16
|
+
return typeof value === "string" || typeof value === "number" ? String(value) : "unknown";
|
|
17
|
+
}
|
|
18
|
+
function isUnknownRecord(value) {
|
|
19
|
+
return typeof value === "object" && value !== null;
|
|
20
|
+
}
|
|
21
|
+
function projectCandidate(reports, candidateId) {
|
|
22
|
+
if (candidateId === "unknown") return void 0;
|
|
23
|
+
const report = reports.filter(isUnknownRecord).find((value) => value.id === candidateId);
|
|
24
|
+
if (!report) return void 0;
|
|
25
|
+
return {
|
|
26
|
+
id: candidateId,
|
|
27
|
+
candidateType: getReportValue(report, "candidateType"),
|
|
28
|
+
protocol: getReportValue(report, "protocol"),
|
|
29
|
+
relayProtocol: getReportValue(report, "relayProtocol")
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function projectP2PPeerConnectionMetrics(metrics) {
|
|
33
|
+
return {
|
|
34
|
+
selectedPairPresent: metrics.selectedPair !== void 0,
|
|
35
|
+
selectedPairId: metrics.selectedPairId,
|
|
36
|
+
state: metrics.state,
|
|
37
|
+
currentRoundTripTime: metrics.currentRoundTripTime,
|
|
38
|
+
totalRoundTripTime: metrics.totalRoundTripTime,
|
|
39
|
+
requestsSent: metrics.requestsSent,
|
|
40
|
+
responsesReceived: metrics.responsesReceived,
|
|
41
|
+
packetsDiscardedOnSend: metrics.packetsDiscardedOnSend,
|
|
42
|
+
bytesSent: metrics.bytesSent,
|
|
43
|
+
bytesReceived: metrics.bytesReceived,
|
|
44
|
+
localCandidate: projectCandidate(metrics.reports, metrics.localCandidateId),
|
|
45
|
+
remoteCandidate: projectCandidate(metrics.reports, metrics.remoteCandidateId)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function connect(owner, state) {
|
|
49
|
+
state.explicitDisconnectVeto = false;
|
|
50
|
+
state.lifecycleClosed = false;
|
|
51
|
+
return owner.setPersistentDemand("explicit", true);
|
|
52
|
+
}
|
|
53
|
+
function clearDelayedAutoStart(state) {
|
|
54
|
+
if (state.delayedAutoStart !== void 0) {
|
|
55
|
+
compatGlobal.clearTimeout(state.delayedAutoStart);
|
|
56
|
+
state.delayedAutoStart = void 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function cancelDelayedAutomation(owner, state) {
|
|
60
|
+
state.lifecycleGeneration += 1;
|
|
61
|
+
owner.beginAutomationLifecycle();
|
|
62
|
+
clearDelayedAutoStart(state);
|
|
63
|
+
}
|
|
64
|
+
function disconnect(owner, state) {
|
|
65
|
+
state.explicitDisconnectVeto = true;
|
|
66
|
+
cancelDelayedAutomation(owner, state);
|
|
67
|
+
return owner.close();
|
|
68
|
+
}
|
|
69
|
+
function openAfterDatabaseRebuild(owner, state) {
|
|
70
|
+
state.lifecycleClosed = false;
|
|
71
|
+
return owner.setPersistentDemand("rebuild-continuation", true);
|
|
72
|
+
}
|
|
73
|
+
function closeForLifecycle(owner, state) {
|
|
74
|
+
state.lifecycleClosed = true;
|
|
75
|
+
cancelDelayedAutomation(owner, state);
|
|
76
|
+
return owner.close();
|
|
77
|
+
}
|
|
78
|
+
function reconcileAutoStart(context, settings) {
|
|
79
|
+
if (!settings.P2P_Enabled || !settings.P2P_AutoStart) {
|
|
80
|
+
return context.roomSessionOwner.setPersistentDemand("automatic", false);
|
|
81
|
+
}
|
|
82
|
+
if (context.state.explicitDisconnectVeto || context.state.lifecycleClosed) return Promise.resolve();
|
|
83
|
+
return context.roomSessionOwner.setPersistentDemand("automatic", true);
|
|
84
|
+
}
|
|
85
|
+
function scheduleAutoStart(context, delayMs = 100) {
|
|
86
|
+
clearDelayedAutoStart(context.state);
|
|
87
|
+
context.state.lifecycleClosed = false;
|
|
88
|
+
const generation = context.state.lifecycleGeneration;
|
|
89
|
+
context.state.delayedAutoStart = compatGlobal.setTimeout(() => {
|
|
90
|
+
context.state.delayedAutoStart = void 0;
|
|
91
|
+
if (generation !== context.state.lifecycleGeneration) return;
|
|
92
|
+
void reconcileAutoStart(context, context.env.services.setting.currentSettings());
|
|
93
|
+
}, delayMs);
|
|
94
|
+
}
|
|
95
|
+
function runWithFiniteRoomDemand(context, task) {
|
|
96
|
+
if (context.state.explicitDisconnectVeto) {
|
|
97
|
+
return Promise.reject(new Error("The P2P room was explicitly disconnected."));
|
|
98
|
+
}
|
|
99
|
+
if (context.state.lifecycleClosed) return Promise.reject(new Error("The P2P room is not ready."));
|
|
100
|
+
return context.roomSessionOwner.runWithFiniteDemand(task);
|
|
101
|
+
}
|
|
102
|
+
async function synchroniseConfiguredTargets(context) {
|
|
103
|
+
if (context.state.explicitDisconnectVeto || context.state.lifecycleClosed) return replicationBlocked("not-ready");
|
|
104
|
+
try {
|
|
105
|
+
const result = await context.roomSessionOwner.runWithFiniteDemand(
|
|
106
|
+
(session) => session.runFiniteOperation((signal) => session.replicator.replicateFromCommand(false, void 0, signal))
|
|
107
|
+
);
|
|
108
|
+
if (result.status === "completed") return REPLICATION_COMPLETED;
|
|
109
|
+
if (result.status === "cancelled") return REPLICATION_CANCELLED;
|
|
110
|
+
if (result.status === "blocked") return replicationBlocked(result.reason);
|
|
111
|
+
return {
|
|
112
|
+
status: "partial",
|
|
113
|
+
detail: { kind: "p2p-configured-targets", targets: result.targets }
|
|
114
|
+
};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return replicationFailed(error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function pullFromPeer(context, peerId, options = {}) {
|
|
120
|
+
return runWithFiniteRoomDemand(
|
|
121
|
+
context,
|
|
122
|
+
(session) => context.env.services.replicator.runFiniteReplicationActivity(
|
|
123
|
+
() => session.replicator.replicateFrom(
|
|
124
|
+
peerId,
|
|
125
|
+
options.showNotice ?? false,
|
|
126
|
+
false,
|
|
127
|
+
options.skipOrdinaryReplicationPolicy ?? false
|
|
128
|
+
),
|
|
129
|
+
{ label: "replication" }
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
function requestPushToPeer(context, peerId) {
|
|
134
|
+
return runWithFiniteRoomDemand(
|
|
135
|
+
context,
|
|
136
|
+
(session) => context.env.services.replicator.runBoundedRemoteActivity(
|
|
137
|
+
() => session.replicator.requestSynchroniseToPeer(peerId),
|
|
138
|
+
{ label: "replication" }
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
function synchroniseWithPeer(context, peerId, showNotice = false) {
|
|
143
|
+
return runWithFiniteRoomDemand(
|
|
144
|
+
context,
|
|
145
|
+
(session) => context.env.services.replicator.runFiniteReplicationActivity(
|
|
146
|
+
() => session.replicator.sync(peerId, showNotice),
|
|
147
|
+
{ label: "replication" }
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
async function getPeerConnectionMetrics(context, peerId) {
|
|
152
|
+
const peerConnection = context.compatibilityReplicator.rawHost?.room?.getPeers()[peerId];
|
|
153
|
+
if (!peerConnection) return void 0;
|
|
154
|
+
const metrics = await getPeerConnectionStats(`p2p-service-${peerId}`, peerConnection);
|
|
155
|
+
return metrics ? projectP2PPeerConnectionMetrics(metrics) : void 0;
|
|
156
|
+
}
|
|
157
|
+
function createCompatibilitySessionAccess(owner, state) {
|
|
158
|
+
return {
|
|
159
|
+
get currentSession() {
|
|
160
|
+
return owner.currentSession;
|
|
161
|
+
},
|
|
162
|
+
get isConnected() {
|
|
163
|
+
return owner.isConnected;
|
|
164
|
+
},
|
|
165
|
+
cancelActiveTransfers: () => owner.cancelActiveTransfers(),
|
|
166
|
+
open: () => connect(owner, state),
|
|
167
|
+
close: () => disconnect(owner, state)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function createServiceViews(context) {
|
|
171
|
+
const transportLifecycle = {
|
|
172
|
+
get isConnected() {
|
|
173
|
+
return context.roomSessionOwner.isConnected;
|
|
174
|
+
},
|
|
175
|
+
connect: () => connect(context.roomSessionOwner, context.state),
|
|
176
|
+
disconnect: () => disconnect(context.roomSessionOwner, context.state)
|
|
177
|
+
};
|
|
178
|
+
const connectionProbe = {
|
|
179
|
+
run: (trialSettings, runOwnedTrial) => context.roomSessionOwner.runConnectionProbe(trialSettings, runOwnedTrial)
|
|
180
|
+
};
|
|
181
|
+
const peerDirectory = {
|
|
182
|
+
getPeers: () => context.compatibilityReplicator.knownAdvertisements
|
|
183
|
+
};
|
|
184
|
+
const peerAdmission = {
|
|
185
|
+
makeDecision: (decision) => context.compatibilityReplicator.makeDecision(decision),
|
|
186
|
+
revokeDecision: (decision) => context.compatibilityReplicator.revokeDecision(decision)
|
|
187
|
+
};
|
|
188
|
+
const targetedTransfer = {
|
|
189
|
+
pullFromPeer: (peerId, options) => pullFromPeer(context, peerId, options),
|
|
190
|
+
requestPushToPeer: (peerId) => requestPushToPeer(context, peerId),
|
|
191
|
+
synchroniseWithPeer: (peerId, showNotice) => synchroniseWithPeer(context, peerId, showNotice),
|
|
192
|
+
synchroniseConfiguredTargets: () => synchroniseConfiguredTargets(context)
|
|
193
|
+
};
|
|
194
|
+
const changeRelay = {
|
|
195
|
+
watchPeer: (peerId) => context.compatibilityReplicator.watchPeer(peerId),
|
|
196
|
+
unwatchPeer: (peerId) => context.compatibilityReplicator.unwatchPeer(peerId),
|
|
197
|
+
enableBroadcastChanges: () => context.compatibilityReplicator.enableBroadcastChanges(),
|
|
198
|
+
disableBroadcastChanges: () => context.compatibilityReplicator.disableBroadcastChanges()
|
|
199
|
+
};
|
|
200
|
+
const configurationExchange = {
|
|
201
|
+
getRemoteConfiguration: (peerId) => context.compatibilityReplicator.getRemoteConfig(peerId)
|
|
202
|
+
};
|
|
203
|
+
const diagnostics = {
|
|
204
|
+
requestStatus: () => context.compatibilityReplicator.requestStatus(),
|
|
205
|
+
getPeerConnectionMetrics: (peerId) => getPeerConnectionMetrics(context, peerId)
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
transportLifecycle,
|
|
209
|
+
connectionProbe,
|
|
210
|
+
peerDirectory,
|
|
211
|
+
peerAdmission,
|
|
212
|
+
targetedTransfer,
|
|
213
|
+
changeRelay,
|
|
214
|
+
configurationExchange,
|
|
215
|
+
diagnostics
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function createServiceLifecycle(context) {
|
|
219
|
+
return {
|
|
220
|
+
requestStatus: () => context.compatibilityReplicator.requestStatus(),
|
|
221
|
+
openAfterDatabaseRebuild: () => openAfterDatabaseRebuild(context.roomSessionOwner, context.state),
|
|
222
|
+
closeForLifecycle: () => closeForLifecycle(context.roomSessionOwner, context.state),
|
|
223
|
+
reconcileAutoStart: (settings) => reconcileAutoStart(context, settings),
|
|
224
|
+
scheduleAutoStart: (delayMs) => scheduleAutoStart(context, delayMs)
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function createP2PService(env) {
|
|
228
|
+
const roomSessionOwner = new P2PRoomSessionOwner(env);
|
|
229
|
+
const state = {
|
|
230
|
+
explicitDisconnectVeto: false,
|
|
231
|
+
lifecycleClosed: false,
|
|
232
|
+
lifecycleGeneration: 0,
|
|
233
|
+
delayedAutoStart: void 0
|
|
234
|
+
};
|
|
235
|
+
const compatibilityReplicator = new LiveSyncTrysteroReplicator(
|
|
236
|
+
env,
|
|
237
|
+
createCompatibilitySessionAccess(roomSessionOwner, state)
|
|
238
|
+
);
|
|
239
|
+
const context = { env, roomSessionOwner, compatibilityReplicator, state };
|
|
240
|
+
return {
|
|
241
|
+
compatibilityReplicator,
|
|
242
|
+
views: createServiceViews(context),
|
|
243
|
+
lifecycle: createServiceLifecycle(context),
|
|
244
|
+
createActiveReplicator: () => new P2PActiveReplicatorAdapter(compatibilityReplicator)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
class P2PActiveReplicatorAdapter {
|
|
248
|
+
constructor(delegate) {
|
|
249
|
+
this.delegate = delegate;
|
|
250
|
+
}
|
|
251
|
+
delegate;
|
|
252
|
+
initializeDatabaseForReplication() {
|
|
253
|
+
return this.delegate.initializeDatabaseForReplication();
|
|
254
|
+
}
|
|
255
|
+
terminateSync() {
|
|
256
|
+
this.delegate.terminateSync();
|
|
257
|
+
}
|
|
258
|
+
openReplication(setting, keepAlive, showResult, ignoreCleanLock) {
|
|
259
|
+
return this.delegate.openReplication(setting, keepAlive, showResult, ignoreCleanLock);
|
|
260
|
+
}
|
|
261
|
+
replicateAllFromServer(setting, showingNotice) {
|
|
262
|
+
return this.delegate.replicateAllFromServer(setting, showingNotice);
|
|
263
|
+
}
|
|
264
|
+
closeReplication() {
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
export {
|
|
268
|
+
ACTIVE_P2P_RELAY_BINDING_CONFLICT,
|
|
269
|
+
createP2PService,
|
|
270
|
+
projectP2PPeerConnectionMetrics
|
|
271
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { P2PCandidateSummary, P2PChangeRelay, P2PConfigurationExchange, P2PDiagnostics, P2PPeerConnectionMetrics, P2PPeerAdmission, P2PPeerDirectory, P2PServiceViews, P2PTargetedTransfer, P2PTransportLifecycle, P2PConnectionProbeAdmission, P2PConnectionProbeAdmissionResult, P2PConnectionProbeSettings, } from "./P2PService.js";
|
|
2
|
+
export { ACTIVE_P2P_RELAY_BINDING_CONFLICT } from "./P2PService.js";
|
|
3
|
+
export { useP2PReplicatorCommands } from "../replication/trystero/useP2PReplicatorCommands.js";
|
|
4
|
+
export { useP2PReplicatorFeature } from "../replication/trystero/useP2PReplicatorFeature.js";
|
|
5
|
+
export type { OpenRebuildUIFactory, OpenReplicationUIFactory, } from "../replication/trystero/useP2PReplicatorFeature.js";
|
|
6
|
+
export type { UseP2PReplicatorResult } from "../replication/trystero/UseP2PReplicatorResult.js";
|
|
7
|
+
export type { P2PReplicationResult } from "../replication/trystero/TrysteroReplicator.js";
|
|
8
|
+
export type { Advertisement } from "../replication/trystero/types.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ACTIVE_P2P_RELAY_BINDING_CONFLICT } from "./P2PService.js";
|
|
2
|
+
import { useP2PReplicatorCommands } from "../replication/trystero/useP2PReplicatorCommands.js";
|
|
3
|
+
import { useP2PReplicatorFeature } from "../replication/trystero/useP2PReplicatorFeature.js";
|
|
4
|
+
export {
|
|
5
|
+
ACTIVE_P2P_RELAY_BINDING_CONFLICT,
|
|
6
|
+
useP2PReplicatorCommands,
|
|
7
|
+
useP2PReplicatorFeature
|
|
8
|
+
};
|
|
@@ -49,12 +49,15 @@ export declare class LiveSyncLocalDB {
|
|
|
49
49
|
refreshSettings(): void;
|
|
50
50
|
offRemoteChunkFetchedHandler?: ReturnType<LiveSyncEventHub["onEvent"]>;
|
|
51
51
|
private databaseCloseCleanup?;
|
|
52
|
+
/** Share owner retirement across every close path for one physical database lifetime. */
|
|
53
|
+
private activeReplicationClose?;
|
|
52
54
|
private databaseUnloadNotification?;
|
|
53
55
|
constructor(dbname: string, env: LiveSyncLocalDBEnv);
|
|
54
56
|
/** Notify dependent services at most once for each physical database lifecycle. */
|
|
55
57
|
private notifyDatabaseUnload;
|
|
56
58
|
onunload(): void;
|
|
57
59
|
close(): Promise<void>;
|
|
60
|
+
private closeActiveReplication;
|
|
58
61
|
private teardownDatabaseDependencies;
|
|
59
62
|
private rollbackFailedInitialisation;
|
|
60
63
|
onNewLeaf(chunk: EntryLeaf): void;
|
|
@@ -44,6 +44,8 @@ class LiveSyncLocalDB {
|
|
|
44
44
|
}
|
|
45
45
|
offRemoteChunkFetchedHandler;
|
|
46
46
|
databaseCloseCleanup;
|
|
47
|
+
/** Share owner retirement across every close path for one physical database lifetime. */
|
|
48
|
+
activeReplicationClose;
|
|
47
49
|
databaseUnloadNotification;
|
|
48
50
|
constructor(dbname, env) {
|
|
49
51
|
this.auth = {
|
|
@@ -72,8 +74,10 @@ class LiveSyncLocalDB {
|
|
|
72
74
|
this.isReady = false;
|
|
73
75
|
this.offRemoteChunkFetchedHandler?.();
|
|
74
76
|
this.offRemoteChunkFetchedHandler = void 0;
|
|
77
|
+
await this.closeActiveReplication();
|
|
75
78
|
if (this.localDatabase != null) {
|
|
76
79
|
const database = this.localDatabase;
|
|
80
|
+
await this.env.services.databaseEvents.onCloseDatabase(this);
|
|
77
81
|
await database.close();
|
|
78
82
|
if (this.databaseCloseCleanup?.database === database) {
|
|
79
83
|
await this.databaseCloseCleanup.promise;
|
|
@@ -85,9 +89,24 @@ class LiveSyncLocalDB {
|
|
|
85
89
|
this._managers = void 0;
|
|
86
90
|
await this.notifyDatabaseUnload();
|
|
87
91
|
}
|
|
92
|
+
async closeActiveReplication() {
|
|
93
|
+
if (this.activeReplicationClose) {
|
|
94
|
+
return await this.activeReplicationClose;
|
|
95
|
+
}
|
|
96
|
+
const closeOperation = this.env.services.replicator.onCloseActiveReplication();
|
|
97
|
+
this.activeReplicationClose = closeOperation;
|
|
98
|
+
try {
|
|
99
|
+
return await closeOperation;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (this.activeReplicationClose === closeOperation) {
|
|
102
|
+
this.activeReplicationClose = void 0;
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
88
107
|
async teardownDatabaseDependencies(managers) {
|
|
89
108
|
try {
|
|
90
|
-
await
|
|
109
|
+
await this.closeActiveReplication();
|
|
91
110
|
} catch (error) {
|
|
92
111
|
this._log("Failed to close replication while tearing down the local database.", LOG_LEVEL_VERBOSE);
|
|
93
112
|
this._log(error, LOG_LEVEL_VERBOSE);
|
|
@@ -147,6 +166,7 @@ class LiveSyncLocalDB {
|
|
|
147
166
|
deterministic_revs: true
|
|
148
167
|
}
|
|
149
168
|
);
|
|
169
|
+
this.activeReplicationClose = void 0;
|
|
150
170
|
this.databaseUnloadNotification = void 0;
|
|
151
171
|
const manager = new LiveSyncManagers({
|
|
152
172
|
database: this.localDatabase,
|
|
@@ -292,12 +312,12 @@ class LiveSyncLocalDB {
|
|
|
292
312
|
}
|
|
293
313
|
async resetDatabase() {
|
|
294
314
|
this.isReady = false;
|
|
295
|
-
await this.
|
|
296
|
-
this.env.services.replicator.getActiveReplicator()?.closeReplication();
|
|
315
|
+
await this.closeActiveReplication();
|
|
297
316
|
if (!await this.env.services.databaseEvents.onResetDatabase(this)) {
|
|
298
317
|
Logger("Database reset has been prevented or failed on some modules.", LOG_LEVEL_NOTICE);
|
|
299
318
|
return false;
|
|
300
319
|
}
|
|
320
|
+
await this.managers.teardownManagers();
|
|
301
321
|
Logger("Database closed for reset Database.");
|
|
302
322
|
await this.localDatabase.destroy();
|
|
303
323
|
this.localDatabase = null;
|