@vrtmrz/livesync-commonlib 0.1.25 → 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/interfaces/DatabaseFileAccess.d.ts +31 -3
- package/dist/interfaces/FileReflectionProvenance.d.ts +2 -2
- package/dist/managers/EntryManager/EntryManager.d.ts +4 -2
- package/dist/managers/EntryManager/EntryManager.js +9 -4
- package/dist/managers/EntryManager/EntryManagerImpls.d.ts +12 -4
- package/dist/managers/EntryManager/EntryManagerImpls.js +27 -9
- package/dist/pouchdb/LiveSyncLocalDB.d.ts +4 -2
- package/dist/pouchdb/LiveSyncLocalDB.js +8 -4
- 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/dist/serviceModules/ServiceDatabaseFileAccessBase.d.ts +26 -2
- package/dist/serviceModules/ServiceDatabaseFileAccessBase.js +57 -5
- package/dist/serviceModules/ServiceFileHandlerBase.d.ts +119 -1
- package/dist/serviceModules/ServiceFileHandlerBase.js +450 -70
- package/docs/conflict-resolution.md +28 -15
- package/docs/proven-in-use.md +3 -1
- package/package.json +1 -1
|
@@ -2,7 +2,10 @@ import type { FilePathWithPrefix, LoadedEntry, MetaEntry, UXFileInfo, UXFileInfo
|
|
|
2
2
|
export interface DatabaseFileAccess {
|
|
3
3
|
delete: (file: UXFileInfoStub | FilePathWithPrefix, rev?: string) => Promise<boolean>;
|
|
4
4
|
store: (file: UXFileInfo, force?: boolean, skipCheck?: boolean) => Promise<boolean>;
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Store a child of the exact supplied revision, even after that branch has advanced.
|
|
7
|
+
* An undefined base uses the current winner. Return the created revision, or `false`.
|
|
8
|
+
*/
|
|
6
9
|
storeWithBaseRevision: (file: UXFileInfo, baseRevision: string | undefined, skipCheck?: boolean) => Promise<string | false>;
|
|
7
10
|
/**
|
|
8
11
|
* Store a file as a child of an exact revision only while that revision remains a live leaf.
|
|
@@ -14,8 +17,18 @@ export interface DatabaseFileAccess {
|
|
|
14
17
|
* write without creating a Metadata successor.
|
|
15
18
|
*/
|
|
16
19
|
storeWithLiveBaseRevision: (file: UXFileInfo, baseRevision: string, skipCheck?: boolean) => Promise<string | false>;
|
|
20
|
+
/**
|
|
21
|
+
* Preserve content of unknown ancestry as a fresh parentless revision of the same document.
|
|
22
|
+
* Existing branches remain intact; no historical byte match is used as the parent.
|
|
23
|
+
* Return the created revision, or `false` when no Metadata revision was stored.
|
|
24
|
+
*/
|
|
25
|
+
storeIndependentRevision: (file: UXFileInfo, skipCheck?: boolean) => Promise<string | false>;
|
|
17
26
|
storeAsConflictedRevision: (file: UXFileInfo, currentRev: string, skipCheck?: boolean) => Promise<boolean>;
|
|
18
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Store a sibling of `currentRev` below its parent and return the created revision.
|
|
29
|
+
* Return `false` if that parent is unavailable. Use `storeIndependentRevision`
|
|
30
|
+
* when the storage content has no known ancestor.
|
|
31
|
+
*/
|
|
19
32
|
storeAsConflictedRevisionWithResult: (file: UXFileInfo, currentRev: string, skipCheck?: boolean) => Promise<string | false>;
|
|
20
33
|
/** Store a user deletion as a visible logical-deletion child of an exact revision. */
|
|
21
34
|
storeDeletionWithBaseRevision: (file: UXFileInfoStub | FilePathWithPrefix, baseRevision: string) => Promise<string | false>;
|
|
@@ -24,9 +37,24 @@ export interface DatabaseFileAccess {
|
|
|
24
37
|
hasContentInRevisionHistory: (file: UXFileInfoStub | FilePathWithPrefix, content: string | string[] | Blob | ArrayBuffer, currentRev?: string) => Promise<boolean>;
|
|
25
38
|
/** Return every available revision whose content exactly matches the supplied bytes. */
|
|
26
39
|
findContentRevisions: (file: UXFileInfoStub | FilePathWithPrefix, content: string | string[] | Blob | ArrayBuffer, currentRev?: string) => Promise<string[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Return current, non-deleted leaf revisions whose decoded content matches the supplied bytes.
|
|
42
|
+
* Include the winning and conflicting leaves, but never their ancestors. Read only locally
|
|
43
|
+
* available Chunks, without requesting remote data or waiting for missing Chunks to arrive.
|
|
44
|
+
*
|
|
45
|
+
* A unique match can recover provenance; several matches avoid a duplicate write but leave
|
|
46
|
+
* its origin ambiguous. An empty result means no match was confirmed, including when data
|
|
47
|
+
* could not be read. This read neither records provenance nor reserves a leaf for a later write.
|
|
48
|
+
*/
|
|
49
|
+
findLiveContentRevisions: (file: UXFileInfoStub | FilePathWithPrefix, content: string | string[] | Blob | ArrayBuffer) => Promise<string[]>;
|
|
27
50
|
fetch: (file: UXFileInfoStub | FilePathWithPrefix, rev?: string, waitForReady?: boolean, skipCheck?: boolean) => Promise<UXFileInfo | false>;
|
|
28
51
|
fetchEntryFromMeta: (meta: MetaEntry, waitForReady?: boolean, skipCheck?: boolean) => Promise<LoadedEntry | false>;
|
|
29
52
|
fetchEntryMeta: (file: UXFileInfoStub | FilePathWithPrefix, rev?: string, skipCheck?: boolean) => Promise<MetaEntry | false>;
|
|
30
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Load a revision's content, or return `false` when it cannot be loaded.
|
|
55
|
+
* `localOnly` overrides `waitForReady`: missing Chunks cause failure without remote requests
|
|
56
|
+
* or waiting for delivery, so provenance checks can use only content already available locally.
|
|
57
|
+
*/
|
|
58
|
+
fetchEntry: (file: UXFileInfoStub | FilePathWithPrefix, rev?: string, waitForReady?: boolean, skipCheck?: boolean, localOnly?: boolean) => Promise<LoadedEntry | false>;
|
|
31
59
|
getConflictedRevs: (file: UXFileInfoStub | FilePathWithPrefix) => Promise<string[]>;
|
|
32
60
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { FilePathWithPrefix } from "../common/types.js";
|
|
2
2
|
import type { SimpleStore } from "../common/utils.js";
|
|
3
3
|
export type FileReflectionProvenanceRecord = {
|
|
4
|
-
/** Exact
|
|
4
|
+
/** Exact revision most recently saved from or reflected in storage; the base for subsequent edits. */
|
|
5
5
|
revision: string;
|
|
6
|
-
/** Raw modification time
|
|
6
|
+
/** Raw modification time of the saved snapshot or storage observed after reflection. */
|
|
7
7
|
observedStorageMtime?: number;
|
|
8
8
|
};
|
|
9
9
|
export interface FileReflectionProvenance {
|
|
@@ -30,10 +30,12 @@ export declare class EntryManager {
|
|
|
30
30
|
isTargetFile(filenameSrc: string): boolean;
|
|
31
31
|
prepareChunk(piece: string): Promise<GeneratedChunk>;
|
|
32
32
|
getDBEntryMeta(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, includeDeleted?: boolean): Promise<false | LoadedEntry>;
|
|
33
|
-
getDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, dump?: boolean, waitForReady?: boolean, includeDeleted?: boolean): Promise<false | LoadedEntry>;
|
|
34
|
-
getDBEntryFromMeta(meta: LoadedEntry | MetaEntry, dump?: boolean, waitForReady?: boolean): Promise<false | LoadedEntry>;
|
|
33
|
+
getDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, dump?: boolean, waitForReady?: boolean, includeDeleted?: boolean, localOnly?: boolean): Promise<false | LoadedEntry>;
|
|
34
|
+
getDBEntryFromMeta(meta: LoadedEntry | MetaEntry, dump?: boolean, waitForReady?: boolean, localOnly?: boolean): Promise<false | LoadedEntry>;
|
|
35
35
|
deleteDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions): Promise<boolean>;
|
|
36
36
|
storeDeletionAtRevision(path: FilePathWithPrefix | FilePath, baseRevision: string): Promise<false | PouchDB.Core.Response>;
|
|
37
37
|
putDBEntry(note: SavingEntry, onlyChunks?: boolean, conflictBaseRev?: string): Promise<false | PouchDB.Core.Response>;
|
|
38
38
|
putDBEntryWithLiveBaseRevision(note: SavingEntry, baseRevision: string, onlyChunks?: boolean): Promise<false | PouchDB.Core.Response>;
|
|
39
|
+
/** Store unknown ancestry as a fresh root; return the Metadata write result or `false`. */
|
|
40
|
+
putDBEntryAsIndependentRoot(note: SavingEntry): Promise<false | PouchDB.Core.Response>;
|
|
39
41
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
prepareChunk,
|
|
11
11
|
putDBEntry,
|
|
12
12
|
putDBEntryWithLiveBaseRevision,
|
|
13
|
+
putDBEntryAsIndependentRoot,
|
|
13
14
|
storeDeletionByPathAtRevision
|
|
14
15
|
} from "./EntryManagerImpls.js";
|
|
15
16
|
class EntryManager {
|
|
@@ -57,11 +58,11 @@ class EntryManager {
|
|
|
57
58
|
async getDBEntryMeta(path, opt, includeDeleted = false) {
|
|
58
59
|
return await getDBEntryMetaByPath(this.serviceHost, this, path, opt, includeDeleted);
|
|
59
60
|
}
|
|
60
|
-
async getDBEntry(path, opt, dump = false, waitForReady = true, includeDeleted = false) {
|
|
61
|
-
return await getDBEntryByPath(this.serviceHost, this, path, opt, dump, waitForReady, includeDeleted);
|
|
61
|
+
async getDBEntry(path, opt, dump = false, waitForReady = true, includeDeleted = false, localOnly = false) {
|
|
62
|
+
return await getDBEntryByPath(this.serviceHost, this, path, opt, dump, waitForReady, includeDeleted, localOnly);
|
|
62
63
|
}
|
|
63
|
-
async getDBEntryFromMeta(meta, dump = false, waitForReady = true) {
|
|
64
|
-
return await getDBEntryFromMeta(this.serviceHost, this, meta, dump, waitForReady);
|
|
64
|
+
async getDBEntryFromMeta(meta, dump = false, waitForReady = true, localOnly = false) {
|
|
65
|
+
return await getDBEntryFromMeta(this.serviceHost, this, meta, dump, waitForReady, localOnly);
|
|
65
66
|
}
|
|
66
67
|
async deleteDBEntry(path, opt) {
|
|
67
68
|
return await deleteDBEntryByPath(this.serviceHost, this, path, opt);
|
|
@@ -75,6 +76,10 @@ class EntryManager {
|
|
|
75
76
|
async putDBEntryWithLiveBaseRevision(note, baseRevision, onlyChunks) {
|
|
76
77
|
return await putDBEntryWithLiveBaseRevision(this.serviceHost, this, note, baseRevision, onlyChunks);
|
|
77
78
|
}
|
|
79
|
+
/** Store unknown ancestry as a fresh root; return the Metadata write result or `false`. */
|
|
80
|
+
async putDBEntryAsIndependentRoot(note) {
|
|
81
|
+
return await putDBEntryAsIndependentRoot(this.serviceHost, this, note);
|
|
82
|
+
}
|
|
78
83
|
}
|
|
79
84
|
export {
|
|
80
85
|
EntryManager
|
|
@@ -20,20 +20,28 @@ export declare function putDBEntry(host: NecessaryServicesInterfaces<"path" | "s
|
|
|
20
20
|
* base has already been advanced, so the caller receives `false` and can retry from fresh state.
|
|
21
21
|
*/
|
|
22
22
|
export declare function putDBEntryWithLiveBaseRevision(host: NecessaryServicesInterfaces<"path" | "setting", never>, managers: NecessaryManagers<"localDatabase" | "chunkManager" | "hashManager" | "splitter">, note: SavingEntry, baseRevision: string, onlyChunks?: boolean): Promise<false | PouchDB.Core.Response>;
|
|
23
|
+
/**
|
|
24
|
+
* Preserve content with unknown ancestry as a fresh parentless revision of the same document.
|
|
25
|
+
* Store the body as Chunks as usual, then insert a Metadata root with a random revision ID using
|
|
26
|
+
* `new_edits: false`. This retains existing branches without reusing an identical historical root.
|
|
27
|
+
* Return the Metadata write result, or `false` when the entry cannot be stored.
|
|
28
|
+
*/
|
|
29
|
+
export declare function putDBEntryAsIndependentRoot(host: NecessaryServicesInterfaces<"path" | "setting", never>, managers: NecessaryManagers<"localDatabase" | "chunkManager" | "hashManager" | "splitter">, note: SavingEntry): Promise<false | PouchDB.Core.Response>;
|
|
23
30
|
export declare function isTargetFile(host: NecessaryServicesInterfaces<"setting", never>, filenameSrc: string): boolean;
|
|
24
31
|
export declare function prepareChunk({ chunkManager, hashManager }: NecessaryManagers<"chunkManager" | "hashManager">, piece: string): Promise<GeneratedChunk>;
|
|
25
32
|
export declare function getDBEntryMetaByPath(host: NecessaryServicesInterfaces<"path" | "setting", never>, { localDatabase }: NecessaryManagers<"localDatabase">, path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, includeDeleted?: boolean): Promise<false | LoadedEntry>;
|
|
26
33
|
export declare function isLegacyNote(meta: LoadedEntry | MetaEntry): boolean;
|
|
27
34
|
export declare function canUseOnDemandChunking(settings: ObsidianLiveSyncSettings): boolean;
|
|
28
35
|
/**
|
|
29
|
-
* Decide how to retrieve chunks based on settings and
|
|
36
|
+
* Decide how to retrieve chunks based on settings and the caller's read policy.
|
|
30
37
|
* `waitForReady` allows an already-observable finite delivery lifecycle to finish.
|
|
38
|
+
* `localOnly` overrides both settings and waiting for provenance comparisons.
|
|
31
39
|
*/
|
|
32
|
-
export declare function computeChunkRetrievalMethod(waitForReady: boolean, settings: ObsidianLiveSyncSettings): {
|
|
40
|
+
export declare function computeChunkRetrievalMethod(waitForReady: boolean, settings: ObsidianLiveSyncSettings, localOnly?: boolean): {
|
|
33
41
|
waitForDelivery: boolean;
|
|
34
42
|
preventRemoteRequest: boolean;
|
|
35
43
|
};
|
|
36
|
-
export declare function getDBEntryFromMeta(host: NecessaryServicesInterfaces<"path" | "setting", never>, { localDatabase, chunkManager }: NecessaryManagers<"localDatabase" | "chunkManager">, meta: LoadedEntry | MetaEntry, dump?: boolean, waitForReady?: boolean): Promise<false | (import("../../common/types.js").DatabaseEntry & EntryBase & import("../../common/types.js").EntryWithEden & {
|
|
44
|
+
export declare function getDBEntryFromMeta(host: NecessaryServicesInterfaces<"path" | "setting", never>, { localDatabase, chunkManager }: NecessaryManagers<"localDatabase" | "chunkManager">, meta: LoadedEntry | MetaEntry, dump?: boolean, waitForReady?: boolean, localOnly?: boolean): Promise<false | (import("../../common/types.js").DatabaseEntry & EntryBase & import("../../common/types.js").EntryWithEden & {
|
|
37
45
|
path: FilePathWithPrefix;
|
|
38
46
|
data: string | string[];
|
|
39
47
|
type: import("../../common/models/db.type.js").EntryTypes["NOTE_LEGACY"];
|
|
@@ -55,7 +63,7 @@ export declare function getDBEntryFromMeta(host: NecessaryServicesInterfaces<"pa
|
|
|
55
63
|
data: string | string[];
|
|
56
64
|
datatype: import("../../common/types.js").EntryTypeNotes;
|
|
57
65
|
} & PouchDB.Core.IdMeta)>;
|
|
58
|
-
export declare function getDBEntryByPath(host: NecessaryServicesInterfaces<"path" | "setting", never>, managers: NecessaryManagers<"localDatabase" | "chunkManager">, path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, dump?: boolean, waitForReady?: boolean, includeDeleted?: boolean): Promise<false | (import("../../common/types.js").DatabaseEntry & EntryBase & import("../../common/types.js").EntryWithEden & {
|
|
66
|
+
export declare function getDBEntryByPath(host: NecessaryServicesInterfaces<"path" | "setting", never>, managers: NecessaryManagers<"localDatabase" | "chunkManager">, path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, dump?: boolean, waitForReady?: boolean, includeDeleted?: boolean, localOnly?: boolean): Promise<false | (import("../../common/types.js").DatabaseEntry & EntryBase & import("../../common/types.js").EntryWithEden & {
|
|
59
67
|
path: FilePathWithPrefix;
|
|
60
68
|
data: string | string[];
|
|
61
69
|
type: import("../../common/models/db.type.js").EntryTypes["NOTE_LEGACY"];
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "../../common/types.js";
|
|
7
7
|
import { serialized } from "octagonal-wheels/concurrency/lock";
|
|
8
8
|
import { createTextBlob, getFileRegExp, isTextBlob } from "../../common/utils.js";
|
|
9
|
+
import { compatGlobal } from "../../common/coreEnvFunctions.js";
|
|
9
10
|
import { isErrorOfMissingDoc } from "../../pouchdb/utils_couchdb.js";
|
|
10
11
|
import { stripAllPrefixes } from "../../string_and_binary/path.js";
|
|
11
12
|
import { ICHeader, ICXHeader, PSCHeader } from "../../common/models/fileaccess.const.js";
|
|
@@ -126,6 +127,9 @@ async function putDBEntryWithLiveBaseRevision(host, managers, note, baseRevision
|
|
|
126
127
|
throw ex;
|
|
127
128
|
}
|
|
128
129
|
}
|
|
130
|
+
async function putDBEntryAsIndependentRoot(host, managers, note) {
|
|
131
|
+
return await putDBEntryInternal(host, managers, note, false, { mode: "independent-root" });
|
|
132
|
+
}
|
|
129
133
|
async function putDBEntryInternal(host, managers, note, onlyChunks, revisionTarget) {
|
|
130
134
|
const { localDatabase, chunkManager, splitter } = managers;
|
|
131
135
|
const filename = host.services.path.id2path(note._id, note);
|
|
@@ -163,9 +167,9 @@ async function putDBEntryInternal(host, managers, note, onlyChunks, revisionTarg
|
|
|
163
167
|
eden: {}
|
|
164
168
|
};
|
|
165
169
|
return await serialized("file:" + filename, async () => {
|
|
166
|
-
if (revisionTarget.mode
|
|
170
|
+
if (revisionTarget.mode === "force-base" || revisionTarget.mode === "live-base") {
|
|
167
171
|
newDoc._rev = revisionTarget.baseRevision;
|
|
168
|
-
} else {
|
|
172
|
+
} else if (revisionTarget.mode === "latest") {
|
|
169
173
|
try {
|
|
170
174
|
const old = await localDatabase.get(newDoc._id);
|
|
171
175
|
newDoc._rev = old._rev;
|
|
@@ -176,6 +180,15 @@ async function putDBEntryInternal(host, managers, note, onlyChunks, revisionTarg
|
|
|
176
180
|
}
|
|
177
181
|
}
|
|
178
182
|
}
|
|
183
|
+
if (revisionTarget.mode === "independent-root") {
|
|
184
|
+
const randomBytes = compatGlobal.crypto.getRandomValues(new Uint8Array(16));
|
|
185
|
+
const rootId = Array.from(randomBytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
186
|
+
newDoc._rev = `1-${rootId}`;
|
|
187
|
+
const imported = { ...newDoc, _revisions: { start: 1, ids: [rootId] } };
|
|
188
|
+
await localDatabase.bulkDocs([imported], { new_edits: false });
|
|
189
|
+
const stored = await localDatabase.get(newDoc._id, { rev: newDoc._rev });
|
|
190
|
+
return stored._rev === newDoc._rev ? { id: newDoc._id, ok: true, rev: newDoc._rev } : false;
|
|
191
|
+
}
|
|
179
192
|
const r = revisionTarget.mode === "live-base" ? await localDatabase.put(newDoc) : await localDatabase.put(newDoc, { force: true });
|
|
180
193
|
if (r.ok) {
|
|
181
194
|
return r;
|
|
@@ -324,7 +337,10 @@ function canFetchRemotely(settings) {
|
|
|
324
337
|
}
|
|
325
338
|
return true;
|
|
326
339
|
}
|
|
327
|
-
function computeChunkRetrievalMethod(waitForReady, settings) {
|
|
340
|
+
function computeChunkRetrievalMethod(waitForReady, settings, localOnly = false) {
|
|
341
|
+
if (localOnly) {
|
|
342
|
+
return { waitForDelivery: false, preventRemoteRequest: true };
|
|
343
|
+
}
|
|
328
344
|
const isOnDemandFetchEnabled = canFetchRemotely(settings);
|
|
329
345
|
if (!waitForReady) {
|
|
330
346
|
if (isOnDemandFetchEnabled) {
|
|
@@ -343,7 +359,7 @@ function computeChunkRetrievalMethod(waitForReady, settings) {
|
|
|
343
359
|
preventRemoteRequest: !isOnDemandFetchEnabled
|
|
344
360
|
};
|
|
345
361
|
}
|
|
346
|
-
async function respondEntryFromMeta({ localDatabase, chunkManager }, settings, filename, meta, dump, waitForReady) {
|
|
362
|
+
async function respondEntryFromMeta({ localDatabase, chunkManager }, settings, filename, meta, dump, waitForReady, localOnly = false) {
|
|
347
363
|
const dispFilename = stripAllPrefixes(filename);
|
|
348
364
|
const deleted = meta.deleted ?? meta._deleted ?? void 0;
|
|
349
365
|
if (dump) {
|
|
@@ -371,7 +387,7 @@ async function respondEntryFromMeta({ localDatabase, chunkManager }, settings, f
|
|
|
371
387
|
}));
|
|
372
388
|
edenChunks = Object.fromEntries(chunks2.map((e) => [e._id, e]));
|
|
373
389
|
}
|
|
374
|
-
const { waitForDelivery, preventRemoteRequest } = computeChunkRetrievalMethod(waitForReady, settings);
|
|
390
|
+
const { waitForDelivery, preventRemoteRequest } = computeChunkRetrievalMethod(waitForReady, settings, localOnly);
|
|
375
391
|
const childrenKeys = [...meta.children];
|
|
376
392
|
const chunks = await chunkManager.read(
|
|
377
393
|
childrenKeys,
|
|
@@ -421,7 +437,7 @@ async function respondEntryFromMeta({ localDatabase, chunkManager }, settings, f
|
|
|
421
437
|
}
|
|
422
438
|
return false;
|
|
423
439
|
}
|
|
424
|
-
async function getDBEntryFromMeta(host, { localDatabase, chunkManager }, meta, dump = false, waitForReady = true) {
|
|
440
|
+
async function getDBEntryFromMeta(host, { localDatabase, chunkManager }, meta, dump = false, waitForReady = true, localOnly = false) {
|
|
425
441
|
const filename = host.services.path.id2path(meta._id, meta);
|
|
426
442
|
if (!isTargetFile(host, filename)) {
|
|
427
443
|
return false;
|
|
@@ -438,15 +454,16 @@ async function getDBEntryFromMeta(host, { localDatabase, chunkManager }, meta, d
|
|
|
438
454
|
filename,
|
|
439
455
|
meta,
|
|
440
456
|
dump,
|
|
441
|
-
waitForReady
|
|
457
|
+
waitForReady,
|
|
458
|
+
localOnly
|
|
442
459
|
);
|
|
443
460
|
}
|
|
444
461
|
return false;
|
|
445
462
|
}
|
|
446
|
-
async function getDBEntryByPath(host, managers, path, opt, dump = false, waitForReady = true, includeDeleted = false) {
|
|
463
|
+
async function getDBEntryByPath(host, managers, path, opt, dump = false, waitForReady = true, includeDeleted = false, localOnly = false) {
|
|
447
464
|
const meta = await getDBEntryMetaByPath(host, managers, path, opt, includeDeleted);
|
|
448
465
|
if (meta) {
|
|
449
|
-
return await getDBEntryFromMeta(host, managers, meta, dump, waitForReady);
|
|
466
|
+
return await getDBEntryFromMeta(host, managers, meta, dump, waitForReady, localOnly);
|
|
450
467
|
} else {
|
|
451
468
|
return false;
|
|
452
469
|
}
|
|
@@ -540,6 +557,7 @@ export {
|
|
|
540
557
|
isTargetFile,
|
|
541
558
|
prepareChunk,
|
|
542
559
|
putDBEntry,
|
|
560
|
+
putDBEntryAsIndependentRoot,
|
|
543
561
|
putDBEntryWithLiveBaseRevision,
|
|
544
562
|
storeDeletionByPathAtRevision
|
|
545
563
|
};
|
|
@@ -196,12 +196,14 @@ export declare class LiveSyncLocalDB {
|
|
|
196
196
|
bulkDocsRaw<T extends EntryDoc>(docs: Array<PouchDB.Core.PutDocument<T>>, options?: PouchDB.Core.BulkDocsOptions): Promise<Array<PouchDB.Core.Response | PouchDB.Core.Error>>;
|
|
197
197
|
isTargetFile(filenameSrc: string): boolean;
|
|
198
198
|
getDBEntryMeta(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, includeDeleted?: boolean): Promise<false | LoadedEntry>;
|
|
199
|
-
getDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, dump?: boolean, waitForReady?: boolean, includeDeleted?: boolean): Promise<false | LoadedEntry>;
|
|
200
|
-
getDBEntryFromMeta(meta: LoadedEntry | MetaEntry, dump?: boolean, waitForReady?: boolean): Promise<false | LoadedEntry>;
|
|
199
|
+
getDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions, dump?: boolean, waitForReady?: boolean, includeDeleted?: boolean, localOnly?: boolean): Promise<false | LoadedEntry>;
|
|
200
|
+
getDBEntryFromMeta(meta: LoadedEntry | MetaEntry, dump?: boolean, waitForReady?: boolean, localOnly?: boolean): Promise<false | LoadedEntry>;
|
|
201
201
|
deleteDBEntry(path: FilePathWithPrefix | FilePath, opt?: PouchDB.Core.GetOptions): Promise<boolean>;
|
|
202
202
|
storeDeletionAtRevision(path: FilePathWithPrefix | FilePath, baseRevision: string): Promise<false | PouchDB.Core.Response>;
|
|
203
203
|
putDBEntry(note: SavingEntry, onlyChunks?: boolean, conflictBaseRev?: string): Promise<false | PouchDB.Core.Response>;
|
|
204
204
|
putDBEntryWithLiveBaseRevision(note: SavingEntry, baseRevision: string, onlyChunks?: boolean): Promise<false | PouchDB.Core.Response>;
|
|
205
|
+
/** Preserve existing branches and delegate a fresh root write to the entry manager. */
|
|
206
|
+
putDBEntryAsIndependentRoot(note: SavingEntry): Promise<false | PouchDB.Core.Response>;
|
|
205
207
|
getConflictedDoc(path: FilePathWithPrefix, rev: string): Promise<false | diff_result_leaf>;
|
|
206
208
|
tryAutoMerge(path: FilePathWithPrefix, enableMarkdownAutoMerge: boolean): AutoMergeResult;
|
|
207
209
|
}
|
|
@@ -457,11 +457,11 @@ class LiveSyncLocalDB {
|
|
|
457
457
|
async getDBEntryMeta(path, opt, includeDeleted = false) {
|
|
458
458
|
return await this.managers.entryManager.getDBEntryMeta(path, opt, includeDeleted);
|
|
459
459
|
}
|
|
460
|
-
async getDBEntry(path, opt, dump = false, waitForReady = true, includeDeleted = false) {
|
|
461
|
-
return await this.managers.entryManager.getDBEntry(path, opt, dump, waitForReady, includeDeleted);
|
|
460
|
+
async getDBEntry(path, opt, dump = false, waitForReady = true, includeDeleted = false, localOnly = false) {
|
|
461
|
+
return await this.managers.entryManager.getDBEntry(path, opt, dump, waitForReady, includeDeleted, localOnly);
|
|
462
462
|
}
|
|
463
|
-
async getDBEntryFromMeta(meta, dump = false, waitForReady = true) {
|
|
464
|
-
return await this.managers.entryManager.getDBEntryFromMeta(meta, dump, waitForReady);
|
|
463
|
+
async getDBEntryFromMeta(meta, dump = false, waitForReady = true, localOnly = false) {
|
|
464
|
+
return await this.managers.entryManager.getDBEntryFromMeta(meta, dump, waitForReady, localOnly);
|
|
465
465
|
}
|
|
466
466
|
async deleteDBEntry(path, opt) {
|
|
467
467
|
return await this.managers.entryManager.deleteDBEntry(path, opt);
|
|
@@ -475,6 +475,10 @@ class LiveSyncLocalDB {
|
|
|
475
475
|
async putDBEntryWithLiveBaseRevision(note, baseRevision, onlyChunks) {
|
|
476
476
|
return await this.managers.entryManager.putDBEntryWithLiveBaseRevision(note, baseRevision, onlyChunks);
|
|
477
477
|
}
|
|
478
|
+
/** Preserve existing branches and delegate a fresh root write to the entry manager. */
|
|
479
|
+
async putDBEntryAsIndependentRoot(note) {
|
|
480
|
+
return await this.managers.entryManager.putDBEntryAsIndependentRoot(note);
|
|
481
|
+
}
|
|
478
482
|
async getConflictedDoc(path, rev) {
|
|
479
483
|
return await this.managers.conflictManager.getConflictedDoc(path, rev);
|
|
480
484
|
}
|
|
@@ -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
|
}
|