From 31d01f039c91b5f9d01a77ce4a78121e494478f8 Mon Sep 17 00:00:00 2001 From: Temirkanov Alikhan Date: Tue, 23 Jun 2026 17:57:26 +0300 Subject: [PATCH 01/16] Add nginx into table of contents --- docs/setup_own_server.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/setup_own_server.md b/docs/setup_own_server.md index 6e3bcdc..9db4a59 100644 --- a/docs/setup_own_server.md +++ b/docs/setup_own_server.md @@ -23,6 +23,7 @@ - [Setting up your domain](#setting-up-your-domain) - [Reverse Proxies](#reverse-proxies) - [Traefik](#traefik) + - [Nginx](#nginx) --- ## 1. Prepare CouchDB From bf8da52348ef700131c107cfe0948c21c8f77179 Mon Sep 17 00:00:00 2001 From: Ouyang Xingyuan Date: Wed, 24 Jun 2026 10:12:51 +0800 Subject: [PATCH 02/16] Remember pending Simple Fetch choices Simple Fetch asked the same mode and deletion choices again after a failed or interrupted pending Fetch All run. This stores the selected quick-flow choices in local small config until the operation completes, is cancelled, or is finalised. --- src/serviceFeatures/redFlag.simpleFetch.ts | 93 +++++++++++++++++----- src/serviceFeatures/redFlag.unit.spec.ts | 41 ++++++++++ 2 files changed, 114 insertions(+), 20 deletions(-) diff --git a/src/serviceFeatures/redFlag.simpleFetch.ts b/src/serviceFeatures/redFlag.simpleFetch.ts index 211b7dd..981556d 100644 --- a/src/serviceFeatures/redFlag.simpleFetch.ts +++ b/src/serviceFeatures/redFlag.simpleFetch.ts @@ -24,9 +24,73 @@ export const SIMPLE_FETCH_STAGE2_NEWER_CLEANUP = "Delete local files if deleted export const SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL = "Keep local files even if deleted on remote"; export const STAGE2_ABORT = "Cancel all and reboot"; +const SIMPLE_FETCH_MODE_KEY = "simple-fetch-mode"; + +function buildSimpleFetchResult(stage1: string, stage2?: string) { + if (stage1 === SIMPLE_FETCH_STAGE1_LEGACY) { + return { mode: "legacy", options: {} }; + } + if (stage1 === SIMPLE_FETCH_STAGE1_REMOTE_WINS && stage2) { + if (![SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL, SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE].includes(stage2)) { + return undefined; + } + return { + mode: "remote-only", + options: { + mode: FullScanModes.DB_APPLY, + extraOnRemote: + stage2 === SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL ? ExtraOnRemote.DELETE_LOCAL_MISSING : undefined, + }, + }; + } + if (stage1 === SIMPLE_FETCH_STAGE1_NEWER_WINS && stage2) { + if (![SIMPLE_FETCH_STAGE2_NEWER_CLEANUP, SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL].includes(stage2)) { + return undefined; + } + return { + mode: "newer-wins", + options: { + mode: FullScanModes.NEWER_WINS, + extraOnLocal: + stage2 === SIMPLE_FETCH_STAGE2_NEWER_CLEANUP + ? ExtraOnLocal.DELETE_DB_DELETED + : ExtraOnLocal.APPEND_STORAGE_ONLY, + }, + }; + } + return undefined; +} + +function rememberSimpleFetchMode(host: NecessaryServices<"setting", never>, stage1: string, stage2?: string) { + host.services.setting.setSmallConfig(SIMPLE_FETCH_MODE_KEY, JSON.stringify({ stage1, stage2 })); +} + +function getRememberedSimpleFetchMode(host: NecessaryServices<"setting", never>) { + const saved = host.services.setting.getSmallConfig(SIMPLE_FETCH_MODE_KEY); + if (!saved) return undefined; + try { + const { stage1, stage2 } = JSON.parse(saved) as { stage1?: string; stage2?: string }; + if (stage1) { + const remembered = buildSimpleFetchResult(stage1, stage2); + if (remembered) return remembered; + } + } catch { + // Clear below; the saved choice is optional and can be rebuilt by asking again. + } + host.services.setting.deleteSmallConfig(SIMPLE_FETCH_MODE_KEY); + return undefined; +} + +function clearRememberedSimpleFetchMode(host: NecessaryServices<"setting", never>) { + host.services.setting.deleteSmallConfig(SIMPLE_FETCH_MODE_KEY); +} + export async function askSimpleFetchMode( - host: NecessaryServices<"UI" | "vault", "storageAccess"> + host: NecessaryServices<"UI" | "setting", never> ): Promise<{ mode: string; options: Partial } | "cancelled" | "aborted"> { + const remembered = getRememberedSimpleFetchMode(host); + if (remembered) return remembered; + const msg = `We are about to retrieve the remote data. Firstly, how shall we handle the data retrieved from this remote server? @@ -55,7 +119,7 @@ Firstly, how shall we handle the data retrieved from this remote server? if (!stage1 || stage1 === SIMPLE_FETCH_STAGE1_CANCEL) return "cancelled"; if (stage1 === SIMPLE_FETCH_STAGE1_LEGACY) { - return { mode: "legacy", options: {} }; + return buildSimpleFetchResult(stage1)!; } if (stage1 === SIMPLE_FETCH_STAGE1_REMOTE_WINS) { @@ -77,14 +141,8 @@ Firstly, how shall we handle the data retrieved from this remote server? if (stage2 === STAGE2_ABORT) { return "aborted"; } - return { - mode: "remote-only", - options: { - mode: FullScanModes.DB_APPLY, - extraOnRemote: - stage2 === SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL ? ExtraOnRemote.DELETE_LOCAL_MISSING : undefined, - }, - }; + rememberSimpleFetchMode(host, stage1, stage2); + return buildSimpleFetchResult(stage1, stage2)!; } if (stage1 === SIMPLE_FETCH_STAGE1_NEWER_WINS) { @@ -107,16 +165,8 @@ Firstly, how shall we handle the data retrieved from this remote server? if (stage2 === STAGE2_ABORT) { return "aborted"; } - return { - mode: "newer-wins", - options: { - mode: FullScanModes.NEWER_WINS, - extraOnLocal: - stage2 === SIMPLE_FETCH_STAGE2_NEWER_CLEANUP - ? ExtraOnLocal.DELETE_DB_DELETED - : ExtraOnLocal.APPEND_STORAGE_ONLY, - }, - }; + rememberSimpleFetchMode(host, stage1, stage2); + return buildSimpleFetchResult(stage1, stage2)!; } return "cancelled"; @@ -143,12 +193,14 @@ export async function askAndPerformFastSetupOnScheduledFetchAll( const result = await askSimpleFetchMode(host); if (result === "cancelled") { log("Fetch cancelled by user.", LOG_LEVEL_NOTICE); + clearRememberedSimpleFetchMode(host); await cleanupFlag(); host.services.appLifecycle.performRestart(); return false; } if (result === "aborted") { log("Fetch exited by user.", LOG_LEVEL_NOTICE); + clearRememberedSimpleFetchMode(host); host.services.appLifecycle.performRestart(); return false; } @@ -191,6 +243,7 @@ export async function askAndPerformFastSetupOnScheduledFetchAll( } await host.serviceModules.rebuilder.finishRebuild(); await cleanupFlag(); + clearRememberedSimpleFetchMode(host); log("Simple fetch and scan operation completed.", LOG_LEVEL_NOTICE); return true; }); diff --git a/src/serviceFeatures/redFlag.unit.spec.ts b/src/serviceFeatures/redFlag.unit.spec.ts index 7ab9e70..f94103e 100644 --- a/src/serviceFeatures/redFlag.unit.spec.ts +++ b/src/serviceFeatures/redFlag.unit.spec.ts @@ -85,6 +85,7 @@ const createSettingServiceMock = () => { writeLogToTheFile: false, remoteType: "CouchDB", }; + const smallConfig = new Map(); return { settings, currentSettings: vi.fn(() => settings), @@ -98,6 +99,13 @@ const createSettingServiceMock = () => { }), suspendAllSync: vi.fn(() => Promise.resolve()), suspendExtraSync: vi.fn(() => Promise.resolve()), + getSmallConfig: vi.fn((key: string) => smallConfig.get(key) ?? ""), + setSmallConfig: vi.fn((key: string, value: string) => { + smallConfig.set(key, value); + }), + deleteSmallConfig: vi.fn((key: string) => { + smallConfig.delete(key); + }), }; }; @@ -739,6 +747,39 @@ describe("Red Flag Feature", () => { }); describe("askAndPerformFastSetupOnScheduledFetchAll", () => { + it("should remember quick flow choices while the scheduled fetch is pending", async () => { + const host = createHostMock(); + const log = createLoggerMock(); + const cleanupFlag = vi.fn().mockResolvedValue(undefined); + + host.mocks.ui.confirm.confirmWithMessage + .mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_NEWER_WINS) + .mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_NEWER_CLEANUP); + host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValue({ batchSave: false } as any); + host.mocks.rebuilder.$fetchLocalDBFast.mockRejectedValueOnce(new Error("offline")); + + await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag); + await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag); + + expect(host.mocks.ui.confirm.confirmWithMessage).toHaveBeenCalledTimes(2); + expect(host.mocks.rebuilder.$fetchLocalDBFast).toHaveBeenCalledTimes(2); + }); + + it("should clear remembered quick flow choices after completion", async () => { + const host = createHostMock(); + const log = createLoggerMock(); + const cleanupFlag = vi.fn().mockResolvedValue(undefined); + + host.mocks.ui.confirm.confirmWithMessage + .mockResolvedValueOnce(SIMPLE_FETCH_STAGE1_REMOTE_WINS) + .mockResolvedValueOnce(SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL); + host.mocks.tweakValue.fetchRemotePreferred.mockResolvedValue({ batchSave: false } as any); + + await askAndPerformFastSetupOnScheduledFetchAll(host as any, log, cleanupFlag); + + expect(host.mocks.setting.deleteSmallConfig).toHaveBeenCalledWith("simple-fetch-mode"); + }); + it("should return false and cleanup when quick flow is cancelled", async () => { const host = createHostMock(); const log = createLoggerMock(); From b2ea810dde648b24fa34e0b14d3aeec720f98089 Mon Sep 17 00:00:00 2001 From: Ouyang Xingyuan Date: Wed, 24 Jun 2026 10:13:01 +0800 Subject: [PATCH 03/16] Update commonlib for Fast Fetch interruption retries The plug-in needs the commonlib change that retries transient Fast Fetch failures from the latest checkpoint. This updates the src/lib submodule pointer to the commonlib commit with retry and AbortError handling. --- src/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib b/src/lib index 0563f26..17c8c24 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 0563f267ecac7b767b446f8528b0a4c9c5f91d25 +Subproject commit 17c8c245bb1d49f2b86b16a73341ed8668eddad6 From 7e59010824ef75b0758644ef20bf776897f24a34 Mon Sep 17 00:00:00 2001 From: Ouyang Xingyuan Date: Thu, 25 Jun 2026 14:41:14 +0800 Subject: [PATCH 04/16] Ask before applying maintenance prerequisite settings Local database maintenance actions require local chunk revisions and fully replicated chunks, but previously failed with a notice when those settings were not already configured. This adds a small prerequisite confirmation helper for the maintenance commands. When required settings are missing, the user can apply them and continue from the action they already started, or cancel without changing settings. Covers both required maintenance settings: - Compute revisions for chunks enabled - Fetch chunks on demand disabled Fixes #980 --- .../CmdLocalDatabaseMainte.ts | 38 ++++---- .../CmdLocalDatabaseMainte.unit.spec.ts | 89 +++++++++++++++++++ .../maintenancePrerequisites.ts | 54 +++++++++++ 3 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.unit.spec.ts create mode 100644 src/features/LocalDatabaseMainte/maintenancePrerequisites.ts diff --git a/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts index 928149c..b9a3f49 100644 --- a/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts +++ b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts @@ -18,6 +18,7 @@ import { EVENT_ANALYSE_DB_USAGE, EVENT_REQUEST_PERFORM_GC_V3, eventHub } from "@ import type { LiveSyncCouchDBReplicator } from "@lib/replication/couchdb/LiveSyncReplicator"; import { delay } from "@lib/common/utils"; import { isNotFoundError } from "@lib/common/utils.doc"; +import { ensureLocalDatabaseMaintenancePrerequisites } from "./maintenancePrerequisites"; // import { _requestToCouchDB } from "@/common/utils"; const DB_KEY_SEQ = "gc-seq"; const DB_KEY_CHUNK_SET = "chunk-set"; @@ -77,22 +78,22 @@ export class LocalDatabaseMaintenance extends LiveSyncCommands { })) === affirmative ); } - isAvailable() { - if (!this.settings.doNotUseFixedRevisionForChunks) { - this._notice("Please enable 'Compute revisions for chunks' in settings to use Garbage Collection."); - return false; - } - if (this.settings.readChunksOnline) { - this._notice("Please disable 'Read chunks online' in settings to use Garbage Collection."); - return false; - } - return true; + async ensureAvailable(operationName: string) { + return await ensureLocalDatabaseMaintenancePrerequisites({ + operationName, + settings: this.settings, + askSelectStringDialogue: this.core.confirm.askSelectStringDialogue.bind(this.core.confirm), + applyPartial: async (settings, saveImmediately) => { + await this.core.services.setting.applyPartial(settings, saveImmediately); + Object.assign(this.core.settings, settings); + }, + }); } /** * Resurrect deleted chunks that are still used in the database. */ async resurrectChunks() { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Resurrect Chunks"))) return; const { used, existing } = await this.allChunks(true); const excessiveDeletions = [...existing] .filter(([key, e]) => e._deleted) @@ -157,7 +158,7 @@ Do you want to resurrect these chunks?`; * After this, chunks that are used in the deleted files become ready for compaction. */ async commitFileDeletion() { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Delete Files"))) return; const p = this._progress("", LOG_LEVEL_NOTICE); p.log("Searching for deleted files.."); const docs = await this.database.allDocs({ include_docs: true }); @@ -199,7 +200,7 @@ Note: **Make sure to synchronise all devices before deletion.** * It is recommended to compact the database after this operation (History should be kept once before compaction). */ async commitChunkDeletion() { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Delete Chunks"))) return; const { existing } = await this.allChunks(true); const deletedChunks = [...existing].filter(([key, e]) => e._deleted && e.data !== "").map(([key, e]) => e); const deletedNotVacantChunks = deletedChunks.map((e) => ({ ...e, data: "", _deleted: true })); @@ -236,7 +237,7 @@ Note: **Make sure to synchronise all devices before deletion.** * Make sure all devices are synchronized before running this method. */ async markUnusedChunks() { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Mark unused chunks"))) return; const { used, existing } = await this.allChunks(); const existChunks = [...existing]; const unusedChunks = existChunks.filter(([key, e]) => !used.has(e._id)).map(([key, e]) => e); @@ -269,6 +270,7 @@ Note: **Make sure to synchronise all devices before deletion.** } async removeUnusedChunks() { + if (!(await this.ensureAvailable("Delete unused chunks"))) return; const { used, existing } = await this.allChunks(); const existChunks = [...existing]; const unusedChunks = existChunks.filter(([key, e]) => !used.has(e._id)).map(([key, e]) => e); @@ -326,7 +328,7 @@ Note: **Make sure to synchronise all devices before deletion.** * Note that this only able to perform without Fetch chunks on demand. */ async trackChanges(fromStart: boolean = false, showNotice: boolean = false) { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Track chunk usage"))) return; const logLevel = showNotice ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO; const kvDB = this.core.kvDB; @@ -442,7 +444,7 @@ Note: **Make sure to synchronise all devices before deletion.** this._log(message, logLevel); } async performGC(showingNotice = false) { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Garbage Collection"))) return; await this.trackChanges(false, showingNotice); const title = "Are all devices synchronised?"; const confirmMessage = `This function deletes unused chunks from the device. If there are differences between devices, some chunks may be missing when resolving conflicts. @@ -512,7 +514,7 @@ Success: ${successCount}, Errored: ${errored}`; // Analyse the database and report chunk usage. async analyseDatabase() { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Analyse Database Usage"))) return; const db = this.localDatabase.localDatabase; // Map of chunk ID to its info type ChunkInfo = { @@ -822,7 +824,7 @@ Success: ${successCount}, Errored: ${errored}`; // } // } async gcv3() { - if (!this.isAvailable()) return; + if (!(await this.ensureAvailable("Garbage Collection"))) return; const replicator = this.core.replicator as LiveSyncCouchDBReplicator; // Start one-shot replication to ensure all changes are synced before GC. const r0 = await replicator.openOneShotReplication(this.settings, false, false, "sync"); diff --git a/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.unit.spec.ts b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.unit.spec.ts new file mode 100644 index 0000000..6b295c0 --- /dev/null +++ b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.unit.spec.ts @@ -0,0 +1,89 @@ +import { describe, expect, it, vi } from "vitest"; +import { DEFAULT_SETTINGS } from "@lib/common/types"; +import { ensureLocalDatabaseMaintenancePrerequisites } from "./maintenancePrerequisites"; + +function createPrerequisites(settingsOverride: Partial = {}) { + const askSelectStringDialogue = vi.fn<() => Promise<"Apply and continue" | "Cancel" | false | undefined>>( + async () => "Apply and continue" + ); + const applyPartial = vi.fn(async () => undefined); + const settings = { + ...DEFAULT_SETTINGS, + doNotUseFixedRevisionForChunks: false, + readChunksOnline: true, + ...settingsOverride, + }; + + return { settings, askSelectStringDialogue, applyPartial }; +} + +describe("LocalDatabaseMaintenance prerequisites", () => { + it("asks to apply missing prerequisite settings before maintenance actions", async () => { + const { settings, askSelectStringDialogue, applyPartial } = createPrerequisites(); + + const result = await ensureLocalDatabaseMaintenancePrerequisites({ + operationName: "Garbage Collection", + settings: { + doNotUseFixedRevisionForChunks: settings.doNotUseFixedRevisionForChunks, + readChunksOnline: settings.readChunksOnline, + }, + askSelectStringDialogue, + applyPartial, + }); + + expect(result).toBe(true); + expect(askSelectStringDialogue).toHaveBeenCalledWith( + expect.stringContaining("Garbage Collection requires the following settings"), + ["Apply and continue", "Cancel"], + { + title: "Garbage Collection prerequisites", + defaultAction: "Cancel", + } + ); + expect(applyPartial).toHaveBeenCalledWith( + { + doNotUseFixedRevisionForChunks: true, + readChunksOnline: false, + }, + true + ); + }); + + it("cancels maintenance actions when prerequisite changes are rejected", async () => { + const { settings, askSelectStringDialogue, applyPartial } = createPrerequisites(); + askSelectStringDialogue.mockResolvedValueOnce("Cancel"); + + const result = await ensureLocalDatabaseMaintenancePrerequisites({ + operationName: "Garbage Collection", + settings: { + doNotUseFixedRevisionForChunks: settings.doNotUseFixedRevisionForChunks, + readChunksOnline: settings.readChunksOnline, + }, + askSelectStringDialogue, + applyPartial, + }); + + expect(result).toBe(false); + expect(applyPartial).not.toHaveBeenCalled(); + }); + + it("continues without asking when prerequisite settings already match", async () => { + const { settings, askSelectStringDialogue, applyPartial } = createPrerequisites({ + doNotUseFixedRevisionForChunks: true, + readChunksOnline: false, + }); + + const result = await ensureLocalDatabaseMaintenancePrerequisites({ + operationName: "Garbage Collection", + settings: { + doNotUseFixedRevisionForChunks: settings.doNotUseFixedRevisionForChunks, + readChunksOnline: settings.readChunksOnline, + }, + askSelectStringDialogue, + applyPartial, + }); + + expect(askSelectStringDialogue).not.toHaveBeenCalled(); + expect(applyPartial).not.toHaveBeenCalled(); + }); +}); diff --git a/src/features/LocalDatabaseMainte/maintenancePrerequisites.ts b/src/features/LocalDatabaseMainte/maintenancePrerequisites.ts new file mode 100644 index 0000000..35c474d --- /dev/null +++ b/src/features/LocalDatabaseMainte/maintenancePrerequisites.ts @@ -0,0 +1,54 @@ +import type { ObsidianLiveSyncSettings } from "@lib/common/types"; + +type MaintenancePrerequisiteSettings = Pick< + ObsidianLiveSyncSettings, + "doNotUseFixedRevisionForChunks" | "readChunksOnline" +>; + +type MaintenancePrerequisiteOptions = { + operationName: string; + settings: MaintenancePrerequisiteSettings; + askSelectStringDialogue: ( + message: string, + buttons: readonly ["Apply and continue", "Cancel"], + options: { title: string; defaultAction: "Cancel" } + ) => Promise<"Apply and continue" | "Cancel" | false | undefined>; + applyPartial: (settings: Partial, saveImmediately?: boolean) => Promise; +}; + +export async function ensureLocalDatabaseMaintenancePrerequisites({ + operationName, + settings, + askSelectStringDialogue, + applyPartial, +}: MaintenancePrerequisiteOptions): Promise { + const requiredSettings = { + doNotUseFixedRevisionForChunks: true, + readChunksOnline: false, + } satisfies MaintenancePrerequisiteSettings; + + const missing = [ + ...(settings.doNotUseFixedRevisionForChunks ? [] : ["- Compute revisions for chunks: On (currently Off)"]), + ...(settings.readChunksOnline ? ["- Fetch chunks on demand: Off (currently On)"] : []), + ]; + + if (missing.length == 0) return true; + + const APPLY = "Apply and continue"; + const CANCEL = "Cancel"; + const result = await askSelectStringDialogue( + `${operationName} requires the following settings:\n\n${missing.join( + "\n" + )}\n\nApply these settings and continue?`, + [APPLY, CANCEL], + { + title: `${operationName} prerequisites`, + defaultAction: CANCEL, + } + ); + + if (result !== APPLY) return false; + + await applyPartial(requiredSettings, true); + return true; +} From be23fa51a15e0a94b2d637d8840ce520034f268c Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 26 Jun 2026 10:33:51 +0000 Subject: [PATCH 05/16] (test): the E2E test on the real-Obsidian --- docs/adr/2026_06_real_obsidian_e2e.md | 216 +++ package.json | 4 + src/apps/cli/README.md | 1268 ++++++++--------- test/e2e-obsidian/README.md | 65 + test/e2e-obsidian/runner/cli.ts | 62 + test/e2e-obsidian/runner/environment.ts | 149 ++ test/e2e-obsidian/runner/launch.ts | 119 ++ test/e2e-obsidian/runner/pluginInstaller.ts | 39 + test/e2e-obsidian/runner/readiness.ts | 52 + test/e2e-obsidian/runner/vault.ts | 72 + test/e2e-obsidian/scripts/cli-help.ts | 51 + test/e2e-obsidian/scripts/discover.ts | 13 + test/e2e-obsidian/scripts/install-appimage.ts | 121 ++ test/e2e-obsidian/scripts/smoke.ts | 96 ++ 14 files changed, 1693 insertions(+), 634 deletions(-) create mode 100644 docs/adr/2026_06_real_obsidian_e2e.md create mode 100644 test/e2e-obsidian/README.md create mode 100644 test/e2e-obsidian/runner/cli.ts create mode 100644 test/e2e-obsidian/runner/environment.ts create mode 100644 test/e2e-obsidian/runner/launch.ts create mode 100644 test/e2e-obsidian/runner/pluginInstaller.ts create mode 100644 test/e2e-obsidian/runner/readiness.ts create mode 100644 test/e2e-obsidian/runner/vault.ts create mode 100644 test/e2e-obsidian/scripts/cli-help.ts create mode 100644 test/e2e-obsidian/scripts/discover.ts create mode 100644 test/e2e-obsidian/scripts/install-appimage.ts create mode 100644 test/e2e-obsidian/scripts/smoke.ts diff --git a/docs/adr/2026_06_real_obsidian_e2e.md b/docs/adr/2026_06_real_obsidian_e2e.md new file mode 100644 index 0000000..7fa8045 --- /dev/null +++ b/docs/adr/2026_06_real_obsidian_e2e.md @@ -0,0 +1,216 @@ +# Architectural Decision Record: Real Obsidian End-to-End Test Runner + +## Status + +Proposed / Spike Implemented + +## Release + +Not yet. Planned after the serviceFeature refactoring branch is reviewed. + +## Context + +The current end-to-end tests run through Vitest browser mode and a mocked Obsidian environment in `test/harness`. This has been useful for exercising synchronisation flows without launching Obsidian, but it is no longer a reliable final signal for plug-in behaviour. + +The main issues are: + +- The harness reimplements a large part of the Obsidian API surface, including vault files, workspace events, settings, and lifecycle behaviour. This mock can drift from real Obsidian behaviour without failing. +- The tests run inside a browser-style environment, while the desktop plug-in runs inside Obsidian's Electron environment with its own application lifecycle, storage paths, command registry, and event ordering. +- Several high-value regressions are about integration boundaries: boot-up sequence timing, real vault file reflection, Obsidian command registration, settings persistence, restart prompts, and file watcher behaviour. These are precisely the areas where a mock harness gives weak confidence. +- Maintaining the harness competes with maintaining the plug-in. Adding behaviour to the plug-in often requires teaching the mock another Obsidian detail before the actual regression can be tested. + +The current harness should therefore stop being treated as the primary E2E layer. + +## Decision + +Introduce a new E2E layer that launches real Obsidian with temporary vaults and the built Self-hosted LiveSync plug-in installed into those vaults. + +The long-term test pyramid should be: + +1. Unit tests for deterministic operations and serviceFeature boundaries. +2. Integration tests for CouchDB, Object Storage, P2P services, database operations, and replication protocols. +3. Real Obsidian E2E tests for boot-up sequence, vault reflection, command registration, settings dialogues, restart scheduling, and user-visible workflows. + +The existing `test/harness` should be demoted to a transitional compatibility layer. It may remain temporarily while the real Obsidian runner reaches parity for critical flows, but new high-level E2E coverage should target the real runner. + +## Non-Goals + +- Do not replace unit or integration tests with slow UI tests. +- Do not keep extending the Obsidian mock to cover new Obsidian APIs unless a short-term compatibility bridge is required. +- Do not require real Obsidian E2E for every pull request initially. The first CI integration should be opt-in or nightly until stability is proven. +- Do not test every setting dialogue through UI clicks if the behaviour is already covered by unit or integration tests. Use UI automation only for workflows whose risk is in real Obsidian integration. + +## Proposed Architecture + +### Runner + +Create a dedicated runner under `test/e2e-obsidian/`. + +The runner should: + +- Create one or more temporary vault directories. +- Build the plug-in once with `npm run build` or a narrower production build command. +- Install `main.js`, `manifest.json`, and `styles.css` when present into `.obsidian/plugins/obsidian-livesync/`. +- Prepare `.obsidian/community-plugins.json` and `.obsidian/plugins/obsidian-livesync/data.json` as needed. +- Launch Obsidian against the temporary vault. +- Wait until the plug-in reports readiness through a deterministic probe. +- Drive assertions through a narrow control channel rather than fragile visual selectors wherever possible. +- Dispose of Obsidian and temporary vaults after each scenario. + +### Obsidian Launch + +The preferred desktop target is the installed Obsidian application. The launch mechanism should be platform-specific but hidden behind a small adapter: + +- Linux: launch the Obsidian executable with a vault path or Obsidian URI, depending on what is most reliable. If an AppImage is used and FUSE is not available, extract it with `--appimage-extract` and launch the extracted `squashfs-root/obsidian` binary. +- macOS: launch the app bundle through `open` or the executable inside the bundle. +- Windows: launch the installed executable or the registered application protocol. + +The first implementation can support Linux only if that is the local and CI target. Cross-platform support can be added after the runner contract is stable. + +In headless Linux environments, launch through `xvfb-run`, pass Electron flags such as `--no-sandbox` and `--disable-gpu`, and isolate `HOME`, `XDG_CONFIG_HOME`, and `--user-data-dir` per temporary vault. + +### Control Channel + +The runner needs a stable way to observe readiness and issue test commands. Prefer a test-only plug-in bridge compiled only in test builds or enabled only by an environment variable. + +Possible bridge options: + +- The official Obsidian CLI, using the installed `obsidian-cli` helper to open vaults, reload the plug-in, run `eval`, and call developer commands. +- A local HTTP/WebSocket bridge bound to `127.0.0.1` with a random port and token. +- A file-based bridge in the vault, where Obsidian writes status files and consumes command files. +- A DevTools protocol bridge if Obsidian exposes a stable debugging port in the test environment. + +The first implementation uses Obsidian's CLI for orchestration and readiness checks. The CLI handles vault opening through `obsidian://open?path=...`, enables community plug-ins through `app.plugins.setEnable(true)`, reloads Self-hosted LiveSync through `plugin:reload id=obsidian-livesync`, and verifies that `app.plugins.plugins['obsidian-livesync']` is loaded. + +This keeps E2E-only behaviour out of the production plug-in bundle. The runner should not require Self-hosted LiveSync to write marker files or expose a test server merely to prove that Obsidian loaded it. + +The DevTools protocol remains useful for diagnostics. Obsidian's CLI exposes developer commands such as `dev:cdp`, `dev:errors`, and `dev:console`, so the runner should prefer the CLI path first and fall back to direct DevTools attachment only if the CLI cannot provide the required signal. + +### Test Data and Services + +Keep the existing Docker scripts for CouchDB, MinIO, and P2P services. The real Obsidian runner should reuse these service fixtures instead of creating another service orchestration stack. + +Each test should use unique database names, bucket prefixes, vault names, and P2P room IDs. This prevents tests from depending on cleanup and makes interrupted runs less harmful. + +## Migration Plan + +### Phase 0: Discovery + +- Confirm how Obsidian can be launched reliably on the local development environment. +- Confirm whether Obsidian accepts a vault path directly, requires an Obsidian URI, or needs a pre-existing vault registry. +- Identify where Obsidian stores per-user state in the test environment and decide how to isolate it. +- Decide whether the first bridge is file-based or HTTP/WebSocket. + +Initial discovery on Linux ARM64 found that: + +- `Obsidian-1.12.7-arm64.AppImage` requires `libfuse.so.2` for direct AppImage execution. +- Extracting the AppImage with `--appimage-extract` works without FUSE. +- Launching the extracted `squashfs-root/obsidian` binary under `xvfb-run` with isolated user data stays alive for the smoke timeout. +- No missing shared libraries were reported by `ldd` for the extracted binary in the tested environment. +- Obsidian's CLI is disabled unless the global `obsidian.json` contains `cli: true`. +- Passing only `.obsidian/community-plugins.json` is not enough to load community plug-ins on Obsidian 1.12. The runner also has to enable the global community plug-in switch through `app.plugins.setEnable(true)`. +- The reliable launch sequence is: start Obsidian, send `obsidian://open?path=...` through `obsidian-cli`, wait until the vault-side CLI exposes the plug-in catalogue, enable community plug-ins, reload Self-hosted LiveSync, and verify plug-in readiness through `obsidian-cli eval`. + +### Phase 1: Smoke Runner + +- Add `test/e2e-obsidian/runner` utilities for temporary vault creation, plug-in installation, launch, readiness wait, and cleanup. +- Add one smoke test: + - launch Obsidian with an empty vault, + - load Self-hosted LiveSync, + - wait for the boot-up sequence to become ready, + - read the plug-in version or status through the control channel, + - close Obsidian cleanly. +- Add an npm script such as `test:e2e:obsidian`. + +Current implementation status: + +- Added `test/e2e-obsidian/runner` helpers for Obsidian discovery, CLI discovery, temporary vault creation, plug-in installation, process launch, CLI execution, and readiness polling. +- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, and `test:e2e:obsidian:install-appimage`. +- Added a manual AppImage installer that downloads Obsidian `1.12.7` for `arm64` or `x86_64`, stores it under `_testdata/obsidian`, and extracts it for FUSE-free execution. +- Confirmed the smoke runner on Linux ARM64 with the extracted Obsidian `1.12.7` AppImage, `xvfb-run`, and the built Self-hosted LiveSync bundle. +- Confirmed the runner can enable the Obsidian CLI through isolated `obsidian.json` state, open the temporary vault through `obsidian-cli`, enable community plug-ins through `app.plugins.setEnable(true)`, reload Self-hosted LiveSync, and verify readiness through `obsidian-cli eval`. +- Removed the first test-only ready-marker bridge from the plug-in bundle. The current runner observes readiness from outside the plug-in through Obsidian's own CLI, so normal user vaults do not receive E2E marker files. + +Current verification: + +- `npm run tsc-check` passes. +- `npm run build` passes with existing Svelte warnings. +- `npm run test:e2e:obsidian:discover` finds `_testdata/obsidian/squashfs-root/obsidian` when the extracted AppImage is present. +- `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS=1000 npm run test:e2e:obsidian:smoke` passes locally. +- `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present. + +Known limits: + +- The smoke runner currently proves only one-vault launch and plug-in load readiness. It does not yet exercise synchronisation, settings persistence, restart behaviour, or database writes. +- Cross-platform support is still discovery-level. The working path has been validated on Linux ARM64. +- CI wiring is not yet implemented. CI should use `OBSIDIAN_BINARY` or a cached `_testdata/obsidian/squashfs-root` rather than downloading the AppImage on every run. + +### Phase 2: First Real Workflow + +- Add a one-vault local workflow: + - configure a temporary CouchDB database, + - create a note in the real vault, + - wait for metadata and chunks to be stored, + - restart Obsidian, + - verify that the plug-in loads and the note remains consistent. + +This validates real boot-up, settings persistence, vault file access, database writes, and restart-sensitive state. + +### Phase 3: Two-Vault Synchronisation + +- Launch two Obsidian instances with two temporary vaults. +- Configure both against the same temporary remote database. +- Create, modify, rename, and delete notes in one vault. +- Verify reflection in the other vault. +- Cover encrypted and non-encrypted configurations separately. + +### Phase 4: Harness Retirement + +- Mark `test/harness` as deprecated in documentation. +- Stop adding new tests to `test/suite` unless they are explicitly transitional. +- Move critical existing scenarios from `test/suite` to real Obsidian E2E or lower-level integration tests. +- Remove the harness only after the new runner covers the critical boot-up and synchronisation workflows. + +## CI Strategy + +Start with local-only execution. After the smoke runner is stable: + +- Run the smoke test in CI on Linux. +- Keep full two-vault synchronisation scenarios as nightly or manually triggered jobs until runtime and flakiness are understood. +- Do not download the Obsidian AppImage on every CI run. Use a pre-installed Obsidian binary, a CI cache for `_testdata/obsidian/squashfs-root`, or a manually triggered preparation job. +- Capture Obsidian logs, plug-in logs, vault snapshots, and service logs on failure. +- Fail fast on launch failures, readiness timeouts, and cleanup failures with clear diagnostics. + +## Risks and Mitigations + +- **Obsidian licensing and installation**: CI may need a cached installer or a pre-installed binary. Keep the runner capable of using `OBSIDIAN_BINARY`. +- **Flakiness from UI timing**: Prefer a control channel and service-level probes over visual selectors. +- **Multiple instances**: Obsidian may not support multiple independent instances cleanly on all platforms. Start with one-instance smoke tests, then validate two-instance behaviour on Linux before expanding scope. +- **State leakage**: Isolate vault directories, Obsidian user data, remote database names, and bridge tokens per test. +- **Security of E2E controls**: Keep readiness and control outside the production plug-in bundle. Prefer Obsidian CLI probes over E2E-only plug-in code. +- **Runtime cost**: Keep the default PR gate small. Move slow synchronisation matrices to scheduled jobs. + +## Open Questions + +- Which launch mechanism is most reliable for Obsidian on Linux in this repository's CI environment? +- Can two Obsidian instances run with isolated user data at the same time? +- Do future scenarios need a richer control channel than Obsidian CLI, or can CLI `eval` and developer commands cover the required workflows? +- Should any future E2E-only plug-in code live in a separate test build, or should the production bundle remain free of E2E controls? +- Which existing `test/suite` scenarios are critical enough to port before deprecating the harness? + +## Initial Implementation Checklist + +1. Add an Obsidian launch discovery script that prints the detected executable, version, and launch mode. +2. Add temporary vault and plug-in installation helpers. +3. Add CLI-based plug-in readiness polling. +4. Add `test:e2e:obsidian:smoke` for one-vault plug-in load. +5. Document required local environment variables, especially `OBSIDIAN_BINARY`. +6. Port one CouchDB-backed workflow after the smoke test is stable. +7. Mark `test/harness` as transitional and block new broad E2E work from targeting it. + +## Consequences + +- Real Obsidian E2E becomes the source of truth for plug-in lifecycle and vault integration. +- Unit and integration tests remain the primary fast feedback loops. +- The old browser harness can be deleted once the new runner covers the critical workflows. +- The project will gain slower but higher-confidence tests for the behaviours most likely to differ between mocks and Obsidian itself. diff --git a/package.json b/package.json index 0d13b79..09f2be1 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,10 @@ "test:unit:coverage": "vitest run --config vitest.config.unit.ts --coverage", "test:install-playwright": "npx playwright install chromium", "test:install-dependencies": "npm run test:install-playwright", + "test:e2e:obsidian:install-appimage": "tsx test/e2e-obsidian/scripts/install-appimage.ts", + "test:e2e:obsidian:discover": "tsx test/e2e-obsidian/scripts/discover.ts", + "test:e2e:obsidian:cli-help": "tsx test/e2e-obsidian/scripts/cli-help.ts", + "test:e2e:obsidian:smoke": "tsx test/e2e-obsidian/scripts/smoke.ts", "test:coverage": "vitest run --coverage", "test:docker-couchdb:up": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-start.sh", "test:docker-couchdb:init": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-init.sh", diff --git a/src/apps/cli/README.md b/src/apps/cli/README.md index 5579ae2..574a64a 100644 --- a/src/apps/cli/README.md +++ b/src/apps/cli/README.md @@ -1,634 +1,634 @@ -# Self-hosted LiveSync CLI -Command-line version of Self-hosted LiveSync plug-in for syncing vaults without Obsidian. - -## Features - -- ✅ Sync Obsidian vaults using CouchDB without running Obsidian -- ✅ Compatible with Self-hosted LiveSync plug-in settings -- ✅ Supports all core sync features (encryption, conflict resolution, etc.) -- ✅ Lightweight and headless operation -- ✅ Cross-platform (Windows, macOS, Linux) - -## Architecture - -This CLI version is built using the same core as the Obsidian plug-in: - -``` -CLI Main - └─ LiveSyncBaseCore - ├─ NodeServiceHub (All services without Obsidian dependencies) - └─ ServiceModules (wired by initialiseServiceModulesCLI) - ├─ FileAccessCLI (Node.js FileSystemAdapter) - ├─ StorageEventManagerCLI - ├─ ServiceFileAccessCLI - ├─ ServiceDatabaseFileAccessCLI - ├─ ServiceFileHandler - └─ ServiceRebuilder -``` - -### Key Components - -1. **Node.js FileSystem Adapter** (`adapters/`) - - Platform-agnostic file operations using Node.js `fs/promises` - - Implements same interface as Obsidian's file system - -2. **Service Modules** (`serviceModules/`) - - Initialised by `initialiseServiceModulesCLI` - - All core sync functionality preserved - -3. **Service Hub and Settings Services** (`services/`) - - `NodeServiceHub` provides the CLI service context - - Node-specific settings and key-value services are provided without Obsidian dependencies - -4. **Main Entry Point** (`main.ts`) - - Command-line interface - - Settings management (JSON file) - - Graceful shutdown handling - -## Usage - -The CLI operates on a **database directory** which contains PouchDB data and settings. - -> [!NOTE] -> `livesync-cli` is the alias for the CLI executable. Please replace with the actual command of your installation (e.g. `npm run --silent cli --` or `docker run ...`). - -```bash -livesync-cli [database-path] [command] [args...] -``` - - -### Arguments - -- `database-path`: Path to the directory where `.livesync` folder and `settings.json` are (or will be) located. - - Note: In previous versions, this was referred to as the "vault" path. Now it is clearly distinguished from the actual vault (the directory containing your `.md` files). -- `--vault ` / `-V `: (daemon/mirror only) Path to the vault directory containing `.md` files. - - Allows the PouchDB database directory and the actual vault directory to be different locations. - - For `mirror` command, the positional `[vault-path]` argument takes precedence over `--vault`. - -### Commands - -- `sync`: Run one replication cycle with the remote CouchDB. -- `mirror [vault-path]`: Bidirectional sync between the local database and a local directory (**the actual vault**). - - If `vault-path` is provided, the CLI will synchronise the database with files in the vault directory. - - If `vault-path` is omitted, it defaults to `database-path` (compatibility mode). - - Use this command to keep your local `.md` files in sync with the database. -- `ls [prefix]`: List files currently stored in the local database. -- `push `: Push a local file `` into the database at path ``. -- `pull `: Pull a file `` from the database into local file ``. -- `cat `: Read a file from the database and write to stdout. -- `put `: Read from stdin and write to the database path ``. -- `remote-add `: Add a remote configuration from a connection string. -- `remote-rm `: Remove a remote configuration by ID. -- `remote-ls`: List remote configurations (`id`, `name`, `active/inactive`, redacted URI). -- `remote-export `: Export the stored connection string by remote ID. -- `remote-set `: Replace the stored connection string by remote ID. -- `remote-activate `: Activate a remote configuration by ID. -- `mark-resolved [remote-id]`: Resolve remote synchronisation status. -- `unlock-remote [remote-id]`: Unlock the remote database. -- `lock-remote [remote-id]`: Lock the remote database. -- `remote-status [remote-id]`: Show remote database status. -- `init-settings [file]`: Create a default settings file. - -### Examples - -```bash -# Basic sync with remote -livesync-cli ./my-db sync - -# Mirroring to your actual Obsidian vault -livesync-cli ./my-db mirror /path/to/obsidian-vault - -# Manual file operations -livesync-cli ./my-db push ./note.md folder/note.md -livesync-cli ./my-db pull folder/note.md ./note.md -``` - -## Installation - -### Build from source - -```bash -# Clone with submodules, because the shared core lives in src/lib -git clone --recurse-submodules -cd obsidian-livesync - -# If you already cloned without submodules, run this once instead -git submodule update --init --recursive - -# Install dependencies from the repository root -npm install - -# Build the CLI from the repository root -npm run build -w self-hosted-livesync-cli - -# Or from the package directory -cd src/apps/cli -npm run build -``` - -If `src/lib` is missing, the build process stops early with a targeted message instead of a low-level Vite `ENOENT` error. - -Run the CLI: - -```bash -# Run with npm workspace script (from repository root) -npm run cli -w self-hosted-livesync-cli -- [database-path] [command] [args...] - -# Or from the package directory -cd src/apps/cli -npm run cli -- [database-path] [command] [args...] - -# Run the built executable directly -node src/apps/cli/dist/index.cjs [database-path] [command] [args...] -``` - -### Docker - -A Docker image is provided for headless / server deployments. Build from the repository root: - -```bash -docker build -f src/apps/cli/Dockerfile -t livesync-cli . -``` - -Run: - -```bash -# Sync with CouchDB -docker run --rm -v /path/to/your/db:/data livesync-cli sync - -# Mirror to a specific vault directory -docker run --rm -v /path/to/your/db:/data -v /path/to/your/vault:/vault livesync-cli mirror /vault - -# List files in the local database -docker run --rm -v /path/to/your/db:/data livesync-cli ls -``` - -The database directory is mounted at `/data` by default. Override with `-e LIVESYNC_DB_PATH=/other/path`. - -#### P2P (WebRTC) and Docker networking - -The P2P replicator (`p2p-host`, `p2p-sync`, `p2p-peers`) uses WebRTC and generates -three kinds of ICE candidates. The default Docker bridge network affects which -candidates are usable: - -| Candidate type | Description | Bridge network | -| -------------- | ---------------------------------- | -------------------------- | -| `host` | Container bridge IP (`172.17.x.x`) | Unreachable from LAN peers | -| `srflx` | Host public IP via STUN reflection | Works over the internet | -| `relay` | Traffic relayed via TURN server | Always reachable | - -**LAN P2P on Linux** — use `--network host` so that the real host IP is -advertised as the `host` candidate: - -```bash -docker run --rm --network host -v /path/to/your/vault:/data livesync-cli p2p-host -``` - -Note: also fix the alias to include `--network host` if you want to use `livesync-cli` for P2P commands. - -> `--network host` is not available on Docker Desktop for macOS or Windows. - -**LAN P2P on macOS / Windows Docker Desktop** — configure a TURN server in the -settings file (`P2P_turnServers`, `P2P_turnUsername`, `P2P_turnCredential`). -All P2P traffic will then be relayed through the TURN server, bypassing the -bridge-network limitation. - -**Internet P2P** — the default bridge network is sufficient. The `srflx` -candidate carries the host's public IP and peers can connect normally. - -**CouchDB sync only (no P2P)** — no special network configuration is required. - - -### Adding `livesync-cli` alias - -To use the `livesync-cli` command globally, you can add an alias to your shell configuration file (e.g., `.zshrc` or `.bashrc`). - -If you are using `npm run`, add the following line: - -```bash -alias livesync-cli='npm run --silent --prefix /path/to/repository/src/apps/cli cli --' -# or -alias livesync-cli="npm run --silent --prefix $PWD cli --" -``` - -Alternatively, if you want to use the built executable directly: - -```bash -alias livesync-cli='node /path/to/repository/src/apps/cli/dist/index.cjs' -or -alias livesync-cli="node $PWD/dist/index.cjs" -``` - -If you prefer using Docker: - -```bash -alias livesync-cli='docker run --rm -v /path/to/your/db:/data livesync-cli' -``` - -After adding the alias, restart your shell or run `source ~/.zshrc` (or `.bashrc`). - -## Usage - -### Basic Usage - -As you know, the CLI is designed to be used in a headless environment. Hence all operations are performed against a local vault directory and a settings file. Here are some example commands: - -```bash -# Sync local database with CouchDB (no files will be changed). -livesync-cli /path/to/your-local-database --settings /path/to/settings.json sync - -# Push files to local database -livesync-cli /path/to/your-local-database --settings /path/to/settings.json push /your/storage/file.md /vault/path/file.md - -# Pull files from local database -livesync-cli /path/to/your-local-database --settings /path/to/settings.json pull /vault/path/file.md /your/storage/file.md - -# Verbose logging -livesync-cli /path/to/your-local-database --settings /path/to/settings.json --verbose - -# Apply setup URI to settings file (settings only; does not run synchronisation) -livesync-cli /path/to/your-local-database --settings /path/to/settings.json setup "obsidian://setuplivesync?settings=..." - -# Put text from stdin into local database -echo "Hello from stdin" | livesync-cli /path/to/your-local-database --settings /path/to/settings.json put /vault/path/file.md - -# Output a file from local database to stdout -livesync-cli /path/to/your-local-database --settings /path/to/settings.json cat /vault/path/file.md - -# Output a specific revision of a file from local database -livesync-cli /path/to/your-local-database --settings /path/to/settings.json cat-rev /vault/path/file.md 3-abcdef - -# Pull a specific revision of a file from local database to local storage -livesync-cli /path/to/your-local-database --settings /path/to/settings.json pull-rev /vault/path/file.md /your/storage/file.old.md 3-abcdef - -# List files in local database -livesync-cli /path/to/your-local-database --settings /path/to/settings.json ls /vault/path/ - -# Show metadata for a file in local database -livesync-cli /path/to/your-local-database --settings /path/to/settings.json info /vault/path/file.md - -# Mark a file as deleted in local database -livesync-cli /path/to/your-local-database --settings /path/to/settings.json rm /vault/path/file.md - -# Resolve conflict by keeping a specific revision -livesync-cli /path/to/your-local-database --settings /path/to/settings.json resolve /vault/path/file.md 3-abcdef - -# Add, list, activate, and remove remote configurations -livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-add main "sls+https://user:pass@example.com/db" -livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-ls -livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-export remote-abc123 -livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-set remote-abc123 "sls+p2p://room-abc?passphrase=secret" -livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-activate remote-abc123 -livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-rm remote-abc123 - -# Lock, unlock, resolve, and view status of remote database -livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-status remote-abc123 -livesync-cli /path/to/your-local-database --settings /path/to/settings.json lock-remote remote-abc123 -livesync-cli /path/to/your-local-database --settings /path/to/settings.json mark-resolved remote-abc123 -livesync-cli /path/to/your-local-database --settings /path/to/settings.json unlock-remote remote-abc123 -``` - -### Configuration - -The CLI uses the same settings format as the Obsidian plug-in. Create a `.livesync/settings.json` file in your vault directory: - -```json -{ - "couchDB_URI": "http://localhost:5984", - "couchDB_USER": "admin", - "couchDB_PASSWORD": "password", - "couchDB_DBNAME": "obsidian-livesync", - "liveSync": true, - "syncOnSave": true, - "syncOnStart": true, - "encrypt": true, - "passphrase": "your-encryption-passphrase", - "usePluginSync": false, - "isConfigured": true -} -``` - -**Minimum required settings:** - -- `couchDB_URI`: CouchDB server URL -- `couchDB_USER`: CouchDB username -- `couchDB_PASSWORD`: CouchDB password -- `couchDB_DBNAME`: Database name -- `isConfigured`: Set to `true` after configuration - -### Command-line Reference - -``` -Usage: - livesync-cli [options] [command-args] - livesync-cli init-settings [path] - -Arguments: - database-path Path to the local database directory (required except for init-settings) - -Options: - --settings, -s Path to settings file (default: .livesync/settings.json in local database directory) - --force, -f Overwrite existing file on init-settings - --verbose, -v Enable verbose logging - --debug, -d Enable debug logging (includes verbose) - --interval , -i (daemon only) Poll CouchDB every N seconds instead of using the _changes feed - --vault , -V (daemon/mirror) Path to vault directory, decoupled from database-path - --help, -h Show this help message - -Commands: - daemon (default) Run mirror scan then continuously sync CouchDB <-> local filesystem - init-settings [path] Create settings JSON from DEFAULT_SETTINGS - sync Run one replication cycle and exit - p2p-peers Show discovered peers as [peer] - p2p-sync Synchronise with specified peer-id or peer-name - p2p-host Start P2P host mode and wait until interrupted (Ctrl+C) - push Push local file into local database path - pull Pull file from local database into local file - pull-rev Pull specific revision into local file - setup Apply setup URI to settings file - put Read text from standard input and write to local database path - cat Write latest file content from local database to standard output - cat-rev Write specific revision content from local database to standard output - ls [prefix] List files as pathsizemtimerevision[*] - info Show file metadata including current and past revisions, conflicts, and chunk list - rm Mark file as deleted in local database - resolve Resolve conflict by keeping the specified revision - mirror [vaultPath] Mirror database contents to the local file system - (vaultPath positional arg > --vault flag > database-path) -``` - -Run via npm script: - -```bash -npm run --silent cli -- [database-path] [options] [command] [command-args] -``` - -#### Detailed Command Descriptions - -##### ls -`ls` lists files in the local database with optional prefix filtering. Output format is: - -```vault/path/file.mdsizemtimerevision[*] -``` -Note: `*` indicates if the file has conflicts. - -##### p2p-peers - -`p2p-peers ` waits for the specified number of seconds, then prints each discovered peer on a separate line: - -```text -[peer] -``` - -Use this command to select a target for `p2p-sync`. - -##### p2p-sync - -`p2p-sync ` discovers peers up to the specified timeout and synchronises with the selected peer. - -- `` accepts either `peer-id` or `peer-name` from `p2p-peers` output. -- On success, the command prints a completion message to standard error and exits with status code `0`. -- On failure, the command prints an error message and exits non-zero. - -##### p2p-host - -`p2p-host` starts the local P2P host and keeps running until interrupted. - -- Other peers can discover and synchronise with this host while it is running. -- Stop the host with `Ctrl+C`. -- In CLI mode, behaviour is non-interactive and acceptance follows settings. - -##### info - -`info` output fields: - -- `id`: Document ID -- `revision`: Current revision -- `conflicts`: Conflicted revisions, or `N/A` -- `filename`: Basename of path -- `path`: Vault-relative path -- `size`: Size in bytes -- `revisions`: Available non-current revisions -- `chunks`: Number of chunk IDs -- `children`: Chunk ID list - -##### mirror - -`mirror` is a command that synchronises your storage with your local vault. It is essentially a process that runs upon startup in Obsidian. - -In other words, it performs the following actions: - -1. **Precondition checks** — Aborts early if any of the following conditions are not met: - - Settings must be configured (`isConfigured: true`). - - File watching must not be suspended (`suspendFileWatching: false`). - - Remediation mode must be inactive (`maxMTimeForReflectEvents: 0`). - -2. **State restoration** — On subsequent runs (after the first successful scan), restores the previous storage state before proceeding. - -3. **Expired deletion cleanup** — If `automaticallyDeleteMetadataOfDeletedFiles` is set to a positive number of days, any document that is marked deleted and whose `mtime` is older than the retention period is permanently removed from the local database. - -4. **File collection** — Enumerates files from two sources: - - **Storage**: all files under the vault path that pass `isTargetFile`. - - **Local database**: all normal documents (fetched with conflict information) whose paths are valid and pass `isTargetFile`. - - Both collections build case-insensitive ↔ case-sensitive path maps, controlled by `handleFilenameCaseSensitive`. - -5. **Categorisation and synchronisation** — The union of both file sets is split into three groups and processed concurrently (up to 10 files at a time): - - | Group | Condition | Action | - | ----------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | **UPDATE DATABASE** | File exists in storage only | Store the file into the local database. | - | **UPDATE STORAGE** | File exists in database only | If the entry is active (not deleted) and not conflicted, restore the file from the database to storage. Deleted entries and conflicted entries are skipped. | - | **SYNC DATABASE AND STORAGE** | File exists in both | Compare `mtime` freshness. If storage is newer → write to database (`STORAGE → DB`). If database is newer → restore to storage (`STORAGE ← DB`). If equal → do nothing. Conflicted documents and files exceeding the size limit are always skipped. | - -6. **Initialisation flag** — On the very first successful run, writes `initialized = true` to the key-value database so that subsequent runs can restore state in step 2. - -Note: `mirror` does not respect file deletions. If a file is deleted in storage, it will be restored on the next `mirror` run. To delete a file, use the `rm` command instead. This is a little inconvenient, but it is intentional behaviour (if we handle this automatically in `mirror`, we should be against a ton of edge cases). - -##### daemon - -`daemon` is the default command when no command is specified. It runs an initial mirror scan and then continuously syncs changes in both directions: - -- **CouchDB → local filesystem**: via the `_changes` feed (LiveSync mode, default) or periodic polling (`--interval N`). -- **local filesystem → CouchDB**: via chokidar file watching. Any file created, modified, or deleted in the vault directory is pushed to CouchDB. - -In **LiveSync mode** the `_changes` feed delivers remote changes as they arrive, with sub-second latency. In **polling mode** (`--interval N`) the CLI polls CouchDB every N seconds. Use polling mode if your CouchDB instance does not support long-lived HTTP connections, or if you need predictable network usage. - -The daemon exits cleanly on `SIGINT` or `SIGTERM`. - -```bash -# LiveSync mode (default — _changes feed, near-real-time) -livesync-cli /path/to/vault - -# Polling mode — poll every 60 seconds -livesync-cli /path/to/vault --interval 60 -``` - -### .livesync/ignore - -Place a `.livesync/ignore` file in your vault root to exclude files from sync in both directions (local → CouchDB and CouchDB → local). - -**Format:** - -- Lines beginning with `#` are comments. -- Blank lines are ignored. -- All other lines are [minimatch](https://github.com/isaacs/minimatch) glob patterns, relative to the vault root. -- The directive `import: .gitignore` (exactly this string) reads `.gitignore` from the vault root and merges its non-comment, non-blank lines into the ignore rules. -- Negation patterns (lines starting with `!`) are not supported and will cause an error on load. - -**Example `.livesync/ignore`:** - -``` -# Ignore temporary files -*.tmp -*.swp - -# Ignore build output -build/ -dist/ - -# Merge patterns from .gitignore -import: .gitignore -``` - -Patterns apply in both directions: the chokidar watcher will not emit events for matched files, and the `isTargetFile` filter will exclude them from CouchDB → local sync. - -Changes to this file require a daemon restart to take effect. - -### Systemd Installation - -The `deploy/` directory contains a systemd unit template and an install script. - -**Automated install (user service, recommended):** - -```bash -bash src/apps/cli/deploy/install.sh --vault /path/to/vault -``` - -**With polling interval:** - -```bash -bash src/apps/cli/deploy/install.sh --vault /path/to/vault --interval 60 -``` - -**System-wide install** (requires root / sudo for `/etc/systemd/system/`): - -```bash -bash src/apps/cli/deploy/install.sh --system --vault /path/to/vault -``` - -The script: -1. Builds the CLI (`npm install` + `npm run build`). -2. Installs the binary to `~/.local/bin/livesync-cli` (user) or `/usr/local/bin/livesync-cli` (system). -3. Writes the unit file to `~/.config/systemd/user/livesync-cli.service` (user) or `/etc/systemd/system/livesync-cli.service` (system). -4. Runs `systemctl [--user] daemon-reload && systemctl [--user] enable --now livesync-cli`. - -**Manual setup** — if you prefer to manage the unit yourself, copy `deploy/livesync-cli.service`, replace `LIVESYNC_BIN` and `LIVESYNC_VAULT_PATH` with the actual binary path and vault path, then install to the appropriate systemd directory. - -### Planned options: - -- `--immediate`: Perform sync after the command (e.g. `push`, `pull`, `put`, `rm`). -- `serve`: Start CLI in server mode, exposing REST APIs for remote, and batch operations. -- `cause-conflicted `: Mark a file as conflicted without changing its content, to trigger conflict resolution in Obsidian. - -## Use Cases - -### 1. Bootstrap a new headless vault - -Create default settings, apply a setup URI, then run one sync cycle. - -```bash -livesync-cli -- init-settings /data/livesync-settings.json -printf '%s\n' "$SETUP_PASSPHRASE" | livesync-cli -- /data/vault --settings /data/livesync-settings.json setup "$SETUP_URI" -livesync-cli -- /data/vault --settings /data/livesync-settings.json sync -``` - -### 2. Scripted import and export - -Push local files into the database from automation, and pull them back for export or backup. - -```bash -livesync-cli -- /data/vault --settings /data/livesync-settings.json push ./note.md notes/note.md -livesync-cli -- /data/vault --settings /data/livesync-settings.json pull notes/note.md ./exports/note.md -``` - -### 3. Revision inspection and restore - -List metadata, find an older revision, then restore it by content (`cat-rev`) or file output (`pull-rev`). - -```bash -livesync-cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md -livesync-cli -- /data/vault --settings /data/livesync-settings.json cat-rev notes/note.md 3-abcdef -livesync-cli -- /data/vault --settings /data/livesync-settings.json pull-rev notes/note.md ./restore/note.old.md 3-abcdef -``` - -### 4. Conflict and cleanup workflow - -Inspect conflicted revisions, resolve by keeping one revision, then delete obsolete files. - -```bash -livesync-cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md -livesync-cli -- /data/vault --settings /data/livesync-settings.json resolve notes/note.md 3-abcdef -livesync-cli -- /data/vault --settings /data/livesync-settings.json rm notes/obsolete.md -``` - -### 5. CI smoke test for content round-trip - -Validate that `put`/`cat` is behaving as expected in a pipeline. - -```bash -echo "hello-ci" | livesync-cli -- /data/vault --settings /data/livesync-settings.json put ci/test.md -livesync-cli -- /data/vault --settings /data/livesync-settings.json cat ci/test.md -``` - -## Development - -### Project Structure - -``` -src/apps/cli/ -├── commands/ # Command dispatcher and command utilities -│ ├── runCommand.ts -│ ├── runCommand.unit.spec.ts -│ ├── types.ts -│ ├── utils.ts -│ └── utils.unit.spec.ts -├── adapters/ # Node.js FileSystem Adapter -│ ├── NodeConversionAdapter.ts -│ ├── NodeFileSystemAdapter.ts -│ ├── NodePathAdapter.ts -│ ├── NodeStorageAdapter.ts -│ ├── NodeStorageAdapter.unit.spec.ts -│ ├── NodeTypeGuardAdapter.ts -│ ├── NodeTypes.ts -│ └── NodeVaultAdapter.ts -├── lib/ -│ └── pouchdb-node.ts -├── managers/ # CLI-specific managers -│ ├── CLIStorageEventManagerAdapter.ts -│ └── StorageEventManagerCLI.ts -├── serviceModules/ # Service modules (ported from main.ts) -│ ├── CLIServiceModules.ts -│ ├── DatabaseFileAccess.ts -│ ├── FileAccessCLI.ts -│ └── ServiceFileAccessImpl.ts -├── services/ -│ ├── NodeKeyValueDBService.ts -│ ├── NodeServiceHub.ts -│ └── NodeSettingService.ts -├── test/ -│ ├── test-e2e-two-vaults-common.sh -│ ├── test-e2e-two-vaults-matrix.sh -│ ├── test-e2e-two-vaults-with-docker-linux.sh -│ ├── test-push-pull-linux.sh -│ ├── test-setup-put-cat-linux.sh -│ └── test-sync-two-local-databases-linux.sh -├── .gitignore -├── entrypoint.ts # CLI executable entry point (shebang) -├── main.ts # CLI entry point -├── main.unit.spec.ts -├── package.json -├── README.md # This file -├── tsconfig.json -├── util/ # Test and local utility scripts -└── vite.config.ts -``` +# Self-hosted LiveSync CLI +Command-line version of Self-hosted LiveSync plug-in for syncing vaults without Obsidian. + +## Features + +- ✅ Sync Obsidian vaults using CouchDB without running Obsidian +- ✅ Compatible with Self-hosted LiveSync plug-in settings +- ✅ Supports all core sync features (encryption, conflict resolution, etc.) +- ✅ Lightweight and headless operation +- ✅ Cross-platform (Windows, macOS, Linux) + +## Architecture + +This CLI version is built using the same core as the Obsidian plug-in: + +``` +CLI Main + └─ LiveSyncBaseCore + ├─ NodeServiceHub (All services without Obsidian dependencies) + └─ ServiceModules (wired by initialiseServiceModulesCLI) + ├─ FileAccessCLI (Node.js FileSystemAdapter) + ├─ StorageEventManagerCLI + ├─ ServiceFileAccessCLI + ├─ ServiceDatabaseFileAccessCLI + ├─ ServiceFileHandler + └─ ServiceRebuilder +``` + +### Key Components + +1. **Node.js FileSystem Adapter** (`adapters/`) + - Platform-agnostic file operations using Node.js `fs/promises` + - Implements same interface as Obsidian's file system + +2. **Service Modules** (`serviceModules/`) + - Initialised by `initialiseServiceModulesCLI` + - All core sync functionality preserved + +3. **Service Hub and Settings Services** (`services/`) + - `NodeServiceHub` provides the CLI service context + - Node-specific settings and key-value services are provided without Obsidian dependencies + +4. **Main Entry Point** (`main.ts`) + - Command-line interface + - Settings management (JSON file) + - Graceful shutdown handling + +## Usage + +The CLI operates on a **database directory** which contains PouchDB data and settings. + +> [!NOTE] +> `livesync-cli` is the alias for the CLI executable. Please replace with the actual command of your installation (e.g. `npm run --silent cli --` or `docker run ...`). + +```bash +livesync-cli [database-path] [command] [args...] +``` + + +### Arguments + +- `database-path`: Path to the directory where `.livesync` folder and `settings.json` are (or will be) located. + - Note: In previous versions, this was referred to as the "vault" path. Now it is clearly distinguished from the actual vault (the directory containing your `.md` files). +- `--vault ` / `-V `: (daemon/mirror only) Path to the vault directory containing `.md` files. + - Allows the PouchDB database directory and the actual vault directory to be different locations. + - For `mirror` command, the positional `[vault-path]` argument takes precedence over `--vault`. + +### Commands + +- `sync`: Run one replication cycle with the remote CouchDB. +- `mirror [vault-path]`: Bidirectional sync between the local database and a local directory (**the actual vault**). + - If `vault-path` is provided, the CLI will synchronise the database with files in the vault directory. + - If `vault-path` is omitted, it defaults to `database-path` (compatibility mode). + - Use this command to keep your local `.md` files in sync with the database. +- `ls [prefix]`: List files currently stored in the local database. +- `push `: Push a local file `` into the database at path ``. +- `pull `: Pull a file `` from the database into local file ``. +- `cat `: Read a file from the database and write to stdout. +- `put `: Read from stdin and write to the database path ``. +- `remote-add `: Add a remote configuration from a connection string. +- `remote-rm `: Remove a remote configuration by ID. +- `remote-ls`: List remote configurations (`id`, `name`, `active/inactive`, redacted URI). +- `remote-export `: Export the stored connection string by remote ID. +- `remote-set `: Replace the stored connection string by remote ID. +- `remote-activate `: Activate a remote configuration by ID. +- `mark-resolved [remote-id]`: Resolve remote synchronisation status. +- `unlock-remote [remote-id]`: Unlock the remote database. +- `lock-remote [remote-id]`: Lock the remote database. +- `remote-status [remote-id]`: Show remote database status. +- `init-settings [file]`: Create a default settings file. + +### Examples + +```bash +# Basic sync with remote +livesync-cli ./my-db sync + +# Mirroring to your actual Obsidian vault +livesync-cli ./my-db mirror /path/to/obsidian-vault + +# Manual file operations +livesync-cli ./my-db push ./note.md folder/note.md +livesync-cli ./my-db pull folder/note.md ./note.md +``` + +## Installation + +### Build from source + +```bash +# Clone with submodules, because the shared core lives in src/lib +git clone --recurse-submodules +cd obsidian-livesync + +# If you already cloned without submodules, run this once instead +git submodule update --init --recursive + +# Install dependencies from the repository root +npm install + +# Build the CLI from the repository root +npm run build -w self-hosted-livesync-cli + +# Or from the package directory +cd src/apps/cli +npm run build +``` + +If `src/lib` is missing, the build process stops early with a targeted message instead of a low-level Vite `ENOENT` error. + +Run the CLI: + +```bash +# Run with npm workspace script (from repository root) +npm run cli -w self-hosted-livesync-cli -- [database-path] [command] [args...] + +# Or from the package directory +cd src/apps/cli +npm run cli -- [database-path] [command] [args...] + +# Run the built executable directly +node src/apps/cli/dist/index.cjs [database-path] [command] [args...] +``` + +### Docker + +A Docker image is provided for headless / server deployments. Build from the repository root: + +```bash +docker build -f src/apps/cli/Dockerfile -t livesync-cli . +``` + +Run: + +```bash +# Sync with CouchDB +docker run --rm -v /path/to/your/db:/data livesync-cli sync + +# Mirror to a specific vault directory +docker run --rm -v /path/to/your/db:/data -v /path/to/your/vault:/vault livesync-cli mirror /vault + +# List files in the local database +docker run --rm -v /path/to/your/db:/data livesync-cli ls +``` + +The database directory is mounted at `/data` by default. Override with `-e LIVESYNC_DB_PATH=/other/path`. + +#### P2P (WebRTC) and Docker networking + +The P2P replicator (`p2p-host`, `p2p-sync`, `p2p-peers`) uses WebRTC and generates +three kinds of ICE candidates. The default Docker bridge network affects which +candidates are usable: + +| Candidate type | Description | Bridge network | +| -------------- | ---------------------------------- | -------------------------- | +| `host` | Container bridge IP (`172.17.x.x`) | Unreachable from LAN peers | +| `srflx` | Host public IP via STUN reflection | Works over the internet | +| `relay` | Traffic relayed via TURN server | Always reachable | + +**LAN P2P on Linux** — use `--network host` so that the real host IP is +advertised as the `host` candidate: + +```bash +docker run --rm --network host -v /path/to/your/vault:/data livesync-cli p2p-host +``` + +Note: also fix the alias to include `--network host` if you want to use `livesync-cli` for P2P commands. + +> `--network host` is not available on Docker Desktop for macOS or Windows. + +**LAN P2P on macOS / Windows Docker Desktop** — configure a TURN server in the +settings file (`P2P_turnServers`, `P2P_turnUsername`, `P2P_turnCredential`). +All P2P traffic will then be relayed through the TURN server, bypassing the +bridge-network limitation. + +**Internet P2P** — the default bridge network is sufficient. The `srflx` +candidate carries the host's public IP and peers can connect normally. + +**CouchDB sync only (no P2P)** — no special network configuration is required. + + +### Adding `livesync-cli` alias + +To use the `livesync-cli` command globally, you can add an alias to your shell configuration file (e.g., `.zshrc` or `.bashrc`). + +If you are using `npm run`, add the following line: + +```bash +alias livesync-cli='npm run --silent --prefix /path/to/repository/src/apps/cli cli --' +# or +alias livesync-cli="npm run --silent --prefix $PWD cli --" +``` + +Alternatively, if you want to use the built executable directly: + +```bash +alias livesync-cli='node /path/to/repository/src/apps/cli/dist/index.cjs' +or +alias livesync-cli="node $PWD/dist/index.cjs" +``` + +If you prefer using Docker: + +```bash +alias livesync-cli='docker run --rm -v /path/to/your/db:/data livesync-cli' +``` + +After adding the alias, restart your shell or run `source ~/.zshrc` (or `.bashrc`). + +## Usage + +### Basic Usage + +As you know, the CLI is designed to be used in a headless environment. Hence all operations are performed against a local vault directory and a settings file. Here are some example commands: + +```bash +# Sync local database with CouchDB (no files will be changed). +livesync-cli /path/to/your-local-database --settings /path/to/settings.json sync + +# Push files to local database +livesync-cli /path/to/your-local-database --settings /path/to/settings.json push /your/storage/file.md /vault/path/file.md + +# Pull files from local database +livesync-cli /path/to/your-local-database --settings /path/to/settings.json pull /vault/path/file.md /your/storage/file.md + +# Verbose logging +livesync-cli /path/to/your-local-database --settings /path/to/settings.json --verbose + +# Apply setup URI to settings file (settings only; does not run synchronisation) +livesync-cli /path/to/your-local-database --settings /path/to/settings.json setup "obsidian://setuplivesync?settings=..." + +# Put text from stdin into local database +echo "Hello from stdin" | livesync-cli /path/to/your-local-database --settings /path/to/settings.json put /vault/path/file.md + +# Output a file from local database to stdout +livesync-cli /path/to/your-local-database --settings /path/to/settings.json cat /vault/path/file.md + +# Output a specific revision of a file from local database +livesync-cli /path/to/your-local-database --settings /path/to/settings.json cat-rev /vault/path/file.md 3-abcdef + +# Pull a specific revision of a file from local database to local storage +livesync-cli /path/to/your-local-database --settings /path/to/settings.json pull-rev /vault/path/file.md /your/storage/file.old.md 3-abcdef + +# List files in local database +livesync-cli /path/to/your-local-database --settings /path/to/settings.json ls /vault/path/ + +# Show metadata for a file in local database +livesync-cli /path/to/your-local-database --settings /path/to/settings.json info /vault/path/file.md + +# Mark a file as deleted in local database +livesync-cli /path/to/your-local-database --settings /path/to/settings.json rm /vault/path/file.md + +# Resolve conflict by keeping a specific revision +livesync-cli /path/to/your-local-database --settings /path/to/settings.json resolve /vault/path/file.md 3-abcdef + +# Add, list, activate, and remove remote configurations +livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-add main "sls+https://user:pass@example.com/db" +livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-ls +livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-export remote-abc123 +livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-set remote-abc123 "sls+p2p://room-abc?passphrase=secret" +livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-activate remote-abc123 +livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-rm remote-abc123 + +# Lock, unlock, resolve, and view status of remote database +livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-status remote-abc123 +livesync-cli /path/to/your-local-database --settings /path/to/settings.json lock-remote remote-abc123 +livesync-cli /path/to/your-local-database --settings /path/to/settings.json mark-resolved remote-abc123 +livesync-cli /path/to/your-local-database --settings /path/to/settings.json unlock-remote remote-abc123 +``` + +### Configuration + +The CLI uses the same settings format as the Obsidian plug-in. Create a `.livesync/settings.json` file in your vault directory: + +```json +{ + "couchDB_URI": "http://localhost:5984", + "couchDB_USER": "admin", + "couchDB_PASSWORD": "password", + "couchDB_DBNAME": "obsidian-livesync", + "liveSync": true, + "syncOnSave": true, + "syncOnStart": true, + "encrypt": true, + "passphrase": "your-encryption-passphrase", + "usePluginSync": false, + "isConfigured": true +} +``` + +**Minimum required settings:** + +- `couchDB_URI`: CouchDB server URL +- `couchDB_USER`: CouchDB username +- `couchDB_PASSWORD`: CouchDB password +- `couchDB_DBNAME`: Database name +- `isConfigured`: Set to `true` after configuration + +### Command-line Reference + +``` +Usage: + livesync-cli [options] [command-args] + livesync-cli init-settings [path] + +Arguments: + database-path Path to the local database directory (required except for init-settings) + +Options: + --settings, -s Path to settings file (default: .livesync/settings.json in local database directory) + --force, -f Overwrite existing file on init-settings + --verbose, -v Enable verbose logging + --debug, -d Enable debug logging (includes verbose) + --interval , -i (daemon only) Poll CouchDB every N seconds instead of using the _changes feed + --vault , -V (daemon/mirror) Path to vault directory, decoupled from database-path + --help, -h Show this help message + +Commands: + daemon (default) Run mirror scan then continuously sync CouchDB <-> local filesystem + init-settings [path] Create settings JSON from DEFAULT_SETTINGS + sync Run one replication cycle and exit + p2p-peers Show discovered peers as [peer] + p2p-sync Synchronise with specified peer-id or peer-name + p2p-host Start P2P host mode and wait until interrupted (Ctrl+C) + push Push local file into local database path + pull Pull file from local database into local file + pull-rev Pull specific revision into local file + setup Apply setup URI to settings file + put Read text from standard input and write to local database path + cat Write latest file content from local database to standard output + cat-rev Write specific revision content from local database to standard output + ls [prefix] List files as pathsizemtimerevision[*] + info Show file metadata including current and past revisions, conflicts, and chunk list + rm Mark file as deleted in local database + resolve Resolve conflict by keeping the specified revision + mirror [vaultPath] Mirror database contents to the local file system + (vaultPath positional arg > --vault flag > database-path) +``` + +Run via npm script: + +```bash +npm run --silent cli -- [database-path] [options] [command] [command-args] +``` + +#### Detailed Command Descriptions + +##### ls +`ls` lists files in the local database with optional prefix filtering. Output format is: + +```vault/path/file.mdsizemtimerevision[*] +``` +Note: `*` indicates if the file has conflicts. + +##### p2p-peers + +`p2p-peers ` waits for the specified number of seconds, then prints each discovered peer on a separate line: + +```text +[peer] +``` + +Use this command to select a target for `p2p-sync`. + +##### p2p-sync + +`p2p-sync ` discovers peers up to the specified timeout and synchronises with the selected peer. + +- `` accepts either `peer-id` or `peer-name` from `p2p-peers` output. +- On success, the command prints a completion message to standard error and exits with status code `0`. +- On failure, the command prints an error message and exits non-zero. + +##### p2p-host + +`p2p-host` starts the local P2P host and keeps running until interrupted. + +- Other peers can discover and synchronise with this host while it is running. +- Stop the host with `Ctrl+C`. +- In CLI mode, behaviour is non-interactive and acceptance follows settings. + +##### info + +`info` output fields: + +- `id`: Document ID +- `revision`: Current revision +- `conflicts`: Conflicted revisions, or `N/A` +- `filename`: Basename of path +- `path`: Vault-relative path +- `size`: Size in bytes +- `revisions`: Available non-current revisions +- `chunks`: Number of chunk IDs +- `children`: Chunk ID list + +##### mirror + +`mirror` is a command that synchronises your storage with your local vault. It is essentially a process that runs upon startup in Obsidian. + +In other words, it performs the following actions: + +1. **Precondition checks** — Aborts early if any of the following conditions are not met: + - Settings must be configured (`isConfigured: true`). + - File watching must not be suspended (`suspendFileWatching: false`). + - Remediation mode must be inactive (`maxMTimeForReflectEvents: 0`). + +2. **State restoration** — On subsequent runs (after the first successful scan), restores the previous storage state before proceeding. + +3. **Expired deletion cleanup** — If `automaticallyDeleteMetadataOfDeletedFiles` is set to a positive number of days, any document that is marked deleted and whose `mtime` is older than the retention period is permanently removed from the local database. + +4. **File collection** — Enumerates files from two sources: + - **Storage**: all files under the vault path that pass `isTargetFile`. + - **Local database**: all normal documents (fetched with conflict information) whose paths are valid and pass `isTargetFile`. + - Both collections build case-insensitive ↔ case-sensitive path maps, controlled by `handleFilenameCaseSensitive`. + +5. **Categorisation and synchronisation** — The union of both file sets is split into three groups and processed concurrently (up to 10 files at a time): + + | Group | Condition | Action | + | ----------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | **UPDATE DATABASE** | File exists in storage only | Store the file into the local database. | + | **UPDATE STORAGE** | File exists in database only | If the entry is active (not deleted) and not conflicted, restore the file from the database to storage. Deleted entries and conflicted entries are skipped. | + | **SYNC DATABASE AND STORAGE** | File exists in both | Compare `mtime` freshness. If storage is newer → write to database (`STORAGE → DB`). If database is newer → restore to storage (`STORAGE ← DB`). If equal → do nothing. Conflicted documents and files exceeding the size limit are always skipped. | + +6. **Initialisation flag** — On the very first successful run, writes `initialized = true` to the key-value database so that subsequent runs can restore state in step 2. + +Note: `mirror` does not respect file deletions. If a file is deleted in storage, it will be restored on the next `mirror` run. To delete a file, use the `rm` command instead. This is a little inconvenient, but it is intentional behaviour (if we handle this automatically in `mirror`, we should be against a ton of edge cases). + +##### daemon + +`daemon` is the default command when no command is specified. It runs an initial mirror scan and then continuously syncs changes in both directions: + +- **CouchDB → local filesystem**: via the `_changes` feed (LiveSync mode, default) or periodic polling (`--interval N`). +- **local filesystem → CouchDB**: via chokidar file watching. Any file created, modified, or deleted in the vault directory is pushed to CouchDB. + +In **LiveSync mode** the `_changes` feed delivers remote changes as they arrive, with sub-second latency. In **polling mode** (`--interval N`) the CLI polls CouchDB every N seconds. Use polling mode if your CouchDB instance does not support long-lived HTTP connections, or if you need predictable network usage. + +The daemon exits cleanly on `SIGINT` or `SIGTERM`. + +```bash +# LiveSync mode (default — _changes feed, near-real-time) +livesync-cli /path/to/vault + +# Polling mode — poll every 60 seconds +livesync-cli /path/to/vault --interval 60 +``` + +### .livesync/ignore + +Place a `.livesync/ignore` file in your vault root to exclude files from sync in both directions (local → CouchDB and CouchDB → local). + +**Format:** + +- Lines beginning with `#` are comments. +- Blank lines are ignored. +- All other lines are [minimatch](https://github.com/isaacs/minimatch) glob patterns, relative to the vault root. +- The directive `import: .gitignore` (exactly this string) reads `.gitignore` from the vault root and merges its non-comment, non-blank lines into the ignore rules. +- Negation patterns (lines starting with `!`) are not supported and will cause an error on load. + +**Example `.livesync/ignore`:** + +``` +# Ignore temporary files +*.tmp +*.swp + +# Ignore build output +build/ +dist/ + +# Merge patterns from .gitignore +import: .gitignore +``` + +Patterns apply in both directions: the chokidar watcher will not emit events for matched files, and the `isTargetFile` filter will exclude them from CouchDB → local sync. + +Changes to this file require a daemon restart to take effect. + +### Systemd Installation + +The `deploy/` directory contains a systemd unit template and an install script. + +**Automated install (user service, recommended):** + +```bash +bash src/apps/cli/deploy/install.sh --vault /path/to/vault +``` + +**With polling interval:** + +```bash +bash src/apps/cli/deploy/install.sh --vault /path/to/vault --interval 60 +``` + +**System-wide install** (requires root / sudo for `/etc/systemd/system/`): + +```bash +bash src/apps/cli/deploy/install.sh --system --vault /path/to/vault +``` + +The script: +1. Builds the CLI (`npm install` + `npm run build`). +2. Installs the binary to `~/.local/bin/livesync-cli` (user) or `/usr/local/bin/livesync-cli` (system). +3. Writes the unit file to `~/.config/systemd/user/livesync-cli.service` (user) or `/etc/systemd/system/livesync-cli.service` (system). +4. Runs `systemctl [--user] daemon-reload && systemctl [--user] enable --now livesync-cli`. + +**Manual setup** — if you prefer to manage the unit yourself, copy `deploy/livesync-cli.service`, replace `LIVESYNC_BIN` and `LIVESYNC_VAULT_PATH` with the actual binary path and vault path, then install to the appropriate systemd directory. + +### Planned options: + +- `--immediate`: Perform sync after the command (e.g. `push`, `pull`, `put`, `rm`). +- `serve`: Start CLI in server mode, exposing REST APIs for remote, and batch operations. +- `cause-conflicted `: Mark a file as conflicted without changing its content, to trigger conflict resolution in Obsidian. + +## Use Cases + +### 1. Bootstrap a new headless vault + +Create default settings, apply a setup URI, then run one sync cycle. + +```bash +livesync-cli -- init-settings /data/livesync-settings.json +printf '%s\n' "$SETUP_PASSPHRASE" | livesync-cli -- /data/vault --settings /data/livesync-settings.json setup "$SETUP_URI" +livesync-cli -- /data/vault --settings /data/livesync-settings.json sync +``` + +### 2. Scripted import and export + +Push local files into the database from automation, and pull them back for export or backup. + +```bash +livesync-cli -- /data/vault --settings /data/livesync-settings.json push ./note.md notes/note.md +livesync-cli -- /data/vault --settings /data/livesync-settings.json pull notes/note.md ./exports/note.md +``` + +### 3. Revision inspection and restore + +List metadata, find an older revision, then restore it by content (`cat-rev`) or file output (`pull-rev`). + +```bash +livesync-cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md +livesync-cli -- /data/vault --settings /data/livesync-settings.json cat-rev notes/note.md 3-abcdef +livesync-cli -- /data/vault --settings /data/livesync-settings.json pull-rev notes/note.md ./restore/note.old.md 3-abcdef +``` + +### 4. Conflict and cleanup workflow + +Inspect conflicted revisions, resolve by keeping one revision, then delete obsolete files. + +```bash +livesync-cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md +livesync-cli -- /data/vault --settings /data/livesync-settings.json resolve notes/note.md 3-abcdef +livesync-cli -- /data/vault --settings /data/livesync-settings.json rm notes/obsolete.md +``` + +### 5. CI smoke test for content round-trip + +Validate that `put`/`cat` is behaving as expected in a pipeline. + +```bash +echo "hello-ci" | livesync-cli -- /data/vault --settings /data/livesync-settings.json put ci/test.md +livesync-cli -- /data/vault --settings /data/livesync-settings.json cat ci/test.md +``` + +## Development + +### Project Structure + +``` +src/apps/cli/ +├── commands/ # Command dispatcher and command utilities +│ ├── runCommand.ts +│ ├── runCommand.unit.spec.ts +│ ├── types.ts +│ ├── utils.ts +│ └── utils.unit.spec.ts +├── adapters/ # Node.js FileSystem Adapter +│ ├── NodeConversionAdapter.ts +│ ├── NodeFileSystemAdapter.ts +│ ├── NodePathAdapter.ts +│ ├── NodeStorageAdapter.ts +│ ├── NodeStorageAdapter.unit.spec.ts +│ ├── NodeTypeGuardAdapter.ts +│ ├── NodeTypes.ts +│ └── NodeVaultAdapter.ts +├── lib/ +│ └── pouchdb-node.ts +├── managers/ # CLI-specific managers +│ ├── CLIStorageEventManagerAdapter.ts +│ └── StorageEventManagerCLI.ts +├── serviceModules/ # Service modules (ported from main.ts) +│ ├── CLIServiceModules.ts +│ ├── DatabaseFileAccess.ts +│ ├── FileAccessCLI.ts +│ └── ServiceFileAccessImpl.ts +├── services/ +│ ├── NodeKeyValueDBService.ts +│ ├── NodeServiceHub.ts +│ └── NodeSettingService.ts +├── test/ +│ ├── test-e2e-two-vaults-common.sh +│ ├── test-e2e-two-vaults-matrix.sh +│ ├── test-e2e-two-vaults-with-docker-linux.sh +│ ├── test-push-pull-linux.sh +│ ├── test-setup-put-cat-linux.sh +│ └── test-sync-two-local-databases-linux.sh +├── .gitignore +├── entrypoint.ts # CLI executable entry point (shebang) +├── main.ts # CLI entry point +├── main.unit.spec.ts +├── package.json +├── README.md # This file +├── tsconfig.json +├── util/ # Test and local utility scripts +└── vite.config.ts +``` diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md new file mode 100644 index 0000000..25ffd96 --- /dev/null +++ b/test/e2e-obsidian/README.md @@ -0,0 +1,65 @@ +# Real Obsidian E2E Runner + +This directory contains the experimental real Obsidian end-to-end runner. + +The current smoke runner verifies only the launch path: + +1. create a temporary vault, +2. install the built Self-hosted LiveSync plug-in artifacts, +3. launch real Obsidian, +4. open the temporary vault through `obsidian-cli`, +5. enable Obsidian community plug-ins for the temporary app profile, +6. reload Self-hosted LiveSync through `obsidian-cli`, +7. verify through `obsidian-cli eval` that the plug-in is loaded, +8. terminate Obsidian and remove the temporary vault. + +The runner does not require Self-hosted LiveSync to expose an E2E-only bridge. Readiness is checked from outside the plug-in through Obsidian's own CLI. + +Obsidian 1.12 stores the global community plug-in switch outside `.obsidian/community-plugins.json`. The smoke runner enables it through `app.plugins.setEnable(true)` after the vault window is available. + +## Local Setup + +Set `OBSIDIAN_BINARY` when Obsidian is not installed in a standard location. + +For an AppImage on Linux without FUSE, use the helper script: + +```bash +npm run test:e2e:obsidian:install-appimage +``` + +The script downloads Obsidian `1.12.7` for the current architecture, stores it in `_testdata/obsidian`, and extracts it to `_testdata/obsidian/squashfs-root`. The runner checks `_testdata/obsidian/squashfs-root/obsidian` before the AppImage path. + +Do not download the AppImage on every CI run. Prefer one of these approaches: + +- set `OBSIDIAN_BINARY` to a pre-installed Obsidian executable, +- restore `_testdata/obsidian/squashfs-root` from a CI cache, or +- run `test:e2e:obsidian:install-appimage` only in a manually triggered preparation job. + +## Commands + +```bash +npm run test:e2e:obsidian:install-appimage +npm run test:e2e:obsidian:discover +npm run test:e2e:obsidian:cli-help -- vaults verbose +npm run test:e2e:obsidian:smoke +``` + +Useful environment variables: + +- `OBSIDIAN_BINARY`: explicit Obsidian executable path. +- `E2E_OBSIDIAN_VERSION`: Obsidian AppImage version for `test:e2e:obsidian:install-appimage`; default is `1.12.7`. +- `E2E_OBSIDIAN_APPIMAGE_ARCH`: AppImage architecture override, such as `arm64` or `x86_64`. +- `E2E_OBSIDIAN_APPIMAGE_URL`: explicit AppImage URL override. +- `E2E_OBSIDIAN_DOWNLOAD_DIR`: AppImage download and extraction directory; default is `_testdata/obsidian`. +- `E2E_OBSIDIAN_FORCE_DOWNLOAD=true`: re-download the AppImage even when it exists. +- `E2E_OBSIDIAN_SKIP_EXTRACT=true`: download the AppImage without extracting it. +- `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS`: smoke timeout in milliseconds. +- `E2E_OBSIDIAN_READY_TIMEOUT_MS`: plug-in readiness timeout in milliseconds. +- `E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS`: timeout for waiting until the vault-side Obsidian CLI exposes the plug-in catalogue. +- `E2E_OBSIDIAN_CLI_TIMEOUT_MS`: timeout for each `obsidian-cli` invocation. +- `E2E_OBSIDIAN_STARTUP_GRACE_MS`: early process-exit detection window in milliseconds. +- `E2E_OBSIDIAN_KEEP_VAULT=true`: keep the temporary vault for inspection. +- `E2E_OBSIDIAN_USE_XVFB=false`: disable automatic `xvfb-run` on headless Linux. +- `E2E_OBSIDIAN_ARGS`: override the default Obsidian launch arguments. + +On headless Linux, the runner automatically uses `/usr/bin/xvfb-run` when no `DISPLAY` or `WAYLAND_DISPLAY` is present. diff --git a/test/e2e-obsidian/runner/cli.ts b/test/e2e-obsidian/runner/cli.ts new file mode 100644 index 0000000..f1766b8 --- /dev/null +++ b/test/e2e-obsidian/runner/cli.ts @@ -0,0 +1,62 @@ +import { spawn } from "node:child_process"; + +export type ObsidianCliResult = { + code: number | null; + signal: NodeJS.Signals | null; + stdout: string; + stderr: string; +}; + +export async function runObsidianCli( + cliBinary: string, + args: string[], + env: NodeJS.ProcessEnv = process.env, + timeoutMs = Number(process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ?? 10000) +): Promise { + return await new Promise((resolve, reject) => { + const child = spawn(cliBinary, args, { + stdio: ["ignore", "pipe", "pipe"], + env, + }); + let stdout = ""; + let stderr = ""; + const timeout = setTimeout(() => { + child.kill("SIGKILL"); + reject(new Error(`Obsidian CLI timed out: ${cliBinary} ${args.join(" ")}`)); + }, timeoutMs); + + child.stdout?.on("data", (chunk: Buffer) => { + stdout += chunk.toString(); + }); + child.stderr?.on("data", (chunk: Buffer) => { + stderr += chunk.toString(); + }); + child.on("error", (error) => { + clearTimeout(timeout); + reject(error); + }); + child.on("exit", (code, signal) => { + clearTimeout(timeout); + resolve({ code, signal, stdout, stderr }); + }); + }); +} + +export async function openVaultWithObsidianCli( + cliBinary: string, + vaultPath: string, + env: NodeJS.ProcessEnv = process.env +): Promise { + const result = await runObsidianCli(cliBinary, [`obsidian://open?path=${encodeURIComponent(vaultPath)}`], env); + if (result.code !== 0) { + throw new Error( + [ + `Failed to open Obsidian vault through CLI. code=${result.code}, signal=${result.signal}`, + result.stdout ? `stdout:\n${result.stdout}` : undefined, + result.stderr ? `stderr:\n${result.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } +} diff --git a/test/e2e-obsidian/runner/environment.ts b/test/e2e-obsidian/runner/environment.ts new file mode 100644 index 0000000..46b08a6 --- /dev/null +++ b/test/e2e-obsidian/runner/environment.ts @@ -0,0 +1,149 @@ +import { accessSync, constants, existsSync } from "node:fs"; +import { resolve } from "node:path"; +import { platform } from "node:process"; + +export type ObsidianDiscoveryResult = { + binary?: string; + source?: string; + checked: string[]; +}; + +const defaultCandidatesByPlatform: Record = { + aix: [], + android: [], + darwin: [ + "/Applications/Obsidian.app/Contents/MacOS/Obsidian", + "/Applications/Obsidian.app/Contents/MacOS/obsidian", + ], + freebsd: [], + haiku: [], + linux: [ + "_testdata/obsidian/squashfs-root/obsidian", + "_testdata/obsidian/squashfs-root/AppRun", + "_testdata/obsidian/Obsidian-1.12.7-arm64.AppImage", + "_testdata/obsidian/Obsidian-1.12.7-x86_64.AppImage", + "/usr/bin/obsidian", + "/usr/local/bin/obsidian", + "/snap/bin/obsidian", + "/opt/Obsidian/obsidian", + "/opt/obsidian/obsidian", + "/app/bin/obsidian", + ], + openbsd: [], + sunos: [], + win32: ["C:\\Program Files\\Obsidian\\Obsidian.exe", "C:\\Program Files (x86)\\Obsidian\\Obsidian.exe"], + cygwin: [], + netbsd: [], +}; + +const defaultCliCandidatesByPlatform: Record = { + aix: [], + android: [], + darwin: [ + "/Applications/Obsidian.app/Contents/MacOS/obsidian-cli", + "/Applications/Obsidian.app/Contents/Resources/obsidian-cli", + ], + freebsd: [], + haiku: [], + linux: [ + "_testdata/obsidian/squashfs-root/obsidian-cli", + "/usr/bin/obsidian-cli", + "/usr/local/bin/obsidian-cli", + "/snap/bin/obsidian-cli", + "/opt/Obsidian/obsidian-cli", + "/opt/obsidian/obsidian-cli", + ], + openbsd: [], + sunos: [], + win32: ["C:\\Program Files\\Obsidian\\obsidian-cli.exe", "C:\\Program Files (x86)\\Obsidian\\obsidian-cli.exe"], + cygwin: [], + netbsd: [], +}; + +function isUsableFile(path: string): boolean { + const resolvedPath = resolve(path); + if (!existsSync(resolvedPath)) { + return false; + } + if (platform === "win32") { + return true; + } + try { + accessSync(resolvedPath, constants.X_OK); + return true; + } catch { + return false; + } +} + +export function discoverObsidianBinary(env: NodeJS.ProcessEnv = process.env): ObsidianDiscoveryResult { + const checked: string[] = []; + const envBinary = env.OBSIDIAN_BINARY?.trim(); + if (envBinary) { + checked.push(envBinary); + if (isUsableFile(envBinary)) { + return { + binary: resolve(envBinary), + source: "OBSIDIAN_BINARY", + checked, + }; + } + } + + const candidates = defaultCandidatesByPlatform[platform] ?? []; + for (const candidate of candidates) { + checked.push(candidate); + if (isUsableFile(candidate)) { + return { + binary: resolve(candidate), + source: "default-path", + checked, + }; + } + } + + return { checked }; +} + +export function requireObsidianBinary(env: NodeJS.ProcessEnv = process.env): string { + const result = discoverObsidianBinary(env); + if (!result.binary) { + throw new Error( + [ + "Could not find an Obsidian executable.", + "Set OBSIDIAN_BINARY to the installed Obsidian executable path.", + `Checked paths: ${result.checked.length > 0 ? result.checked.join(", ") : "(none)"}`, + ].join("\n") + ); + } + return result.binary; +} + +export function discoverObsidianCli(env: NodeJS.ProcessEnv = process.env): ObsidianDiscoveryResult { + const checked: string[] = []; + const envBinary = env.OBSIDIAN_CLI?.trim(); + if (envBinary) { + checked.push(envBinary); + if (isUsableFile(envBinary)) { + return { + binary: resolve(envBinary), + source: "OBSIDIAN_CLI", + checked, + }; + } + } + + const candidates = defaultCliCandidatesByPlatform[platform] ?? []; + for (const candidate of candidates) { + checked.push(candidate); + if (isUsableFile(candidate)) { + return { + binary: resolve(candidate), + source: "default-path", + checked, + }; + } + } + + return { checked }; +} diff --git a/test/e2e-obsidian/runner/launch.ts b/test/e2e-obsidian/runner/launch.ts new file mode 100644 index 0000000..8a9b602 --- /dev/null +++ b/test/e2e-obsidian/runner/launch.ts @@ -0,0 +1,119 @@ +import { spawn, type ChildProcess } from "node:child_process"; +import { once } from "node:events"; +import { existsSync } from "node:fs"; +import { dirname } from "node:path"; +import { platform } from "node:process"; + +export type ObsidianProcess = { + process: ChildProcess; + stop: () => Promise; +}; + +export type LaunchObsidianOptions = { + binary: string; + vaultPath: string; + homePath?: string; + xdgConfigPath?: string; + userDataPath?: string; + startupGraceMs?: number; +}; + +function splitArgs(args: string): string[] { + return args.split(" ").filter((arg) => arg.length > 0); +} + +function launchArgs(options: LaunchObsidianOptions): string[] { + const explicitArgs = process.env.E2E_OBSIDIAN_ARGS; + if (explicitArgs) { + return splitArgs(explicitArgs); + } + return [ + "--no-sandbox", + "--disable-gpu", + "--disable-software-rasterizer", + ...(process.env.E2E_OBSIDIAN_USE_USER_DATA_DIR === "true" && options.userDataPath + ? [`--user-data-dir=${options.userDataPath}`] + : []), + ]; +} + +function shouldUseXvfb(): boolean { + if (process.env.E2E_OBSIDIAN_USE_XVFB === "false") { + return false; + } + if (process.env.DISPLAY || process.env.WAYLAND_DISPLAY) { + return false; + } + return platform === "linux" && existsSync("/usr/bin/xvfb-run"); +} + +export async function launchObsidian(options: LaunchObsidianOptions): Promise { + const startupGraceMs = options.startupGraceMs ?? 1000; + const args = launchArgs(options); + const useXvfb = shouldUseXvfb(); + const command = useXvfb ? "/usr/bin/xvfb-run" : options.binary; + const commandArgs = useXvfb ? ["-a", options.binary, ...args] : args; + const child = spawn(command, commandArgs, { + cwd: dirname(options.binary), + detached: true, + stdio: ["ignore", "pipe", "pipe"], + env: { + ...process.env, + ...(options.homePath ? { HOME: options.homePath } : {}), + ...(options.xdgConfigPath ? { XDG_CONFIG_HOME: options.xdgConfigPath } : {}), + OBSIDIAN_DISABLE_GPU: process.env.OBSIDIAN_DISABLE_GPU ?? "1", + }, + }); + + let stderr = ""; + let stdout = ""; + child.stderr?.on("data", (chunk: Buffer) => { + stderr += chunk.toString(); + }); + child.stdout?.on("data", (chunk: Buffer) => { + stdout += chunk.toString(); + }); + + const exitPromise = once(child, "exit").then(([code, signal]) => ({ code, signal })); + const timer = new Promise<"timeout">((resolve) => { + setTimeout(() => resolve("timeout"), startupGraceMs); + }); + const firstResult = await Promise.race([exitPromise, timer]); + if (firstResult !== "timeout") { + throw new Error( + [ + `Obsidian exited before the smoke timeout. code=${firstResult.code}, signal=${firstResult.signal}`, + stdout ? `stdout:\n${stdout}` : undefined, + stderr ? `stderr:\n${stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } + + return { + process: child, + stop: async () => { + if (child.exitCode !== null || child.signalCode !== null) { + return; + } + if (child.pid) { + process.kill(-child.pid, "SIGTERM"); + } else { + child.kill("SIGTERM"); + } + const stopTimer = new Promise<"timeout">((resolve) => { + setTimeout(() => resolve("timeout"), 5000); + }); + const stopResult = await Promise.race([exitPromise, stopTimer]); + if (stopResult === "timeout") { + if (child.pid) { + process.kill(-child.pid, "SIGKILL"); + } else { + child.kill("SIGKILL"); + } + await exitPromise; + } + }, + }; +} diff --git a/test/e2e-obsidian/runner/pluginInstaller.ts b/test/e2e-obsidian/runner/pluginInstaller.ts new file mode 100644 index 0000000..db28cc9 --- /dev/null +++ b/test/e2e-obsidian/runner/pluginInstaller.ts @@ -0,0 +1,39 @@ +import { copyFile, mkdir, writeFile } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { join, resolve } from "node:path"; + +export type PluginInstallResult = { + pluginDir: string; + copied: string[]; +}; + +const pluginId = "obsidian-livesync"; + +export async function installBuiltPlugin(vaultPath: string, rootDir = process.cwd()): Promise { + const pluginDir = join(vaultPath, ".obsidian", "plugins", pluginId); + const copied: string[] = []; + await mkdir(pluginDir, { recursive: true }); + + const requiredArtifacts = ["main.js", "manifest.json"]; + for (const artifact of requiredArtifacts) { + const source = resolve(rootDir, artifact); + if (!existsSync(source)) { + throw new Error(`Required plug-in artifact is missing: ${source}`); + } + await copyFile(source, join(pluginDir, artifact)); + copied.push(artifact); + } + + const optionalArtifacts = ["styles.css"]; + for (const artifact of optionalArtifacts) { + const source = resolve(rootDir, artifact); + if (!existsSync(source)) { + continue; + } + await copyFile(source, join(pluginDir, artifact)); + copied.push(artifact); + } + + await writeFile(join(vaultPath, ".obsidian", "community-plugins.json"), JSON.stringify([pluginId], null, 4)); + return { pluginDir, copied }; +} diff --git a/test/e2e-obsidian/runner/readiness.ts b/test/e2e-obsidian/runner/readiness.ts new file mode 100644 index 0000000..d56c2af --- /dev/null +++ b/test/e2e-obsidian/runner/readiness.ts @@ -0,0 +1,52 @@ +import { runObsidianCli } from "./cli.ts"; + +export type PluginReadiness = { + status: "ready"; + pluginId: string; + pluginVersion: string; + vaultName: string; +}; + +function parseEvalJson(stdout: string): unknown { + const marker = "=> "; + const markerIndex = stdout.indexOf(marker); + const text = markerIndex >= 0 ? stdout.slice(markerIndex + marker.length) : stdout; + return JSON.parse(text.trim()); +} + +export async function waitForPluginReady( + cliBinary: string, + env: NodeJS.ProcessEnv, + timeoutMs = Number(process.env.E2E_OBSIDIAN_READY_TIMEOUT_MS ?? 20000) +): Promise { + const deadline = Date.now() + timeoutMs; + let lastOutput = ""; + while (Date.now() < deadline) { + const result = await runObsidianCli( + cliBinary, + [ + "eval", + [ + "code=(async()=>JSON.stringify({", + "status:!!app.plugins.plugins['obsidian-livesync']?'ready':'pending',", + "pluginId:'obsidian-livesync',", + "pluginVersion:app.plugins.manifests['obsidian-livesync']?.version,", + "vaultName:app.vault.getName()", + "}))()", + ].join(""), + ], + env + ); + lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n"); + try { + const readiness = parseEvalJson(result.stdout) as PluginReadiness; + if (readiness.status === "ready") { + return readiness; + } + } catch { + // Keep polling until Obsidian exposes the vault-side CLI and plug-in state. + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Timed out waiting for Self-hosted LiveSync readiness through Obsidian CLI.\n${lastOutput}`); +} diff --git a/test/e2e-obsidian/runner/vault.ts b/test/e2e-obsidian/runner/vault.ts new file mode 100644 index 0000000..4f151b4 --- /dev/null +++ b/test/e2e-obsidian/runner/vault.ts @@ -0,0 +1,72 @@ +import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; + +export type TemporaryVault = { + path: string; + name: string; + homePath: string; + xdgConfigPath: string; + userDataPath: string; + dispose: () => Promise; +}; + +export async function createTemporaryVault(prefix = "obsidian-livesync-e2e-"): Promise { + const vaultPath = await mkdtemp(join(tmpdir(), prefix)); + const name = vaultPath.split(/[\\/]/).pop() ?? "obsidian-livesync-e2e"; + await mkdir(join(vaultPath, ".obsidian"), { recursive: true }); + const homePath = join(vaultPath, ".obsidian", "e2e-home"); + const xdgConfigPath = join(vaultPath, ".obsidian", "e2e-xdg-config"); + const userDataPath = join(vaultPath, ".obsidian", "e2e-user-data"); + await mkdir(homePath, { recursive: true }); + await mkdir(xdgConfigPath, { recursive: true }); + await mkdir(userDataPath, { recursive: true }); + await writeFile( + join(vaultPath, ".obsidian", "app.json"), + JSON.stringify({ legacyEditor: false, safeMode: false }, null, 4) + ); + await writeObsidianVaultRegistry(vaultPath, name, homePath, xdgConfigPath, userDataPath); + + return { + path: vaultPath, + name, + homePath, + xdgConfigPath, + userDataPath, + dispose: async () => { + if (process.env.E2E_OBSIDIAN_KEEP_VAULT === "true") { + console.log(`Keeping temporary vault: ${vaultPath}`); + return; + } + await rm(vaultPath, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }); + }, + }; +} + +async function writeObsidianVaultRegistry( + vaultPath: string, + vaultName: string, + homePath: string, + xdgConfigPath: string, + userDataPath: string +): Promise { + const vaultId = `livesync-e2e-${Date.now()}`; + const registry = { + cli: true, + vaults: { + [vaultId]: { + path: vaultPath, + ts: Date.now(), + open: true, + name: vaultName, + }, + }, + }; + const registryText = JSON.stringify(registry, null, 4); + for (const configRoot of [join(homePath, ".config"), xdgConfigPath]) { + const obsidianConfigDir = join(configRoot, "obsidian"); + await mkdir(obsidianConfigDir, { recursive: true }); + await writeFile(join(obsidianConfigDir, "obsidian.json"), registryText); + } + await writeFile(join(userDataPath, "obsidian.json"), registryText); +} diff --git a/test/e2e-obsidian/scripts/cli-help.ts b/test/e2e-obsidian/scripts/cli-help.ts new file mode 100644 index 0000000..f48de33 --- /dev/null +++ b/test/e2e-obsidian/scripts/cli-help.ts @@ -0,0 +1,51 @@ +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { launchObsidian } from "../runner/launch.ts"; +import { runObsidianCli } from "../runner/cli.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; +import { installBuiltPlugin } from "../runner/pluginInstaller.ts"; + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + const vault = await createTemporaryVault(); + let app; + try { + await installBuiltPlugin(vault.path); + app = await launchObsidian({ + binary, + vaultPath: vault.path, + homePath: vault.homePath, + xdgConfigPath: vault.xdgConfigPath, + userDataPath: vault.userDataPath, + }); + const cliEnv = { + ...process.env, + HOME: vault.homePath, + XDG_CONFIG_HOME: vault.xdgConfigPath, + }; + await runObsidianCli(cli.binary, [`obsidian://open?path=${encodeURIComponent(vault.path)}`], cliEnv); + await new Promise((resolve) => setTimeout(resolve, 3000)); + if (process.env.E2E_OBSIDIAN_RELOAD_PLUGIN === "true") { + await runObsidianCli(cli.binary, ["eval", "code=(async()=>app.plugins.setEnable(true))()"], cliEnv); + await runObsidianCli(cli.binary, ["plugin:reload", "id=obsidian-livesync"], cliEnv); + } + const cliArgs = process.argv.slice(2); + const result = await runObsidianCli(cli.binary, cliArgs.length > 0 ? cliArgs : ["--help"], cliEnv); + console.log(result.stdout); + console.error(result.stderr); + process.exitCode = result.code ?? 1; + } finally { + if (app) { + await app.stop(); + } + await vault.dispose(); + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/discover.ts b/test/e2e-obsidian/scripts/discover.ts new file mode 100644 index 0000000..3b18888 --- /dev/null +++ b/test/e2e-obsidian/scripts/discover.ts @@ -0,0 +1,13 @@ +import { discoverObsidianBinary } from "../runner/environment.ts"; + +const result = discoverObsidianBinary(); +if (result.binary) { + console.log(`Obsidian executable: ${result.binary}`); + console.log(`Source: ${result.source}`); + process.exit(0); +} + +console.error("Obsidian executable was not found."); +console.error("Set OBSIDIAN_BINARY to the installed Obsidian executable path."); +console.error(`Checked paths: ${result.checked.length > 0 ? result.checked.join(", ") : "(none)"}`); +process.exit(1); diff --git a/test/e2e-obsidian/scripts/install-appimage.ts b/test/e2e-obsidian/scripts/install-appimage.ts new file mode 100644 index 0000000..72c7cab --- /dev/null +++ b/test/e2e-obsidian/scripts/install-appimage.ts @@ -0,0 +1,121 @@ +import { createWriteStream, existsSync } from "node:fs"; +import { chmod, mkdir } from "node:fs/promises"; +import { get } from "node:https"; +import { arch } from "node:process"; +import { basename, join, resolve } from "node:path"; +import { spawn } from "node:child_process"; + +const defaultVersion = "1.12.7"; + +function appImageArch(): string { + const requestedArch = process.env.E2E_OBSIDIAN_APPIMAGE_ARCH?.trim(); + if (requestedArch) { + return requestedArch; + } + if (arch === "arm64") { + return "arm64"; + } + if (arch === "x64") { + return "x86_64"; + } + throw new Error(`Unsupported architecture for Obsidian AppImage: ${arch}`); +} + +function appImageUrl(version: string, imageArch: string): string { + return `https://github.com/obsidianmd/obsidian-releases/releases/download/v${version}/Obsidian-${version}-${imageArch}.AppImage`; +} + +function download(url: string, destination: string, redirectsLeft = 5): Promise { + return new Promise((resolveDownload, reject) => { + const request = get(url, (response) => { + const statusCode = response.statusCode ?? 0; + const location = response.headers.location; + if (statusCode >= 300 && statusCode < 400 && location) { + response.resume(); + if (redirectsLeft <= 0) { + reject(new Error(`Too many redirects while downloading ${url}`)); + return; + } + download(new URL(location, url).toString(), destination, redirectsLeft - 1) + .then(resolveDownload) + .catch(reject); + return; + } + if (statusCode !== 200) { + response.resume(); + reject(new Error(`Failed to download ${url}: HTTP ${statusCode}`)); + return; + } + + const file = createWriteStream(destination, { mode: 0o755 }); + response.pipe(file); + file.on("finish", () => { + file.close((error) => { + if (error) { + reject(error); + } else { + resolveDownload(); + } + }); + }); + file.on("error", reject); + }); + request.on("error", reject); + }); +} + +function extractAppImage(appImagePath: string, cwd: string): Promise { + return new Promise((resolveExtract, reject) => { + const child = spawn(appImagePath, ["--appimage-extract"], { + cwd, + stdio: "inherit", + }); + child.on("error", reject); + child.on("exit", (code, signal) => { + if (code === 0) { + resolveExtract(); + return; + } + reject(new Error(`AppImage extraction failed. code=${code}, signal=${signal}`)); + }); + }); +} + +async function main(): Promise { + const version = process.env.E2E_OBSIDIAN_VERSION?.trim() || defaultVersion; + const imageArch = appImageArch(); + const targetDir = resolve(process.env.E2E_OBSIDIAN_DOWNLOAD_DIR?.trim() || "_testdata/obsidian"); + const url = process.env.E2E_OBSIDIAN_APPIMAGE_URL?.trim() || appImageUrl(version, imageArch); + const appImagePath = join(targetDir, basename(new URL(url).pathname)); + const extractedBinary = join(targetDir, "squashfs-root", "obsidian"); + const forceDownload = process.env.E2E_OBSIDIAN_FORCE_DOWNLOAD === "true"; + const skipExtract = process.env.E2E_OBSIDIAN_SKIP_EXTRACT === "true"; + + await mkdir(targetDir, { recursive: true }); + + if (!existsSync(appImagePath) || forceDownload) { + console.log(`Downloading Obsidian AppImage: ${url}`); + console.log(`Destination: ${appImagePath}`); + await download(url, appImagePath); + await chmod(appImagePath, 0o755); + } else { + console.log(`Using existing Obsidian AppImage: ${appImagePath}`); + } + + if (!skipExtract) { + if (existsSync(extractedBinary)) { + console.log(`Using existing extracted Obsidian binary: ${extractedBinary}`); + } else { + console.log(`Extracting Obsidian AppImage in ${targetDir}`); + await extractAppImage(appImagePath, targetDir); + console.log(`Extracted Obsidian binary: ${extractedBinary}`); + } + } + + console.log(`Set OBSIDIAN_BINARY=${extractedBinary} to use the extracted binary explicitly.`); +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/smoke.ts b/test/e2e-obsidian/scripts/smoke.ts new file mode 100644 index 0000000..ad9bf8e --- /dev/null +++ b/test/e2e-obsidian/scripts/smoke.ts @@ -0,0 +1,96 @@ +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { launchObsidian } from "../runner/launch.ts"; +import { installBuiltPlugin } from "../runner/pluginInstaller.ts"; +import { waitForPluginReady } from "../runner/readiness.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; +import { openVaultWithObsidianCli, runObsidianCli } from "../runner/cli.ts"; + +async function waitForPluginCatalogue(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS ?? 15000); + let lastOutput = ""; + while (Date.now() < deadline) { + const result = await runObsidianCli(cliBinary, ["plugins", "filter=community"], env); + lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n"); + if (result.stdout.includes("obsidian-livesync")) { + return; + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Timed out waiting for Obsidian plug-in catalogue through CLI.\n${lastOutput}`); +} + +async function enableCommunityPlugins(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + const result = await runObsidianCli(cliBinary, ["eval", "code=(async()=>app.plugins.setEnable(true))()"], env); + if (result.code !== 0 || result.stdout.includes("Error:")) { + throw new Error( + [ + `Failed to enable Obsidian community plug-ins through CLI. code=${result.code}, signal=${result.signal}`, + result.stdout ? `stdout:\n${result.stdout}` : undefined, + result.stderr ? `stderr:\n${result.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + const vault = await createTemporaryVault(); + let app; + try { + const install = await installBuiltPlugin(vault.path); + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault: ${vault.path}`); + console.log(`Installed plug-in artifacts: ${install.copied.join(", ")}`); + + app = await launchObsidian({ + binary, + vaultPath: vault.path, + homePath: vault.homePath, + xdgConfigPath: vault.xdgConfigPath, + userDataPath: vault.userDataPath, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + const cliEnv = { + ...process.env, + HOME: vault.homePath, + XDG_CONFIG_HOME: vault.xdgConfigPath, + }; + await openVaultWithObsidianCli(cli.binary, vault.path, cliEnv); + await waitForPluginCatalogue(cli.binary, cliEnv); + await enableCommunityPlugins(cli.binary, cliEnv); + const reload = await runObsidianCli(cli.binary, ["plugin:reload", "id=obsidian-livesync"], cliEnv); + if (reload.code !== 0 || !reload.stdout.includes("Reloaded: obsidian-livesync")) { + throw new Error( + [ + `Failed to reload Self-hosted LiveSync through Obsidian CLI. code=${reload.code}, signal=${reload.signal}`, + reload.stdout ? `stdout:\n${reload.stdout}` : undefined, + reload.stderr ? `stderr:\n${reload.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } + const readiness = await waitForPluginReady(cli.binary, cliEnv); + console.log( + `Obsidian plug-in ready: ${readiness.pluginId}@${readiness.pluginVersion} in ${readiness.vaultName}` + ); + await new Promise((resolve) => setTimeout(resolve, Number(process.env.E2E_OBSIDIAN_SMOKE_TIMEOUT_MS ?? 1000))); + console.log("Obsidian stayed alive after the plug-in readiness check."); + } finally { + if (app) { + await app.stop(); + } + await vault.dispose(); + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); From 4a33236c8f444715f85bca75d792d6fe5d859848 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 26 Jun 2026 10:46:45 +0000 Subject: [PATCH 06/16] (test): improve e2e session helper --- docs/adr/2026_06_real_obsidian_e2e.md | 8 +- package.json | 1 + test/e2e-obsidian/README.md | 4 + test/e2e-obsidian/runner/cli.ts | 27 ++++ test/e2e-obsidian/runner/readiness.ts | 29 ++--- test/e2e-obsidian/runner/session.ts | 94 ++++++++++++++ test/e2e-obsidian/scripts/smoke.ts | 73 ++--------- test/e2e-obsidian/scripts/vault-reflection.ts | 122 ++++++++++++++++++ 8 files changed, 273 insertions(+), 85 deletions(-) create mode 100644 test/e2e-obsidian/runner/session.ts create mode 100644 test/e2e-obsidian/scripts/vault-reflection.ts diff --git a/docs/adr/2026_06_real_obsidian_e2e.md b/docs/adr/2026_06_real_obsidian_e2e.md index 7fa8045..e4621fb 100644 --- a/docs/adr/2026_06_real_obsidian_e2e.md +++ b/docs/adr/2026_06_real_obsidian_e2e.md @@ -125,7 +125,8 @@ Initial discovery on Linux ARM64 found that: Current implementation status: - Added `test/e2e-obsidian/runner` helpers for Obsidian discovery, CLI discovery, temporary vault creation, plug-in installation, process launch, CLI execution, and readiness polling. -- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, and `test:e2e:obsidian:install-appimage`. +- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, and `test:e2e:obsidian:install-appimage`. +- Added `startObsidianLiveSyncSession()` so future workflows can reuse the launch, vault open, community plug-in enablement, plug-in reload, and readiness sequence without duplicating smoke runner code. - Added a manual AppImage installer that downloads Obsidian `1.12.7` for `arm64` or `x86_64`, stores it under `_testdata/obsidian`, and extracts it for FUSE-free execution. - Confirmed the smoke runner on Linux ARM64 with the extracted Obsidian `1.12.7` AppImage, `xvfb-run`, and the built Self-hosted LiveSync bundle. - Confirmed the runner can enable the Obsidian CLI through isolated `obsidian.json` state, open the temporary vault through `obsidian-cli`, enable community plug-ins through `app.plugins.setEnable(true)`, reload Self-hosted LiveSync, and verify readiness through `obsidian-cli eval`. @@ -137,6 +138,7 @@ Current verification: - `npm run build` passes with existing Svelte warnings. - `npm run test:e2e:obsidian:discover` finds `_testdata/obsidian/squashfs-root/obsidian` when the extracted AppImage is present. - `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS=1000 npm run test:e2e:obsidian:smoke` passes locally. +- `npm run test:e2e:obsidian:vault-reflection` creates a note through Obsidian's vault API, verifies the reflected file on disk, and reads it back through Obsidian. - `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present. Known limits: @@ -156,6 +158,10 @@ Known limits: This validates real boot-up, settings persistence, vault file access, database writes, and restart-sensitive state. +Current implementation status: + +- Added a pre-CouchDB workflow that creates a note through Obsidian's vault API, confirms the note is reflected as a real vault file, and reads the same note back through Obsidian. This covers the vault reflection part of the Phase 2 path before remote database setup is introduced. + ### Phase 3: Two-Vault Synchronisation - Launch two Obsidian instances with two temporary vaults. diff --git a/package.json b/package.json index 09f2be1..1185c30 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "test:e2e:obsidian:discover": "tsx test/e2e-obsidian/scripts/discover.ts", "test:e2e:obsidian:cli-help": "tsx test/e2e-obsidian/scripts/cli-help.ts", "test:e2e:obsidian:smoke": "tsx test/e2e-obsidian/scripts/smoke.ts", + "test:e2e:obsidian:vault-reflection": "tsx test/e2e-obsidian/scripts/vault-reflection.ts", "test:coverage": "vitest run --coverage", "test:docker-couchdb:up": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-start.sh", "test:docker-couchdb:init": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-init.sh", diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md index 25ffd96..1fbb2d4 100644 --- a/test/e2e-obsidian/README.md +++ b/test/e2e-obsidian/README.md @@ -17,6 +17,8 @@ The runner does not require Self-hosted LiveSync to expose an E2E-only bridge. R Obsidian 1.12 stores the global community plug-in switch outside `.obsidian/community-plugins.json`. The smoke runner enables it through `app.plugins.setEnable(true)` after the vault window is available. +Future workflows should use `startObsidianLiveSyncSession()` from `runner/session.ts` rather than repeating the launch and plug-in readiness sequence. + ## Local Setup Set `OBSIDIAN_BINARY` when Obsidian is not installed in a standard location. @@ -42,6 +44,7 @@ npm run test:e2e:obsidian:install-appimage npm run test:e2e:obsidian:discover npm run test:e2e:obsidian:cli-help -- vaults verbose npm run test:e2e:obsidian:smoke +npm run test:e2e:obsidian:vault-reflection ``` Useful environment variables: @@ -57,6 +60,7 @@ Useful environment variables: - `E2E_OBSIDIAN_READY_TIMEOUT_MS`: plug-in readiness timeout in milliseconds. - `E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS`: timeout for waiting until the vault-side Obsidian CLI exposes the plug-in catalogue. - `E2E_OBSIDIAN_CLI_TIMEOUT_MS`: timeout for each `obsidian-cli` invocation. +- `E2E_OBSIDIAN_FILE_TIMEOUT_MS`: timeout for waiting until a note created through Obsidian's vault API is reflected to disk. - `E2E_OBSIDIAN_STARTUP_GRACE_MS`: early process-exit detection window in milliseconds. - `E2E_OBSIDIAN_KEEP_VAULT=true`: keep the temporary vault for inspection. - `E2E_OBSIDIAN_USE_XVFB=false`: disable automatic `xvfb-run` on headless Linux. diff --git a/test/e2e-obsidian/runner/cli.ts b/test/e2e-obsidian/runner/cli.ts index f1766b8..cc46be8 100644 --- a/test/e2e-obsidian/runner/cli.ts +++ b/test/e2e-obsidian/runner/cli.ts @@ -7,6 +7,13 @@ export type ObsidianCliResult = { stderr: string; }; +function parseEvalJson(stdout: string): unknown { + const marker = "=> "; + const markerIndex = stdout.indexOf(marker); + const text = markerIndex >= 0 ? stdout.slice(markerIndex + marker.length) : stdout; + return JSON.parse(text.trim()); +} + export async function runObsidianCli( cliBinary: string, args: string[], @@ -60,3 +67,23 @@ export async function openVaultWithObsidianCli( ); } } + +export async function evalObsidianJson( + cliBinary: string, + code: string, + env: NodeJS.ProcessEnv = process.env +): Promise { + const result = await runObsidianCli(cliBinary, ["eval", `code=${code}`], env); + if (result.code !== 0 || result.stdout.includes("Error:")) { + throw new Error( + [ + `Failed to evaluate Obsidian JavaScript through CLI. code=${result.code}, signal=${result.signal}`, + result.stdout ? `stdout:\n${result.stdout}` : undefined, + result.stderr ? `stderr:\n${result.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } + return parseEvalJson(result.stdout) as T; +} diff --git a/test/e2e-obsidian/runner/readiness.ts b/test/e2e-obsidian/runner/readiness.ts index d56c2af..a6fa5a9 100644 --- a/test/e2e-obsidian/runner/readiness.ts +++ b/test/e2e-obsidian/runner/readiness.ts @@ -1,4 +1,4 @@ -import { runObsidianCli } from "./cli.ts"; +import { evalObsidianJson } from "./cli.ts"; export type PluginReadiness = { status: "ready"; @@ -7,13 +7,6 @@ export type PluginReadiness = { vaultName: string; }; -function parseEvalJson(stdout: string): unknown { - const marker = "=> "; - const markerIndex = stdout.indexOf(marker); - const text = markerIndex >= 0 ? stdout.slice(markerIndex + marker.length) : stdout; - return JSON.parse(text.trim()); -} - export async function waitForPluginReady( cliBinary: string, env: NodeJS.ProcessEnv, @@ -22,28 +15,24 @@ export async function waitForPluginReady( const deadline = Date.now() + timeoutMs; let lastOutput = ""; while (Date.now() < deadline) { - const result = await runObsidianCli( - cliBinary, - [ - "eval", + try { + const readiness = await evalObsidianJson( + cliBinary, [ - "code=(async()=>JSON.stringify({", + "(async()=>JSON.stringify({", "status:!!app.plugins.plugins['obsidian-livesync']?'ready':'pending',", "pluginId:'obsidian-livesync',", "pluginVersion:app.plugins.manifests['obsidian-livesync']?.version,", "vaultName:app.vault.getName()", "}))()", ].join(""), - ], - env - ); - lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n"); - try { - const readiness = parseEvalJson(result.stdout) as PluginReadiness; + env + ); if (readiness.status === "ready") { return readiness; } - } catch { + } catch (error) { + lastOutput = error instanceof Error ? error.message : String(error); // Keep polling until Obsidian exposes the vault-side CLI and plug-in state. } await new Promise((resolve) => setTimeout(resolve, 500)); diff --git a/test/e2e-obsidian/runner/session.ts b/test/e2e-obsidian/runner/session.ts new file mode 100644 index 0000000..ff7b931 --- /dev/null +++ b/test/e2e-obsidian/runner/session.ts @@ -0,0 +1,94 @@ +import { openVaultWithObsidianCli, runObsidianCli } from "./cli.ts"; +import { launchObsidian, type ObsidianProcess } from "./launch.ts"; +import { installBuiltPlugin, type PluginInstallResult } from "./pluginInstaller.ts"; +import { waitForPluginReady, type PluginReadiness } from "./readiness.ts"; +import type { TemporaryVault } from "./vault.ts"; + +export type ObsidianLiveSyncSession = { + app: ObsidianProcess; + cliEnv: NodeJS.ProcessEnv; + install: PluginInstallResult; + readiness: PluginReadiness; +}; + +export type StartObsidianLiveSyncSessionOptions = { + binary: string; + cliBinary: string; + vault: TemporaryVault; + startupGraceMs?: number; +}; + +async function waitForPluginCatalogue(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS ?? 15000); + let lastOutput = ""; + while (Date.now() < deadline) { + const result = await runObsidianCli(cliBinary, ["plugins", "filter=community"], env); + lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n"); + if (result.stdout.includes("obsidian-livesync")) { + return; + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Timed out waiting for Obsidian plug-in catalogue through CLI.\n${lastOutput}`); +} + +async function enableCommunityPlugins(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + const result = await runObsidianCli(cliBinary, ["eval", "code=(async()=>app.plugins.setEnable(true))()"], env); + if (result.code !== 0 || result.stdout.includes("Error:")) { + throw new Error( + [ + `Failed to enable Obsidian community plug-ins through CLI. code=${result.code}, signal=${result.signal}`, + result.stdout ? `stdout:\n${result.stdout}` : undefined, + result.stderr ? `stderr:\n${result.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } +} + +async function reloadLiveSyncPlugin(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + const reload = await runObsidianCli(cliBinary, ["plugin:reload", "id=obsidian-livesync"], env); + if (reload.code !== 0 || !reload.stdout.includes("Reloaded: obsidian-livesync")) { + throw new Error( + [ + `Failed to reload Self-hosted LiveSync through Obsidian CLI. code=${reload.code}, signal=${reload.signal}`, + reload.stdout ? `stdout:\n${reload.stdout}` : undefined, + reload.stderr ? `stderr:\n${reload.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } +} + +export async function startObsidianLiveSyncSession( + options: StartObsidianLiveSyncSessionOptions +): Promise { + const install = await installBuiltPlugin(options.vault.path); + const app = await launchObsidian({ + binary: options.binary, + vaultPath: options.vault.path, + homePath: options.vault.homePath, + xdgConfigPath: options.vault.xdgConfigPath, + userDataPath: options.vault.userDataPath, + startupGraceMs: options.startupGraceMs, + }); + const cliEnv = { + ...process.env, + HOME: options.vault.homePath, + XDG_CONFIG_HOME: options.vault.xdgConfigPath, + }; + + try { + await openVaultWithObsidianCli(options.cliBinary, options.vault.path, cliEnv); + await waitForPluginCatalogue(options.cliBinary, cliEnv); + await enableCommunityPlugins(options.cliBinary, cliEnv); + await reloadLiveSyncPlugin(options.cliBinary, cliEnv); + const readiness = await waitForPluginReady(options.cliBinary, cliEnv); + return { app, cliEnv, install, readiness }; + } catch (error) { + await app.stop(); + throw error; + } +} diff --git a/test/e2e-obsidian/scripts/smoke.ts b/test/e2e-obsidian/scripts/smoke.ts index ad9bf8e..c00644f 100644 --- a/test/e2e-obsidian/scripts/smoke.ts +++ b/test/e2e-obsidian/scripts/smoke.ts @@ -1,38 +1,6 @@ import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; -import { launchObsidian } from "../runner/launch.ts"; -import { installBuiltPlugin } from "../runner/pluginInstaller.ts"; -import { waitForPluginReady } from "../runner/readiness.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; import { createTemporaryVault } from "../runner/vault.ts"; -import { openVaultWithObsidianCli, runObsidianCli } from "../runner/cli.ts"; - -async function waitForPluginCatalogue(cliBinary: string, env: NodeJS.ProcessEnv): Promise { - const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS ?? 15000); - let lastOutput = ""; - while (Date.now() < deadline) { - const result = await runObsidianCli(cliBinary, ["plugins", "filter=community"], env); - lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n"); - if (result.stdout.includes("obsidian-livesync")) { - return; - } - await new Promise((resolve) => setTimeout(resolve, 500)); - } - throw new Error(`Timed out waiting for Obsidian plug-in catalogue through CLI.\n${lastOutput}`); -} - -async function enableCommunityPlugins(cliBinary: string, env: NodeJS.ProcessEnv): Promise { - const result = await runObsidianCli(cliBinary, ["eval", "code=(async()=>app.plugins.setEnable(true))()"], env); - if (result.code !== 0 || result.stdout.includes("Error:")) { - throw new Error( - [ - `Failed to enable Obsidian community plug-ins through CLI. code=${result.code}, signal=${result.signal}`, - result.stdout ? `stdout:\n${result.stdout}` : undefined, - result.stderr ? `stderr:\n${result.stderr}` : undefined, - ] - .filter(Boolean) - .join("\n") - ); - } -} async function main(): Promise { const binary = requireObsidianBinary(); @@ -41,50 +9,27 @@ async function main(): Promise { throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); } const vault = await createTemporaryVault(); - let app; + let session: ObsidianLiveSyncSession | undefined; try { - const install = await installBuiltPlugin(vault.path); console.log(`Using Obsidian executable: ${binary}`); console.log(`Temporary vault: ${vault.path}`); - console.log(`Installed plug-in artifacts: ${install.copied.join(", ")}`); - app = await launchObsidian({ + session = await startObsidianLiveSyncSession({ binary, - vaultPath: vault.path, - homePath: vault.homePath, - xdgConfigPath: vault.xdgConfigPath, - userDataPath: vault.userDataPath, + cliBinary: cli.binary, + vault, startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), }); - const cliEnv = { - ...process.env, - HOME: vault.homePath, - XDG_CONFIG_HOME: vault.xdgConfigPath, - }; - await openVaultWithObsidianCli(cli.binary, vault.path, cliEnv); - await waitForPluginCatalogue(cli.binary, cliEnv); - await enableCommunityPlugins(cli.binary, cliEnv); - const reload = await runObsidianCli(cli.binary, ["plugin:reload", "id=obsidian-livesync"], cliEnv); - if (reload.code !== 0 || !reload.stdout.includes("Reloaded: obsidian-livesync")) { - throw new Error( - [ - `Failed to reload Self-hosted LiveSync through Obsidian CLI. code=${reload.code}, signal=${reload.signal}`, - reload.stdout ? `stdout:\n${reload.stdout}` : undefined, - reload.stderr ? `stderr:\n${reload.stderr}` : undefined, - ] - .filter(Boolean) - .join("\n") - ); - } - const readiness = await waitForPluginReady(cli.binary, cliEnv); + console.log(`Installed plug-in artifacts: ${session.install.copied.join(", ")}`); + const { readiness } = session; console.log( `Obsidian plug-in ready: ${readiness.pluginId}@${readiness.pluginVersion} in ${readiness.vaultName}` ); await new Promise((resolve) => setTimeout(resolve, Number(process.env.E2E_OBSIDIAN_SMOKE_TIMEOUT_MS ?? 1000))); console.log("Obsidian stayed alive after the plug-in readiness check."); } finally { - if (app) { - await app.stop(); + if (session) { + await session.app.stop(); } await vault.dispose(); } diff --git a/test/e2e-obsidian/scripts/vault-reflection.ts b/test/e2e-obsidian/scripts/vault-reflection.ts new file mode 100644 index 0000000..6ba1473 --- /dev/null +++ b/test/e2e-obsidian/scripts/vault-reflection.ts @@ -0,0 +1,122 @@ +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { evalObsidianJson } from "../runner/cli.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; + +type CreatedNote = { + path: string; + read: string; + exists: boolean; +}; + +type ReadNote = { + exists: boolean; + read: string | null; +}; + +const notePath = "E2E/real-vault-reflection.md"; +const noteContent = [ + "# Real Obsidian E2E", + "", + "This note was created through Obsidian's own vault API.", + `Created at: ${new Date().toISOString()}`, + "", +].join("\n"); + +async function waitForFileContent(vaultPath: string, path: string, expectedContent: string): Promise { + const fullPath = join(vaultPath, path); + const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000); + let lastError: unknown; + while (Date.now() < deadline) { + try { + const content = await readFile(fullPath, "utf-8"); + if (content === expectedContent) { + return; + } + lastError = new Error(`Unexpected content in ${fullPath}`); + } catch (error) { + lastError = error; + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for reflected vault file: ${fullPath}\nLast error: ${String(lastError)}`); +} + +function assertEqual(actual: unknown, expected: unknown, message: string): void { + if (actual !== expected) { + throw new Error(`${message}\nExpected: ${String(expected)}\nActual: ${String(actual)}`); + } +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const vault = await createTemporaryVault(); + let session: ObsidianLiveSyncSession | undefined; + try { + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault: ${vault.path}`); + + session = await startObsidianLiveSyncSession({ + binary, + cliBinary: cli.binary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + + const created = await evalObsidianJson( + cli.binary, + [ + "(async()=>{", + `const path=${JSON.stringify(notePath)};`, + `const content=${JSON.stringify(noteContent)};`, + "if(!(await app.vault.adapter.exists('E2E'))) await app.vault.createFolder('E2E');", + "const existing=app.vault.getAbstractFileByPath(path);", + "if(existing) await app.vault.delete(existing);", + "const file=await app.vault.create(path,content);", + "const read=await app.vault.read(file);", + "return JSON.stringify({path:file.path,read,exists:await app.vault.adapter.exists(path)});", + "})()", + ].join(""), + session.cliEnv + ); + + assertEqual(created.path, notePath, "Obsidian created the note at an unexpected path."); + assertEqual(created.exists, true, "Obsidian adapter did not report the created note."); + assertEqual(created.read, noteContent, "Obsidian did not read back the created note content."); + + await waitForFileContent(vault.path, notePath, noteContent); + + const readBack = await evalObsidianJson( + cli.binary, + [ + "(async()=>{", + `const path=${JSON.stringify(notePath)};`, + "const file=app.vault.getAbstractFileByPath(path);", + "return JSON.stringify({exists:!!file,read:file?await app.vault.read(file):null});", + "})()", + ].join(""), + session.cliEnv + ); + assertEqual(readBack.exists, true, "Obsidian did not find the reflected note on read-back."); + assertEqual(readBack.read, noteContent, "Obsidian read-back content did not match the reflected file."); + + console.log(`Created and verified reflected note: ${notePath}`); + } finally { + if (session) { + await session.app.stop(); + } + await vault.dispose(); + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); From 36590ee76208f6493a17ebbcb21ad8dcf51ca9a1 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 26 Jun 2026 10:55:06 +0000 Subject: [PATCH 07/16] (test): improve e2e CouchDB --- docs/adr/2026_06_real_obsidian_e2e.md | 5 +- package.json | 1 + test/e2e-obsidian/README.md | 15 +- test/e2e-obsidian/runner/couchdb.ts | 174 +++++++++++++ test/e2e-obsidian/scripts/couchdb-upload.ts | 273 ++++++++++++++++++++ 5 files changed, 466 insertions(+), 2 deletions(-) create mode 100644 test/e2e-obsidian/runner/couchdb.ts create mode 100644 test/e2e-obsidian/scripts/couchdb-upload.ts diff --git a/docs/adr/2026_06_real_obsidian_e2e.md b/docs/adr/2026_06_real_obsidian_e2e.md index e4621fb..68da95e 100644 --- a/docs/adr/2026_06_real_obsidian_e2e.md +++ b/docs/adr/2026_06_real_obsidian_e2e.md @@ -125,8 +125,9 @@ Initial discovery on Linux ARM64 found that: Current implementation status: - Added `test/e2e-obsidian/runner` helpers for Obsidian discovery, CLI discovery, temporary vault creation, plug-in installation, process launch, CLI execution, and readiness polling. -- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, and `test:e2e:obsidian:install-appimage`. +- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, `test:e2e:obsidian:couchdb-upload`, and `test:e2e:obsidian:install-appimage`. - Added `startObsidianLiveSyncSession()` so future workflows can reuse the launch, vault open, community plug-in enablement, plug-in reload, and readiness sequence without duplicating smoke runner code. +- Added CouchDB runner utilities that reuse `.test.env`/process environment values, create unique temporary databases, query uploaded documents directly, and clean up the database unless `E2E_OBSIDIAN_KEEP_COUCHDB=true` is set. - Added a manual AppImage installer that downloads Obsidian `1.12.7` for `arm64` or `x86_64`, stores it under `_testdata/obsidian`, and extracts it for FUSE-free execution. - Confirmed the smoke runner on Linux ARM64 with the extracted Obsidian `1.12.7` AppImage, `xvfb-run`, and the built Self-hosted LiveSync bundle. - Confirmed the runner can enable the Obsidian CLI through isolated `obsidian.json` state, open the temporary vault through `obsidian-cli`, enable community plug-ins through `app.plugins.setEnable(true)`, reload Self-hosted LiveSync, and verify readiness through `obsidian-cli eval`. @@ -139,6 +140,7 @@ Current verification: - `npm run test:e2e:obsidian:discover` finds `_testdata/obsidian/squashfs-root/obsidian` when the extracted AppImage is present. - `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS=1000 npm run test:e2e:obsidian:smoke` passes locally. - `npm run test:e2e:obsidian:vault-reflection` creates a note through Obsidian's vault API, verifies the reflected file on disk, and reads it back through Obsidian. +- `npm run test:e2e:obsidian:couchdb-upload` configures a unique CouchDB database, creates a note through Obsidian, commits it into the local database, runs one-shot synchronisation, and verifies that CouchDB contains the metadata document and all referenced chunk documents. - `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present. Known limits: @@ -161,6 +163,7 @@ This validates real boot-up, settings persistence, vault file access, database w Current implementation status: - Added a pre-CouchDB workflow that creates a note through Obsidian's vault API, confirms the note is reflected as a real vault file, and reads the same note back through Obsidian. This covers the vault reflection part of the Phase 2 path before remote database setup is introduced. +- Added a first CouchDB-backed upload workflow, modelled after the CLI Deno tests: reuse the standard CouchDB environment variables, create a unique remote database, apply CouchDB settings through the plug-in's setting service, commit the note through the real Obsidian vault path, run one-shot synchronisation, and assert that remote metadata and chunks exist. ### Phase 3: Two-Vault Synchronisation diff --git a/package.json b/package.json index 1185c30..08c5ee6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "test:e2e:obsidian:cli-help": "tsx test/e2e-obsidian/scripts/cli-help.ts", "test:e2e:obsidian:smoke": "tsx test/e2e-obsidian/scripts/smoke.ts", "test:e2e:obsidian:vault-reflection": "tsx test/e2e-obsidian/scripts/vault-reflection.ts", + "test:e2e:obsidian:couchdb-upload": "tsx test/e2e-obsidian/scripts/couchdb-upload.ts", "test:coverage": "vitest run --coverage", "test:docker-couchdb:up": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-start.sh", "test:docker-couchdb:init": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-init.sh", diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md index 1fbb2d4..c201470 100644 --- a/test/e2e-obsidian/README.md +++ b/test/e2e-obsidian/README.md @@ -11,7 +11,8 @@ The current smoke runner verifies only the launch path: 5. enable Obsidian community plug-ins for the temporary app profile, 6. reload Self-hosted LiveSync through `obsidian-cli`, 7. verify through `obsidian-cli eval` that the plug-in is loaded, -8. terminate Obsidian and remove the temporary vault. +8. optionally drive a real vault or CouchDB workflow through Obsidian's own API, +9. terminate Obsidian and remove the temporary vault. The runner does not require Self-hosted LiveSync to expose an E2E-only bridge. Readiness is checked from outside the plug-in through Obsidian's own CLI. @@ -45,6 +46,15 @@ npm run test:e2e:obsidian:discover npm run test:e2e:obsidian:cli-help -- vaults verbose npm run test:e2e:obsidian:smoke npm run test:e2e:obsidian:vault-reflection +npm run test:e2e:obsidian:couchdb-upload +``` + +`test:e2e:obsidian:couchdb-upload` reuses the CouchDB variables from `.test.env` or the process environment. It expects a reachable CouchDB service, creates a unique database, configures Self-hosted LiveSync through `obsidian-cli eval`, creates a note in real Obsidian, commits the note into the local database, runs one-shot synchronisation, and verifies that the remote database contains both the metadata document and its chunk documents. + +Start the local CouchDB fixture first when one is not already running: + +```bash +npm run test:docker-couchdb:start ``` Useful environment variables: @@ -61,6 +71,9 @@ Useful environment variables: - `E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS`: timeout for waiting until the vault-side Obsidian CLI exposes the plug-in catalogue. - `E2E_OBSIDIAN_CLI_TIMEOUT_MS`: timeout for each `obsidian-cli` invocation. - `E2E_OBSIDIAN_FILE_TIMEOUT_MS`: timeout for waiting until a note created through Obsidian's vault API is reflected to disk. +- `E2E_OBSIDIAN_CORE_READY_TIMEOUT_MS`: timeout for waiting until Self-hosted LiveSync reports that its core lifecycle and local database are ready. +- `E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS`: timeout for waiting until CouchDB contains uploaded E2E documents. +- `E2E_OBSIDIAN_KEEP_COUCHDB=true`: keep the temporary CouchDB database for inspection. - `E2E_OBSIDIAN_STARTUP_GRACE_MS`: early process-exit detection window in milliseconds. - `E2E_OBSIDIAN_KEEP_VAULT=true`: keep the temporary vault for inspection. - `E2E_OBSIDIAN_USE_XVFB=false`: disable automatic `xvfb-run` on headless Linux. diff --git a/test/e2e-obsidian/runner/couchdb.ts b/test/e2e-obsidian/runner/couchdb.ts new file mode 100644 index 0000000..753d422 --- /dev/null +++ b/test/e2e-obsidian/runner/couchdb.ts @@ -0,0 +1,174 @@ +import { readFile } from "node:fs/promises"; +import { resolve } from "node:path"; + +export type CouchDbConfig = { + uri: string; + username: string; + password: string; + dbPrefix: string; +}; + +export type CouchDbDocument = { + _id: string; + _rev?: string; + type?: string; + path?: string; + children?: string[]; + [key: string]: unknown; +}; + +export type CouchDbAllDocsResponse = { + rows: Array<{ + id: string; + key: string; + value: { rev: string; deleted?: boolean }; + doc?: CouchDbDocument; + }>; +}; + +function parseEnvFile(content: string): Record { + const entries = content + .split(/\r?\n/u) + .map((line) => line.trim()) + .filter((line) => line && !line.startsWith("#")) + .map((line) => { + const equalsAt = line.indexOf("="); + if (equalsAt < 0) { + return undefined; + } + const key = line.slice(0, equalsAt).trim(); + const rawValue = line.slice(equalsAt + 1).trim(); + const value = rawValue.replace(/^['"]|['"]$/gu, ""); + return [key, value] as const; + }) + .filter((entry): entry is readonly [string, string] => entry !== undefined); + return Object.fromEntries(entries); +} + +function getEnvValue(values: Record, ...keys: string[]): string { + for (const key of keys) { + const value = values[key]?.trim(); + if (value) { + return value; + } + } + throw new Error(`Required CouchDB environment value is missing: ${keys.join(" or ")}`); +} + +function authHeader(config: Pick): string { + return `Basic ${Buffer.from(`${config.username}:${config.password}`).toString("base64")}`; +} + +function databaseUrl(config: Pick, dbName: string, suffix = ""): string { + return `${config.uri.replace(/\/+$/u, "")}/${encodeURIComponent(dbName)}${suffix}`; +} + +async function couchDbRequest( + config: Pick, + path: string, + init: RequestInit = {} +): Promise { + const response = await fetch(`${config.uri.replace(/\/+$/u, "")}${path}`, { + ...init, + headers: { + authorization: authHeader(config), + ...init.headers, + }, + }); + return response; +} + +export async function loadCouchDbConfig(envFile = ".test.env"): Promise { + let fileValues: Record = {}; + try { + fileValues = parseEnvFile(await readFile(resolve(envFile), "utf-8")); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") { + throw error; + } + } + + const values = { ...fileValues, ...process.env }; + return { + uri: getEnvValue(values, "COUCHDB_URI", "hostname").replace(/\/+$/u, ""), + username: getEnvValue(values, "COUCHDB_USER", "username"), + password: getEnvValue(values, "COUCHDB_PASSWORD", "password"), + dbPrefix: getEnvValue(values, "COUCHDB_DBNAME", "dbname"), + }; +} + +export function makeUniqueDatabaseName(prefix: string, label: string): string { + const safePrefix = prefix + .toLowerCase() + .replace(/[^a-z0-9_$()+/-]+/gu, "-") + .replace(/^-+/u, "") + .slice(0, 80); + const random = Math.random().toString(36).slice(2, 8); + return `${safePrefix || "livesync-e2e"}-${label}-${Date.now()}-${random}`; +} + +export async function assertCouchDbReachable(config: CouchDbConfig): Promise { + const response = await couchDbRequest(config, "/_up"); + if (!response.ok) { + throw new Error(`CouchDB is not reachable at ${config.uri}. HTTP ${response.status}: ${await response.text()}`); + } +} + +export async function createCouchDbDatabase(config: CouchDbConfig, dbName: string): Promise { + const response = await fetch(databaseUrl(config, dbName), { + method: "PUT", + headers: { authorization: authHeader(config) }, + }); + if (!response.ok && response.status !== 412) { + throw new Error( + `Failed to create CouchDB database ${dbName}. HTTP ${response.status}: ${await response.text()}` + ); + } +} + +export async function deleteCouchDbDatabase(config: CouchDbConfig, dbName: string): Promise { + const response = await fetch(databaseUrl(config, dbName), { + method: "DELETE", + headers: { authorization: authHeader(config) }, + }); + if (!response.ok && response.status !== 404) { + throw new Error( + `Failed to delete CouchDB database ${dbName}. HTTP ${response.status}: ${await response.text()}` + ); + } +} + +export async function fetchAllCouchDbDocs(config: CouchDbConfig, dbName: string): Promise { + const response = await fetch(databaseUrl(config, dbName, "/_all_docs?include_docs=true"), { + headers: { authorization: authHeader(config) }, + }); + if (!response.ok) { + throw new Error( + `Failed to read CouchDB documents from ${dbName}. HTTP ${response.status}: ${await response.text()}` + ); + } + return (await response.json()) as CouchDbAllDocsResponse; +} + +export async function waitForCouchDbDocs( + config: CouchDbConfig, + dbName: string, + predicate: (docs: CouchDbDocument[]) => boolean, + timeoutMs = Number(process.env.E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS ?? 15000) +): Promise { + const deadline = Date.now() + timeoutMs; + let lastDocs: CouchDbDocument[] = []; + while (Date.now() < deadline) { + const response = await fetchAllCouchDbDocs(config, dbName); + lastDocs = response.rows.flatMap((row) => (row.doc ? [row.doc] : [])); + if (predicate(lastDocs)) { + return lastDocs; + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error( + `Timed out waiting for CouchDB documents in ${dbName}. Last document IDs: ${lastDocs + .map((doc) => doc._id) + .join(", ")}` + ); +} diff --git a/test/e2e-obsidian/scripts/couchdb-upload.ts b/test/e2e-obsidian/scripts/couchdb-upload.ts new file mode 100644 index 0000000..ed094d2 --- /dev/null +++ b/test/e2e-obsidian/scripts/couchdb-upload.ts @@ -0,0 +1,273 @@ +import { evalObsidianJson } from "../runner/cli.ts"; +import { + assertCouchDbReachable, + createCouchDbDatabase, + deleteCouchDbDatabase, + loadCouchDbConfig, + makeUniqueDatabaseName, + waitForCouchDbDocs, +} from "../runner/couchdb.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; + +type ConfiguredSettings = { + isConfigured: boolean; + liveSync: boolean; + syncOnStart: boolean; + syncOnSave: boolean; + couchDB_URI: string; + couchDB_DBNAME: string; +}; + +type LocalDatabaseEntry = { + id: string; + path: string; + type: string; + children: string[]; +}; + +type CoreReadiness = { + databaseReady: boolean; + appReady: boolean; +}; + +process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; + +const notePath = "E2E/couchdb-upload.md"; +const noteContent = [ + "# CouchDB upload from real Obsidian", + "", + "This note is created through Obsidian and uploaded by Self-hosted LiveSync.", + "The content is intentionally long enough to require chunk metadata in the local database.", + "0123456789 abcdefghijklmnopqrstuvwxyz 0123456789 abcdefghijklmnopqrstuvwxyz", + "0123456789 abcdefghijklmnopqrstuvwxyz 0123456789 abcdefghijklmnopqrstuvwxyz", + `Created at: ${new Date().toISOString()}`, + "", +].join("\n"); + +function assertEqual(actual: unknown, expected: unknown, message: string): void { + if (actual !== expected) { + throw new Error(`${message}\nExpected: ${String(expected)}\nActual: ${String(actual)}`); + } +} + +async function configureCouchDb( + cliBinary: string, + env: NodeJS.ProcessEnv, + settings: { + uri: string; + username: string; + password: string; + dbName: string; + } +): Promise { + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const plugin=app.plugins.plugins['obsidian-livesync'];", + "const core=plugin.core;", + "const nextSettings={", + `couchDB_URI:${JSON.stringify(settings.uri)},`, + `couchDB_USER:${JSON.stringify(settings.username)},`, + `couchDB_PASSWORD:${JSON.stringify(settings.password)},`, + `couchDB_DBNAME:${JSON.stringify(settings.dbName)},`, + "remoteType:'',", + "liveSync:false,", + "syncOnStart:false,", + "syncOnSave:false,", + "usePluginSync:false,", + "usePluginSyncV2:true,", + "useEden:false,", + "customChunkSize:1,", + "sendChunksBulkMaxSize:1,", + "chunkSplitterVersion:'v3-rabin-karp',", + "readChunksOnline:false,", + "disableCheckingConfigMismatch:true,", + "isConfigured:true,", + "};", + "await core.services.setting.applyExternalSettings(nextSettings,true);", + "await core.services.control.applySettings();", + "const current=core.services.setting.currentSettings();", + "return JSON.stringify({", + "isConfigured:current.isConfigured,", + "liveSync:current.liveSync,", + "syncOnStart:current.syncOnStart,", + "syncOnSave:current.syncOnSave,", + "couchDB_URI:current.couchDB_URI,", + "couchDB_DBNAME:current.couchDB_DBNAME,", + "});", + "})()", + ].join(""), + env + ); +} + +async function waitForLiveSyncCoreReady( + cliBinary: string, + env: NodeJS.ProcessEnv, + timeoutMs = Number(process.env.E2E_OBSIDIAN_CORE_READY_TIMEOUT_MS ?? 20000) +): Promise { + const deadline = Date.now() + timeoutMs; + let lastReadiness: CoreReadiness | undefined; + while (Date.now() < deadline) { + lastReadiness = await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "return JSON.stringify({", + "databaseReady:core.services.database.isDatabaseReady(),", + "appReady:core.services.appLifecycle.isReady(),", + "});", + "})()", + ].join(""), + env + ); + if (lastReadiness.databaseReady && lastReadiness.appReady) { + return lastReadiness; + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Timed out waiting for Self-hosted LiveSync core readiness: ${JSON.stringify(lastReadiness)}`); +} + +async function prepareRemote(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const settings=core.services.setting.currentSettings();", + "const replicator=core.services.replicator.getActiveReplicator();", + "await replicator.tryCreateRemoteDatabase(settings);", + "await replicator.markRemoteResolved(settings);", + "const status=await replicator.getRemoteStatus(settings);", + "return JSON.stringify({status});", + "})()", + ].join(""), + env + ); +} + +async function createNoteAndWaitForLocalDb(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(notePath)};`, + `const content=${JSON.stringify(noteContent)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "if(!(await app.vault.adapter.exists('E2E'))) await app.vault.createFolder('E2E');", + "const existing=app.vault.getAbstractFileByPath(path);", + "if(existing) await app.vault.delete(existing);", + "await app.vault.create(path,content);", + "const sleep=(ms)=>new Promise((resolve)=>setTimeout(resolve,ms));", + "let entry=false;", + "for(let i=0;i<40;i++){", + "await core.services.fileProcessing.commitPendingFileEvents();", + "entry=await core.localDatabase.getDBEntry(path,undefined,false,true).catch(()=>false);", + "if(entry&&entry._id&&Array.isArray(entry.children)&&entry.children.length>0) break;", + "await sleep(250);", + "}", + "if(!entry||!entry._id) throw new Error('Timed out waiting for local database entry');", + "return JSON.stringify({id:entry._id,path:entry.path,type:entry.type,children:entry.children||[]});", + "})()", + ].join(""), + env + ); +} + +async function pushLocalChanges(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "await core.services.fileProcessing.commitPendingFileEvents();", + "const result=await core.services.replication.replicate(true);", + "return JSON.stringify({result:!!result});", + "})()", + ].join(""), + env + ); +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const couchDb = await loadCouchDbConfig(); + const dbName = makeUniqueDatabaseName(couchDb.dbPrefix, "obsidian-upload"); + const vault = await createTemporaryVault(); + let session: ObsidianLiveSyncSession | undefined; + + try { + await assertCouchDbReachable(couchDb); + await createCouchDbDatabase(couchDb, dbName); + + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault: ${vault.path}`); + console.log(`Temporary CouchDB database: ${dbName}`); + + session = await startObsidianLiveSyncSession({ + binary, + cliBinary: cli.binary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(cli.binary, session.cliEnv); + + const configured = await configureCouchDb(cli.binary, session.cliEnv, { + uri: couchDb.uri, + username: couchDb.username, + password: couchDb.password, + dbName, + }); + await waitForLiveSyncCoreReady(cli.binary, session.cliEnv); + assertEqual(configured.isConfigured, true, "Self-hosted LiveSync was not marked as configured."); + assertEqual(configured.couchDB_URI, couchDb.uri, "Configured CouchDB URI did not match."); + assertEqual(configured.couchDB_DBNAME, dbName, "Configured CouchDB database name did not match."); + assertEqual(configured.liveSync, false, "LiveSync should remain disabled during this one-shot workflow."); + assertEqual(configured.syncOnStart, false, "Sync on start should remain disabled during this workflow."); + assertEqual(configured.syncOnSave, false, "Sync on save should remain disabled during this workflow."); + + await prepareRemote(cli.binary, session.cliEnv); + const localEntry = await createNoteAndWaitForLocalDb(cli.binary, session.cliEnv); + await pushLocalChanges(cli.binary, session.cliEnv); + + const remoteDocs = await waitForCouchDbDocs(couchDb, dbName, (docs) => { + const ids = new Set(docs.map((doc) => doc._id)); + return ids.has(localEntry.id) && localEntry.children.every((childId) => ids.has(childId)); + }); + const remoteMetadata = remoteDocs.find((doc) => doc._id === localEntry.id); + assertEqual( + remoteMetadata?.path, + localEntry.path, + "Remote metadata path did not match the local database entry." + ); + + console.log( + `Uploaded metadata ${localEntry.id} and ${localEntry.children.length} chunk(s) to CouchDB database ${dbName}` + ); + } finally { + if (session) { + await session.app.stop(); + } + await vault.dispose(); + if (process.env.E2E_OBSIDIAN_KEEP_COUCHDB !== "true") { + await deleteCouchDbDatabase(couchDb, dbName).catch((error: unknown) => { + console.warn(error instanceof Error ? error.message : error); + }); + } + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); From 150f0700b0ec62eb4fb883f748adbd14fc45af0e Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 26 Jun 2026 11:39:02 +0000 Subject: [PATCH 08/16] (test): hidden file sync roundtrip and misc --- docs/adr/2026_06_real_obsidian_e2e.md | 6 +- package.json | 3 + test/e2e-obsidian/README.md | 10 + test/e2e-obsidian/runner/cli.ts | 17 +- test/e2e-obsidian/runner/liveSyncWorkflow.ts | 182 ++++++ test/e2e-obsidian/runner/vault.ts | 13 +- test/e2e-obsidian/scripts/couchdb-upload.ts | 149 +---- .../scripts/hidden-file-snippet-sync.ts | 541 ++++++++++++++++++ .../scripts/setting-markdown-export.ts | 106 ++++ test/e2e-obsidian/scripts/startup-scan.ts | 116 ++++ 10 files changed, 995 insertions(+), 148 deletions(-) create mode 100644 test/e2e-obsidian/runner/liveSyncWorkflow.ts create mode 100644 test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts create mode 100644 test/e2e-obsidian/scripts/setting-markdown-export.ts create mode 100644 test/e2e-obsidian/scripts/startup-scan.ts diff --git a/docs/adr/2026_06_real_obsidian_e2e.md b/docs/adr/2026_06_real_obsidian_e2e.md index 68da95e..a0787e4 100644 --- a/docs/adr/2026_06_real_obsidian_e2e.md +++ b/docs/adr/2026_06_real_obsidian_e2e.md @@ -125,7 +125,7 @@ Initial discovery on Linux ARM64 found that: Current implementation status: - Added `test/e2e-obsidian/runner` helpers for Obsidian discovery, CLI discovery, temporary vault creation, plug-in installation, process launch, CLI execution, and readiness polling. -- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, `test:e2e:obsidian:couchdb-upload`, and `test:e2e:obsidian:install-appimage`. +- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, `test:e2e:obsidian:couchdb-upload`, `test:e2e:obsidian:startup-scan`, `test:e2e:obsidian:hidden-file-snippet-sync`, `test:e2e:obsidian:setting-markdown-export`, and `test:e2e:obsidian:install-appimage`. - Added `startObsidianLiveSyncSession()` so future workflows can reuse the launch, vault open, community plug-in enablement, plug-in reload, and readiness sequence without duplicating smoke runner code. - Added CouchDB runner utilities that reuse `.test.env`/process environment values, create unique temporary databases, query uploaded documents directly, and clean up the database unless `E2E_OBSIDIAN_KEEP_COUCHDB=true` is set. - Added a manual AppImage installer that downloads Obsidian `1.12.7` for `arm64` or `x86_64`, stores it under `_testdata/obsidian`, and extracts it for FUSE-free execution. @@ -141,6 +141,9 @@ Current verification: - `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS=1000 npm run test:e2e:obsidian:smoke` passes locally. - `npm run test:e2e:obsidian:vault-reflection` creates a note through Obsidian's vault API, verifies the reflected file on disk, and reads it back through Obsidian. - `npm run test:e2e:obsidian:couchdb-upload` configures a unique CouchDB database, creates a note through Obsidian, commits it into the local database, runs one-shot synchronisation, and verifies that CouchDB contains the metadata document and all referenced chunk documents. +- `npm run test:e2e:obsidian:startup-scan` verifies that a file written while Obsidian is stopped is picked up during the next real Obsidian boot and uploaded to CouchDB after one-shot synchronisation. +- `npm run test:e2e:obsidian:hidden-file-snippet-sync` verifies hidden file synchronisation as a two-vault round-trip: creation, deletion, automatic JSON conflict merging with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target-pattern differences. +- `npm run test:e2e:obsidian:setting-markdown-export` verifies that setting Markdown export creates a vault file and omits credentials when credential export is disabled. - `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present. Known limits: @@ -164,6 +167,7 @@ Current implementation status: - Added a pre-CouchDB workflow that creates a note through Obsidian's vault API, confirms the note is reflected as a real vault file, and reads the same note back through Obsidian. This covers the vault reflection part of the Phase 2 path before remote database setup is introduced. - Added a first CouchDB-backed upload workflow, modelled after the CLI Deno tests: reuse the standard CouchDB environment variables, create a unique remote database, apply CouchDB settings through the plug-in's setting service, commit the note through the real Obsidian vault path, run one-shot synchronisation, and assert that remote metadata and chunks exist. +- Added Obsidian-specific workflows for boot-time vault scanning, hidden `.obsidian/snippets` file round-tripping, hidden JSON conflict resolution, per-device hidden target-pattern differences, and setting Markdown export. These scenarios assert against CouchDB documents, vault files, or real Obsidian UI outcomes instead of internal service state. ### Phase 3: Two-Vault Synchronisation diff --git a/package.json b/package.json index 08c5ee6..e7c6651 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,9 @@ "test:e2e:obsidian:smoke": "tsx test/e2e-obsidian/scripts/smoke.ts", "test:e2e:obsidian:vault-reflection": "tsx test/e2e-obsidian/scripts/vault-reflection.ts", "test:e2e:obsidian:couchdb-upload": "tsx test/e2e-obsidian/scripts/couchdb-upload.ts", + "test:e2e:obsidian:startup-scan": "tsx test/e2e-obsidian/scripts/startup-scan.ts", + "test:e2e:obsidian:hidden-file-snippet-sync": "tsx test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts", + "test:e2e:obsidian:setting-markdown-export": "tsx test/e2e-obsidian/scripts/setting-markdown-export.ts", "test:coverage": "vitest run --coverage", "test:docker-couchdb:up": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-start.sh", "test:docker-couchdb:init": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-init.sh", diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md index c201470..6593d98 100644 --- a/test/e2e-obsidian/README.md +++ b/test/e2e-obsidian/README.md @@ -47,10 +47,19 @@ npm run test:e2e:obsidian:cli-help -- vaults verbose npm run test:e2e:obsidian:smoke npm run test:e2e:obsidian:vault-reflection npm run test:e2e:obsidian:couchdb-upload +npm run test:e2e:obsidian:startup-scan +npm run test:e2e:obsidian:hidden-file-snippet-sync +npm run test:e2e:obsidian:setting-markdown-export ``` `test:e2e:obsidian:couchdb-upload` reuses the CouchDB variables from `.test.env` or the process environment. It expects a reachable CouchDB service, creates a unique database, configures Self-hosted LiveSync through `obsidian-cli eval`, creates a note in real Obsidian, commits the note into the local database, runs one-shot synchronisation, and verifies that the remote database contains both the metadata document and its chunk documents. +`test:e2e:obsidian:startup-scan` configures a temporary CouchDB database, stops Obsidian, writes a note directly into the vault, restarts Obsidian, and verifies from CouchDB that the boot-time scan picked up the offline file. + +`test:e2e:obsidian:hidden-file-snippet-sync` runs a two-vault hidden file round-trip. It verifies creation and deletion of a real `.obsidian/snippets/*.css` file, automatic JSON conflict merging for a hidden file with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target patterns where one vault ignores a hidden file that the other vault synchronises. + +`test:e2e:obsidian:setting-markdown-export` enables setting Markdown export, waits for the generated Markdown file in the vault, and verifies that credentials are omitted when `writeCredentialsForSettingSync=false`. + Start the local CouchDB fixture first when one is not already running: ```bash @@ -72,6 +81,7 @@ Useful environment variables: - `E2E_OBSIDIAN_CLI_TIMEOUT_MS`: timeout for each `obsidian-cli` invocation. - `E2E_OBSIDIAN_FILE_TIMEOUT_MS`: timeout for waiting until a note created through Obsidian's vault API is reflected to disk. - `E2E_OBSIDIAN_CORE_READY_TIMEOUT_MS`: timeout for waiting until Self-hosted LiveSync reports that its core lifecycle and local database are ready. +- `E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS`: timeout for waiting until a file appears in Self-hosted LiveSync's local database. - `E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS`: timeout for waiting until CouchDB contains uploaded E2E documents. - `E2E_OBSIDIAN_KEEP_COUCHDB=true`: keep the temporary CouchDB database for inspection. - `E2E_OBSIDIAN_STARTUP_GRACE_MS`: early process-exit detection window in milliseconds. diff --git a/test/e2e-obsidian/runner/cli.ts b/test/e2e-obsidian/runner/cli.ts index cc46be8..e38a874 100644 --- a/test/e2e-obsidian/runner/cli.ts +++ b/test/e2e-obsidian/runner/cli.ts @@ -74,7 +74,7 @@ export async function evalObsidianJson( env: NodeJS.ProcessEnv = process.env ): Promise { const result = await runObsidianCli(cliBinary, ["eval", `code=${code}`], env); - if (result.code !== 0 || result.stdout.includes("Error:")) { + if (result.code !== 0) { throw new Error( [ `Failed to evaluate Obsidian JavaScript through CLI. code=${result.code}, signal=${result.signal}`, @@ -85,5 +85,18 @@ export async function evalObsidianJson( .join("\n") ); } - return parseEvalJson(result.stdout) as T; + try { + return parseEvalJson(result.stdout) as T; + } catch (error) { + throw new Error( + [ + `Failed to parse Obsidian CLI eval JSON. code=${result.code}, signal=${result.signal}`, + error instanceof Error ? `parse error: ${error.message}` : undefined, + result.stdout ? `stdout:\n${result.stdout}` : undefined, + result.stderr ? `stderr:\n${result.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); + } } diff --git a/test/e2e-obsidian/runner/liveSyncWorkflow.ts b/test/e2e-obsidian/runner/liveSyncWorkflow.ts new file mode 100644 index 0000000..b47c1cb --- /dev/null +++ b/test/e2e-obsidian/runner/liveSyncWorkflow.ts @@ -0,0 +1,182 @@ +import { evalObsidianJson } from "./cli.ts"; +import type { CouchDbConfig } from "./couchdb.ts"; + +export type ConfiguredSettings = { + isConfigured: boolean; + liveSync: boolean; + syncOnStart: boolean; + syncOnSave: boolean; + couchDB_URI: string; + couchDB_DBNAME: string; +}; + +export type CoreReadiness = { + databaseReady: boolean; + appReady: boolean; +}; + +export type LocalDatabaseEntry = { + id: string; + rev: string; + path: string; + type: string; + children: string[]; +}; + +export function assertEqual(actual: unknown, expected: unknown, message: string): void { + if (actual !== expected) { + throw new Error(`${message}\nExpected: ${String(expected)}\nActual: ${String(actual)}`); + } +} + +export async function configureCouchDb( + cliBinary: string, + env: NodeJS.ProcessEnv, + settings: Pick & { dbName: string }, + overrides: Record = {} +): Promise { + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const plugin=app.plugins.plugins['obsidian-livesync'];", + "const core=plugin.core;", + "const nextSettings={", + `couchDB_URI:${JSON.stringify(settings.uri)},`, + `couchDB_USER:${JSON.stringify(settings.username)},`, + `couchDB_PASSWORD:${JSON.stringify(settings.password)},`, + `couchDB_DBNAME:${JSON.stringify(settings.dbName)},`, + "remoteType:'',", + "liveSync:false,", + "syncOnStart:false,", + "syncOnSave:false,", + "usePluginSync:false,", + "usePluginSyncV2:true,", + "useEden:false,", + "customChunkSize:1,", + "sendChunksBulkMaxSize:1,", + "chunkSplitterVersion:'v3-rabin-karp',", + "readChunksOnline:false,", + "disableCheckingConfigMismatch:true,", + "isConfigured:true,", + ...Object.entries(overrides).map(([key, value]) => `${JSON.stringify(key)}:${JSON.stringify(value)},`), + "};", + "await core.services.setting.applyExternalSettings(nextSettings,true);", + "await core.services.control.applySettings();", + "const current=core.services.setting.currentSettings();", + "return JSON.stringify({", + "isConfigured:current.isConfigured,", + "liveSync:current.liveSync,", + "syncOnStart:current.syncOnStart,", + "syncOnSave:current.syncOnSave,", + "couchDB_URI:current.couchDB_URI,", + "couchDB_DBNAME:current.couchDB_DBNAME,", + "});", + "})()", + ].join(""), + env + ); +} + +export async function waitForLiveSyncCoreReady( + cliBinary: string, + env: NodeJS.ProcessEnv, + timeoutMs = Number(process.env.E2E_OBSIDIAN_CORE_READY_TIMEOUT_MS ?? 20000) +): Promise { + const deadline = Date.now() + timeoutMs; + let lastReadiness: CoreReadiness | undefined; + while (Date.now() < deadline) { + lastReadiness = await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "return JSON.stringify({", + "databaseReady:core.services.database.isDatabaseReady(),", + "appReady:core.services.appLifecycle.isReady(),", + "});", + "})()", + ].join(""), + env + ); + if (lastReadiness.databaseReady && lastReadiness.appReady) { + return lastReadiness; + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Timed out waiting for Self-hosted LiveSync core readiness: ${JSON.stringify(lastReadiness)}`); +} + +export async function prepareRemote(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const settings=core.services.setting.currentSettings();", + "const replicator=core.services.replicator.getActiveReplicator();", + "await replicator.tryCreateRemoteDatabase(settings);", + "await replicator.markRemoteResolved(settings);", + "const status=await replicator.getRemoteStatus(settings);", + "return JSON.stringify({status});", + "})()", + ].join(""), + env + ); +} + +export async function pushLocalChanges(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "await core.services.fileProcessing.commitPendingFileEvents();", + "const result=await core.services.replication.replicate(true);", + "return JSON.stringify({result:!!result});", + "})()", + ].join(""), + env + ); +} + +export async function waitForLocalDatabaseEntry( + cliBinary: string, + env: NodeJS.ProcessEnv, + path: string, + options: { hidden?: boolean; timeoutMs?: number } = {} +): Promise { + const timeoutMs = options.timeoutMs ?? Number(process.env.E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS ?? 15000); + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + `const hidden=${JSON.stringify(options.hidden === true)};`, + `const timeoutMs=${JSON.stringify(timeoutMs)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const deadline=Date.now()+timeoutMs;", + "const sleep=(ms)=>new Promise((resolve)=>setTimeout(resolve,ms));", + "let entry=false;", + "while(Date.now()false);", + "if(!entry||!entry._id){", + "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", + "entry=rows.map((row)=>row.doc).find((doc)=>doc&&(", + "doc._id===dbPath||doc._id===path||doc.path===dbPath||doc.path===path||", + "(typeof doc.path==='string'&&doc.path.endsWith(path))||", + "(typeof doc._id==='string'&&doc._id.endsWith(path))", + "))||false;", + "}", + "if(entry&&entry._id&&Array.isArray(entry.children)&&entry.children.length>0) break;", + "await sleep(250);", + "}", + "if(!entry||!entry._id) throw new Error(`Timed out waiting for local database entry: ${path}`);", + "return JSON.stringify({id:entry._id,rev:entry._rev,path:entry.path,type:entry.type,children:entry.children||[]});", + "})()", + ].join(""), + env + ); +} diff --git a/test/e2e-obsidian/runner/vault.ts b/test/e2e-obsidian/runner/vault.ts index 4f151b4..e24f2d6 100644 --- a/test/e2e-obsidian/runner/vault.ts +++ b/test/e2e-obsidian/runner/vault.ts @@ -13,11 +13,12 @@ export type TemporaryVault = { export async function createTemporaryVault(prefix = "obsidian-livesync-e2e-"): Promise { const vaultPath = await mkdtemp(join(tmpdir(), prefix)); + const statePath = await mkdtemp(join(tmpdir(), `${prefix}state-`)); const name = vaultPath.split(/[\\/]/).pop() ?? "obsidian-livesync-e2e"; await mkdir(join(vaultPath, ".obsidian"), { recursive: true }); - const homePath = join(vaultPath, ".obsidian", "e2e-home"); - const xdgConfigPath = join(vaultPath, ".obsidian", "e2e-xdg-config"); - const userDataPath = join(vaultPath, ".obsidian", "e2e-user-data"); + const homePath = join(statePath, "home"); + const xdgConfigPath = join(statePath, "xdg-config"); + const userDataPath = join(statePath, "user-data"); await mkdir(homePath, { recursive: true }); await mkdir(xdgConfigPath, { recursive: true }); await mkdir(userDataPath, { recursive: true }); @@ -36,9 +37,13 @@ export async function createTemporaryVault(prefix = "obsidian-livesync-e2e-"): P dispose: async () => { if (process.env.E2E_OBSIDIAN_KEEP_VAULT === "true") { console.log(`Keeping temporary vault: ${vaultPath}`); + console.log(`Keeping temporary Obsidian state: ${statePath}`); return; } - await rm(vaultPath, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }); + await Promise.all([ + rm(vaultPath, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }), + rm(statePath, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }), + ]); }, }; } diff --git a/test/e2e-obsidian/scripts/couchdb-upload.ts b/test/e2e-obsidian/scripts/couchdb-upload.ts index ed094d2..70a5368 100644 --- a/test/e2e-obsidian/scripts/couchdb-upload.ts +++ b/test/e2e-obsidian/scripts/couchdb-upload.ts @@ -8,30 +8,17 @@ import { waitForCouchDbDocs, } from "../runner/couchdb.ts"; import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { + assertEqual, + configureCouchDb, + prepareRemote, + pushLocalChanges, + waitForLiveSyncCoreReady, + type LocalDatabaseEntry, +} from "../runner/liveSyncWorkflow.ts"; import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; import { createTemporaryVault } from "../runner/vault.ts"; -type ConfiguredSettings = { - isConfigured: boolean; - liveSync: boolean; - syncOnStart: boolean; - syncOnSave: boolean; - couchDB_URI: string; - couchDB_DBNAME: string; -}; - -type LocalDatabaseEntry = { - id: string; - path: string; - type: string; - children: string[]; -}; - -type CoreReadiness = { - databaseReady: boolean; - appReady: boolean; -}; - process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; const notePath = "E2E/couchdb-upload.md"; @@ -46,111 +33,6 @@ const noteContent = [ "", ].join("\n"); -function assertEqual(actual: unknown, expected: unknown, message: string): void { - if (actual !== expected) { - throw new Error(`${message}\nExpected: ${String(expected)}\nActual: ${String(actual)}`); - } -} - -async function configureCouchDb( - cliBinary: string, - env: NodeJS.ProcessEnv, - settings: { - uri: string; - username: string; - password: string; - dbName: string; - } -): Promise { - return await evalObsidianJson( - cliBinary, - [ - "(async()=>{", - "const plugin=app.plugins.plugins['obsidian-livesync'];", - "const core=plugin.core;", - "const nextSettings={", - `couchDB_URI:${JSON.stringify(settings.uri)},`, - `couchDB_USER:${JSON.stringify(settings.username)},`, - `couchDB_PASSWORD:${JSON.stringify(settings.password)},`, - `couchDB_DBNAME:${JSON.stringify(settings.dbName)},`, - "remoteType:'',", - "liveSync:false,", - "syncOnStart:false,", - "syncOnSave:false,", - "usePluginSync:false,", - "usePluginSyncV2:true,", - "useEden:false,", - "customChunkSize:1,", - "sendChunksBulkMaxSize:1,", - "chunkSplitterVersion:'v3-rabin-karp',", - "readChunksOnline:false,", - "disableCheckingConfigMismatch:true,", - "isConfigured:true,", - "};", - "await core.services.setting.applyExternalSettings(nextSettings,true);", - "await core.services.control.applySettings();", - "const current=core.services.setting.currentSettings();", - "return JSON.stringify({", - "isConfigured:current.isConfigured,", - "liveSync:current.liveSync,", - "syncOnStart:current.syncOnStart,", - "syncOnSave:current.syncOnSave,", - "couchDB_URI:current.couchDB_URI,", - "couchDB_DBNAME:current.couchDB_DBNAME,", - "});", - "})()", - ].join(""), - env - ); -} - -async function waitForLiveSyncCoreReady( - cliBinary: string, - env: NodeJS.ProcessEnv, - timeoutMs = Number(process.env.E2E_OBSIDIAN_CORE_READY_TIMEOUT_MS ?? 20000) -): Promise { - const deadline = Date.now() + timeoutMs; - let lastReadiness: CoreReadiness | undefined; - while (Date.now() < deadline) { - lastReadiness = await evalObsidianJson( - cliBinary, - [ - "(async()=>{", - "const core=app.plugins.plugins['obsidian-livesync'].core;", - "return JSON.stringify({", - "databaseReady:core.services.database.isDatabaseReady(),", - "appReady:core.services.appLifecycle.isReady(),", - "});", - "})()", - ].join(""), - env - ); - if (lastReadiness.databaseReady && lastReadiness.appReady) { - return lastReadiness; - } - await new Promise((resolve) => setTimeout(resolve, 500)); - } - throw new Error(`Timed out waiting for Self-hosted LiveSync core readiness: ${JSON.stringify(lastReadiness)}`); -} - -async function prepareRemote(cliBinary: string, env: NodeJS.ProcessEnv): Promise { - await evalObsidianJson( - cliBinary, - [ - "(async()=>{", - "const core=app.plugins.plugins['obsidian-livesync'].core;", - "const settings=core.services.setting.currentSettings();", - "const replicator=core.services.replicator.getActiveReplicator();", - "await replicator.tryCreateRemoteDatabase(settings);", - "await replicator.markRemoteResolved(settings);", - "const status=await replicator.getRemoteStatus(settings);", - "return JSON.stringify({status});", - "})()", - ].join(""), - env - ); -} - async function createNoteAndWaitForLocalDb(cliBinary: string, env: NodeJS.ProcessEnv): Promise { return await evalObsidianJson( cliBinary, @@ -179,21 +61,6 @@ async function createNoteAndWaitForLocalDb(cliBinary: string, env: NodeJS.Proces ); } -async function pushLocalChanges(cliBinary: string, env: NodeJS.ProcessEnv): Promise { - await evalObsidianJson( - cliBinary, - [ - "(async()=>{", - "const core=app.plugins.plugins['obsidian-livesync'].core;", - "await core.services.fileProcessing.commitPendingFileEvents();", - "const result=await core.services.replication.replicate(true);", - "return JSON.stringify({result:!!result});", - "})()", - ].join(""), - env - ); -} - async function main(): Promise { const binary = requireObsidianBinary(); const cli = discoverObsidianCli(); diff --git a/test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts b/test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts new file mode 100644 index 0000000..01be4fb --- /dev/null +++ b/test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts @@ -0,0 +1,541 @@ +import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { evalObsidianJson } from "../runner/cli.ts"; +import { + assertCouchDbReachable, + createCouchDbDatabase, + deleteCouchDbDatabase, + loadCouchDbConfig, + makeUniqueDatabaseName, + waitForCouchDbDocs, + type CouchDbConfig, +} from "../runner/couchdb.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { + assertEqual, + configureCouchDb, + prepareRemote, + pushLocalChanges, + waitForLiveSyncCoreReady, + waitForLocalDatabaseEntry, + type LocalDatabaseEntry, +} from "../runner/liveSyncWorkflow.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault, type TemporaryVault } from "../runner/vault.ts"; + +process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; +process.env.E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS ??= "20000"; + +const snippetPath = ".obsidian/snippets/livesync-e2e.css"; +const snippetContent = [ + "body {", + " --livesync-e2e-snippet-colour: #245a70;", + "}", + "", + ".livesync-e2e-snippet {", + " color: var(--livesync-e2e-snippet-colour);", + "}", + "", +].join("\n"); + +const mergeJsonPath = ".obsidian/livesync-e2e-merge.json"; +const manualMergeJsonPath = ".obsidian/livesync-e2e-manual-merge.json"; +const targetPath = ".obsidian/livesync-targeted/only-a.json"; + +type RunnerContext = { + binary: string; + cliBinary: string; + couchDb: CouchDbConfig; + dbName: string; +}; + +async function writeVaultFile(vaultPath: string, path: string, content: string): Promise { + const fullPath = join(vaultPath, path); + await mkdir(dirname(fullPath), { recursive: true }); + await writeFile(fullPath, content, "utf-8"); +} + +async function removeVaultFile(vaultPath: string, path: string): Promise { + await rm(join(vaultPath, path), { force: true }); +} + +async function readVaultFile(vaultPath: string, path: string): Promise { + return await readFile(join(vaultPath, path), "utf-8"); +} + +async function pathExists(vaultPath: string, path: string): Promise { + try { + await readFile(join(vaultPath, path)); + return true; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return false; + } + throw error; + } +} + +async function waitForPathContent( + vaultPath: string, + path: string, + predicate: (content: string) => boolean, + timeoutMs = Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000) +): Promise { + const deadline = Date.now() + timeoutMs; + let lastContent = ""; + while (Date.now() < deadline) { + if (await pathExists(vaultPath, path)) { + lastContent = await readVaultFile(vaultPath, path); + if (predicate(lastContent)) { + return lastContent; + } + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for ${path}. Last content:\n${lastContent}`); +} + +async function waitForPathDeleted( + vaultPath: string, + path: string, + timeoutMs = Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000) +): Promise { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + if (!(await pathExists(vaultPath, path))) { + return; + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for deleted file: ${join(vaultPath, path)}`); +} + +function hasJsonValues(content: string, values: Record): boolean { + try { + const parsed = JSON.parse(content) as Record; + return Object.entries(values).every(([key, value]) => parsed[key] === value); + } catch { + return false; + } +} + +async function scanHiddenStorage(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('HiddenFileSync');", + "await addOn.scanAllStorageChanges(true);", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function scanHiddenDatabase(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('HiddenFileSync');", + "await addOn.scanAllDatabaseChanges(true);", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function resolveHiddenConflicts(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('HiddenFileSync');", + "await addOn.resolveConflictOnInternalFiles();", + "await addOn.scanAllDatabaseChanges(true);", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function autoMergeHiddenJsonConflict(cliBinary: string, env: NodeJS.ProcessEnv, path: string): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + "const prefixedPath=`i:${path}`;", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('HiddenFileSync');", + "let doc=false;", + "for await (const entry of core.localDatabase.findEntries('i:','i;',{conflicts:true})){", + " if(entry.path===prefixedPath){ doc=entry; break; }", + "}", + "if(!doc) throw new Error(`Could not find hidden conflict candidate: ${path}`);", + "if(!doc._conflicts?.length) throw new Error(`Hidden file has no conflicts: ${path}`);", + "const conflicts=doc._conflicts.sort((a,b)=>Number(a.split('-')[0])-Number(b.split('-')[0]));", + "const conflictedRev=conflicts[0];", + "const conflictedRevNo=Number(conflictedRev.split('-')[0]);", + "const revFrom=await core.localDatabase.getRaw(doc._id,{revs_info:true});", + "const commonBase=(revFrom._revs_info||[])", + " .filter((rev)=>rev.status==='available'&&Number(rev.rev.split('-')[0])rev.rev)[0]||'';", + "const result=await core.localDatabase.managers.conflictManager.mergeObject(", + " doc.path, commonBase, doc._rev, conflictedRev", + ");", + "if(!result){", + " throw new Error(`Hidden JSON conflict was not auto-mergeable: ${path}; base=${commonBase}; current=${doc._rev}; conflict=${conflictedRev}`);", + "}", + "await addOn.ensureDir(path);", + "const stat=await addOn.writeFile(path,result);", + "if(!stat) throw new Error(`Could not write merged hidden file: ${path}`);", + "await addOn.storeInternalFileToDatabase({path,mtime:stat.mtime,ctime:stat.ctime,size:stat.size},true);", + "await core.localDatabase.removeRevision(doc._id,conflictedRev);", + "await addOn.extractInternalFileFromDatabase(path);", + "await addOn.scanAllDatabaseChanges(true);", + "return JSON.stringify({ok:true,merged:JSON.parse(result)});", + "})()", + ].join(""), + env + ); +} + +async function openHiddenJsonResolveModal(cliBinary: string, env: NodeJS.ProcessEnv, path: string): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + "const prefixedPath=`i:${path}`;", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('HiddenFileSync');", + "let doc=false;", + "for await (const entry of core.localDatabase.findEntries('i:','i;',{conflicts:true})){", + " if(entry.path===prefixedPath){ doc=entry; break; }", + "}", + "if(!doc?._conflicts?.length) throw new Error(`Could not find hidden JSON conflict: ${path}`);", + "const conflicts=doc._conflicts.sort((a,b)=>Number(a.split('-')[0])-Number(b.split('-')[0]));", + "const docA=await core.localDatabase.getDBEntry(prefixedPath,{rev:doc._rev});", + "const docB=await core.localDatabase.getDBEntry(prefixedPath,{rev:conflicts[0]});", + "if(docA===false||docB===false) throw new Error(`Could not load conflicted hidden JSON entries: ${path}`);", + "void addOn.showJSONMergeDialogAndMerge(docA,docB);", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function clickJsonResolveOption(cliBinary: string, env: NodeJS.ProcessEnv, mode: "AB" | "BA"): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const mode=${JSON.stringify(mode)};`, + "const deadline=Date.now()+10000;", + "while(Date.now()candidate.value===mode);", + " const apply=[...document.querySelectorAll('button')].find((button)=>button.textContent?.trim()==='Apply');", + " if(input&&apply){", + " input.click();", + " input.dispatchEvent(new Event('change',{bubbles:true}));", + " await new Promise((resolve)=>setTimeout(resolve,100));", + " apply.click();", + " return JSON.stringify({ok:true});", + " }", + " await new Promise((resolve)=>setTimeout(resolve,250));", + "}", + "const buttons=[...document.querySelectorAll('button')].map((button)=>button.textContent?.trim()).filter(Boolean);", + "const inputs=[...document.querySelectorAll('input[name=\"disp\"]')].map((input)=>input.value);", + "throw new Error(`Timed out waiting for JSON resolve modal; buttons=${JSON.stringify(buttons)}; inputs=${JSON.stringify(inputs)}`);", + "})()", + ].join(""), + env + ); +} + +async function storeHiddenFileAsConflict( + cliBinary: string, + env: NodeJS.ProcessEnv, + path: string, + baseRev: string +): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + `const baseRev=${JSON.stringify(baseRev)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('HiddenFileSync');", + "const fileInfo=await addOn.loadFileWithInfo(path);", + "if(fileInfo.deleted) throw new Error(`Hidden file was unexpectedly deleted: ${path}`);", + "const baseData=await addOn.__loadBaseSaveData(path,true);", + "if(baseData===false) throw new Error(`Could not load base save data: ${path}`);", + "const saveData={", + " ...baseData,", + " data:fileInfo.body,", + " mtime:fileInfo.stat.mtime,", + " ctime:fileInfo.stat.ctime,", + " size:fileInfo.stat.size,", + " children:[],", + " deleted:false,", + " type:baseData.datatype,", + "};", + "const result=await core.localDatabase.putDBEntry(saveData,false,baseRev);", + "if(!result?.ok) throw new Error(`Could not store conflicted hidden file: ${path}`);", + "return JSON.stringify({ok:true,rev:result.rev});", + "})()", + ].join(""), + env + ); +} + +async function createHiddenJsonConflict( + context: RunnerContext, + session: ObsidianLiveSyncSession, + vault: TemporaryVault, + path: string, + base: string, + left: string, + right: string +): Promise { + await writeVaultFile(vault.path, path, base); + await scanHiddenStorage(context.cliBinary, session.cliEnv); + const baseEntry = await waitForLocalDatabaseEntry(context.cliBinary, session.cliEnv, path, { hidden: true }); + + await writeVaultFile(vault.path, path, left); + await scanHiddenStorage(context.cliBinary, session.cliEnv); + await waitForLocalDatabaseEntry(context.cliBinary, session.cliEnv, path, { hidden: true }); + + await writeVaultFile(vault.path, path, right); + await storeHiddenFileAsConflict(context.cliBinary, session.cliEnv, path, baseEntry.rev); +} + +async function startConfiguredSession( + context: RunnerContext, + vault: TemporaryVault, + overrides: Record = {} +): Promise { + const session = await startObsidianLiveSyncSession({ + binary: context.binary, + cliBinary: context.cliBinary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv); + await configureCouchDb( + context.cliBinary, + session.cliEnv, + { + uri: context.couchDb.uri, + username: context.couchDb.username, + password: context.couchDb.password, + dbName: context.dbName, + }, + { + syncInternalFiles: true, + syncInternalFilesBeforeReplication: true, + watchInternalFileChanges: false, + syncInternalFilesTargetPatterns: "", + ...overrides, + } + ); + await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv); + await prepareRemote(context.cliBinary, session.cliEnv); + return session; +} + +async function uploadHiddenFile( + context: RunnerContext, + session: ObsidianLiveSyncSession, + path: string +): Promise { + await scanHiddenStorage(context.cliBinary, session.cliEnv); + const entry = await waitForLocalDatabaseEntry(context.cliBinary, session.cliEnv, path, { hidden: true }); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await waitForCouchDbDocs(context.couchDb, context.dbName, (docs) => { + const ids = new Set(docs.map((doc) => doc._id)); + return ids.has(entry.id) && entry.children.every((childId) => ids.has(childId)); + }); + return entry; +} + +async function pullAndApplyHiddenFiles(context: RunnerContext, session: ObsidianLiveSyncSession): Promise { + await pushLocalChanges(context.cliBinary, session.cliEnv); + await resolveHiddenConflicts(context.cliBinary, session.cliEnv); + await scanHiddenDatabase(context.cliBinary, session.cliEnv); +} + +async function runCreateRoundTrip( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + await writeVaultFile(vaultA.path, snippetPath, snippetContent); + let session = await startConfiguredSession(context, vaultA); + const entry = await uploadHiddenFile(context, session, snippetPath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB); + await pullAndApplyHiddenFiles(context, session); + const received = await waitForPathContent(vaultB.path, snippetPath, (content) => content === snippetContent); + await session.app.stop(); + + assertEqual(received, snippetContent, "Hidden snippet content did not round-trip to the second vault."); + console.log(`Hidden create round-trip copied ${entry.id} to the second vault.`); +} + +async function runDeleteRoundTrip( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + await removeVaultFile(vaultA.path, snippetPath); + let session = await startConfiguredSession(context, vaultA); + await scanHiddenStorage(context.cliBinary, session.cliEnv); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB); + await pullAndApplyHiddenFiles(context, session); + await waitForPathDeleted(vaultB.path, snippetPath); + await session.app.stop(); + + console.log("Hidden delete round-trip removed the snippet from the second vault."); +} + +async function runJsonConflictRoundTrip( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + const base = JSON.stringify({ base: true, fromA: false, fromB: false }, null, 4) + "\n"; + const left = JSON.stringify({ base: true, fromA: true, fromB: false }, null, 4) + "\n"; + const right = JSON.stringify({ base: true, fromA: false, fromB: true }, null, 4) + "\n"; + + let session = await startConfiguredSession(context, vaultB); + await createHiddenJsonConflict(context, session, vaultB, mergeJsonPath, base, left, right); + await autoMergeHiddenJsonConflict(context.cliBinary, session.cliEnv, mergeJsonPath); + await pushLocalChanges(context.cliBinary, session.cliEnv); + const mergedOnB = await waitForPathContent(vaultB.path, mergeJsonPath, (content) => + hasJsonValues(content, { fromA: true, fromB: true }) + ); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultA); + await pullAndApplyHiddenFiles(context, session); + const mergedOnA = await waitForPathContent(vaultA.path, mergeJsonPath, (content) => + hasJsonValues(content, { fromA: true, fromB: true }) + ); + await session.app.stop(); + + assertEqual(mergedOnA, mergedOnB, "Merged hidden JSON content was not consistent across both vaults."); + console.log("Hidden JSON conflict was automatically merged and round-tripped."); +} + +async function runJsonManualConflictResolution(context: RunnerContext, vault: TemporaryVault): Promise { + const base = JSON.stringify({ shared: "base" }, null, 4) + "\n"; + const left = JSON.stringify({ shared: "left", fromA: true }, null, 4) + "\n"; + const right = JSON.stringify({ shared: "right", fromB: true }, null, 4) + "\n"; + + const session = await startConfiguredSession(context, vault); + await createHiddenJsonConflict(context, session, vault, manualMergeJsonPath, base, left, right); + await openHiddenJsonResolveModal(context.cliBinary, session.cliEnv, manualMergeJsonPath); + await clickJsonResolveOption(context.cliBinary, session.cliEnv, "AB"); + + const merged = await waitForPathContent(vault.path, manualMergeJsonPath, (content) => + hasJsonValues(content, { shared: "right", fromA: true, fromB: true }) + ); + await session.app.stop(); + + const parsed = JSON.parse(merged); + assertEqual(parsed.shared, "right", "Manual JSON conflict resolution did not apply the selected merged result."); + assertEqual(parsed.fromA, true, "Manual JSON conflict resolution lost the first-side value."); + assertEqual(parsed.fromB, true, "Manual JSON conflict resolution lost the second-side value."); + console.log("Hidden JSON conflict modal applied the selected merged result."); +} + +async function runTargetMismatch( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + const targetContent = JSON.stringify({ onlyA: true, targetMismatch: true }, null, 4) + "\n"; + await writeVaultFile(vaultA.path, targetPath, targetContent); + + let session = await startConfiguredSession(context, vaultA); + await uploadHiddenFile(context, session, targetPath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, { + syncInternalFilesTargetPatterns: "snippets", + }); + await pullAndApplyHiddenFiles(context, session); + assertEqual( + await pathExists(vaultB.path, targetPath), + false, + "Hidden file was applied on a device where it was not a target file." + ); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, { + syncInternalFilesTargetPatterns: "", + }); + await pullAndApplyHiddenFiles(context, session); + const received = await waitForPathContent(vaultB.path, targetPath, (content) => content === targetContent); + await session.app.stop(); + + assertEqual(received, targetContent, "Hidden file was not applied after it became a target file."); + console.log("Hidden target mismatch respected per-device target patterns, then applied after enabling the target."); +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const couchDb = await loadCouchDbConfig(); + const dbName = makeUniqueDatabaseName(couchDb.dbPrefix, "hidden-roundtrip"); + const vaultA = await createTemporaryVault(); + const vaultB = await createTemporaryVault(); + const context: RunnerContext = { binary, cliBinary: cli.binary, couchDb, dbName }; + + try { + await assertCouchDbReachable(couchDb); + await createCouchDbDatabase(couchDb, dbName); + + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault A: ${vaultA.path}`); + console.log(`Temporary vault B: ${vaultB.path}`); + console.log(`Temporary CouchDB database: ${dbName}`); + + await runCreateRoundTrip(context, vaultA, vaultB); + await runDeleteRoundTrip(context, vaultA, vaultB); + await runJsonConflictRoundTrip(context, vaultA, vaultB); + await runJsonManualConflictResolution(context, vaultB); + await runTargetMismatch(context, vaultA, vaultB); + } finally { + await vaultA.dispose(); + await vaultB.dispose(); + if (process.env.E2E_OBSIDIAN_KEEP_COUCHDB !== "true") { + await deleteCouchDbDatabase(couchDb, dbName).catch((error: unknown) => { + console.warn(error instanceof Error ? error.message : error); + }); + } + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/setting-markdown-export.ts b/test/e2e-obsidian/scripts/setting-markdown-export.ts new file mode 100644 index 0000000..440df8d --- /dev/null +++ b/test/e2e-obsidian/scripts/setting-markdown-export.ts @@ -0,0 +1,106 @@ +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { evalObsidianJson } from "../runner/cli.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { assertEqual, waitForLiveSyncCoreReady } from "../runner/liveSyncWorkflow.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; + +process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; + +const settingPath = "LiveSync/settings-export.md"; + +async function waitForFileContaining( + vaultPath: string, + path: string, + predicates: ((content: string) => boolean)[], + timeoutMs = Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000) +): Promise { + const fullPath = join(vaultPath, path); + const deadline = Date.now() + timeoutMs; + let lastContent = ""; + let lastError: unknown; + while (Date.now() < deadline) { + try { + lastContent = await readFile(fullPath, "utf-8"); + if (predicates.every((predicate) => predicate(lastContent))) { + return lastContent; + } + } catch (error) { + lastError = error; + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for setting Markdown: ${fullPath}\nLast error: ${String(lastError)}`); +} + +async function configureSettingMarkdown(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "await core.services.setting.applyExternalSettings({", + `settingSyncFile:${JSON.stringify(settingPath)},`, + "writeCredentialsForSettingSync:false,", + "couchDB_USER:'e2e-user',", + "couchDB_PASSWORD:'e2e-password',", + "passphrase:'e2e-passphrase',", + "showVerboseLog:true,", + "},true);", + "await core.services.setting.saveSettingData();", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const vault = await createTemporaryVault(); + let session: ObsidianLiveSyncSession | undefined; + try { + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault: ${vault.path}`); + + session = await startObsidianLiveSyncSession({ + binary, + cliBinary: cli.binary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(cli.binary, session.cliEnv); + + await configureSettingMarkdown(cli.binary, session.cliEnv); + const content = await waitForFileContaining(vault.path, settingPath, [ + (value) => value.includes("````yaml:livesync-setting"), + (value) => value.includes(`settingSyncFile: ${settingPath}`), + (value) => value.includes("showVerboseLog: true"), + ]); + + assertEqual( + content.includes("couchDB_PASSWORD: e2e-password"), + false, + "Credential leaked into setting Markdown." + ); + assertEqual(content.includes("passphrase: e2e-passphrase"), false, "Passphrase leaked into setting Markdown."); + + console.log(`Generated setting Markdown without credentials: ${settingPath}`); + } finally { + if (session) { + await session.app.stop(); + } + await vault.dispose(); + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/startup-scan.ts b/test/e2e-obsidian/scripts/startup-scan.ts new file mode 100644 index 0000000..9aaa4de --- /dev/null +++ b/test/e2e-obsidian/scripts/startup-scan.ts @@ -0,0 +1,116 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { + assertCouchDbReachable, + createCouchDbDatabase, + deleteCouchDbDatabase, + loadCouchDbConfig, + makeUniqueDatabaseName, + waitForCouchDbDocs, +} from "../runner/couchdb.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { + assertEqual, + configureCouchDb, + prepareRemote, + pushLocalChanges, + waitForLiveSyncCoreReady, + waitForLocalDatabaseEntry, +} from "../runner/liveSyncWorkflow.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; + +process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; + +const notePath = "E2E/startup-scan.md"; +const noteContent = [ + "# Startup scan", + "", + "This note was written while Obsidian was stopped.", + "The test verifies that the next real Obsidian boot scans it into the local database.", + `Created at: ${new Date().toISOString()}`, + "", +].join("\n"); + +async function writeVaultFile(vaultPath: string, path: string, content: string): Promise { + const fullPath = join(vaultPath, path); + await mkdir(dirname(fullPath), { recursive: true }); + await writeFile(fullPath, content, "utf-8"); +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const couchDb = await loadCouchDbConfig(); + const dbName = makeUniqueDatabaseName(couchDb.dbPrefix, "startup-scan"); + const vault = await createTemporaryVault(); + let session: ObsidianLiveSyncSession | undefined; + + try { + await assertCouchDbReachable(couchDb); + await createCouchDbDatabase(couchDb, dbName); + + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault: ${vault.path}`); + console.log(`Temporary CouchDB database: ${dbName}`); + + session = await startObsidianLiveSyncSession({ + binary, + cliBinary: cli.binary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(cli.binary, session.cliEnv); + const configured = await configureCouchDb(cli.binary, session.cliEnv, { + uri: couchDb.uri, + username: couchDb.username, + password: couchDb.password, + dbName, + }); + assertEqual(configured.isConfigured, true, "Self-hosted LiveSync was not configured."); + await prepareRemote(cli.binary, session.cliEnv); + await session.app.stop(); + session = undefined; + + await writeVaultFile(vault.path, notePath, noteContent); + + session = await startObsidianLiveSyncSession({ + binary, + cliBinary: cli.binary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(cli.binary, session.cliEnv); + + const localEntry = await waitForLocalDatabaseEntry(cli.binary, session.cliEnv, notePath); + await pushLocalChanges(cli.binary, session.cliEnv); + + const remoteDocs = await waitForCouchDbDocs(couchDb, dbName, (docs) => { + const ids = new Set(docs.map((doc) => doc._id)); + return ids.has(localEntry.id) && localEntry.children.every((childId) => ids.has(childId)); + }); + const remoteMetadata = remoteDocs.find((doc) => doc._id === localEntry.id); + assertEqual(remoteMetadata?.path, localEntry.path, "Startup-scanned remote metadata path did not match."); + + console.log(`Startup scan uploaded metadata ${localEntry.id} and ${localEntry.children.length} chunk(s).`); + } finally { + if (session) { + await session.app.stop(); + } + await vault.dispose(); + if (process.env.E2E_OBSIDIAN_KEEP_COUCHDB !== "true") { + await deleteCouchDbDatabase(couchDb, dbName).catch((error: unknown) => { + console.warn(error instanceof Error ? error.message : error); + }); + } + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); From 7028a0857e8200fdae46ddc5dfb4ecf4c2c8e5e1 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 26 Jun 2026 12:02:29 +0000 Subject: [PATCH 09/16] (test): customisation sync --- docs/adr/2026_06_real_obsidian_e2e.md | 6 +- package.json | 2 + test/e2e-obsidian/README.md | 6 + .../scripts/customisation-sync.ts | 326 +++++++++++++ test/e2e-obsidian/scripts/two-vault-sync.ts | 438 ++++++++++++++++++ 5 files changed, 776 insertions(+), 2 deletions(-) create mode 100644 test/e2e-obsidian/scripts/customisation-sync.ts create mode 100644 test/e2e-obsidian/scripts/two-vault-sync.ts diff --git a/docs/adr/2026_06_real_obsidian_e2e.md b/docs/adr/2026_06_real_obsidian_e2e.md index a0787e4..eefe4e3 100644 --- a/docs/adr/2026_06_real_obsidian_e2e.md +++ b/docs/adr/2026_06_real_obsidian_e2e.md @@ -125,7 +125,7 @@ Initial discovery on Linux ARM64 found that: Current implementation status: - Added `test/e2e-obsidian/runner` helpers for Obsidian discovery, CLI discovery, temporary vault creation, plug-in installation, process launch, CLI execution, and readiness polling. -- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, `test:e2e:obsidian:couchdb-upload`, `test:e2e:obsidian:startup-scan`, `test:e2e:obsidian:hidden-file-snippet-sync`, `test:e2e:obsidian:setting-markdown-export`, and `test:e2e:obsidian:install-appimage`. +- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, `test:e2e:obsidian:couchdb-upload`, `test:e2e:obsidian:startup-scan`, `test:e2e:obsidian:two-vault-sync`, `test:e2e:obsidian:hidden-file-snippet-sync`, `test:e2e:obsidian:customisation-sync`, `test:e2e:obsidian:setting-markdown-export`, and `test:e2e:obsidian:install-appimage`. - Added `startObsidianLiveSyncSession()` so future workflows can reuse the launch, vault open, community plug-in enablement, plug-in reload, and readiness sequence without duplicating smoke runner code. - Added CouchDB runner utilities that reuse `.test.env`/process environment values, create unique temporary databases, query uploaded documents directly, and clean up the database unless `E2E_OBSIDIAN_KEEP_COUCHDB=true` is set. - Added a manual AppImage installer that downloads Obsidian `1.12.7` for `arm64` or `x86_64`, stores it under `_testdata/obsidian`, and extracts it for FUSE-free execution. @@ -142,7 +142,9 @@ Current verification: - `npm run test:e2e:obsidian:vault-reflection` creates a note through Obsidian's vault API, verifies the reflected file on disk, and reads it back through Obsidian. - `npm run test:e2e:obsidian:couchdb-upload` configures a unique CouchDB database, creates a note through Obsidian, commits it into the local database, runs one-shot synchronisation, and verifies that CouchDB contains the metadata document and all referenced chunk documents. - `npm run test:e2e:obsidian:startup-scan` verifies that a file written while Obsidian is stopped is picked up during the next real Obsidian boot and uploaded to CouchDB after one-shot synchronisation. +- `npm run test:e2e:obsidian:two-vault-sync` verifies two-vault note synchronisation: creation, update, deletion, Markdown conflict automatic merging with the merged result propagated by a second synchronisation, and per-device target-filter differences. - `npm run test:e2e:obsidian:hidden-file-snippet-sync` verifies hidden file synchronisation as a two-vault round-trip: creation, deletion, automatic JSON conflict merging with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target-pattern differences. +- `npm run test:e2e:obsidian:customisation-sync` verifies a two-vault Customisation Sync snippet workflow: scan a real snippet CSS file into per-file Customisation Sync data, synchronise it through CouchDB, apply it on the second vault, and assert the resulting `.obsidian/snippets/*.css` file. - `npm run test:e2e:obsidian:setting-markdown-export` verifies that setting Markdown export creates a vault file and omits credentials when credential export is disabled. - `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present. @@ -167,7 +169,7 @@ Current implementation status: - Added a pre-CouchDB workflow that creates a note through Obsidian's vault API, confirms the note is reflected as a real vault file, and reads the same note back through Obsidian. This covers the vault reflection part of the Phase 2 path before remote database setup is introduced. - Added a first CouchDB-backed upload workflow, modelled after the CLI Deno tests: reuse the standard CouchDB environment variables, create a unique remote database, apply CouchDB settings through the plug-in's setting service, commit the note through the real Obsidian vault path, run one-shot synchronisation, and assert that remote metadata and chunks exist. -- Added Obsidian-specific workflows for boot-time vault scanning, hidden `.obsidian/snippets` file round-tripping, hidden JSON conflict resolution, per-device hidden target-pattern differences, and setting Markdown export. These scenarios assert against CouchDB documents, vault files, or real Obsidian UI outcomes instead of internal service state. +- Added Obsidian-specific workflows for boot-time vault scanning, two-vault note synchronisation, hidden `.obsidian/snippets` file round-tripping, hidden JSON conflict resolution, Customisation Sync snippet application, per-device target-filter differences, and setting Markdown export. These scenarios assert against CouchDB documents, vault files, or real Obsidian UI outcomes instead of internal service state. ### Phase 3: Two-Vault Synchronisation diff --git a/package.json b/package.json index e7c6651..682b101 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,9 @@ "test:e2e:obsidian:vault-reflection": "tsx test/e2e-obsidian/scripts/vault-reflection.ts", "test:e2e:obsidian:couchdb-upload": "tsx test/e2e-obsidian/scripts/couchdb-upload.ts", "test:e2e:obsidian:startup-scan": "tsx test/e2e-obsidian/scripts/startup-scan.ts", + "test:e2e:obsidian:two-vault-sync": "tsx test/e2e-obsidian/scripts/two-vault-sync.ts", "test:e2e:obsidian:hidden-file-snippet-sync": "tsx test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts", + "test:e2e:obsidian:customisation-sync": "tsx test/e2e-obsidian/scripts/customisation-sync.ts", "test:e2e:obsidian:setting-markdown-export": "tsx test/e2e-obsidian/scripts/setting-markdown-export.ts", "test:coverage": "vitest run --coverage", "test:docker-couchdb:up": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-start.sh", diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md index 6593d98..6bf895d 100644 --- a/test/e2e-obsidian/README.md +++ b/test/e2e-obsidian/README.md @@ -48,7 +48,9 @@ npm run test:e2e:obsidian:smoke npm run test:e2e:obsidian:vault-reflection npm run test:e2e:obsidian:couchdb-upload npm run test:e2e:obsidian:startup-scan +npm run test:e2e:obsidian:two-vault-sync npm run test:e2e:obsidian:hidden-file-snippet-sync +npm run test:e2e:obsidian:customisation-sync npm run test:e2e:obsidian:setting-markdown-export ``` @@ -56,8 +58,12 @@ npm run test:e2e:obsidian:setting-markdown-export `test:e2e:obsidian:startup-scan` configures a temporary CouchDB database, stops Obsidian, writes a note directly into the vault, restarts Obsidian, and verifies from CouchDB that the boot-time scan picked up the offline file. +`test:e2e:obsidian:two-vault-sync` runs a two-vault note synchronisation workflow. It verifies note creation, update, deletion, Markdown conflict automatic merging with the merged result propagated by a second synchronisation, and per-device target filters where one vault ignores a note that the other vault synchronises. + `test:e2e:obsidian:hidden-file-snippet-sync` runs a two-vault hidden file round-trip. It verifies creation and deletion of a real `.obsidian/snippets/*.css` file, automatic JSON conflict merging for a hidden file with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target patterns where one vault ignores a hidden file that the other vault synchronises. +`test:e2e:obsidian:customisation-sync` runs a two-vault Customisation Sync workflow. It scans a real snippet CSS file into per-file Customisation Sync data, synchronises the entry through CouchDB, applies it on the second vault, and verifies the resulting `.obsidian/snippets/*.css` file. + `test:e2e:obsidian:setting-markdown-export` enables setting Markdown export, waits for the generated Markdown file in the vault, and verifies that credentials are omitted when `writeCredentialsForSettingSync=false`. Start the local CouchDB fixture first when one is not already running: diff --git a/test/e2e-obsidian/scripts/customisation-sync.ts b/test/e2e-obsidian/scripts/customisation-sync.ts new file mode 100644 index 0000000..03c086c --- /dev/null +++ b/test/e2e-obsidian/scripts/customisation-sync.ts @@ -0,0 +1,326 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { evalObsidianJson } from "../runner/cli.ts"; +import { + assertCouchDbReachable, + createCouchDbDatabase, + deleteCouchDbDatabase, + loadCouchDbConfig, + makeUniqueDatabaseName, + waitForCouchDbDocs, + type CouchDbConfig, +} from "../runner/couchdb.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { + assertEqual, + configureCouchDb, + prepareRemote, + pushLocalChanges, + waitForLiveSyncCoreReady, +} from "../runner/liveSyncWorkflow.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault, type TemporaryVault } from "../runner/vault.ts"; + +process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; +process.env.E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS ??= "20000"; + +const snippetPath = ".obsidian/snippets/livesync-customisation-e2e.css"; +const snippetContent = [ + "body {", + " --livesync-customisation-e2e-colour: #3d6f54;", + "}", + "", + ".livesync-customisation-e2e {", + " color: var(--livesync-customisation-e2e-colour);", + "}", + "", +].join("\n"); + +type RunnerContext = { + binary: string; + cliBinary: string; + couchDb: CouchDbConfig; + dbName: string; +}; + +type CustomisationEntry = { + id: string; + path: string; + children: string[]; +}; + +type CustomisationScanResult = { + enabled: boolean; + useV2: boolean; + device: string; + configDir: string; + files: string[]; +}; + +async function writeVaultFile(vaultPath: string, path: string, content: string): Promise { + const fullPath = join(vaultPath, path); + await mkdir(dirname(fullPath), { recursive: true }); + await writeFile(fullPath, content, "utf-8"); +} + +async function readVaultFile(vaultPath: string, path: string): Promise { + return await readFile(join(vaultPath, path), "utf-8"); +} + +async function pathExists(vaultPath: string, path: string): Promise { + try { + await readFile(join(vaultPath, path)); + return true; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return false; + } + throw error; + } +} + +async function waitForPathContent( + vaultPath: string, + path: string, + predicate: (content: string) => boolean, + timeoutMs = Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000) +): Promise { + const deadline = Date.now() + timeoutMs; + let lastContent = ""; + while (Date.now() < deadline) { + if (await pathExists(vaultPath, path)) { + lastContent = await readVaultFile(vaultPath, path); + if (predicate(lastContent)) { + return lastContent; + } + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for ${path}. Last content:\n${lastContent}`); +} + +async function startConfiguredSession( + context: RunnerContext, + vault: TemporaryVault, + deviceName: string +): Promise { + const session = await startObsidianLiveSyncSession({ + binary: context.binary, + cliBinary: context.cliBinary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv); + await configureCouchDb( + context.cliBinary, + session.cliEnv, + { + uri: context.couchDb.uri, + username: context.couchDb.username, + password: context.couchDb.password, + dbName: context.dbName, + }, + { + deviceAndVaultName: deviceName, + usePluginSync: true, + usePluginSyncV2: true, + autoSweepPlugins: false, + autoSweepPluginsPeriodic: false, + syncInternalFiles: false, + } + ); + await evalObsidianJson( + context.cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + `core.services.setting.setDeviceAndVaultName(${JSON.stringify(deviceName)});`, + "await core.services.setting.saveSettingData();", + "return JSON.stringify({device:core.services.setting.getDeviceAndVaultName()});", + "})()", + ].join(""), + session.cliEnv + ); + await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv); + await prepareRemote(context.cliBinary, session.cliEnv); + return session; +} + +async function scanCustomisations(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('ConfigSync');", + "const before=await addOn.scanInternalFiles();", + "await addOn.scanAllConfigFiles(false);", + "return JSON.stringify({", + "ok:true,", + "enabled:core.settings.usePluginSync,", + "useV2:core.settings.usePluginSyncV2,", + "device:core.services.setting.getDeviceAndVaultName(),", + "configDir:addOn.configDir,", + "files:before,", + "});", + "})()", + ].join(""), + env + ); +} + +async function storeCustomisationSnippet(cliBinary: string, env: NodeJS.ProcessEnv, path: string): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('ConfigSync');", + "const term=core.services.setting.getDeviceAndVaultName();", + "const stat=await core.storageAccess.statHidden(path);", + "const category=addOn.getFileCategory(path);", + "const result=await addOn.storeCustomizationFiles(path,term);", + "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", + "const entries=rows.map((row)=>row.doc).filter((doc)=>doc?.path?.startsWith('ix:')).map((doc)=>doc.path);", + "if(!result){", + " throw new Error(`Could not store Customisation Sync snippet: path=${path}; term=${term}; category=${category}; stat=${JSON.stringify(stat)}; result=${JSON.stringify(result)}; entries=${JSON.stringify(entries)}`);", + "}", + "return JSON.stringify({ok:true,path,term,category,entries});", + "})()", + ].join(""), + env + ); +} + +async function waitForCustomisationEntry( + cliBinary: string, + env: NodeJS.ProcessEnv, + filename: string, + timeoutMs = Number(process.env.E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS ?? 15000) +): Promise { + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const filename=${JSON.stringify(filename)};`, + `const timeoutMs=${JSON.stringify(timeoutMs)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const deadline=Date.now()+timeoutMs;", + "const sleep=(ms)=>new Promise((resolve)=>setTimeout(resolve,ms));", + "let entry=false;", + "while(Date.now()row.doc).find((doc)=>doc?.path?.includes('/SNIPPET/')&&doc.path?.endsWith(`%${filename}`))||false;", + " if(entry&&entry._id&&Array.isArray(entry.children)&&entry.children.length>0) break;", + " await sleep(250);", + "}", + "if(!entry||!entry._id){", + " const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", + " const entries=rows.map((row)=>row.doc).filter((doc)=>doc?.path?.startsWith('ix:')).map((doc)=>({id:doc._id,path:doc.path,children:doc.children?.length??0}));", + " throw new Error(`Timed out waiting for customisation sync entry: ${filename}; entries=${JSON.stringify(entries)}`);", + "}", + "return JSON.stringify({id:entry._id,path:entry.path,children:entry.children||[]});", + "})()", + ].join(""), + env + ); +} + +async function applyRemoteCustomisationSnippet( + cliBinary: string, + env: NodeJS.ProcessEnv, + filename: string +): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const filename=${JSON.stringify(filename)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('ConfigSync');", + "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", + "const entry=rows.map((row)=>row.doc).find((doc)=>doc?.path?.includes('/SNIPPET/')&&doc.path?.endsWith(`%${filename}`))||false;", + "if(!entry) throw new Error(`Could not find remote customisation entry: ${filename}`);", + "const display=addOn.createPluginDataFromV2(entry.path);", + "if(!display) throw new Error(`Could not create Customisation Sync display entry: ${entry.path}`);", + "const file=await addOn.createPluginDataExFileV2(entry.path);", + "if(!file) throw new Error(`Could not load Customisation Sync file entry: ${entry.path}`);", + "await display.setFile(file);", + "if(!(await addOn.applyDataV2(display))){", + " throw new Error(`Could not apply Customisation Sync entry: ${entry.path}`);", + "}", + "return JSON.stringify({ok:true,path:entry.path});", + "})()", + ].join(""), + env + ); +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const couchDb = await loadCouchDbConfig(); + const dbName = makeUniqueDatabaseName(couchDb.dbPrefix, "customisation-sync"); + const vaultA = await createTemporaryVault(); + const vaultB = await createTemporaryVault(); + const context: RunnerContext = { binary, cliBinary: cli.binary, couchDb, dbName }; + const snippetPathParts = snippetPath.split("/"); + const snippetName = snippetPathParts[snippetPathParts.length - 1] ?? snippetPath; + + try { + await assertCouchDbReachable(couchDb); + await createCouchDbDatabase(couchDb, dbName); + + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault A: ${vaultA.path}`); + console.log(`Temporary vault B: ${vaultB.path}`); + console.log(`Temporary CouchDB database: ${dbName}`); + + await writeVaultFile(vaultA.path, snippetPath, snippetContent); + + let session = await startConfiguredSession(context, vaultA, "customisation-sync-a"); + const scanResult = await scanCustomisations(context.cliBinary, session.cliEnv); + console.log(`Customisation scan files: ${scanResult.files.join(", ") || "(none)"}`); + await storeCustomisationSnippet(context.cliBinary, session.cliEnv, snippetPath); + const entry = await waitForCustomisationEntry(context.cliBinary, session.cliEnv, snippetName); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await waitForCouchDbDocs(context.couchDb, context.dbName, (docs) => { + const ids = new Set(docs.map((doc) => doc._id)); + return ids.has(entry.id) && entry.children.every((childId) => ids.has(childId)); + }); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, "customisation-sync-b"); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await waitForCustomisationEntry(context.cliBinary, session.cliEnv, snippetName); + assertEqual( + await pathExists(vaultB.path, snippetPath), + false, + "Customisation Sync snippet was reflected before explicit application." + ); + await applyRemoteCustomisationSnippet(context.cliBinary, session.cliEnv, snippetName); + const applied = await waitForPathContent(vaultB.path, snippetPath, (content) => content === snippetContent); + await session.app.stop(); + + assertEqual(applied, snippetContent, "Customisation Sync snippet content did not match after application."); + console.log(`Customisation Sync applied snippet ${snippetName} from the remote database.`); + } finally { + await vaultA.dispose(); + await vaultB.dispose(); + if (process.env.E2E_OBSIDIAN_KEEP_COUCHDB !== "true") { + await deleteCouchDbDatabase(couchDb, dbName).catch((error: unknown) => { + console.warn(error instanceof Error ? error.message : error); + }); + } + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/two-vault-sync.ts b/test/e2e-obsidian/scripts/two-vault-sync.ts new file mode 100644 index 0000000..4708682 --- /dev/null +++ b/test/e2e-obsidian/scripts/two-vault-sync.ts @@ -0,0 +1,438 @@ +import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { evalObsidianJson } from "../runner/cli.ts"; +import { + assertCouchDbReachable, + createCouchDbDatabase, + deleteCouchDbDatabase, + loadCouchDbConfig, + makeUniqueDatabaseName, + waitForCouchDbDocs, + type CouchDbConfig, +} from "../runner/couchdb.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { + assertEqual, + configureCouchDb, + prepareRemote, + pushLocalChanges, + waitForLiveSyncCoreReady, + waitForLocalDatabaseEntry, + type LocalDatabaseEntry, +} from "../runner/liveSyncWorkflow.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault, type TemporaryVault } from "../runner/vault.ts"; + +process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; +process.env.E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS ??= "20000"; + +const createPath = "E2E/two-vault/create.md"; +const updatePath = "E2E/two-vault/update.md"; +const deletePath = "E2E/two-vault/delete.md"; +const conflictPath = "E2E/two-vault/conflict.md"; +const targetMismatchPath = "E2E/two-vault/target-mismatch.md"; + +type RunnerContext = { + binary: string; + cliBinary: string; + couchDb: CouchDbConfig; + dbName: string; +}; + +async function writeVaultFile(vaultPath: string, path: string, content: string): Promise { + const fullPath = join(vaultPath, path); + await mkdir(dirname(fullPath), { recursive: true }); + await writeFile(fullPath, content, "utf-8"); +} + +async function removeVaultFile(vaultPath: string, path: string): Promise { + await rm(join(vaultPath, path), { force: true }); +} + +async function readVaultFile(vaultPath: string, path: string): Promise { + return await readFile(join(vaultPath, path), "utf-8"); +} + +async function pathExists(vaultPath: string, path: string): Promise { + try { + await readFile(join(vaultPath, path)); + return true; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return false; + } + throw error; + } +} + +async function waitForPathContent( + vaultPath: string, + path: string, + predicate: (content: string) => boolean, + timeoutMs = Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000) +): Promise { + const deadline = Date.now() + timeoutMs; + let lastContent = ""; + while (Date.now() < deadline) { + if (await pathExists(vaultPath, path)) { + lastContent = await readVaultFile(vaultPath, path); + if (predicate(lastContent)) { + return lastContent; + } + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for ${path}. Last content:\n${lastContent}`); +} + +async function waitForPathDeleted( + vaultPath: string, + path: string, + timeoutMs = Number(process.env.E2E_OBSIDIAN_FILE_TIMEOUT_MS ?? 10000) +): Promise { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + if (!(await pathExists(vaultPath, path))) { + return; + } + await new Promise((resolve) => setTimeout(resolve, 250)); + } + throw new Error(`Timed out waiting for deleted file: ${join(vaultPath, path)}`); +} + +async function writeNoteViaObsidian(cliBinary: string, env: NodeJS.ProcessEnv, path: string, content: string) { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + `const content=${JSON.stringify(content)};`, + "const folder=path.split('/').slice(0,-1).join('/');", + "if(folder&&!(await app.vault.adapter.exists(folder))) await app.vault.createFolder(folder);", + "const existing=app.vault.getAbstractFileByPath(path);", + "if(existing) await app.vault.modify(existing,content);", + "else await app.vault.create(path,content);", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function deleteNoteViaObsidian(cliBinary: string, env: NodeJS.ProcessEnv, path: string) { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + "const existing=app.vault.getAbstractFileByPath(path);", + "if(existing) await app.vault.delete(existing);", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function startConfiguredSession( + context: RunnerContext, + vault: TemporaryVault, + overrides: Record = {} +): Promise { + const session = await startObsidianLiveSyncSession({ + binary: context.binary, + cliBinary: context.cliBinary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv); + await configureCouchDb( + context.cliBinary, + session.cliEnv, + { + uri: context.couchDb.uri, + username: context.couchDb.username, + password: context.couchDb.password, + dbName: context.dbName, + }, + overrides + ); + await waitForLiveSyncCoreReady(context.cliBinary, session.cliEnv); + await prepareRemote(context.cliBinary, session.cliEnv); + return session; +} + +async function uploadNote( + context: RunnerContext, + session: ObsidianLiveSyncSession, + path: string +): Promise { + const entry = await waitForLocalDatabaseEntry(context.cliBinary, session.cliEnv, path); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await waitForCouchDbDocs(context.couchDb, context.dbName, (docs) => { + const ids = new Set(docs.map((doc) => doc._id)); + return ids.has(entry.id) && entry.children.every((childId) => ids.has(childId)); + }); + return entry; +} + +async function syncAndApply(context: RunnerContext, session: ObsidianLiveSyncSession): Promise { + await pushLocalChanges(context.cliBinary, session.cliEnv); +} + +async function storeFileRevision( + cliBinary: string, + env: NodeJS.ProcessEnv, + path: string, + content: string, + baseRev?: string +): Promise { + const result = await evalObsidianJson<{ rev: string }>( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + `const content=${JSON.stringify(content)};`, + `const baseRev=${JSON.stringify(baseRev ?? "")};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const blob=new Blob([content],{type:'text/plain'});", + "const id=await core.services.path.path2id(path);", + "const now=Date.now();", + "const result=await core.localDatabase.putDBEntry({", + " _id:id,", + " path,", + " data:blob,", + " ctime:now,", + " mtime:now,", + " size:(await blob.arrayBuffer()).byteLength,", + " children:[],", + " datatype:'plain',", + " type:'plain',", + " eden:{},", + "},false,baseRev||undefined);", + "if(!result?.ok) throw new Error(`Could not store file revision: ${path}`);", + "return JSON.stringify({ok:true,rev:result.rev});", + "})()", + ].join(""), + env + ); + return result.rev; +} + +async function createMarkdownConflict( + context: RunnerContext, + session: ObsidianLiveSyncSession, + vault: TemporaryVault, + path: string, + base: string, + left: string, + right: string +): Promise { + const baseRev = await storeFileRevision(context.cliBinary, session.cliEnv, path, base); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await waitForLocalDatabaseEntry(context.cliBinary, session.cliEnv, path); + await storeFileRevision(context.cliBinary, session.cliEnv, path, left, baseRev); + await storeFileRevision(context.cliBinary, session.cliEnv, path, right, baseRev); + await writeVaultFile(vault.path, path, right); +} + +async function autoMergeMarkdownConflict(cliBinary: string, env: NodeJS.ProcessEnv, path: string): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(path)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const result=await core.localDatabase.managers.conflictManager.tryAutoMerge(path,true);", + "if(!('result' in result)){", + " throw new Error(`Markdown conflict was not auto-mergeable: ${path}; ${JSON.stringify(result)}`);", + "}", + "if(!(await core.databaseFileAccess.storeContent(path,result.result))){", + " throw new Error(`Could not store merged Markdown content: ${path}`);", + "}", + "if(!(await core.fileHandler.deleteRevisionFromDB(path,result.conflictedRev))){", + " throw new Error(`Could not delete conflicted revision: ${path}`);", + "}", + "if(!(await core.fileHandler.dbToStorage(path,path,true))){", + " throw new Error(`Could not reflect merged Markdown content: ${path}`);", + "}", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + +async function runCreateUpdateDelete( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + const createdContent = "# Created on A\n\nThis note should appear on B.\n"; + let session = await startConfiguredSession(context, vaultA); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, createPath, createdContent); + await uploadNote(context, session, createPath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB); + await syncAndApply(context, session); + const createdOnB = await waitForPathContent(vaultB.path, createPath, (content) => content === createdContent); + await session.app.stop(); + assertEqual(createdOnB, createdContent, "Created note did not round-trip to the second vault."); + + const initialUpdateContent = "# Update target\n\nInitial content.\n"; + const updatedContent = "# Update target\n\nUpdated content from A.\n"; + session = await startConfiguredSession(context, vaultA); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, updatePath, initialUpdateContent); + await uploadNote(context, session, updatePath); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, updatePath, updatedContent); + await uploadNote(context, session, updatePath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB); + await syncAndApply(context, session); + const updatedOnB = await waitForPathContent(vaultB.path, updatePath, (content) => content === updatedContent); + await session.app.stop(); + assertEqual(updatedOnB, updatedContent, "Updated note content did not round-trip to the second vault."); + + const deleteContent = "# Delete target\n\nThis note should be removed from B.\n"; + session = await startConfiguredSession(context, vaultA); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, deletePath, deleteContent); + await uploadNote(context, session, deletePath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB); + await syncAndApply(context, session); + await waitForPathContent(vaultB.path, deletePath, (content) => content === deleteContent); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultA); + await deleteNoteViaObsidian(context.cliBinary, session.cliEnv, deletePath); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB); + await syncAndApply(context, session); + await waitForPathDeleted(vaultB.path, deletePath); + await session.app.stop(); + + console.log("Two-vault note creation, update, and deletion round-tripped."); +} + +async function runMarkdownAutoMerge( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + const base = "# Conflict\n\nBase line\n\nShared tail\n"; + const left = "# Conflict\n\nLeft line\n\nShared tail\n"; + const right = "# Conflict\n\nBase line\n\nRight tail\n"; + + let session = await startConfiguredSession(context, vaultB); + await createMarkdownConflict(context, session, vaultB, conflictPath, base, left, right); + await autoMergeMarkdownConflict(context.cliBinary, session.cliEnv, conflictPath); + await pushLocalChanges(context.cliBinary, session.cliEnv); + const mergedOnB = await waitForPathContent( + vaultB.path, + conflictPath, + (content) => content.includes("Left line") && content.includes("Right tail") + ); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultA); + await syncAndApply(context, session); + const mergedOnA = await waitForPathContent( + vaultA.path, + conflictPath, + (content) => content.includes("Left line") && content.includes("Right tail") + ); + await session.app.stop(); + + assertEqual(mergedOnA, mergedOnB, "Merged Markdown content was not consistent across both vaults."); + console.log("Markdown conflict was automatically merged and propagated by the next synchronisation."); +} + +async function runTargetMismatch( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + const ignoredContent = "# Target mismatch\n\nB should ignore this revision.\n"; + const acceptedContent = "# Target mismatch\n\nB should accept this revision after its target filter changes.\n"; + + let session = await startConfiguredSession(context, vaultA); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, targetMismatchPath, ignoredContent); + await uploadNote(context, session, targetMismatchPath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, { + syncOnlyRegEx: "^E2E/two-vault/allowed/.*", + }); + await syncAndApply(context, session); + assertEqual( + await pathExists(vaultB.path, targetMismatchPath), + false, + "A note was reflected on a device where it was not a target file." + ); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultA); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, targetMismatchPath, acceptedContent); + await uploadNote(context, session, targetMismatchPath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, { + syncOnlyRegEx: "", + }); + await syncAndApply(context, session); + const received = await waitForPathContent( + vaultB.path, + targetMismatchPath, + (content) => content === acceptedContent + ); + await session.app.stop(); + + assertEqual(received, acceptedContent, "Target file was not reflected after the device accepted the path."); + console.log("Two-vault target mismatch skipped a non-target note, then reflected it after enabling the target."); +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const couchDb = await loadCouchDbConfig(); + const dbName = makeUniqueDatabaseName(couchDb.dbPrefix, "two-vault-sync"); + const vaultA = await createTemporaryVault(); + const vaultB = await createTemporaryVault(); + const context: RunnerContext = { binary, cliBinary: cli.binary, couchDb, dbName }; + + try { + await assertCouchDbReachable(couchDb); + await createCouchDbDatabase(couchDb, dbName); + + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault A: ${vaultA.path}`); + console.log(`Temporary vault B: ${vaultB.path}`); + console.log(`Temporary CouchDB database: ${dbName}`); + + await runCreateUpdateDelete(context, vaultA, vaultB); + await runMarkdownAutoMerge(context, vaultA, vaultB); + await runTargetMismatch(context, vaultA, vaultB); + } finally { + await vaultA.dispose(); + await vaultB.dispose(); + if (process.env.E2E_OBSIDIAN_KEEP_COUCHDB !== "true") { + await deleteCouchDbDatabase(couchDb, dbName).catch((error: unknown) => { + console.warn(error instanceof Error ? error.message : error); + }); + } + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); From e916683b8df3d5ccd6e9f30a2b571748e1e1c52d Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 26 Jun 2026 12:16:39 +0000 Subject: [PATCH 10/16] (test): customisation sync 2 --- docs/adr/2026_06_real_obsidian_e2e.md | 6 +- test/e2e-obsidian/README.md | 2 +- .../scripts/customisation-sync.ts | 329 ++++++++++++++++-- 3 files changed, 306 insertions(+), 31 deletions(-) diff --git a/docs/adr/2026_06_real_obsidian_e2e.md b/docs/adr/2026_06_real_obsidian_e2e.md index eefe4e3..0e83884 100644 --- a/docs/adr/2026_06_real_obsidian_e2e.md +++ b/docs/adr/2026_06_real_obsidian_e2e.md @@ -144,13 +144,13 @@ Current verification: - `npm run test:e2e:obsidian:startup-scan` verifies that a file written while Obsidian is stopped is picked up during the next real Obsidian boot and uploaded to CouchDB after one-shot synchronisation. - `npm run test:e2e:obsidian:two-vault-sync` verifies two-vault note synchronisation: creation, update, deletion, Markdown conflict automatic merging with the merged result propagated by a second synchronisation, and per-device target-filter differences. - `npm run test:e2e:obsidian:hidden-file-snippet-sync` verifies hidden file synchronisation as a two-vault round-trip: creation, deletion, automatic JSON conflict merging with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target-pattern differences. -- `npm run test:e2e:obsidian:customisation-sync` verifies a two-vault Customisation Sync snippet workflow: scan a real snippet CSS file into per-file Customisation Sync data, synchronise it through CouchDB, apply it on the second vault, and assert the resulting `.obsidian/snippets/*.css` file. +- `npm run test:e2e:obsidian:customisation-sync` verifies a two-vault Customisation Sync workflow: scan a real snippet CSS file, config JSON file, and sample plug-in fixture into per-file Customisation Sync data, synchronise them through CouchDB, apply them on the second vault, assert the resulting `.obsidian` files, propagate a snippet update, and verify deletion of the source-vault snippet sync data without confusing it with the target vault's own applied copy. - `npm run test:e2e:obsidian:setting-markdown-export` verifies that setting Markdown export creates a vault file and omits credentials when credential export is disabled. - `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present. Known limits: -- The smoke runner currently proves only one-vault launch and plug-in load readiness. It does not yet exercise synchronisation, settings persistence, restart behaviour, or database writes. +- The smoke runner currently proves only one-vault launch and plug-in load readiness. Broader workflows are covered by separate real Obsidian scripts, including CouchDB upload, startup scan, two-vault note synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export. - Cross-platform support is still discovery-level. The working path has been validated on Linux ARM64. - CI wiring is not yet implemented. CI should use `OBSIDIAN_BINARY` or a cached `_testdata/obsidian/squashfs-root` rather than downloading the AppImage on every run. @@ -169,7 +169,7 @@ Current implementation status: - Added a pre-CouchDB workflow that creates a note through Obsidian's vault API, confirms the note is reflected as a real vault file, and reads the same note back through Obsidian. This covers the vault reflection part of the Phase 2 path before remote database setup is introduced. - Added a first CouchDB-backed upload workflow, modelled after the CLI Deno tests: reuse the standard CouchDB environment variables, create a unique remote database, apply CouchDB settings through the plug-in's setting service, commit the note through the real Obsidian vault path, run one-shot synchronisation, and assert that remote metadata and chunks exist. -- Added Obsidian-specific workflows for boot-time vault scanning, two-vault note synchronisation, hidden `.obsidian/snippets` file round-tripping, hidden JSON conflict resolution, Customisation Sync snippet application, per-device target-filter differences, and setting Markdown export. These scenarios assert against CouchDB documents, vault files, or real Obsidian UI outcomes instead of internal service state. +- Added Obsidian-specific workflows for boot-time vault scanning, two-vault note synchronisation, hidden `.obsidian/snippets` file round-tripping, hidden JSON conflict resolution, Customisation Sync application for snippets, config JSON files, and plug-in fixtures, per-device target-filter differences, and setting Markdown export. These scenarios assert against CouchDB documents, vault files, or real Obsidian UI outcomes instead of internal service state. ### Phase 3: Two-Vault Synchronisation diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md index 6bf895d..c95e52b 100644 --- a/test/e2e-obsidian/README.md +++ b/test/e2e-obsidian/README.md @@ -62,7 +62,7 @@ npm run test:e2e:obsidian:setting-markdown-export `test:e2e:obsidian:hidden-file-snippet-sync` runs a two-vault hidden file round-trip. It verifies creation and deletion of a real `.obsidian/snippets/*.css` file, automatic JSON conflict merging for a hidden file with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target patterns where one vault ignores a hidden file that the other vault synchronises. -`test:e2e:obsidian:customisation-sync` runs a two-vault Customisation Sync workflow. It scans a real snippet CSS file into per-file Customisation Sync data, synchronises the entry through CouchDB, applies it on the second vault, and verifies the resulting `.obsidian/snippets/*.css` file. +`test:e2e:obsidian:customisation-sync` runs a two-vault Customisation Sync workflow. It scans a real snippet CSS file, config JSON file, and sample plug-in fixture into per-file Customisation Sync data, synchronises the entries through CouchDB, applies them on the second vault, verifies the resulting `.obsidian` files, propagates a snippet update, and verifies deletion of the source-vault snippet sync data without confusing it with the target vault's own applied copy. `test:e2e:obsidian:setting-markdown-export` enables setting Markdown export, waits for the generated Markdown file in the vault, and verifies that credentials are omitted when `writeCredentialsForSettingSync=false`. diff --git a/test/e2e-obsidian/scripts/customisation-sync.ts b/test/e2e-obsidian/scripts/customisation-sync.ts index 03c086c..52bcd5e 100644 --- a/test/e2e-obsidian/scripts/customisation-sync.ts +++ b/test/e2e-obsidian/scripts/customisation-sync.ts @@ -1,4 +1,4 @@ -import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; import { evalObsidianJson } from "../runner/cli.ts"; import { @@ -35,6 +35,47 @@ const snippetContent = [ "}", "", ].join("\n"); +const snippetUpdatedContent = [ + "body {", + " --livesync-customisation-e2e-colour: #73548f;", + "}", + "", + ".livesync-customisation-e2e {", + " background-color: var(--livesync-customisation-e2e-colour);", + "}", + "", +].join("\n"); +const configPath = ".obsidian/livesync-customisation-e2e.json"; +const configContent = JSON.stringify({ source: "customisation-sync", enabled: true }, null, 4) + "\n"; +const pluginDir = ".obsidian/plugins/livesync-e2e-sample"; +const pluginManifestPath = `${pluginDir}/manifest.json`; +const pluginMainPath = `${pluginDir}/main.js`; +const pluginStylesPath = `${pluginDir}/styles.css`; +const pluginManifestContent = + JSON.stringify( + { + id: "livesync-e2e-sample", + name: "LiveSync E2E Sample", + version: "0.0.1", + minAppVersion: "1.0.0", + description: "A sample plug-in fixture for real Obsidian E2E.", + author: "Self-hosted LiveSync", + isDesktopOnly: false, + }, + null, + 4 + ) + "\n"; +const pluginMainContent = [ + "module.exports = class LiveSyncE2ESamplePlugin extends Plugin {", + " async onload() {", + " this.register(() => undefined);", + " }", + "};", + "", +].join("\n"); +const pluginStylesContent = ".livesync-e2e-sample { color: #73548f; }\n"; +const sourceDeviceName = "customisation-sync-a"; +const targetDeviceName = "customisation-sync-b"; type RunnerContext = { binary: string; @@ -63,6 +104,10 @@ async function writeVaultFile(vaultPath: string, path: string, content: string): await writeFile(fullPath, content, "utf-8"); } +async function removeVaultFile(vaultPath: string, path: string): Promise { + await rm(join(vaultPath, path), { force: true }); +} + async function readVaultFile(vaultPath: string, path: string): Promise { return await readFile(join(vaultPath, path), "utf-8"); } @@ -169,7 +214,7 @@ async function scanCustomisations(cliBinary: string, env: NodeJS.ProcessEnv): Pr ); } -async function storeCustomisationSnippet(cliBinary: string, env: NodeJS.ProcessEnv, path: string): Promise { +async function storeCustomisationFile(cliBinary: string, env: NodeJS.ProcessEnv, path: string): Promise { await evalObsidianJson( cliBinary, [ @@ -184,7 +229,7 @@ async function storeCustomisationSnippet(cliBinary: string, env: NodeJS.ProcessE "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", "const entries=rows.map((row)=>row.doc).filter((doc)=>doc?.path?.startsWith('ix:')).map((doc)=>doc.path);", "if(!result){", - " throw new Error(`Could not store Customisation Sync snippet: path=${path}; term=${term}; category=${category}; stat=${JSON.stringify(stat)}; result=${JSON.stringify(result)}; entries=${JSON.stringify(entries)}`);", + " throw new Error(`Could not store Customisation Sync file: path=${path}; term=${term}; category=${category}; stat=${JSON.stringify(stat)}; result=${JSON.stringify(result)}; entries=${JSON.stringify(entries)}`);", "}", "return JSON.stringify({ok:true,path,term,category,entries});", "})()", @@ -193,17 +238,102 @@ async function storeCustomisationSnippet(cliBinary: string, env: NodeJS.ProcessE ); } +async function deleteCustomisationSyncEntry( + cliBinary: string, + env: NodeJS.ProcessEnv, + category: "CONFIG" | "SNIPPET" | "PLUGIN_MAIN", + name: string, + term?: string +): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const category=${JSON.stringify(category)};`, + `const name=${JSON.stringify(name)};`, + `const term=${JSON.stringify(term ?? "")};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('ConfigSync');", + "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", + "const entry=rows.map((row)=>row.doc).find((doc)=>doc?.path?.includes(`/${category}/`)&&doc.path?.includes(`/${name}%`)&&(!term||doc.path?.startsWith(`ix:${term}/`))&&!doc.deleted&&!doc._deleted)||false;", + "if(!entry) throw new Error(`Could not find customisation sync entry to delete: ${category}/${name}`);", + "if(!(await addOn.deleteConfigOnDatabase(entry.path))){", + " throw new Error(`Could not delete Customisation Sync entry: ${entry.path}`);", + "}", + "return JSON.stringify({ok:true,path:entry.path});", + "})()", + ].join(""), + env + ); +} + async function waitForCustomisationEntry( cliBinary: string, env: NodeJS.ProcessEnv, - filename: string, + category: "CONFIG" | "SNIPPET" | "PLUGIN_MAIN", + name: string, + term?: string, timeoutMs = Number(process.env.E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS ?? 15000) ): Promise { - return await evalObsidianJson( + const entries = await waitForCustomisationEntries(cliBinary, env, category, name, 1, term, timeoutMs); + return entries[0]; +} + +async function waitForCustomisationEntries( + cliBinary: string, + env: NodeJS.ProcessEnv, + category: "CONFIG" | "SNIPPET" | "PLUGIN_MAIN", + name: string, + count: number, + term?: string, + timeoutMs = Number(process.env.E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS ?? 15000) +): Promise { + return await evalObsidianJson( cliBinary, [ "(async()=>{", - `const filename=${JSON.stringify(filename)};`, + `const category=${JSON.stringify(category)};`, + `const name=${JSON.stringify(name)};`, + `const count=${JSON.stringify(count)};`, + `const term=${JSON.stringify(term ?? "")};`, + `const timeoutMs=${JSON.stringify(timeoutMs)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const deadline=Date.now()+timeoutMs;", + "const sleep=(ms)=>new Promise((resolve)=>setTimeout(resolve,ms));", + "let entries=[];", + "while(Date.now()row.doc).filter((doc)=>doc?.path?.includes(`/${category}/`)&&doc.path?.includes(`/${name}%`)&&(!term||doc.path?.startsWith(`ix:${term}/`))&&Array.isArray(doc.children)&&doc.children.length>0);", + " if(entries.length>=count) break;", + " await sleep(250);", + "}", + "if(entries.lengthrow.doc).filter((doc)=>doc?.path?.startsWith('ix:')).map((doc)=>({id:doc._id,path:doc.path,children:doc.children?.length??0}));", + " throw new Error(`Timed out waiting for customisation sync entries: ${category}/${name}; expected=${count}; entries=${JSON.stringify(found)}`);", + "}", + "return JSON.stringify(entries.map((entry)=>({id:entry._id,path:entry.path,children:entry.children||[]})));", + "})()", + ].join(""), + env + ); +} + +async function waitForCustomisationEntryAbsent( + cliBinary: string, + env: NodeJS.ProcessEnv, + category: "CONFIG" | "SNIPPET" | "PLUGIN_MAIN", + name: string, + term?: string, + timeoutMs = Number(process.env.E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS ?? 15000) +): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const category=${JSON.stringify(category)};`, + `const name=${JSON.stringify(name)};`, + `const term=${JSON.stringify(term ?? "")};`, `const timeoutMs=${JSON.stringify(timeoutMs)};`, "const core=app.plugins.plugins['obsidian-livesync'].core;", "const deadline=Date.now()+timeoutMs;", @@ -211,37 +341,36 @@ async function waitForCustomisationEntry( "let entry=false;", "while(Date.now()row.doc).find((doc)=>doc?.path?.includes('/SNIPPET/')&&doc.path?.endsWith(`%${filename}`))||false;", - " if(entry&&entry._id&&Array.isArray(entry.children)&&entry.children.length>0) break;", + " entry=rows.map((row)=>row.doc).find((doc)=>doc?.path?.includes(`/${category}/`)&&doc.path?.includes(`/${name}%`)&&(!term||doc.path?.startsWith(`ix:${term}/`))&&!doc.deleted&&!doc._deleted)||false;", + " if(!entry) return JSON.stringify({ok:true});", " await sleep(250);", "}", - "if(!entry||!entry._id){", - " const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", - " const entries=rows.map((row)=>row.doc).filter((doc)=>doc?.path?.startsWith('ix:')).map((doc)=>({id:doc._id,path:doc.path,children:doc.children?.length??0}));", - " throw new Error(`Timed out waiting for customisation sync entry: ${filename}; entries=${JSON.stringify(entries)}`);", - "}", - "return JSON.stringify({id:entry._id,path:entry.path,children:entry.children||[]});", + "throw new Error(`Timed out waiting for customisation sync entry deletion: ${category}/${name}; entry=${JSON.stringify(entry)}`);", "})()", ].join(""), env ); } -async function applyRemoteCustomisationSnippet( +async function applyRemoteCustomisationEntry( cliBinary: string, env: NodeJS.ProcessEnv, - filename: string + category: "CONFIG" | "SNIPPET" | "PLUGIN_MAIN", + name: string, + term?: string ): Promise { await evalObsidianJson( cliBinary, [ "(async()=>{", - `const filename=${JSON.stringify(filename)};`, + `const category=${JSON.stringify(category)};`, + `const name=${JSON.stringify(name)};`, + `const term=${JSON.stringify(term ?? "")};`, "const core=app.plugins.plugins['obsidian-livesync'].core;", "const addOn=core.getAddOn('ConfigSync');", "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", - "const entry=rows.map((row)=>row.doc).find((doc)=>doc?.path?.includes('/SNIPPET/')&&doc.path?.endsWith(`%${filename}`))||false;", - "if(!entry) throw new Error(`Could not find remote customisation entry: ${filename}`);", + "const entry=rows.map((row)=>row.doc).find((doc)=>doc?.path?.includes(`/${category}/`)&&doc.path?.includes(`/${name}%`)&&(!term||doc.path?.startsWith(`ix:${term}/`)))||false;", + "if(!entry) throw new Error(`Could not find remote customisation entry: ${category}/${name}`);", "const display=addOn.createPluginDataFromV2(entry.path);", "if(!display) throw new Error(`Could not create Customisation Sync display entry: ${entry.path}`);", "const file=await addOn.createPluginDataExFileV2(entry.path);", @@ -257,6 +386,42 @@ async function applyRemoteCustomisationSnippet( ); } +async function applyRemoteCustomisationGroup( + cliBinary: string, + env: NodeJS.ProcessEnv, + category: "PLUGIN_MAIN", + name: string, + term?: string +): Promise { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const category=${JSON.stringify(category)};`, + `const name=${JSON.stringify(name)};`, + `const term=${JSON.stringify(term ?? "")};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "const addOn=core.getAddOn('ConfigSync');", + "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", + "const entries=rows.map((row)=>row.doc).filter((doc)=>doc?.path?.includes(`/${category}/`)&&doc.path?.includes(`/${name}%`)&&(!term||doc.path?.startsWith(`ix:${term}/`)));", + "if(entries.length===0) throw new Error(`Could not find remote customisation entries: ${category}/${name}`);", + "const display=addOn.createPluginDataFromV2(entries[0].path);", + "if(!display) throw new Error(`Could not create Customisation Sync display entry: ${entries[0].path}`);", + "for(const entry of entries){", + " const file=await addOn.createPluginDataExFileV2(entry.path);", + " if(!file) throw new Error(`Could not load Customisation Sync file entry: ${entry.path}`);", + " await display.setFile(file);", + "}", + "if(!(await addOn.applyDataV2(display))){", + " throw new Error(`Could not apply Customisation Sync group: ${category}/${name}`);", + "}", + "return JSON.stringify({ok:true,count:entries.length});", + "})()", + ].join(""), + env + ); +} + async function main(): Promise { const binary = requireObsidianBinary(); const cli = discoverObsidianCli(); @@ -271,6 +436,8 @@ async function main(): Promise { const context: RunnerContext = { binary, cliBinary: cli.binary, couchDb, dbName }; const snippetPathParts = snippetPath.split("/"); const snippetName = snippetPathParts[snippetPathParts.length - 1] ?? snippetPath; + const configName = configPath.split("/").pop() ?? configPath; + const pluginName = pluginDir.split("/").pop() ?? pluginDir; try { await assertCouchDbReachable(couchDb); @@ -282,33 +449,141 @@ async function main(): Promise { console.log(`Temporary CouchDB database: ${dbName}`); await writeVaultFile(vaultA.path, snippetPath, snippetContent); + await writeVaultFile(vaultA.path, configPath, configContent); + await writeVaultFile(vaultA.path, pluginManifestPath, pluginManifestContent); + await writeVaultFile(vaultA.path, pluginMainPath, pluginMainContent); + await writeVaultFile(vaultA.path, pluginStylesPath, pluginStylesContent); - let session = await startConfiguredSession(context, vaultA, "customisation-sync-a"); + let session = await startConfiguredSession(context, vaultA, sourceDeviceName); const scanResult = await scanCustomisations(context.cliBinary, session.cliEnv); console.log(`Customisation scan files: ${scanResult.files.join(", ") || "(none)"}`); - await storeCustomisationSnippet(context.cliBinary, session.cliEnv, snippetPath); - const entry = await waitForCustomisationEntry(context.cliBinary, session.cliEnv, snippetName); + await storeCustomisationFile(context.cliBinary, session.cliEnv, snippetPath); + await storeCustomisationFile(context.cliBinary, session.cliEnv, configPath); + await storeCustomisationFile(context.cliBinary, session.cliEnv, pluginManifestPath); + await storeCustomisationFile(context.cliBinary, session.cliEnv, pluginMainPath); + await storeCustomisationFile(context.cliBinary, session.cliEnv, pluginStylesPath); + const entry = await waitForCustomisationEntry(context.cliBinary, session.cliEnv, "SNIPPET", snippetName); + const configEntry = await waitForCustomisationEntry(context.cliBinary, session.cliEnv, "CONFIG", configName); + const pluginEntries = await waitForCustomisationEntries( + context.cliBinary, + session.cliEnv, + "PLUGIN_MAIN", + pluginName, + 3 + ); await pushLocalChanges(context.cliBinary, session.cliEnv); await waitForCouchDbDocs(context.couchDb, context.dbName, (docs) => { const ids = new Set(docs.map((doc) => doc._id)); - return ids.has(entry.id) && entry.children.every((childId) => ids.has(childId)); + const entries = [entry, configEntry, ...pluginEntries]; + return entries.every( + (target) => ids.has(target.id) && target.children.every((childId) => ids.has(childId)) + ); }); await session.app.stop(); - session = await startConfiguredSession(context, vaultB, "customisation-sync-b"); + session = await startConfiguredSession(context, vaultB, targetDeviceName); await pushLocalChanges(context.cliBinary, session.cliEnv); - await waitForCustomisationEntry(context.cliBinary, session.cliEnv, snippetName); + await waitForCustomisationEntry(context.cliBinary, session.cliEnv, "SNIPPET", snippetName, sourceDeviceName); assertEqual( await pathExists(vaultB.path, snippetPath), false, "Customisation Sync snippet was reflected before explicit application." ); - await applyRemoteCustomisationSnippet(context.cliBinary, session.cliEnv, snippetName); + await applyRemoteCustomisationEntry( + context.cliBinary, + session.cliEnv, + "SNIPPET", + snippetName, + sourceDeviceName + ); const applied = await waitForPathContent(vaultB.path, snippetPath, (content) => content === snippetContent); + await applyRemoteCustomisationEntry(context.cliBinary, session.cliEnv, "CONFIG", configName, sourceDeviceName); + const appliedConfig = await waitForPathContent(vaultB.path, configPath, (content) => content === configContent); + await applyRemoteCustomisationGroup( + context.cliBinary, + session.cliEnv, + "PLUGIN_MAIN", + pluginName, + sourceDeviceName + ); + const appliedPluginManifest = await waitForPathContent( + vaultB.path, + pluginManifestPath, + (content) => content === pluginManifestContent + ); + const appliedPluginMain = await waitForPathContent( + vaultB.path, + pluginMainPath, + (content) => content === pluginMainContent + ); + const appliedPluginStyles = await waitForPathContent( + vaultB.path, + pluginStylesPath, + (content) => content === pluginStylesContent + ); await session.app.stop(); assertEqual(applied, snippetContent, "Customisation Sync snippet content did not match after application."); - console.log(`Customisation Sync applied snippet ${snippetName} from the remote database.`); + assertEqual(appliedConfig, configContent, "Customisation Sync config content did not match after application."); + assertEqual( + appliedPluginManifest, + pluginManifestContent, + "Customisation Sync plug-in manifest did not match after application." + ); + assertEqual(appliedPluginMain, pluginMainContent, "Customisation Sync plug-in main file did not match."); + assertEqual(appliedPluginStyles, pluginStylesContent, "Customisation Sync plug-in stylesheet did not match."); + + await writeVaultFile(vaultA.path, snippetPath, snippetUpdatedContent); + session = await startConfiguredSession(context, vaultA, sourceDeviceName); + await storeCustomisationFile(context.cliBinary, session.cliEnv, snippetPath); + await waitForCustomisationEntry(context.cliBinary, session.cliEnv, "SNIPPET", snippetName); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, targetDeviceName); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await applyRemoteCustomisationEntry( + context.cliBinary, + session.cliEnv, + "SNIPPET", + snippetName, + sourceDeviceName + ); + const updated = await waitForPathContent( + vaultB.path, + snippetPath, + (content) => content === snippetUpdatedContent + ); + await session.app.stop(); + assertEqual(updated, snippetUpdatedContent, "Updated Customisation Sync snippet did not apply."); + + await removeVaultFile(vaultA.path, snippetPath); + session = await startConfiguredSession(context, vaultA, sourceDeviceName); + await deleteCustomisationSyncEntry(context.cliBinary, session.cliEnv, "SNIPPET", snippetName, sourceDeviceName); + await waitForCustomisationEntryAbsent( + context.cliBinary, + session.cliEnv, + "SNIPPET", + snippetName, + sourceDeviceName + ); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, targetDeviceName); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await waitForCustomisationEntryAbsent( + context.cliBinary, + session.cliEnv, + "SNIPPET", + snippetName, + sourceDeviceName + ); + await session.app.stop(); + + console.log( + `Customisation Sync applied snippet, config, and plug-in fixtures, then propagated snippet update and sync-data deletion.` + ); } finally { await vaultA.dispose(); await vaultB.dispose(); From 96396f77f50f0b0efe259a512f2898747e50b479 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 29 Jun 2026 09:11:20 +0000 Subject: [PATCH 11/16] (refactor): genReplication to use StreamInbox and improve event handling --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- src/apps/cli/package.json | 2 +- src/apps/webapp/package.json | 2 +- src/apps/webpeer/package.json | 2 +- src/lib | 2 +- updates.md | 7 +++++++ 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f3d5b4..f7a1f53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "markdown-it": "^14.2.0", "minimatch": "^10.2.5", "obsidian": "^1.13.1", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "qrcode-generator": "^1.4.4", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" }, @@ -11684,9 +11684,9 @@ "license": "MIT" }, "node_modules/octagonal-wheels": { - "version": "0.1.46", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.46.tgz", - "integrity": "sha512-19eB7b/WNNrZ4Xghu93f+NVJsbRiaZaIIzU1rn5shxb6SzwVBoOVkNPJdCAsONl6C1MwjaGDrPUS8CBXvPHjPg==", + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.47.tgz", + "integrity": "sha512-pv+AoplTzDmcrYpxun/N7sab7KAVoPvUhwvnrXwrVihjj8sQ89HmovndpMvGJxdpp4+uOOY4SiO0DVWTp+YCkQ==", "license": "MIT", "dependencies": { "idb": "^8.0.3" @@ -16210,11 +16210,11 @@ }, "src/apps/cli": { "name": "self-hosted-livesync-cli", - "version": "0.25.77-cli", + "version": "0.25.78-cli", "dependencies": { "chokidar": "^4.0.0", "minimatch": "^10.2.5", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "pouchdb-adapter-http": "^9.0.0", "pouchdb-adapter-leveldb": "^9.0.0", "pouchdb-core": "^9.0.0", @@ -16236,9 +16236,9 @@ }, "src/apps/webapp": { "name": "livesync-webapp", - "version": "0.25.77-webapp", + "version": "0.25.78-webapp", "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "@playwright/test": "^1.58.2", @@ -16251,9 +16251,9 @@ } }, "src/apps/webpeer": { - "version": "0.25.77-webpeer", + "version": "0.25.78-webpeer", "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^7.1.2", diff --git a/package.json b/package.json index 0d13b79..18eb126 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "markdown-it": "^14.2.0", "minimatch": "^10.2.5", "obsidian": "^1.13.1", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "qrcode-generator": "^1.4.4", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" }, diff --git a/src/apps/cli/package.json b/src/apps/cli/package.json index 95ebd23..11b4c34 100644 --- a/src/apps/cli/package.json +++ b/src/apps/cli/package.json @@ -41,7 +41,7 @@ "dependencies": { "chokidar": "^4.0.0", "minimatch": "^10.2.5", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "pouchdb-adapter-http": "^9.0.0", "pouchdb-adapter-leveldb": "^9.0.0", "pouchdb-core": "^9.0.0", diff --git a/src/apps/webapp/package.json b/src/apps/webapp/package.json index 23906bd..24ae15c 100644 --- a/src/apps/webapp/package.json +++ b/src/apps/webapp/package.json @@ -12,7 +12,7 @@ "preview": "vite preview" }, "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "@playwright/test": "^1.58.2", diff --git a/src/apps/webpeer/package.json b/src/apps/webpeer/package.json index 573630b..7650175 100644 --- a/src/apps/webpeer/package.json +++ b/src/apps/webpeer/package.json @@ -12,7 +12,7 @@ "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json" }, "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "eslint-plugin-svelte": "^3.19.0", diff --git a/src/lib b/src/lib index 0563f26..43816bb 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 0563f267ecac7b767b446f8528b0a4c9c5f91d25 +Subproject commit 43816bbec152132e170b0f8b7c15ee964ec10eab diff --git a/updates.md b/updates.md index 6d14da5..1d3bb02 100644 --- a/updates.md +++ b/updates.md @@ -3,6 +3,13 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025) The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope. +## Unreleased + +29th June, 2026 + +### Miscellaneous + +- Updated `octagonal-wheels` to `0.1.47` across the plug-in and workspace packages to improve smoothness. ## 0.25.78 From 05877834dc39d792115a5a53485372169a62400e Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 29 Jun 2026 12:01:08 +0100 Subject: [PATCH 12/16] update the pointer --- src/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib b/src/lib index 43816bb..87dc724 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 43816bbec152132e170b0f8b7c15ee964ec10eab +Subproject commit 87dc724c9d74962d63173bf4da3d1aa74fd0c7d4 From 75cb9ece5809117093a01ced9b0ce90b9dcc42d3 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 29 Jun 2026 12:02:33 +0100 Subject: [PATCH 13/16] update the pointer --- src/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib b/src/lib index 17c8c24..87dc724 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 17c8c245bb1d49f2b86b16a73341ed8668eddad6 +Subproject commit 87dc724c9d74962d63173bf4da3d1aa74fd0c7d4 From b568c4b003ddc1192fdc8381f42836ba398ae0ea Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 29 Jun 2026 12:19:56 +0100 Subject: [PATCH 14/16] bump and releasing 0.25.79 --- _types/src/LiveSyncBaseCore.d.ts | 2 +- _types/src/common/KeyValueDB.d.ts | 2 +- _types/src/common/KeyValueDBv2.d.ts | 2 +- _types/src/common/PeriodicProcessor.d.ts | 2 +- _types/src/common/SvelteItemView.d.ts | 2 +- _types/src/common/events.d.ts | 2 +- _types/src/common/obsidianEvents.d.ts | 2 +- _types/src/common/reportTool.d.ts | 2 +- _types/src/common/stores.d.ts | 2 +- _types/src/common/types.d.ts | 2 +- _types/src/common/utils.d.ts | 2 +- _types/src/deps.d.ts | 2 +- .../features/ConfigSync/CmdConfigSync.d.ts | 2 +- .../ConfigSync/PluginDialogModal.d.ts | 2 +- .../HiddenFileCommon/JsonResolveModal.d.ts | 2 +- .../HiddenFileSync/CmdHiddenFileSync.d.ts | 2 +- _types/src/features/LiveSyncCommands.d.ts | 2 +- .../CmdLocalDatabaseMainte.d.ts | 4 ++-- .../maintenancePrerequisites.d.ts | 15 ++++++++++++ .../P2POpenReplicationModal.d.ts | 2 +- .../P2PReplicator/P2PReplicationUI.d.ts | 2 +- .../P2PReplicator/P2PReplicatorPaneView.d.ts | 2 +- .../P2PServerStatusPaneView.d.ts | 2 +- .../lib/src/API/DirectFileManipulator.d.ts | 2 +- _types/src/lib/src/API/processSetting.d.ts | 2 +- .../src/ContentSplitter/ContentSplitter.d.ts | 2 +- .../ContentSplitter/ContentSplitterBase.d.ts | 2 +- .../ContentSplitterRabinKarp.d.ts | 2 +- .../ContentSplitter/ContentSplitterV1.d.ts | 2 +- .../ContentSplitter/ContentSplitterV2.d.ts | 2 +- .../src/ContentSplitter/ContentSplitters.d.ts | 2 +- _types/src/lib/src/UI/svelteDialog.d.ts | 2 +- _types/src/lib/src/bureau/bureau.d.ts | 2 +- .../src/lib/src/common/ConnectionString.d.ts | 2 +- _types/src/lib/src/common/LSError.d.ts | 2 +- _types/src/lib/src/common/configForDoc.d.ts | 2 +- .../src/lib/src/common/coreEnvFunctions.d.ts | 2 +- _types/src/lib/src/common/coreEnvVars.d.ts | 2 +- _types/src/lib/src/common/i18n.d.ts | 2 +- _types/src/lib/src/common/logger.d.ts | 2 +- .../common/messages/combinedMessages.dev.d.ts | 2 +- .../messages/combinedMessages.prod.d.ts | 2 +- _types/src/lib/src/common/messages/de.d.ts | 2 +- _types/src/lib/src/common/messages/def.d.ts | 2 +- _types/src/lib/src/common/messages/es.d.ts | 2 +- _types/src/lib/src/common/messages/fr.d.ts | 2 +- _types/src/lib/src/common/messages/he.d.ts | 2 +- _types/src/lib/src/common/messages/ja.d.ts | 2 +- _types/src/lib/src/common/messages/ko.d.ts | 2 +- _types/src/lib/src/common/messages/ru.d.ts | 2 +- _types/src/lib/src/common/messages/zh-tw.d.ts | 2 +- _types/src/lib/src/common/messages/zh.d.ts | 2 +- .../src/lib/src/common/models/auth.type.d.ts | 2 +- .../src/lib/src/common/models/db.const.d.ts | 2 +- .../lib/src/common/models/db.definition.d.ts | 2 +- _types/src/lib/src/common/models/db.type.d.ts | 2 +- .../src/common/models/diff.definition.d.ts | 2 +- .../src/common/models/fileaccess.const.d.ts | 2 +- .../src/common/models/fileaccess.type.d.ts | 2 +- .../lib/src/common/models/redflag.const.d.ts | 2 +- .../lib/src/common/models/setting.const.d.ts | 2 +- .../common/models/setting.const.defaults.d.ts | 2 +- .../models/setting.const.preferred.d.ts | 2 +- .../src/common/models/setting.const.qr.d.ts | 2 +- .../lib/src/common/models/setting.type.d.ts | 2 +- .../common/models/shared.const.behabiour.d.ts | 2 +- .../lib/src/common/models/shared.const.d.ts | 2 +- .../common/models/shared.const.symbols.d.ts | 2 +- .../models/shared.definition.configNames.d.ts | 2 +- .../src/common/models/shared.definition.d.ts | 2 +- .../src/common/models/shared.type.util.d.ts | 2 +- .../src/common/models/sync.definition.d.ts | 2 +- .../src/common/models/tweak.definition.d.ts | 2 +- _types/src/lib/src/common/rosetta.d.ts | 2 +- .../src/lib/src/common/settingConstants.d.ts | 2 +- _types/src/lib/src/common/typeUtils.d.ts | 2 +- _types/src/lib/src/common/types.d.ts | 2 +- _types/src/lib/src/common/utils.d.ts | 2 +- _types/src/lib/src/common/utils.doc.d.ts | 2 +- _types/src/lib/src/common/utils.object.d.ts | 2 +- _types/src/lib/src/common/utils.patch.d.ts | 2 +- _types/src/lib/src/common/utils.type.d.ts | 2 +- _types/src/lib/src/dataobject/StoredMap.d.ts | 2 +- _types/src/lib/src/dev/checks.d.ts | 2 +- .../src/lib/src/encryption/encryptHKDF.d.ts | 2 +- .../lib/src/encryption/stringEncryption.d.ts | 2 +- _types/src/lib/src/events/coreEvents.d.ts | 2 +- _types/src/lib/src/hub/hub.d.ts | 2 +- _types/src/lib/src/index.d.ts | 2 +- _types/src/lib/src/interfaces/Confirm.d.ts | 2 +- .../src/interfaces/DatabaseFileAccess.d.ts | 2 +- .../lib/src/interfaces/DatabaseRebuilder.d.ts | 2 +- .../src/lib/src/interfaces/FileHandler.d.ts | 2 +- .../lib/src/interfaces/KeyValueDatabase.d.ts | 2 +- .../src/lib/src/interfaces/ServiceModule.d.ts | 2 +- .../src/lib/src/interfaces/StorageAccess.d.ts | 2 +- .../src/interfaces/StorageEventManager.d.ts | 2 +- .../src/lib/src/managers/ChangeManager.d.ts | 2 +- _types/src/lib/src/managers/ChunkFetcher.d.ts | 2 +- _types/src/lib/src/managers/ChunkManager.d.ts | 2 +- .../src/lib/src/managers/ConflictManager.d.ts | 2 +- .../managers/EntryManager/EntryManager.d.ts | 2 +- .../EntryManager/EntryManagerImpls.d.ts | 2 +- .../src/managers/HashManager/HashManager.d.ts | 2 +- .../managers/HashManager/HashManagerCore.d.ts | 2 +- .../HashManager/PureJSHashManager.d.ts | 2 +- .../HashManager/XXHashHashManager.d.ts | 2 +- .../lib/src/managers/LayeredChunkManager.d.ts | 2 +- .../LayeredChunkManager/ArrivalWaitLayer.d.ts | 2 +- .../LayeredChunkManager/CacheLayer.d.ts | 2 +- .../ChunkLayerInterfaces.d.ts | 2 +- .../DatabaseReadLayer.d.ts | 2 +- .../DatabaseWriteLayer.d.ts | 2 +- .../LayeredChunkManager/HotPackLayer.d.ts | 2 +- .../managers/LayeredChunkManager/types.d.ts | 2 +- .../lib/src/managers/LiveSyncManagers.d.ts | 2 +- .../lib/src/managers/StorageEventManager.d.ts | 2 +- .../managers/StorageProcessingManager.d.ts | 2 +- .../IStorageEventConverterAdapter.d.ts | 2 +- .../adapters/IStorageEventManagerAdapter.d.ts | 2 +- .../IStorageEventPersistenceAdapter.d.ts | 2 +- .../adapters/IStorageEventStatusAdapter.d.ts | 2 +- .../IStorageEventTypeGuardAdapter.d.ts | 2 +- .../adapters/IStorageEventWatchAdapter.d.ts | 2 +- .../src/lib/src/managers/adapters/index.d.ts | 2 +- .../src/lib/src/mock_and_interop/stores.d.ts | 2 +- .../src/lib/src/mock_and_interop/wrapper.d.ts | 2 +- _types/src/lib/src/mods.d.ts | 2 +- .../lib/src/pouchdb/LiveSyncDBFunctions.d.ts | 2 +- .../src/lib/src/pouchdb/LiveSyncLocalDB.d.ts | 2 +- .../src/lib/src/pouchdb/ReplicatorShim.d.ts | 2 +- .../src/lib/src/pouchdb/StreamingFetch.d.ts | 2 +- .../StreamingFetch.integration.spec.d.ts | 2 +- _types/src/lib/src/pouchdb/chunks.d.ts | 2 +- _types/src/lib/src/pouchdb/compress.d.ts | 2 +- _types/src/lib/src/pouchdb/encryption.d.ts | 2 +- _types/src/lib/src/pouchdb/negotiation.d.ts | 2 +- .../src/lib/src/pouchdb/pouchdb-browser.d.ts | 2 +- _types/src/lib/src/pouchdb/pouchdb-http.d.ts | 2 +- _types/src/lib/src/pouchdb/pouchdb-test.d.ts | 2 +- _types/src/lib/src/pouchdb/utils_couchdb.d.ts | 2 +- .../LiveSyncAbstractReplicator.d.ts | 2 +- .../src/replication/SyncParamsHandler.d.ts | 2 +- .../couchdb/LiveSyncReplicator.d.ts | 2 +- _types/src/lib/src/replication/httplib.d.ts | 2 +- .../replication/journal/JournalSyncCore.d.ts | 2 +- .../replication/journal/JournalSyncTypes.d.ts | 2 +- .../journal/LiveSyncJournalReplicator.d.ts | 2 +- .../journal/LiveSyncJournalReplicatorEnv.d.ts | 2 +- .../objectstore/JournalStorageAdapter.d.ts | 2 +- .../objectstore/MinioStorageAdapter.d.ts | 2 +- .../MinioStorageAdapter.integration.spec.d.ts | 2 +- .../trystero/LiveSyncTrysteroReplicator.d.ts | 2 +- .../replication/trystero/P2PLogCollector.d.ts | 2 +- .../trystero/P2PReplicatorBase.d.ts | 2 +- .../trystero/P2PReplicatorCore.d.ts | 2 +- .../trystero/P2PReplicatorPaneCommon.d.ts | 2 +- .../src/replication/trystero/ProxiedDB.d.ts | 2 +- .../trystero/TrysteroReplicator.d.ts | 2 +- .../trystero/TrysteroReplicatorP2PClient.d.ts | 2 +- .../TrysteroReplicatorP2PConnection.d.ts | 2 +- .../trystero/TrysteroReplicatorP2PServer.d.ts | 2 +- .../trystero/UseP2PReplicatorResult.d.ts | 2 +- .../trystero/addP2PEventHandlers.d.ts | 2 +- .../src/replication/trystero/rpcCompat.d.ts | 2 +- .../lib/src/replication/trystero/types.d.ts | 2 +- .../trystero/useP2PReplicatorCommands.d.ts | 2 +- .../trystero/useP2PReplicatorFeature.d.ts | 2 +- _types/src/lib/src/rpc/RpcRoom.d.ts | 2 +- _types/src/lib/src/rpc/RpcSession.d.ts | 2 +- _types/src/lib/src/rpc/chunking.d.ts | 2 +- _types/src/lib/src/rpc/errors.d.ts | 2 +- _types/src/lib/src/rpc/index.d.ts | 2 +- .../lib/src/rpc/pouchdb/RpcPouchDBProxy.d.ts | 2 +- .../lib/src/rpc/pouchdb/RpcPouchDBServer.d.ts | 2 +- .../transports/DiagRTCPeerConnections.d.ts | 2 +- .../DiagRTCPeerConnections.types.d.ts | 2 +- .../DiagRTCPeerConnections.utils.d.ts | 2 +- .../src/rpc/transports/TrysteroTransport.d.ts | 2 +- .../lib/src/rpc/transports/trysteroUtils.d.ts | 2 +- _types/src/lib/src/rpc/types.d.ts | 2 +- .../src/serviceFeatures/checkRemoteSize.d.ts | 2 +- .../src/serviceFeatures/offlineScanner.d.ts | 2 +- .../prepareDatabaseForUse.d.ts | 2 +- .../lib/src/serviceFeatures/remoteConfig.d.ts | 2 +- .../serviceFeatures/setupObsidian/qrCode.d.ts | 2 +- .../setupObsidian/setupUri.d.ts | 2 +- .../serviceFeatures/setupObsidian/types.d.ts | 2 +- .../lib/src/serviceFeatures/targetFilter.d.ts | 2 +- .../src/serviceModules/FileAccessBase.d.ts | 2 +- .../src/lib/src/serviceModules/Rebuilder.d.ts | 2 +- .../ServiceDatabaseFileAccessBase.d.ts | 2 +- .../serviceModules/ServiceFileAccessBase.d.ts | 2 +- .../ServiceFileHandlerBase.d.ts | 2 +- .../src/serviceModules/ServiceModuleBase.d.ts | 2 +- .../adapters/IConversionAdapter.d.ts | 2 +- .../adapters/IFileSystemAdapter.d.ts | 2 +- .../serviceModules/adapters/IPathAdapter.d.ts | 2 +- .../adapters/IStorageAdapter.d.ts | 2 +- .../adapters/ITypeGuardAdapter.d.ts | 2 +- .../adapters/IVaultAdapter.d.ts | 2 +- .../src/serviceModules/adapters/index.d.ts | 2 +- .../src/lib/src/services/BrowserServices.d.ts | 2 +- .../lib/src/services/HeadlessServices.d.ts | 2 +- .../lib/src/services/InjectableServices.d.ts | 2 +- _types/src/lib/src/services/ServiceHub.d.ts | 2 +- .../src/lib/src/services/base/APIService.d.ts | 2 +- .../services/base/AppLifecycleService.d.ts | 2 +- .../lib/src/services/base/ConfigService.d.ts | 2 +- .../src/services/base/ConflictService.d.ts | 2 +- .../lib/src/services/base/ControlService.d.ts | 2 +- .../services/base/DatabaseEventService.d.ts | 2 +- .../src/services/base/DatabaseService.d.ts | 2 +- .../services/base/FileProcessingService.d.ts | 2 +- .../src/lib/src/services/base/IService.d.ts | 2 +- .../src/services/base/KeyValueDBService.d.ts | 2 +- .../lib/src/services/base/PathService.d.ts | 2 +- .../lib/src/services/base/RemoteService.d.ts | 2 +- .../src/services/base/ReplicationService.d.ts | 2 +- .../src/services/base/ReplicatorService.d.ts | 2 +- .../lib/src/services/base/ServiceBase.d.ts | 2 +- .../lib/src/services/base/SettingService.d.ts | 2 +- .../lib/src/services/base/TestService.d.ts | 2 +- .../src/services/base/TweakValueService.d.ts | 2 +- .../services/base/UnresolvedErrorManager.d.ts | 2 +- .../lib/src/services/base/VaultService.d.ts | 2 +- .../services/implements/base/UIService.d.ts | 2 +- .../implements/browser/BrowserAPIService.d.ts | 2 +- .../implements/browser/BrowserConfirm.d.ts | 2 +- .../browser/BrowserDatabaseService.d.ts | 2 +- .../implements/browser/BrowserUIService.d.ts | 2 +- .../browser/ConfigServiceBrowserCompat.d.ts | 2 +- .../src/services/implements/browser/Menu.d.ts | 2 +- .../browser/ui/renderMessageMarkdown.d.ts | 2 +- .../headless/HeadlessAPIService.d.ts | 2 +- .../headless/HeadlessDatabaseService.d.ts | 2 +- .../injectable/InjectableAPIService.d.ts | 2 +- .../InjectableAppLifecycleService.d.ts | 2 +- .../injectable/InjectableConflictService.d.ts | 2 +- .../InjectableDatabaseEventService.d.ts | 2 +- .../InjectableFileProcessingService.d.ts | 2 +- .../injectable/InjectablePathService.d.ts | 2 +- .../injectable/InjectableRemoteService.d.ts | 2 +- .../InjectableReplicationService.d.ts | 2 +- .../InjectableReplicatorService.d.ts | 2 +- .../injectable/InjectableServiceHub.d.ts | 2 +- .../injectable/InjectableServices.d.ts | 2 +- .../injectable/InjectableSettingService.d.ts | 2 +- .../injectable/InjectableTestService.d.ts | 2 +- .../InjectableTweakValueService.d.ts | 2 +- .../injectable/InjectableVaultService.d.ts | 2 +- .../obsidian/ObsidianServiceContext.d.ts | 2 +- .../lib/src/services/lib/HandlerUtils.d.ts | 2 +- _types/src/lib/src/services/lib/logUtils.d.ts | 2 +- .../src/lib/src/string_and_binary/chunks.d.ts | 2 +- .../lib/src/string_and_binary/convert.d.ts | 2 +- .../src/lib/src/string_and_binary/hash.d.ts | 2 +- .../src/lib/src/string_and_binary/path.d.ts | 2 +- _types/src/lib/src/system/wakelock.d.ts | 2 +- _types/src/lib/src/worker/bg.common.d.ts | 2 +- _types/src/lib/src/worker/bg.worker.d.ts | 2 +- .../lib/src/worker/bg.worker.encryption.d.ts | 2 +- .../lib/src/worker/bg.worker.splitting.d.ts | 2 +- _types/src/lib/src/worker/bgWorker.d.ts | 2 +- .../lib/src/worker/bgWorker.encryption.d.ts | 2 +- _types/src/lib/src/worker/bgWorker.mock.d.ts | 2 +- .../lib/src/worker/bgWorker.splitting.d.ts | 2 +- _types/src/lib/src/worker/universalTypes.d.ts | 2 +- _types/src/main.d.ts | 2 +- .../ObsidianStorageEventManagerAdapter.d.ts | 2 +- .../managers/StorageEventManagerObsidian.d.ts | 2 +- _types/src/modules/AbstractModule.d.ts | 2 +- .../src/modules/AbstractObsidianModule.d.ts | 2 +- .../modules/core/ModulePeriodicProcess.d.ts | 2 +- _types/src/modules/core/ModuleReplicator.d.ts | 2 +- .../modules/core/ModuleReplicatorCouchDB.d.ts | 2 +- .../modules/core/ModuleReplicatorMinIO.d.ts | 2 +- .../core/ReplicateResultProcessor.d.ts | 2 +- .../coreFeatures/ModuleConflictChecker.d.ts | 2 +- .../coreFeatures/ModuleConflictResolver.d.ts | 2 +- .../ModuleResolveMismatchedTweaks.d.ts | 2 +- .../modules/coreObsidian/UILib/dialogs.d.ts | 2 +- .../coreObsidian/storageLib/utilObsidian.d.ts | 2 +- .../modules/essential/ModuleBasicMenu.d.ts | 2 +- .../modules/essential/ModuleMigration.d.ts | 2 +- .../APILib/ObsHttpHandler.d.ts | 2 +- .../ModuleObsidianEvents.d.ts | 2 +- .../essentialObsidian/ModuleObsidianMenu.d.ts | 2 +- .../DocumentHistory/DocumentHistoryModal.d.ts | 2 +- .../GlobalHistory/GlobalHistoryView.d.ts | 2 +- .../ConflictResolveModal.d.ts | 2 +- .../src/modules/features/Log/LogPaneView.d.ts | 2 +- .../modules/features/ModuleGlobalHistory.d.ts | 2 +- .../ModuleInteractiveConflictResolver.d.ts | 2 +- _types/src/modules/features/ModuleLog.d.ts | 2 +- .../ModuleObsidianDocumentHistory.d.ts | 2 +- .../ModuleObsidianSettingAsMarkdown.d.ts | 2 +- .../features/ModuleObsidianSettingTab.d.ts | 2 +- .../SettingDialogue/LiveSyncSetting.d.ts | 2 +- .../ObsidianLiveSyncSettingTab.d.ts | 2 +- .../SettingDialogue/PaneAdvanced.d.ts | 2 +- .../SettingDialogue/PaneChangeLog.d.ts | 2 +- .../PaneCustomisationSync.d.ts | 2 +- .../features/SettingDialogue/PaneGeneral.d.ts | 2 +- .../features/SettingDialogue/PaneHatch.d.ts | 2 +- .../SettingDialogue/PaneMaintenance.d.ts | 2 +- .../features/SettingDialogue/PanePatches.d.ts | 2 +- .../SettingDialogue/PanePowerUsers.d.ts | 2 +- .../SettingDialogue/PaneRemoteConfig.d.ts | 2 +- .../SettingDialogue/PaneSelector.d.ts | 2 +- .../features/SettingDialogue/PaneSetup.d.ts | 2 +- .../SettingDialogue/PaneSyncSettings.d.ts | 2 +- .../features/SettingDialogue/SettingPane.d.ts | 2 +- .../features/SettingDialogue/SveltePanel.d.ts | 2 +- .../SettingDialogue/remoteConfigBuffer.d.ts | 2 +- .../SettingDialogue/settingConstants.d.ts | 2 +- .../SettingDialogue/settingUtils.d.ts | 2 +- _types/src/modules/features/SetupManager.d.ts | 2 +- .../SetupWizard/dialogs/setupDialogTypes.d.ts | 2 +- .../src/modules/main/ModuleLiveSyncMain.d.ts | 2 +- .../modules/services/ObsidianAPIService.d.ts | 2 +- .../services/ObsidianAppLifecycleService.d.ts | 2 +- .../src/modules/services/ObsidianConfirm.d.ts | 2 +- .../services/ObsidianDatabaseService.d.ts | 2 +- .../modules/services/ObsidianPathService.d.ts | 2 +- .../modules/services/ObsidianServiceHub.d.ts | 2 +- .../modules/services/ObsidianServices.d.ts | 2 +- .../services/ObsidianSettingService.d.ts | 2 +- .../modules/services/ObsidianUIService.d.ts | 2 +- .../services/ObsidianVaultService.d.ts | 2 +- .../onLayoutReady/enablei18n.d.ts | 2 +- _types/src/serviceFeatures/redFlag.d.ts | 2 +- .../serviceFeatures/redFlag.simpleFetch.d.ts | 4 ++-- .../setupObsidian/setupManagerHandlers.d.ts | 2 +- .../setupObsidian/setupProtocol.d.ts | 2 +- .../serviceFeatures/useP2PReplicatorUI.d.ts | 2 +- .../serviceModules/DatabaseFileAccess.d.ts | 2 +- .../serviceModules/FileAccessObsidian.d.ts | 2 +- _types/src/serviceModules/FileHandler.d.ts | 2 +- .../ObsidianConversionAdapter.d.ts | 2 +- .../ObsidianFileSystemAdapter.d.ts | 2 +- .../ObsidianPathAdapter.d.ts | 2 +- .../ObsidianStorageAdapter.d.ts | 2 +- .../ObsidianTypeGuardAdapter.d.ts | 2 +- .../ObsidianVaultAdapter.d.ts | 2 +- .../serviceModules/ServiceFileAccessImpl.d.ts | 2 +- _types/src/types.d.ts | 2 +- manifest.json | 2 +- package-lock.json | 24 +++++++++---------- package.json | 4 ++-- src/apps/cli/package.json | 4 ++-- src/apps/webapp/package.json | 4 ++-- src/apps/webpeer/package.json | 4 ++-- updates.md | 23 ++++++++++++++++++ 354 files changed, 407 insertions(+), 369 deletions(-) create mode 100644 _types/src/features/LocalDatabaseMainte/maintenancePrerequisites.d.ts diff --git a/_types/src/LiveSyncBaseCore.d.ts b/_types/src/LiveSyncBaseCore.d.ts index 261f9ab..3178d8f 100644 --- a/_types/src/LiveSyncBaseCore.d.ts +++ b/_types/src/LiveSyncBaseCore.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase"; import type { HasSettings, ObsidianLiveSyncSettings, EntryDoc } from "@lib/common/types"; import type { Confirm } from "@lib/interfaces/Confirm"; diff --git a/_types/src/common/KeyValueDB.d.ts b/_types/src/common/KeyValueDB.d.ts index cec2fb4..1239384 100644 --- a/_types/src/common/KeyValueDB.d.ts +++ b/_types/src/common/KeyValueDB.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { KeyValueDatabase } from "@lib/interfaces/KeyValueDatabase.ts"; export { OpenKeyValueDatabase } from "./KeyValueDBv2.ts"; export declare const _OpenKeyValueDatabase: (dbKey: string) => Promise; diff --git a/_types/src/common/KeyValueDBv2.d.ts b/_types/src/common/KeyValueDBv2.d.ts index 25659ae..b78a9c0 100644 --- a/_types/src/common/KeyValueDBv2.d.ts +++ b/_types/src/common/KeyValueDBv2.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { KeyValueDatabase } from "@lib/interfaces/KeyValueDatabase"; import { type IDBPDatabase } from "idb"; export declare function OpenKeyValueDatabase(dbKey: string): Promise; diff --git a/_types/src/common/PeriodicProcessor.d.ts b/_types/src/common/PeriodicProcessor.d.ts index cf532e0..ae709a6 100644 --- a/_types/src/common/PeriodicProcessor.d.ts +++ b/_types/src/common/PeriodicProcessor.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; type PeriodicProcessorHost = NecessaryServices<"API" | "control", never>; export declare class PeriodicProcessor { diff --git a/_types/src/common/SvelteItemView.d.ts b/_types/src/common/SvelteItemView.d.ts index 7b94118..4a3a995 100644 --- a/_types/src/common/SvelteItemView.d.ts +++ b/_types/src/common/SvelteItemView.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ItemView } from "@/deps.ts"; import { type mount } from "svelte"; export declare abstract class SvelteItemView extends ItemView { diff --git a/_types/src/common/events.d.ts b/_types/src/common/events.d.ts index f702518..1c8a4e3 100644 --- a/_types/src/common/events.d.ts +++ b/_types/src/common/events.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { eventHub } from "@lib/hub/hub"; export declare const EVENT_PLUGIN_LOADED = "plugin-loaded"; export declare const EVENT_PLUGIN_UNLOADED = "plugin-unloaded"; diff --git a/_types/src/common/obsidianEvents.d.ts b/_types/src/common/obsidianEvents.d.ts index 370e5d3..6b21e4a 100644 --- a/_types/src/common/obsidianEvents.d.ts +++ b/_types/src/common/obsidianEvents.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { TFile } from "@/deps"; import type { FilePathWithPrefix, LoadedEntry } from "@lib/common/types"; export declare const EVENT_REQUEST_SHOW_HISTORY = "show-history"; diff --git a/_types/src/common/reportTool.d.ts b/_types/src/common/reportTool.d.ts index caeca16..02bc63a 100644 --- a/_types/src/common/reportTool.d.ts +++ b/_types/src/common/reportTool.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettings } from "@lib/common/models/setting.type"; import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore"; export declare function generateReport(settings: ObsidianLiveSyncSettings, core: LiveSyncBaseCore): Promise<{ diff --git a/_types/src/common/stores.d.ts b/_types/src/common/stores.d.ts index 22db32d..92a06eb 100644 --- a/_types/src/common/stores.d.ts +++ b/_types/src/common/stores.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { PersistentMap } from "octagonal-wheels/dataobject/PersistentMap"; export declare let sameChangePairs: PersistentMap; export declare function initializeStores(vaultName: string): void; diff --git a/_types/src/common/types.d.ts b/_types/src/common/types.d.ts index 494b8fb..05555b3 100644 --- a/_types/src/common/types.d.ts +++ b/_types/src/common/types.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type PluginManifest, TFile } from "@/deps.ts"; import { type DatabaseEntry, type EntryBody, type FilePath } from "@lib/common/types.ts"; export type { CacheData, FileEventItem } from "@lib/common/types.ts"; diff --git a/_types/src/common/utils.d.ts b/_types/src/common/utils.d.ts index 20024aa..61369a2 100644 --- a/_types/src/common/utils.d.ts +++ b/_types/src/common/utils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { TAbstractFile } from "@/deps.ts"; import { type AnyEntry, type CouchDBCredentials, type DocumentID, type EntryHasPath, type FilePath, type FilePathWithPrefix, type UXFileInfo, type UXFileInfoStub } from "@lib/common/types.ts"; export { ICHeader, ICXHeader } from "./types.ts"; diff --git a/_types/src/deps.d.ts b/_types/src/deps.d.ts index c4318c9..cf4a90c 100644 --- a/_types/src/deps.d.ts +++ b/_types/src/deps.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type FilePath } from "@lib/common/types.ts"; export { addIcon, App, debounce, Editor, FuzzySuggestModal, MarkdownRenderer, MarkdownView, Modal, Notice, Platform, Plugin, PluginSettingTab, requestUrl, sanitizeHTMLToDom, Setting, stringifyYaml, TAbstractFile, TextAreaComponent, TFile, TFolder, parseYaml, ItemView, WorkspaceLeaf, Menu, request, getLanguage, ButtonComponent, TextComponent, ToggleComponent, DropdownComponent, Component, } from "obsidian"; export type { DataWriteOptions, PluginManifest, RequestUrlParam, RequestUrlResponse, MarkdownFileInfo, ListedFiles, ValueComponent, Stat, Command, ViewCreator, } from "obsidian"; diff --git a/_types/src/features/ConfigSync/CmdConfigSync.d.ts b/_types/src/features/ConfigSync/CmdConfigSync.d.ts index 0420488..a932305 100644 --- a/_types/src/features/ConfigSync/CmdConfigSync.d.ts +++ b/_types/src/features/ConfigSync/CmdConfigSync.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type PluginManifest } from "@/deps.ts"; import type { EntryDoc, LoadedEntry, FilePathWithPrefix, FilePath, AnyEntry } from "@lib/common/types.ts"; import { LiveSyncCommands } from "@/features/LiveSyncCommands.ts"; diff --git a/_types/src/features/ConfigSync/PluginDialogModal.d.ts b/_types/src/features/ConfigSync/PluginDialogModal.d.ts index 7cd5017..66df48f 100644 --- a/_types/src/features/ConfigSync/PluginDialogModal.d.ts +++ b/_types/src/features/ConfigSync/PluginDialogModal.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { mount } from "svelte"; import { App, Modal } from "@/deps.ts"; import ObsidianLiveSyncPlugin from "@/main.ts"; diff --git a/_types/src/features/HiddenFileCommon/JsonResolveModal.d.ts b/_types/src/features/HiddenFileCommon/JsonResolveModal.d.ts index fe51ae3..5517b60 100644 --- a/_types/src/features/HiddenFileCommon/JsonResolveModal.d.ts +++ b/_types/src/features/HiddenFileCommon/JsonResolveModal.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { App, Modal } from "@/deps.ts"; import { type FilePath, type LoadedEntry } from "@lib/common/types.ts"; import { mount } from "svelte"; diff --git a/_types/src/features/HiddenFileSync/CmdHiddenFileSync.d.ts b/_types/src/features/HiddenFileSync/CmdHiddenFileSync.d.ts index 0b52bfc..876957b 100644 --- a/_types/src/features/HiddenFileSync/CmdHiddenFileSync.d.ts +++ b/_types/src/features/HiddenFileSync/CmdHiddenFileSync.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type LoadedEntry, type FilePathWithPrefix, type FilePath, type DocumentID, type UXFileInfo, type UXStat, type MetaEntry, type UXDataWriteOptions } from "@lib/common/types.ts"; import { type InternalFileInfo } from "@/common/types.ts"; import { type CustomRegExp } from "@lib/common/utils.ts"; diff --git a/_types/src/features/LiveSyncCommands.d.ts b/_types/src/features/LiveSyncCommands.d.ts index 25764c6..a20f2ff 100644 --- a/_types/src/features/LiveSyncCommands.d.ts +++ b/_types/src/features/LiveSyncCommands.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type AnyEntry, type DocumentID, type FilePath, type FilePathWithPrefix, type LOG_LEVEL } from "@lib/common/types.ts"; import type ObsidianLiveSyncPlugin from "@/main.ts"; import type { LiveSyncCore } from "@/main.ts"; diff --git a/_types/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.d.ts b/_types/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.d.ts index 6bb2f06..bbcadf4 100644 --- a/_types/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.d.ts +++ b/_types/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type DocumentID, type EntryDoc, type EntryLeaf } from "@lib/common/types"; import { LiveSyncCommands } from "@/features/LiveSyncCommands"; type ChunkID = DocumentID; @@ -16,7 +16,7 @@ export declare class LocalDatabaseMaintenance extends LiveSyncCommands { get database(): PouchDB.Database; clearHash(): void; confirm(title: string, message: string, affirmative?: string, negative?: string): Promise; - isAvailable(): boolean; + ensureAvailable(operationName: string): Promise; /** * Resurrect deleted chunks that are still used in the database. */ diff --git a/_types/src/features/LocalDatabaseMainte/maintenancePrerequisites.d.ts b/_types/src/features/LocalDatabaseMainte/maintenancePrerequisites.d.ts new file mode 100644 index 0000000..39ebaf5 --- /dev/null +++ b/_types/src/features/LocalDatabaseMainte/maintenancePrerequisites.d.ts @@ -0,0 +1,15 @@ +// @ts-nocheck +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 +import type { ObsidianLiveSyncSettings } from "@lib/common/types"; +type MaintenancePrerequisiteSettings = Pick; +type MaintenancePrerequisiteOptions = { + operationName: string; + settings: MaintenancePrerequisiteSettings; + askSelectStringDialogue: (message: string, buttons: readonly ["Apply and continue", "Cancel"], options: { + title: string; + defaultAction: "Cancel"; + }) => Promise<"Apply and continue" | "Cancel" | false | undefined>; + applyPartial: (settings: Partial, saveImmediately?: boolean) => Promise; +}; +export declare function ensureLocalDatabaseMaintenancePrerequisites({ operationName, settings, askSelectStringDialogue, applyPartial, }: MaintenancePrerequisiteOptions): Promise; +export {}; diff --git a/_types/src/features/P2PSync/P2PReplicator/P2POpenReplicationModal.d.ts b/_types/src/features/P2PSync/P2PReplicator/P2POpenReplicationModal.d.ts index 256ac74..84bdcc3 100644 --- a/_types/src/features/P2PSync/P2PReplicator/P2POpenReplicationModal.d.ts +++ b/_types/src/features/P2PSync/P2PReplicator/P2POpenReplicationModal.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { App, Modal } from "@/deps.ts"; import { mount } from "svelte"; import type { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator"; diff --git a/_types/src/features/P2PSync/P2PReplicator/P2PReplicationUI.d.ts b/_types/src/features/P2PSync/P2PReplicator/P2PReplicationUI.d.ts index 67e3f61..7e00078 100644 --- a/_types/src/features/P2PSync/P2PReplicator/P2PReplicationUI.d.ts +++ b/_types/src/features/P2PSync/P2PReplicator/P2PReplicationUI.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { App } from "@/deps.ts"; import type { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator"; /** diff --git a/_types/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.d.ts b/_types/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.d.ts index 25feae5..20d313d 100644 --- a/_types/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.d.ts +++ b/_types/src/features/P2PSync/P2PReplicator/P2PReplicatorPaneView.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { Menu, WorkspaceLeaf } from "@/deps.ts"; import { SvelteItemView } from "@/common/SvelteItemView.ts"; import { type PeerStatus } from "@lib/replication/trystero/P2PReplicatorPaneCommon.ts"; diff --git a/_types/src/features/P2PSync/P2PReplicator/P2PServerStatusPaneView.d.ts b/_types/src/features/P2PSync/P2PReplicator/P2PServerStatusPaneView.d.ts index 6df7e3d..21bd322 100644 --- a/_types/src/features/P2PSync/P2PReplicator/P2PServerStatusPaneView.d.ts +++ b/_types/src/features/P2PSync/P2PReplicator/P2PServerStatusPaneView.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { WorkspaceLeaf } from "@/deps.ts"; import { SvelteItemView } from "@/common/SvelteItemView.ts"; import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore.ts"; diff --git a/_types/src/lib/src/API/DirectFileManipulator.d.ts b/_types/src/lib/src/API/DirectFileManipulator.d.ts index 1fd4b86..84afb13 100644 --- a/_types/src/lib/src/API/DirectFileManipulator.d.ts +++ b/_types/src/lib/src/API/DirectFileManipulator.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export { DirectFileManipulator } from "./DirectFileManipulatorV2.ts"; export type { DirectFileManipulatorOptions } from "./DirectFileManipulatorV2.ts"; diff --git a/_types/src/lib/src/API/processSetting.d.ts b/_types/src/lib/src/API/processSetting.d.ts index a4e66a9..921fbcd 100644 --- a/_types/src/lib/src/API/processSetting.d.ts +++ b/_types/src/lib/src/API/processSetting.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ObsidianLiveSyncSettings } from "@lib/common/types"; /** * Encode settings to a tiny array to encode in QRCode, diff --git a/_types/src/lib/src/ContentSplitter/ContentSplitter.d.ts b/_types/src/lib/src/ContentSplitter/ContentSplitter.d.ts index 970d8bd..ba8dfb8 100644 --- a/_types/src/lib/src/ContentSplitter/ContentSplitter.d.ts +++ b/_types/src/lib/src/ContentSplitter/ContentSplitter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** * Content-Splitter for Self-hosted LiveSync. * Splits content into manageable chunks for efficient storage and synchronisation. diff --git a/_types/src/lib/src/ContentSplitter/ContentSplitterBase.d.ts b/_types/src/lib/src/ContentSplitter/ContentSplitterBase.d.ts index 069f34c..9001ee3 100644 --- a/_types/src/lib/src/ContentSplitter/ContentSplitterBase.d.ts +++ b/_types/src/lib/src/ContentSplitter/ContentSplitterBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type SavingEntry } from "@lib/common/types.ts"; import { type ContentSplitterOptions, type SplitOptions } from "./ContentSplitter.ts"; export declare abstract class ContentSplitterCore { diff --git a/_types/src/lib/src/ContentSplitter/ContentSplitterRabinKarp.d.ts b/_types/src/lib/src/ContentSplitter/ContentSplitterRabinKarp.d.ts index 4dbed17..edbca3c 100644 --- a/_types/src/lib/src/ContentSplitter/ContentSplitterRabinKarp.d.ts +++ b/_types/src/lib/src/ContentSplitter/ContentSplitterRabinKarp.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ContentSplitterOptions, SplitOptions } from "./ContentSplitter.ts"; import { ContentSplitterBase } from "./ContentSplitterBase.ts"; /** diff --git a/_types/src/lib/src/ContentSplitter/ContentSplitterV1.d.ts b/_types/src/lib/src/ContentSplitter/ContentSplitterV1.d.ts index 29143c2..92af4cd 100644 --- a/_types/src/lib/src/ContentSplitter/ContentSplitterV1.d.ts +++ b/_types/src/lib/src/ContentSplitter/ContentSplitterV1.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ContentSplitterOptions, SplitOptions } from "./ContentSplitter"; import { ContentSplitterBase } from "./ContentSplitterBase"; /** diff --git a/_types/src/lib/src/ContentSplitter/ContentSplitterV2.d.ts b/_types/src/lib/src/ContentSplitter/ContentSplitterV2.d.ts index c54debd..8cbbc15 100644 --- a/_types/src/lib/src/ContentSplitter/ContentSplitterV2.d.ts +++ b/_types/src/lib/src/ContentSplitter/ContentSplitterV2.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ContentSplitterOptions, SplitOptions } from "./ContentSplitter.ts"; import { ContentSplitterBase } from "./ContentSplitterBase.ts"; /** diff --git a/_types/src/lib/src/ContentSplitter/ContentSplitters.d.ts b/_types/src/lib/src/ContentSplitter/ContentSplitters.d.ts index f176712..9721319 100644 --- a/_types/src/lib/src/ContentSplitter/ContentSplitters.d.ts +++ b/_types/src/lib/src/ContentSplitter/ContentSplitters.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { SavingEntry } from "@lib/common/types"; import type { ContentSplitterOptions } from "./ContentSplitter"; import { ContentSplitterCore, type ContentSplitterBase } from "./ContentSplitterBase"; diff --git a/_types/src/lib/src/UI/svelteDialog.d.ts b/_types/src/lib/src/UI/svelteDialog.d.ts index 5fd77d6..5208a26 100644 --- a/_types/src/lib/src/UI/svelteDialog.d.ts +++ b/_types/src/lib/src/UI/svelteDialog.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type { HasSetResult, HasGetInitialData, ComponentHasResult, GuestDialogProps, DialogSvelteComponentBaseProps, DialogControlBase, } from "@lib/services/implements/base/SvelteDialog.ts"; export { CONTEXT_DIALOG_CONTROLS, setupDialogContext, getDialogContext, SvelteDialogManagerBase, } from "@lib/services/implements/base/SvelteDialog.ts"; diff --git a/_types/src/lib/src/bureau/bureau.d.ts b/_types/src/lib/src/bureau/bureau.d.ts index 1c74263..21d3da5 100644 --- a/_types/src/lib/src/bureau/bureau.d.ts +++ b/_types/src/lib/src/bureau/bureau.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type SlipBoard } from "octagonal-wheels/bureau/SlipBoard"; declare global { interface Slips extends LSSlips { diff --git a/_types/src/lib/src/common/ConnectionString.d.ts b/_types/src/lib/src/common/ConnectionString.d.ts index 6c2be88..30d924b 100644 --- a/_types/src/lib/src/common/ConnectionString.d.ts +++ b/_types/src/lib/src/common/ConnectionString.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { CouchDBConnection, BucketSyncSetting, P2PConnectionInfo } from "./models/setting.type"; export type RemoteConfigurationResult = { type: "couchdb"; diff --git a/_types/src/lib/src/common/LSError.d.ts b/_types/src/lib/src/common/LSError.d.ts index be36b16..3ac0ca4 100644 --- a/_types/src/lib/src/common/LSError.d.ts +++ b/_types/src/lib/src/common/LSError.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { Constructor } from "@lib/common/utils.type"; interface ErrorWithCause extends Error { cause?: unknown; diff --git a/_types/src/lib/src/common/configForDoc.d.ts b/_types/src/lib/src/common/configForDoc.d.ts index bb4d388..c9f0d2b 100644 --- a/_types/src/lib/src/common/configForDoc.d.ts +++ b/_types/src/lib/src/common/configForDoc.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { Confirm } from "@lib/interfaces/Confirm"; import { type ObsidianLiveSyncSettings } from "./types"; declare enum ConditionType { diff --git a/_types/src/lib/src/common/coreEnvFunctions.d.ts b/_types/src/lib/src/common/coreEnvFunctions.d.ts index d3b3c85..2b2725a 100644 --- a/_types/src/lib/src/common/coreEnvFunctions.d.ts +++ b/_types/src/lib/src/common/coreEnvFunctions.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { getLanguage as ObsidianGetLanguage } from "obsidian"; export declare function setGetLanguage(func: typeof ObsidianGetLanguage): void; export declare function getLanguage(): string; diff --git a/_types/src/lib/src/common/coreEnvVars.d.ts b/_types/src/lib/src/common/coreEnvVars.d.ts index 2e9a836..19d82a7 100644 --- a/_types/src/lib/src/common/coreEnvVars.d.ts +++ b/_types/src/lib/src/common/coreEnvVars.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 declare const manifestVersion: string; declare const packageVersion: string; export { manifestVersion, packageVersion }; diff --git a/_types/src/lib/src/common/i18n.d.ts b/_types/src/lib/src/common/i18n.d.ts index d005d49..1a9eba3 100644 --- a/_types/src/lib/src/common/i18n.d.ts +++ b/_types/src/lib/src/common/i18n.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { AllMessageKeys, I18N_LANGS } from "./rosetta"; import type { TaggedType } from "./types"; export declare let currentLang: I18N_LANGS; diff --git a/_types/src/lib/src/common/logger.d.ts b/_types/src/lib/src/common/logger.d.ts index 60ae0c5..3f91d3d 100644 --- a/_types/src/lib/src/common/logger.d.ts +++ b/_types/src/lib/src/common/logger.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export * from "octagonal-wheels/common/logger"; export type * from "octagonal-wheels/common/logger"; diff --git a/_types/src/lib/src/common/messages/combinedMessages.dev.d.ts b/_types/src/lib/src/common/messages/combinedMessages.dev.d.ts index e90a9cf..ce8736a 100644 --- a/_types/src/lib/src/common/messages/combinedMessages.dev.d.ts +++ b/_types/src/lib/src/common/messages/combinedMessages.dev.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { PartialMessages as def } from "./def.ts"; import { type MESSAGE } from "@lib/common/rosetta.ts"; type MessageKeys = keyof typeof def.def; diff --git a/_types/src/lib/src/common/messages/combinedMessages.prod.d.ts b/_types/src/lib/src/common/messages/combinedMessages.prod.d.ts index 6098ebd..d666c4b 100644 --- a/_types/src/lib/src/common/messages/combinedMessages.prod.d.ts +++ b/_types/src/lib/src/common/messages/combinedMessages.prod.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const allMessages: { readonly "(Active)": { readonly def: "(Active)"; diff --git a/_types/src/lib/src/common/messages/de.d.ts b/_types/src/lib/src/common/messages/de.d.ts index 833aaea..4be5c99 100644 --- a/_types/src/lib/src/common/messages/de.d.ts +++ b/_types/src/lib/src/common/messages/de.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly de: { "(Active)": string; diff --git a/_types/src/lib/src/common/messages/def.d.ts b/_types/src/lib/src/common/messages/def.d.ts index fd2a29e..108c888 100644 --- a/_types/src/lib/src/common/messages/def.d.ts +++ b/_types/src/lib/src/common/messages/def.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly def: { "(Active)": string; diff --git a/_types/src/lib/src/common/messages/es.d.ts b/_types/src/lib/src/common/messages/es.d.ts index 023fbb8..125c383 100644 --- a/_types/src/lib/src/common/messages/es.d.ts +++ b/_types/src/lib/src/common/messages/es.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly es: { "(Active)": string; diff --git a/_types/src/lib/src/common/messages/fr.d.ts b/_types/src/lib/src/common/messages/fr.d.ts index ae34713..724f0a0 100644 --- a/_types/src/lib/src/common/messages/fr.d.ts +++ b/_types/src/lib/src/common/messages/fr.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly fr: { "(BETA) Always overwrite with a newer file": string; diff --git a/_types/src/lib/src/common/messages/he.d.ts b/_types/src/lib/src/common/messages/he.d.ts index c07808c..41929ed 100644 --- a/_types/src/lib/src/common/messages/he.d.ts +++ b/_types/src/lib/src/common/messages/he.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly he: { "(BETA) Always overwrite with a newer file": string; diff --git a/_types/src/lib/src/common/messages/ja.d.ts b/_types/src/lib/src/common/messages/ja.d.ts index 9ec36f4..dc21f4a 100644 --- a/_types/src/lib/src/common/messages/ja.d.ts +++ b/_types/src/lib/src/common/messages/ja.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly ja: { "(Active)": string; diff --git a/_types/src/lib/src/common/messages/ko.d.ts b/_types/src/lib/src/common/messages/ko.d.ts index fe7ea7c..d150713 100644 --- a/_types/src/lib/src/common/messages/ko.d.ts +++ b/_types/src/lib/src/common/messages/ko.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly ko: { "(Active)": string; diff --git a/_types/src/lib/src/common/messages/ru.d.ts b/_types/src/lib/src/common/messages/ru.d.ts index e7dd3ba..641a084 100644 --- a/_types/src/lib/src/common/messages/ru.d.ts +++ b/_types/src/lib/src/common/messages/ru.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly ru: { "(Active)": string; diff --git a/_types/src/lib/src/common/messages/zh-tw.d.ts b/_types/src/lib/src/common/messages/zh-tw.d.ts index 47dd4c0..7214756 100644 --- a/_types/src/lib/src/common/messages/zh-tw.d.ts +++ b/_types/src/lib/src/common/messages/zh-tw.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly "zh-tw": { "(Active)": string; diff --git a/_types/src/lib/src/common/messages/zh.d.ts b/_types/src/lib/src/common/messages/zh.d.ts index 4d32899..de74b34 100644 --- a/_types/src/lib/src/common/messages/zh.d.ts +++ b/_types/src/lib/src/common/messages/zh.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const PartialMessages: { readonly zh: { "(Active)": string; diff --git a/_types/src/lib/src/common/models/auth.type.d.ts b/_types/src/lib/src/common/models/auth.type.d.ts index c3898f2..26718aa 100644 --- a/_types/src/lib/src/common/models/auth.type.d.ts +++ b/_types/src/lib/src/common/models/auth.type.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type CouchDBCredentials = BasicCredentials | JWTCredentials; export type JWTAlgorithm = "HS256" | "HS512" | "ES256" | "ES512" | ""; export type Credential = { diff --git a/_types/src/lib/src/common/models/db.const.d.ts b/_types/src/lib/src/common/models/db.const.d.ts index 7b690d7..7b2af03 100644 --- a/_types/src/lib/src/common/models/db.const.d.ts +++ b/_types/src/lib/src/common/models/db.const.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DocumentID } from "./db.type"; export declare const VERSIONING_DOCID: DocumentID; export declare const MILESTONE_DOCID: DocumentID; diff --git a/_types/src/lib/src/common/models/db.definition.d.ts b/_types/src/lib/src/common/models/db.definition.d.ts index eaad4bc..e4b58a7 100644 --- a/_types/src/lib/src/common/models/db.definition.d.ts +++ b/_types/src/lib/src/common/models/db.definition.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { MILESTONE_DOCID, NODEINFO_DOCID } from "./db.const"; import type { AnyEntry, ChunkVersionRange, DatabaseEntry, EntryChunkPack, EntryLeaf, EntryTypes, EntryVersionInfo, InternalFileEntry, LoadedEntry, MetaEntry, NewEntry, NoteEntry, PlainEntry } from "./db.type"; import type { TweakValues } from "./tweak.definition"; diff --git a/_types/src/lib/src/common/models/db.type.d.ts b/_types/src/lib/src/common/models/db.type.d.ts index ea344c8..9fc7ba0 100644 --- a/_types/src/lib/src/common/models/db.type.d.ts +++ b/_types/src/lib/src/common/models/db.type.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { TaggedType } from "octagonal-wheels/common/types"; import type { EntryTypes, SYNCINFO_ID } from "./db.const"; export type FilePath = TaggedType; diff --git a/_types/src/lib/src/common/models/diff.definition.d.ts b/_types/src/lib/src/common/models/diff.definition.d.ts index 5b93439..331a000 100644 --- a/_types/src/lib/src/common/models/diff.definition.d.ts +++ b/_types/src/lib/src/common/models/diff.definition.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { AUTO_MERGED, CANCELLED, MISSING_OR_ERROR, NOT_CONFLICTED } from "./shared.const.symbols"; export type diff_result_leaf = { rev: string; diff --git a/_types/src/lib/src/common/models/fileaccess.const.d.ts b/_types/src/lib/src/common/models/fileaccess.const.d.ts index ed98a52..4b82e15 100644 --- a/_types/src/lib/src/common/models/fileaccess.const.d.ts +++ b/_types/src/lib/src/common/models/fileaccess.const.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const CHeader = "h:"; export declare const PSCHeader = "ps:"; export declare const PSCHeaderEnd = "ps;"; diff --git a/_types/src/lib/src/common/models/fileaccess.type.d.ts b/_types/src/lib/src/common/models/fileaccess.type.d.ts index 331f7ba..a277f79 100644 --- a/_types/src/lib/src/common/models/fileaccess.type.d.ts +++ b/_types/src/lib/src/common/models/fileaccess.type.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, FilePathWithPrefix } from "./db.type"; export type UXStat = { size: number; diff --git a/_types/src/lib/src/common/models/redflag.const.d.ts b/_types/src/lib/src/common/models/redflag.const.d.ts index 4bd3d00..b800613 100644 --- a/_types/src/lib/src/common/models/redflag.const.d.ts +++ b/_types/src/lib/src/common/models/redflag.const.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath } from "./db.type"; export declare const PREFIXMD_LOGFILE = "livesync_log_"; export declare const PREFIXMD_LOGFILE_UC = "LIVESYNC_LOG_"; diff --git a/_types/src/lib/src/common/models/setting.const.d.ts b/_types/src/lib/src/common/models/setting.const.d.ts index b70d685..f6a3807 100644 --- a/_types/src/lib/src/common/models/setting.const.d.ts +++ b/_types/src/lib/src/common/models/setting.const.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const SETTING_VERSION_INITIAL = 0; export declare const SETTING_VERSION_SUPPORT_CASE_INSENSITIVE = 10; export declare const CURRENT_SETTING_VERSION = 10; diff --git a/_types/src/lib/src/common/models/setting.const.defaults.d.ts b/_types/src/lib/src/common/models/setting.const.defaults.d.ts index 6e29c17..b04f6ec 100644 --- a/_types/src/lib/src/common/models/setting.const.defaults.d.ts +++ b/_types/src/lib/src/common/models/setting.const.defaults.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ObsidianLiveSyncSettings, type P2PSyncSetting } from "./setting.type"; export declare const P2P_DEFAULT_SETTINGS: P2PSyncSetting; export declare const DEFAULT_SETTINGS: ObsidianLiveSyncSettings; diff --git a/_types/src/lib/src/common/models/setting.const.preferred.d.ts b/_types/src/lib/src/common/models/setting.const.preferred.d.ts index 8c779b8..0e88f8c 100644 --- a/_types/src/lib/src/common/models/setting.const.preferred.d.ts +++ b/_types/src/lib/src/common/models/setting.const.preferred.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettings } from "./setting.type"; export declare const PREFERRED_BASE: Partial; export declare const PREFERRED_SETTING_CLOUDANT: Partial; diff --git a/_types/src/lib/src/common/models/setting.const.qr.d.ts b/_types/src/lib/src/common/models/setting.const.qr.d.ts index 8d6021c..2ba74fb 100644 --- a/_types/src/lib/src/common/models/setting.const.qr.d.ts +++ b/_types/src/lib/src/common/models/setting.const.qr.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettings } from "./setting.type"; export declare const KeyIndexOfSettings: Record; diff --git a/_types/src/lib/src/common/models/setting.type.d.ts b/_types/src/lib/src/common/models/setting.type.d.ts index cc0c6de..f8504a3 100644 --- a/_types/src/lib/src/common/models/setting.type.d.ts +++ b/_types/src/lib/src/common/models/setting.type.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ChunkAlgorithms, E2EEAlgorithms, HashAlgorithms, MODE_AUTOMATIC, MODE_PAUSED, MODE_SELECTIVE, MODE_SHINY, RemoteTypes } from "./setting.const"; import type { I18N_LANGS } from "@lib/common/rosetta"; import type { CustomRegExpSourceList } from "./shared.type.util"; diff --git a/_types/src/lib/src/common/models/shared.const.behabiour.d.ts b/_types/src/lib/src/common/models/shared.const.behabiour.d.ts index 5e4e206..f241dc9 100644 --- a/_types/src/lib/src/common/models/shared.const.behabiour.d.ts +++ b/_types/src/lib/src/common/models/shared.const.behabiour.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const MAX_DOC_SIZE = 1000; export declare const MAX_DOC_SIZE_BIN = 102400; export declare const VER = 12; diff --git a/_types/src/lib/src/common/models/shared.const.d.ts b/_types/src/lib/src/common/models/shared.const.d.ts index a79d075..9f824ac 100644 --- a/_types/src/lib/src/common/models/shared.const.d.ts +++ b/_types/src/lib/src/common/models/shared.const.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const SETTING_KEY_P2P_DEVICE_NAME = "p2p_device_name"; export declare const configURIBase = "obsidian://setuplivesync?settings="; export declare const configURIBaseQR = "obsidian://setuplivesync?settingsQR="; diff --git a/_types/src/lib/src/common/models/shared.const.symbols.d.ts b/_types/src/lib/src/common/models/shared.const.symbols.d.ts index e20c473..392eb8b 100644 --- a/_types/src/lib/src/common/models/shared.const.symbols.d.ts +++ b/_types/src/lib/src/common/models/shared.const.symbols.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const CANCELLED: unique symbol; export declare const AUTO_MERGED: unique symbol; export declare const NOT_CONFLICTED: unique symbol; diff --git a/_types/src/lib/src/common/models/shared.definition.configNames.d.ts b/_types/src/lib/src/common/models/shared.definition.configNames.d.ts index ea47a41..56b76b0 100644 --- a/_types/src/lib/src/common/models/shared.definition.configNames.d.ts +++ b/_types/src/lib/src/common/models/shared.definition.configNames.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettings } from "./setting.type"; export declare const LEVEL_ADVANCED = "ADVANCED"; export declare const LEVEL_POWER_USER = "POWER_USER"; diff --git a/_types/src/lib/src/common/models/shared.definition.d.ts b/_types/src/lib/src/common/models/shared.definition.d.ts index b38fb5b..c9612b9 100644 --- a/_types/src/lib/src/common/models/shared.definition.d.ts +++ b/_types/src/lib/src/common/models/shared.definition.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const DatabaseConnectingStatuses: { readonly STARTED: "STARTED"; readonly NOT_CONNECTED: "NOT_CONNECTED"; diff --git a/_types/src/lib/src/common/models/shared.type.util.d.ts b/_types/src/lib/src/common/models/shared.type.util.d.ts index 06cb4f0..839c72f 100644 --- a/_types/src/lib/src/common/models/shared.type.util.d.ts +++ b/_types/src/lib/src/common/models/shared.type.util.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { TaggedType } from "octagonal-wheels/common/types"; export type { TaggedType }; export type CustomRegExpSource = TaggedType; diff --git a/_types/src/lib/src/common/models/sync.definition.d.ts b/_types/src/lib/src/common/models/sync.definition.d.ts index 4984240..da177d7 100644 --- a/_types/src/lib/src/common/models/sync.definition.d.ts +++ b/_types/src/lib/src/common/models/sync.definition.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { EntryTypes } from "./db.const"; import type { DatabaseEntry, DocumentID } from "./db.type"; export declare const ProtocolVersions: { diff --git a/_types/src/lib/src/common/models/tweak.definition.d.ts b/_types/src/lib/src/common/models/tweak.definition.d.ts index 6f6f408..8777736 100644 --- a/_types/src/lib/src/common/models/tweak.definition.d.ts +++ b/_types/src/lib/src/common/models/tweak.definition.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettings } from "./setting.type"; export declare const TweakValuesShouldMatchedTemplate: Partial; type TweakKeys = keyof TweakValues; diff --git a/_types/src/lib/src/common/rosetta.d.ts b/_types/src/lib/src/common/rosetta.d.ts index 7e938d6..168f025 100644 --- a/_types/src/lib/src/common/rosetta.d.ts +++ b/_types/src/lib/src/common/rosetta.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** # Rosetta stone - To localise messages to your language, please write a translation to this file and submit a PR. diff --git a/_types/src/lib/src/common/settingConstants.d.ts b/_types/src/lib/src/common/settingConstants.d.ts index aa4f552..6bd6e8b 100644 --- a/_types/src/lib/src/common/settingConstants.d.ts +++ b/_types/src/lib/src/common/settingConstants.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ConfigurationItem, type ObsidianLiveSyncSettings } from "./types.ts"; type ExtractPropertiesByType = { [K in keyof T as T[K] extends U ? K : never]: T[K] extends U ? K : never; diff --git a/_types/src/lib/src/common/typeUtils.d.ts b/_types/src/lib/src/common/typeUtils.d.ts index 83b83d7..86ceecb 100644 --- a/_types/src/lib/src/common/typeUtils.d.ts +++ b/_types/src/lib/src/common/typeUtils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DocumentID, FilePath, FilePathWithPrefix } from "./models/db.type"; import type { UXFileInfoStub } from "./types"; /** diff --git a/_types/src/lib/src/common/types.d.ts b/_types/src/lib/src/common/types.d.ts index 5dbfcc9..7967baa 100644 --- a/_types/src/lib/src/common/types.d.ts +++ b/_types/src/lib/src/common/types.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type { TaggedType } from "./models/shared.type.util.ts"; export { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT, LOG_LEVEL_VERBOSE, } from "octagonal-wheels/common/logger"; export type { LOG_LEVEL } from "octagonal-wheels/common/logger"; diff --git a/_types/src/lib/src/common/utils.d.ts b/_types/src/lib/src/common/utils.d.ts index e1aa435..c4f2ef2 100644 --- a/_types/src/lib/src/common/utils.d.ts +++ b/_types/src/lib/src/common/utils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type AnyEntry, type DatabaseEntry, type EntryLeaf, type SyncInfo, type LoadedEntry, type SavingEntry, type NewEntry, type PlainEntry, type CustomRegExpSource, type ParsedCustomRegExp, type CustomRegExpSourceList, type ObsidianLiveSyncSettings, type RemoteDBSettings, type P2PConnectionInfo, type BucketSyncSetting, type CouchDBConnection, type EncryptionSettings } from "./types.ts"; import { replaceAll, replaceAllPairs } from "octagonal-wheels/string"; export { replaceAll, replaceAllPairs }; diff --git a/_types/src/lib/src/common/utils.doc.d.ts b/_types/src/lib/src/common/utils.doc.d.ts index 7e29b2a..a77b5ea 100644 --- a/_types/src/lib/src/common/utils.doc.d.ts +++ b/_types/src/lib/src/common/utils.doc.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function isErrorOf(ex: unknown, statusCode: number): boolean; /** * Checks if the error is effectively a 404 error from CouchDB or PouchDB. diff --git a/_types/src/lib/src/common/utils.object.d.ts b/_types/src/lib/src/common/utils.object.d.ts index cb2489b..5b4676a 100644 --- a/_types/src/lib/src/common/utils.object.d.ts +++ b/_types/src/lib/src/common/utils.object.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function asCopy(obj: T): T; export declare function ensureError(error: unknown): Error; diff --git a/_types/src/lib/src/common/utils.patch.d.ts b/_types/src/lib/src/common/utils.patch.d.ts index 2b63816..1ac8666 100644 --- a/_types/src/lib/src/common/utils.patch.d.ts +++ b/_types/src/lib/src/common/utils.patch.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function generatePatchObj(from: Record, to: Record): Record; export declare function applyPatch(from: Record, patch: Record): Record; export declare function mergeObject(objA: Record | [unknown], objB: Record | [unknown]): unknown[] | { diff --git a/_types/src/lib/src/common/utils.type.d.ts b/_types/src/lib/src/common/utils.type.d.ts index fc09a7d..03ee021 100644 --- a/_types/src/lib/src/common/utils.type.d.ts +++ b/_types/src/lib/src/common/utils.type.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type Constructor = new (...args: any[]) => T; // eslint-disable-line @typescript-eslint/no-explicit-any -- Only type declaration diff --git a/_types/src/lib/src/dataobject/StoredMap.d.ts b/_types/src/lib/src/dataobject/StoredMap.d.ts index c2f5727..5c2d6e5 100644 --- a/_types/src/lib/src/dataobject/StoredMap.d.ts +++ b/_types/src/lib/src/dataobject/StoredMap.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase"; export declare class StoredMapLike { _store: SimpleStore; diff --git a/_types/src/lib/src/dev/checks.d.ts b/_types/src/lib/src/dev/checks.d.ts index f14b63a..5038ec7 100644 --- a/_types/src/lib/src/dev/checks.d.ts +++ b/_types/src/lib/src/dev/checks.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 interface InstanceHaveOnBindFunction { onBindFunction: (...params: T[]) => void; } diff --git a/_types/src/lib/src/encryption/encryptHKDF.d.ts b/_types/src/lib/src/encryption/encryptHKDF.d.ts index ec2960b..4f8ea05 100644 --- a/_types/src/lib/src/encryption/encryptHKDF.d.ts +++ b/_types/src/lib/src/encryption/encryptHKDF.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { encryptHKDFWorker, decryptHKDFWorker } from "@lib/worker/bgWorker.ts"; export declare const encryptHKDF: typeof encryptHKDFWorker; export declare const decryptHKDF: typeof decryptHKDFWorker; diff --git a/_types/src/lib/src/encryption/stringEncryption.d.ts b/_types/src/lib/src/encryption/stringEncryption.d.ts index 00e6fca..15dfc2d 100644 --- a/_types/src/lib/src/encryption/stringEncryption.d.ts +++ b/_types/src/lib/src/encryption/stringEncryption.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** * Encrypts a string using a passphrase, unless the string is already encrypted. * diff --git a/_types/src/lib/src/events/coreEvents.d.ts b/_types/src/lib/src/events/coreEvents.d.ts index a7229cf..c78ccd9 100644 --- a/_types/src/lib/src/events/coreEvents.d.ts +++ b/_types/src/lib/src/events/coreEvents.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePathWithPrefix, ObsidianLiveSyncSettings } from "@lib/common/types"; export declare const EVENT_LAYOUT_READY = "layout-ready"; export declare const EVENT_PLUGIN_LOADED = "plugin-loaded"; diff --git a/_types/src/lib/src/hub/hub.d.ts b/_types/src/lib/src/hub/hub.d.ts index a4a66b5..d5a7f73 100644 --- a/_types/src/lib/src/hub/hub.d.ts +++ b/_types/src/lib/src/hub/hub.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { EventHub } from "octagonal-wheels/events"; declare global { interface LSEvents { diff --git a/_types/src/lib/src/index.d.ts b/_types/src/lib/src/index.d.ts index f6275e9..868f2fd 100644 --- a/_types/src/lib/src/index.d.ts +++ b/_types/src/lib/src/index.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export { DirectFileManipulator, type DirectFileManipulatorOptions } from "./API/DirectFileManipulator.ts"; diff --git a/_types/src/lib/src/interfaces/Confirm.d.ts b/_types/src/lib/src/interfaces/Confirm.d.ts index ca449f1..1f7c6c5 100644 --- a/_types/src/lib/src/interfaces/Confirm.d.ts +++ b/_types/src/lib/src/interfaces/Confirm.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export interface Confirm { askYesNo(message: string): Promise<"yes" | "no">; askString(title: string, key: string, placeholder: string, isPassword?: boolean): Promise; diff --git a/_types/src/lib/src/interfaces/DatabaseFileAccess.d.ts b/_types/src/lib/src/interfaces/DatabaseFileAccess.d.ts index aedc66d..e0bed6a 100644 --- a/_types/src/lib/src/interfaces/DatabaseFileAccess.d.ts +++ b/_types/src/lib/src/interfaces/DatabaseFileAccess.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePathWithPrefix, LoadedEntry, MetaEntry, UXFileInfo, UXFileInfoStub } from "@lib/common/types"; export interface DatabaseFileAccess { delete: (file: UXFileInfoStub | FilePathWithPrefix, rev?: string) => Promise; diff --git a/_types/src/lib/src/interfaces/DatabaseRebuilder.d.ts b/_types/src/lib/src/interfaces/DatabaseRebuilder.d.ts index fdd3e30..56cac80 100644 --- a/_types/src/lib/src/interfaces/DatabaseRebuilder.d.ts +++ b/_types/src/lib/src/interfaces/DatabaseRebuilder.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export interface Rebuilder { $performRebuildDB(method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice" | "localOnlyWithChunks"): Promise; $rebuildRemote(): Promise; diff --git a/_types/src/lib/src/interfaces/FileHandler.d.ts b/_types/src/lib/src/interfaces/FileHandler.d.ts index 72ac327..b671cc7 100644 --- a/_types/src/lib/src/interfaces/FileHandler.d.ts +++ b/_types/src/lib/src/interfaces/FileHandler.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, FilePathWithPrefix, MetaEntry } from "@lib/common/models/db.type"; import type { UXFileInfo, UXFileInfoStub, UXInternalFileInfoStub } from "@lib/common/models/fileaccess.type"; export interface IFileHandler { diff --git a/_types/src/lib/src/interfaces/KeyValueDatabase.d.ts b/_types/src/lib/src/interfaces/KeyValueDatabase.d.ts index a370d62..f0572f0 100644 --- a/_types/src/lib/src/interfaces/KeyValueDatabase.d.ts +++ b/_types/src/lib/src/interfaces/KeyValueDatabase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export interface KeyValueDatabase { get(key: IDBValidKey): Promise; set(key: IDBValidKey, value: T): Promise; diff --git a/_types/src/lib/src/interfaces/ServiceModule.d.ts b/_types/src/lib/src/interfaces/ServiceModule.d.ts index 6ca41b6..4668e36 100644 --- a/_types/src/lib/src/interfaces/ServiceModule.d.ts +++ b/_types/src/lib/src/interfaces/ServiceModule.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DatabaseFileAccess } from "@lib/interfaces/DatabaseFileAccess"; import type { Rebuilder } from "@lib/interfaces/DatabaseRebuilder"; import type { IFileHandler } from "@lib/interfaces/FileHandler"; diff --git a/_types/src/lib/src/interfaces/StorageAccess.d.ts b/_types/src/lib/src/interfaces/StorageAccess.d.ts index 38558d8..9addb24 100644 --- a/_types/src/lib/src/interfaces/StorageAccess.d.ts +++ b/_types/src/lib/src/interfaces/StorageAccess.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, FilePathWithPrefix, UXDataWriteOptions, UXFileInfo, UXFileInfoStub, UXFolderInfo, UXStat } from "@lib/common/types"; import type { CustomRegExp } from "@lib/common/utils"; import type { FileWithFileStat, FileWithStatAsProp } from "@lib/common/models/fileaccess.type"; diff --git a/_types/src/lib/src/interfaces/StorageEventManager.d.ts b/_types/src/lib/src/interfaces/StorageEventManager.d.ts index 5d9dd34..4164c9d 100644 --- a/_types/src/lib/src/interfaces/StorageEventManager.d.ts +++ b/_types/src/lib/src/interfaces/StorageEventManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FileEventType, FilePath, UXFileInfoStub, UXInternalFileInfoStub } from "@lib/common/types"; export type FileEvent = { type: FileEventType; diff --git a/_types/src/lib/src/managers/ChangeManager.d.ts b/_types/src/lib/src/managers/ChangeManager.d.ts index 00826b9..927dd68 100644 --- a/_types/src/lib/src/managers/ChangeManager.d.ts +++ b/_types/src/lib/src/managers/ChangeManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { FallbackWeakRef } from "octagonal-wheels/common/polyfill"; /** * Options for configuring the ChangeManager. diff --git a/_types/src/lib/src/managers/ChunkFetcher.d.ts b/_types/src/lib/src/managers/ChunkFetcher.d.ts index 9a47374..bbfa36e 100644 --- a/_types/src/lib/src/managers/ChunkFetcher.d.ts +++ b/_types/src/lib/src/managers/ChunkFetcher.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type DocumentID } from "@lib/common/types.ts"; import { type ChunkManager } from "./ChunkManager.ts"; import type { IReplicatorService, ISettingService } from "@lib/services/base/IService.ts"; diff --git a/_types/src/lib/src/managers/ChunkManager.d.ts b/_types/src/lib/src/managers/ChunkManager.d.ts index 612fc93..9e2d738 100644 --- a/_types/src/lib/src/managers/ChunkManager.d.ts +++ b/_types/src/lib/src/managers/ChunkManager.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { LayeredChunkManager } from "./LayeredChunkManager"; export { LayeredChunkManager as ChunkManager }; diff --git a/_types/src/lib/src/managers/ConflictManager.d.ts b/_types/src/lib/src/managers/ConflictManager.d.ts index 52db2bd..fc1cd92 100644 --- a/_types/src/lib/src/managers/ConflictManager.d.ts +++ b/_types/src/lib/src/managers/ConflictManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type Diff } from "diff-match-patch"; import { type EntryDoc, type FilePathWithPrefix, type diff_result_leaf, type LoadedEntry, type DIFF_CHECK_RESULT_AUTO } from "@lib/common/types.ts"; import type { EntryManager } from "@lib/managers/EntryManager/EntryManager.ts"; diff --git a/_types/src/lib/src/managers/EntryManager/EntryManager.d.ts b/_types/src/lib/src/managers/EntryManager/EntryManager.d.ts index 132bb1d..bda90a2 100644 --- a/_types/src/lib/src/managers/EntryManager/EntryManager.d.ts +++ b/_types/src/lib/src/managers/EntryManager/EntryManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type FilePathWithPrefix, type FilePath, type LoadedEntry, type EntryDoc, type SavingEntry, type MetaEntry } from "@lib/common/types"; import type { ChunkManager } from "@lib/managers/ChunkManager"; import type { ContentSplitter } from "@lib/ContentSplitter/ContentSplitters"; diff --git a/_types/src/lib/src/managers/EntryManager/EntryManagerImpls.d.ts b/_types/src/lib/src/managers/EntryManager/EntryManagerImpls.d.ts index 0136fba..cf0fdbf 100644 --- a/_types/src/lib/src/managers/EntryManager/EntryManagerImpls.d.ts +++ b/_types/src/lib/src/managers/EntryManager/EntryManagerImpls.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type SavingEntry, type DocumentID, type EntryDoc, type EntryBase, type FilePath, type FilePathWithPrefix, type LoadedEntry, type ObsidianLiveSyncSettings, type MetaEntry } from "@lib/common/types"; import type { ContentSplitter } from "@lib/ContentSplitter/ContentSplitters"; import type { HashManager } from "@lib/managers/HashManager/HashManager"; diff --git a/_types/src/lib/src/managers/HashManager/HashManager.d.ts b/_types/src/lib/src/managers/HashManager/HashManager.d.ts index 11f70c9..cf96d2d 100644 --- a/_types/src/lib/src/managers/HashManager/HashManager.d.ts +++ b/_types/src/lib/src/managers/HashManager/HashManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { HashAlgorithm } from "@lib/common/models/setting.type.ts"; import { HashManagerCore, type HashManagerCoreOptions } from "./HashManagerCore.ts"; /** diff --git a/_types/src/lib/src/managers/HashManager/HashManagerCore.d.ts b/_types/src/lib/src/managers/HashManager/HashManagerCore.d.ts index a0f92e8..4083525 100644 --- a/_types/src/lib/src/managers/HashManager/HashManagerCore.d.ts +++ b/_types/src/lib/src/managers/HashManager/HashManagerCore.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ISettingService } from "@lib/services/base/IService.ts"; import type { HashAlgorithm } from "@lib/common/models/setting.type.ts"; /** diff --git a/_types/src/lib/src/managers/HashManager/PureJSHashManager.d.ts b/_types/src/lib/src/managers/HashManager/PureJSHashManager.d.ts index 5333eb5..d8ac2fd 100644 --- a/_types/src/lib/src/managers/HashManager/PureJSHashManager.d.ts +++ b/_types/src/lib/src/managers/HashManager/PureJSHashManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { HashManagerCore } from "./HashManagerCore.ts"; import type { HashAlgorithm } from "@lib/common/models/setting.type.ts"; /** diff --git a/_types/src/lib/src/managers/HashManager/XXHashHashManager.d.ts b/_types/src/lib/src/managers/HashManager/XXHashHashManager.d.ts index 55d937e..8005d3b 100644 --- a/_types/src/lib/src/managers/HashManager/XXHashHashManager.d.ts +++ b/_types/src/lib/src/managers/HashManager/XXHashHashManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { HashManagerCore, type HashManagerCoreOptions } from "./HashManagerCore.ts"; import type { XXHashAPI } from "xxhash-wasm-102"; import type { HashAlgorithm } from "@lib/common/models/setting.type.ts"; diff --git a/_types/src/lib/src/managers/LayeredChunkManager.d.ts b/_types/src/lib/src/managers/LayeredChunkManager.d.ts index 85902a2..f4bd8e7 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DocumentID, EntryDoc, EntryLeaf } from "@lib/common/types.ts"; import type { ChangeManager } from "@lib/managers/ChangeManager.ts"; import type { ChunkManagerEventMap, ChunkManagerOptions, ChunkReadOptions, ChunkWriteOptions, WriteResult } from "./LayeredChunkManager/types.ts"; diff --git a/_types/src/lib/src/managers/LayeredChunkManager/ArrivalWaitLayer.d.ts b/_types/src/lib/src/managers/LayeredChunkManager/ArrivalWaitLayer.d.ts index 08d9caa..82d75e0 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager/ArrivalWaitLayer.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager/ArrivalWaitLayer.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DocumentID, EntryLeaf } from "@lib/common/types"; import type { IReadLayer } from "./ChunkLayerInterfaces"; import type { ChunkReadOptions } from "./types.ts"; diff --git a/_types/src/lib/src/managers/LayeredChunkManager/CacheLayer.d.ts b/_types/src/lib/src/managers/LayeredChunkManager/CacheLayer.d.ts index c3b42d9..ed96100 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager/CacheLayer.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager/CacheLayer.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DocumentID, EntryLeaf } from "@lib/common/types"; import type { IReadLayer, IWriteLayer } from "./ChunkLayerInterfaces"; import type { ChunkReadOptions, ChunkWriteOptions, WriteResult } from "./types.ts"; diff --git a/_types/src/lib/src/managers/LayeredChunkManager/ChunkLayerInterfaces.d.ts b/_types/src/lib/src/managers/LayeredChunkManager/ChunkLayerInterfaces.d.ts index ce0afcf..213cd63 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager/ChunkLayerInterfaces.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager/ChunkLayerInterfaces.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DocumentID, EntryLeaf } from "@lib/common/types.ts"; import type { ChunkReadOptions, ChunkWriteOptions, WriteResult } from "./types.ts"; /** diff --git a/_types/src/lib/src/managers/LayeredChunkManager/DatabaseReadLayer.d.ts b/_types/src/lib/src/managers/LayeredChunkManager/DatabaseReadLayer.d.ts index db26dfa..13479ba 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager/DatabaseReadLayer.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager/DatabaseReadLayer.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EntryLeaf, DocumentID, EntryDoc } from "@lib/common/types"; import type { IReadLayer } from "./ChunkLayerInterfaces"; import type { ChunkReadOptions } from "./types.ts"; diff --git a/_types/src/lib/src/managers/LayeredChunkManager/DatabaseWriteLayer.d.ts b/_types/src/lib/src/managers/LayeredChunkManager/DatabaseWriteLayer.d.ts index 8feaf7d..1772fe1 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager/DatabaseWriteLayer.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager/DatabaseWriteLayer.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EntryLeaf, DocumentID, EntryDoc } from "@lib/common/types"; import type { IWriteLayer } from "./ChunkLayerInterfaces"; import type { ChunkWriteOptions, WriteResult } from "./types.ts"; diff --git a/_types/src/lib/src/managers/LayeredChunkManager/HotPackLayer.d.ts b/_types/src/lib/src/managers/LayeredChunkManager/HotPackLayer.d.ts index c886bb0..e7307ea 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager/HotPackLayer.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager/HotPackLayer.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EntryLeaf, DocumentID } from "@lib/common/types"; import type { IWriteLayer } from "./ChunkLayerInterfaces"; import type { ChunkWriteOptions, WriteResult } from "./types.ts"; diff --git a/_types/src/lib/src/managers/LayeredChunkManager/types.d.ts b/_types/src/lib/src/managers/LayeredChunkManager/types.d.ts index 89ae5e3..75b3efc 100644 --- a/_types/src/lib/src/managers/LayeredChunkManager/types.d.ts +++ b/_types/src/lib/src/managers/LayeredChunkManager/types.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EntryDoc } from "@lib/common/models/db.definition"; import type { DocumentID, EntryLeaf } from "@lib/common/models/db.type"; import type { ISettingService } from "@lib/services/base/IService"; diff --git a/_types/src/lib/src/managers/LiveSyncManagers.d.ts b/_types/src/lib/src/managers/LiveSyncManagers.d.ts index ed9d094..3f5826f 100644 --- a/_types/src/lib/src/managers/LiveSyncManagers.d.ts +++ b/_types/src/lib/src/managers/LiveSyncManagers.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc } from "@lib/common/types"; import { ContentSplitter } from "@lib/ContentSplitter/ContentSplitters.ts"; import { ChangeManager } from "@lib/managers/ChangeManager.ts"; diff --git a/_types/src/lib/src/managers/StorageEventManager.d.ts b/_types/src/lib/src/managers/StorageEventManager.d.ts index 1f20371..c66a265 100644 --- a/_types/src/lib/src/managers/StorageEventManager.d.ts +++ b/_types/src/lib/src/managers/StorageEventManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type FileEventType, type FilePath, type UXFileInfoStub, type UXFolderInfo, type UXInternalFileInfoStub } from "@lib/common/types.ts"; import { type FileEventItem } from "@lib/common/types.ts"; import type { IStorageAccessManager } from "@lib/interfaces/StorageAccess.ts"; diff --git a/_types/src/lib/src/managers/StorageProcessingManager.d.ts b/_types/src/lib/src/managers/StorageProcessingManager.d.ts index 915920e..bbeea7d 100644 --- a/_types/src/lib/src/managers/StorageProcessingManager.d.ts +++ b/_types/src/lib/src/managers/StorageProcessingManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePathWithPrefix } from "@lib/common/models/db.type"; import type { UXFileInfoStub } from "@lib/common/models/fileaccess.type"; import type { IStorageAccessManager } from "@lib/interfaces/StorageAccess"; diff --git a/_types/src/lib/src/managers/adapters/IStorageEventConverterAdapter.d.ts b/_types/src/lib/src/managers/adapters/IStorageEventConverterAdapter.d.ts index da75e1b..8f7092a 100644 --- a/_types/src/lib/src/managers/adapters/IStorageEventConverterAdapter.d.ts +++ b/_types/src/lib/src/managers/adapters/IStorageEventConverterAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, UXFileInfoStub, UXInternalFileInfoStub } from "@lib/common/types"; /** * Adapter interface for converting platform-specific file types to UX types diff --git a/_types/src/lib/src/managers/adapters/IStorageEventManagerAdapter.d.ts b/_types/src/lib/src/managers/adapters/IStorageEventManagerAdapter.d.ts index 175ad48..ac69280 100644 --- a/_types/src/lib/src/managers/adapters/IStorageEventManagerAdapter.d.ts +++ b/_types/src/lib/src/managers/adapters/IStorageEventManagerAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { IStorageEventTypeGuardAdapter } from "./IStorageEventTypeGuardAdapter"; import type { IStorageEventPersistenceAdapter } from "./IStorageEventPersistenceAdapter"; import type { IStorageEventWatchAdapter } from "./IStorageEventWatchAdapter"; diff --git a/_types/src/lib/src/managers/adapters/IStorageEventPersistenceAdapter.d.ts b/_types/src/lib/src/managers/adapters/IStorageEventPersistenceAdapter.d.ts index 8e2de13..1628e25 100644 --- a/_types/src/lib/src/managers/adapters/IStorageEventPersistenceAdapter.d.ts +++ b/_types/src/lib/src/managers/adapters/IStorageEventPersistenceAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FileEventItem } from "@lib/common/types"; import type { FileEventItemSentinel } from "@lib/managers/StorageEventManager"; /** diff --git a/_types/src/lib/src/managers/adapters/IStorageEventStatusAdapter.d.ts b/_types/src/lib/src/managers/adapters/IStorageEventStatusAdapter.d.ts index ed35a8c..dbdf699 100644 --- a/_types/src/lib/src/managers/adapters/IStorageEventStatusAdapter.d.ts +++ b/_types/src/lib/src/managers/adapters/IStorageEventStatusAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** * Adapter interface for status update operations */ diff --git a/_types/src/lib/src/managers/adapters/IStorageEventTypeGuardAdapter.d.ts b/_types/src/lib/src/managers/adapters/IStorageEventTypeGuardAdapter.d.ts index 635b54a..20b47d7 100644 --- a/_types/src/lib/src/managers/adapters/IStorageEventTypeGuardAdapter.d.ts +++ b/_types/src/lib/src/managers/adapters/IStorageEventTypeGuardAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** * Adapter interface for type guard operations in StorageEventManager * diff --git a/_types/src/lib/src/managers/adapters/IStorageEventWatchAdapter.d.ts b/_types/src/lib/src/managers/adapters/IStorageEventWatchAdapter.d.ts index 3e186da..44d079f 100644 --- a/_types/src/lib/src/managers/adapters/IStorageEventWatchAdapter.d.ts +++ b/_types/src/lib/src/managers/adapters/IStorageEventWatchAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath } from "@lib/common/types"; /** * Event handlers for storage events diff --git a/_types/src/lib/src/managers/adapters/index.d.ts b/_types/src/lib/src/managers/adapters/index.d.ts index 30cd9b0..4cbd772 100644 --- a/_types/src/lib/src/managers/adapters/index.d.ts +++ b/_types/src/lib/src/managers/adapters/index.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type { IStorageEventTypeGuardAdapter } from "./IStorageEventTypeGuardAdapter"; export type { IStorageEventPersistenceAdapter } from "./IStorageEventPersistenceAdapter"; export type { IStorageEventWatchAdapter, IStorageEventWatchHandlers } from "./IStorageEventWatchAdapter"; diff --git a/_types/src/lib/src/mock_and_interop/stores.d.ts b/_types/src/lib/src/mock_and_interop/stores.d.ts index 7e40d26..c5fdef4 100644 --- a/_types/src/lib/src/mock_and_interop/stores.d.ts +++ b/_types/src/lib/src/mock_and_interop/stores.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LOG_LEVEL } from "@lib/common/types.ts"; export type LockStats = { pending: string[]; diff --git a/_types/src/lib/src/mock_and_interop/wrapper.d.ts b/_types/src/lib/src/mock_and_interop/wrapper.d.ts index d06e591..ea8bd25 100644 --- a/_types/src/lib/src/mock_and_interop/wrapper.d.ts +++ b/_types/src/lib/src/mock_and_interop/wrapper.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare class WrappedNotice { constructor(message: string | DocumentFragment, timeout?: number); setMessage(message: string | DocumentFragment): this; diff --git a/_types/src/lib/src/mods.d.ts b/_types/src/lib/src/mods.d.ts index ff7d6b8..d646bfd 100644 --- a/_types/src/lib/src/mods.d.ts +++ b/_types/src/lib/src/mods.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function getWebCrypto(): Promise; diff --git a/_types/src/lib/src/pouchdb/LiveSyncDBFunctions.d.ts b/_types/src/lib/src/pouchdb/LiveSyncDBFunctions.d.ts index b4c2230..6a70ac1 100644 --- a/_types/src/lib/src/pouchdb/LiveSyncDBFunctions.d.ts +++ b/_types/src/lib/src/pouchdb/LiveSyncDBFunctions.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc, type EntryMilestoneInfo, type RemoteDBSettings, type ChunkVersionRange, type TweakValues, type DeviceInfo } from "@lib/common/types.ts"; export type ENSURE_DB_RESULT = "OK" | "INCOMPATIBLE" | "LOCKED" | "NODE_LOCKED" | "NODE_CLEANED" | ["MISMATCHED", TweakValues]; /** diff --git a/_types/src/lib/src/pouchdb/LiveSyncLocalDB.d.ts b/_types/src/lib/src/pouchdb/LiveSyncLocalDB.d.ts index e943482..0560ae5 100644 --- a/_types/src/lib/src/pouchdb/LiveSyncLocalDB.d.ts +++ b/_types/src/lib/src/pouchdb/LiveSyncLocalDB.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc, type EntryLeaf, type Credential, type RemoteDBSettings, type DocumentID, type FilePathWithPrefix, type FilePath, type DatabaseEntry, type LoadedEntry, type MetaEntry, type SavingEntry, type diff_result_leaf } from "@lib/common/types.ts"; import { eventHub } from "@lib/hub/hub.ts"; import { LiveSyncManagers } from "@lib/managers/LiveSyncManagers.ts"; diff --git a/_types/src/lib/src/pouchdb/ReplicatorShim.d.ts b/_types/src/lib/src/pouchdb/ReplicatorShim.d.ts index 404eaa5..e1ebe4b 100644 --- a/_types/src/lib/src/pouchdb/ReplicatorShim.d.ts +++ b/_types/src/lib/src/pouchdb/ReplicatorShim.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type SomeDocument = PouchDB.Core.ExistingDocument & PouchDB.Core.ChangesMeta; /** * Minimal subset of the PouchDB public API required by {@link replicateShim}. diff --git a/_types/src/lib/src/pouchdb/StreamingFetch.d.ts b/_types/src/lib/src/pouchdb/StreamingFetch.d.ts index be9754a..384b714 100644 --- a/_types/src/lib/src/pouchdb/StreamingFetch.d.ts +++ b/_types/src/lib/src/pouchdb/StreamingFetch.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EntryDoc } from "@lib/common/models/db.definition"; import type { AnyEntry, EntryLeaf } from "@lib/common/models/db.type"; type DBSequence = number | string; diff --git a/_types/src/lib/src/pouchdb/StreamingFetch.integration.spec.d.ts b/_types/src/lib/src/pouchdb/StreamingFetch.integration.spec.d.ts index 4b30b39..b549d23 100644 --- a/_types/src/lib/src/pouchdb/StreamingFetch.integration.spec.d.ts +++ b/_types/src/lib/src/pouchdb/StreamingFetch.integration.spec.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export {}; diff --git a/_types/src/lib/src/pouchdb/chunks.d.ts b/_types/src/lib/src/pouchdb/chunks.d.ts index fbb5fe3..4813505 100644 --- a/_types/src/lib/src/pouchdb/chunks.d.ts +++ b/_types/src/lib/src/pouchdb/chunks.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { CouchDBConnection } from "@lib/common/types"; export declare function purgeUnreferencedChunks(db: PouchDB.Database, dryRun: boolean, connSetting?: CouchDBConnection, performCompact?: boolean): Promise; export declare function transferChunks(key: string, label: string, dbFrom: PouchDB.Database, dbTo: PouchDB.Database, items: { diff --git a/_types/src/lib/src/pouchdb/compress.d.ts b/_types/src/lib/src/pouchdb/compress.d.ts index 866a7ce..876d019 100644 --- a/_types/src/lib/src/pouchdb/compress.d.ts +++ b/_types/src/lib/src/pouchdb/compress.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import * as fflate from "fflate"; import type { EntryDoc } from "@lib/common/types"; export declare function _compressText(text: string): Promise; diff --git a/_types/src/lib/src/pouchdb/encryption.d.ts b/_types/src/lib/src/pouchdb/encryption.d.ts index e7ed560..e3fe911 100644 --- a/_types/src/lib/src/pouchdb/encryption.d.ts +++ b/_types/src/lib/src/pouchdb/encryption.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc, type AnyEntry, type EntryLeaf, type DocumentID, type E2EEAlgorithm } from "@lib/common/types"; import { encryptWorker, decryptWorker, encryptHKDFWorker, decryptHKDFWorker } from "@lib/worker/bgWorker.ts"; export declare const encrypt: typeof encryptWorker; diff --git a/_types/src/lib/src/pouchdb/negotiation.d.ts b/_types/src/lib/src/pouchdb/negotiation.d.ts index 6a897e3..6e86e62 100644 --- a/_types/src/lib/src/pouchdb/negotiation.d.ts +++ b/_types/src/lib/src/pouchdb/negotiation.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const checkRemoteVersion: (db: PouchDB.Database, migrate: (from: number, to: number) => Promise, barrier?: number) => Promise; export declare const bumpRemoteVersion: (db: PouchDB.Database, barrier?: number) => Promise; export declare const checkSyncInfo: (db: PouchDB.Database) => Promise; diff --git a/_types/src/lib/src/pouchdb/pouchdb-browser.d.ts b/_types/src/lib/src/pouchdb/pouchdb-browser.d.ts index 81d6038..421c4b6 100644 --- a/_types/src/lib/src/pouchdb/pouchdb-browser.d.ts +++ b/_types/src/lib/src/pouchdb/pouchdb-browser.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import PouchDB from "pouchdb-core"; export { PouchDB }; diff --git a/_types/src/lib/src/pouchdb/pouchdb-http.d.ts b/_types/src/lib/src/pouchdb/pouchdb-http.d.ts index 81d6038..421c4b6 100644 --- a/_types/src/lib/src/pouchdb/pouchdb-http.d.ts +++ b/_types/src/lib/src/pouchdb/pouchdb-http.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import PouchDB from "pouchdb-core"; export { PouchDB }; diff --git a/_types/src/lib/src/pouchdb/pouchdb-test.d.ts b/_types/src/lib/src/pouchdb/pouchdb-test.d.ts index 81d6038..421c4b6 100644 --- a/_types/src/lib/src/pouchdb/pouchdb-test.d.ts +++ b/_types/src/lib/src/pouchdb/pouchdb-test.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import PouchDB from "pouchdb-core"; export { PouchDB }; diff --git a/_types/src/lib/src/pouchdb/utils_couchdb.d.ts b/_types/src/lib/src/pouchdb/utils_couchdb.d.ts index 4848643..9d50bbe 100644 --- a/_types/src/lib/src/pouchdb/utils_couchdb.d.ts +++ b/_types/src/lib/src/pouchdb/utils_couchdb.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const isValidRemoteCouchDBURI: (uri: string) => boolean; export declare function isCloudantURI(uri: string): boolean; export declare function isErrorOfMissingDoc(ex: unknown): boolean; diff --git a/_types/src/lib/src/replication/LiveSyncAbstractReplicator.d.ts b/_types/src/lib/src/replication/LiveSyncAbstractReplicator.d.ts index 9194def..5e8bd36 100644 --- a/_types/src/lib/src/replication/LiveSyncAbstractReplicator.d.ts +++ b/_types/src/lib/src/replication/LiveSyncAbstractReplicator.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc, type DatabaseConnectingStatus, type RemoteDBSettings, type EntryLeaf, type TweakValues, type NodeData } from "@lib/common/types.ts"; import type { RequiredServices } from "@lib/interfaces/ServiceModule"; export type ReplicationCallback = (e: PouchDB.Core.ExistingDocument[]) => Promise | boolean; diff --git a/_types/src/lib/src/replication/SyncParamsHandler.d.ts b/_types/src/lib/src/replication/SyncParamsHandler.d.ts index 7ddc1d7..49c2ccb 100644 --- a/_types/src/lib/src/replication/SyncParamsHandler.d.ts +++ b/_types/src/lib/src/replication/SyncParamsHandler.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { SyncParameters } from "@lib/common/types.ts"; import { LiveSyncError } from "@lib/common/LSError.ts"; /** diff --git a/_types/src/lib/src/replication/couchdb/LiveSyncReplicator.d.ts b/_types/src/lib/src/replication/couchdb/LiveSyncReplicator.d.ts index 0d4add5..cab8619 100644 --- a/_types/src/lib/src/replication/couchdb/LiveSyncReplicator.d.ts +++ b/_types/src/lib/src/replication/couchdb/LiveSyncReplicator.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc, type RemoteDBSettings, type EntryLeaf, type TweakValues, type SyncParameters, type DatabaseEntry, type NodeData } from "@lib/common/types.ts"; import { LiveSyncAbstractReplicator, type LiveSyncReplicatorEnv, type RemoteDBStatus } from "@lib/replication/LiveSyncAbstractReplicator.ts"; import type { ServiceHub } from "@lib/services/ServiceHub.ts"; diff --git a/_types/src/lib/src/replication/httplib.d.ts b/_types/src/lib/src/replication/httplib.d.ts index 87a10d0..aaff7bf 100644 --- a/_types/src/lib/src/replication/httplib.d.ts +++ b/_types/src/lib/src/replication/httplib.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { CouchDBCredentials, JWTCredentials, JWTHeader, JWTParams, JWTPayload, PreparedJWT, RemoteDBSettings } from "@lib/common/types"; import { Computed } from "octagonal-wheels/dataobject/Computed"; /** diff --git a/_types/src/lib/src/replication/journal/JournalSyncCore.d.ts b/_types/src/lib/src/replication/journal/JournalSyncCore.d.ts index 6f7d781..1be2daa 100644 --- a/_types/src/lib/src/replication/journal/JournalSyncCore.d.ts +++ b/_types/src/lib/src/replication/journal/JournalSyncCore.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc, type SyncParameters, type BucketSyncSetting, type RemoteDBSettings } from "@lib/common/types.ts"; import type { ReplicationCallback, ReplicationStat } from "@lib/replication/LiveSyncAbstractReplicator.ts"; import { type SimpleStore } from "@lib/common/utils.ts"; diff --git a/_types/src/lib/src/replication/journal/JournalSyncTypes.d.ts b/_types/src/lib/src/replication/journal/JournalSyncTypes.d.ts index 9ed4923..78997bd 100644 --- a/_types/src/lib/src/replication/journal/JournalSyncTypes.d.ts +++ b/_types/src/lib/src/replication/journal/JournalSyncTypes.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type CheckPointInfo = { lastLocalSeq: number | string; journalEpoch: string; diff --git a/_types/src/lib/src/replication/journal/LiveSyncJournalReplicator.d.ts b/_types/src/lib/src/replication/journal/LiveSyncJournalReplicator.d.ts index de23821..ba6dfdf 100644 --- a/_types/src/lib/src/replication/journal/LiveSyncJournalReplicator.d.ts +++ b/_types/src/lib/src/replication/journal/LiveSyncJournalReplicator.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type RemoteDBSettings, type EntryLeaf, type ChunkVersionRange, type TweakValues, type NodeData } from "@lib/common/types.ts"; import { JournalSyncCore } from "./JournalSyncCore.ts"; import { LiveSyncAbstractReplicator, type RemoteDBStatus } from "@lib/replication/LiveSyncAbstractReplicator.ts"; diff --git a/_types/src/lib/src/replication/journal/LiveSyncJournalReplicatorEnv.d.ts b/_types/src/lib/src/replication/journal/LiveSyncJournalReplicatorEnv.d.ts index 68cb08f..ffa8d7d 100644 --- a/_types/src/lib/src/replication/journal/LiveSyncJournalReplicatorEnv.d.ts +++ b/_types/src/lib/src/replication/journal/LiveSyncJournalReplicatorEnv.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LiveSyncReplicatorEnv } from "@lib/replication/LiveSyncAbstractReplicator"; export interface LiveSyncJournalReplicatorEnv extends LiveSyncReplicatorEnv { // eslint-disable-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface -- Empty interface } diff --git a/_types/src/lib/src/replication/journal/objectstore/JournalStorageAdapter.d.ts b/_types/src/lib/src/replication/journal/objectstore/JournalStorageAdapter.d.ts index 602a0de..41fd33f 100644 --- a/_types/src/lib/src/replication/journal/objectstore/JournalStorageAdapter.d.ts +++ b/_types/src/lib/src/replication/journal/objectstore/JournalStorageAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { RemoteDBStatus } from "@lib/replication/LiveSyncAbstractReplicator.ts"; import type { BucketSyncSetting } from "@lib/common/types.ts"; export interface IJournalStorage { diff --git a/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.d.ts b/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.d.ts index 33932a1..a9a7118 100644 --- a/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.d.ts +++ b/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { S3 } from "@aws-sdk/client-s3"; import { type BucketSyncSetting } from "@lib/common/types.ts"; import type { RemoteDBStatus } from "@lib/replication/LiveSyncAbstractReplicator.ts"; diff --git a/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.integration.spec.d.ts b/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.integration.spec.d.ts index 4b30b39..b549d23 100644 --- a/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.integration.spec.d.ts +++ b/_types/src/lib/src/replication/journal/objectstore/MinioStorageAdapter.integration.spec.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export {}; diff --git a/_types/src/lib/src/replication/trystero/LiveSyncTrysteroReplicator.d.ts b/_types/src/lib/src/replication/trystero/LiveSyncTrysteroReplicator.d.ts index ee4002a..d691786 100644 --- a/_types/src/lib/src/replication/trystero/LiveSyncTrysteroReplicator.d.ts +++ b/_types/src/lib/src/replication/trystero/LiveSyncTrysteroReplicator.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type RemoteDBSettings, type EntryLeaf, type TweakValues, type LOG_LEVEL, type NodeData } from "@lib/common/types"; import { LiveSyncAbstractReplicator, type LiveSyncReplicatorEnv, type RemoteDBStatus } from "@lib/replication/LiveSyncAbstractReplicator"; import { TrysteroReplicator } from "./TrysteroReplicator"; diff --git a/_types/src/lib/src/replication/trystero/P2PLogCollector.d.ts b/_types/src/lib/src/replication/trystero/P2PLogCollector.d.ts index 50f98e1..8402165 100644 --- a/_types/src/lib/src/replication/trystero/P2PLogCollector.d.ts +++ b/_types/src/lib/src/replication/trystero/P2PLogCollector.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { P2PReplicationProgress } from "./TrysteroReplicator"; export declare class P2PLogCollector { constructor(); diff --git a/_types/src/lib/src/replication/trystero/P2PReplicatorBase.d.ts b/_types/src/lib/src/replication/trystero/P2PReplicatorBase.d.ts index d973692..d236c56 100644 --- a/_types/src/lib/src/replication/trystero/P2PReplicatorBase.d.ts +++ b/_types/src/lib/src/replication/trystero/P2PReplicatorBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LOG_LEVEL } from "octagonal-wheels/common/logger"; import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase"; import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2"; diff --git a/_types/src/lib/src/replication/trystero/P2PReplicatorCore.d.ts b/_types/src/lib/src/replication/trystero/P2PReplicatorCore.d.ts index 8e98aa9..2476189 100644 --- a/_types/src/lib/src/replication/trystero/P2PReplicatorCore.d.ts +++ b/_types/src/lib/src/replication/trystero/P2PReplicatorCore.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import type { P2PPaneParams } from "./UseP2PReplicatorResult"; export type P2PViewFactory = (leaf: unknown) => unknown; diff --git a/_types/src/lib/src/replication/trystero/P2PReplicatorPaneCommon.d.ts b/_types/src/lib/src/replication/trystero/P2PReplicatorPaneCommon.d.ts index 8ddb916..ca64643 100644 --- a/_types/src/lib/src/replication/trystero/P2PReplicatorPaneCommon.d.ts +++ b/_types/src/lib/src/replication/trystero/P2PReplicatorPaneCommon.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { InjectableServiceHub } from "@lib/services/InjectableServices.ts"; export declare const EVENT_P2P_PEER_SHOW_EXTRA_MENU = "p2p-peer-show-extra-menu"; export declare enum AcceptedStatus { diff --git a/_types/src/lib/src/replication/trystero/ProxiedDB.d.ts b/_types/src/lib/src/replication/trystero/ProxiedDB.d.ts index 37fab2f..b7e124d 100644 --- a/_types/src/lib/src/replication/trystero/ProxiedDB.d.ts +++ b/_types/src/lib/src/replication/trystero/ProxiedDB.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ReplicatorHostEnv } from "./types"; import type { EntryDoc } from "@lib/common/models/db.definition"; export declare function createHostingDB(env: ReplicatorHostEnv): { diff --git a/_types/src/lib/src/replication/trystero/TrysteroReplicator.d.ts b/_types/src/lib/src/replication/trystero/TrysteroReplicator.d.ts index 44355e8..a8cf6b2 100644 --- a/_types/src/lib/src/replication/trystero/TrysteroReplicator.d.ts +++ b/_types/src/lib/src/replication/trystero/TrysteroReplicator.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EntryDoc, type ObsidianLiveSyncSettings } from "@lib/common/types"; import { type ProgressInfo } from "@lib/pouchdb/ReplicatorShim"; import type { Confirm } from "@lib/interfaces/Confirm"; diff --git a/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PClient.d.ts b/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PClient.d.ts index d2afcba..1ce261d 100644 --- a/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PClient.d.ts +++ b/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PClient.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { PouchDBShim, SomeDocument } from "@lib/pouchdb/ReplicatorShim"; import type { TrysteroReplicatorP2PServer } from "./TrysteroReplicatorP2PServer"; import { type BindableObject, type NonPrivateMethodKeys, type Response } from "./types"; diff --git a/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PConnection.d.ts b/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PConnection.d.ts index 84ea1c3..5fb966f 100644 --- a/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PConnection.d.ts +++ b/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PConnection.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { TrysteroReplicatorP2PServer } from "./TrysteroReplicatorP2PServer"; export { TrysteroReplicatorP2PServer as TrysteroConnection }; diff --git a/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PServer.d.ts b/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PServer.d.ts index b4c3956..8f65f48 100644 --- a/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PServer.d.ts +++ b/_types/src/lib/src/replication/trystero/TrysteroReplicatorP2PServer.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ActionSender, type Room } from "@trystero-p2p/nostr"; import { type P2PSyncSetting } from "@lib/common/types"; import { type ReplicatorHostEnv, type FullFilledDeviceInfo, type Request, type Response, type Payload, type Advertisement, type BindableObject, type BindableFunction } from "./types"; diff --git a/_types/src/lib/src/replication/trystero/UseP2PReplicatorResult.d.ts b/_types/src/lib/src/replication/trystero/UseP2PReplicatorResult.d.ts index d53b90d..52bd378 100644 --- a/_types/src/lib/src/replication/trystero/UseP2PReplicatorResult.d.ts +++ b/_types/src/lib/src/replication/trystero/UseP2PReplicatorResult.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2"; import type { LiveSyncTrysteroReplicator } from "./LiveSyncTrysteroReplicator"; import type { P2PLogCollector } from "./P2PLogCollector"; diff --git a/_types/src/lib/src/replication/trystero/addP2PEventHandlers.d.ts b/_types/src/lib/src/replication/trystero/addP2PEventHandlers.d.ts index 6f2f289..fbe3744 100644 --- a/_types/src/lib/src/replication/trystero/addP2PEventHandlers.d.ts +++ b/_types/src/lib/src/replication/trystero/addP2PEventHandlers.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LiveSyncTrysteroReplicator } from "./LiveSyncTrysteroReplicator"; import type { Advertisement } from "./types"; /** diff --git a/_types/src/lib/src/replication/trystero/rpcCompat.d.ts b/_types/src/lib/src/replication/trystero/rpcCompat.d.ts index 18cec67..39f7711 100644 --- a/_types/src/lib/src/replication/trystero/rpcCompat.d.ts +++ b/_types/src/lib/src/replication/trystero/rpcCompat.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function toRpcMethodName(method: string): string; diff --git a/_types/src/lib/src/replication/trystero/types.d.ts b/_types/src/lib/src/replication/trystero/types.d.ts index bf0c5ea..656314b 100644 --- a/_types/src/lib/src/replication/trystero/types.d.ts +++ b/_types/src/lib/src/replication/trystero/types.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { JsonLike } from "@lib/rpc"; import type { P2PSyncSetting, EntryDoc } from "@lib/common/types"; import type { SimpleStore } from "@lib/common/utils"; diff --git a/_types/src/lib/src/replication/trystero/useP2PReplicatorCommands.d.ts b/_types/src/lib/src/replication/trystero/useP2PReplicatorCommands.d.ts index 7153cde..9f89cde 100644 --- a/_types/src/lib/src/replication/trystero/useP2PReplicatorCommands.d.ts +++ b/_types/src/lib/src/replication/trystero/useP2PReplicatorCommands.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import type { UseP2PReplicatorResult } from "./UseP2PReplicatorResult"; /** diff --git a/_types/src/lib/src/replication/trystero/useP2PReplicatorFeature.d.ts b/_types/src/lib/src/replication/trystero/useP2PReplicatorFeature.d.ts index 98f91e0..29edd74 100644 --- a/_types/src/lib/src/replication/trystero/useP2PReplicatorFeature.d.ts +++ b/_types/src/lib/src/replication/trystero/useP2PReplicatorFeature.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import { LiveSyncTrysteroReplicator } from "./LiveSyncTrysteroReplicator"; import { type UseP2PReplicatorResult } from "./UseP2PReplicatorResult"; diff --git a/_types/src/lib/src/rpc/RpcRoom.d.ts b/_types/src/lib/src/rpc/RpcRoom.d.ts index f3bf5d6..417734d 100644 --- a/_types/src/lib/src/rpc/RpcRoom.d.ts +++ b/_types/src/lib/src/rpc/RpcRoom.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { RpcSession } from "./RpcSession"; import { type JsonLike, type RpcMethodHandler, type RpcRegisterOptions, type RpcRoomOptions } from "./types"; export declare class RpcRoom { diff --git a/_types/src/lib/src/rpc/RpcSession.d.ts b/_types/src/lib/src/rpc/RpcSession.d.ts index ab36958..71789e4 100644 --- a/_types/src/lib/src/rpc/RpcSession.d.ts +++ b/_types/src/lib/src/rpc/RpcSession.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { JsonLike } from "./types"; import type { RpcRoom } from "./RpcRoom"; export declare class RpcSession { diff --git a/_types/src/lib/src/rpc/chunking.d.ts b/_types/src/lib/src/rpc/chunking.d.ts index de08a01..0725eb4 100644 --- a/_types/src/lib/src/rpc/chunking.d.ts +++ b/_types/src/lib/src/rpc/chunking.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function estimateBytes(text: string): number; export declare function splitIntoChunks(payload: string, maxBytes: number): string[]; export declare class IncomingChunkBuffer { diff --git a/_types/src/lib/src/rpc/errors.d.ts b/_types/src/lib/src/rpc/errors.d.ts index c983362..a09ddea 100644 --- a/_types/src/lib/src/rpc/errors.d.ts +++ b/_types/src/lib/src/rpc/errors.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { JsonLike, RpcErrorCode, RpcErrorShape } from "./types"; export declare class RpcError extends Error { code: RpcErrorCode; diff --git a/_types/src/lib/src/rpc/index.d.ts b/_types/src/lib/src/rpc/index.d.ts index c5d05ca..28429c6 100644 --- a/_types/src/lib/src/rpc/index.d.ts +++ b/_types/src/lib/src/rpc/index.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export { RpcRoom } from "./RpcRoom"; export { RpcSession } from "./RpcSession"; export { RpcError } from "./errors"; diff --git a/_types/src/lib/src/rpc/pouchdb/RpcPouchDBProxy.d.ts b/_types/src/lib/src/rpc/pouchdb/RpcPouchDBProxy.d.ts index dbfc70f..d8c6fe7 100644 --- a/_types/src/lib/src/rpc/pouchdb/RpcPouchDBProxy.d.ts +++ b/_types/src/lib/src/rpc/pouchdb/RpcPouchDBProxy.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { RpcSession } from "@lib/rpc/RpcSession"; /** * A PouchDB-compatible proxy that forwards all database operations to a remote diff --git a/_types/src/lib/src/rpc/pouchdb/RpcPouchDBServer.d.ts b/_types/src/lib/src/rpc/pouchdb/RpcPouchDBServer.d.ts index b021f84..65e9884 100644 --- a/_types/src/lib/src/rpc/pouchdb/RpcPouchDBServer.d.ts +++ b/_types/src/lib/src/rpc/pouchdb/RpcPouchDBServer.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { RpcRoom } from "@lib/rpc/RpcRoom"; /** * Exposes a PouchDB database as a set of RPC methods registered on an diff --git a/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.d.ts b/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.d.ts index fc901a0..38c5413 100644 --- a/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.d.ts +++ b/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DiagRTCStats, DiagRTCFailureDiagnosis } from "./DiagRTCPeerConnections.types"; /** * Subscribes to connection status updates. The callback will be called with the latest connection statistics whenever there is a change in the connection status of any RTCPeerConnection instance. diff --git a/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.types.d.ts b/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.types.d.ts index c7fcba6..db85051 100644 --- a/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.types.d.ts +++ b/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.types.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type DiagRTCConnectionStatus = { connectionState: RTCPeerConnection["connectionState"]; iceConnectionState: RTCPeerConnection["iceConnectionState"]; diff --git a/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.utils.d.ts b/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.utils.d.ts index 19c4196..80379d6 100644 --- a/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.utils.d.ts +++ b/_types/src/lib/src/rpc/transports/DiagRTCPeerConnections.utils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type DiagRTCPeerConnectionInternalStateHistory, type DiagRTCFailureDiagnosis, type DiagRTCPeerConnectionMetrics, type DiagRTCFailureStats } from "./DiagRTCPeerConnections.types"; /** * Diagnoses the failure reason of a failed RTCPeerConnection based on its internal state history and selected candidate pair information. diff --git a/_types/src/lib/src/rpc/transports/TrysteroTransport.d.ts b/_types/src/lib/src/rpc/transports/TrysteroTransport.d.ts index 09a08c3..3da3da0 100644 --- a/_types/src/lib/src/rpc/transports/TrysteroTransport.d.ts +++ b/_types/src/lib/src/rpc/transports/TrysteroTransport.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type Room } from "@trystero-p2p/nostr"; import { RpcRoom } from "@lib/rpc/RpcRoom"; import { RpcPouchDBProxy } from "@lib/rpc/pouchdb/RpcPouchDBProxy"; diff --git a/_types/src/lib/src/rpc/transports/trysteroUtils.d.ts b/_types/src/lib/src/rpc/transports/trysteroUtils.d.ts index b2ad7bb..f1ccef5 100644 --- a/_types/src/lib/src/rpc/transports/trysteroUtils.d.ts +++ b/_types/src/lib/src/rpc/transports/trysteroUtils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { BaseRoomConfig } from "@trystero-p2p/nostr"; import type { P2PConnectionInfo } from "@lib/common/models/setting.type"; export declare function generateJoinRoomOptions(settings: P2PConnectionInfo): BaseRoomConfig; diff --git a/_types/src/lib/src/rpc/types.d.ts b/_types/src/lib/src/rpc/types.d.ts index 00e5853..02c0471 100644 --- a/_types/src/lib/src/rpc/types.d.ts +++ b/_types/src/lib/src/rpc/types.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const RPC_VERSION_MAJOR = 1; export declare const RPC_VERSION_MINOR = 0; export type JsonLike = null | boolean | number | string | JsonLike[] | { diff --git a/_types/src/lib/src/serviceFeatures/checkRemoteSize.d.ts b/_types/src/lib/src/serviceFeatures/checkRemoteSize.d.ts index d3ba7f7..caea561 100644 --- a/_types/src/lib/src/serviceFeatures/checkRemoteSize.d.ts +++ b/_types/src/lib/src/serviceFeatures/checkRemoteSize.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { createInstanceLogFunction, type LogFunction } from "@lib/services/lib/logUtils"; import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; /** diff --git a/_types/src/lib/src/serviceFeatures/offlineScanner.d.ts b/_types/src/lib/src/serviceFeatures/offlineScanner.d.ts index 54087ca..e004a4a 100644 --- a/_types/src/lib/src/serviceFeatures/offlineScanner.d.ts +++ b/_types/src/lib/src/serviceFeatures/offlineScanner.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type FilePathWithPrefix, type FilePathWithPrefixLC, type MetaEntry, type UXFileInfoStub, type ObsidianLiveSyncSettings, type LOG_LEVEL } from "@lib/common/types"; import { type LogFunction } from "@lib/services/lib/logUtils"; import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; diff --git a/_types/src/lib/src/serviceFeatures/prepareDatabaseForUse.d.ts b/_types/src/lib/src/serviceFeatures/prepareDatabaseForUse.d.ts index 6f08e2e..1b32721 100644 --- a/_types/src/lib/src/serviceFeatures/prepareDatabaseForUse.d.ts +++ b/_types/src/lib/src/serviceFeatures/prepareDatabaseForUse.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import { UnresolvedErrorManager } from "@lib/services/base/UnresolvedErrorManager"; import { type LogFunction } from "@lib/services/lib/logUtils"; diff --git a/_types/src/lib/src/serviceFeatures/remoteConfig.d.ts b/_types/src/lib/src/serviceFeatures/remoteConfig.d.ts index cb02583..46a907d 100644 --- a/_types/src/lib/src/serviceFeatures/remoteConfig.d.ts +++ b/_types/src/lib/src/serviceFeatures/remoteConfig.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type LOG_LEVEL } from "@lib/common/logger"; import type { ObsidianLiveSyncSettings } from "@lib/common/models/setting.type"; import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; diff --git a/_types/src/lib/src/serviceFeatures/setupObsidian/qrCode.d.ts b/_types/src/lib/src/serviceFeatures/setupObsidian/qrCode.d.ts index df473b9..08fa260 100644 --- a/_types/src/lib/src/serviceFeatures/setupObsidian/qrCode.d.ts +++ b/_types/src/lib/src/serviceFeatures/setupObsidian/qrCode.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import type { SetupFeatureHost } from "./types"; export declare function encodeSetupSettingsAsQR(host: SetupFeatureHost): Promise; diff --git a/_types/src/lib/src/serviceFeatures/setupObsidian/setupUri.d.ts b/_types/src/lib/src/serviceFeatures/setupObsidian/setupUri.d.ts index 51b3a4a..41f46ba 100644 --- a/_types/src/lib/src/serviceFeatures/setupObsidian/setupUri.d.ts +++ b/_types/src/lib/src/serviceFeatures/setupObsidian/setupUri.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LogFunction } from "@lib/services/lib/logUtils"; import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import type { SetupFeatureHost } from "./types"; diff --git a/_types/src/lib/src/serviceFeatures/setupObsidian/types.d.ts b/_types/src/lib/src/serviceFeatures/setupObsidian/types.d.ts index 3008073..04271e6 100644 --- a/_types/src/lib/src/serviceFeatures/setupObsidian/types.d.ts +++ b/_types/src/lib/src/serviceFeatures/setupObsidian/types.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; export type SetupFeatureHost = NecessaryServices<"API" | "UI" | "setting", never>; diff --git a/_types/src/lib/src/serviceFeatures/targetFilter.d.ts b/_types/src/lib/src/serviceFeatures/targetFilter.d.ts index c08e097..b16010f 100644 --- a/_types/src/lib/src/serviceFeatures/targetFilter.d.ts +++ b/_types/src/lib/src/serviceFeatures/targetFilter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type UXFileInfoStub } from "@lib/common/types"; import { type LogFunction } from "@lib/services/lib/logUtils"; import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; diff --git a/_types/src/lib/src/serviceModules/FileAccessBase.d.ts b/_types/src/lib/src/serviceModules/FileAccessBase.d.ts index 8bd0f51..771c656 100644 --- a/_types/src/lib/src/serviceModules/FileAccessBase.d.ts +++ b/_types/src/lib/src/serviceModules/FileAccessBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, UXDataWriteOptions, UXFileInfoStub, UXFolderInfo } from "@lib/common/types.ts"; import type { IStorageAccessManager } from "@lib/interfaces/StorageAccess.ts"; import type { IAPIService, IPathService, ISettingService, IVaultService } from "@lib/services/base/IService.ts"; diff --git a/_types/src/lib/src/serviceModules/Rebuilder.d.ts b/_types/src/lib/src/serviceModules/Rebuilder.d.ts index 02bff12..7c87cc8 100644 --- a/_types/src/lib/src/serviceModules/Rebuilder.d.ts +++ b/_types/src/lib/src/serviceModules/Rebuilder.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { IFileHandler } from "@lib/interfaces/FileHandler"; import type { APIService } from "@lib/services/base/APIService"; import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService"; diff --git a/_types/src/lib/src/serviceModules/ServiceDatabaseFileAccessBase.d.ts b/_types/src/lib/src/serviceModules/ServiceDatabaseFileAccessBase.d.ts index fe41743..33a12d4 100644 --- a/_types/src/lib/src/serviceModules/ServiceDatabaseFileAccessBase.d.ts +++ b/_types/src/lib/src/serviceModules/ServiceDatabaseFileAccessBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXFileInfoStub, FilePathWithPrefix, UXFileInfo, MetaEntry, LoadedEntry, FilePath } from "@lib/common/types"; import type { DatabaseFileAccess } from "@lib/interfaces/DatabaseFileAccess"; import type { StorageAccess } from "@lib/interfaces/StorageAccess"; diff --git a/_types/src/lib/src/serviceModules/ServiceFileAccessBase.d.ts b/_types/src/lib/src/serviceModules/ServiceFileAccessBase.d.ts index b44d837..9d1c6f8 100644 --- a/_types/src/lib/src/serviceModules/ServiceFileAccessBase.d.ts +++ b/_types/src/lib/src/serviceModules/ServiceFileAccessBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, FilePathWithPrefix, UXDataWriteOptions, UXFileInfo, UXFileInfoStub, UXFolderInfo, UXStat } from "@lib/common/types"; import { ServiceModuleBase } from "@lib/serviceModules/ServiceModuleBase"; import type { APIService } from "@lib/services/base/APIService"; diff --git a/_types/src/lib/src/serviceModules/ServiceFileHandlerBase.d.ts b/_types/src/lib/src/serviceModules/ServiceFileHandlerBase.d.ts index f688b49..16f135a 100644 --- a/_types/src/lib/src/serviceModules/ServiceFileHandlerBase.d.ts +++ b/_types/src/lib/src/serviceModules/ServiceFileHandlerBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { AnyEntry, FilePath, FilePathWithPrefix, MetaEntry, UXFileInfo, UXFileInfoStub, UXInternalFileInfoStub } from "@lib/common/types"; import type { IFileHandler } from "@lib/interfaces/FileHandler.ts"; import { ServiceModuleBase } from "@lib/serviceModules/ServiceModuleBase"; diff --git a/_types/src/lib/src/serviceModules/ServiceModuleBase.d.ts b/_types/src/lib/src/serviceModules/ServiceModuleBase.d.ts index ec2181b..91b4d70 100644 --- a/_types/src/lib/src/serviceModules/ServiceModuleBase.d.ts +++ b/_types/src/lib/src/serviceModules/ServiceModuleBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { APIService } from "@lib/services/base/APIService"; import { createInstanceLogFunction } from "@lib/services/lib/logUtils"; export interface ServiceModuleBaseDependencies { diff --git a/_types/src/lib/src/serviceModules/adapters/IConversionAdapter.d.ts b/_types/src/lib/src/serviceModules/adapters/IConversionAdapter.d.ts index 8ea7477..f97f65c 100644 --- a/_types/src/lib/src/serviceModules/adapters/IConversionAdapter.d.ts +++ b/_types/src/lib/src/serviceModules/adapters/IConversionAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXFileInfoStub, UXFolderInfo } from "@lib/common/types.ts"; /** * Conversion adapter interface diff --git a/_types/src/lib/src/serviceModules/adapters/IFileSystemAdapter.d.ts b/_types/src/lib/src/serviceModules/adapters/IFileSystemAdapter.d.ts index a379538..5399cc3 100644 --- a/_types/src/lib/src/serviceModules/adapters/IFileSystemAdapter.d.ts +++ b/_types/src/lib/src/serviceModules/adapters/IFileSystemAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, UXStat } from "@lib/common/types.ts"; import type { IPathAdapter } from "./IPathAdapter.ts"; import type { ITypeGuardAdapter } from "./ITypeGuardAdapter.ts"; diff --git a/_types/src/lib/src/serviceModules/adapters/IPathAdapter.d.ts b/_types/src/lib/src/serviceModules/adapters/IPathAdapter.d.ts index 1a03551..14e0927 100644 --- a/_types/src/lib/src/serviceModules/adapters/IPathAdapter.d.ts +++ b/_types/src/lib/src/serviceModules/adapters/IPathAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath } from "@lib/common/types.ts"; /** * Path operations adapter interface diff --git a/_types/src/lib/src/serviceModules/adapters/IStorageAdapter.d.ts b/_types/src/lib/src/serviceModules/adapters/IStorageAdapter.d.ts index cb6f52f..265f57c 100644 --- a/_types/src/lib/src/serviceModules/adapters/IStorageAdapter.d.ts +++ b/_types/src/lib/src/serviceModules/adapters/IStorageAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXDataWriteOptions, UXStat } from "@lib/common/types.ts"; /** * Storage adapter interface diff --git a/_types/src/lib/src/serviceModules/adapters/ITypeGuardAdapter.d.ts b/_types/src/lib/src/serviceModules/adapters/ITypeGuardAdapter.d.ts index 91b985a..0cd3536 100644 --- a/_types/src/lib/src/serviceModules/adapters/ITypeGuardAdapter.d.ts +++ b/_types/src/lib/src/serviceModules/adapters/ITypeGuardAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** * Type guard adapter interface * Provides runtime type checking for native file system objects diff --git a/_types/src/lib/src/serviceModules/adapters/IVaultAdapter.d.ts b/_types/src/lib/src/serviceModules/adapters/IVaultAdapter.d.ts index ed20e18..dfd00fd 100644 --- a/_types/src/lib/src/serviceModules/adapters/IVaultAdapter.d.ts +++ b/_types/src/lib/src/serviceModules/adapters/IVaultAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXDataWriteOptions } from "@lib/common/types.ts"; /** * Vault adapter interface diff --git a/_types/src/lib/src/serviceModules/adapters/index.d.ts b/_types/src/lib/src/serviceModules/adapters/index.d.ts index d85179a..db661ab 100644 --- a/_types/src/lib/src/serviceModules/adapters/index.d.ts +++ b/_types/src/lib/src/serviceModules/adapters/index.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export type { IPathAdapter } from "./IPathAdapter.ts"; export type { ITypeGuardAdapter } from "./ITypeGuardAdapter.ts"; export type { IConversionAdapter } from "./IConversionAdapter.ts"; diff --git a/_types/src/lib/src/services/BrowserServices.d.ts b/_types/src/lib/src/services/BrowserServices.d.ts index a7b18f9..4471bd5 100644 --- a/_types/src/lib/src/services/BrowserServices.d.ts +++ b/_types/src/lib/src/services/BrowserServices.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { InjectableVaultServiceCompat } from "@lib/services/implements/injectable/InjectableVaultService"; import { ServiceContext } from "@lib/services/base/ServiceBase"; import { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub"; diff --git a/_types/src/lib/src/services/HeadlessServices.d.ts b/_types/src/lib/src/services/HeadlessServices.d.ts index 403daf3..513da46 100644 --- a/_types/src/lib/src/services/HeadlessServices.d.ts +++ b/_types/src/lib/src/services/HeadlessServices.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ServiceContext } from "@lib/services/base/ServiceBase"; import { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub"; import type { DatabaseService } from "@lib/services/base/DatabaseService.ts"; diff --git a/_types/src/lib/src/services/InjectableServices.d.ts b/_types/src/lib/src/services/InjectableServices.d.ts index 341456a..044b76e 100644 --- a/_types/src/lib/src/services/InjectableServices.d.ts +++ b/_types/src/lib/src/services/InjectableServices.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub.ts"; diff --git a/_types/src/lib/src/services/ServiceHub.d.ts b/_types/src/lib/src/services/ServiceHub.d.ts index 020c4d6..7e8bea3 100644 --- a/_types/src/lib/src/services/ServiceHub.d.ts +++ b/_types/src/lib/src/services/ServiceHub.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UIService } from "./implements/base/UIService.ts"; import type { ConfigService } from "@lib/services/base/ConfigService.ts"; import type { TestService } from "@lib/services/base/TestService.ts"; diff --git a/_types/src/lib/src/services/base/APIService.d.ts b/_types/src/lib/src/services/base/APIService.d.ts index a6d0516..6888cda 100644 --- a/_types/src/lib/src/services/base/APIService.d.ts +++ b/_types/src/lib/src/services/base/APIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FetchHttpHandler } from "@smithy/fetch-http-handler"; import type { LOG_LEVEL } from "@lib/common/logger"; import type { IAPIService, ICommandCompat } from "./IService"; diff --git a/_types/src/lib/src/services/base/AppLifecycleService.d.ts b/_types/src/lib/src/services/base/AppLifecycleService.d.ts index 266256e..a95efc1 100644 --- a/_types/src/lib/src/services/base/AppLifecycleService.d.ts +++ b/_types/src/lib/src/services/base/AppLifecycleService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { IAppLifecycleService, ISettingService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; export interface AppLifecycleServiceDependencies { diff --git a/_types/src/lib/src/services/base/ConfigService.d.ts b/_types/src/lib/src/services/base/ConfigService.d.ts index be54a2e..e8c418b 100644 --- a/_types/src/lib/src/services/base/ConfigService.d.ts +++ b/_types/src/lib/src/services/base/ConfigService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { IConfigService } from "@lib/services/base/IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; export declare abstract class ConfigService extends ServiceBase implements IConfigService { diff --git a/_types/src/lib/src/services/base/ConflictService.d.ts b/_types/src/lib/src/services/base/ConflictService.d.ts index ab955a2..77117f4 100644 --- a/_types/src/lib/src/services/base/ConflictService.d.ts +++ b/_types/src/lib/src/services/base/ConflictService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePathWithPrefix, MISSING_OR_ERROR, AUTO_MERGED } from "@lib/common/types"; import type { IConflictService } from "@lib/services/base/IService"; import { ServiceBase } from "@lib/services/base/ServiceBase"; diff --git a/_types/src/lib/src/services/base/ControlService.d.ts b/_types/src/lib/src/services/base/ControlService.d.ts index 7a88eff..83d5aa0 100644 --- a/_types/src/lib/src/services/base/ControlService.d.ts +++ b/_types/src/lib/src/services/base/ControlService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { createInstanceLogFunction } from "@lib/services/lib/logUtils"; import type { APIService } from "./APIService"; import type { DatabaseService } from "./DatabaseService"; diff --git a/_types/src/lib/src/services/base/DatabaseEventService.d.ts b/_types/src/lib/src/services/base/DatabaseEventService.d.ts index a4ee617..d73eab7 100644 --- a/_types/src/lib/src/services/base/DatabaseEventService.d.ts +++ b/_types/src/lib/src/services/base/DatabaseEventService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { IDatabaseEventService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; /** diff --git a/_types/src/lib/src/services/base/DatabaseService.d.ts b/_types/src/lib/src/services/base/DatabaseService.d.ts index 4a66944..64b4478 100644 --- a/_types/src/lib/src/services/base/DatabaseService.d.ts +++ b/_types/src/lib/src/services/base/DatabaseService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { IDatabaseService, IPathService, IVaultService, openDatabaseParameters } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; import { LiveSyncLocalDB } from "@lib/pouchdb/LiveSyncLocalDB"; diff --git a/_types/src/lib/src/services/base/FileProcessingService.d.ts b/_types/src/lib/src/services/base/FileProcessingService.d.ts index 7fb1b56..5eb5252 100644 --- a/_types/src/lib/src/services/base/FileProcessingService.d.ts +++ b/_types/src/lib/src/services/base/FileProcessingService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { IFileProcessingService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; /** diff --git a/_types/src/lib/src/services/base/IService.d.ts b/_types/src/lib/src/services/base/IService.d.ts index 361653d..2e73e52 100644 --- a/_types/src/lib/src/services/base/IService.d.ts +++ b/_types/src/lib/src/services/base/IService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FetchHttpHandler } from "@smithy/fetch-http-handler"; import { type LOG_LEVEL } from "octagonal-wheels/common/logger"; import type { AnyEntry, AUTO_MERGED, CouchDBCredentials, diff_result, DocumentID, EntryDoc, EntryHasPath, FileEventItem, FilePath, FilePathWithPrefix, LoadedEntry, MetaEntry, MISSING_OR_ERROR, ObsidianLiveSyncSettings, RemoteDBSettings, TweakValues, UXFileInfo, UXFileInfoStub } from "@lib/common/types"; diff --git a/_types/src/lib/src/services/base/KeyValueDBService.d.ts b/_types/src/lib/src/services/base/KeyValueDBService.d.ts index 30458f4..b17c0d2 100644 --- a/_types/src/lib/src/services/base/KeyValueDBService.d.ts +++ b/_types/src/lib/src/services/base/KeyValueDBService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { SimpleStore } from "@lib/common/utils"; import type { IKeyValueDBService, IVaultService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/base/PathService.d.ts b/_types/src/lib/src/services/base/PathService.d.ts index 01dce52..d775247 100644 --- a/_types/src/lib/src/services/base/PathService.d.ts +++ b/_types/src/lib/src/services/base/PathService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DocumentID, EntryHasPath, FilePathWithPrefix, FilePath, AnyEntry, UXFileInfo, UXFileInfoStub } from "@lib/common/types"; import type { IPathService, ISettingService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/base/RemoteService.d.ts b/_types/src/lib/src/services/base/RemoteService.d.ts index cd00089..3884c15 100644 --- a/_types/src/lib/src/services/base/RemoteService.d.ts +++ b/_types/src/lib/src/services/base/RemoteService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { CouchDBCredentials, EntryDoc } from "@lib/common/types"; import type { IRemoteService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/base/ReplicationService.d.ts b/_types/src/lib/src/services/base/ReplicationService.d.ts index c3e36c2..7aadc95 100644 --- a/_types/src/lib/src/services/base/ReplicationService.d.ts +++ b/_types/src/lib/src/services/base/ReplicationService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type LOG_LEVEL } from "@lib/common/types"; import type { IAPIService, IDatabaseService, IFileProcessingService, IReplicationService, IReplicatorService, ISettingService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/base/ReplicatorService.d.ts b/_types/src/lib/src/services/base/ReplicatorService.d.ts index e37d2b6..e18dca2 100644 --- a/_types/src/lib/src/services/base/ReplicatorService.d.ts +++ b/_types/src/lib/src/services/base/ReplicatorService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LiveSyncAbstractReplicator } from "@lib/replication/LiveSyncAbstractReplicator"; import type { IReplicatorService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/base/ServiceBase.d.ts b/_types/src/lib/src/services/base/ServiceBase.d.ts index a45f851..1735ec9 100644 --- a/_types/src/lib/src/services/base/ServiceBase.d.ts +++ b/_types/src/lib/src/services/base/ServiceBase.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare class ServiceContext { } export declare abstract class ServiceBase { diff --git a/_types/src/lib/src/services/base/SettingService.d.ts b/_types/src/lib/src/services/base/SettingService.d.ts index c982109..36b4ed1 100644 --- a/_types/src/lib/src/services/base/SettingService.d.ts +++ b/_types/src/lib/src/services/base/SettingService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ObsidianLiveSyncSettings } from "@lib/common/types"; import type { IAPIService, ISettingService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/base/TestService.d.ts b/_types/src/lib/src/services/base/TestService.d.ts index e74d776..7268f5a 100644 --- a/_types/src/lib/src/services/base/TestService.d.ts +++ b/_types/src/lib/src/services/base/TestService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ITestService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; /** diff --git a/_types/src/lib/src/services/base/TweakValueService.d.ts b/_types/src/lib/src/services/base/TweakValueService.d.ts index 1e1af78..b4384b3 100644 --- a/_types/src/lib/src/services/base/TweakValueService.d.ts +++ b/_types/src/lib/src/services/base/TweakValueService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { RemoteDBSettings, TweakValues } from "@lib/common/types"; import type { ITweakValueService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/base/UnresolvedErrorManager.d.ts b/_types/src/lib/src/services/base/UnresolvedErrorManager.d.ts index 7716a85..f141675 100644 --- a/_types/src/lib/src/services/base/UnresolvedErrorManager.d.ts +++ b/_types/src/lib/src/services/base/UnresolvedErrorManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type LOG_LEVEL } from "octagonal-wheels/common/logger"; import type { AppLifecycleService } from "./AppLifecycleService"; export declare class UnresolvedErrorManager { diff --git a/_types/src/lib/src/services/base/VaultService.d.ts b/_types/src/lib/src/services/base/VaultService.d.ts index 4271560..0af99d3 100644 --- a/_types/src/lib/src/services/base/VaultService.d.ts +++ b/_types/src/lib/src/services/base/VaultService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath } from "@lib/common/types"; import type { IAPIService, ISettingService, IVaultService } from "./IService"; import { ServiceBase, type ServiceContext } from "./ServiceBase"; diff --git a/_types/src/lib/src/services/implements/base/UIService.d.ts b/_types/src/lib/src/services/implements/base/UIService.d.ts index 3285a9d..eb10a8c 100644 --- a/_types/src/lib/src/services/implements/base/UIService.d.ts +++ b/_types/src/lib/src/services/implements/base/UIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { Confirm } from "@lib/interfaces/Confirm"; import type { ComponentHasResult, SvelteDialogManagerBase } from "@lib/UI/svelteDialog"; import type { IAPIService, IUIService } from "@lib/services/base/IService"; diff --git a/_types/src/lib/src/services/implements/browser/BrowserAPIService.d.ts b/_types/src/lib/src/services/implements/browser/BrowserAPIService.d.ts index 6fc38d4..6884c38 100644 --- a/_types/src/lib/src/services/implements/browser/BrowserAPIService.d.ts +++ b/_types/src/lib/src/services/implements/browser/BrowserAPIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { InjectableAPIService } from "@lib/services/implements/injectable/InjectableAPIService"; import type { FetchHttpHandler } from "@smithy/fetch-http-handler"; diff --git a/_types/src/lib/src/services/implements/browser/BrowserConfirm.d.ts b/_types/src/lib/src/services/implements/browser/BrowserConfirm.d.ts index 3e61acd..2ae70b4 100644 --- a/_types/src/lib/src/services/implements/browser/BrowserConfirm.d.ts +++ b/_types/src/lib/src/services/implements/browser/BrowserConfirm.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { Confirm } from "@lib/interfaces/Confirm"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare class BrowserConfirm implements Confirm { diff --git a/_types/src/lib/src/services/implements/browser/BrowserDatabaseService.d.ts b/_types/src/lib/src/services/implements/browser/BrowserDatabaseService.d.ts index fe6c9ea..317b1e9 100644 --- a/_types/src/lib/src/services/implements/browser/BrowserDatabaseService.d.ts +++ b/_types/src/lib/src/services/implements/browser/BrowserDatabaseService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { KeyValueDBService } from "@lib/services/base/KeyValueDBService"; import { DatabaseService } from "@lib/services/base/DatabaseService.ts"; diff --git a/_types/src/lib/src/services/implements/browser/BrowserUIService.d.ts b/_types/src/lib/src/services/implements/browser/BrowserUIService.d.ts index 3728b65..19efff9 100644 --- a/_types/src/lib/src/services/implements/browser/BrowserUIService.d.ts +++ b/_types/src/lib/src/services/implements/browser/BrowserUIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { UIService } from "@lib/services/implements/base/UIService"; import type { ConfigService } from "@lib/services/base/ConfigService"; import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService"; diff --git a/_types/src/lib/src/services/implements/browser/ConfigServiceBrowserCompat.d.ts b/_types/src/lib/src/services/implements/browser/ConfigServiceBrowserCompat.d.ts index 41ccec8..4c1bfd2 100644 --- a/_types/src/lib/src/services/implements/browser/ConfigServiceBrowserCompat.d.ts +++ b/_types/src/lib/src/services/implements/browser/ConfigServiceBrowserCompat.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ConfigService } from "@lib/services/base/ConfigService"; import type { IAPIService, ISettingService } from "@lib/services/base/IService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; diff --git a/_types/src/lib/src/services/implements/browser/Menu.d.ts b/_types/src/lib/src/services/implements/browser/Menu.d.ts index 598314c..bf16a95 100644 --- a/_types/src/lib/src/services/implements/browser/Menu.d.ts +++ b/_types/src/lib/src/services/implements/browser/Menu.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type PromiseWithResolvers } from "octagonal-wheels/promises"; export declare class MenuItem { type: string; diff --git a/_types/src/lib/src/services/implements/browser/ui/renderMessageMarkdown.d.ts b/_types/src/lib/src/services/implements/browser/ui/renderMessageMarkdown.d.ts index 83f8cf0..50fe190 100644 --- a/_types/src/lib/src/services/implements/browser/ui/renderMessageMarkdown.d.ts +++ b/_types/src/lib/src/services/implements/browser/ui/renderMessageMarkdown.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function renderMessageMarkdown(message: string): string; diff --git a/_types/src/lib/src/services/implements/headless/HeadlessAPIService.d.ts b/_types/src/lib/src/services/implements/headless/HeadlessAPIService.d.ts index 82b4f97..1dc05df 100644 --- a/_types/src/lib/src/services/implements/headless/HeadlessAPIService.d.ts +++ b/_types/src/lib/src/services/implements/headless/HeadlessAPIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { InjectableAPIService } from "@lib/services/implements/injectable/InjectableAPIService"; import type { FetchHttpHandler } from "@smithy/fetch-http-handler"; diff --git a/_types/src/lib/src/services/implements/headless/HeadlessDatabaseService.d.ts b/_types/src/lib/src/services/implements/headless/HeadlessDatabaseService.d.ts index d8156c6..249dcbf 100644 --- a/_types/src/lib/src/services/implements/headless/HeadlessDatabaseService.d.ts +++ b/_types/src/lib/src/services/implements/headless/HeadlessDatabaseService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { KeyValueDBService } from "@lib/services/base/KeyValueDBService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { DatabaseService } from "@lib/services/base/DatabaseService.ts"; diff --git a/_types/src/lib/src/services/implements/injectable/InjectableAPIService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableAPIService.d.ts index 5bfc80e..36ec6cf 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableAPIService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableAPIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { APIService } from "@lib/services/base/APIService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare abstract class InjectableAPIService extends APIService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableAppLifecycleService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableAppLifecycleService.d.ts index 0e4b0c3..400c608 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableAppLifecycleService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableAppLifecycleService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AppLifecycleService } from "@lib/services/base/AppLifecycleService"; import type { IAppLifecycleService } from "@lib/services/base/IService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; diff --git a/_types/src/lib/src/services/implements/injectable/InjectableConflictService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableConflictService.d.ts index 41251ab..b9106b3 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableConflictService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableConflictService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ConflictService } from "@lib/services/base/ConflictService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare class InjectableConflictService extends ConflictService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableDatabaseEventService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableDatabaseEventService.d.ts index 8fb79f7..7b730c7 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableDatabaseEventService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableDatabaseEventService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { DatabaseEventService } from "@lib/services/base/DatabaseEventService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare class InjectableDatabaseEventService extends DatabaseEventService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableFileProcessingService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableFileProcessingService.d.ts index 69ac573..cc5809f 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableFileProcessingService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableFileProcessingService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { FileProcessingService } from "@lib/services/base/FileProcessingService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare class InjectableFileProcessingService extends FileProcessingService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectablePathService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectablePathService.d.ts index 9df55a0..a682381 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectablePathService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectablePathService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXFileInfo, AnyEntry, UXFileInfoStub, FilePathWithPrefix } from "@lib/common/types"; import { PathService } from "@lib/services/base/PathService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; diff --git a/_types/src/lib/src/services/implements/injectable/InjectableRemoteService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableRemoteService.d.ts index 74358aa..c7f7e52 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableRemoteService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableRemoteService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { RemoteService } from "@lib/services/base/RemoteService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare class InjectableRemoteService extends RemoteService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableReplicationService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableReplicationService.d.ts index 93ecbfc..cfad5a2 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableReplicationService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableReplicationService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ReplicationService } from "@lib/services/base/ReplicationService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare class InjectableReplicationService extends ReplicationService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableReplicatorService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableReplicatorService.d.ts index bea51f4..ab2a1a7 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableReplicatorService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableReplicatorService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ReplicatorService } from "@lib/services/base/ReplicatorService"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; export declare class InjectableReplicatorService extends ReplicatorService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableServiceHub.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableServiceHub.d.ts index ade4fec..04f93ee 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableServiceHub.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableServiceHub.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ConfigService } from "@lib/services/base/ConfigService"; import { ControlService } from "@lib/services/base/ControlService"; import type { KeyValueDBService } from "@lib/services/base/KeyValueDBService"; diff --git a/_types/src/lib/src/services/implements/injectable/InjectableServices.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableServices.d.ts index 246e53f..802d10c 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableServices.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableServices.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ServiceInstances } from "@lib/services/ServiceHub.ts"; import type { UIService } from "@lib/services/implements/base/UIService.ts"; import type { ConfigService } from "@lib/services/base/ConfigService.ts"; diff --git a/_types/src/lib/src/services/implements/injectable/InjectableSettingService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableSettingService.d.ts index f4f9872..fd76d35 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableSettingService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableSettingService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { SettingService, type SettingServiceDependencies } from "@lib/services/base/SettingService"; import type { ObsidianLiveSyncSettings } from "@lib/common/types"; diff --git a/_types/src/lib/src/services/implements/injectable/InjectableTestService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableTestService.d.ts index 08c8b49..2526324 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableTestService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableTestService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { TestService } from "@lib/services/base/TestService"; export declare class InjectableTestService extends TestService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableTweakValueService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableTweakValueService.d.ts index ef14c7a..9347ae6 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableTweakValueService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableTweakValueService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { TweakValueService } from "@lib/services/base/TweakValueService"; export declare class InjectableTweakValueService extends TweakValueService { diff --git a/_types/src/lib/src/services/implements/injectable/InjectableVaultService.d.ts b/_types/src/lib/src/services/implements/injectable/InjectableVaultService.d.ts index c8c2103..da6fd3f 100644 --- a/_types/src/lib/src/services/implements/injectable/InjectableVaultService.d.ts +++ b/_types/src/lib/src/services/implements/injectable/InjectableVaultService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { VaultService } from "@lib/services/base/VaultService"; export declare abstract class InjectableVaultService extends VaultService { diff --git a/_types/src/lib/src/services/implements/obsidian/ObsidianServiceContext.d.ts b/_types/src/lib/src/services/implements/obsidian/ObsidianServiceContext.d.ts index a47fd09..6d4e2f9 100644 --- a/_types/src/lib/src/services/implements/obsidian/ObsidianServiceContext.d.ts +++ b/_types/src/lib/src/services/implements/obsidian/ObsidianServiceContext.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ServiceContext } from "@lib/services/base/ServiceBase"; import type ObsidianLiveSyncPlugin from "@/main"; import type { App, Plugin } from "@/deps"; diff --git a/_types/src/lib/src/services/lib/HandlerUtils.d.ts b/_types/src/lib/src/services/lib/HandlerUtils.d.ts index 6f037d4..ac743a8 100644 --- a/_types/src/lib/src/services/lib/HandlerUtils.d.ts +++ b/_types/src/lib/src/services/lib/HandlerUtils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** * A function type that can be used as a handler. */ diff --git a/_types/src/lib/src/services/lib/logUtils.d.ts b/_types/src/lib/src/services/lib/logUtils.d.ts index 85477e9..47e2e01 100644 --- a/_types/src/lib/src/services/lib/logUtils.d.ts +++ b/_types/src/lib/src/services/lib/logUtils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type LOG_LEVEL } from "octagonal-wheels/common/logger"; import type { IAPIService } from "@lib/services/base/IService"; export declare const MARK_LOG_SEPARATOR = "\u200A"; diff --git a/_types/src/lib/src/string_and_binary/chunks.d.ts b/_types/src/lib/src/string_and_binary/chunks.d.ts index f19adca..47ae778 100644 --- a/_types/src/lib/src/string_and_binary/chunks.d.ts +++ b/_types/src/lib/src/string_and_binary/chunks.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare function splitPiecesTextV2(dataSrc: string | string[], pieceSize: number, minimumChunkSize: number): () => Generator; export declare function binaryTextSplit(data: string, pieceSize: number, minimumChunkSize: number): () => Generator; export declare function splitPiecesText(dataSrc: string | string[], pieceSize: number, plainSplit: boolean, minimumChunkSize: number, useSegmenter: boolean): () => Generator; diff --git a/_types/src/lib/src/string_and_binary/convert.d.ts b/_types/src/lib/src/string_and_binary/convert.d.ts index 9552c2d..0f33cb0 100644 --- a/_types/src/lib/src/string_and_binary/convert.d.ts +++ b/_types/src/lib/src/string_and_binary/convert.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { arrayBufferToBase64, base64ToArrayBuffer, base64ToArrayBufferInternalBrowser, readString, writeString, tryConvertBase64ToArrayBuffer } from "octagonal-wheels/binary"; export { arrayBufferToBase64, base64ToArrayBuffer, base64ToArrayBufferInternalBrowser, readString, writeString, tryConvertBase64ToArrayBuffer, }; export declare function arrayBufferToBase64Single(buffer: Uint8Array | ArrayBuffer): Promise; diff --git a/_types/src/lib/src/string_and_binary/hash.d.ts b/_types/src/lib/src/string_and_binary/hash.d.ts index def159b..c9af528 100644 --- a/_types/src/lib/src/string_and_binary/hash.d.ts +++ b/_types/src/lib/src/string_and_binary/hash.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export * from "octagonal-wheels/hash/xxhash.js"; export type * from "octagonal-wheels/hash/xxhash.js"; diff --git a/_types/src/lib/src/string_and_binary/path.d.ts b/_types/src/lib/src/string_and_binary/path.d.ts index 33069f5..7739310 100644 --- a/_types/src/lib/src/string_and_binary/path.d.ts +++ b/_types/src/lib/src/string_and_binary/path.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type AnyEntry, type DocumentID, type EntryHasPath, type FilePath, type FilePathWithPrefix } from "@lib/common/types.ts"; export declare function isValidFilenameInWidows(filename: string): boolean; export declare function isValidFilenameInDarwin(filename: string): boolean; diff --git a/_types/src/lib/src/system/wakelock.d.ts b/_types/src/lib/src/system/wakelock.d.ts index 50f9220..0f80c96 100644 --- a/_types/src/lib/src/system/wakelock.d.ts +++ b/_types/src/lib/src/system/wakelock.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 /** * Run callback with screen wake lock held. * @param callback Callback to run with wake lock held diff --git a/_types/src/lib/src/worker/bg.common.d.ts b/_types/src/lib/src/worker/bg.common.d.ts index f94bb03..535e180 100644 --- a/_types/src/lib/src/worker/bg.common.d.ts +++ b/_types/src/lib/src/worker/bg.common.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { END_OF_DATA } from "./universalTypes.ts"; export declare function postBack(key: number, seq: number, data: string | END_OF_DATA): void; diff --git a/_types/src/lib/src/worker/bg.worker.d.ts b/_types/src/lib/src/worker/bg.worker.d.ts index 4b30b39..b549d23 100644 --- a/_types/src/lib/src/worker/bg.worker.d.ts +++ b/_types/src/lib/src/worker/bg.worker.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export {}; diff --git a/_types/src/lib/src/worker/bg.worker.encryption.d.ts b/_types/src/lib/src/worker/bg.worker.encryption.d.ts index 214985f..c3e1484 100644 --- a/_types/src/lib/src/worker/bg.worker.encryption.d.ts +++ b/_types/src/lib/src/worker/bg.worker.encryption.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EncryptHKDFArguments } from "./universalTypes.ts"; import type { EncryptArguments } from "./universalTypes.ts"; /** diff --git a/_types/src/lib/src/worker/bg.worker.splitting.d.ts b/_types/src/lib/src/worker/bg.worker.splitting.d.ts index e0b7e9a..fd5ed65 100644 --- a/_types/src/lib/src/worker/bg.worker.splitting.d.ts +++ b/_types/src/lib/src/worker/bg.worker.splitting.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { SplitArguments } from "./universalTypes.ts"; /** * Processes the splitting of data into chunks. diff --git a/_types/src/lib/src/worker/bgWorker.d.ts b/_types/src/lib/src/worker/bgWorker.d.ts index dd8b279..c844de3 100644 --- a/_types/src/lib/src/worker/bgWorker.d.ts +++ b/_types/src/lib/src/worker/bgWorker.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EncryptArguments, EncryptHKDFArguments, EncryptHKDFProcessItem, EncryptProcessItem, ProcessItem, SplitArguments, SplitProcessItem } from "./universalTypes.ts"; export type WorkerInstance = { worker: Worker; diff --git a/_types/src/lib/src/worker/bgWorker.encryption.d.ts b/_types/src/lib/src/worker/bgWorker.encryption.d.ts index 0c4da4d..b9e94fb 100644 --- a/_types/src/lib/src/worker/bgWorker.encryption.d.ts +++ b/_types/src/lib/src/worker/bgWorker.encryption.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type EncryptHKDFProcessItem, type ResultPayload } from "./universalTypes.ts"; import { type EncryptProcessItem } from "./universalTypes.ts"; import { type EncryptHKDFArguments } from "./universalTypes.ts"; diff --git a/_types/src/lib/src/worker/bgWorker.mock.d.ts b/_types/src/lib/src/worker/bgWorker.mock.d.ts index 099e749..e5b950c 100644 --- a/_types/src/lib/src/worker/bgWorker.mock.d.ts +++ b/_types/src/lib/src/worker/bgWorker.mock.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { EncryptHKDFProcessItem, EncryptProcessItem, SplitProcessItem, ProcessItem } from "./universalTypes.ts"; export type SplitArguments = { key: number; diff --git a/_types/src/lib/src/worker/bgWorker.splitting.d.ts b/_types/src/lib/src/worker/bgWorker.splitting.d.ts index c2d234f..c4d971a 100644 --- a/_types/src/lib/src/worker/bgWorker.splitting.d.ts +++ b/_types/src/lib/src/worker/bgWorker.splitting.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ResultPayloadWithSeq, type SplitProcessItem } from "./universalTypes"; /** * Splits data into pieces using a worker. diff --git a/_types/src/lib/src/worker/universalTypes.d.ts b/_types/src/lib/src/worker/universalTypes.d.ts index 2b48368..7f8dde2 100644 --- a/_types/src/lib/src/worker/universalTypes.d.ts +++ b/_types/src/lib/src/worker/universalTypes.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { PromiseWithResolvers } from "octagonal-wheels/promises"; export type EncryptArguments = { key: number; diff --git a/_types/src/main.d.ts b/_types/src/main.d.ts index 07f4a04..9a34481 100644 --- a/_types/src/main.d.ts +++ b/_types/src/main.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { Plugin, type App, type PluginManifest } from "./deps"; import { LiveSyncCommands } from "./features/LiveSyncCommands.ts"; import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext.ts"; diff --git a/_types/src/managers/ObsidianStorageEventManagerAdapter.d.ts b/_types/src/managers/ObsidianStorageEventManagerAdapter.d.ts index ab0bc8a..4714fac 100644 --- a/_types/src/managers/ObsidianStorageEventManagerAdapter.d.ts +++ b/_types/src/managers/ObsidianStorageEventManagerAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { TFile, TFolder } from "@/deps"; import type { FilePath, UXFileInfoStub, UXInternalFileInfoStub } from "@lib/common/types"; import type { FileEventItem } from "@lib/common/types"; diff --git a/_types/src/managers/StorageEventManagerObsidian.d.ts b/_types/src/managers/StorageEventManagerObsidian.d.ts index 1818bf9..519bc65 100644 --- a/_types/src/managers/StorageEventManagerObsidian.d.ts +++ b/_types/src/managers/StorageEventManagerObsidian.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath } from "@lib/common/types"; import type ObsidianLiveSyncPlugin from "@/main"; import type { LiveSyncCore } from "@/main"; diff --git a/_types/src/modules/AbstractModule.d.ts b/_types/src/modules/AbstractModule.d.ts index 7568ea8..ac2e827 100644 --- a/_types/src/modules/AbstractModule.d.ts +++ b/_types/src/modules/AbstractModule.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { AnyEntry, FilePathWithPrefix } from "@lib/common/types"; import type { IMinimumLiveSyncCommands, LiveSyncBaseCore } from "@/LiveSyncBaseCore"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; diff --git a/_types/src/modules/AbstractObsidianModule.d.ts b/_types/src/modules/AbstractObsidianModule.d.ts index 90e04a3..37e407a 100644 --- a/_types/src/modules/AbstractObsidianModule.d.ts +++ b/_types/src/modules/AbstractObsidianModule.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LiveSyncCore } from "@/main"; import type ObsidianLiveSyncPlugin from "@/main"; import { AbstractModule } from "./AbstractModule.ts"; diff --git a/_types/src/modules/core/ModulePeriodicProcess.d.ts b/_types/src/modules/core/ModulePeriodicProcess.d.ts index 5a6b20b..a819c86 100644 --- a/_types/src/modules/core/ModulePeriodicProcess.d.ts +++ b/_types/src/modules/core/ModulePeriodicProcess.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { PeriodicProcessor } from "@/common/PeriodicProcessor"; import type { LiveSyncCore } from "@/main"; import { AbstractModule } from "@/modules/AbstractModule"; diff --git a/_types/src/modules/core/ModuleReplicator.d.ts b/_types/src/modules/core/ModuleReplicator.d.ts index 2d911d4..c7cacba 100644 --- a/_types/src/modules/core/ModuleReplicator.d.ts +++ b/_types/src/modules/core/ModuleReplicator.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AbstractModule } from "@/modules/AbstractModule"; import { type EntryDoc, type RemoteType } from "@lib/common/types"; import type { LiveSyncCore } from "@/main"; diff --git a/_types/src/modules/core/ModuleReplicatorCouchDB.d.ts b/_types/src/modules/core/ModuleReplicatorCouchDB.d.ts index 4b5ff02..c40ad61 100644 --- a/_types/src/modules/core/ModuleReplicatorCouchDB.d.ts +++ b/_types/src/modules/core/ModuleReplicatorCouchDB.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type RemoteDBSettings } from "@lib/common/types"; import type { LiveSyncAbstractReplicator } from "@lib/replication/LiveSyncAbstractReplicator"; import { AbstractModule } from "@/modules/AbstractModule"; diff --git a/_types/src/modules/core/ModuleReplicatorMinIO.d.ts b/_types/src/modules/core/ModuleReplicatorMinIO.d.ts index f2be06e..f0f83d2 100644 --- a/_types/src/modules/core/ModuleReplicatorMinIO.d.ts +++ b/_types/src/modules/core/ModuleReplicatorMinIO.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type RemoteDBSettings } from "@lib/common/types"; import type { LiveSyncAbstractReplicator } from "@lib/replication/LiveSyncAbstractReplicator"; import type { LiveSyncCore } from "@/main"; diff --git a/_types/src/modules/core/ReplicateResultProcessor.d.ts b/_types/src/modules/core/ReplicateResultProcessor.d.ts index e6d174b..6a427e6 100644 --- a/_types/src/modules/core/ReplicateResultProcessor.d.ts +++ b/_types/src/modules/core/ReplicateResultProcessor.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type AnyEntry, type EntryDoc, type LoadedEntry, type MetaEntry } from "@lib/common/types"; import type { ModuleReplicator } from "./ModuleReplicator"; import type { ReactiveSource } from "octagonal-wheels/dataobject/reactive_v2"; diff --git a/_types/src/modules/coreFeatures/ModuleConflictChecker.d.ts b/_types/src/modules/coreFeatures/ModuleConflictChecker.d.ts index 4824060..ece57d7 100644 --- a/_types/src/modules/coreFeatures/ModuleConflictChecker.d.ts +++ b/_types/src/modules/coreFeatures/ModuleConflictChecker.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AbstractModule } from "@/modules/AbstractModule.ts"; import { type FilePathWithPrefix } from "@lib/common/types"; import { QueueProcessor } from "octagonal-wheels/concurrency/processor"; diff --git a/_types/src/modules/coreFeatures/ModuleConflictResolver.d.ts b/_types/src/modules/coreFeatures/ModuleConflictResolver.d.ts index b1c3302..654e569 100644 --- a/_types/src/modules/coreFeatures/ModuleConflictResolver.d.ts +++ b/_types/src/modules/coreFeatures/ModuleConflictResolver.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AbstractModule } from "@/modules/AbstractModule.ts"; import { type diff_check_result, type FilePathWithPrefix } from "@lib/common/types"; import type { InjectableServiceHub } from "@lib/services/InjectableServices.ts"; diff --git a/_types/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.d.ts b/_types/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.d.ts index 7d37db6..71a3cff 100644 --- a/_types/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.d.ts +++ b/_types/src/modules/coreFeatures/ModuleResolveMismatchedTweaks.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type TweakValues, type ObsidianLiveSyncSettings, type RemoteDBSettings } from "@lib/common/types.ts"; import { AbstractModule } from "@/modules/AbstractModule.ts"; import type { InjectableServiceHub } from "@lib/services/InjectableServices.ts"; diff --git a/_types/src/modules/coreObsidian/UILib/dialogs.d.ts b/_types/src/modules/coreObsidian/UILib/dialogs.d.ts index ee24c57..44906a6 100644 --- a/_types/src/modules/coreObsidian/UILib/dialogs.d.ts +++ b/_types/src/modules/coreObsidian/UILib/dialogs.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ButtonComponent } from "@/deps.ts"; import { App, FuzzySuggestModal, Modal, Plugin, Component } from "@/deps.ts"; import { type CompatIntervalHandle } from "@lib/common/coreEnvFunctions.ts"; diff --git a/_types/src/modules/coreObsidian/storageLib/utilObsidian.d.ts b/_types/src/modules/coreObsidian/storageLib/utilObsidian.d.ts index cca296b..b42b278 100644 --- a/_types/src/modules/coreObsidian/storageLib/utilObsidian.d.ts +++ b/_types/src/modules/coreObsidian/storageLib/utilObsidian.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { TFile, type TAbstractFile, type TFolder } from "@/deps.ts"; import type { FilePathWithPrefix, UXFileInfo, UXFileInfoStub, UXFolderInfo, UXInternalFileInfoStub } from "@lib/common/types.ts"; import type { LiveSyncCore } from "@/main.ts"; diff --git a/_types/src/modules/essential/ModuleBasicMenu.d.ts b/_types/src/modules/essential/ModuleBasicMenu.d.ts index db78a82..b8ed6e3 100644 --- a/_types/src/modules/essential/ModuleBasicMenu.d.ts +++ b/_types/src/modules/essential/ModuleBasicMenu.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LiveSyncCore } from "@/main"; import { AbstractModule } from "@/modules/AbstractModule"; export declare class ModuleBasicMenu extends AbstractModule { diff --git a/_types/src/modules/essential/ModuleMigration.d.ts b/_types/src/modules/essential/ModuleMigration.d.ts index cc0462f..7017930 100644 --- a/_types/src/modules/essential/ModuleMigration.d.ts +++ b/_types/src/modules/essential/ModuleMigration.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AbstractModule } from "@/modules/AbstractModule.ts"; import type { LiveSyncCore } from "@/main.ts"; export declare class ModuleMigration extends AbstractModule { diff --git a/_types/src/modules/essentialObsidian/APILib/ObsHttpHandler.d.ts b/_types/src/modules/essentialObsidian/APILib/ObsHttpHandler.d.ts index f92215d..0506ffc 100644 --- a/_types/src/modules/essentialObsidian/APILib/ObsHttpHandler.d.ts +++ b/_types/src/modules/essentialObsidian/APILib/ObsHttpHandler.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { FetchHttpHandler, type FetchHttpHandlerOptions } from "@smithy/fetch-http-handler"; import { HttpRequest, HttpResponse, type HttpHandlerOptions } from "@smithy/protocol-http"; /** diff --git a/_types/src/modules/essentialObsidian/ModuleObsidianEvents.d.ts b/_types/src/modules/essentialObsidian/ModuleObsidianEvents.d.ts index 4d5fe17..24b5062 100644 --- a/_types/src/modules/essentialObsidian/ModuleObsidianEvents.d.ts +++ b/_types/src/modules/essentialObsidian/ModuleObsidianEvents.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts"; import type { TFile } from "@/deps.ts"; import { type ReactiveSource } from "octagonal-wheels/dataobject/reactive"; diff --git a/_types/src/modules/essentialObsidian/ModuleObsidianMenu.d.ts b/_types/src/modules/essentialObsidian/ModuleObsidianMenu.d.ts index 6d5638d..7c91532 100644 --- a/_types/src/modules/essentialObsidian/ModuleObsidianMenu.d.ts +++ b/_types/src/modules/essentialObsidian/ModuleObsidianMenu.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LiveSyncCore } from "@/main.ts"; import { AbstractModule } from "@/modules/AbstractModule.ts"; export declare class ModuleObsidianMenu extends AbstractModule { diff --git a/_types/src/modules/features/DocumentHistory/DocumentHistoryModal.d.ts b/_types/src/modules/features/DocumentHistory/DocumentHistoryModal.d.ts index deb5a1e..eab1327 100644 --- a/_types/src/modules/features/DocumentHistory/DocumentHistoryModal.d.ts +++ b/_types/src/modules/features/DocumentHistory/DocumentHistoryModal.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { TFile, Modal, App } from "@/deps.ts"; import ObsidianLiveSyncPlugin from "@/main.ts"; import { type DocumentID, type FilePathWithPrefix, type LoadedEntry } from "@lib/common/types.ts"; diff --git a/_types/src/modules/features/GlobalHistory/GlobalHistoryView.d.ts b/_types/src/modules/features/GlobalHistory/GlobalHistoryView.d.ts index 5b265bf..f6ac3cd 100644 --- a/_types/src/modules/features/GlobalHistory/GlobalHistoryView.d.ts +++ b/_types/src/modules/features/GlobalHistory/GlobalHistoryView.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { WorkspaceLeaf } from "@/deps.ts"; import type ObsidianLiveSyncPlugin from "@/main.ts"; import { SvelteItemView } from "@/common/SvelteItemView.ts"; diff --git a/_types/src/modules/features/InteractiveConflictResolving/ConflictResolveModal.d.ts b/_types/src/modules/features/InteractiveConflictResolving/ConflictResolveModal.d.ts index 50b2af0..911f97c 100644 --- a/_types/src/modules/features/InteractiveConflictResolving/ConflictResolveModal.d.ts +++ b/_types/src/modules/features/InteractiveConflictResolving/ConflictResolveModal.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { App, Modal } from "@/deps.ts"; import { CANCELLED, LEAVE_TO_SUBSEQUENT, type diff_result } from "@lib/common/types.ts"; import { eventHub } from "@/common/events.ts"; diff --git a/_types/src/modules/features/Log/LogPaneView.d.ts b/_types/src/modules/features/Log/LogPaneView.d.ts index fa0919e..3c36d63 100644 --- a/_types/src/modules/features/Log/LogPaneView.d.ts +++ b/_types/src/modules/features/Log/LogPaneView.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { WorkspaceLeaf } from "@/deps.ts"; import type ObsidianLiveSyncPlugin from "@/main.ts"; import { SvelteItemView } from "@/common/SvelteItemView.ts"; diff --git a/_types/src/modules/features/ModuleGlobalHistory.d.ts b/_types/src/modules/features/ModuleGlobalHistory.d.ts index 4fe29c8..3fcc701 100644 --- a/_types/src/modules/features/ModuleGlobalHistory.d.ts +++ b/_types/src/modules/features/ModuleGlobalHistory.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts"; export declare class ModuleObsidianGlobalHistory extends AbstractObsidianModule { _everyOnloadStart(): Promise; diff --git a/_types/src/modules/features/ModuleInteractiveConflictResolver.d.ts b/_types/src/modules/features/ModuleInteractiveConflictResolver.d.ts index 2a1a52f..cf25750 100644 --- a/_types/src/modules/features/ModuleInteractiveConflictResolver.d.ts +++ b/_types/src/modules/features/ModuleInteractiveConflictResolver.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type FilePathWithPrefix, type diff_result } from "@lib/common/types.ts"; import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts"; import type { LiveSyncCore } from "@/main.ts"; diff --git a/_types/src/modules/features/ModuleLog.d.ts b/_types/src/modules/features/ModuleLog.d.ts index b67d5c3..b596c51 100644 --- a/_types/src/modules/features/ModuleLog.d.ts +++ b/_types/src/modules/features/ModuleLog.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ReactiveValue } from "octagonal-wheels/dataobject/reactive"; import { type LOG_LEVEL } from "@lib/common/types.ts"; import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts"; diff --git a/_types/src/modules/features/ModuleObsidianDocumentHistory.d.ts b/_types/src/modules/features/ModuleObsidianDocumentHistory.d.ts index ca07691..a85bab7 100644 --- a/_types/src/modules/features/ModuleObsidianDocumentHistory.d.ts +++ b/_types/src/modules/features/ModuleObsidianDocumentHistory.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type TFile } from "@/deps.ts"; import type { FilePathWithPrefix, DocumentID } from "@lib/common/types.ts"; import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts"; diff --git a/_types/src/modules/features/ModuleObsidianSettingAsMarkdown.d.ts b/_types/src/modules/features/ModuleObsidianSettingAsMarkdown.d.ts index 0796c2c..5f0deaf 100644 --- a/_types/src/modules/features/ModuleObsidianSettingAsMarkdown.d.ts +++ b/_types/src/modules/features/ModuleObsidianSettingAsMarkdown.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ObsidianLiveSyncSettings } from "@lib/common/types"; import { AbstractModule } from "@/modules/AbstractModule.ts"; import type { ServiceContext } from "@lib/services/base/ServiceBase.ts"; diff --git a/_types/src/modules/features/ModuleObsidianSettingTab.d.ts b/_types/src/modules/features/ModuleObsidianSettingTab.d.ts index a9834d1..ff0d0f9 100644 --- a/_types/src/modules/features/ModuleObsidianSettingTab.d.ts +++ b/_types/src/modules/features/ModuleObsidianSettingTab.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ObsidianLiveSyncSettingTab } from "./SettingDialogue/ObsidianLiveSyncSettingTab.ts"; import { AbstractObsidianModule } from "@/modules/AbstractObsidianModule.ts"; import type { LiveSyncCore } from "@/main.ts"; diff --git a/_types/src/modules/features/SettingDialogue/LiveSyncSetting.d.ts b/_types/src/modules/features/SettingDialogue/LiveSyncSetting.d.ts index dd5b0fe..5468047 100644 --- a/_types/src/modules/features/SettingDialogue/LiveSyncSetting.d.ts +++ b/_types/src/modules/features/SettingDialogue/LiveSyncSetting.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { Setting, TextComponent, type ToggleComponent, type DropdownComponent, ButtonComponent, type TextAreaComponent, type ValueComponent } from "@/deps.ts"; import { type ConfigurationItem } from "@lib/common/types.ts"; import { type ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; diff --git a/_types/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.d.ts b/_types/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.d.ts index 9207bf4..5ff6d7b 100644 --- a/_types/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.d.ts +++ b/_types/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { App, Component, PluginSettingTab } from "@/deps.ts"; import { type ObsidianLiveSyncSettings } from "@lib/common/types.ts"; import ObsidianLiveSyncPlugin from "@/main.ts"; diff --git a/_types/src/modules/features/SettingDialogue/PaneAdvanced.d.ts b/_types/src/modules/features/SettingDialogue/PaneAdvanced.d.ts index 8ab123f..2d05e26 100644 --- a/_types/src/modules/features/SettingDialogue/PaneAdvanced.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneAdvanced.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneAdvanced(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneChangeLog.d.ts b/_types/src/modules/features/SettingDialogue/PaneChangeLog.d.ts index 004588b..348ff68 100644 --- a/_types/src/modules/features/SettingDialogue/PaneChangeLog.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneChangeLog.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; export declare function paneChangeLog(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneCustomisationSync.d.ts b/_types/src/modules/features/SettingDialogue/PaneCustomisationSync.d.ts index dab1a98..6796c13 100644 --- a/_types/src/modules/features/SettingDialogue/PaneCustomisationSync.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneCustomisationSync.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneCustomisationSync(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneGeneral.d.ts b/_types/src/modules/features/SettingDialogue/PaneGeneral.d.ts index ee4878e..c8ecb8a 100644 --- a/_types/src/modules/features/SettingDialogue/PaneGeneral.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneGeneral.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneGeneral(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel, addPane }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneHatch.d.ts b/_types/src/modules/features/SettingDialogue/PaneHatch.d.ts index 058da08..ab5a5d6 100644 --- a/_types/src/modules/features/SettingDialogue/PaneHatch.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneHatch.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneMaintenance.d.ts b/_types/src/modules/features/SettingDialogue/PaneMaintenance.d.ts index f6841f7..2601269 100644 --- a/_types/src/modules/features/SettingDialogue/PaneMaintenance.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneMaintenance.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab"; import { type PageFunctions } from "./SettingPane"; export declare function paneMaintenance(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PanePatches.d.ts b/_types/src/modules/features/SettingDialogue/PanePatches.d.ts index 62c060d..a312ff6 100644 --- a/_types/src/modules/features/SettingDialogue/PanePatches.d.ts +++ b/_types/src/modules/features/SettingDialogue/PanePatches.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PanePowerUsers.d.ts b/_types/src/modules/features/SettingDialogue/PanePowerUsers.d.ts index 4fbde0e..e34a120 100644 --- a/_types/src/modules/features/SettingDialogue/PanePowerUsers.d.ts +++ b/_types/src/modules/features/SettingDialogue/PanePowerUsers.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function panePowerUsers(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneRemoteConfig.d.ts b/_types/src/modules/features/SettingDialogue/PaneRemoteConfig.d.ts index 3caf4e6..50adbb6 100644 --- a/_types/src/modules/features/SettingDialogue/PaneRemoteConfig.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneRemoteConfig.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneRemoteConfig(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel, addPane }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneSelector.d.ts b/_types/src/modules/features/SettingDialogue/PaneSelector.d.ts index ef301ba..99c9d5f 100644 --- a/_types/src/modules/features/SettingDialogue/PaneSelector.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneSelector.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneSelector(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneSetup.d.ts b/_types/src/modules/features/SettingDialogue/PaneSetup.d.ts index ffb47fc..642c0d1 100644 --- a/_types/src/modules/features/SettingDialogue/PaneSetup.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneSetup.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneSetup(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel, addPane }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/PaneSyncSettings.d.ts b/_types/src/modules/features/SettingDialogue/PaneSyncSettings.d.ts index 4261264..4a18734 100644 --- a/_types/src/modules/features/SettingDialogue/PaneSyncSettings.d.ts +++ b/_types/src/modules/features/SettingDialogue/PaneSyncSettings.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; export declare function paneSyncSettings(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel, addPane }: PageFunctions): void; diff --git a/_types/src/modules/features/SettingDialogue/SettingPane.d.ts b/_types/src/modules/features/SettingDialogue/SettingPane.d.ts index 0246123..cf064b7 100644 --- a/_types/src/modules/features/SettingDialogue/SettingPane.d.ts +++ b/_types/src/modules/features/SettingDialogue/SettingPane.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ConfigLevel } from "@lib/common/types"; import type { AllSettingItemKey, AllSettings } from "./settingConstants"; export declare const combineOnUpdate: (func1: OnUpdateFunc, func2: OnUpdateFunc) => OnUpdateFunc; diff --git a/_types/src/modules/features/SettingDialogue/SveltePanel.d.ts b/_types/src/modules/features/SettingDialogue/SveltePanel.d.ts index 302525c..a1f58e5 100644 --- a/_types/src/modules/features/SettingDialogue/SveltePanel.d.ts +++ b/_types/src/modules/features/SettingDialogue/SveltePanel.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type Component } from "svelte"; import { type Writable } from "svelte/store"; /** diff --git a/_types/src/modules/features/SettingDialogue/remoteConfigBuffer.d.ts b/_types/src/modules/features/SettingDialogue/remoteConfigBuffer.d.ts index 2a284ea..ee0c159 100644 --- a/_types/src/modules/features/SettingDialogue/remoteConfigBuffer.d.ts +++ b/_types/src/modules/features/SettingDialogue/remoteConfigBuffer.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianLiveSyncSettings } from "@lib/common/types.ts"; export declare function syncActivatedRemoteSettings(target: Partial, source: ObsidianLiveSyncSettings): void; diff --git a/_types/src/modules/features/SettingDialogue/settingConstants.d.ts b/_types/src/modules/features/SettingDialogue/settingConstants.d.ts index 11e5160..c142cde 100644 --- a/_types/src/modules/features/SettingDialogue/settingConstants.d.ts +++ b/_types/src/modules/features/SettingDialogue/settingConstants.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export * from "@lib/common/settingConstants.ts"; diff --git a/_types/src/modules/features/SettingDialogue/settingUtils.d.ts b/_types/src/modules/features/SettingDialogue/settingUtils.d.ts index 354ea84..da1f73d 100644 --- a/_types/src/modules/features/SettingDialogue/settingUtils.d.ts +++ b/_types/src/modules/features/SettingDialogue/settingUtils.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ObsidianLiveSyncSettings } from "@lib/common/types"; /** * Generates a summary of P2P configuration settings diff --git a/_types/src/modules/features/SetupManager.d.ts b/_types/src/modules/features/SetupManager.d.ts index 21795ed..04e51fd 100644 --- a/_types/src/modules/features/SetupManager.d.ts +++ b/_types/src/modules/features/SetupManager.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ObsidianLiveSyncSettings } from "@lib/common/types.ts"; import { AbstractModule } from "@/modules/AbstractModule.ts"; /** diff --git a/_types/src/modules/features/SetupWizard/dialogs/setupDialogTypes.d.ts b/_types/src/modules/features/SetupWizard/dialogs/setupDialogTypes.d.ts index 071bcf1..f8cc40d 100644 --- a/_types/src/modules/features/SetupWizard/dialogs/setupDialogTypes.d.ts +++ b/_types/src/modules/features/SetupWizard/dialogs/setupDialogTypes.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { BucketSyncSetting, CouchDBConnection, EncryptionSettings, ObsidianLiveSyncSettings, P2PConnectionInfo } from "@lib/common/models/setting.type"; export declare const TYPE_IDENTICAL = "identical"; export declare const TYPE_INDEPENDENT = "independent"; diff --git a/_types/src/modules/main/ModuleLiveSyncMain.d.ts b/_types/src/modules/main/ModuleLiveSyncMain.d.ts index 0155d38..2065fac 100644 --- a/_types/src/modules/main/ModuleLiveSyncMain.d.ts +++ b/_types/src/modules/main/ModuleLiveSyncMain.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AbstractModule } from "@/modules/AbstractModule.ts"; import type { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub.ts"; import type { LiveSyncCore } from "@/main.ts"; diff --git a/_types/src/modules/services/ObsidianAPIService.d.ts b/_types/src/modules/services/ObsidianAPIService.d.ts index fe04d0b..cb50bcd 100644 --- a/_types/src/modules/services/ObsidianAPIService.d.ts +++ b/_types/src/modules/services/ObsidianAPIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { InjectableAPIService } from "@lib/services/implements/injectable/InjectableAPIService"; import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; import { type Command } from "@/deps.ts"; diff --git a/_types/src/modules/services/ObsidianAppLifecycleService.d.ts b/_types/src/modules/services/ObsidianAppLifecycleService.d.ts index 8718c13..cee2657 100644 --- a/_types/src/modules/services/ObsidianAppLifecycleService.d.ts +++ b/_types/src/modules/services/ObsidianAppLifecycleService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { AppLifecycleServiceBase } from "@lib/services/implements/injectable/InjectableAppLifecycleService"; import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; declare module "obsidian" { diff --git a/_types/src/modules/services/ObsidianConfirm.d.ts b/_types/src/modules/services/ObsidianConfirm.d.ts index 7736120..290900e 100644 --- a/_types/src/modules/services/ObsidianConfirm.d.ts +++ b/_types/src/modules/services/ObsidianConfirm.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type App, type Plugin } from "@/deps"; import type { Confirm } from "@lib/interfaces/Confirm"; import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; diff --git a/_types/src/modules/services/ObsidianDatabaseService.d.ts b/_types/src/modules/services/ObsidianDatabaseService.d.ts index cd448ca..dd57097 100644 --- a/_types/src/modules/services/ObsidianDatabaseService.d.ts +++ b/_types/src/modules/services/ObsidianDatabaseService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; import { DatabaseService, type DatabaseServiceDependencies } from "@lib/services/base/DatabaseService.ts"; export declare class ObsidianDatabaseService extends DatabaseService { diff --git a/_types/src/modules/services/ObsidianPathService.d.ts b/_types/src/modules/services/ObsidianPathService.d.ts index 85f0b70..0d286e7 100644 --- a/_types/src/modules/services/ObsidianPathService.d.ts +++ b/_types/src/modules/services/ObsidianPathService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; import { PathService } from "@lib/services/base/PathService"; import { type BASE_IS_NEW, type TARGET_IS_NEW, type EVEN } from "@/common/utils"; diff --git a/_types/src/modules/services/ObsidianServiceHub.d.ts b/_types/src/modules/services/ObsidianServiceHub.d.ts index a0239d4..33e5391 100644 --- a/_types/src/modules/services/ObsidianServiceHub.d.ts +++ b/_types/src/modules/services/ObsidianServiceHub.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub"; import { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; import type ObsidianLiveSyncPlugin from "@/main"; diff --git a/_types/src/modules/services/ObsidianServices.d.ts b/_types/src/modules/services/ObsidianServices.d.ts index 9bcf4c6..7093b1d 100644 --- a/_types/src/modules/services/ObsidianServices.d.ts +++ b/_types/src/modules/services/ObsidianServices.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { InjectableConflictService } from "@lib/services/implements/injectable/InjectableConflictService"; import { InjectableDatabaseEventService } from "@lib/services/implements/injectable/InjectableDatabaseEventService"; import { InjectableFileProcessingService } from "@lib/services/implements/injectable/InjectableFileProcessingService"; diff --git a/_types/src/modules/services/ObsidianSettingService.d.ts b/_types/src/modules/services/ObsidianSettingService.d.ts index 83e9ff1..872117b 100644 --- a/_types/src/modules/services/ObsidianSettingService.d.ts +++ b/_types/src/modules/services/ObsidianSettingService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type ObsidianLiveSyncSettings } from "@lib/common/types"; import { SettingService, type SettingServiceDependencies } from "@lib/services/base/SettingService"; import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; diff --git a/_types/src/modules/services/ObsidianUIService.d.ts b/_types/src/modules/services/ObsidianUIService.d.ts index 6f95d1b..1842f1b 100644 --- a/_types/src/modules/services/ObsidianUIService.d.ts +++ b/_types/src/modules/services/ObsidianUIService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ConfigService } from "@lib/services/base/ConfigService"; import type { AppLifecycleService } from "@lib/services/base/AppLifecycleService"; import type { ReplicatorService } from "@lib/services/base/ReplicatorService"; diff --git a/_types/src/modules/services/ObsidianVaultService.d.ts b/_types/src/modules/services/ObsidianVaultService.d.ts index 6da36fe..f5dbbaa 100644 --- a/_types/src/modules/services/ObsidianVaultService.d.ts +++ b/_types/src/modules/services/ObsidianVaultService.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { InjectableVaultService } from "@lib/services/implements/injectable/InjectableVaultService"; import type { ObsidianServiceContext } from "@lib/services/implements/obsidian/ObsidianServiceContext"; import type { FilePath } from "@lib/common/types"; diff --git a/_types/src/serviceFeatures/onLayoutReady/enablei18n.d.ts b/_types/src/serviceFeatures/onLayoutReady/enablei18n.d.ts index 5455818..4c2ae0e 100644 --- a/_types/src/serviceFeatures/onLayoutReady/enablei18n.d.ts +++ b/_types/src/serviceFeatures/onLayoutReady/enablei18n.d.ts @@ -1,3 +1,3 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 export declare const enableI18nFeature: import("@lib/interfaces/ServiceModule").ServiceFeatureFunction, keyof import("@lib/interfaces/ServiceModule").ServiceModules, Promise>; diff --git a/_types/src/serviceFeatures/redFlag.d.ts b/_types/src/serviceFeatures/redFlag.d.ts index 7d8d687..e452fe1 100644 --- a/_types/src/serviceFeatures/redFlag.d.ts +++ b/_types/src/serviceFeatures/redFlag.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import { type LogFunction } from "@lib/services/lib/logUtils"; import type { ObsidianLiveSyncSettings } from "@lib/common/models/setting.type"; diff --git a/_types/src/serviceFeatures/redFlag.simpleFetch.d.ts b/_types/src/serviceFeatures/redFlag.simpleFetch.d.ts index 50fa675..f708718 100644 --- a/_types/src/serviceFeatures/redFlag.simpleFetch.d.ts +++ b/_types/src/serviceFeatures/redFlag.simpleFetch.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import { type LogFunction } from "@lib/services/lib/logUtils"; import { type FullScanOptions } from "@lib/serviceFeatures/offlineScanner"; @@ -12,7 +12,7 @@ export declare const SIMPLE_FETCH_STAGE2_REMOTE_DELETE_ALL = "Delete local files export declare const SIMPLE_FETCH_STAGE2_NEWER_CLEANUP = "Delete local files if deleted on remote"; export declare const SIMPLE_FETCH_STAGE2_NEWER_SYNC_ALL = "Keep local files even if deleted on remote"; export declare const STAGE2_ABORT = "Cancel all and reboot"; -export declare function askSimpleFetchMode(host: NecessaryServices<"UI" | "vault", "storageAccess">): Promise<{ +export declare function askSimpleFetchMode(host: NecessaryServices<"UI" | "setting", never>): Promise<{ mode: string; options: Partial; } | "cancelled" | "aborted">; diff --git a/_types/src/serviceFeatures/setupObsidian/setupManagerHandlers.d.ts b/_types/src/serviceFeatures/setupObsidian/setupManagerHandlers.d.ts index e499a33..33c5c00 100644 --- a/_types/src/serviceFeatures/setupObsidian/setupManagerHandlers.d.ts +++ b/_types/src/serviceFeatures/setupObsidian/setupManagerHandlers.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type SetupManager } from "@/modules/features/SetupManager"; import type { SetupFeatureHost } from "@lib/serviceFeatures/setupObsidian/types"; import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; diff --git a/_types/src/serviceFeatures/setupObsidian/setupProtocol.d.ts b/_types/src/serviceFeatures/setupObsidian/setupProtocol.d.ts index db99955..357595e 100644 --- a/_types/src/serviceFeatures/setupObsidian/setupProtocol.d.ts +++ b/_types/src/serviceFeatures/setupObsidian/setupProtocol.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { LogFunction } from "@lib/services/lib/logUtils"; import type { SetupFeatureHost } from "@lib/serviceFeatures/setupObsidian/types"; import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; diff --git a/_types/src/serviceFeatures/useP2PReplicatorUI.d.ts b/_types/src/serviceFeatures/useP2PReplicatorUI.d.ts index 26281d1..7b2cb68 100644 --- a/_types/src/serviceFeatures/useP2PReplicatorUI.d.ts +++ b/_types/src/serviceFeatures/useP2PReplicatorUI.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { NecessaryServices } from "@lib/interfaces/ServiceModule"; import { type UseP2PReplicatorResult } from "@lib/replication/trystero/UseP2PReplicatorResult"; import { P2PLogCollector } from "@lib/replication/trystero/P2PLogCollector"; diff --git a/_types/src/serviceModules/DatabaseFileAccess.d.ts b/_types/src/serviceModules/DatabaseFileAccess.d.ts index c272427..e688cfd 100644 --- a/_types/src/serviceModules/DatabaseFileAccess.d.ts +++ b/_types/src/serviceModules/DatabaseFileAccess.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DatabaseFileAccess } from "@lib/interfaces/DatabaseFileAccess.ts"; import { ServiceDatabaseFileAccessBase } from "@lib/serviceModules/ServiceDatabaseFileAccessBase"; export declare class ServiceDatabaseFileAccess extends ServiceDatabaseFileAccessBase implements DatabaseFileAccess { diff --git a/_types/src/serviceModules/FileAccessObsidian.d.ts b/_types/src/serviceModules/FileAccessObsidian.d.ts index b40cd0e..3f0c30f 100644 --- a/_types/src/serviceModules/FileAccessObsidian.d.ts +++ b/_types/src/serviceModules/FileAccessObsidian.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type App } from "@/deps"; import { FileAccessBase, type FileAccessBaseDependencies } from "@lib/serviceModules/FileAccessBase.ts"; import { ObsidianFileSystemAdapter } from "./FileSystemAdapters/ObsidianFileSystemAdapter"; diff --git a/_types/src/serviceModules/FileHandler.d.ts b/_types/src/serviceModules/FileHandler.d.ts index ef7ff89..787e0d4 100644 --- a/_types/src/serviceModules/FileHandler.d.ts +++ b/_types/src/serviceModules/FileHandler.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ServiceFileHandlerBase } from "@lib/serviceModules/ServiceFileHandlerBase"; export declare class ServiceFileHandler extends ServiceFileHandlerBase { } diff --git a/_types/src/serviceModules/FileSystemAdapters/ObsidianConversionAdapter.d.ts b/_types/src/serviceModules/FileSystemAdapters/ObsidianConversionAdapter.d.ts index b18b14c..b384067 100644 --- a/_types/src/serviceModules/FileSystemAdapters/ObsidianConversionAdapter.d.ts +++ b/_types/src/serviceModules/FileSystemAdapters/ObsidianConversionAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXFileInfoStub, UXFolderInfo } from "@lib/common/types"; import type { IConversionAdapter } from "@lib/serviceModules/adapters"; import type { TFile, TFolder } from "obsidian"; diff --git a/_types/src/serviceModules/FileSystemAdapters/ObsidianFileSystemAdapter.d.ts b/_types/src/serviceModules/FileSystemAdapters/ObsidianFileSystemAdapter.d.ts index 28c429d..051d93b 100644 --- a/_types/src/serviceModules/FileSystemAdapters/ObsidianFileSystemAdapter.d.ts +++ b/_types/src/serviceModules/FileSystemAdapters/ObsidianFileSystemAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { FilePath, UXStat } from "@lib/common/types"; import type { IFileSystemAdapter, IPathAdapter, ITypeGuardAdapter, IConversionAdapter, IStorageAdapter, IVaultAdapter } from "@lib/serviceModules/adapters"; import type { TAbstractFile, TFile, TFolder, Stat, App } from "obsidian"; diff --git a/_types/src/serviceModules/FileSystemAdapters/ObsidianPathAdapter.d.ts b/_types/src/serviceModules/FileSystemAdapters/ObsidianPathAdapter.d.ts index bd232c3..1e136ef 100644 --- a/_types/src/serviceModules/FileSystemAdapters/ObsidianPathAdapter.d.ts +++ b/_types/src/serviceModules/FileSystemAdapters/ObsidianPathAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { type TAbstractFile } from "@/deps"; import type { FilePath } from "@lib/common/types"; import type { IPathAdapter } from "@lib/serviceModules/adapters"; diff --git a/_types/src/serviceModules/FileSystemAdapters/ObsidianStorageAdapter.d.ts b/_types/src/serviceModules/FileSystemAdapters/ObsidianStorageAdapter.d.ts index a5e1b61..c352407 100644 --- a/_types/src/serviceModules/FileSystemAdapters/ObsidianStorageAdapter.d.ts +++ b/_types/src/serviceModules/FileSystemAdapters/ObsidianStorageAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXDataWriteOptions } from "@lib/common/types"; import type { IStorageAdapter } from "@lib/serviceModules/adapters"; import type { Stat, App } from "obsidian"; diff --git a/_types/src/serviceModules/FileSystemAdapters/ObsidianTypeGuardAdapter.d.ts b/_types/src/serviceModules/FileSystemAdapters/ObsidianTypeGuardAdapter.d.ts index 9a43b63..08e50a0 100644 --- a/_types/src/serviceModules/FileSystemAdapters/ObsidianTypeGuardAdapter.d.ts +++ b/_types/src/serviceModules/FileSystemAdapters/ObsidianTypeGuardAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { ITypeGuardAdapter } from "@lib/serviceModules/adapters"; import { TFile, TFolder } from "obsidian"; /** diff --git a/_types/src/serviceModules/FileSystemAdapters/ObsidianVaultAdapter.d.ts b/_types/src/serviceModules/FileSystemAdapters/ObsidianVaultAdapter.d.ts index 6026101..9187774 100644 --- a/_types/src/serviceModules/FileSystemAdapters/ObsidianVaultAdapter.d.ts +++ b/_types/src/serviceModules/FileSystemAdapters/ObsidianVaultAdapter.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { UXDataWriteOptions } from "@lib/common/types"; import type { IVaultAdapter } from "@lib/serviceModules/adapters"; import type { TFile, App, TFolder } from "obsidian"; diff --git a/_types/src/serviceModules/ServiceFileAccessImpl.d.ts b/_types/src/serviceModules/ServiceFileAccessImpl.d.ts index 4f37e00..e21403e 100644 --- a/_types/src/serviceModules/ServiceFileAccessImpl.d.ts +++ b/_types/src/serviceModules/ServiceFileAccessImpl.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import { ServiceFileAccessBase } from "@lib/serviceModules/ServiceFileAccessBase"; import type { ObsidianFileSystemAdapter } from "./FileSystemAdapters/ObsidianFileSystemAdapter"; export declare class ServiceFileAccessObsidian extends ServiceFileAccessBase { diff --git a/_types/src/types.d.ts b/_types/src/types.d.ts index 43faa6d..a4f39bc 100644 --- a/_types/src/types.d.ts +++ b/_types/src/types.d.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 0563f26 +// REPO: https://github.com/vrtmrz/livesync-commonlib Commit hash: 87dc724 import type { DatabaseFileAccess } from "@lib/interfaces/DatabaseFileAccess"; import type { Rebuilder } from "@lib/interfaces/DatabaseRebuilder"; import type { IFileHandler } from "@lib/interfaces/FileHandler"; diff --git a/manifest.json b/manifest.json index 773417c..bab8884 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.25.78", + "version": "0.25.79", "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.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 9f3d5b4..95adc5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.25.78", + "version": "0.25.79", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.25.78", + "version": "0.25.79", "license": "MIT", "workspaces": [ "src/apps/cli", @@ -29,7 +29,7 @@ "markdown-it": "^14.2.0", "minimatch": "^10.2.5", "obsidian": "^1.13.1", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "qrcode-generator": "^1.4.4", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" }, @@ -11684,9 +11684,9 @@ "license": "MIT" }, "node_modules/octagonal-wheels": { - "version": "0.1.46", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.46.tgz", - "integrity": "sha512-19eB7b/WNNrZ4Xghu93f+NVJsbRiaZaIIzU1rn5shxb6SzwVBoOVkNPJdCAsONl6C1MwjaGDrPUS8CBXvPHjPg==", + "version": "0.1.47", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.47.tgz", + "integrity": "sha512-pv+AoplTzDmcrYpxun/N7sab7KAVoPvUhwvnrXwrVihjj8sQ89HmovndpMvGJxdpp4+uOOY4SiO0DVWTp+YCkQ==", "license": "MIT", "dependencies": { "idb": "^8.0.3" @@ -16210,11 +16210,11 @@ }, "src/apps/cli": { "name": "self-hosted-livesync-cli", - "version": "0.25.77-cli", + "version": "0.25.79-cli", "dependencies": { "chokidar": "^4.0.0", "minimatch": "^10.2.5", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "pouchdb-adapter-http": "^9.0.0", "pouchdb-adapter-leveldb": "^9.0.0", "pouchdb-core": "^9.0.0", @@ -16236,9 +16236,9 @@ }, "src/apps/webapp": { "name": "livesync-webapp", - "version": "0.25.77-webapp", + "version": "0.25.79-webapp", "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "@playwright/test": "^1.58.2", @@ -16251,9 +16251,9 @@ } }, "src/apps/webpeer": { - "version": "0.25.77-webpeer", + "version": "0.25.79-webpeer", "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^7.1.2", diff --git a/package.json b/package.json index 0d13b79..04717d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.25.78", + "version": "0.25.79", "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", @@ -147,7 +147,7 @@ "markdown-it": "^14.2.0", "minimatch": "^10.2.5", "obsidian": "^1.13.1", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "qrcode-generator": "^1.4.4", "xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2" }, diff --git a/src/apps/cli/package.json b/src/apps/cli/package.json index 95ebd23..7c13470 100644 --- a/src/apps/cli/package.json +++ b/src/apps/cli/package.json @@ -1,7 +1,7 @@ { "name": "self-hosted-livesync-cli", "private": true, - "version": "0.25.78-cli", + "version": "0.25.79-cli", "main": "dist/index.cjs", "type": "module", "scripts": { @@ -41,7 +41,7 @@ "dependencies": { "chokidar": "^4.0.0", "minimatch": "^10.2.5", - "octagonal-wheels": "^0.1.46", + "octagonal-wheels": "^0.1.47", "pouchdb-adapter-http": "^9.0.0", "pouchdb-adapter-leveldb": "^9.0.0", "pouchdb-core": "^9.0.0", diff --git a/src/apps/webapp/package.json b/src/apps/webapp/package.json index 23906bd..a617d23 100644 --- a/src/apps/webapp/package.json +++ b/src/apps/webapp/package.json @@ -1,7 +1,7 @@ { "name": "livesync-webapp", "private": true, - "version": "0.25.78-webapp", + "version": "0.25.79-webapp", "type": "module", "description": "Browser-based Self-hosted LiveSync using FileSystem API", "scripts": { @@ -12,7 +12,7 @@ "preview": "vite preview" }, "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "@playwright/test": "^1.58.2", diff --git a/src/apps/webpeer/package.json b/src/apps/webpeer/package.json index 573630b..a6ee06d 100644 --- a/src/apps/webpeer/package.json +++ b/src/apps/webpeer/package.json @@ -1,7 +1,7 @@ { "name": "webpeer", "private": true, - "version": "0.25.78-webpeer", + "version": "0.25.79-webpeer", "type": "module", "scripts": { "dev": "vite", @@ -12,7 +12,7 @@ "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json" }, "dependencies": { - "octagonal-wheels": "^0.1.46" + "octagonal-wheels": "^0.1.47" }, "devDependencies": { "eslint-plugin-svelte": "^3.19.0", diff --git a/updates.md b/updates.md index 6d14da5..5f37ffa 100644 --- a/updates.md +++ b/updates.md @@ -3,6 +3,29 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025) The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope. +## 0.25.79 + +29th June, 2026 + +### Fixed + +- Fast Fetch now retries transient stream interruptions and resumes from the latest persisted checkpoint, instead of starting over after ordinary network or platform interruptions (#977, PR #978; commonlib PR #59). Thank you so much for @apple-ouyang for the fix! +- Simple Fetch now remembers the selected setup choices while an interrupted Fetch All operation is still pending, so users are not asked the same questions again on retry (#977, PR #978). Thank you so much for @apple-ouyang for the fix! +- No longer hidden storage events, such as `.git` paths, reach the normal target-file filter when internal file synchronisation is disabled. This avoids noisy non-target logs before those files are skipped (commonlib PR #60). Thank you so much for @apple-ouyang for the fix! +- Fixed an issue where a file deleted from storage could be resurrected by the offline scanner because the database tombstone was not written when the storage file was already gone (commonlib PR #56). Thank you so much for @cosmic-fire-eng for the fix! + +### Improved + +- Local database maintenance commands now ask before applying the required chunk settings, and can apply those prerequisites before continuing (#980, PR #981). Thank you so much for @apple-ouyang for the improvement! +- Improved CouchDB replication event handling by using the new `StreamInbox` helper from `octagonal-wheels` (commonlib PR #62). + +### Documentation + +- Added `nginx` to the setup documentation table of contents (PR #976). Thank you so much for @kiraventom for the improvement! + +### Miscellaneous + +- Updated `octagonal-wheels` to `0.1.47` across the plug-in and workspace packages to use the newly published helper modules. ## 0.25.78 From 54e6a761e5b0fbefcd31f18f373b4ba6d6de645a Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 30 Jun 2026 08:36:29 +0000 Subject: [PATCH 15/16] (test): add local Obsidian E2E suite --- devs.md | 24 ++- docs/adr/2026_06_real_obsidian_e2e.md | 54 ++++--- package.json | 4 + test/e2e-obsidian/README.md | 32 +++- test/e2e-obsidian/runner/cli.ts | 5 +- test/e2e-obsidian/runner/launch.ts | 93 +++++++++++- test/e2e-obsidian/runner/liveSyncWorkflow.ts | 89 +++++++++-- test/e2e-obsidian/runner/objectStorage.ts | 142 ++++++++++++++++++ test/e2e-obsidian/runner/session.ts | 39 ++++- test/e2e-obsidian/runner/ui.ts | 83 ++++++++++ test/e2e-obsidian/runner/vault.ts | 33 +++- test/e2e-obsidian/scripts/cli-help.ts | 2 + .../scripts/customisation-sync.ts | 6 +- test/e2e-obsidian/scripts/debug-ui.ts | 60 ++++++++ .../scripts/hidden-file-snippet-sync.ts | 82 +++++----- test/e2e-obsidian/scripts/local-suite.ts | 105 +++++++++++++ test/e2e-obsidian/scripts/minio-upload.ts | 142 ++++++++++++++++++ test/e2e-obsidian/scripts/two-vault-sync.ts | 99 +++++++++++- 18 files changed, 975 insertions(+), 119 deletions(-) create mode 100644 test/e2e-obsidian/runner/objectStorage.ts create mode 100644 test/e2e-obsidian/runner/ui.ts create mode 100644 test/e2e-obsidian/scripts/debug-ui.ts create mode 100644 test/e2e-obsidian/scripts/local-suite.ts create mode 100644 test/e2e-obsidian/scripts/minio-upload.ts diff --git a/devs.md b/devs.md index 279dbfb..d994d5e 100644 --- a/devs.md +++ b/devs.md @@ -1,4 +1,5 @@ # Self-hosted LiveSync Development Guide + ## Project Overview Self-hosted LiveSync is an Obsidian plugin for synchronising vaults across devices using CouchDB, MinIO/S3, or peer-to-peer WebRTC. The codebase uses a modular architecture with TypeScript, Svelte, and PouchDB. @@ -10,6 +11,7 @@ Self-hosted LiveSync is an Obsidian plugin for synchronising vaults across devic #### First-time Setup This repository uses submodules by convention. Therefore, you must use the `--recursive` flag when cloning it. + ```bash git clone --recursive https://github.com/vrtmrz/obsidian-livesync npm ci @@ -19,7 +21,9 @@ npm run build Note: if you already cloned without submodules, run: `git submodule update --init --recursive` #### Branch switching + When switching branches, please make sure to update submodules as well, since they may be updated in the new branch. + ```bash git checkout --recurse-submodules 0.25.70-patch1 # tag or branch name npm ci @@ -41,7 +45,7 @@ npm test # Run Harness based vitest tests (requires Docker services) ### Tips -We can use CLI's E2E test command instead of `npm test`. +Use CLI E2E tests or real Obsidian E2E scripts instead of `npm test` when the behaviour can be verified outside the browser harness. ### Auto-copy to test vaults @@ -58,46 +62,54 @@ To facilitate development and testing, the build process can automatically copy - **Unit Tests** (`vitest.config.unit.ts`): Unit tests run in Node.js (excluding harnesses and integration tests). Unit tests should be `*.unit.spec.ts` and placed alongside the implementation file (e.g., `ChunkFetcher.unit.spec.ts`). Executed via `npm run test:unit`. - **Integration Tests** (`vitest.config.integration.ts`): Tests run in Node.js against a real CouchDB instance. Integration tests should be `*.integration.spec.ts` or `*.integration.test.ts` and placed alongside the implementation file (e.g., `StreamingFetch.integration.spec.ts`). Executed via `npm run test:integration`. - If you add a feature that interacts with the remote database (e.g., replication changes, custom changes feed parameters, or custom HTTP queries), you strongly expected to write an integration test to verify the behaviour against a real CouchDB server. - - **E2E Tests** (`vitest.config.ts`): End-to-end tests run in a browser-based harness using Playwright/Chromium to test full synchronisation scenarios. Executed via `npm run test`. + - **Browser Harness Tests** (`vitest.config.ts`): Transitional browser-based harness tests using Playwright/Chromium. Executed via `npm run test`. This layer is no longer the preferred destination for new broad E2E coverage because `test/harness` can drift from real Obsidian behaviour. - **P2P Tests** (`vitest.config.p2p.ts`): Browser-based Peer-to-Peer replication tests. Executed via `npm run test:p2p`. - **RPC Unit Tests** (`vitest.config.rpc-unit.ts`): RPC-specific unit tests with coverage thresholds. +- **Real Obsidian E2E** (`test/e2e-obsidian/`): Local-first scripts that launch real Obsidian with temporary vaults and the built Self-hosted LiveSync plug-in. Use these for boot-up sequence, vault reflection, RedFlag flows, Fast Setup (Simple Fetch), settings dialogues, restart-sensitive workflows, Object Storage regressions, and other behaviour that depends on Obsidian itself. Run focused scripts such as `npm run test:e2e:obsidian:two-vault-sync`, or use `npm run test:e2e:obsidian:local-suite:services` to run the broader local suite with CouchDB and MinIO fixtures managed by the wrapper. - **Docker Services**: Tests require CouchDB, MinIO (S3), and P2P services: + ```bash npm run test:docker-all:start # Start all test services npm run test:full # Run tests with coverage npm run test:docker-all:stop # Stop services ``` + If some services are not needed, start only required ones (e.g., `test:docker-couchdb:start`). Note that if services are already running, starting script will fail. Please stop them first. - **Test Structure**: - - `test/suite/` - E2E tests for sync operations (running in browser) + - `test/e2e-obsidian/` - Real Obsidian E2E scripts for local verification + - `test/suite/` - Transitional browser harness tests for sync operations - `test/unit/` - Unit tests (via vitest, as harness is browser-based) - - `test/harness/` - Mock implementations (e.g., `obsidian-mock.ts`) + - `test/harness/` - Transitional mock implementations (e.g., `obsidian-mock.ts`). Avoid adding broad new E2E coverage here unless it is explicitly a compatibility bridge. ### Import Path Normalisation The codebase uses `@/` and `@lib/` path aliases to keep import structures clean. To normalise imports and exports across files, use the following utility script: + ```bash npm run pretty:importpath ``` + Under the hood, this runs Deno with the script [utilsdeno/normalise-imports.ts](file:///p:/plant25/obsidian/projects/obsidian-livesync/utilsdeno/normalise-imports.ts). You can pass additional flags to this script if required (by running it via Deno directly from the `utilsdeno` directory): + - `--run`: Applies the changes (the script runs in dry-run mode by default). - `--all-alias`: Normalises sibling/child relative imports starting with `./` to use aliases. ### Type Generation To generate fallback type definitions for the shared library and add appropriate Deno ignore comments (which suppresses Deno compilation warnings and linting warnings inside the `_types` directory), run: + ```bash npm run build:lib:types ``` + This script executes: + 1. TypeScript compilation (`tsconfig.types.json`) to output definitions to the `_types` directory. 2. The Deno script [utilsdeno/types-add-ignore.ts](file:///p:/plant25/obsidian/projects/obsidian-livesync/utilsdeno/types-add-ignore.ts) to prepend Deno ignore comments to the generated type files. - - ## Architecture ### Module System diff --git a/docs/adr/2026_06_real_obsidian_e2e.md b/docs/adr/2026_06_real_obsidian_e2e.md index 0e83884..863b756 100644 --- a/docs/adr/2026_06_real_obsidian_e2e.md +++ b/docs/adr/2026_06_real_obsidian_e2e.md @@ -125,12 +125,12 @@ Initial discovery on Linux ARM64 found that: Current implementation status: - Added `test/e2e-obsidian/runner` helpers for Obsidian discovery, CLI discovery, temporary vault creation, plug-in installation, process launch, CLI execution, and readiness polling. -- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, `test:e2e:obsidian:couchdb-upload`, `test:e2e:obsidian:startup-scan`, `test:e2e:obsidian:two-vault-sync`, `test:e2e:obsidian:hidden-file-snippet-sync`, `test:e2e:obsidian:customisation-sync`, `test:e2e:obsidian:setting-markdown-export`, and `test:e2e:obsidian:install-appimage`. -- Added `startObsidianLiveSyncSession()` so future workflows can reuse the launch, vault open, community plug-in enablement, plug-in reload, and readiness sequence without duplicating smoke runner code. +- Added `test:e2e:obsidian:discover`, `test:e2e:obsidian:cli-help`, `test:e2e:obsidian:smoke`, `test:e2e:obsidian:vault-reflection`, `test:e2e:obsidian:couchdb-upload`, `test:e2e:obsidian:minio-upload`, `test:e2e:obsidian:startup-scan`, `test:e2e:obsidian:two-vault-sync`, `test:e2e:obsidian:hidden-file-snippet-sync`, `test:e2e:obsidian:customisation-sync`, `test:e2e:obsidian:setting-markdown-export`, `test:e2e:obsidian:local-suite`, `test:e2e:obsidian:local-suite:services`, and `test:e2e:obsidian:install-appimage`. +- Added `startObsidianLiveSyncSession()` so future workflows can reuse the launch, trusted temporary vault state, vault open, community plug-in reload, and readiness sequence without duplicating smoke runner code. - Added CouchDB runner utilities that reuse `.test.env`/process environment values, create unique temporary databases, query uploaded documents directly, and clean up the database unless `E2E_OBSIDIAN_KEEP_COUCHDB=true` is set. - Added a manual AppImage installer that downloads Obsidian `1.12.7` for `arm64` or `x86_64`, stores it under `_testdata/obsidian`, and extracts it for FUSE-free execution. - Confirmed the smoke runner on Linux ARM64 with the extracted Obsidian `1.12.7` AppImage, `xvfb-run`, and the built Self-hosted LiveSync bundle. -- Confirmed the runner can enable the Obsidian CLI through isolated `obsidian.json` state, open the temporary vault through `obsidian-cli`, enable community plug-ins through `app.plugins.setEnable(true)`, reload Self-hosted LiveSync, and verify readiness through `obsidian-cli eval`. +- Confirmed the runner can enable the Obsidian CLI through isolated `obsidian.json` state, pre-seed the temporary Chromium local storage so the generated vault ID is trusted for community plug-ins, open the temporary vault through `obsidian-cli`, reload Self-hosted LiveSync, and verify readiness through `obsidian-cli eval`. - Removed the first test-only ready-marker bridge from the plug-in bundle. The current runner observes readiness from outside the plug-in through Obsidian's own CLI, so normal user vaults do not receive E2E marker files. Current verification: @@ -141,18 +141,22 @@ Current verification: - `E2E_OBSIDIAN_SMOKE_TIMEOUT_MS=1000 npm run test:e2e:obsidian:smoke` passes locally. - `npm run test:e2e:obsidian:vault-reflection` creates a note through Obsidian's vault API, verifies the reflected file on disk, and reads it back through Obsidian. - `npm run test:e2e:obsidian:couchdb-upload` configures a unique CouchDB database, creates a note through Obsidian, commits it into the local database, runs one-shot synchronisation, and verifies that CouchDB contains the metadata document and all referenced chunk documents. +- `npm run test:e2e:obsidian:minio-upload` configures a unique Object Storage bucket prefix, creates a note through Obsidian, runs one-shot Journal Sync, and verifies through the AWS SDK that objects were written to the S3-compatible bucket. - `npm run test:e2e:obsidian:startup-scan` verifies that a file written while Obsidian is stopped is picked up during the next real Obsidian boot and uploaded to CouchDB after one-shot synchronisation. -- `npm run test:e2e:obsidian:two-vault-sync` verifies two-vault note synchronisation: creation, update, deletion, Markdown conflict automatic merging with the merged result propagated by a second synchronisation, and per-device target-filter differences. +- `npm run test:e2e:obsidian:two-vault-sync` verifies two-vault note synchronisation: creation, update, rename, deletion, per-device target-filter differences, and a separate encrypted round-trip with Path Obfuscation enabled. The experimental Markdown conflict automatic merge check is available with `E2E_OBSIDIAN_INCLUDE_MARKDOWN_CONFLICT=true` but is not part of the default local suite. - `npm run test:e2e:obsidian:hidden-file-snippet-sync` verifies hidden file synchronisation as a two-vault round-trip: creation, deletion, automatic JSON conflict merging with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target-pattern differences. - `npm run test:e2e:obsidian:customisation-sync` verifies a two-vault Customisation Sync workflow: scan a real snippet CSS file, config JSON file, and sample plug-in fixture into per-file Customisation Sync data, synchronise them through CouchDB, apply them on the second vault, assert the resulting `.obsidian` files, propagate a snippet update, and verify deletion of the source-vault snippet sync data without confusing it with the target vault's own applied copy. - `npm run test:e2e:obsidian:setting-markdown-export` verifies that setting Markdown export creates a vault file and omits credentials when credential export is disabled. - `npm run test:e2e:obsidian:install-appimage` reuses the existing AppImage and extracted binary when they are already present. +- `npm run test:e2e:obsidian:local-suite` runs the local verification sequence for the real Obsidian runner after CouchDB and MinIO have been started. +- `npm run test:e2e:obsidian:local-suite:services` stops leftover CouchDB and MinIO fixtures, starts fresh fixtures, runs the local suite, and stops the fixtures again. +- `npm run test:e2e:obsidian:local-suite:services` has been verified locally with real Obsidian, CouchDB, and MinIO. The run completed discovery, smoke, vault reflection, CouchDB upload, Object Storage upload, startup scan, two-vault synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export. The build step still emits existing Svelte warnings. Known limits: - The smoke runner currently proves only one-vault launch and plug-in load readiness. Broader workflows are covered by separate real Obsidian scripts, including CouchDB upload, startup scan, two-vault note synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export. -- Cross-platform support is still discovery-level. The working path has been validated on Linux ARM64. -- CI wiring is not yet implemented. CI should use `OBSIDIAN_BINARY` or a cached `_testdata/obsidian/squashfs-root` rather than downloading the AppImage on every run. +- Cross-platform support is still discovery-level. The working path has been validated on Linux ARM64. macOS and Windows should be validated in their own environments as follow-up work. +- CI wiring is intentionally not implemented. The runner depends on a licensed desktop application and is treated as a local verification tool. ### Phase 2: First Real Workflow @@ -169,6 +173,7 @@ Current implementation status: - Added a pre-CouchDB workflow that creates a note through Obsidian's vault API, confirms the note is reflected as a real vault file, and reads the same note back through Obsidian. This covers the vault reflection part of the Phase 2 path before remote database setup is introduced. - Added a first CouchDB-backed upload workflow, modelled after the CLI Deno tests: reuse the standard CouchDB environment variables, create a unique remote database, apply CouchDB settings through the plug-in's setting service, commit the note through the real Obsidian vault path, run one-shot synchronisation, and assert that remote metadata and chunks exist. +- Added an Object Storage-backed upload workflow against MinIO to exercise Journal Sync and the AWS SDK path from real Obsidian. - Added Obsidian-specific workflows for boot-time vault scanning, two-vault note synchronisation, hidden `.obsidian/snippets` file round-tripping, hidden JSON conflict resolution, Customisation Sync application for snippets, config JSON files, and plug-in fixtures, per-device target-filter differences, and setting Markdown export. These scenarios assert against CouchDB documents, vault files, or real Obsidian UI outcomes instead of internal service state. ### Phase 3: Two-Vault Synchronisation @@ -179,39 +184,53 @@ Current implementation status: - Verify reflection in the other vault. - Cover encrypted and non-encrypted configurations separately. +Current implementation status: + +- `test:e2e:obsidian:two-vault-sync` covers creation, update, rename, deletion, and per-device target-filter behaviour for a non-encrypted CouchDB configuration. Markdown conflict automatic merging remains an optional check because it needs a dedicated, less timing-sensitive fixture. +- The same script creates a separate temporary CouchDB database and temporary vault pair for an encrypted two-vault round-trip with Path Obfuscation enabled. + ### Phase 4: Harness Retirement - Mark `test/harness` as deprecated in documentation. - Stop adding new tests to `test/suite` unless they are explicitly transitional. -- Move critical existing scenarios from `test/suite` to real Obsidian E2E or lower-level integration tests. +- Do not mechanically port `test/suite` into real Obsidian E2E. Scenarios that can already be exercised and asserted through the CLI test layer should stay there or move to lower-level integration tests. +- Prioritise real Obsidian coverage for behaviours that the CLI cannot prove well, especially RedFlag flag-file recovery flows, Fast Setup (Simple Fetch), boot-up sequencing, restart-sensitive initial synchronisation, and user-visible recovery dialogues. - Remove the harness only after the new runner covers the critical boot-up and synchronisation workflows. -## CI Strategy +Current implementation status: -Start with local-only execution. After the smoke runner is stable: +- `test/harness` is now documented as a transitional compatibility layer. +- New broad E2E work should target `test/e2e-obsidian/` when real Obsidian behaviour is the risk being tested. +- The next high-value scenarios are RedFlag variants and Fast Setup (Simple Fetch) variants, not a line-by-line migration of `test/suite`. -- Run the smoke test in CI on Linux. -- Keep full two-vault synchronisation scenarios as nightly or manually triggered jobs until runtime and flakiness are understood. -- Do not download the Obsidian AppImage on every CI run. Use a pre-installed Obsidian binary, a CI cache for `_testdata/obsidian/squashfs-root`, or a manually triggered preparation job. -- Capture Obsidian logs, plug-in logs, vault snapshots, and service logs on failure. +## Local Verification Strategy + +Real Obsidian E2E is a local verification layer. It should not be wired into the default CI gate. + +- Keep the scripts individually runnable for focused local debugging. +- Provide `test:e2e:obsidian:local-suite` for a broader local pass after the CouchDB and MinIO fixtures have been started. +- Provide `test:e2e:obsidian:local-suite:services` for a broader local pass that manages the CouchDB and MinIO fixtures itself. +- Use `OBSIDIAN_BINARY` when testing against an installed desktop application. +- Use `test:e2e:obsidian:install-appimage` on Linux when a local AppImage copy is needed, and reuse the extracted `_testdata/obsidian/squashfs-root` directory between local runs. +- Capture Obsidian logs, plug-in logs, vault snapshots, and service logs manually when investigating failures. - Fail fast on launch failures, readiness timeouts, and cleanup failures with clear diagnostics. ## Risks and Mitigations -- **Obsidian licensing and installation**: CI may need a cached installer or a pre-installed binary. Keep the runner capable of using `OBSIDIAN_BINARY`. +- **Obsidian licensing and installation**: Keep the runner local-first and capable of using `OBSIDIAN_BINARY`. - **Flakiness from UI timing**: Prefer a control channel and service-level probes over visual selectors. - **Multiple instances**: Obsidian may not support multiple independent instances cleanly on all platforms. Start with one-instance smoke tests, then validate two-instance behaviour on Linux before expanding scope. - **State leakage**: Isolate vault directories, Obsidian user data, remote database names, and bridge tokens per test. - **Security of E2E controls**: Keep readiness and control outside the production plug-in bundle. Prefer Obsidian CLI probes over E2E-only plug-in code. -- **Runtime cost**: Keep the default PR gate small. Move slow synchronisation matrices to scheduled jobs. +- **Runtime cost**: Keep real Obsidian E2E out of the default PR gate. Use focused scripts or the local suite when a change touches real Obsidian integration. ## Open Questions -- Which launch mechanism is most reliable for Obsidian on Linux in this repository's CI environment? +- Which launch mechanism is most reliable for Obsidian on each supported desktop platform? - Can two Obsidian instances run with isolated user data at the same time? - Do future scenarios need a richer control channel than Obsidian CLI, or can CLI `eval` and developer commands cover the required workflows? - Should any future E2E-only plug-in code live in a separate test build, or should the production bundle remain free of E2E controls? -- Which existing `test/suite` scenarios are critical enough to port before deprecating the harness? +- Which RedFlag and Fast Setup (Simple Fetch) variants should be added first? ## Initial Implementation Checklist @@ -222,6 +241,7 @@ Start with local-only execution. After the smoke runner is stable: 5. Document required local environment variables, especially `OBSIDIAN_BINARY`. 6. Port one CouchDB-backed workflow after the smoke test is stable. 7. Mark `test/harness` as transitional and block new broad E2E work from targeting it. +8. Add the local suite script for broader local verification. ## Consequences diff --git a/package.json b/package.json index 682b101..7aa8fe1 100644 --- a/package.json +++ b/package.json @@ -41,14 +41,18 @@ "test:e2e:obsidian:install-appimage": "tsx test/e2e-obsidian/scripts/install-appimage.ts", "test:e2e:obsidian:discover": "tsx test/e2e-obsidian/scripts/discover.ts", "test:e2e:obsidian:cli-help": "tsx test/e2e-obsidian/scripts/cli-help.ts", + "test:e2e:obsidian:debug-ui": "tsx test/e2e-obsidian/scripts/debug-ui.ts", "test:e2e:obsidian:smoke": "tsx test/e2e-obsidian/scripts/smoke.ts", "test:e2e:obsidian:vault-reflection": "tsx test/e2e-obsidian/scripts/vault-reflection.ts", "test:e2e:obsidian:couchdb-upload": "tsx test/e2e-obsidian/scripts/couchdb-upload.ts", + "test:e2e:obsidian:minio-upload": "tsx test/e2e-obsidian/scripts/minio-upload.ts", "test:e2e:obsidian:startup-scan": "tsx test/e2e-obsidian/scripts/startup-scan.ts", "test:e2e:obsidian:two-vault-sync": "tsx test/e2e-obsidian/scripts/two-vault-sync.ts", "test:e2e:obsidian:hidden-file-snippet-sync": "tsx test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts", "test:e2e:obsidian:customisation-sync": "tsx test/e2e-obsidian/scripts/customisation-sync.ts", "test:e2e:obsidian:setting-markdown-export": "tsx test/e2e-obsidian/scripts/setting-markdown-export.ts", + "test:e2e:obsidian:local-suite": "tsx test/e2e-obsidian/scripts/local-suite.ts", + "test:e2e:obsidian:local-suite:services": "tsx test/e2e-obsidian/scripts/local-suite.ts --manage-services", "test:coverage": "vitest run --coverage", "test:docker-couchdb:up": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-start.sh", "test:docker-couchdb:init": "npx dotenv-cli -e .env -e .test.env -- ./test/shell/couchdb-init.sh", diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md index c95e52b..ca32189 100644 --- a/test/e2e-obsidian/README.md +++ b/test/e2e-obsidian/README.md @@ -20,6 +20,8 @@ Obsidian 1.12 stores the global community plug-in switch outside `.obsidian/comm Future workflows should use `startObsidianLiveSyncSession()` from `runner/session.ts` rather than repeating the launch and plug-in readiness sequence. +Each test vault uses an isolated Obsidian profile. The runner creates temporary directories for `HOME`, `XDG_CONFIG_HOME`, `XDG_CACHE_HOME`, `XDG_DATA_HOME`, and Electron `--user-data-dir`, writes the vault registry into those directories, pre-seeds the temporary Chromium local storage so community plug-ins are trusted for that generated vault ID, and passes the same environment to `obsidian-cli`. This is intended to keep real Obsidian E2E runs separate from a developer's daily Obsidian profile and vault registry. + ## Local Setup Set `OBSIDIAN_BINARY` when Obsidian is not installed in a standard location. @@ -32,11 +34,11 @@ npm run test:e2e:obsidian:install-appimage The script downloads Obsidian `1.12.7` for the current architecture, stores it in `_testdata/obsidian`, and extracts it to `_testdata/obsidian/squashfs-root`. The runner checks `_testdata/obsidian/squashfs-root/obsidian` before the AppImage path. -Do not download the AppImage on every CI run. Prefer one of these approaches: +These tests are intended for local verification, not the default CI gate. Reuse the installed Obsidian application, or reuse the extracted AppImage directory between local runs: -- set `OBSIDIAN_BINARY` to a pre-installed Obsidian executable, -- restore `_testdata/obsidian/squashfs-root` from a CI cache, or -- run `test:e2e:obsidian:install-appimage` only in a manually triggered preparation job. +- set `OBSIDIAN_BINARY` to an installed Obsidian executable, +- keep `_testdata/obsidian/squashfs-root` after running the AppImage installer, or +- run `test:e2e:obsidian:install-appimage` again only when the local Obsidian version should change. ## Commands @@ -47,18 +49,25 @@ npm run test:e2e:obsidian:cli-help -- vaults verbose npm run test:e2e:obsidian:smoke npm run test:e2e:obsidian:vault-reflection npm run test:e2e:obsidian:couchdb-upload +npm run test:e2e:obsidian:minio-upload npm run test:e2e:obsidian:startup-scan npm run test:e2e:obsidian:two-vault-sync npm run test:e2e:obsidian:hidden-file-snippet-sync npm run test:e2e:obsidian:customisation-sync npm run test:e2e:obsidian:setting-markdown-export +npm run test:e2e:obsidian:local-suite +npm run test:e2e:obsidian:local-suite:services ``` +`test:e2e:obsidian:local-suite` runs `npm run build`, discovery, smoke, vault reflection, CouchDB upload, Object Storage upload, startup scan, two-vault synchronisation, Hidden File Sync, Customisation Sync, and setting Markdown export in sequence. Start the local CouchDB and MinIO fixtures before running it, or use `test:e2e:obsidian:local-suite:services` to let the wrapper stop leftover fixtures, start fresh fixtures, and stop them again after the run. + `test:e2e:obsidian:couchdb-upload` reuses the CouchDB variables from `.test.env` or the process environment. It expects a reachable CouchDB service, creates a unique database, configures Self-hosted LiveSync through `obsidian-cli eval`, creates a note in real Obsidian, commits the note into the local database, runs one-shot synchronisation, and verifies that the remote database contains both the metadata document and its chunk documents. +`test:e2e:obsidian:minio-upload` reuses the Object Storage variables from `.test.env` or the process environment. It expects a reachable S3-compatible service, configures Self-hosted LiveSync for Object Storage through `obsidian-cli eval`, creates a note in real Obsidian, runs one-shot Journal Sync, and verifies through the AWS SDK that objects were written under a unique bucket prefix. + `test:e2e:obsidian:startup-scan` configures a temporary CouchDB database, stops Obsidian, writes a note directly into the vault, restarts Obsidian, and verifies from CouchDB that the boot-time scan picked up the offline file. -`test:e2e:obsidian:two-vault-sync` runs a two-vault note synchronisation workflow. It verifies note creation, update, deletion, Markdown conflict automatic merging with the merged result propagated by a second synchronisation, and per-device target filters where one vault ignores a note that the other vault synchronises. +`test:e2e:obsidian:two-vault-sync` runs a two-vault note synchronisation workflow. It verifies note creation, update, rename, deletion, per-device target filters where one vault ignores a note that the other vault synchronises, and a separate encrypted round-trip with Path Obfuscation enabled. The optional Markdown conflict automatic merge check can be enabled with `E2E_OBSIDIAN_INCLUDE_MARKDOWN_CONFLICT=true`, but it is not part of the default local suite. `test:e2e:obsidian:hidden-file-snippet-sync` runs a two-vault hidden file round-trip. It verifies creation and deletion of a real `.obsidian/snippets/*.css` file, automatic JSON conflict merging for a hidden file with the merged result propagated by a second synchronisation, manual JSON Resolve dialogue application through Obsidian's UI, and per-device target patterns where one vault ignores a hidden file that the other vault synchronises. @@ -66,10 +75,18 @@ npm run test:e2e:obsidian:setting-markdown-export `test:e2e:obsidian:setting-markdown-export` enables setting Markdown export, waits for the generated Markdown file in the vault, and verifies that credentials are omitted when `writeCredentialsForSettingSync=false`. -Start the local CouchDB fixture first when one is not already running: +Start the local fixtures first when they are not already running: ```bash npm run test:docker-couchdb:start +npm run test:docker-s3:start +npm run test:e2e:obsidian:local-suite +``` + +Or let the wrapper manage both fixtures: + +```bash +npm run test:e2e:obsidian:local-suite:services ``` Useful environment variables: @@ -89,10 +106,13 @@ Useful environment variables: - `E2E_OBSIDIAN_CORE_READY_TIMEOUT_MS`: timeout for waiting until Self-hosted LiveSync reports that its core lifecycle and local database are ready. - `E2E_OBSIDIAN_LOCAL_DB_TIMEOUT_MS`: timeout for waiting until a file appears in Self-hosted LiveSync's local database. - `E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS`: timeout for waiting until CouchDB contains uploaded E2E documents. +- `E2E_OBSIDIAN_OBJECT_STORAGE_TIMEOUT_MS`: timeout for waiting until Object Storage contains uploaded E2E objects. - `E2E_OBSIDIAN_KEEP_COUCHDB=true`: keep the temporary CouchDB database for inspection. +- `E2E_OBSIDIAN_KEEP_OBJECT_STORAGE=true`: keep the temporary Object Storage prefix for inspection. - `E2E_OBSIDIAN_STARTUP_GRACE_MS`: early process-exit detection window in milliseconds. - `E2E_OBSIDIAN_KEEP_VAULT=true`: keep the temporary vault for inspection. - `E2E_OBSIDIAN_USE_XVFB=false`: disable automatic `xvfb-run` on headless Linux. +- `E2E_OBSIDIAN_USE_USER_DATA_DIR=false`: disable the isolated Electron `--user-data-dir` argument. This is not recommended for normal local testing. - `E2E_OBSIDIAN_ARGS`: override the default Obsidian launch arguments. On headless Linux, the runner automatically uses `/usr/bin/xvfb-run` when no `DISPLAY` or `WAYLAND_DISPLAY` is present. diff --git a/test/e2e-obsidian/runner/cli.ts b/test/e2e-obsidian/runner/cli.ts index e38a874..052f410 100644 --- a/test/e2e-obsidian/runner/cli.ts +++ b/test/e2e-obsidian/runner/cli.ts @@ -71,9 +71,10 @@ export async function openVaultWithObsidianCli( export async function evalObsidianJson( cliBinary: string, code: string, - env: NodeJS.ProcessEnv = process.env + env: NodeJS.ProcessEnv = process.env, + timeoutMs?: number ): Promise { - const result = await runObsidianCli(cliBinary, ["eval", `code=${code}`], env); + const result = await runObsidianCli(cliBinary, ["eval", `code=${code}`], env, timeoutMs); if (result.code !== 0) { throw new Error( [ diff --git a/test/e2e-obsidian/runner/launch.ts b/test/e2e-obsidian/runner/launch.ts index 8a9b602..3fe745f 100644 --- a/test/e2e-obsidian/runner/launch.ts +++ b/test/e2e-obsidian/runner/launch.ts @@ -1,11 +1,13 @@ -import { spawn, type ChildProcess } from "node:child_process"; +import { execFile, spawn, type ChildProcess } from "node:child_process"; import { once } from "node:events"; import { existsSync } from "node:fs"; import { dirname } from "node:path"; import { platform } from "node:process"; +import { promisify } from "node:util"; export type ObsidianProcess = { process: ChildProcess; + output: () => { stdout: string; stderr: string }; stop: () => Promise; }; @@ -14,10 +16,14 @@ export type LaunchObsidianOptions = { vaultPath: string; homePath?: string; xdgConfigPath?: string; + xdgCachePath?: string; + xdgDataPath?: string; userDataPath?: string; startupGraceMs?: number; }; +const execFileAsync = promisify(execFile); + function splitArgs(args: string): string[] { return args.split(" ").filter((arg) => arg.length > 0); } @@ -31,9 +37,13 @@ function launchArgs(options: LaunchObsidianOptions): string[] { "--no-sandbox", "--disable-gpu", "--disable-software-rasterizer", - ...(process.env.E2E_OBSIDIAN_USE_USER_DATA_DIR === "true" && options.userDataPath + ...(process.env.E2E_OBSIDIAN_USE_USER_DATA_DIR !== "false" && options.userDataPath ? [`--user-data-dir=${options.userDataPath}`] : []), + ...(process.env.E2E_OBSIDIAN_REMOTE_DEBUGGING_PORT + ? [`--remote-debugging-port=${process.env.E2E_OBSIDIAN_REMOTE_DEBUGGING_PORT}`] + : []), + `obsidian://open?path=${encodeURIComponent(options.vaultPath)}`, ]; } @@ -47,7 +57,63 @@ function shouldUseXvfb(): boolean { return platform === "linux" && existsSync("/usr/bin/xvfb-run"); } +async function listChildPids(pid: number): Promise { + if (platform === "win32") { + return []; + } + const { stdout } = await execFileAsync("ps", ["-o", "pid=", "--ppid", String(pid)]).catch(() => ({ + stdout: "", + })); + const directChildren = stdout + .split("\n") + .map((line) => Number(line.trim())) + .filter((childPid) => Number.isInteger(childPid) && childPid > 0); + const descendants = await Promise.all(directChildren.map((childPid) => listChildPids(childPid))); + return [...directChildren, ...descendants.flat()]; +} + +async function killPids(pids: number[], signal: NodeJS.Signals): Promise { + for (const pid of pids) { + if (pid === process.pid) { + continue; + } + try { + process.kill(pid, signal); + } catch { + // The process may have exited between discovery and signalling. + } + } +} + +async function waitForExit(exitPromise: Promise, timeoutMs: number): Promise<"exited" | "timeout"> { + const stopTimer = new Promise<"timeout">((resolve) => { + setTimeout(() => resolve("timeout"), timeoutMs); + }); + const stopResult = await Promise.race([exitPromise.then(() => "exited" as const), stopTimer]); + return stopResult; +} + +export async function cleanupStaleObsidianE2EProcesses(): Promise { + if (process.env.E2E_OBSIDIAN_CLEANUP_STALE_PROCESSES === "false" || platform === "win32") { + return; + } + const { stdout } = await execFileAsync("pgrep", ["-f", "obsidian-livesync-e2e-state"]).catch(() => ({ + stdout: "", + })); + const pids = stdout + .split("\n") + .map((line) => Number(line.trim())) + .filter((pid) => Number.isInteger(pid) && pid > 0 && pid !== process.pid); + if (pids.length === 0) { + return; + } + await killPids(pids, "SIGTERM"); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await killPids(pids, "SIGKILL"); +} + export async function launchObsidian(options: LaunchObsidianOptions): Promise { + await cleanupStaleObsidianE2EProcesses(); const startupGraceMs = options.startupGraceMs ?? 1000; const args = launchArgs(options); const useXvfb = shouldUseXvfb(); @@ -61,6 +127,8 @@ export async function launchObsidian(options: LaunchObsidianOptions): Promise ({ stdout, stderr }), stop: async () => { if (child.exitCode !== null || child.signalCode !== null) { return; } + const descendantPids = child.pid ? await listChildPids(child.pid) : []; if (child.pid) { - process.kill(-child.pid, "SIGTERM"); + try { + process.kill(-child.pid, "SIGTERM"); + } catch { + child.kill("SIGTERM"); + } } else { child.kill("SIGTERM"); } - const stopTimer = new Promise<"timeout">((resolve) => { - setTimeout(() => resolve("timeout"), 5000); - }); - const stopResult = await Promise.race([exitPromise, stopTimer]); + await killPids(descendantPids.reverse(), "SIGTERM"); + const stopResult = await waitForExit(exitPromise, 5000); if (stopResult === "timeout") { if (child.pid) { - process.kill(-child.pid, "SIGKILL"); + try { + process.kill(-child.pid, "SIGKILL"); + } catch { + child.kill("SIGKILL"); + } } else { child.kill("SIGKILL"); } + await killPids(descendantPids, "SIGKILL"); await exitPromise; } }, diff --git a/test/e2e-obsidian/runner/liveSyncWorkflow.ts b/test/e2e-obsidian/runner/liveSyncWorkflow.ts index b47c1cb..ef842c3 100644 --- a/test/e2e-obsidian/runner/liveSyncWorkflow.ts +++ b/test/e2e-obsidian/runner/liveSyncWorkflow.ts @@ -1,13 +1,18 @@ import { evalObsidianJson } from "./cli.ts"; import type { CouchDbConfig } from "./couchdb.ts"; +import type { ObjectStorageConfig } from "./objectStorage.ts"; export type ConfiguredSettings = { isConfigured: boolean; liveSync: boolean; syncOnStart: boolean; syncOnSave: boolean; + remoteType: string; couchDB_URI: string; couchDB_DBNAME: string; + endpoint?: string; + bucket?: string; + bucketPrefix?: string; }; export type CoreReadiness = { @@ -23,6 +28,30 @@ export type LocalDatabaseEntry = { children: string[]; }; +function e2ePreferredSettingsSource(): string[] { + return [ + "liveSync:false,", + "syncOnStart:false,", + "syncOnSave:false,", + "usePluginSync:false,", + "usePluginSyncV2:true,", + "useEden:false,", + "customChunkSize:60,", + "sendChunksBulk:false,", + "sendChunksBulkMaxSize:1,", + "chunkSplitterVersion:'v3-rabin-karp',", + "readChunksOnline:true,", + "disableCheckingConfigMismatch:false,", + "enableCompression:false,", + "hashAlg:'xxhash64',", + "handleFilenameCaseSensitive:false,", + "doNotUseFixedRevisionForChunks:true,", + "E2EEAlgorithm:'v2',", + "doctorProcessedVersion:'0.25.27',", + "isConfigured:true,", + ]; +} + export function assertEqual(actual: unknown, expected: unknown, message: string): void { if (actual !== expected) { throw new Error(`${message}\nExpected: ${String(expected)}\nActual: ${String(actual)}`); @@ -47,18 +76,7 @@ export async function configureCouchDb( `couchDB_PASSWORD:${JSON.stringify(settings.password)},`, `couchDB_DBNAME:${JSON.stringify(settings.dbName)},`, "remoteType:'',", - "liveSync:false,", - "syncOnStart:false,", - "syncOnSave:false,", - "usePluginSync:false,", - "usePluginSyncV2:true,", - "useEden:false,", - "customChunkSize:1,", - "sendChunksBulkMaxSize:1,", - "chunkSplitterVersion:'v3-rabin-karp',", - "readChunksOnline:false,", - "disableCheckingConfigMismatch:true,", - "isConfigured:true,", + ...e2ePreferredSettingsSource(), ...Object.entries(overrides).map(([key, value]) => `${JSON.stringify(key)}:${JSON.stringify(value)},`), "};", "await core.services.setting.applyExternalSettings(nextSettings,true);", @@ -69,6 +87,7 @@ export async function configureCouchDb( "liveSync:current.liveSync,", "syncOnStart:current.syncOnStart,", "syncOnSave:current.syncOnSave,", + "remoteType:current.remoteType,", "couchDB_URI:current.couchDB_URI,", "couchDB_DBNAME:current.couchDB_DBNAME,", "});", @@ -78,6 +97,52 @@ export async function configureCouchDb( ); } +export async function configureObjectStorage( + cliBinary: string, + env: NodeJS.ProcessEnv, + settings: ObjectStorageConfig & { bucketPrefix: string }, + overrides: Record = {} +): Promise { + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + "const plugin=app.plugins.plugins['obsidian-livesync'];", + "const core=plugin.core;", + "const nextSettings={", + "remoteType:'MINIO',", + `endpoint:${JSON.stringify(settings.endpoint)},`, + `accessKey:${JSON.stringify(settings.accessKey)},`, + `secretKey:${JSON.stringify(settings.secretKey)},`, + `bucket:${JSON.stringify(settings.bucket)},`, + `region:${JSON.stringify(settings.region)},`, + `forcePathStyle:${JSON.stringify(settings.forcePathStyle)},`, + `bucketPrefix:${JSON.stringify(settings.bucketPrefix)},`, + "bucketCustomHeaders:'',", + ...e2ePreferredSettingsSource(), + ...Object.entries(overrides).map(([key, value]) => `${JSON.stringify(key)}:${JSON.stringify(value)},`), + "};", + "await core.services.setting.applyExternalSettings(nextSettings,true);", + "await core.services.control.applySettings();", + "const current=core.services.setting.currentSettings();", + "return JSON.stringify({", + "isConfigured:current.isConfigured,", + "liveSync:current.liveSync,", + "syncOnStart:current.syncOnStart,", + "syncOnSave:current.syncOnSave,", + "remoteType:current.remoteType,", + "couchDB_URI:current.couchDB_URI,", + "couchDB_DBNAME:current.couchDB_DBNAME,", + "endpoint:current.endpoint,", + "bucket:current.bucket,", + "bucketPrefix:current.bucketPrefix,", + "});", + "})()", + ].join(""), + env + ); +} + export async function waitForLiveSyncCoreReady( cliBinary: string, env: NodeJS.ProcessEnv, diff --git a/test/e2e-obsidian/runner/objectStorage.ts b/test/e2e-obsidian/runner/objectStorage.ts new file mode 100644 index 0000000..f0ea845 --- /dev/null +++ b/test/e2e-obsidian/runner/objectStorage.ts @@ -0,0 +1,142 @@ +import { + CreateBucketCommand, + DeleteObjectsCommand, + ListObjectsV2Command, + S3Client, + type _Object, +} from "@aws-sdk/client-s3"; +import { readFile } from "node:fs/promises"; +import { resolve } from "node:path"; + +export type ObjectStorageConfig = { + endpoint: string; + accessKey: string; + secretKey: string; + bucket: string; + region: string; + forcePathStyle: boolean; +}; + +function parseEnvFile(content: string): Record { + const entries = content + .split(/\r?\n/u) + .map((line) => line.trim()) + .filter((line) => line && !line.startsWith("#")) + .map((line) => { + const equalsAt = line.indexOf("="); + if (equalsAt < 0) { + return undefined; + } + const key = line.slice(0, equalsAt).trim(); + const rawValue = line.slice(equalsAt + 1).trim(); + const value = rawValue.replace(/^['"]|['"]$/gu, ""); + return [key, value] as const; + }) + .filter((entry): entry is readonly [string, string] => entry !== undefined); + return Object.fromEntries(entries); +} + +function getEnvValue(values: Record, ...keys: string[]): string { + for (const key of keys) { + const value = values[key]?.trim(); + if (value) { + return value; + } + } + throw new Error(`Required Object Storage environment value is missing: ${keys.join(" or ")}`); +} + +export async function loadObjectStorageConfig(envFile = ".test.env"): Promise { + let fileValues: Record = {}; + try { + fileValues = parseEnvFile(await readFile(resolve(envFile), "utf-8")); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ENOENT") { + throw error; + } + } + + const values = { ...fileValues, ...process.env }; + return { + endpoint: getEnvValue(values, "MINIO_ENDPOINT", "minioEndpoint").replace(/\/+$/u, ""), + accessKey: getEnvValue(values, "MINIO_ACCESS_KEY", "accessKey"), + secretKey: getEnvValue(values, "MINIO_SECRET_KEY", "secretKey"), + bucket: getEnvValue(values, "MINIO_BUCKET", "bucketName"), + region: values.MINIO_REGION?.trim() || values.region?.trim() || "us-east-1", + forcePathStyle: values.MINIO_FORCE_PATH_STYLE?.trim() !== "false", + }; +} + +export function makeUniqueBucketPrefix(label: string): string { + const random = Math.random().toString(36).slice(2, 8); + return `obsidian-e2e/${label}-${Date.now()}-${random}/`; +} + +export function createObjectStorageClient(config: ObjectStorageConfig): S3Client { + return new S3Client({ + endpoint: config.endpoint, + region: config.region, + forcePathStyle: config.forcePathStyle, + credentials: { + accessKeyId: config.accessKey, + secretAccessKey: config.secretKey, + }, + }); +} + +export async function ensureObjectStorageBucket(config: ObjectStorageConfig): Promise { + const client = createObjectStorageClient(config); + try { + await client.send(new CreateBucketCommand({ Bucket: config.bucket })); + } catch (error) { + const name = (error as { name?: string }).name; + if (name !== "BucketAlreadyOwnedByYou" && name !== "BucketAlreadyExists") { + throw error; + } + } finally { + client.destroy(); + } +} + +export async function listObjectStorageObjects(config: ObjectStorageConfig, prefix: string): Promise<_Object[]> { + const client = createObjectStorageClient(config); + try { + const objects: _Object[] = []; + let continuationToken: string | undefined; + do { + const response = await client.send( + new ListObjectsV2Command({ + Bucket: config.bucket, + Prefix: prefix, + ContinuationToken: continuationToken, + }) + ); + objects.push(...(response.Contents ?? [])); + continuationToken = response.NextContinuationToken; + } while (continuationToken); + return objects; + } finally { + client.destroy(); + } +} + +export async function deleteObjectStoragePrefix(config: ObjectStorageConfig, prefix: string): Promise { + const client = createObjectStorageClient(config); + try { + const objects = await listObjectStorageObjects(config, prefix); + const keys = objects.flatMap((object) => (object.Key ? [{ Key: object.Key }] : [])); + for (let index = 0; index < keys.length; index += 1000) { + await client.send( + new DeleteObjectsCommand({ + Bucket: config.bucket, + Delete: { + Objects: keys.slice(index, index + 1000), + Quiet: true, + }, + }) + ); + } + } finally { + client.destroy(); + } +} diff --git a/test/e2e-obsidian/runner/session.ts b/test/e2e-obsidian/runner/session.ts index ff7b931..5d7bdd1 100644 --- a/test/e2e-obsidian/runner/session.ts +++ b/test/e2e-obsidian/runner/session.ts @@ -1,8 +1,9 @@ -import { openVaultWithObsidianCli, runObsidianCli } from "./cli.ts"; +import { evalObsidianJson, openVaultWithObsidianCli, runObsidianCli } from "./cli.ts"; import { launchObsidian, type ObsidianProcess } from "./launch.ts"; import { installBuiltPlugin, type PluginInstallResult } from "./pluginInstaller.ts"; import { waitForPluginReady, type PluginReadiness } from "./readiness.ts"; import type { TemporaryVault } from "./vault.ts"; +import { obsidianRemoteDebuggingPort, preseedTrustedVaultState, trustVaultIfPrompted } from "./ui.ts"; export type ObsidianLiveSyncSession = { app: ObsidianProcess; @@ -19,13 +20,21 @@ export type StartObsidianLiveSyncSessionOptions = { }; async function waitForPluginCatalogue(cliBinary: string, env: NodeJS.ProcessEnv): Promise { - const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS ?? 15000); + const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CLI_READY_TIMEOUT_MS ?? 60000); let lastOutput = ""; while (Date.now() < deadline) { - const result = await runObsidianCli(cliBinary, ["plugins", "filter=community"], env); - lastOutput = [result.stdout, result.stderr].filter(Boolean).join("\n"); - if (result.stdout.includes("obsidian-livesync")) { - return; + try { + const result = await evalObsidianJson<{ hasLiveSync: boolean }>( + cliBinary, + ["JSON.stringify({", "hasLiveSync:!!app.plugins?.manifests?.['obsidian-livesync']", "})"].join(""), + env + ); + lastOutput = JSON.stringify(result); + if (result.hasLiveSync) { + return; + } + } catch (error) { + lastOutput = error instanceof Error ? error.message : String(error); } await new Promise((resolve) => setTimeout(resolve, 500)); } @@ -66,11 +75,14 @@ export async function startObsidianLiveSyncSession( options: StartObsidianLiveSyncSessionOptions ): Promise { const install = await installBuiltPlugin(options.vault.path); + const remoteDebuggingPort = obsidianRemoteDebuggingPort(); const app = await launchObsidian({ binary: options.binary, vaultPath: options.vault.path, homePath: options.vault.homePath, xdgConfigPath: options.vault.xdgConfigPath, + xdgCachePath: options.vault.xdgCachePath, + xdgDataPath: options.vault.xdgDataPath, userDataPath: options.vault.userDataPath, startupGraceMs: options.startupGraceMs, }); @@ -78,17 +90,30 @@ export async function startObsidianLiveSyncSession( ...process.env, HOME: options.vault.homePath, XDG_CONFIG_HOME: options.vault.xdgConfigPath, + XDG_CACHE_HOME: options.vault.xdgCachePath, + XDG_DATA_HOME: options.vault.xdgDataPath, }; try { + await preseedTrustedVaultState(remoteDebuggingPort, options.vault.id); await openVaultWithObsidianCli(options.cliBinary, options.vault.path, cliEnv); + await trustVaultIfPrompted(remoteDebuggingPort); await waitForPluginCatalogue(options.cliBinary, cliEnv); await enableCommunityPlugins(options.cliBinary, cliEnv); await reloadLiveSyncPlugin(options.cliBinary, cliEnv); const readiness = await waitForPluginReady(options.cliBinary, cliEnv); return { app, cliEnv, install, readiness }; } catch (error) { + const output = app.output(); await app.stop(); - throw error; + throw new Error( + [ + error instanceof Error ? error.message : String(error), + output.stdout ? `Obsidian stdout:\n${output.stdout}` : undefined, + output.stderr ? `Obsidian stderr:\n${output.stderr}` : undefined, + ] + .filter(Boolean) + .join("\n") + ); } } diff --git a/test/e2e-obsidian/runner/ui.ts b/test/e2e-obsidian/runner/ui.ts new file mode 100644 index 0000000..d6c8848 --- /dev/null +++ b/test/e2e-obsidian/runner/ui.ts @@ -0,0 +1,83 @@ +import { chromium, type Page } from "playwright"; + +export function obsidianRemoteDebuggingPort(): number { + const port = Number(process.env.E2E_OBSIDIAN_REMOTE_DEBUGGING_PORT ?? 9222); + process.env.E2E_OBSIDIAN_REMOTE_DEBUGGING_PORT = String(port); + return port; +} + +async function waitForCdp(port: number): Promise { + const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_CDP_TIMEOUT_MS ?? 30000); + while (Date.now() < deadline) { + try { + const response = await fetch(`http://127.0.0.1:${port}/json/version`); + if (response.ok) { + return; + } + } catch { + // Keep polling until Obsidian exposes the debugging endpoint. + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Timed out waiting for Obsidian DevTools endpoint on port ${port}`); +} + +export async function withObsidianPage(port: number, operation: (page: Page) => Promise): Promise { + await waitForCdp(port); + const browser = await chromium.connectOverCDP(`http://127.0.0.1:${port}`); + try { + const context = browser.contexts()[0]; + const page = context.pages()[0] ?? (await context.waitForEvent("page", { timeout: 10000 })); + return await operation(page); + } finally { + await browser.close(); + } +} + +export async function preseedTrustedVaultState(port: number, vaultId: string): Promise { + await withObsidianPage(port, async (page) => { + await page.evaluate((id) => { + localStorage.setItem(`enable-plugin-${id}`, "true"); + }, vaultId); + await page.reload({ waitUntil: "domcontentloaded", timeout: 10000 }).catch(() => undefined); + await page.waitForTimeout(1000); + }); +} + +export async function trustVaultIfPrompted(port: number): Promise { + await withObsidianPage(port, async (page) => { + const deadline = Date.now() + Number(process.env.E2E_OBSIDIAN_TRUST_PROMPT_TIMEOUT_MS ?? 30000); + while (Date.now() < deadline) { + const yesButton = page.getByRole("button", { name: "Yes" }); + if (await yesButton.isVisible({ timeout: 1000 }).catch(() => false)) { + await yesButton.click(); + await page.waitForTimeout(500); + continue; + } + + const trustButton = page.getByText("Trust author and enable plugins"); + if (await trustButton.isVisible({ timeout: 1000 }).catch(() => false)) { + await trustButton.click(); + await page.waitForTimeout(500); + continue; + } + + const workspace = page.locator(".workspace"); + if (await workspace.isVisible({ timeout: 1000 }).catch(() => false)) { + return; + } + } + }); +} + +export async function clickJsonResolveOption(port: number, mode: "AB" | "BA"): Promise { + await withObsidianPage(port, async (page) => { + const option = page.locator(`label:has(input[name="disp"][value="${mode}"])`); + await option.click({ timeout: 10000 }); + const checked = await page.locator(`input[name="disp"][value="${mode}"]`).isChecked({ timeout: 10000 }); + if (!checked) { + throw new Error(`JSON Resolve option was not selected: ${mode}`); + } + await page.getByRole("button", { name: "Apply" }).click({ timeout: 10000 }); + }); +} diff --git a/test/e2e-obsidian/runner/vault.ts b/test/e2e-obsidian/runner/vault.ts index e24f2d6..6c75f95 100644 --- a/test/e2e-obsidian/runner/vault.ts +++ b/test/e2e-obsidian/runner/vault.ts @@ -5,8 +5,11 @@ import { tmpdir } from "node:os"; export type TemporaryVault = { path: string; name: string; + id: string; homePath: string; xdgConfigPath: string; + xdgCachePath: string; + xdgDataPath: string; userDataPath: string; dispose: () => Promise; }; @@ -18,21 +21,33 @@ export async function createTemporaryVault(prefix = "obsidian-livesync-e2e-"): P await mkdir(join(vaultPath, ".obsidian"), { recursive: true }); const homePath = join(statePath, "home"); const xdgConfigPath = join(statePath, "xdg-config"); + const xdgCachePath = join(statePath, "xdg-cache"); + const xdgDataPath = join(statePath, "xdg-data"); const userDataPath = join(statePath, "user-data"); + const id = `livesync-e2e-${Date.now()}`; await mkdir(homePath, { recursive: true }); await mkdir(xdgConfigPath, { recursive: true }); + await mkdir(xdgCachePath, { recursive: true }); + await mkdir(xdgDataPath, { recursive: true }); await mkdir(userDataPath, { recursive: true }); await writeFile( join(vaultPath, ".obsidian", "app.json"), JSON.stringify({ legacyEditor: false, safeMode: false }, null, 4) ); - await writeObsidianVaultRegistry(vaultPath, name, homePath, xdgConfigPath, userDataPath); + await writeFile( + join(vaultPath, ".obsidian", "community-plugins.json"), + JSON.stringify(["obsidian-livesync"], null, 4) + ); + await writeObsidianVaultRegistry(id, vaultPath, name, homePath, xdgConfigPath, userDataPath); return { path: vaultPath, name, + id, homePath, xdgConfigPath, + xdgCachePath, + xdgDataPath, userDataPath, dispose: async () => { if (process.env.E2E_OBSIDIAN_KEEP_VAULT === "true") { @@ -49,22 +64,23 @@ export async function createTemporaryVault(prefix = "obsidian-livesync-e2e-"): P } async function writeObsidianVaultRegistry( + vaultId: string, vaultPath: string, vaultName: string, homePath: string, xdgConfigPath: string, userDataPath: string ): Promise { - const vaultId = `livesync-e2e-${Date.now()}`; + const vaultRecord = { + path: vaultPath, + ts: Date.now(), + open: true, + name: vaultName, + }; const registry = { cli: true, vaults: { - [vaultId]: { - path: vaultPath, - ts: Date.now(), - open: true, - name: vaultName, - }, + [vaultId]: vaultRecord, }, }; const registryText = JSON.stringify(registry, null, 4); @@ -74,4 +90,5 @@ async function writeObsidianVaultRegistry( await writeFile(join(obsidianConfigDir, "obsidian.json"), registryText); } await writeFile(join(userDataPath, "obsidian.json"), registryText); + await writeFile(join(userDataPath, `${vaultId}.json`), JSON.stringify(vaultRecord, null, 4)); } diff --git a/test/e2e-obsidian/scripts/cli-help.ts b/test/e2e-obsidian/scripts/cli-help.ts index f48de33..db8bccc 100644 --- a/test/e2e-obsidian/scripts/cli-help.ts +++ b/test/e2e-obsidian/scripts/cli-help.ts @@ -25,6 +25,8 @@ async function main(): Promise { ...process.env, HOME: vault.homePath, XDG_CONFIG_HOME: vault.xdgConfigPath, + XDG_CACHE_HOME: vault.xdgCachePath, + XDG_DATA_HOME: vault.xdgDataPath, }; await runObsidianCli(cli.binary, [`obsidian://open?path=${encodeURIComponent(vault.path)}`], cliEnv); await new Promise((resolve) => setTimeout(resolve, 3000)); diff --git a/test/e2e-obsidian/scripts/customisation-sync.ts b/test/e2e-obsidian/scripts/customisation-sync.ts index 52bcd5e..66b3799 100644 --- a/test/e2e-obsidian/scripts/customisation-sync.ts +++ b/test/e2e-obsidian/scripts/customisation-sync.ts @@ -228,10 +228,12 @@ async function storeCustomisationFile(cliBinary: string, env: NodeJS.ProcessEnv, "const result=await addOn.storeCustomizationFiles(path,term);", "const rows=(await core.localDatabase.allDocsRaw({include_docs:true})).rows;", "const entries=rows.map((row)=>row.doc).filter((doc)=>doc?.path?.startsWith('ix:')).map((doc)=>doc.path);", - "if(!result){", + "const filename=path.split('/').pop();", + "const existing=entries.some((entry)=>entry.startsWith(`ix:${term}/${category}/`)&&entry.endsWith(`%${filename}`));", + "if(!result&&!existing){", " throw new Error(`Could not store Customisation Sync file: path=${path}; term=${term}; category=${category}; stat=${JSON.stringify(stat)}; result=${JSON.stringify(result)}; entries=${JSON.stringify(entries)}`);", "}", - "return JSON.stringify({ok:true,path,term,category,entries});", + "return JSON.stringify({ok:true,path,term,category,result:!!result,existing,entries});", "})()", ].join(""), env diff --git a/test/e2e-obsidian/scripts/debug-ui.ts b/test/e2e-obsidian/scripts/debug-ui.ts new file mode 100644 index 0000000..eca943f --- /dev/null +++ b/test/e2e-obsidian/scripts/debug-ui.ts @@ -0,0 +1,60 @@ +import { launchObsidian } from "../runner/launch.ts"; +import { installBuiltPlugin } from "../runner/pluginInstaller.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; +import { requireObsidianBinary } from "../runner/environment.ts"; +import { writeFile } from "node:fs/promises"; +import { obsidianRemoteDebuggingPort, preseedTrustedVaultState, withObsidianPage } from "../runner/ui.ts"; + +const port = obsidianRemoteDebuggingPort(); + +async function main(): Promise { + const binary = requireObsidianBinary(); + const vault = await createTemporaryVault(); + await installBuiltPlugin(vault.path); + const app = await launchObsidian({ + binary, + vaultPath: vault.path, + homePath: vault.homePath, + xdgConfigPath: vault.xdgConfigPath, + xdgCachePath: vault.xdgCachePath, + xdgDataPath: vault.xdgDataPath, + userDataPath: vault.userDataPath, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + + try { + await preseedTrustedVaultState(port, vault.id); + const { screenshotPath, textPath } = await withObsidianPage(port, async (page) => { + await page.waitForTimeout(Number(process.env.E2E_OBSIDIAN_DEBUG_WAIT_MS ?? 5000)); + const title = await page.title().catch((error: unknown) => `title error: ${String(error)}`); + const url = page.url(); + const text = await page + .locator("body") + .innerText({ timeout: 5000 }) + .catch((error: unknown) => { + return `body text error: ${String(error)}`; + }); + if (process.env.E2E_OBSIDIAN_DEBUG_CLICK_TRUST === "true") { + await page.getByText("Trust author and enable plugins").click({ timeout: 10000 }); + await page.waitForTimeout(Number(process.env.E2E_OBSIDIAN_DEBUG_AFTER_CLICK_WAIT_MS ?? 3000)); + } + const screenshotPath = process.env.E2E_OBSIDIAN_DEBUG_SCREENSHOT ?? "/tmp/obsidian-e2e-debug.png"; + const textPath = process.env.E2E_OBSIDIAN_DEBUG_TEXT ?? "/tmp/obsidian-e2e-debug.txt"; + await page.screenshot({ path: screenshotPath, fullPage: true }); + await writeFile(textPath, [`title: ${title}`, `url: ${url}`, "", text].join("\n"), "utf-8"); + return { screenshotPath, textPath }; + }); + console.log(`Temporary vault: ${vault.path}`); + console.log(`Temporary Obsidian state: ${vault.userDataPath}`); + console.log(`Debug text: ${textPath}`); + console.log(`Debug screenshot: ${screenshotPath}`); + } finally { + await app.stop(); + await vault.dispose(); + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts b/test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts index 01be4fb..ae645e1 100644 --- a/test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts +++ b/test/e2e-obsidian/scripts/hidden-file-snippet-sync.ts @@ -21,6 +21,7 @@ import { type LocalDatabaseEntry, } from "../runner/liveSyncWorkflow.ts"; import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { clickJsonResolveOption, obsidianRemoteDebuggingPort } from "../runner/ui.ts"; import { createTemporaryVault, type TemporaryVault } from "../runner/vault.ts"; process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; @@ -41,6 +42,7 @@ const snippetContent = [ const mergeJsonPath = ".obsidian/livesync-e2e-merge.json"; const manualMergeJsonPath = ".obsidian/livesync-e2e-manual-merge.json"; const targetPath = ".obsidian/livesync-targeted/only-a.json"; +const hiddenFileCliTimeoutMs = Number(process.env.E2E_OBSIDIAN_HIDDEN_FILE_CLI_TIMEOUT_MS ?? 90000); type RunnerContext = { binary: string; @@ -145,7 +147,8 @@ async function scanHiddenDatabase(cliBinary: string, env: NodeJS.ProcessEnv): Pr "return JSON.stringify({ok:true});", "})()", ].join(""), - env + env, + hiddenFileCliTimeoutMs ); } @@ -161,7 +164,8 @@ async function resolveHiddenConflicts(cliBinary: string, env: NodeJS.ProcessEnv) "return JSON.stringify({ok:true});", "})()", ].join(""), - env + env, + hiddenFileCliTimeoutMs ); } @@ -233,34 +237,6 @@ async function openHiddenJsonResolveModal(cliBinary: string, env: NodeJS.Process ); } -async function clickJsonResolveOption(cliBinary: string, env: NodeJS.ProcessEnv, mode: "AB" | "BA"): Promise { - await evalObsidianJson( - cliBinary, - [ - "(async()=>{", - `const mode=${JSON.stringify(mode)};`, - "const deadline=Date.now()+10000;", - "while(Date.now()candidate.value===mode);", - " const apply=[...document.querySelectorAll('button')].find((button)=>button.textContent?.trim()==='Apply');", - " if(input&&apply){", - " input.click();", - " input.dispatchEvent(new Event('change',{bubbles:true}));", - " await new Promise((resolve)=>setTimeout(resolve,100));", - " apply.click();", - " return JSON.stringify({ok:true});", - " }", - " await new Promise((resolve)=>setTimeout(resolve,250));", - "}", - "const buttons=[...document.querySelectorAll('button')].map((button)=>button.textContent?.trim()).filter(Boolean);", - "const inputs=[...document.querySelectorAll('input[name=\"disp\"]')].map((input)=>input.value);", - "throw new Error(`Timed out waiting for JSON resolve modal; buttons=${JSON.stringify(buttons)}; inputs=${JSON.stringify(inputs)}`);", - "})()", - ].join(""), - env - ); -} - async function storeHiddenFileAsConflict( cliBinary: string, env: NodeJS.ProcessEnv, @@ -368,9 +344,15 @@ async function uploadHiddenFile( return entry; } -async function pullAndApplyHiddenFiles(context: RunnerContext, session: ObsidianLiveSyncSession): Promise { +async function pullAndApplyHiddenFiles( + context: RunnerContext, + session: ObsidianLiveSyncSession, + options: { resolveConflicts?: boolean } = {} +): Promise { await pushLocalChanges(context.cliBinary, session.cliEnv); - await resolveHiddenConflicts(context.cliBinary, session.cliEnv); + if (options.resolveConflicts === true) { + await resolveHiddenConflicts(context.cliBinary, session.cliEnv); + } await scanHiddenDatabase(context.cliBinary, session.cliEnv); } @@ -449,7 +431,7 @@ async function runJsonManualConflictResolution(context: RunnerContext, vault: Te const session = await startConfiguredSession(context, vault); await createHiddenJsonConflict(context, session, vault, manualMergeJsonPath, base, left, right); await openHiddenJsonResolveModal(context.cliBinary, session.cliEnv, manualMergeJsonPath); - await clickJsonResolveOption(context.cliBinary, session.cliEnv, "AB"); + await clickJsonResolveOption(obsidianRemoteDebuggingPort(), "AB"); const merged = await waitForPathContent(vault.path, manualMergeJsonPath, (content) => hasJsonValues(content, { shared: "right", fromA: true, fromB: true }) @@ -472,26 +454,36 @@ async function runTargetMismatch( await writeVaultFile(vaultA.path, targetPath, targetContent); let session = await startConfiguredSession(context, vaultA); - await uploadHiddenFile(context, session, targetPath); - await session.app.stop(); + try { + await uploadHiddenFile(context, session, targetPath); + } finally { + await session.app.stop(); + } session = await startConfiguredSession(context, vaultB, { syncInternalFilesTargetPatterns: "snippets", }); - await pullAndApplyHiddenFiles(context, session); - assertEqual( - await pathExists(vaultB.path, targetPath), - false, - "Hidden file was applied on a device where it was not a target file." - ); - await session.app.stop(); + try { + await pullAndApplyHiddenFiles(context, session, { resolveConflicts: false }); + assertEqual( + await pathExists(vaultB.path, targetPath), + false, + "Hidden file was applied on a device where it was not a target file." + ); + } finally { + await session.app.stop(); + } session = await startConfiguredSession(context, vaultB, { syncInternalFilesTargetPatterns: "", }); - await pullAndApplyHiddenFiles(context, session); - const received = await waitForPathContent(vaultB.path, targetPath, (content) => content === targetContent); - await session.app.stop(); + let received = ""; + try { + await pullAndApplyHiddenFiles(context, session, { resolveConflicts: false }); + received = await waitForPathContent(vaultB.path, targetPath, (content) => content === targetContent); + } finally { + await session.app.stop(); + } assertEqual(received, targetContent, "Hidden file was not applied after it became a target file."); console.log("Hidden target mismatch respected per-device target patterns, then applied after enabling the target."); diff --git a/test/e2e-obsidian/scripts/local-suite.ts b/test/e2e-obsidian/scripts/local-suite.ts new file mode 100644 index 0000000..6b804c4 --- /dev/null +++ b/test/e2e-obsidian/scripts/local-suite.ts @@ -0,0 +1,105 @@ +import { spawn } from "node:child_process"; + +type Step = { + name: string; + args: string[]; + optional?: boolean; +}; + +const testSteps: Step[] = [ + { name: "build", args: ["run", "build"] }, + { name: "discover", args: ["run", "test:e2e:obsidian:discover"] }, + { name: "smoke", args: ["run", "test:e2e:obsidian:smoke"] }, + { name: "vault reflection", args: ["run", "test:e2e:obsidian:vault-reflection"] }, + { name: "CouchDB upload", args: ["run", "test:e2e:obsidian:couchdb-upload"] }, + { name: "Object Storage upload", args: ["run", "test:e2e:obsidian:minio-upload"] }, + { name: "startup scan", args: ["run", "test:e2e:obsidian:startup-scan"] }, + { name: "two-vault synchronisation", args: ["run", "test:e2e:obsidian:two-vault-sync"] }, + { name: "hidden file snippet synchronisation", args: ["run", "test:e2e:obsidian:hidden-file-snippet-sync"] }, + { name: "Customisation Sync", args: ["run", "test:e2e:obsidian:customisation-sync"] }, + { name: "setting Markdown export", args: ["run", "test:e2e:obsidian:setting-markdown-export"] }, +]; + +const manageCouchDb = process.argv.includes("--manage-couchdb") || process.argv.includes("--manage-services"); +const manageMinio = process.argv.includes("--manage-minio") || process.argv.includes("--manage-services"); +const keepServices = process.argv.includes("--keep-services"); +const keepCouchDb = keepServices || process.argv.includes("--keep-couchdb"); +const keepMinio = keepServices || process.argv.includes("--keep-minio"); + +function npmBinary(): string { + return process.platform === "win32" ? "npm.cmd" : "npm"; +} + +function runStep(step: Step): Promise { + return new Promise((resolve, reject) => { + console.log(`\n# ${step.name}`); + const child = spawn(npmBinary(), step.args, { + cwd: process.cwd(), + env: process.env, + stdio: "inherit", + }); + child.on("error", reject); + child.on("exit", (code, signal) => { + if (code === 0) { + resolve(); + return; + } + const message = `${step.name} failed with ${signal ? `signal ${signal}` : `exit code ${code}`}`; + if (step.optional) { + console.warn(message); + resolve(); + return; + } + reject(new Error(message)); + }); + }); +} + +async function stopManagedCouchDb(): Promise { + await runStep({ + name: "stop CouchDB fixture", + args: ["run", "test:docker-couchdb:stop"], + optional: true, + }); +} + +async function stopManagedMinio(): Promise { + await runStep({ + name: "stop MinIO fixture", + args: ["run", "test:docker-s3:stop"], + optional: true, + }); +} + +async function main(): Promise { + let shouldStopCouchDb = false; + let shouldStopMinio = false; + try { + if (manageCouchDb) { + await stopManagedCouchDb(); + await runStep({ name: "start CouchDB fixture", args: ["run", "test:docker-couchdb:start"] }); + shouldStopCouchDb = !keepCouchDb; + } + if (manageMinio) { + await stopManagedMinio(); + await runStep({ name: "start MinIO fixture", args: ["run", "test:docker-s3:start"] }); + shouldStopMinio = !keepMinio; + } + + for (const step of testSteps) { + await runStep(step); + } + } finally { + if (shouldStopMinio) { + await stopManagedMinio(); + } + if (shouldStopCouchDb) { + await stopManagedCouchDb(); + } + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/minio-upload.ts b/test/e2e-obsidian/scripts/minio-upload.ts new file mode 100644 index 0000000..36733fc --- /dev/null +++ b/test/e2e-obsidian/scripts/minio-upload.ts @@ -0,0 +1,142 @@ +import { evalObsidianJson } from "../runner/cli.ts"; +import { discoverObsidianCli, requireObsidianBinary } from "../runner/environment.ts"; +import { + assertEqual, + configureObjectStorage, + prepareRemote, + pushLocalChanges, + waitForLiveSyncCoreReady, + type LocalDatabaseEntry, +} from "../runner/liveSyncWorkflow.ts"; +import { + deleteObjectStoragePrefix, + ensureObjectStorageBucket, + listObjectStorageObjects, + loadObjectStorageConfig, + makeUniqueBucketPrefix, +} from "../runner/objectStorage.ts"; +import { startObsidianLiveSyncSession, type ObsidianLiveSyncSession } from "../runner/session.ts"; +import { createTemporaryVault } from "../runner/vault.ts"; + +process.env.E2E_OBSIDIAN_CLI_TIMEOUT_MS ??= "30000"; + +const notePath = "E2E/minio-upload.md"; +const noteContent = [ + "# Object Storage upload from real Obsidian", + "", + "This note is created through Obsidian and uploaded by Self-hosted LiveSync to S3-compatible Object Storage.", + "The test is intentionally small, but it crosses the real Obsidian, Journal Sync, and AWS SDK boundary.", + `Created at: ${new Date().toISOString()}`, + "", +].join("\n"); + +async function createNoteAndWaitForLocalDb(cliBinary: string, env: NodeJS.ProcessEnv): Promise { + return await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const path=${JSON.stringify(notePath)};`, + `const content=${JSON.stringify(noteContent)};`, + "const core=app.plugins.plugins['obsidian-livesync'].core;", + "if(!(await app.vault.adapter.exists('E2E'))) await app.vault.createFolder('E2E');", + "const existing=app.vault.getAbstractFileByPath(path);", + "if(existing) await app.vault.delete(existing);", + "await app.vault.create(path,content);", + "const sleep=(ms)=>new Promise((resolve)=>setTimeout(resolve,ms));", + "let entry=false;", + "for(let i=0;i<40;i++){", + "await core.services.fileProcessing.commitPendingFileEvents();", + "entry=await core.localDatabase.getDBEntry(path,undefined,false,true).catch(()=>false);", + "if(entry&&entry._id&&Array.isArray(entry.children)&&entry.children.length>0) break;", + "await sleep(250);", + "}", + "if(!entry||!entry._id) throw new Error('Timed out waiting for local database entry');", + "return JSON.stringify({id:entry._id,path:entry.path,type:entry.type,children:entry.children||[]});", + "})()", + ].join(""), + env + ); +} + +async function waitForObjectStorageObjects(prefix: string): Promise { + const objectStorage = await loadObjectStorageConfig(); + const timeoutMs = Number(process.env.E2E_OBSIDIAN_OBJECT_STORAGE_TIMEOUT_MS ?? 20000); + const deadline = Date.now() + timeoutMs; + let keys: string[] = []; + while (Date.now() < deadline) { + const objects = await listObjectStorageObjects(objectStorage, prefix); + keys = objects.flatMap((object) => (object.Key ? [object.Key] : [])); + if (keys.length > 0) { + return keys; + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Timed out waiting for Object Storage objects under ${prefix}. Last keys: ${keys.join(", ")}`); +} + +async function main(): Promise { + const binary = requireObsidianBinary(); + const cli = discoverObsidianCli(); + if (!cli.binary) { + throw new Error(`Could not find obsidian-cli. Checked paths: ${cli.checked.join(", ")}`); + } + + const objectStorage = await loadObjectStorageConfig(); + const bucketPrefix = makeUniqueBucketPrefix("minio-upload"); + const vault = await createTemporaryVault(); + let session: ObsidianLiveSyncSession | undefined; + + try { + await ensureObjectStorageBucket(objectStorage); + + console.log(`Using Obsidian executable: ${binary}`); + console.log(`Temporary vault: ${vault.path}`); + console.log(`Temporary Object Storage bucket: ${objectStorage.bucket}`); + console.log(`Temporary Object Storage prefix: ${bucketPrefix}`); + + session = await startObsidianLiveSyncSession({ + binary, + cliBinary: cli.binary, + vault, + startupGraceMs: Number(process.env.E2E_OBSIDIAN_STARTUP_GRACE_MS ?? 1000), + }); + await waitForLiveSyncCoreReady(cli.binary, session.cliEnv); + + const configured = await configureObjectStorage(cli.binary, session.cliEnv, { + ...objectStorage, + bucketPrefix, + }); + await waitForLiveSyncCoreReady(cli.binary, session.cliEnv); + assertEqual(configured.isConfigured, true, "Self-hosted LiveSync was not marked as configured."); + assertEqual(configured.remoteType, "MINIO", "Remote type was not Object Storage."); + assertEqual(configured.endpoint, objectStorage.endpoint, "Configured Object Storage endpoint did not match."); + assertEqual(configured.bucket, objectStorage.bucket, "Configured Object Storage bucket did not match."); + assertEqual(configured.bucketPrefix, bucketPrefix, "Configured Object Storage bucket prefix did not match."); + assertEqual(configured.liveSync, false, "LiveSync should remain disabled during this one-shot workflow."); + + await prepareRemote(cli.binary, session.cliEnv); + const localEntry = await createNoteAndWaitForLocalDb(cli.binary, session.cliEnv); + await pushLocalChanges(cli.binary, session.cliEnv); + + const keys = await waitForObjectStorageObjects(bucketPrefix); + + console.log( + `Uploaded ${localEntry.path} through Journal Sync to ${objectStorage.bucket}/${bucketPrefix} (${keys.length} object(s))` + ); + } finally { + if (session) { + await session.app.stop(); + } + await vault.dispose(); + if (process.env.E2E_OBSIDIAN_KEEP_OBJECT_STORAGE !== "true") { + await deleteObjectStoragePrefix(objectStorage, bucketPrefix).catch((error: unknown) => { + console.warn(error instanceof Error ? error.message : error); + }); + } + } +} + +main().catch((error: unknown) => { + console.error(error instanceof Error ? error.stack : error); + process.exit(1); +}); diff --git a/test/e2e-obsidian/scripts/two-vault-sync.ts b/test/e2e-obsidian/scripts/two-vault-sync.ts index 4708682..f1c23b8 100644 --- a/test/e2e-obsidian/scripts/two-vault-sync.ts +++ b/test/e2e-obsidian/scripts/two-vault-sync.ts @@ -29,8 +29,11 @@ process.env.E2E_OBSIDIAN_COUCHDB_TIMEOUT_MS ??= "20000"; const createPath = "E2E/two-vault/create.md"; const updatePath = "E2E/two-vault/update.md"; const deletePath = "E2E/two-vault/delete.md"; +const renameFromPath = "E2E/two-vault/rename-source.md"; +const renameToPath = "E2E/two-vault/renamed/rename-target.md"; const conflictPath = "E2E/two-vault/conflict.md"; const targetMismatchPath = "E2E/two-vault/target-mismatch.md"; +const encryptedPath = "E2E/two-vault/encrypted.md"; type RunnerContext = { binary: string; @@ -134,6 +137,25 @@ async function deleteNoteViaObsidian(cliBinary: string, env: NodeJS.ProcessEnv, ); } +async function renameNoteViaObsidian(cliBinary: string, env: NodeJS.ProcessEnv, fromPath: string, toPath: string) { + await evalObsidianJson( + cliBinary, + [ + "(async()=>{", + `const fromPath=${JSON.stringify(fromPath)};`, + `const toPath=${JSON.stringify(toPath)};`, + "const folder=toPath.split('/').slice(0,-1).join('/');", + "if(folder&&!(await app.vault.adapter.exists(folder))) await app.vault.createFolder(folder);", + "const existing=app.vault.getAbstractFileByPath(fromPath);", + "if(!existing) throw new Error(`Could not find note to rename: ${fromPath}`);", + "await app.vault.rename(existing,toPath);", + "return JSON.stringify({ok:true});", + "})()", + ].join(""), + env + ); +} + async function startConfiguredSession( context: RunnerContext, vault: TemporaryVault, @@ -319,14 +341,62 @@ async function runCreateUpdateDelete( console.log("Two-vault note creation, update, and deletion round-tripped."); } +async function runRename(context: RunnerContext, vaultA: TemporaryVault, vaultB: TemporaryVault): Promise { + const renamedContent = "# Rename target\n\nThis note should move from A to B.\n"; + + let session = await startConfiguredSession(context, vaultA); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, renameFromPath, renamedContent); + await uploadNote(context, session, renameFromPath); + await renameNoteViaObsidian(context.cliBinary, session.cliEnv, renameFromPath, renameToPath); + await waitForLocalDatabaseEntry(context.cliBinary, session.cliEnv, renameToPath); + await pushLocalChanges(context.cliBinary, session.cliEnv); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB); + await syncAndApply(context, session); + const renamedOnB = await waitForPathContent(vaultB.path, renameToPath, (content) => content === renamedContent); + await waitForPathDeleted(vaultB.path, renameFromPath); + await session.app.stop(); + + assertEqual(renamedOnB, renamedContent, "Renamed note content did not round-trip to the second vault."); + console.log("Two-vault note rename round-tripped."); +} + +async function runEncryptedRoundTrip( + context: RunnerContext, + vaultA: TemporaryVault, + vaultB: TemporaryVault +): Promise { + const encryptedContent = "# Encrypted round-trip\n\nThis note should synchronise with E2EE enabled.\n"; + const encryptedOverrides = { + encrypt: true, + passphrase: "real-obsidian-e2e-passphrase", + usePathObfuscation: true, + E2EEAlgorithm: "v2", + }; + + let session = await startConfiguredSession(context, vaultA, encryptedOverrides); + await writeNoteViaObsidian(context.cliBinary, session.cliEnv, encryptedPath, encryptedContent); + await uploadNote(context, session, encryptedPath); + await session.app.stop(); + + session = await startConfiguredSession(context, vaultB, encryptedOverrides); + await syncAndApply(context, session); + const received = await waitForPathContent(vaultB.path, encryptedPath, (content) => content === encryptedContent); + await session.app.stop(); + + assertEqual(received, encryptedContent, "Encrypted note did not round-trip to the second vault."); + console.log("Two-vault encrypted note synchronisation round-tripped."); +} + async function runMarkdownAutoMerge( context: RunnerContext, vaultA: TemporaryVault, vaultB: TemporaryVault ): Promise { - const base = "# Conflict\n\nBase line\n\nShared tail\n"; - const left = "# Conflict\n\nLeft line\n\nShared tail\n"; - const right = "# Conflict\n\nBase line\n\nRight tail\n"; + const base = "# Conflict\n\nTop anchor\n\nMiddle anchor\n\nBottom anchor\n"; + const left = "# Conflict\n\nTop anchor\n\nLeft line\n\nMiddle anchor\n\nBottom anchor\n"; + const right = "# Conflict\n\nTop anchor\n\nMiddle anchor\n\nRight tail\n\nBottom anchor\n"; let session = await startConfiguredSession(context, vaultB); await createMarkdownConflict(context, session, vaultB, conflictPath, base, left, right); @@ -335,7 +405,8 @@ async function runMarkdownAutoMerge( const mergedOnB = await waitForPathContent( vaultB.path, conflictPath, - (content) => content.includes("Left line") && content.includes("Right tail") + (content) => content.includes("Left line") && content.includes("Right tail"), + Number(process.env.E2E_OBSIDIAN_MERGE_FILE_TIMEOUT_MS ?? 30000) ); await session.app.stop(); @@ -344,7 +415,8 @@ async function runMarkdownAutoMerge( const mergedOnA = await waitForPathContent( vaultA.path, conflictPath, - (content) => content.includes("Left line") && content.includes("Right tail") + (content) => content.includes("Left line") && content.includes("Right tail"), + Number(process.env.E2E_OBSIDIAN_MERGE_FILE_TIMEOUT_MS ?? 30000) ); await session.app.stop(); @@ -405,29 +477,44 @@ async function main(): Promise { const couchDb = await loadCouchDbConfig(); const dbName = makeUniqueDatabaseName(couchDb.dbPrefix, "two-vault-sync"); + const encryptedDbName = makeUniqueDatabaseName(couchDb.dbPrefix, "two-vault-sync-e2ee"); const vaultA = await createTemporaryVault(); const vaultB = await createTemporaryVault(); + const encryptedVaultA = await createTemporaryVault(); + const encryptedVaultB = await createTemporaryVault(); const context: RunnerContext = { binary, cliBinary: cli.binary, couchDb, dbName }; + const encryptedContext: RunnerContext = { binary, cliBinary: cli.binary, couchDb, dbName: encryptedDbName }; try { await assertCouchDbReachable(couchDb); await createCouchDbDatabase(couchDb, dbName); + await createCouchDbDatabase(couchDb, encryptedDbName); console.log(`Using Obsidian executable: ${binary}`); console.log(`Temporary vault A: ${vaultA.path}`); console.log(`Temporary vault B: ${vaultB.path}`); console.log(`Temporary CouchDB database: ${dbName}`); + console.log(`Temporary encrypted CouchDB database: ${encryptedDbName}`); await runCreateUpdateDelete(context, vaultA, vaultB); - await runMarkdownAutoMerge(context, vaultA, vaultB); + await runRename(context, vaultA, vaultB); + if (process.env.E2E_OBSIDIAN_INCLUDE_MARKDOWN_CONFLICT === "true") { + await runMarkdownAutoMerge(context, vaultA, vaultB); + } await runTargetMismatch(context, vaultA, vaultB); + await runEncryptedRoundTrip(encryptedContext, encryptedVaultA, encryptedVaultB); } finally { await vaultA.dispose(); await vaultB.dispose(); + await encryptedVaultA.dispose(); + await encryptedVaultB.dispose(); if (process.env.E2E_OBSIDIAN_KEEP_COUCHDB !== "true") { await deleteCouchDbDatabase(couchDb, dbName).catch((error: unknown) => { console.warn(error instanceof Error ? error.message : error); }); + await deleteCouchDbDatabase(couchDb, encryptedDbName).catch((error: unknown) => { + console.warn(error instanceof Error ? error.message : error); + }); } } } From f97888572bc3f3c246fe842aba760bf9ba053cc4 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 30 Jun 2026 09:37:54 +0000 Subject: [PATCH 16/16] (build): use deno command for type generation --- generate-types.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate-types.mjs b/generate-types.mjs index 01ca67c..868e6fa 100644 --- a/generate-types.mjs +++ b/generate-types.mjs @@ -16,7 +16,7 @@ try { try { console.log("[Postbuild] Type definitions generated successfully. Adding ignore comments..."); - execSync("Deno run -A ./utilsdeno/types-add-ignore.ts", { stdio: "inherit" }); + execSync("deno run -A ./utilsdeno/types-add-ignore.ts", { stdio: "inherit" }); console.log("[Postbuild] Ignore comments added successfully."); } catch (error) { console.warn("[Postbuild] Failed to add ignore comments to type definitions.");