@vrtmrz/livesync-commonlib 0.1.0-rc.0 → 0.1.0-rc.10
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 +101 -17
- package/dist/API/DirectFileManipulatorV2.d.ts +3 -0
- package/dist/common/configForDoc.d.ts +1 -0
- package/dist/common/configForDoc.js +24 -7
- package/dist/common/models/setting.const.defaults.d.ts +19 -0
- package/dist/common/models/setting.const.defaults.js +21 -2
- package/dist/common/models/setting.const.preferred.js +1 -2
- package/dist/common/models/setting.lifecycle.d.ts +33 -0
- package/dist/common/models/setting.lifecycle.js +121 -0
- package/dist/common/models/setting.type.d.ts +10 -4
- package/dist/common/models/shared.definition.configNames.js +3 -3
- package/dist/common/models/tweak.definition.d.ts +1 -1
- package/dist/common/models/tweak.definition.js +1 -6
- package/dist/common/settingConstants.d.ts +3 -2
- package/dist/common/settingConstants.js +6 -6
- package/dist/common/types.d.ts +4 -2
- package/dist/common/types.js +16 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.js +4 -0
- package/dist/interfaces/Confirm.d.ts +10 -2
- package/dist/interfaces/DatabaseFileAccess.d.ts +8 -0
- package/dist/interfaces/DatabaseRebuilder.d.ts +12 -2
- package/dist/interfaces/FileReflectionProvenance.d.ts +32 -0
- package/dist/interfaces/FileReflectionProvenance.js +25 -0
- package/dist/managers/ConflictManager.js +16 -7
- package/dist/managers/EntryManager/EntryManager.d.ts +1 -0
- package/dist/managers/EntryManager/EntryManager.js +5 -1
- package/dist/managers/EntryManager/EntryManagerImpls.d.ts +1 -0
- package/dist/managers/EntryManager/EntryManagerImpls.js +29 -1
- package/dist/platform/node/index.d.ts +1 -0
- package/dist/platform/node/index.js +4 -0
- package/dist/platform/node/standardIo.d.ts +24 -0
- package/dist/platform/node/standardIo.js +39 -0
- package/dist/platform/node/storage.d.ts +10 -1
- package/dist/platform/node/storage.js +86 -25
- package/dist/platform/standardIo.d.ts +19 -0
- package/dist/platform/standardIo.js +0 -0
- package/dist/pouchdb/LiveSyncLocalDB.d.ts +1 -0
- package/dist/pouchdb/LiveSyncLocalDB.js +3 -0
- package/dist/remoteConfigurations.d.ts +9 -0
- package/dist/remoteConfigurations.js +14 -0
- package/dist/replication/LiveSyncAbstractReplicator.d.ts +3 -3
- package/dist/replication/couchdb/LiveSyncReplicator.d.ts +0 -1
- package/dist/replication/couchdb/LiveSyncReplicator.js +0 -3
- package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +0 -1
- package/dist/replication/journal/LiveSyncJournalReplicator.js +0 -3
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.d.ts +5 -3
- package/dist/replication/trystero/LiveSyncTrysteroReplicator.js +42 -30
- package/dist/replication/trystero/P2PReplicatorCore.d.ts +2 -6
- package/dist/replication/trystero/P2PReplicatorCore.js +16 -86
- package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -1
- package/dist/replication/trystero/TrysteroReplicator.js +10 -4
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.d.ts +4 -5
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +22 -30
- package/dist/replication/trystero/UseP2PReplicatorResult.d.ts +10 -2
- package/dist/replication/trystero/addP2PEventHandlers.d.ts +4 -2
- package/dist/replication/trystero/addP2PEventHandlers.js +9 -10
- package/dist/replication/trystero/types.d.ts +2 -0
- package/dist/replication/trystero/useP2PReplicatorCommands.d.ts +1 -1
- package/dist/replication/trystero/useP2PReplicatorCommands.js +3 -1
- package/dist/replication/trystero/useP2PReplicatorFeature.js +25 -15
- package/dist/rpc/RpcRoom.d.ts +1 -1
- package/dist/rpc/RpcRoom.js +22 -16
- package/dist/rpc/transports/TrysteroTransport.d.ts +2 -0
- package/dist/rpc/transports/TrysteroTransport.js +24 -17
- package/dist/rpc/transports/trysteroRoomEvents.d.ts +14 -0
- package/dist/rpc/transports/trysteroRoomEvents.js +34 -0
- package/dist/serviceFeatures/checkRemoteSize.d.ts +5 -3
- package/dist/serviceFeatures/checkRemoteSize.js +51 -11
- package/dist/serviceFeatures/remoteConfig.d.ts +37 -1
- package/dist/serviceFeatures/remoteConfig.js +90 -0
- package/dist/serviceModules/Rebuilder.d.ts +3 -6
- package/dist/serviceModules/Rebuilder.js +31 -22
- package/dist/serviceModules/ServiceDatabaseFileAccessBase.d.ts +5 -0
- package/dist/serviceModules/ServiceDatabaseFileAccessBase.js +69 -13
- package/dist/serviceModules/ServiceFileHandlerBase.d.ts +20 -0
- package/dist/serviceModules/ServiceFileHandlerBase.js +224 -40
- package/dist/services/BrowserServices.d.ts +2 -0
- package/dist/services/BrowserServices.js +4 -1
- package/dist/services/HeadlessServices.d.ts +2 -0
- package/dist/services/HeadlessServices.js +2 -1
- package/dist/services/base/CommonlibMessages.d.ts +318 -0
- package/dist/services/base/CommonlibMessages.js +313 -0
- package/dist/services/base/IService.d.ts +13 -6
- package/dist/services/base/KeyValueDBService.d.ts +1 -1
- package/dist/services/base/KeyValueDBService.js +3 -1
- package/dist/services/base/MessageTranslator.d.ts +5 -2
- package/dist/services/base/MessageTranslator.js +21 -0
- package/dist/services/base/ReplicationService.d.ts +4 -2
- package/dist/services/base/ReplicationService.js +11 -16
- package/dist/services/base/ServiceBase.d.ts +1 -1
- package/dist/services/base/ServiceBase.js +2 -4
- package/dist/services/base/SettingService.d.ts +17 -9
- package/dist/services/base/SettingService.js +38 -19
- package/dist/services/implements/headless/HeadlessAPIService.d.ts +3 -3
- package/dist/services/implements/headless/HeadlessAPIService.js +2 -2
- package/dist/services/implements/injectable/InjectableTweakValueService.d.ts +3 -3
- package/dist/settings.d.ts +10 -0
- package/dist/settings.js +32 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/conflict-resolution.md +153 -0
- package/docs/development.md +108 -8
- package/docs/p2p-transport-lifecycle.md +62 -0
- package/docs/platform-standard-io.md +44 -0
- package/docs/platform-storage.md +4 -0
- package/docs/proven-in-use.md +65 -0
- package/docs/releasing.md +26 -6
- package/docs/remote-configurations.md +63 -0
- package/docs/settings-lifecycle.md +85 -0
- package/package.json +17 -27
- package/dist/common/i18n.d.ts +0 -18
- package/dist/common/i18n.js +0 -7193
- package/dist/common/messages/combinedMessages.dev.d.ts +0 -7
- package/dist/common/messages/combinedMessages.dev.js +0 -44
- package/dist/common/messages/combinedMessages.prod.d.ts +0 -9264
- package/dist/common/messages/combinedMessages.prod.js +0 -9271
- package/dist/common/messages/de.d.ts +0 -297
- package/dist/common/messages/de.js +0 -7
- package/dist/common/messages/def.d.ts +0 -1119
- package/dist/common/messages/def.js +0 -7
- package/dist/common/messages/es.d.ts +0 -689
- package/dist/common/messages/es.js +0 -7
- package/dist/common/messages/fr.d.ts +0 -582
- package/dist/common/messages/fr.js +0 -7
- package/dist/common/messages/he.d.ts +0 -583
- package/dist/common/messages/he.js +0 -7
- package/dist/common/messages/ja.d.ts +0 -838
- package/dist/common/messages/ja.js +0 -7
- package/dist/common/messages/ko.d.ts +0 -800
- package/dist/common/messages/ko.js +0 -7
- package/dist/common/messages/ru.d.ts +0 -858
- package/dist/common/messages/ru.js +0 -7
- package/dist/common/messages/zh-tw.d.ts +0 -384
- package/dist/common/messages/zh-tw.js +0 -7
- package/dist/common/messages/zh.d.ts +0 -1093
- package/dist/common/messages/zh.js +0 -7
- package/dist/common/messagesJson/de.json +0 -295
- package/dist/common/messagesJson/en.json +0 -1117
- package/dist/common/messagesJson/es.json +0 -687
- package/dist/common/messagesJson/fr.json +0 -580
- package/dist/common/messagesJson/he.json +0 -581
- package/dist/common/messagesJson/ja.json +0 -836
- package/dist/common/messagesJson/ko.json +0 -798
- package/dist/common/messagesJson/ru.json +0 -856
- package/dist/common/messagesJson/zh-tw.json +0 -382
- package/dist/common/messagesJson/zh.json +0 -1091
- package/dist/common/rosetta.d.ts +0 -42
- package/dist/common/rosetta.js +0 -57
|
@@ -2,7 +2,17 @@ import { sizeToHumanReadable } from "octagonal-wheels/number";
|
|
|
2
2
|
import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "../common/types.js";
|
|
3
3
|
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
|
+
const REMOTE_SIZE_NOTICE_DURATION_MS = 5 * 60 * 1e3;
|
|
7
|
+
function configureReviewAnchor(anchor, label, review) {
|
|
8
|
+
anchor.href = "#";
|
|
9
|
+
anchor.textContent = label;
|
|
10
|
+
anchor.addEventListener("click", (event) => {
|
|
11
|
+
event.preventDefault();
|
|
12
|
+
fireAndForget(review);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function onNotifyRemoteSizeNotConfiguredFactory(host, log, mode = "notice") {
|
|
6
16
|
return async () => {
|
|
7
17
|
const translate = host.services.context.translate;
|
|
8
18
|
log(translate("moduleCheckRemoteSize.logCheckingStorageSizes"), LOG_LEVEL_VERBOSE);
|
|
@@ -15,13 +25,25 @@ function onNotifyRemoteSizeNotConfiguredFactory(host, log) {
|
|
|
15
25
|
const ANSWER_800 = translate("moduleCheckRemoteSize.option800MB");
|
|
16
26
|
const ANSWER_2000 = translate("moduleCheckRemoteSize.option2GB");
|
|
17
27
|
const ASK_ME_NEXT_TIME = translate("moduleCheckRemoteSize.optionAskMeLater");
|
|
28
|
+
if (mode === "notice") {
|
|
29
|
+
host.services.API.confirm.askInPopup(
|
|
30
|
+
"remote-size-not-configured",
|
|
31
|
+
translate("moduleCheckRemoteSize.noticeNotConfigured"),
|
|
32
|
+
(anchor) => configureReviewAnchor(
|
|
33
|
+
anchor,
|
|
34
|
+
translate("moduleCheckRemoteSize.optionReview"),
|
|
35
|
+
() => onNotifyRemoteSizeNotConfiguredFactory(host, log, "dialogue")()
|
|
36
|
+
),
|
|
37
|
+
REMOTE_SIZE_NOTICE_DURATION_MS
|
|
38
|
+
);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
18
41
|
const ret = await host.services.API.confirm.askSelectStringDialogue(
|
|
19
42
|
message,
|
|
20
43
|
[ANSWER_0, ANSWER_800, ANSWER_2000, ASK_ME_NEXT_TIME],
|
|
21
44
|
{
|
|
22
45
|
defaultAction: ASK_ME_NEXT_TIME,
|
|
23
|
-
title: translate("moduleCheckRemoteSize.titleDatabaseSizeNotify")
|
|
24
|
-
timeout: 40
|
|
46
|
+
title: translate("moduleCheckRemoteSize.titleDatabaseSizeNotify")
|
|
25
47
|
}
|
|
26
48
|
);
|
|
27
49
|
if (ret == ANSWER_0) {
|
|
@@ -49,7 +71,7 @@ function onNotifyRemoteSizeNotConfiguredFactory(host, log) {
|
|
|
49
71
|
return true;
|
|
50
72
|
};
|
|
51
73
|
}
|
|
52
|
-
function onNotifyRemoteSizeExceedFactory(host, log) {
|
|
74
|
+
function onNotifyRemoteSizeExceedFactory(host, log, mode = "notice") {
|
|
53
75
|
return async () => {
|
|
54
76
|
const translate = host.services.context.translate;
|
|
55
77
|
const settings = host.services.setting.currentSettings();
|
|
@@ -89,13 +111,28 @@ function onNotifyRemoteSizeExceedFactory(host, log) {
|
|
|
89
111
|
});
|
|
90
112
|
const ANSWER_REBUILD = translate("moduleCheckRemoteSize.optionRebuildAll");
|
|
91
113
|
const ANSWER_IGNORE = translate("moduleCheckRemoteSize.optionDismiss");
|
|
114
|
+
if (mode === "notice") {
|
|
115
|
+
host.services.API.confirm.askInPopup(
|
|
116
|
+
"remote-size-exceeded",
|
|
117
|
+
translate("moduleCheckRemoteSize.noticeExceeded", {
|
|
118
|
+
measuredSize: sizeToHumanReadable(estimatedSize),
|
|
119
|
+
notifySize: sizeToHumanReadable(maxSize)
|
|
120
|
+
}),
|
|
121
|
+
(anchor) => configureReviewAnchor(
|
|
122
|
+
anchor,
|
|
123
|
+
translate("moduleCheckRemoteSize.optionReview"),
|
|
124
|
+
() => onNotifyRemoteSizeExceedFactory(host, log, "dialogue")()
|
|
125
|
+
),
|
|
126
|
+
REMOTE_SIZE_NOTICE_DURATION_MS
|
|
127
|
+
);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
92
130
|
const ret = await host.services.API.confirm.askSelectStringDialogue(
|
|
93
131
|
message,
|
|
94
132
|
[ANSWER_ENLARGE_LIMIT, ANSWER_REBUILD, ANSWER_IGNORE],
|
|
95
133
|
{
|
|
96
134
|
defaultAction: ANSWER_IGNORE,
|
|
97
|
-
title: translate("moduleCheckRemoteSize.titleDatabaseSizeLimitExceeded")
|
|
98
|
-
timeout: 60
|
|
135
|
+
title: translate("moduleCheckRemoteSize.titleDatabaseSizeLimitExceeded")
|
|
99
136
|
}
|
|
100
137
|
);
|
|
101
138
|
if (ret == ANSWER_REBUILD) {
|
|
@@ -142,7 +179,7 @@ function onNotifyRemoteSizeExceedFactory(host, log) {
|
|
|
142
179
|
return true;
|
|
143
180
|
};
|
|
144
181
|
}
|
|
145
|
-
async function scanAllStat(host, log, resetThreshold = false) {
|
|
182
|
+
async function scanAllStat(host, log, resetThreshold = false, mode = "notice") {
|
|
146
183
|
if (resetThreshold) {
|
|
147
184
|
await host.services.setting.applyPartial(
|
|
148
185
|
{
|
|
@@ -151,8 +188,8 @@ async function scanAllStat(host, log, resetThreshold = false) {
|
|
|
151
188
|
true
|
|
152
189
|
);
|
|
153
190
|
}
|
|
154
|
-
const onNotifyNotConfigured = onNotifyRemoteSizeNotConfiguredFactory(host, log);
|
|
155
|
-
const onNotifyExceed = onNotifyRemoteSizeExceedFactory(host, log);
|
|
191
|
+
const onNotifyNotConfigured = onNotifyRemoteSizeNotConfiguredFactory(host, log, mode);
|
|
192
|
+
const onNotifyExceed = onNotifyRemoteSizeExceedFactory(host, log, mode);
|
|
156
193
|
return await onNotifyNotConfigured() && await onNotifyExceed();
|
|
157
194
|
}
|
|
158
195
|
function useCheckRemoteSize(host) {
|
|
@@ -163,10 +200,13 @@ function useCheckRemoteSize(host) {
|
|
|
163
200
|
id: "livesync-reset-remote-size-threshold-and-check",
|
|
164
201
|
name: "Reset notification threshold and check the remote database usage",
|
|
165
202
|
callback: async () => {
|
|
166
|
-
await scanAllStat(host, log, true);
|
|
203
|
+
await scanAllStat(host, log, true, "dialogue");
|
|
167
204
|
}
|
|
168
205
|
});
|
|
169
|
-
host.services.context.events.onEvent(
|
|
206
|
+
host.services.context.events.onEvent(
|
|
207
|
+
EVENT_REQUEST_CHECK_REMOTE_SIZE,
|
|
208
|
+
() => scanAllStat(host, log, true, "dialogue")
|
|
209
|
+
);
|
|
170
210
|
return Promise.resolve(true);
|
|
171
211
|
});
|
|
172
212
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type LOG_LEVEL } from "../common/logger.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type RemoteConfigurationResult } from "../common/ConnectionString.js";
|
|
3
|
+
import type { ObsidianLiveSyncSettings, RemoteConfiguration } from "../common/models/setting.type.js";
|
|
3
4
|
import type { NecessaryServices } from "../interfaces/ServiceModule.js";
|
|
4
5
|
export type RemoteConfigHost = NecessaryServices<"setting" | "UI" | "replication" | "control" | "appLifecycle" | "API", never>;
|
|
5
6
|
export declare function migrateLegacyRemoteConfigurationsInPlace(settings: ObsidianLiveSyncSettings, log?: (message: string, level?: LOG_LEVEL) => void): boolean;
|
|
@@ -8,6 +9,41 @@ export declare function migrateLegacyRemoteConfigurationsInPlace(settings: Obsid
|
|
|
8
9
|
* @returns A unique string identifier.
|
|
9
10
|
*/
|
|
10
11
|
export declare function createRemoteConfigurationId(): string;
|
|
12
|
+
export type SerializableRemoteConfigurationType = Exclude<RemoteConfigurationResult["type"], "webdav">;
|
|
13
|
+
export interface UpsertRemoteConfigurationOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Reuse this identifier to update a known profile. Omit it to allocate a new opaque identifier.
|
|
16
|
+
*/
|
|
17
|
+
id?: string;
|
|
18
|
+
/**
|
|
19
|
+
* User-visible name. Omit it to derive a descriptive name from the connection settings.
|
|
20
|
+
*/
|
|
21
|
+
name?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Select this profile as the main remote and project it onto the compatibility fields.
|
|
24
|
+
*/
|
|
25
|
+
activate?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Select this profile for P2P features without changing the main remote selection.
|
|
28
|
+
*/
|
|
29
|
+
activateForP2P?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Suggest a concise display name from a serialisable remote configuration.
|
|
33
|
+
*
|
|
34
|
+
* The name is presentation only. Callers must continue to use the profile ID for identity and
|
|
35
|
+
* `activeConfigurationId` for the selected main remote.
|
|
36
|
+
*/
|
|
37
|
+
export declare function suggestRemoteConfigurationName(configuration: RemoteConfigurationResult): string;
|
|
38
|
+
/**
|
|
39
|
+
* Create or update a multiple-remote profile from the corresponding compatibility fields.
|
|
40
|
+
*
|
|
41
|
+
* This mutates `settings`. Passing an existing `id` intentionally replaces that profile; omitting
|
|
42
|
+
* it always allocates a new opaque ID and preserves all existing profiles. Generated display names
|
|
43
|
+
* are made unique for readability, but names never act as identifiers. The stored connection URI is
|
|
44
|
+
* plaintext at this boundary; `SettingService` applies configured at-rest encryption when saving it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function upsertRemoteConfigurationInPlace(settings: ObsidianLiveSyncSettings, type: SerializableRemoteConfigurationType, options?: UpsertRemoteConfigurationOptions): RemoteConfiguration;
|
|
11
47
|
/**
|
|
12
48
|
* Keep compatibility for users who were already using P2P as their main active remote.
|
|
13
49
|
*/
|
|
@@ -51,6 +51,94 @@ function migrateLegacyRemoteConfigurationsInPlace(settings, log) {
|
|
|
51
51
|
function createRemoteConfigurationId() {
|
|
52
52
|
return `remote-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
53
53
|
}
|
|
54
|
+
function toRemoteConfigurationResult(type, settings) {
|
|
55
|
+
if (type === "couchdb") {
|
|
56
|
+
return { type, settings };
|
|
57
|
+
}
|
|
58
|
+
if (type === "s3") {
|
|
59
|
+
return { type, settings };
|
|
60
|
+
}
|
|
61
|
+
return { type, settings };
|
|
62
|
+
}
|
|
63
|
+
function suggestRemoteConfigurationName(configuration) {
|
|
64
|
+
if (configuration.type === "couchdb") {
|
|
65
|
+
try {
|
|
66
|
+
const host = new URL(configuration.settings.couchDB_URI).host;
|
|
67
|
+
return host ? `CouchDB ${host}` : "CouchDB remote";
|
|
68
|
+
} catch {
|
|
69
|
+
return "CouchDB remote";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (configuration.type === "s3") {
|
|
73
|
+
const bucket = configuration.settings.bucket.trim();
|
|
74
|
+
if (bucket) {
|
|
75
|
+
return `S3 ${bucket}`;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const host = new URL(configuration.settings.endpoint).host;
|
|
79
|
+
return host ? `S3 ${host}` : "Object Storage remote";
|
|
80
|
+
} catch {
|
|
81
|
+
return "Object Storage remote";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (configuration.type === "p2p") {
|
|
85
|
+
const room = configuration.settings.P2P_roomID.trim();
|
|
86
|
+
return room ? `P2P ${room}` : "P2P remote";
|
|
87
|
+
}
|
|
88
|
+
return "Remote configuration";
|
|
89
|
+
}
|
|
90
|
+
function allocateRemoteConfigurationId(configurations) {
|
|
91
|
+
let id = createRemoteConfigurationId();
|
|
92
|
+
while (configurations[id]) {
|
|
93
|
+
id = createRemoteConfigurationId();
|
|
94
|
+
}
|
|
95
|
+
return id;
|
|
96
|
+
}
|
|
97
|
+
function allocateRemoteConfigurationName(configurations, baseName, updatingId) {
|
|
98
|
+
const usedNames = new Set(
|
|
99
|
+
Object.values(configurations).filter((configuration) => configuration.id !== updatingId).map((configuration) => configuration.name)
|
|
100
|
+
);
|
|
101
|
+
if (!usedNames.has(baseName)) {
|
|
102
|
+
return baseName;
|
|
103
|
+
}
|
|
104
|
+
let suffix = 2;
|
|
105
|
+
while (usedNames.has(`${baseName} (${suffix})`)) {
|
|
106
|
+
suffix += 1;
|
|
107
|
+
}
|
|
108
|
+
return `${baseName} (${suffix})`;
|
|
109
|
+
}
|
|
110
|
+
function upsertRemoteConfigurationInPlace(settings, type, options = {}) {
|
|
111
|
+
if (options.activateForP2P && type !== "p2p") {
|
|
112
|
+
throw new Error("Only a P2P remote configuration can be selected for P2P features.");
|
|
113
|
+
}
|
|
114
|
+
const configurations = settings.remoteConfigurations ?? {};
|
|
115
|
+
const requestedId = options.id?.trim();
|
|
116
|
+
const id = requestedId || allocateRemoteConfigurationId(configurations);
|
|
117
|
+
const existing = configurations[id];
|
|
118
|
+
const serialisable = toRemoteConfigurationResult(type, settings);
|
|
119
|
+
const suggestedName = suggestRemoteConfigurationName(serialisable);
|
|
120
|
+
const requestedName = options.name?.trim();
|
|
121
|
+
const name = requestedName || existing?.name || allocateRemoteConfigurationName(configurations, suggestedName, existing?.id);
|
|
122
|
+
const configuration = {
|
|
123
|
+
id,
|
|
124
|
+
name,
|
|
125
|
+
uri: ConnectionStringParser.serialize(serialisable),
|
|
126
|
+
isEncrypted: false
|
|
127
|
+
};
|
|
128
|
+
settings.remoteConfigurations ??= configurations;
|
|
129
|
+
configurations[id] = configuration;
|
|
130
|
+
if (options.activate) {
|
|
131
|
+
if (!activateRemoteConfiguration(settings, id)) {
|
|
132
|
+
throw new Error(`Failed to activate remote configuration '${id}'.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (options.activateForP2P) {
|
|
136
|
+
if (!activateP2PRemoteConfiguration(settings, id)) {
|
|
137
|
+
throw new Error(`Failed to activate P2P remote configuration '${id}'.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return configuration;
|
|
141
|
+
}
|
|
54
142
|
function migrateP2PActiveRemoteConfigurationIdInPlace(settings) {
|
|
55
143
|
if ((settings.P2P_ActiveRemoteConfigurationId ?? "").trim() !== "") {
|
|
56
144
|
return false;
|
|
@@ -242,6 +330,8 @@ export {
|
|
|
242
330
|
migrateLegacyRemoteConfigurationsInPlace,
|
|
243
331
|
migrateP2PActiveRemoteConfigurationIdInPlace,
|
|
244
332
|
migrateToMultipleRemoteConfigurations,
|
|
333
|
+
suggestRemoteConfigurationName,
|
|
334
|
+
upsertRemoteConfigurationInPlace,
|
|
245
335
|
useRemoteConfiguration,
|
|
246
336
|
useRemoteConfigurationMigration
|
|
247
337
|
};
|
|
@@ -48,10 +48,6 @@ export declare class ServiceRebuilder extends ServiceModuleBase<ServiceRebuilder
|
|
|
48
48
|
constructor(services: ServiceRebuilderDependencies);
|
|
49
49
|
$performRebuildDB(method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice" | "localOnlyWithChunks"): Promise<void>;
|
|
50
50
|
informOptionalFeatures(): Promise<void>;
|
|
51
|
-
askUsingOptionalFeature(opt: {
|
|
52
|
-
enableFetch?: boolean;
|
|
53
|
-
enableOverwrite?: boolean;
|
|
54
|
-
}): Promise<void>;
|
|
55
51
|
rebuildRemote(): Promise<void>;
|
|
56
52
|
private performRemoteRebuild;
|
|
57
53
|
$rebuildRemote(): Promise<void>;
|
|
@@ -60,8 +56,9 @@ export declare class ServiceRebuilder extends ServiceModuleBase<ServiceRebuilder
|
|
|
60
56
|
$rebuildEverything(): Promise<void>;
|
|
61
57
|
$fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean): Promise<void>;
|
|
62
58
|
$fetchLocalDBFast(autoResume: boolean): Promise<void>;
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
private scheduleInitialisation;
|
|
60
|
+
scheduleRebuild(prepareBeforeRestart?: () => Promise<void>): Promise<boolean>;
|
|
61
|
+
scheduleFetch(prepareBeforeRestart?: () => Promise<void>): Promise<boolean>;
|
|
65
62
|
private _tryResetRemoteDatabase;
|
|
66
63
|
private _onResetLocalDatabase;
|
|
67
64
|
suspendAllSync(): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FlagFilesHumanReadable } from "../common/models/redflag.const.js";
|
|
2
|
-
import { REMOTE_COUCHDB, REMOTE_MINIO } from "../common/models/setting.const.js";
|
|
2
|
+
import { REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P } from "../common/models/setting.const.js";
|
|
3
3
|
import { DEFAULT_SETTINGS } from "../common/models/setting.const.defaults.js";
|
|
4
4
|
import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
|
|
5
5
|
import { delay } from "octagonal-wheels/promises";
|
|
@@ -67,14 +67,6 @@ Please enable them from the settings screen after setup is complete.`,
|
|
|
67
67
|
["OK"]
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
|
-
async askUsingOptionalFeature(opt) {
|
|
71
|
-
if (await this.UI.confirm.askYesNoDialog(
|
|
72
|
-
"Do you want to enable extra features? If you are new to Self-hosted LiveSync, try the core feature first!",
|
|
73
|
-
{ title: "Enable extra features", defaultOption: "No", timeout: 15 }
|
|
74
|
-
) == "yes") {
|
|
75
|
-
await this.setting.suggestOptionalFeatures(opt);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
70
|
async rebuildRemote() {
|
|
79
71
|
await this.replicator.runBoundedRemoteActivity(() => this.performRemoteRebuild(), {
|
|
80
72
|
label: "rebuild-remote"
|
|
@@ -95,7 +87,7 @@ Please enable them from the settings screen after setup is complete.`,
|
|
|
95
87
|
await delay(1e3);
|
|
96
88
|
await this.replication.replicateAllToRemote(true);
|
|
97
89
|
await delay(1e3);
|
|
98
|
-
await this.replication.replicateAllToRemote(true
|
|
90
|
+
await this.replication.replicateAllToRemote(true);
|
|
99
91
|
}
|
|
100
92
|
$rebuildRemote() {
|
|
101
93
|
return this.rebuildRemote();
|
|
@@ -116,6 +108,9 @@ Please enable them from the settings screen after setup is complete.`,
|
|
|
116
108
|
await this.resetLocalDatabase();
|
|
117
109
|
await delay(1e3);
|
|
118
110
|
await this.databaseEvents.initialiseDatabase(true, true, true);
|
|
111
|
+
if (this.setting.currentSettings().remoteType === REMOTE_P2P) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
119
114
|
await this.replication.markLocked();
|
|
120
115
|
await this._tryResetRemoteDatabase();
|
|
121
116
|
await this.replication.markLocked();
|
|
@@ -123,7 +118,7 @@ Please enable them from the settings screen after setup is complete.`,
|
|
|
123
118
|
await delay(1e3);
|
|
124
119
|
await this.replication.replicateAllToRemote(true);
|
|
125
120
|
await delay(1e3);
|
|
126
|
-
await this.replication.replicateAllToRemote(true
|
|
121
|
+
await this.replication.replicateAllToRemote(true);
|
|
127
122
|
}
|
|
128
123
|
$rebuildEverything() {
|
|
129
124
|
return this.rebuildEverything();
|
|
@@ -134,23 +129,35 @@ Please enable them from the settings screen after setup is complete.`,
|
|
|
134
129
|
$fetchLocalDBFast(autoResume) {
|
|
135
130
|
return this.fetchLocalDBFast(autoResume);
|
|
136
131
|
}
|
|
137
|
-
async
|
|
132
|
+
async scheduleInitialisation(flag, prepareBeforeRestart) {
|
|
138
133
|
try {
|
|
139
|
-
await this.storageAccess.writeFileAuto(
|
|
134
|
+
await this.storageAccess.writeFileAuto(flag, "");
|
|
140
135
|
} catch (ex) {
|
|
141
|
-
this._log(`Could not create ${
|
|
136
|
+
this._log(`Could not create ${flag}`, LOG_LEVEL_NOTICE);
|
|
142
137
|
this._log(ex, LOG_LEVEL_VERBOSE);
|
|
138
|
+
return false;
|
|
143
139
|
}
|
|
144
|
-
this.appLifecycle.
|
|
145
|
-
}
|
|
146
|
-
async scheduleFetch() {
|
|
140
|
+
this.appLifecycle.setSuspended(true);
|
|
147
141
|
try {
|
|
148
|
-
await
|
|
142
|
+
await prepareBeforeRestart?.();
|
|
149
143
|
} catch (ex) {
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
try {
|
|
145
|
+
await this.storageAccess.delete(flag, true);
|
|
146
|
+
} catch (cleanupError) {
|
|
147
|
+
this._log(`Could not remove ${flag} after restart preparation failed`, LOG_LEVEL_NOTICE);
|
|
148
|
+
this._log(cleanupError, LOG_LEVEL_VERBOSE);
|
|
149
|
+
}
|
|
150
|
+
this.appLifecycle.setSuspended(false);
|
|
151
|
+
throw ex;
|
|
152
152
|
}
|
|
153
153
|
this.appLifecycle.performRestart();
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
scheduleRebuild(prepareBeforeRestart) {
|
|
157
|
+
return this.scheduleInitialisation(FlagFilesHumanReadable.REBUILD_ALL, prepareBeforeRestart);
|
|
158
|
+
}
|
|
159
|
+
scheduleFetch(prepareBeforeRestart) {
|
|
160
|
+
return this.scheduleInitialisation(FlagFilesHumanReadable.FETCH_ALL, prepareBeforeRestart);
|
|
154
161
|
}
|
|
155
162
|
async _tryResetRemoteDatabase() {
|
|
156
163
|
const currentReplicator = this.replicator.getActiveReplicator();
|
|
@@ -269,8 +276,10 @@ Are you sure you wish to proceed?`;
|
|
|
269
276
|
await this.replication.markResolved();
|
|
270
277
|
await delay(500);
|
|
271
278
|
await this.replication.replicateAllFromRemote(true);
|
|
272
|
-
|
|
273
|
-
|
|
279
|
+
if (this.setting.currentSettings().remoteType !== REMOTE_P2P) {
|
|
280
|
+
await delay(1e3);
|
|
281
|
+
await this.replication.replicateAllFromRemote(true);
|
|
282
|
+
}
|
|
274
283
|
if (autoResume) {
|
|
275
284
|
await this.finishRebuild();
|
|
276
285
|
}
|
|
@@ -26,10 +26,15 @@ export declare class ServiceDatabaseFileAccessBase extends ServiceModuleBase<Ser
|
|
|
26
26
|
delete(file: UXFileInfoStub | FilePathWithPrefix, rev?: string): Promise<boolean>;
|
|
27
27
|
createChunks(file: UXFileInfo, force?: boolean, skipCheck?: boolean): Promise<boolean>;
|
|
28
28
|
store(file: UXFileInfo, force?: boolean, skipCheck?: boolean): Promise<boolean>;
|
|
29
|
+
storeWithBaseRevision(file: UXFileInfo, baseRevision: string | undefined, skipCheck?: boolean): Promise<string | false>;
|
|
29
30
|
storeAsConflictedRevision(file: UXFileInfo, currentRev: string, skipCheck?: boolean): Promise<boolean>;
|
|
31
|
+
storeAsConflictedRevisionWithResult(file: UXFileInfo, currentRev: string, skipCheck?: boolean): Promise<string | false>;
|
|
32
|
+
storeDeletionWithBaseRevision(file: UXFileInfoStub | FilePathWithPrefix, baseRevision: string): Promise<string | false>;
|
|
30
33
|
storeContent(path: FilePathWithPrefix, content: string): Promise<boolean>;
|
|
31
34
|
private __store;
|
|
32
35
|
private getParentRev;
|
|
36
|
+
private findContentRevisionsInternal;
|
|
37
|
+
findContentRevisions(file: UXFileInfoStub | FilePathWithPrefix, content: string | string[] | Blob | ArrayBuffer, currentRev?: string): Promise<string[]>;
|
|
33
38
|
hasContentInRevisionHistory(file: UXFileInfoStub | FilePathWithPrefix, content: string | string[] | Blob | ArrayBuffer, currentRev?: string): Promise<boolean>;
|
|
34
39
|
getConflictedRevs(file: UXFileInfoStub | FilePathWithPrefix): Promise<string[]>;
|
|
35
40
|
fetch(file: UXFileInfoStub | FilePathWithPrefix, rev?: string, waitForReady?: boolean, skipCheck?: boolean): Promise<UXFileInfo | false>;
|
|
@@ -47,18 +47,37 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
async createChunks(file, force = false, skipCheck) {
|
|
50
|
-
return await this.__store(file, force, skipCheck, true);
|
|
50
|
+
return await this.__store(file, force, skipCheck, true) !== false;
|
|
51
51
|
}
|
|
52
52
|
async store(file, force = false, skipCheck) {
|
|
53
|
-
return await this.__store(file, force, skipCheck, false);
|
|
53
|
+
return await this.__store(file, force, skipCheck, false) !== false;
|
|
54
|
+
}
|
|
55
|
+
async storeWithBaseRevision(file, baseRevision, skipCheck) {
|
|
56
|
+
const result = await this.__store(file, true, skipCheck, false, baseRevision);
|
|
57
|
+
return result !== null && typeof result === "object" && "rev" in result ? result.rev : false;
|
|
54
58
|
}
|
|
55
59
|
async storeAsConflictedRevision(file, currentRev, skipCheck) {
|
|
60
|
+
return await this.storeAsConflictedRevisionWithResult(file, currentRev, skipCheck) !== false;
|
|
61
|
+
}
|
|
62
|
+
async storeAsConflictedRevisionWithResult(file, currentRev, skipCheck) {
|
|
56
63
|
const conflictBaseRev = await this.getParentRev(file, currentRev);
|
|
57
64
|
if (!conflictBaseRev) {
|
|
58
65
|
this._log(`Could not find parent revision for ${file.path} (${currentRev})`, LOG_LEVEL_VERBOSE);
|
|
59
66
|
return false;
|
|
60
67
|
}
|
|
61
|
-
return await this.
|
|
68
|
+
return await this.storeWithBaseRevision(file, conflictBaseRev, skipCheck);
|
|
69
|
+
}
|
|
70
|
+
async storeDeletionWithBaseRevision(file, baseRevision) {
|
|
71
|
+
if (!await this.checkIsTargetFile(file)) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const fullPath = getDatabasePathFromUXFileInfo(file);
|
|
75
|
+
const result = await this.database.localDatabase.storeDeletionAtRevision(fullPath, baseRevision);
|
|
76
|
+
if (result === false) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
this.events.emitEvent(EVENT_FILE_SAVED);
|
|
80
|
+
return result.rev;
|
|
62
81
|
}
|
|
63
82
|
async storeContent(path, content) {
|
|
64
83
|
const blob = createTextBlob(content);
|
|
@@ -76,7 +95,7 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
76
95
|
body: blob,
|
|
77
96
|
isInternal
|
|
78
97
|
};
|
|
79
|
-
return await this.__store(dummyUXFileInfo, true, false, false);
|
|
98
|
+
return await this.__store(dummyUXFileInfo, true, false, false) !== false;
|
|
80
99
|
}
|
|
81
100
|
async __store(file, force = false, skipCheck, onlyChunks, conflictBaseRev) {
|
|
82
101
|
if (!skipCheck) {
|
|
@@ -169,7 +188,7 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
169
188
|
this._log(msg + fullPath);
|
|
170
189
|
this.events.emitEvent(EVENT_FILE_SAVED);
|
|
171
190
|
}
|
|
172
|
-
return ret
|
|
191
|
+
return ret;
|
|
173
192
|
}
|
|
174
193
|
async getParentRev(file, rev) {
|
|
175
194
|
const filename = getDatabasePathFromUXFileInfo(file);
|
|
@@ -189,10 +208,7 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
189
208
|
return false;
|
|
190
209
|
}
|
|
191
210
|
}
|
|
192
|
-
async
|
|
193
|
-
if (!await this.checkIsTargetFile(file)) {
|
|
194
|
-
return true;
|
|
195
|
-
}
|
|
211
|
+
async findContentRevisionsInternal(file, content, currentRev) {
|
|
196
212
|
const filename = getDatabasePathFromUXFileInfo(file);
|
|
197
213
|
try {
|
|
198
214
|
const doc = await this.database.localDatabase.getDBEntryMeta(
|
|
@@ -201,24 +217,64 @@ class ServiceDatabaseFileAccessBase extends ServiceModuleBase {
|
|
|
201
217
|
true
|
|
202
218
|
);
|
|
203
219
|
if (doc === false) {
|
|
204
|
-
return
|
|
220
|
+
return [];
|
|
205
221
|
}
|
|
206
222
|
const revisions = doc._revs_info;
|
|
207
223
|
const availableRevs = new Set((revisions || []).filter((e) => e.status === "available").map((e) => e.rev));
|
|
208
224
|
if (currentRev) {
|
|
209
225
|
availableRevs.add(currentRev);
|
|
210
226
|
}
|
|
227
|
+
const leaves = await this.database.localDatabase.getRaw(doc._id, {
|
|
228
|
+
open_revs: "all"
|
|
229
|
+
});
|
|
230
|
+
if (!Array.isArray(leaves)) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
for (const leaf of leaves) {
|
|
234
|
+
const leafRev = leaf.ok?._rev;
|
|
235
|
+
if (!leafRev) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const branch = await this.database.localDatabase.getDBEntryMeta(
|
|
239
|
+
filename,
|
|
240
|
+
{ rev: leafRev, revs_info: true },
|
|
241
|
+
true
|
|
242
|
+
);
|
|
243
|
+
if (branch === false) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const branchRevisions = branch._revs_info;
|
|
247
|
+
for (const revision of branchRevisions || []) {
|
|
248
|
+
if (revision.status === "available") {
|
|
249
|
+
availableRevs.add(revision.rev);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const matchingRevisions = [];
|
|
211
254
|
for (const rev of availableRevs) {
|
|
212
255
|
const entry = await this.database.localDatabase.getDBEntry(filename, { rev }, false, true, true);
|
|
213
|
-
if (entry !== false && await isDocContentSame(readContent(entry), content)) {
|
|
214
|
-
|
|
256
|
+
if (entry !== false && !entry._deleted && await isDocContentSame(readContent(entry), content)) {
|
|
257
|
+
matchingRevisions.push(rev);
|
|
215
258
|
}
|
|
216
259
|
}
|
|
260
|
+
return matchingRevisions;
|
|
217
261
|
} catch (ex) {
|
|
218
262
|
this._log(`Could not check revision history for ${filename}`, LOG_LEVEL_VERBOSE);
|
|
219
263
|
this._log(ex, LOG_LEVEL_VERBOSE);
|
|
220
264
|
}
|
|
221
|
-
return
|
|
265
|
+
return [];
|
|
266
|
+
}
|
|
267
|
+
async findContentRevisions(file, content, currentRev) {
|
|
268
|
+
if (!await this.checkIsTargetFile(file)) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
return await this.findContentRevisionsInternal(file, content, currentRev);
|
|
272
|
+
}
|
|
273
|
+
async hasContentInRevisionHistory(file, content, currentRev) {
|
|
274
|
+
if (!await this.checkIsTargetFile(file)) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
return (await this.findContentRevisionsInternal(file, content, currentRev)).length > 0;
|
|
222
278
|
}
|
|
223
279
|
async getConflictedRevs(file) {
|
|
224
280
|
if (!await this.checkIsTargetFile(file)) {
|
|
@@ -11,6 +11,7 @@ import type { ConflictService } from "../services/base/ConflictService.js";
|
|
|
11
11
|
import type { PathService } from "../services/base/PathService.js";
|
|
12
12
|
import type { SettingService } from "../services/base/SettingService.js";
|
|
13
13
|
import type { VaultService } from "../services/base/VaultService.js";
|
|
14
|
+
import type { FileReflectionProvenance } from "../interfaces/FileReflectionProvenance.js";
|
|
14
15
|
export interface ServiceFileHandlerDependencies {
|
|
15
16
|
events: LiveSyncEventHub;
|
|
16
17
|
API: APIService;
|
|
@@ -22,6 +23,17 @@ export interface ServiceFileHandlerDependencies {
|
|
|
22
23
|
path: PathService;
|
|
23
24
|
setting: SettingService;
|
|
24
25
|
vault: VaultService;
|
|
26
|
+
/**
|
|
27
|
+
* Device-local record of the exact database revision reflected in storage.
|
|
28
|
+
*
|
|
29
|
+
* This is optional for compatibility hosts. Maintained hosts should provide
|
|
30
|
+
* it so edits made while a document is conflicted extend the displayed
|
|
31
|
+
* branch instead of whichever branch PouchDB currently selects as winner.
|
|
32
|
+
* The host must finish opening its backing store before it dispatches file
|
|
33
|
+
* or replication events; provenance does not hide lifecycle violations by
|
|
34
|
+
* waiting for readiness.
|
|
35
|
+
*/
|
|
36
|
+
fileReflectionProvenance?: FileReflectionProvenance;
|
|
25
37
|
}
|
|
26
38
|
export declare abstract class ServiceFileHandlerBase extends ServiceModuleBase<ServiceFileHandlerDependencies> implements IFileHandler {
|
|
27
39
|
private events;
|
|
@@ -31,20 +43,28 @@ export declare abstract class ServiceFileHandlerBase extends ServiceModuleBase<S
|
|
|
31
43
|
private path;
|
|
32
44
|
private setting;
|
|
33
45
|
private vault;
|
|
46
|
+
private fileReflectionProvenance?;
|
|
34
47
|
constructor(services: ServiceFileHandlerDependencies);
|
|
35
48
|
get db(): DatabaseFileAccess;
|
|
36
49
|
get storage(): StorageAccess;
|
|
50
|
+
private getProvenance;
|
|
51
|
+
private setProvenance;
|
|
52
|
+
private deleteProvenance;
|
|
53
|
+
private findUniqueContentRevision;
|
|
54
|
+
private getProvenBaseRevision;
|
|
37
55
|
getPath(entry: AnyEntry): FilePathWithPrefix;
|
|
38
56
|
getPathWithoutPrefix(entry: AnyEntry): FilePathWithPrefix;
|
|
39
57
|
readFileFromStub(file: UXFileInfoStub | UXFileInfo): Promise<UXFileInfo>;
|
|
40
58
|
private infoToStub;
|
|
41
59
|
storeFileToDB(info: UXFileInfoStub | UXFileInfo | UXInternalFileInfoStub | FilePathWithPrefix, force?: boolean, onlyChunks?: boolean): Promise<boolean>;
|
|
60
|
+
private storeFileToDBFromRevision;
|
|
42
61
|
deleteFileFromDB(info: UXFileInfoStub | UXInternalFileInfoStub | FilePath): Promise<boolean>;
|
|
43
62
|
renameFileInDB(info: UXFileInfoStub | UXFileInfo, oldPath: FilePath | FilePathWithPrefix): Promise<boolean>;
|
|
44
63
|
deleteRevisionFromDB(info: UXFileInfoStub | FilePath | FilePathWithPrefix, rev: string): Promise<boolean | undefined>;
|
|
45
64
|
resolveConflictedByDeletingRevision(info: UXFileInfoStub | FilePath, rev: string): Promise<boolean | undefined>;
|
|
46
65
|
dbToStorageWithSpecificRev(info: UXFileInfoStub | UXFileInfo | FilePath | null, rev: string, force?: boolean): Promise<boolean>;
|
|
47
66
|
dbToStorage(entryInfo: MetaEntry | FilePathWithPrefix, info: UXFileInfoStub | UXFileInfo | FilePath | null, force?: boolean): Promise<boolean>;
|
|
67
|
+
private applyDatabaseEntryToStorage;
|
|
48
68
|
private preserveUnsyncedStorageAsConflict;
|
|
49
69
|
private _anyHandlerProcessesFileEvent;
|
|
50
70
|
_anyProcessReplicatedDoc(entry: MetaEntry): Promise<boolean>;
|