mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-03-14 13:58:49 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce4b61557a | ||
|
|
52b02f3888 | ||
|
|
7535999388 | ||
|
|
dccf8580b8 |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.24.30",
|
||||
"version": "0.24.31",
|
||||
"minAppVersion": "0.9.12",
|
||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"author": "vorotamoroz",
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.24.30",
|
||||
"version": "0.24.31",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.24.30",
|
||||
"version": "0.24.31",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.808.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.24.30",
|
||||
"version": "0.24.31",
|
||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||
"main": "main.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -37,13 +37,17 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
|
||||
// TODO-> Move to ModuleConflictResolver?
|
||||
conflictResolveQueue = new QueueProcessor(
|
||||
async (filenames: FilePathWithPrefix[]) => {
|
||||
await this.core.$$resolveConflict(filenames[0]);
|
||||
const filename = filenames[0];
|
||||
return await this.core.$$resolveConflict(filename);
|
||||
},
|
||||
{
|
||||
suspended: false,
|
||||
batchSize: 1,
|
||||
concurrentLimit: 1,
|
||||
delay: 10,
|
||||
// No need to limit concurrency to `1` here, subsequent process will handle it,
|
||||
// And, some cases, we do not need to synchronised. (e.g., auto-merge available).
|
||||
// Therefore, limiting global concurrency is performed on resolver with the UI.
|
||||
concurrentLimit: 10,
|
||||
delay: 0,
|
||||
keepResultUntilDownstreamConnected: false,
|
||||
}
|
||||
).replaceEnqueueProcessor((queue, newEntity) => {
|
||||
@@ -57,19 +61,13 @@ export class ModuleConflictChecker extends AbstractModule implements ICoreModule
|
||||
new QueueProcessor(
|
||||
(files: FilePathWithPrefix[]) => {
|
||||
const filename = files[0];
|
||||
// const file = await this.core.storageAccess.isExists(filename);
|
||||
// if (!file) return [];
|
||||
// if (!(file instanceof TFile)) return;
|
||||
// if ((file instanceof TFolder)) return [];
|
||||
// Check again?
|
||||
return Promise.resolve([filename]);
|
||||
// this.conflictResolveQueue.enqueueWithKey(filename, { filename, file });
|
||||
},
|
||||
{
|
||||
suspended: false,
|
||||
batchSize: 1,
|
||||
concurrentLimit: 5,
|
||||
delay: 10,
|
||||
concurrentLimit: 10,
|
||||
delay: 0,
|
||||
keepResultUntilDownstreamConnected: true,
|
||||
pipeTo: this.conflictResolveQueue,
|
||||
totalRemainingReactiveSource: this.core.conflictProcessQueueCount,
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ConflictResolveModal } from "./InteractiveConflictResolving/ConflictRes
|
||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||
import { displayRev, getPath, getPathWithoutPrefix } from "../../common/utils.ts";
|
||||
import { fireAndForget } from "octagonal-wheels/promises";
|
||||
import { serialized } from "../../lib/src/concurrency/lock.ts";
|
||||
|
||||
export class ModuleInteractiveConflictResolver extends AbstractObsidianModule implements IObsidianModule {
|
||||
$everyOnloadStart(): Promise<boolean> {
|
||||
@@ -34,67 +35,71 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
||||
}
|
||||
|
||||
async $anyResolveConflictByUI(filename: FilePathWithPrefix, conflictCheckResult: diff_result): Promise<boolean> {
|
||||
this._log("Merge:open conflict dialog", LOG_LEVEL_VERBOSE);
|
||||
const dialog = new ConflictResolveModal(this.app, filename, conflictCheckResult);
|
||||
dialog.open();
|
||||
const selected = await dialog.waitForResult();
|
||||
if (selected === CANCELLED) {
|
||||
// Cancelled by UI, or another conflict.
|
||||
this._log(`Merge: Cancelled ${filename}`, LOG_LEVEL_INFO);
|
||||
return false;
|
||||
}
|
||||
const testDoc = await this.localDatabase.getDBEntry(filename, { conflicts: true }, false, true, true);
|
||||
if (testDoc === false) {
|
||||
this._log(`Merge: Could not read ${filename} from the local database`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
if (!testDoc._conflicts) {
|
||||
this._log(`Merge: Nothing to do ${filename}`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
const toDelete = selected;
|
||||
// const toKeep = conflictCheckResult.left.rev != toDelete ? conflictCheckResult.left.rev : conflictCheckResult.right.rev;
|
||||
if (toDelete === LEAVE_TO_SUBSEQUENT) {
|
||||
// Concatenate both conflicted revisions.
|
||||
// Create a new file by concatenating both conflicted revisions.
|
||||
const p = conflictCheckResult.diff.map((e) => e[1]).join("");
|
||||
const delRev = testDoc._conflicts[0];
|
||||
if (!(await this.core.databaseFileAccess.storeContent(filename, p))) {
|
||||
this._log(`Concatenated content cannot be stored:${filename}`, LOG_LEVEL_NOTICE);
|
||||
// UI for resolving conflicts should one-by-one.
|
||||
return await serialized(`conflict-resolve-ui`, async () => {
|
||||
this._log("Merge:open conflict dialog", LOG_LEVEL_VERBOSE);
|
||||
const dialog = new ConflictResolveModal(this.app, filename, conflictCheckResult);
|
||||
dialog.open();
|
||||
const selected = await dialog.waitForResult();
|
||||
if (selected === CANCELLED) {
|
||||
// Cancelled by UI, or another conflict.
|
||||
this._log(`Merge: Cancelled ${filename}`, LOG_LEVEL_INFO);
|
||||
return false;
|
||||
}
|
||||
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(
|
||||
`Concatenated saved, but cannot delete conflicted revisions: ${filename}, (${displayRev(delRev)})`,
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
const testDoc = await this.localDatabase.getDBEntry(filename, { conflicts: true }, false, true, true);
|
||||
if (testDoc === false) {
|
||||
this._log(`Merge: Could not read ${filename} from the local database`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
} else if (typeof toDelete === "string") {
|
||||
// Select one of the conflicted revision to delete.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) == MISSING_OR_ERROR
|
||||
) {
|
||||
if (!testDoc._conflicts) {
|
||||
this._log(`Merge: Nothing to do ${filename}`, LOG_LEVEL_VERBOSE);
|
||||
return false;
|
||||
}
|
||||
const toDelete = selected;
|
||||
// const toKeep = conflictCheckResult.left.rev != toDelete ? conflictCheckResult.left.rev : conflictCheckResult.right.rev;
|
||||
if (toDelete === LEAVE_TO_SUBSEQUENT) {
|
||||
// Concatenate both conflicted revisions.
|
||||
// Create a new file by concatenating both conflicted revisions.
|
||||
const p = conflictCheckResult.diff.map((e) => e[1]).join("");
|
||||
const delRev = testDoc._conflicts[0];
|
||||
if (!(await this.core.databaseFileAccess.storeContent(filename, p))) {
|
||||
this._log(`Concatenated content cannot be stored:${filename}`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
// 2. As usual, delete the conflicted revision and if there are no conflicts, write the resolved content to the storage.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, delRev, "UI Concatenated")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(
|
||||
`Concatenated saved, but cannot delete conflicted revisions: ${filename}, (${displayRev(delRev)})`,
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} else if (typeof toDelete === "string") {
|
||||
// Select one of the conflicted revision to delete.
|
||||
if (
|
||||
(await this.core.$$resolveConflictByDeletingRev(filename, toDelete, "UI Selected")) ==
|
||||
MISSING_OR_ERROR
|
||||
) {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE);
|
||||
// In here, some merge has been processed.
|
||||
// So we have to run replication if configured.
|
||||
// TODO: Make this is as a event request
|
||||
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||
await this.core.$$replicateByEvent();
|
||||
}
|
||||
// And, check it again.
|
||||
await this.core.$$queueConflictCheck(filename);
|
||||
return false;
|
||||
}
|
||||
// In here, some merge has been processed.
|
||||
// So we have to run replication if configured.
|
||||
// TODO: Make this is as a event request
|
||||
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||
await this.core.$$replicateByEvent();
|
||||
}
|
||||
// And, check it again.
|
||||
await this.core.$$queueConflictCheck(filename);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
async allConflictCheck() {
|
||||
while (await this.pickFileForResolve());
|
||||
|
||||
@@ -367,7 +367,7 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
||||
},
|
||||
enableDebugTools: {
|
||||
name: "Enable Developers' Debug Tools.",
|
||||
desc: "Requires restart of Obsidian",
|
||||
desc: "While enabled, it causes very performance impact but debugging replication testing and other features will be enabled. Please disable this if you have not read the source code. Requires restart of Obsidian.",
|
||||
},
|
||||
suppressNotifyHiddenFilesChange: {
|
||||
name: "Suppress notification of hidden files change",
|
||||
|
||||
67
updates.md
67
updates.md
@@ -1,5 +1,20 @@
|
||||
## 0.24.31
|
||||
|
||||
10th July, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- The description of `Enable Developers' Debug Tools.` has been refined.
|
||||
- Now performance impact is more clearly stated.
|
||||
- Automatic conflict checking and resolution has been improved.
|
||||
- It now works parallelly for each other file, instead of sequentially. It makes significantly faster on first synchronisation when with local files information.
|
||||
- Resolving conflicts dialogue will not be shown for the multiple files at once.
|
||||
- It will be shown for each file, one by one.
|
||||
|
||||
## 0.24.30
|
||||
|
||||
9th July, 2025
|
||||
|
||||
### New Feature
|
||||
|
||||
- New chunking algorithm `V3: Fine deduplication` has been added, and will be recommended after updates.
|
||||
@@ -30,8 +45,14 @@
|
||||
- Never-ending `ObsidianLiveSyncSettingTab.ts` has finally been separated into each pane's file.
|
||||
- Some commented-out code has been removed.
|
||||
|
||||
### Acknowledgement
|
||||
|
||||
- Jun Murakami, Shun Ishiguro, and Yoshihiro Oyama. 2012. Implementation and Evaluation of a Cache Deduplication Mechanism with Content-Defined Chunking. In _IPSJ SIG Technical Report_, Vol.2012-ARC-202, No.4. Information Processing Society of Japan, 1-7.
|
||||
|
||||
## 0.24.29
|
||||
|
||||
20th June, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Synchronisation with buckets now works correctly, regardless of whether a prefix is set or the bucket has been (re-) initialised (#664).
|
||||
@@ -43,6 +64,8 @@
|
||||
|
||||
## 0.24.28
|
||||
|
||||
15th June, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Batch Update is no longer available in LiveSync mode to avoid unexpected behaviour. (#653)
|
||||
@@ -52,6 +75,8 @@
|
||||
|
||||
## 0.24.27
|
||||
|
||||
10th June, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- We can use prefix for path for the Bucket synchronisation.
|
||||
@@ -68,6 +93,8 @@
|
||||
|
||||
## 0.24.26
|
||||
|
||||
14th May, 2025
|
||||
|
||||
This update introduces an option to circumvent Cross-Origin Resource Sharing
|
||||
(CORS) constraints for CouchDB requests, by leveraging Obsidian's native request
|
||||
API. The implementation of such a feature had previously been deferred due to
|
||||
@@ -141,45 +168,5 @@ However, just to whisper, this is tremendously fast.
|
||||
- Dependent libraries have been updated to the latest version.
|
||||
- Some build processes have been separated to `pre` and `post` processes.
|
||||
|
||||
## 0.24.25
|
||||
|
||||
### Improved
|
||||
|
||||
- Peer-to-peer synchronisation has been got more robust.
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken falsy values in settings during set-up by the QR code
|
||||
generation.
|
||||
|
||||
### Refactored
|
||||
|
||||
- Some `window` references now have pointed to `globalThis`.
|
||||
- Some sloppy-import has been fixed.
|
||||
- A server side implementation `Synchromesh` has been suffixed with `deno`
|
||||
instead of `server` now.
|
||||
|
||||
## 0.24.24
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken JSON files including `\n`, during the bucket synchronisation.
|
||||
(#623)
|
||||
- Custom headers and JWT tokens are now correctly sent to the server during
|
||||
configuration checking. (#624)
|
||||
|
||||
### Improved
|
||||
|
||||
- Bucket synchronisation has been enhanced for better performance and
|
||||
reliability.
|
||||
- Now less duplicated chunks are sent to the server. Note: If you have
|
||||
encountered about too less chunks, please let me know. However, you can send
|
||||
it to the server by `Overwrite remote`.
|
||||
- Fetching conflicted files from the server is now more reliable.
|
||||
- Dependent libraries have been updated to the latest version.
|
||||
- Also, let me know if you have encountered any issues with this update.
|
||||
Especially you are using a device that has been in use for a little
|
||||
longer.
|
||||
|
||||
Older notes are in
|
||||
[updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
||||
|
||||
@@ -14,8 +14,54 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
---
|
||||
|
||||
## 0.24.25
|
||||
|
||||
22nd April, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Peer-to-peer synchronisation has been got more robust.
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken falsy values in settings during set-up by the QR code
|
||||
generation.
|
||||
|
||||
### Refactored
|
||||
|
||||
- Some `window` references now have pointed to `globalThis`.
|
||||
- Some sloppy-import has been fixed.
|
||||
- A server side implementation `Synchromesh` has been suffixed with `deno`
|
||||
instead of `server` now.
|
||||
|
||||
## 0.24.24
|
||||
|
||||
15th April, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- No longer broken JSON files including `\n`, during the bucket synchronisation.
|
||||
(#623)
|
||||
- Custom headers and JWT tokens are now correctly sent to the server during
|
||||
configuration checking. (#624)
|
||||
|
||||
### Improved
|
||||
|
||||
- Bucket synchronisation has been enhanced for better performance and
|
||||
reliability.
|
||||
- Now less duplicated chunks are sent to the server. Note: If you have
|
||||
encountered about too less chunks, please let me know. However, you can send
|
||||
it to the server by `Overwrite remote`.
|
||||
- Fetching conflicted files from the server is now more reliable.
|
||||
- Dependent libraries have been updated to the latest version.
|
||||
- Also, let me know if you have encountered any issues with this update.
|
||||
Especially you are using a device that has been in use for a little
|
||||
longer.
|
||||
|
||||
## 0.24.23
|
||||
|
||||
10th April, 2025
|
||||
|
||||
### New Feature
|
||||
|
||||
- Now, we can send custom headers to the server.
|
||||
@@ -37,6 +83,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.22 ~~0.24.21~~
|
||||
|
||||
1st April, 2025
|
||||
|
||||
(Really sorry for the confusion. I have got a miss at releasing...).
|
||||
|
||||
### Fixed
|
||||
@@ -65,6 +113,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.20
|
||||
|
||||
24th March, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Now we can see the detail of `TypeError` using Obsidian API during remote
|
||||
@@ -79,6 +129,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.19
|
||||
|
||||
5th March, 2025
|
||||
|
||||
### New Feature
|
||||
|
||||
- Now we can generate a QR Code for transferring the configuration to another device.
|
||||
@@ -87,6 +139,8 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.18
|
||||
|
||||
28th February, 2025
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now no chunk creation errors will be raised after switching `Compute revisions for chunks`.
|
||||
@@ -106,10 +160,13 @@ Thank you, and I hope your troubles will be resolved!
|
||||
|
||||
## 0.24.17
|
||||
|
||||
27th February, 2025
|
||||
|
||||
Confession. I got the default values wrong. So scary and sorry.
|
||||
|
||||
## 0.24.16
|
||||
|
||||
|
||||
### Improved
|
||||
|
||||
#### Peer-to-Peer
|
||||
|
||||
Reference in New Issue
Block a user