@vrtmrz/livesync-commonlib 0.1.0-rc.1 → 0.1.0-rc.11
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 +90 -20
- 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 +2 -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 +42 -9
- 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/storage.d.ts +10 -1
- package/dist/platform/node/storage.js +86 -25
- 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 +161 -0
- package/docs/development.md +106 -7
- package/docs/p2p-transport-lifecycle.md +62 -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
|
@@ -45,6 +45,7 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
45
45
|
path;
|
|
46
46
|
setting;
|
|
47
47
|
vault;
|
|
48
|
+
fileReflectionProvenance;
|
|
48
49
|
constructor(services) {
|
|
49
50
|
super(services);
|
|
50
51
|
this.events = services.events;
|
|
@@ -54,6 +55,7 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
54
55
|
this.path = services.path;
|
|
55
56
|
this.setting = services.setting;
|
|
56
57
|
this.vault = services.vault;
|
|
58
|
+
this.fileReflectionProvenance = services.fileReflectionProvenance;
|
|
57
59
|
services.fileProcessing.processFileEvent.addHandler(this._anyHandlerProcessesFileEvent.bind(this), 100);
|
|
58
60
|
services.replication.processSynchroniseResult.addHandler(this._anyProcessReplicatedDoc.bind(this), 100);
|
|
59
61
|
}
|
|
@@ -63,6 +65,66 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
63
65
|
get storage() {
|
|
64
66
|
return this.storageAccess;
|
|
65
67
|
}
|
|
68
|
+
async getProvenance(path) {
|
|
69
|
+
if (!this.fileReflectionProvenance) return void 0;
|
|
70
|
+
try {
|
|
71
|
+
const record = await this.fileReflectionProvenance.get(path);
|
|
72
|
+
if (!record) return void 0;
|
|
73
|
+
const entry = await this.db.fetchEntryMeta(path, record.revision, true);
|
|
74
|
+
if (entry && !entry._deleted && !entry.deleted) {
|
|
75
|
+
return record;
|
|
76
|
+
}
|
|
77
|
+
await this.fileReflectionProvenance.delete(path);
|
|
78
|
+
} catch (ex) {
|
|
79
|
+
this._log(`Could not read file reflection provenance for ${path}`, LOG_LEVEL_VERBOSE);
|
|
80
|
+
this._log(ex, LOG_LEVEL_VERBOSE);
|
|
81
|
+
}
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
async setProvenance(path, revision, observedStorageMtime) {
|
|
85
|
+
if (!this.fileReflectionProvenance || !revision) return;
|
|
86
|
+
try {
|
|
87
|
+
await this.fileReflectionProvenance.set(path, {
|
|
88
|
+
revision,
|
|
89
|
+
observedStorageMtime
|
|
90
|
+
});
|
|
91
|
+
} catch (ex) {
|
|
92
|
+
this._log(`Could not record file reflection provenance for ${path}`, LOG_LEVEL_VERBOSE);
|
|
93
|
+
this._log(ex, LOG_LEVEL_VERBOSE);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async deleteProvenance(path) {
|
|
97
|
+
if (!this.fileReflectionProvenance) return;
|
|
98
|
+
try {
|
|
99
|
+
await this.fileReflectionProvenance.delete(path);
|
|
100
|
+
} catch (ex) {
|
|
101
|
+
this._log(`Could not delete file reflection provenance for ${path}`, LOG_LEVEL_VERBOSE);
|
|
102
|
+
this._log(ex, LOG_LEVEL_VERBOSE);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async findUniqueContentRevision(file) {
|
|
106
|
+
try {
|
|
107
|
+
const revisions = await this.db.findContentRevisions(file, file.body);
|
|
108
|
+
return revisions.length === 1 ? revisions[0] : void 0;
|
|
109
|
+
} catch (ex) {
|
|
110
|
+
this._log(`Could not reconstruct file reflection provenance for ${file.path}`, LOG_LEVEL_VERBOSE);
|
|
111
|
+
this._log(ex, LOG_LEVEL_VERBOSE);
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async getProvenBaseRevision(file, preferredPath) {
|
|
116
|
+
const path = preferredPath ?? file.path;
|
|
117
|
+
const recorded = await this.getProvenance(path) ?? (preferredPath && preferredPath !== file.path ? await this.getProvenance(file.path) : void 0);
|
|
118
|
+
if (recorded) {
|
|
119
|
+
return recorded.revision;
|
|
120
|
+
}
|
|
121
|
+
const matched = await this.findUniqueContentRevision(file);
|
|
122
|
+
if (matched) {
|
|
123
|
+
await this.setProvenance(path, matched, file.stat.mtime);
|
|
124
|
+
return matched;
|
|
125
|
+
}
|
|
126
|
+
return void 0;
|
|
127
|
+
}
|
|
66
128
|
getPath(entry) {
|
|
67
129
|
return this.path.getPath(entry);
|
|
68
130
|
}
|
|
@@ -85,6 +147,9 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
85
147
|
return file;
|
|
86
148
|
}
|
|
87
149
|
async storeFileToDB(info, force = false, onlyChunks = false) {
|
|
150
|
+
return await this.storeFileToDBFromRevision(info, force, onlyChunks);
|
|
151
|
+
}
|
|
152
|
+
async storeFileToDBFromRevision(info, force = false, onlyChunks = false, preferredBasePath) {
|
|
88
153
|
const file = await this.infoToStub(info);
|
|
89
154
|
if (file == null) {
|
|
90
155
|
this._log(`File ${tryGetFilePath(info)} is not exist on the storage`, LOG_LEVEL_VERBOSE);
|
|
@@ -97,27 +162,69 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
97
162
|
);
|
|
98
163
|
return false;
|
|
99
164
|
}
|
|
165
|
+
if (onlyChunks) {
|
|
166
|
+
const readFile2 = await this.readFileFromStub(file);
|
|
167
|
+
return await this.db.createChunks(readFile2, force, true);
|
|
168
|
+
}
|
|
169
|
+
const readFile = await this.readFileFromStub(file);
|
|
100
170
|
const entry = await this.db.fetchEntry(file, void 0, true, true);
|
|
171
|
+
const conflictedRevs = await this.db.getConflictedRevs(file);
|
|
172
|
+
const isConflicted = conflictedRevs.length > 0;
|
|
173
|
+
if (isConflicted) {
|
|
174
|
+
const baseRevision = await this.getProvenBaseRevision(readFile, preferredBasePath);
|
|
175
|
+
if (baseRevision) {
|
|
176
|
+
const baseEntry = await this.db.fetchEntry(file, baseRevision, true, true);
|
|
177
|
+
if (baseEntry && await isDocContentSame(getDocDataAsArray(baseEntry.data), readFile.body) && !force) {
|
|
178
|
+
await this.setProvenance(file.path, baseRevision, readFile.stat.mtime);
|
|
179
|
+
this._log(`File ${file.path} is not changed on its displayed conflict branch`, LOG_LEVEL_VERBOSE);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
const storedRevision3 = await this.db.storeWithBaseRevision(readFile, baseRevision, true);
|
|
183
|
+
if (storedRevision3 === false) return false;
|
|
184
|
+
if (preferredBasePath && preferredBasePath !== file.path) {
|
|
185
|
+
await this.deleteProvenance(preferredBasePath);
|
|
186
|
+
}
|
|
187
|
+
await this.setProvenance(file.path, storedRevision3, readFile.stat.mtime);
|
|
188
|
+
await this.conflict.queueCheckFor(file.path);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
const currentRevision = entry && entry._rev;
|
|
192
|
+
if (!currentRevision) {
|
|
193
|
+
this._log(
|
|
194
|
+
`Could not preserve the unknown conflict branch for ${file.path}; no current revision is available`,
|
|
195
|
+
LOG_LEVEL_NOTICE
|
|
196
|
+
);
|
|
197
|
+
await this.conflict.queueCheckFor(file.path);
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
const storedRevision2 = await this.db.storeAsConflictedRevisionWithResult(readFile, currentRevision, true);
|
|
201
|
+
if (storedRevision2 === false) {
|
|
202
|
+
this._log(`Could not preserve the unknown conflict branch for ${file.path}`, LOG_LEVEL_NOTICE);
|
|
203
|
+
await this.conflict.queueCheckFor(file.path);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
if (preferredBasePath && preferredBasePath !== file.path) {
|
|
207
|
+
await this.deleteProvenance(preferredBasePath);
|
|
208
|
+
}
|
|
209
|
+
await this.setProvenance(file.path, storedRevision2, readFile.stat.mtime);
|
|
210
|
+
await this.conflict.queueCheckFor(file.path);
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
101
213
|
if (!entry || entry.deleted || entry._deleted) {
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return await this.db.createChunks(readFile, false, true);
|
|
214
|
+
const storedRevision2 = await this.db.storeWithBaseRevision(readFile, entry && entry._rev, true);
|
|
215
|
+
if (storedRevision2 === false) return false;
|
|
216
|
+
if (preferredBasePath && preferredBasePath !== file.path) {
|
|
217
|
+
await this.deleteProvenance(preferredBasePath);
|
|
107
218
|
}
|
|
219
|
+
await this.setProvenance(file.path, storedRevision2, readFile.stat.mtime);
|
|
220
|
+
return true;
|
|
108
221
|
}
|
|
109
222
|
let shouldApplied = false;
|
|
110
223
|
if (!force && !onlyChunks) {
|
|
111
224
|
if (this.path.compareFileFreshness(file, entry) !== EVEN) {
|
|
112
225
|
shouldApplied = true;
|
|
113
226
|
}
|
|
114
|
-
let readFile = void 0;
|
|
115
227
|
if (!shouldApplied) {
|
|
116
|
-
readFile = await this.readFileFromStub(file);
|
|
117
|
-
if (!readFile) {
|
|
118
|
-
this._log(`File ${file.path} is not exist on the storage`, LOG_LEVEL_NOTICE);
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
228
|
if (await isDocContentSame(getDocDataAsArray(entry.data), readFile.body)) {
|
|
122
229
|
this.path.markChangesAreSame(readFile, readFile.stat.mtime, entry.mtime);
|
|
123
230
|
} else {
|
|
@@ -125,28 +232,23 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
125
232
|
}
|
|
126
233
|
}
|
|
127
234
|
if (!shouldApplied) {
|
|
235
|
+
await this.setProvenance(file.path, entry._rev, readFile.stat.mtime);
|
|
128
236
|
this._log(`File ${file.path} is not changed`, LOG_LEVEL_VERBOSE);
|
|
129
237
|
return true;
|
|
130
238
|
}
|
|
131
|
-
if (!readFile) readFile = await this.readFileFromStub(file);
|
|
132
|
-
if (onlyChunks) {
|
|
133
|
-
return await this.db.createChunks(readFile, false, true);
|
|
134
|
-
} else {
|
|
135
|
-
return await this.db.store(readFile, false, true);
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
const readFile = await this.readFileFromStub(file);
|
|
139
|
-
if (onlyChunks) {
|
|
140
|
-
return await this.db.createChunks(readFile, true, true);
|
|
141
|
-
} else {
|
|
142
|
-
return await this.db.store(readFile, true, true);
|
|
143
|
-
}
|
|
144
239
|
}
|
|
240
|
+
const storedRevision = await this.db.storeWithBaseRevision(readFile, entry._rev, true);
|
|
241
|
+
if (storedRevision === false) return false;
|
|
242
|
+
if (preferredBasePath && preferredBasePath !== file.path) {
|
|
243
|
+
await this.deleteProvenance(preferredBasePath);
|
|
244
|
+
}
|
|
245
|
+
await this.setProvenance(file.path, storedRevision, readFile.stat.mtime);
|
|
246
|
+
return true;
|
|
145
247
|
}
|
|
146
248
|
async deleteFileFromDB(info) {
|
|
147
249
|
const file = await this.infoToStub(info);
|
|
250
|
+
const path = typeof info === "string" ? info : tryGetFilePath(info);
|
|
148
251
|
if (file == null) {
|
|
149
|
-
const path = typeof info === "string" ? info : tryGetFilePath(info);
|
|
150
252
|
if (path === void 0) {
|
|
151
253
|
this._log(`File ${tryGetFilePath(info)} is not exist on the storage`, LOG_LEVEL_VERBOSE);
|
|
152
254
|
return false;
|
|
@@ -159,8 +261,27 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
159
261
|
);
|
|
160
262
|
return false;
|
|
161
263
|
}
|
|
264
|
+
const conflictedRevs2 = await this.db.getConflictedRevs(path);
|
|
265
|
+
if (conflictedRevs2.length > 0) {
|
|
266
|
+
const provenance = await this.getProvenance(path);
|
|
267
|
+
if (!provenance) {
|
|
268
|
+
this._log(
|
|
269
|
+
`The deleted storage file ${path} has conflicts, but its displayed revision is unknown; preserving every database branch`,
|
|
270
|
+
LOG_LEVEL_NOTICE
|
|
271
|
+
);
|
|
272
|
+
await this.conflict.queueCheckFor(path);
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
const storedRevision = await this.db.storeDeletionWithBaseRevision(path, provenance.revision);
|
|
276
|
+
if (storedRevision === false) return false;
|
|
277
|
+
await this.deleteProvenance(path);
|
|
278
|
+
await this.conflict.queueCheckFor(path);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
162
281
|
this._log(`File ${path} is missing on storage; deleting from the database by path`, LOG_LEVEL_INFO);
|
|
163
|
-
|
|
282
|
+
const deleted2 = await this.db.delete(path);
|
|
283
|
+
if (deleted2) await this.deleteProvenance(path);
|
|
284
|
+
return deleted2;
|
|
164
285
|
}
|
|
165
286
|
if (file.isInternal) {
|
|
166
287
|
this._log(
|
|
@@ -176,9 +297,31 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
176
297
|
}
|
|
177
298
|
const conflictedRevs = await this.db.getConflictedRevs(file);
|
|
178
299
|
if (conflictedRevs.length > 0) {
|
|
179
|
-
|
|
300
|
+
let baseRevision = (await this.getProvenance(file.path))?.revision;
|
|
301
|
+
if (!baseRevision) {
|
|
302
|
+
try {
|
|
303
|
+
const readFile = await this.readFileFromStub(file);
|
|
304
|
+
baseRevision = await this.findUniqueContentRevision(readFile);
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (!baseRevision) {
|
|
309
|
+
this._log(
|
|
310
|
+
`The deleted storage file ${file.path} has conflicts, but its displayed revision is unknown; preserving every database branch`,
|
|
311
|
+
LOG_LEVEL_NOTICE
|
|
312
|
+
);
|
|
313
|
+
await this.conflict.queueCheckFor(file.path);
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
const storedRevision = await this.db.storeDeletionWithBaseRevision(file.path, baseRevision);
|
|
317
|
+
if (storedRevision === false) return false;
|
|
318
|
+
await this.deleteProvenance(file.path);
|
|
319
|
+
await this.conflict.queueCheckFor(file.path);
|
|
320
|
+
return true;
|
|
180
321
|
}
|
|
181
|
-
|
|
322
|
+
const deleted = await this.db.delete(file);
|
|
323
|
+
if (deleted) await this.deleteProvenance(file.path);
|
|
324
|
+
return deleted;
|
|
182
325
|
}
|
|
183
326
|
async renameFileInDB(info, oldPath) {
|
|
184
327
|
const newPath = getStoragePathFromUXFileInfo(info);
|
|
@@ -188,9 +331,17 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
188
331
|
]);
|
|
189
332
|
if (oldDocumentId === newDocumentId) {
|
|
190
333
|
this._log(`Updating the stored path for case-only rename: ${oldPath} -> ${newPath}`, LOG_LEVEL_VERBOSE);
|
|
191
|
-
return await this.
|
|
334
|
+
return await this.storeFileToDBFromRevision(info, true, false, oldPath);
|
|
192
335
|
}
|
|
193
336
|
const oldEntry = await this.db.fetchEntryMeta(oldPath, void 0, true);
|
|
337
|
+
const newEntry = await this.db.fetchEntryMeta(newPath, void 0, true);
|
|
338
|
+
if (newEntry && !newEntry.deleted && !newEntry._deleted) {
|
|
339
|
+
this._log(
|
|
340
|
+
`Refusing to overwrite the existing database entry while renaming ${oldPath} to ${newPath}`,
|
|
341
|
+
LOG_LEVEL_NOTICE
|
|
342
|
+
);
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
194
345
|
if (!await this.storeFileToDB(info, true)) {
|
|
195
346
|
this._log(`Failed to store rename target; preserving source in the database: ${oldPath}`, LOG_LEVEL_NOTICE);
|
|
196
347
|
return false;
|
|
@@ -199,13 +350,34 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
199
350
|
this._log(`Rename source is not present in the database: ${oldPath}`, LOG_LEVEL_VERBOSE);
|
|
200
351
|
return true;
|
|
201
352
|
}
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
353
|
+
const oldConflicts = await this.db.getConflictedRevs(oldPath);
|
|
354
|
+
if (oldConflicts.length > 0) {
|
|
355
|
+
let baseRevision = (await this.getProvenance(oldPath))?.revision;
|
|
356
|
+
if (!baseRevision) {
|
|
357
|
+
const readFile = await this.readFileFromStub(info);
|
|
358
|
+
const revisions = await this.db.findContentRevisions(oldPath, readFile.body);
|
|
359
|
+
baseRevision = revisions.length === 1 ? revisions[0] : void 0;
|
|
360
|
+
}
|
|
361
|
+
if (!baseRevision) {
|
|
362
|
+
this._log(
|
|
363
|
+
`Renamed ${oldPath} to ${newPath}, but preserved every conflicted source branch because the displayed source revision is unknown`,
|
|
364
|
+
LOG_LEVEL_NOTICE
|
|
365
|
+
);
|
|
366
|
+
await this.conflict.queueCheckFor(oldPath);
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
const storedRevision = await this.db.storeDeletionWithBaseRevision(
|
|
370
|
+
oldPath,
|
|
371
|
+
baseRevision
|
|
372
|
+
);
|
|
373
|
+
if (storedRevision === false) return false;
|
|
374
|
+
await this.deleteProvenance(oldPath);
|
|
375
|
+
await this.conflict.queueCheckFor(oldPath);
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
const deleted = await this.db.delete(oldPath);
|
|
379
|
+
if (deleted) await this.deleteProvenance(oldPath);
|
|
380
|
+
return deleted;
|
|
209
381
|
}
|
|
210
382
|
async deleteRevisionFromDB(info, rev) {
|
|
211
383
|
return await this.db.delete(info, rev);
|
|
@@ -232,17 +404,20 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
232
404
|
this._log(`File ${file.path} is not exist on the database`, LOG_LEVEL_VERBOSE);
|
|
233
405
|
return false;
|
|
234
406
|
}
|
|
235
|
-
return await this.
|
|
407
|
+
return await this.applyDatabaseEntryToStorage(docEntry, file, force);
|
|
236
408
|
}
|
|
237
409
|
async dbToStorage(entryInfo, info, force) {
|
|
238
410
|
const file = await this.infoToStub(info);
|
|
239
|
-
const mode = file == null ? "create" : "modify";
|
|
240
411
|
const pathFromEntryInfo = typeof entryInfo === "string" ? entryInfo : this.getPath(entryInfo);
|
|
241
412
|
const docEntry = await this.db.fetchEntryMeta(pathFromEntryInfo, void 0, true);
|
|
242
413
|
if (!docEntry) {
|
|
243
414
|
this._log(`File ${pathFromEntryInfo} is not exist on the database`, LOG_LEVEL_VERBOSE);
|
|
244
415
|
return false;
|
|
245
416
|
}
|
|
417
|
+
return await this.applyDatabaseEntryToStorage(docEntry, file, force);
|
|
418
|
+
}
|
|
419
|
+
async applyDatabaseEntryToStorage(docEntry, file, force) {
|
|
420
|
+
const mode = file == null ? "create" : "modify";
|
|
246
421
|
const path = this.getPath(docEntry);
|
|
247
422
|
const settings = this.setting.currentSettings();
|
|
248
423
|
const revs = await this.db.getConflictedRevs(path);
|
|
@@ -261,6 +436,7 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
261
436
|
const existOnDB = !(docEntry._deleted || docEntry.deleted || false);
|
|
262
437
|
if (!existOnDB && !existDoc) {
|
|
263
438
|
this._log(`File ${path} seems to be deleted, but already not on storage`, LOG_LEVEL_VERBOSE);
|
|
439
|
+
await this.deleteProvenance(path);
|
|
264
440
|
return true;
|
|
265
441
|
}
|
|
266
442
|
if (!existOnDB && existDoc) {
|
|
@@ -268,6 +444,7 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
268
444
|
return true;
|
|
269
445
|
}
|
|
270
446
|
await this.storage.deleteVaultItem(path);
|
|
447
|
+
await this.deleteProvenance(path);
|
|
271
448
|
return true;
|
|
272
449
|
}
|
|
273
450
|
if (existDoc && existDoc.path !== path) {
|
|
@@ -294,6 +471,7 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
294
471
|
this._log(`Could not apply the stored filename case: ${existDoc.path} -> ${path}`, LOG_LEVEL_NOTICE);
|
|
295
472
|
return false;
|
|
296
473
|
}
|
|
474
|
+
await this.deleteProvenance(existDoc.path);
|
|
297
475
|
existDoc = renamedFile;
|
|
298
476
|
}
|
|
299
477
|
const docRead = await this.db.fetchEntryFromMeta(docEntry);
|
|
@@ -327,6 +505,7 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
327
505
|
}
|
|
328
506
|
}
|
|
329
507
|
if (!shouldApplied) {
|
|
508
|
+
await this.setProvenance(path, docEntry._rev, existDoc.stat.mtime);
|
|
330
509
|
this._log(`File ${docRead.path} is not changed`, LOG_LEVEL_VERBOSE);
|
|
331
510
|
return true;
|
|
332
511
|
}
|
|
@@ -343,6 +522,10 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
343
522
|
const ret = await this.storage.writeFileAuto(path, docData, { ctime: docRead.ctime, mtime: docRead.mtime });
|
|
344
523
|
await this.storage.touched(path);
|
|
345
524
|
this.storage.triggerFileEvent(mode, path);
|
|
525
|
+
if (ret && this.fileReflectionProvenance) {
|
|
526
|
+
const storedStat = await this.storage.stat(path);
|
|
527
|
+
await this.setProvenance(path, docEntry._rev, storedStat?.mtime);
|
|
528
|
+
}
|
|
346
529
|
return ret;
|
|
347
530
|
}
|
|
348
531
|
async preserveUnsyncedStorageAsConflict(path, existDoc, incomingEntry, incomingContent) {
|
|
@@ -359,11 +542,12 @@ class ServiceFileHandlerBase extends ServiceModuleBase {
|
|
|
359
542
|
if (await this.db.hasContentInRevisionHistory(path, readFile.body, incomingEntry._rev)) {
|
|
360
543
|
return false;
|
|
361
544
|
}
|
|
362
|
-
const
|
|
363
|
-
if (
|
|
545
|
+
const storedRevision = await this.db.storeAsConflictedRevisionWithResult(readFile, incomingEntry._rev, true);
|
|
546
|
+
if (storedRevision === false) {
|
|
364
547
|
this._log(`Prevented overwriting unsynchronised local changes for ${path}`, LOG_LEVEL_NOTICE);
|
|
365
548
|
return true;
|
|
366
549
|
}
|
|
550
|
+
await this.setProvenance(path, storedRevision, readFile.stat.mtime);
|
|
367
551
|
this._log(`Preserved unsynchronised local changes as a conflict for ${path}`, LOG_LEVEL_NOTICE);
|
|
368
552
|
await this.conflict.queueCheckFor(path);
|
|
369
553
|
return true;
|
|
@@ -8,6 +8,7 @@ import type { ReplicatorService } from "./base/ReplicatorService.js";
|
|
|
8
8
|
import type { UIService } from "./implements/base/UIService.js";
|
|
9
9
|
import type { InjectableAPIService } from "./implements/injectable/InjectableAPIService.js";
|
|
10
10
|
import type { KeyValueDatabaseFactory } from "../interfaces/KeyValueDatabase.js";
|
|
11
|
+
import type { ObsidianLiveSyncSettings } from "../common/types.js";
|
|
11
12
|
export type BrowserServiceHostDependencies<T extends ServiceContext> = {
|
|
12
13
|
API: InjectableAPIService<T>;
|
|
13
14
|
appLifecycle: AppLifecycleService<T>;
|
|
@@ -29,6 +30,7 @@ export type BrowserServiceHubOptions<T extends ServiceContext> = {
|
|
|
29
30
|
context?: T;
|
|
30
31
|
host: BrowserServiceHost<T>;
|
|
31
32
|
openKeyValueDatabase?: KeyValueDatabaseFactory;
|
|
33
|
+
onDisplayLanguageChanged?: (language: ObsidianLiveSyncSettings["displayLanguage"]) => void;
|
|
32
34
|
};
|
|
33
35
|
export declare class BrowserServiceHub<T extends ServiceContext> extends InjectableServiceHub<T> {
|
|
34
36
|
get vault(): InjectableVaultServiceCompat<T>;
|
|
@@ -32,7 +32,10 @@ class BrowserServiceHub extends InjectableServiceHub {
|
|
|
32
32
|
const API = options.host.createAPI(context);
|
|
33
33
|
const conflict = new InjectableConflictService(context);
|
|
34
34
|
const fileProcessing = new InjectableFileProcessingService(context);
|
|
35
|
-
const setting = new InjectableSettingService(context, {
|
|
35
|
+
const setting = new InjectableSettingService(context, {
|
|
36
|
+
APIService: API,
|
|
37
|
+
onDisplayLanguageChanged: options.onDisplayLanguageChanged
|
|
38
|
+
});
|
|
36
39
|
const appLifecycle = new BrowserAppLifecycleService(context, { settingService: setting });
|
|
37
40
|
const remote = new InjectableRemoteService(context, {
|
|
38
41
|
pouchDB: PouchDB,
|
|
@@ -4,11 +4,13 @@ import type { DatabaseService } from "./base/DatabaseService.js";
|
|
|
4
4
|
import type { Constructor } from "../common/utils.type.js";
|
|
5
5
|
import type { KeyValueDatabaseFactory } from "../interfaces/KeyValueDatabase.js";
|
|
6
6
|
import type { PouchDBConstructor } from "../pouchdb/PouchDBConstructor.js";
|
|
7
|
+
import type { ObsidianLiveSyncSettings } from "../common/types.js";
|
|
7
8
|
export declare class HeadlessServiceHub<T extends ServiceContext> extends InjectableServiceHub<T> {
|
|
8
9
|
constructor(_context: T | undefined, overrideServiceConstructor: {
|
|
9
10
|
/** PouchDB with the adapters required by this headless runtime. */
|
|
10
11
|
pouchDB: PouchDBConstructor;
|
|
11
12
|
database?: Constructor<DatabaseService<T>>;
|
|
12
13
|
openKeyValueDatabase?: KeyValueDatabaseFactory;
|
|
14
|
+
onDisplayLanguageChanged?: (language: ObsidianLiveSyncSettings["displayLanguage"]) => void;
|
|
13
15
|
});
|
|
14
16
|
}
|
|
@@ -54,7 +54,8 @@ class HeadlessServiceHub extends InjectableServiceHub {
|
|
|
54
54
|
const conflict = new InjectableConflictService(context);
|
|
55
55
|
const fileProcessing = new InjectableFileProcessingService(context);
|
|
56
56
|
const setting = new InjectableSettingService(context, {
|
|
57
|
-
APIService: API
|
|
57
|
+
APIService: API,
|
|
58
|
+
onDisplayLanguageChanged: overrideServiceConstructor.onDisplayLanguageChanged
|
|
58
59
|
});
|
|
59
60
|
const appLifecycle = new HeadlessAppLifecycleService(context, {
|
|
60
61
|
settingService: setting
|