mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-09-11 21:27:06 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c71650c58 | ||
|
|
37a6db05b9 | ||
|
|
203ccfb74a | ||
|
|
31d30ed4f0 | ||
|
|
7439b75cd0 | ||
|
|
9ffde2dd3e |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.25.83",
|
"version": "0.25.82",
|
||||||
"minAppVersion": "1.7.2",
|
"minAppVersion": "1.7.2",
|
||||||
"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.",
|
"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",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
Generated
+5
-5
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.83",
|
"version": "0.25.82",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.83",
|
"version": "0.25.82",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"src/apps/cli",
|
"src/apps/cli",
|
||||||
@@ -16226,7 +16226,7 @@
|
|||||||
},
|
},
|
||||||
"src/apps/cli": {
|
"src/apps/cli": {
|
||||||
"name": "self-hosted-livesync-cli",
|
"name": "self-hosted-livesync-cli",
|
||||||
"version": "0.25.83-cli",
|
"version": "0.25.82-cli",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"minimatch": "^10.2.5",
|
"minimatch": "^10.2.5",
|
||||||
@@ -16252,7 +16252,7 @@
|
|||||||
},
|
},
|
||||||
"src/apps/webapp": {
|
"src/apps/webapp": {
|
||||||
"name": "livesync-webapp",
|
"name": "livesync-webapp",
|
||||||
"version": "0.25.83-webapp",
|
"version": "0.25.82-webapp",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"octagonal-wheels": "^0.1.51"
|
"octagonal-wheels": "^0.1.51"
|
||||||
},
|
},
|
||||||
@@ -16267,7 +16267,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/apps/webpeer": {
|
"src/apps/webpeer": {
|
||||||
"version": "0.25.83-webpeer",
|
"version": "0.25.82-webpeer",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"octagonal-wheels": "^0.1.51"
|
"octagonal-wheels": "^0.1.51"
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.83",
|
"version": "0.25.82",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"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",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "self-hosted-livesync-cli",
|
"name": "self-hosted-livesync-cli",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.25.83-cli",
|
"version": "0.25.82-cli",
|
||||||
"main": "dist/index.cjs",
|
"main": "dist/index.cjs",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "livesync-webapp",
|
"name": "livesync-webapp",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.25.83-webapp",
|
"version": "0.25.82-webapp",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Browser-based Self-hosted LiveSync using FileSystem API",
|
"description": "Browser-based Self-hosted LiveSync using FileSystem API",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "webpeer",
|
"name": "webpeer",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.25.83-webpeer",
|
"version": "0.25.82-webpeer",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ export class ModuleConflictResolver extends AbstractModule {
|
|||||||
private async _resolveConflictByDeletingRev(
|
private async _resolveConflictByDeletingRev(
|
||||||
path: FilePathWithPrefix,
|
path: FilePathWithPrefix,
|
||||||
deleteRevision: string,
|
deleteRevision: string,
|
||||||
subTitle = "",
|
subTitle = ""
|
||||||
showNotice = true
|
|
||||||
): Promise<typeof MISSING_OR_ERROR | typeof AUTO_MERGED> {
|
): Promise<typeof MISSING_OR_ERROR | typeof AUTO_MERGED> {
|
||||||
const title = `Resolving ${subTitle ? `[${subTitle}]` : ""}:`;
|
const title = `Resolving ${subTitle ? `[${subTitle}]` : ""}:`;
|
||||||
if (!(await this.core.fileHandler.deleteRevisionFromDB(path, deleteRevision))) {
|
if (!(await this.core.fileHandler.deleteRevisionFromDB(path, deleteRevision))) {
|
||||||
@@ -59,7 +58,7 @@ export class ModuleConflictResolver extends AbstractModule {
|
|||||||
this._log(`Could not write the resolved content to the storage: ${path}`, LOG_LEVEL_NOTICE);
|
this._log(`Could not write the resolved content to the storage: ${path}`, LOG_LEVEL_NOTICE);
|
||||||
return MISSING_OR_ERROR;
|
return MISSING_OR_ERROR;
|
||||||
}
|
}
|
||||||
const level = subTitle.indexOf("same") !== -1 || !showNotice ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE;
|
const level = subTitle.indexOf("same") !== -1 ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE;
|
||||||
this._log(`${path} has been merged automatically`, level);
|
this._log(`${path} has been merged automatically`, level);
|
||||||
return AUTO_MERGED;
|
return AUTO_MERGED;
|
||||||
}
|
}
|
||||||
@@ -166,7 +165,7 @@ export class ModuleConflictResolver extends AbstractModule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _anyResolveConflictByNewest(filename: FilePathWithPrefix, showNotice = true): Promise<boolean> {
|
private async _anyResolveConflictByNewest(filename: FilePathWithPrefix): Promise<boolean> {
|
||||||
const currentRev = await this.core.databaseFileAccess.fetchEntryMeta(filename, undefined, true);
|
const currentRev = await this.core.databaseFileAccess.fetchEntryMeta(filename, undefined, true);
|
||||||
if (currentRev == false) {
|
if (currentRev == false) {
|
||||||
this._log(`Could not get current revision of ${filename}`);
|
this._log(`Could not get current revision of ${filename}`);
|
||||||
@@ -204,7 +203,7 @@ export class ModuleConflictResolver extends AbstractModule {
|
|||||||
this._log(
|
this._log(
|
||||||
`conflict: Deleting the older revision ${mTimeAndRev[i][1]} (${new Date(mTimeAndRev[i][0]).toLocaleString()}) of ${filename}`
|
`conflict: Deleting the older revision ${mTimeAndRev[i][1]} (${new Date(mTimeAndRev[i][0]).toLocaleString()}) of ${filename}`
|
||||||
);
|
);
|
||||||
await this._resolveConflictByDeletingRev(filename, mTimeAndRev[i][1], "NEWEST", showNotice);
|
await this.services.conflict.resolveByDeletingRevision(filename, mTimeAndRev[i][1], "NEWEST");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -215,14 +214,13 @@ export class ModuleConflictResolver extends AbstractModule {
|
|||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
i++;
|
if (i++ % 10)
|
||||||
if (i % 10 === 0)
|
|
||||||
this._log(
|
this._log(
|
||||||
`Check and Processing ${i} / ${files.length}`,
|
`Check and Processing ${i} / ${files.length}`,
|
||||||
LOG_LEVEL_NOTICE,
|
LOG_LEVEL_NOTICE,
|
||||||
"resolveAllConflictedFilesByNewerOnes"
|
"resolveAllConflictedFilesByNewerOnes"
|
||||||
);
|
);
|
||||||
await this._anyResolveConflictByNewest(file, false);
|
await this.services.conflict.resolveByNewest(file);
|
||||||
}
|
}
|
||||||
this._log(`Done!`, LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes");
|
this._log(`Done!`, LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
|
||||||
import {
|
|
||||||
DEFAULT_SETTINGS,
|
|
||||||
LOG_LEVEL_INFO,
|
|
||||||
LOG_LEVEL_NOTICE,
|
|
||||||
type FilePathWithPrefix,
|
|
||||||
type MetaEntry,
|
|
||||||
} from "@lib/common/types";
|
|
||||||
import { ModuleConflictResolver } from "./ModuleConflictResolver";
|
|
||||||
|
|
||||||
function createModule(files: FilePathWithPrefix[] = []) {
|
|
||||||
const core = {
|
|
||||||
_services: {
|
|
||||||
API: {
|
|
||||||
addLog: vi.fn(),
|
|
||||||
addCommand: vi.fn(),
|
|
||||||
registerWindow: vi.fn(),
|
|
||||||
addRibbonIcon: vi.fn(),
|
|
||||||
registerProtocolHandler: vi.fn(),
|
|
||||||
},
|
|
||||||
setting: {
|
|
||||||
saveSettingData: vi.fn(async () => undefined),
|
|
||||||
},
|
|
||||||
conflict: {
|
|
||||||
resolveByNewest: vi.fn(async () => true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
settings: DEFAULT_SETTINGS,
|
|
||||||
fileHandler: {
|
|
||||||
deleteRevisionFromDB: vi.fn(async () => true),
|
|
||||||
dbToStorage: vi.fn(async () => true),
|
|
||||||
},
|
|
||||||
databaseFileAccess: {
|
|
||||||
getConflictedRevs: vi.fn(async () => []),
|
|
||||||
},
|
|
||||||
storageAccess: {
|
|
||||||
getFileNames: vi.fn(async () => files),
|
|
||||||
},
|
|
||||||
} as any;
|
|
||||||
Object.defineProperty(core, "services", { get: () => core._services });
|
|
||||||
|
|
||||||
const module = new ModuleConflictResolver(core);
|
|
||||||
module._log = vi.fn();
|
|
||||||
return { module };
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("ModuleConflictResolver bulk newest resolution", () => {
|
|
||||||
it("retains the success notice for a non-bulk newest resolution", async () => {
|
|
||||||
const { module } = createModule();
|
|
||||||
const path = "example.md" as FilePathWithPrefix;
|
|
||||||
module.core.databaseFileAccess.fetchEntryMeta = vi.fn(
|
|
||||||
async (_path: unknown, rev?: string): Promise<MetaEntry> =>
|
|
||||||
({
|
|
||||||
_id: "doc-id",
|
|
||||||
_rev: rev ?? "2-current",
|
|
||||||
path,
|
|
||||||
ctime: 1,
|
|
||||||
mtime: rev ? 1 : 2,
|
|
||||||
size: 0,
|
|
||||||
children: [],
|
|
||||||
type: "plain",
|
|
||||||
eden: {},
|
|
||||||
}) as unknown as MetaEntry
|
|
||||||
);
|
|
||||||
module.core.databaseFileAccess.getConflictedRevs = vi
|
|
||||||
.fn()
|
|
||||||
.mockResolvedValueOnce(["1-old"])
|
|
||||||
.mockResolvedValue([]);
|
|
||||||
|
|
||||||
await (module as any)._anyResolveConflictByNewest(path);
|
|
||||||
|
|
||||||
expect(module._log).toHaveBeenLastCalledWith(`${path} has been merged automatically`, LOG_LEVEL_NOTICE);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("logs a successful bulk newest resolution without displaying a notice", async () => {
|
|
||||||
const { module } = createModule();
|
|
||||||
const path = "example.md" as FilePathWithPrefix;
|
|
||||||
module.core.databaseFileAccess.fetchEntryMeta = vi.fn(
|
|
||||||
async (_path: unknown, rev?: string): Promise<MetaEntry> =>
|
|
||||||
({
|
|
||||||
_id: "doc-id",
|
|
||||||
_rev: rev ?? "2-current",
|
|
||||||
path,
|
|
||||||
ctime: 1,
|
|
||||||
mtime: rev ? 1 : 2,
|
|
||||||
size: 0,
|
|
||||||
children: [],
|
|
||||||
type: "plain",
|
|
||||||
eden: {},
|
|
||||||
}) as unknown as MetaEntry
|
|
||||||
);
|
|
||||||
module.core.databaseFileAccess.getConflictedRevs = vi
|
|
||||||
.fn()
|
|
||||||
.mockResolvedValueOnce(["1-old"])
|
|
||||||
.mockResolvedValue([]);
|
|
||||||
|
|
||||||
await (module as any)._anyResolveConflictByNewest(path, false);
|
|
||||||
|
|
||||||
expect(module._log).toHaveBeenLastCalledWith(`${path} has been merged automatically`, LOG_LEVEL_INFO);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates notice-level progress once every ten checked files", async () => {
|
|
||||||
const files = Array.from({ length: 11 }, (_, index) => `note-${index}.md` as FilePathWithPrefix);
|
|
||||||
const { module } = createModule(files);
|
|
||||||
const resolveByNewest = vi.spyOn(module as any, "_anyResolveConflictByNewest").mockResolvedValue(true);
|
|
||||||
|
|
||||||
await (module as any)._resolveAllConflictedFilesByNewerOnes();
|
|
||||||
|
|
||||||
expect(resolveByNewest).toHaveBeenCalledTimes(11);
|
|
||||||
expect(resolveByNewest).toHaveBeenCalledWith(files[0], false);
|
|
||||||
expect(module._log).toHaveBeenCalledWith(
|
|
||||||
"Check and Processing 10 / 11",
|
|
||||||
LOG_LEVEL_NOTICE,
|
|
||||||
"resolveAllConflictedFilesByNewerOnes"
|
|
||||||
);
|
|
||||||
expect(module._log).toHaveBeenCalledTimes(3);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+2
-9
@@ -5,18 +5,11 @@ The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsid
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
## 0.25.83
|
|
||||||
|
|
||||||
16th July, 2026
|
|
||||||
|
|
||||||
Our plug-in continues to improve every day thanks to all of your contributions. We have finally resolved an issue first reported in 2023. Thank you for everything you contribute.
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed the 📲 remote-activity indicator remaining visible after CouchDB requests had completed.
|
- Fixed the 📲 remote-activity indicator remaining visible after CouchDB requests had completed.
|
||||||
- Fixed missing chunks being reported unavailable while an in-progress on-demand fetch or finite replication could still deliver them. Reads now follow the actual delivery lifecycle and recheck the local database when it finishes.
|
- Fixed missing chunks being reported unavailable while an in-progress on-demand fetch or finite replication could still deliver them. Reads now follow the actual delivery lifecycle and recheck the local database when it finishes.
|
||||||
- Fixed an issue where changing only the letter case of a file name within the same directory could delete it on other devices when 'Handle files as Case-Sensitive' was disabled. Directory case changes remain unsupported (#198, PR #1014; [commonlib PR #68](https://github.com/vrtmrz/livesync-commonlib/pull/68)). Thank you to @metrovoc for the fix!
|
- Fixed an issue where changing only the letter case of a file name within the same directory could delete it on other devices when 'Handle files as Case-Sensitive' was disabled. Directory case changes remain unsupported (#198).
|
||||||
- Fixed **Resolve All conflicted files by the newer one** displaying a separate success notice for every resolved file and updating its progress notice for nine out of every ten checked files. Successful per-file results are now logged, progress updates every ten files, and errors and non-bulk success notices remain visible (#1016, PR #1017). Thank you to @apple-ouyang for the fix!
|
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
@@ -30,7 +23,7 @@ Recently, I created a repository called Fancy Kit and have been trying to build
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Refreshed the remote Security Seed before each replication, preventing a client that remained open during a remote database rebuild from uploading data encrypted with the previous seed (#1018, PR #1019). Thank you to @apple-ouyang for the fix!
|
- Refreshed the remote Security Seed before each replication, preventing a client that remained open during a remote database rebuild from uploading data encrypted with the previous seed (#1018).
|
||||||
- The P2P **Start Sync & Close** action now waits for synchronisation to settle before closing the dialogue, avoiding premature release of screen-awake protection while work remains in flight.
|
- The P2P **Start Sync & Close** action now waits for synchronisation to settle before closing the dialogue, avoiding premature release of screen-awake protection while work remains in flight.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|||||||
+1
-2
@@ -4,6 +4,5 @@
|
|||||||
"1.0.1": "0.9.12",
|
"1.0.1": "0.9.12",
|
||||||
"1.0.0": "0.9.7",
|
"1.0.0": "0.9.7",
|
||||||
"0.25.81": "1.7.2",
|
"0.25.81": "1.7.2",
|
||||||
"0.25.82": "1.7.2",
|
"0.25.82": "1.7.2"
|
||||||
"0.25.83": "1.7.2"
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user