From 45fe0b36826d2cf24bdc08a813673484a6c30d4f Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 26 May 2026 11:24:41 +0100 Subject: [PATCH 01/30] chore: lint: enable cache and concurrency --- .gitignore | 3 ++- eslint.config.mjs | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a6bced9..c443c8e 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ cov_profile/** coverage src/apps/cli/dist/* _testdata/** -utils/bench/splitResults.csv \ No newline at end of file +utils/bench/splitResults.csv +.eslintcache \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 35b0d25..8109b89 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -43,7 +43,7 @@ export default defineConfig([ { files: ["**/*.ts"], languageOptions: { - globals: { ...globals.browser }, + globals: { ...globals.browser, "PouchDB": "readonly" }, parser: tsParser, parserOptions: { project: "./tsconfig.json", @@ -79,5 +79,5 @@ export default defineConfig([ "no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], "obsidianmd/no-plugin-as-component": "off", // Temporary }, - }, + } ]); diff --git a/package.json b/package.json index 2311f08..f74d849 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "buildVite": "npx dotenv-cli -e .env -- vite build --mode production", "buildViteOriginal": "npx dotenv-cli -e .env -- vite build --mode original", "buildDev": "node esbuild.config.mjs dev", - "lint": "eslint src", + "lint": "eslint --cache --concurrency auto src", "svelte-check": "svelte-check --tsconfig ./tsconfig.json", "tsc-check": "tsc --noEmit", "pretty": "npm run prettyNoWrite -- --write --log-level error", From 8841ef4619adfe328609f48cf5669602c4cc1275 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 26 May 2026 11:27:31 +0100 Subject: [PATCH 02/30] fix css duplicated props --- styles.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/styles.css b/styles.css index 09a7f79..296e515 100644 --- a/styles.css +++ b/styles.css @@ -171,7 +171,7 @@ body { .sls-setting-menu-buttons { border: 1px solid var(--sls-col-warn); - padding: 2px; + /* padding: 2px; */ margin: 1px; border-radius: 4px; background-image: linear-gradient(-45deg, @@ -180,7 +180,7 @@ body { background-size: 30px 30px; display: flex; flex-direction: row; - justify-content: flex-end; + /* justify-content: flex-end; */ padding: 0.5em 0.25em; justify-content: center; align-items: center; @@ -315,9 +315,9 @@ body { .sls-setting-obsolete { - background-image: linear-gradient(-45deg, + /* background-image: linear-gradient(-45deg, var(--sls-col-warn-stripe1) 25%, var(--sls-col-warn-stripe2) 25%, var(--sls-col-warn-stripe2) 50%, - var(--sls-col-warn-stripe1) 50%, var(--sls-col-warn-stripe1) 75%, var(--sls-col-warn-stripe2) 75%, var(--sls-col-warn-stripe2)); + var(--sls-col-warn-stripe1) 50%, var(--sls-col-warn-stripe1) 75%, var(--sls-col-warn-stripe2) 75%, var(--sls-col-warn-stripe2)); */ background-image: linear-gradient(-45deg, transparent 25%, rgba(var(--background-secondary), 0.1) 25%, rgba(var(--background-secondary), 0.1) 50%, transparent 50%, transparent 75%, rgba(var(--background-secondary), 0.1) 75%, rgba(var(--background-secondary), 0.1)); background-size: 60px 60px; From 1130bbcee89d92b9ad2a305ce1e4ad470cbe4fea Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Tue, 26 May 2026 11:33:14 +0100 Subject: [PATCH 03/30] remove unmaintained tests --- devs.md | 2 +- src/main.ts | 4 - src/modules/extras/ModuleIntegratedTest.ts | 446 ---------------- src/modules/extras/ModuleReplicateTest.ts | 590 --------------------- 4 files changed, 1 insertion(+), 1041 deletions(-) delete mode 100644 src/modules/extras/ModuleIntegratedTest.ts delete mode 100644 src/modules/extras/ModuleReplicateTest.ts diff --git a/devs.md b/devs.md index d9d9ef1..816c2be 100644 --- a/devs.md +++ b/devs.md @@ -17,7 +17,7 @@ The plugin uses a dynamic module system to reduce coupling and improve maintaina - `coreObsidian/` - Obsidian-specific core (e.g., `ModuleFileAccessObsidian`) - `essential/` - Required modules (e.g., `ModuleMigration`, `ModuleKeyValueDB`) - `features/` - Optional features (e.g., `ModuleLog`, `ModuleObsidianSettings`) - - `extras/` - Development/testing tools (e.g., `ModuleDev`, `ModuleIntegratedTest`) + - `extras/` - Development/testing tools (e.g., `ModuleDev`, ~~`ModuleIntegratedTest`~~) - **Services**: Core services (e.g., `database`, `replicator`, `storageAccess`) are registered in `ServiceHub` and accessed by modules. They provide an extension point for add new behaviour without modifying existing code. - For example, checks before the replication can be added to the `replication.onBeforeReplicate` handler, and the handlers can be return `false` to prevent replication-starting. `vault.isTargetFile` also can be used to prevent processing specific files. - **ServiceModule**: A new type of module that directly depends on services. diff --git a/src/main.ts b/src/main.ts index 6c5c3f3..3de86ce 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,8 +12,6 @@ import { ModuleObsidianEvents } from "./modules/essentialObsidian/ModuleObsidian import { ModuleObsidianSettingDialogue } from "./modules/features/ModuleObsidianSettingTab.ts"; import { ModuleObsidianDocumentHistory } from "./modules/features/ModuleObsidianDocumentHistory.ts"; import { ModuleObsidianGlobalHistory } from "./modules/features/ModuleGlobalHistory.ts"; -import { ModuleIntegratedTest } from "./modules/extras/ModuleIntegratedTest.ts"; -import { ModuleReplicateTest } from "./modules/extras/ModuleReplicateTest.ts"; import { LocalDatabaseMaintenance } from "./features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts"; import type { InjectableServiceHub } from "@lib/services/implements/injectable/InjectableServiceHub.ts"; import { ObsidianServiceHub } from "./modules/services/ObsidianServiceHub.ts"; @@ -156,8 +154,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin { new ModuleInteractiveConflictResolver(this, core), new ModuleObsidianGlobalHistory(this, core), new ModuleDev(this, core), - new ModuleReplicateTest(this, core), - new ModuleIntegratedTest(this, core), new SetupManager(core), // this should be moved to core? new ModuleMigration(core), ]; diff --git a/src/modules/extras/ModuleIntegratedTest.ts b/src/modules/extras/ModuleIntegratedTest.ts deleted file mode 100644 index df5cc3a..0000000 --- a/src/modules/extras/ModuleIntegratedTest.ts +++ /dev/null @@ -1,446 +0,0 @@ -import { delay } from "octagonal-wheels/promises"; -import { LOG_LEVEL_NOTICE, REMOTE_MINIO, type FilePathWithPrefix } from "src/lib/src/common/types"; -import { shareRunningResult } from "octagonal-wheels/concurrency/lock"; -import { AbstractObsidianModule } from "../AbstractObsidianModule"; - -export class ModuleIntegratedTest extends AbstractObsidianModule { - async waitFor(proc: () => Promise, timeout = 10000): Promise { - await delay(100); - const start = Date.now(); - while (!(await proc())) { - if (timeout > 0) { - if (Date.now() - start > timeout) { - this._log(`Timeout`); - return false; - } - } - await delay(500); - } - return true; - } - waitWithReplicating(proc: () => Promise, timeout = 10000): Promise { - return this.waitFor(async () => { - await this.tryReplicate(); - return await proc(); - }, timeout); - } - async storageContentIsEqual(file: string, content: string): Promise { - try { - const fileContent = await this.readStorageContent(file as FilePathWithPrefix); - if (fileContent === content) { - return true; - } else { - // this._log(`Content is not same \n Expected:${content}\n Actual:${fileContent}`, LOG_LEVEL_VERBOSE); - return false; - } - } catch (e) { - this._log(`Error: ${e}`); - return false; - } - } - async assert(proc: () => Promise): Promise { - if (!(await proc())) { - this._log(`Assertion failed`); - return false; - } - return true; - } - async __orDie(key: string, proc: () => Promise): Promise | never { - if (!(await this._test(key, proc))) { - throw new Error(`${key}`); - } - return true; - } - tryReplicate() { - if (!this.settings.liveSync) { - return shareRunningResult("replicate-test", async () => { - await this.services.replication.replicate(); - }); - } - } - async readStorageContent(file: FilePathWithPrefix): Promise { - if (!(await this.core.storageAccess.isExistsIncludeHidden(file))) { - return undefined; - } - return await this.core.storageAccess.readHiddenFileText(file); - } - async __proceed(no: number, title: string): Promise { - const stepFile = "_STEP.md" as FilePathWithPrefix; - const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix; - const stepContent = `Step ${no}`; - await this.services.conflict.resolveByNewest(stepFile); - await this.core.storageAccess.writeFileAuto(stepFile, stepContent); - await this.__orDie(`Wait for acknowledge ${no}`, async () => { - if ( - !(await this.waitWithReplicating(async () => { - return await this.storageContentIsEqual(stepAckFile, stepContent); - }, 20000)) - ) - return false; - return true; - }); - return true; - } - async __join(no: number, title: string): Promise { - const stepFile = "_STEP.md" as FilePathWithPrefix; - const stepAckFile = "_STEP_ACK.md" as FilePathWithPrefix; - // const otherStepFile = `_STEP_${isLeader ? "R" : "L"}.md` as FilePathWithPrefix; - const stepContent = `Step ${no}`; - - await this.__orDie(`Wait for step ${no} (${title})`, async () => { - if ( - !(await this.waitWithReplicating(async () => { - return await this.storageContentIsEqual(stepFile, stepContent); - }, 20000)) - ) - return false; - return true; - }); - await this.services.conflict.resolveByNewest(stepAckFile); - await this.core.storageAccess.writeFileAuto(stepAckFile, stepContent); - await this.tryReplicate(); - return true; - } - - async performStep({ - step, - title, - isGameChanger, - proc, - check, - }: { - step: number; - title: string; - isGameChanger: boolean; - proc: () => Promise; - check: () => Promise; - }): Promise { - if (isGameChanger) { - await this.__proceed(step, title); - try { - await proc(); - } catch (e) { - this._log(`Error: ${e}`); - return false; - } - return await this.__orDie(`Step ${step} - ${title}`, async () => await this.waitWithReplicating(check)); - } else { - return await this.__join(step, title); - } - } - // // see scenario.md - // async testLeader(testMain: (testFileName: FilePathWithPrefix) => Promise): Promise { - - // } - // async testReceiver(testMain: (testFileName: FilePathWithPrefix) => Promise): Promise { - - // } - async nonLiveTestRunner( - isLeader: boolean, - testMain: (testFileName: FilePathWithPrefix, isLeader: boolean) => Promise - ): Promise { - const storage = this.core.storageAccess; - // const database = this.core.databaseFileAccess; - // const _orDie = this._orDie.bind(this); - const testCommandFile = "IT.md" as FilePathWithPrefix; - const textCommandResponseFile = "ITx.md" as FilePathWithPrefix; - let testFileName: FilePathWithPrefix; - this.addTestResult( - "-------Starting ... ", - true, - `Test as ${isLeader ? "Leader" : "Receiver"} command file ${testCommandFile}` - ); - if (isLeader) { - await this.__proceed(0, "start"); - } - await this.tryReplicate(); - - await this.performStep({ - step: 0, - title: "Make sure that command File Not Exists", - isGameChanger: isLeader, - proc: async () => await storage.removeHidden(testCommandFile), - check: async () => !(await storage.isExistsIncludeHidden(testCommandFile)), - }); - await this.performStep({ - step: 1, - title: "Make sure that command File Not Exists On Receiver", - isGameChanger: !isLeader, - proc: async () => await storage.removeHidden(textCommandResponseFile), - check: async () => !(await storage.isExistsIncludeHidden(textCommandResponseFile)), - }); - - await this.performStep({ - step: 2, - title: "Decide the test file name", - isGameChanger: isLeader, - proc: async () => { - testFileName = (Date.now() + "-" + Math.ceil(Math.random() * 1000) + ".md") as FilePathWithPrefix; - const testCommandFile = "IT.md" as FilePathWithPrefix; - await storage.writeFileAuto(testCommandFile, testFileName); - }, - check: () => Promise.resolve(true), - }); - await this.performStep({ - step: 3, - title: "Wait for the command file to be arrived", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => await storage.isExistsIncludeHidden(testCommandFile), - }); - - await this.performStep({ - step: 4, - title: "Send the response file", - isGameChanger: !isLeader, - proc: async () => { - await storage.writeHiddenFileAuto(textCommandResponseFile, "!"); - }, - check: () => Promise.resolve(true), - }); - await this.performStep({ - step: 5, - title: "Wait for the response file to be arrived", - isGameChanger: isLeader, - proc: async () => {}, - check: async () => await storage.isExistsIncludeHidden(textCommandResponseFile), - }); - - await this.performStep({ - step: 6, - title: "Proceed to begin the test", - isGameChanger: isLeader, - proc: async () => {}, - check: () => Promise.resolve(true), - }); - await this.performStep({ - step: 6, - title: "Begin the test", - isGameChanger: !false, - proc: async () => {}, - check: () => { - return Promise.resolve(true); - }, - }); - // await this.step(0, isLeader, true); - try { - this.addTestResult("** Main------", true, ``); - if (isLeader) { - return await testMain(testFileName!, true); - } else { - const testFileName = await this.readStorageContent(testCommandFile); - this.addTestResult("testFileName", true, `Request client to use :${testFileName!}`); - return await testMain(testFileName! as FilePathWithPrefix, false); - } - } finally { - this.addTestResult("Teardown", true, `Deleting ${testFileName!}`); - await storage.removeHidden(testFileName!); - } - - return true; - // Make sure the - } - - async testBasic(filename: FilePathWithPrefix, isLeader: boolean): Promise { - const storage = this.core.storageAccess; - const database = this.core.databaseFileAccess; - - await this.addTestResult( - `---**Starting Basic Test**---`, - true, - `Test as ${isLeader ? "Leader" : "Receiver"} command file ${filename}` - ); - // if (isLeader) { - // await this._proceed(0); - // } - // await this.tryReplicate(); - - await this.performStep({ - step: 0, - title: "Make sure that file is not exist", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => !(await storage.isExists(filename)), - }); - - await this.performStep({ - step: 1, - title: "Write a file", - isGameChanger: isLeader, - proc: async () => await storage.writeFileAuto(filename, "Hello World"), - check: async () => await storage.isExists(filename), - }); - await this.performStep({ - step: 2, - title: "Make sure the file is arrived", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => await storage.isExists(filename), - }); - await this.performStep({ - step: 3, - title: "Update to Hello World 2", - isGameChanger: isLeader, - proc: async () => await storage.writeFileAuto(filename, "Hello World 2"), - check: async () => await this.storageContentIsEqual(filename, "Hello World 2"), - }); - await this.performStep({ - step: 4, - title: "Make sure the modified file is arrived", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => await this.storageContentIsEqual(filename, "Hello World 2"), - }); - await this.performStep({ - step: 5, - title: "Update to Hello World 3", - isGameChanger: !isLeader, - proc: async () => await storage.writeFileAuto(filename, "Hello World 3"), - check: async () => await this.storageContentIsEqual(filename, "Hello World 3"), - }); - await this.performStep({ - step: 6, - title: "Make sure the modified file is arrived", - isGameChanger: isLeader, - proc: async () => {}, - check: async () => await this.storageContentIsEqual(filename, "Hello World 3"), - }); - - const multiLineContent = `Line1:A -Line2:B -Line3:C -Line4:D`; - - await this.performStep({ - step: 7, - title: "Update to Multiline", - isGameChanger: isLeader, - proc: async () => await storage.writeFileAuto(filename, multiLineContent), - check: async () => await this.storageContentIsEqual(filename, multiLineContent), - }); - - await this.performStep({ - step: 8, - title: "Make sure the modified file is arrived", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => await this.storageContentIsEqual(filename, multiLineContent), - }); - - // While LiveSync, possibly cannot cause the conflict. - if (!this.settings.liveSync) { - // Step 9 Make Conflict But Resolvable - const multiLineContentL = `Line1:A -Line2:B -Line3:C! -Line4:D`; - const multiLineContentC = `Line1:A -Line2:bbbbb -Line3:C -Line4:D`; - - await this.performStep({ - step: 9, - title: "Progress to be conflicted", - isGameChanger: isLeader, - proc: async () => {}, - check: () => Promise.resolve(true), - }); - - await storage.writeFileAuto(filename, isLeader ? multiLineContentL : multiLineContentC); - - await this.performStep({ - step: 10, - title: "Update As Conflicted", - isGameChanger: !isLeader, - proc: async () => {}, - check: () => Promise.resolve(true), - }); - - await this.performStep({ - step: 10, - title: "Make sure Automatically resolved", - isGameChanger: isLeader, - proc: async () => {}, - check: async () => (await database.getConflictedRevs(filename)).length === 0, - }); - await this.performStep({ - step: 11, - title: "Make sure Automatically resolved", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => (await database.getConflictedRevs(filename)).length === 0, - }); - - const sensiblyMergedContent = `Line1:A -Line2:bbbbb -Line3:C! -Line4:D`; - - await this.performStep({ - step: 12, - title: "Make sure Sensibly Merged on Leader", - isGameChanger: isLeader, - proc: async () => {}, - check: async () => await this.storageContentIsEqual(filename, sensiblyMergedContent), - }); - await this.performStep({ - step: 13, - title: "Make sure Sensibly Merged on Receiver", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => await this.storageContentIsEqual(filename, sensiblyMergedContent), - }); - } - await this.performStep({ - step: 14, - title: "Delete File", - isGameChanger: isLeader, - proc: async () => { - await storage.removeHidden(filename); - }, - check: async () => !(await storage.isExists(filename)), - }); - - await this.performStep({ - step: 15, - title: "Make sure File is deleted", - isGameChanger: !isLeader, - proc: async () => {}, - check: async () => !(await storage.isExists(filename)), - }); - this._log(`The Basic Test has been completed`, LOG_LEVEL_NOTICE); - return true; - } - - async testBasicEvent(isLeader: boolean) { - this.settings.liveSync = false; - await this.saveSettings(); - await this._test("basic", async () => await this.nonLiveTestRunner(isLeader, (t, l) => this.testBasic(t, l))); - } - async testBasicLive(isLeader: boolean) { - this.settings.liveSync = true; - await this.saveSettings(); - await this._test("basic", async () => await this.nonLiveTestRunner(isLeader, (t, l) => this.testBasic(t, l))); - } - - async _everyModuleTestMultiDevice(): Promise { - if (!this.settings.enableDebugTools) return Promise.resolve(true); - const isLeader = this.core.services.vault.vaultName().indexOf("recv") === -1; - this.addTestResult("-------", true, `Test as ${isLeader ? "Leader" : "Receiver"}`); - try { - this._log(`Starting Test`); - await this.testBasicEvent(isLeader); - if (this.settings.remoteType == REMOTE_MINIO) await this.testBasicLive(isLeader); - } catch (e) { - this._log(e); - this._log(`Error: ${e}`); - return Promise.resolve(false); - } - - return Promise.resolve(true); - } - override onBindFunction(core: typeof this.core, services: typeof core.services): void { - services.test.testMultiDevice.addHandler(this._everyModuleTestMultiDevice.bind(this)); - } -} diff --git a/src/modules/extras/ModuleReplicateTest.ts b/src/modules/extras/ModuleReplicateTest.ts deleted file mode 100644 index 394042a..0000000 --- a/src/modules/extras/ModuleReplicateTest.ts +++ /dev/null @@ -1,590 +0,0 @@ -// I intend to discontinue maintenance of this class. It seems preferable to test it externally. -import { delay } from "octagonal-wheels/promises"; -import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; -import { LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; -import { eventHub } from "../../common/events"; -import { getWebCrypto } from "../../lib/src/mods.ts"; -import { uint8ArrayToHexString } from "octagonal-wheels/binary/hex"; -import { parseYaml, requestUrl, stringifyYaml } from "@/deps.ts"; -import type { FilePath } from "../../lib/src/common/types.ts"; -import { scheduleTask } from "octagonal-wheels/concurrency/task"; -import { getFileRegExp } from "../../lib/src/common/utils.ts"; -import type { LiveSyncCore } from "../../main.ts"; - -declare global { - interface LSEvents { - "debug-sync-status": string[]; - } -} - -export class ModuleReplicateTest extends AbstractObsidianModule { - testRootPath = "_test/"; - testInfoPath = "_testinfo/"; - - get isLeader() { - return ( - this.services.vault.getVaultName().indexOf("dev") >= 0 && - this.services.vault.vaultName().indexOf("recv") < 0 - ); - } - - get nameByKind() { - if (!this.isLeader) { - return "RECV"; - } else if (this.isLeader) { - return "LEADER"; - } - } - get pairName() { - if (this.isLeader) { - return "RECV"; - } else if (!this.isLeader) { - return "LEADER"; - } - } - - watchIsSynchronised = false; - - statusBarSyncStatus?: HTMLElement; - async readFileContent(file: string) { - try { - return await this.core.storageAccess.readHiddenFileText(file); - } catch { - return ""; - } - } - - async dumpList() { - if (this.settings.syncInternalFiles) { - this._log("Write file list (Include Hidden)"); - await this.__dumpFileListIncludeHidden("files.md"); - } else { - this._log("Write file list"); - await this.__dumpFileList("files.md"); - } - } - async _everyBeforeReplicate(showMessage: boolean): Promise { - if (!this.settings.enableDebugTools) return Promise.resolve(true); - await this.dumpList(); - return true; - } - private _everyOnloadAfterLoadSettings(): Promise { - if (!this.settings.enableDebugTools) return Promise.resolve(true); - this.addCommand({ - id: "dump-file-structure-normal", - name: `Dump Structure (Normal)`, - callback: () => { - void this.__dumpFileList("files.md").finally(() => { - void this.refreshSyncStatus(); - }); - }, - }); - this.addCommand({ - id: "dump-file-structure-ih", - name: "Dump Structure (Include Hidden)", - callback: () => { - const d = "files.md"; - void this.__dumpFileListIncludeHidden(d); - }, - }); - this.addCommand({ - id: "dump-file-structure-auto", - name: "Dump Structure", - callback: () => { - void this.dumpList(); - }, - }); - this.addCommand({ - id: "dump-file-test", - name: `Perform Test (Dev) ${this.isLeader ? "(Leader)" : "(Recv)"}`, - callback: () => { - void this.performTestManually(); - }, - }); - this.addCommand({ - id: "watch-sync-result", - name: `Watch sync result is matched between devices`, - callback: () => { - this.watchIsSynchronised = !this.watchIsSynchronised; - void this.refreshSyncStatus(); - }, - }); - this.app.vault.on("modify", async (file) => { - if (file.path.startsWith(this.testInfoPath)) { - await this.refreshSyncStatus(); - } else { - scheduleTask("dumpStatus", 125, async () => { - await this.dumpList(); - return true; - }); - } - }); - this.statusBarSyncStatus = this.plugin.addStatusBarItem(); - return Promise.resolve(true); - } - async getSyncStatusAsText() { - const fileMine = this.testInfoPath + this.nameByKind + "/" + "files.md"; - const filePair = this.testInfoPath + this.pairName + "/" + "files.md"; - const mine = parseYaml(await this.readFileContent(fileMine)); - const pair = parseYaml(await this.readFileContent(filePair)); - const result = [] as string[]; - if (mine.length != pair.length) { - result.push(`File count is different: ${mine.length} vs ${pair.length}`); - } - const filesAll = new Set([...mine.map((e: any) => e.path), ...pair.map((e: any) => e.path)]); - for (const file of filesAll) { - const mineFile = mine.find((e: any) => e.path == file); - const pairFile = pair.find((e: any) => e.path == file); - if (!mineFile || !pairFile) { - result.push(`File not found: ${file}`); - } else { - if (mineFile.size != pairFile.size) { - result.push(`Size is different: ${file} ${mineFile.size} vs ${pairFile.size}`); - } - if (mineFile.hash != pairFile.hash) { - result.push(`Hash is different: ${file} ${mineFile.hash} vs ${pairFile.hash}`); - } - } - } - eventHub.emitEvent("debug-sync-status", result); - return result.join("\n"); - } - - async refreshSyncStatus() { - if (this.watchIsSynchronised) { - // Normal Files - const syncStatus = await this.getSyncStatusAsText(); - if (syncStatus) { - this.statusBarSyncStatus!.setText(`Sync Status: Having Error`); - this._log(`Sync Status: Having Error\n${syncStatus}`, LOG_LEVEL_INFO); - } else { - this.statusBarSyncStatus!.setText(`Sync Status: Synchronised`); - } - } else { - this.statusBarSyncStatus!.setText(""); - } - } - - async __dumpFileList(outFile?: string) { - if (!this.core || !this.core.storageAccess) { - this._log("No storage access", LOG_LEVEL_INFO); - return; - } - const files = await this.core.storageAccess.getFiles(); - const out = [] as any[]; - const webcrypto = await getWebCrypto(); - for (const file of files) { - if (!(await this.services.vault.isTargetFile(file.path))) { - continue; - } - if (file.path.startsWith(this.testInfoPath)) continue; - const stat = await this.core.storageAccess.stat(file.path); - if (stat) { - const hashSrc = await this.core.storageAccess.readHiddenFileBinary(file.path); - const hash = await webcrypto.subtle.digest("SHA-1", hashSrc); - const hashStr = uint8ArrayToHexString(new Uint8Array(hash)); - const item = { - path: file.path, - name: file.name, - size: stat.size, - mtime: stat.mtime, - hash: hashStr, - }; - // const fileLine = `-${file.path}:${stat.size}:${stat.mtime}:${hashStr}`; - out.push(item); - } - } - out.sort((a, b) => a.path.localeCompare(b.path)); - if (outFile) { - outFile = this.testInfoPath + this.nameByKind + "/" + outFile; - await this.core.storageAccess.ensureDir(outFile); - await this.core.storageAccess.writeHiddenFileAuto(outFile, stringifyYaml(out)); - } else { - // console.dir(out); - } - this._log(`Dumped ${out.length} files`, LOG_LEVEL_INFO); - } - - async __dumpFileListIncludeHidden(outFile?: string) { - const ignorePatterns = getFileRegExp(this.core.settings, "syncInternalFilesIgnorePatterns"); - const targetPatterns = getFileRegExp(this.core.settings, "syncInternalFilesTargetPatterns"); - const out = [] as any[]; - const files = await this.core.storageAccess.getFilesIncludeHidden("", targetPatterns, ignorePatterns); - // console.dir(files); - const webcrypto = await getWebCrypto(); - for (const file of files) { - // if (!await this.core.$$isTargetFile(file)) { - // continue; - // } - if (file.startsWith(this.testInfoPath)) continue; - const stat = await this.core.storageAccess.statHidden(file); - if (stat) { - const hashSrc = await this.core.storageAccess.readHiddenFileBinary(file); - const hash = await webcrypto.subtle.digest("SHA-1", hashSrc); - const hashStr = uint8ArrayToHexString(new Uint8Array(hash)); - const item = { - path: file, - name: file.split("/").pop(), - size: stat.size, - mtime: stat.mtime, - hash: hashStr, - }; - // const fileLine = `-${file.path}:${stat.size}:${stat.mtime}:${hashStr}`; - out.push(item); - } - } - out.sort((a, b) => a.path.localeCompare(b.path)); - if (outFile) { - outFile = this.testInfoPath + this.nameByKind + "/" + outFile; - await this.core.storageAccess.ensureDir(outFile); - await this.core.storageAccess.writeHiddenFileAuto(outFile, stringifyYaml(out)); - } else { - // console.dir(out); - } - this._log(`Dumped ${out.length} files`, LOG_LEVEL_NOTICE); - } - - async collectTestFiles() { - const remoteTopDir = "https://raw.githubusercontent.com/vrtmrz/obsidian-livesync/refs/heads/main/"; - const files = [ - "README.md", - "docs/adding_translations.md", - "docs/design_docs_of_journalsync.md", - "docs/design_docs_of_keep_newborn_chunks.md", - "docs/design_docs_of_prefixed_hidden_file_sync.md", - "docs/design_docs_of_sharing_tweak_value.md", - "docs/quick_setup_cn.md", - "docs/quick_setup_ja.md", - "docs/quick_setup.md", - "docs/settings_ja.md", - "docs/settings.md", - "docs/setup_cloudant_ja.md", - "docs/setup_cloudant.md", - "docs/setup_flyio.md", - "docs/setup_own_server_cn.md", - "docs/setup_own_server_ja.md", - "docs/setup_own_server.md", - "docs/tech_info_ja.md", - "docs/tech_info.md", - "docs/terms.md", - "docs/troubleshooting.md", - "images/1.png", - "images/2.png", - "images/corrupted_data.png", - "images/hatch.png", - "images/lock_pattern1.png", - "images/lock_pattern2.png", - "images/quick_setup_1.png", - "images/quick_setup_2.png", - "images/quick_setup_3.png", - "images/quick_setup_3b.png", - "images/quick_setup_4.png", - "images/quick_setup_5.png", - "images/quick_setup_6.png", - "images/quick_setup_7.png", - "images/quick_setup_8.png", - "images/quick_setup_9_1.png", - "images/quick_setup_9_2.png", - "images/quick_setup_10.png", - "images/remote_db_setting.png", - "images/write_logs_into_the_file.png", - ]; - for (const file of files) { - const remote = remoteTopDir + file; - const local = this.testRootPath + file; - try { - const f = (await requestUrl(remote)).arrayBuffer; - await this.core.storageAccess.ensureDir(local); - await this.core.storageAccess.writeHiddenFileAuto(local, f); - } catch (ex) { - this._log(`Could not fetch ${remote}`, LOG_LEVEL_VERBOSE); - this._log(ex, LOG_LEVEL_VERBOSE); - } - } - - await this.dumpList(); - } - - async waitFor(proc: () => Promise, timeout = 10000): Promise { - await delay(100); - const start = Date.now(); - while (!(await proc())) { - if (timeout > 0) { - if (Date.now() - start > timeout) { - this._log(`Timeout`); - return false; - } - } - await delay(500); - } - return true; - } - - async testConflictedManually1() { - await this.services.replication.replicate(); - - const commonFile = `Resolve! -*****, the amazing chocolatier!!`; - - if (this.isLeader) { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "wonka.md", commonFile); - } - - await this.services.replication.replicate(); - await this.services.replication.replicate(); - if ( - (await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 1?", { - timeout: 30, - defaultOption: "Yes", - })) == "no" - ) { - return; - } - - const fileA = `Resolve to KEEP THIS -Willy Wonka, Willy Wonka, the amazing chocolatier!!`; - - const fileB = `Resolve to DISCARD THIS -Charlie Bucket, Charlie Bucket, the amazing chocolatier!!`; - - if (this.isLeader) { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "wonka.md", fileA); - } else { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "wonka.md", fileB); - } - - if ( - (await this.core.confirm.askYesNoDialog("Ready to check the result of Manually 1?", { - timeout: 30, - defaultOption: "Yes", - })) == "no" - ) { - return; - } - await this.services.replication.replicate(); - await this.services.replication.replicate(); - - if ( - !(await this.waitFor(async () => { - await this.services.replication.replicate(); - return ( - (await this.__assertStorageContent( - (this.testRootPath + "wonka.md") as FilePath, - fileA, - false, - true - )) == true - ); - }, 30000)) - ) { - return await this.__assertStorageContent((this.testRootPath + "wonka.md") as FilePath, fileA, false, true); - } - return true; - // We have to check the result - } - - async testConflictedManually2() { - await this.services.replication.replicate(); - - const commonFile = `Resolve To concatenate -ABCDEFG`; - - if (this.isLeader) { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "concat.md", commonFile); - } - - await this.services.replication.replicate(); - await this.services.replication.replicate(); - if ( - (await this.core.confirm.askYesNoDialog("Ready to begin the test conflict Manually 2?", { - timeout: 30, - defaultOption: "Yes", - })) == "no" - ) { - return; - } - - const fileA = `Resolve to Concatenate -ABCDEFGHIJKLMNOPQRSTYZ`; - - const fileB = `Resolve to Concatenate -AJKLMNOPQRSTUVWXYZ`; - - const concatenated = `Resolve to Concatenate -ABCDEFGHIJKLMNOPQRSTUVWXYZ`; - if (this.isLeader) { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "concat.md", fileA); - } else { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "concat.md", fileB); - } - if ( - (await this.core.confirm.askYesNoDialog("Ready to test conflict Manually 2?", { - timeout: 30, - defaultOption: "Yes", - })) == "no" - ) { - return; - } - await this.services.replication.replicate(); - await this.services.replication.replicate(); - - if ( - !(await this.waitFor(async () => { - await this.services.replication.replicate(); - return ( - (await this.__assertStorageContent( - (this.testRootPath + "concat.md") as FilePath, - concatenated, - false, - true - )) == true - ); - }, 30000)) - ) { - return await this.__assertStorageContent( - (this.testRootPath + "concat.md") as FilePath, - concatenated, - false, - true - ); - } - return true; - } - - async testConflictAutomatic() { - if (this.isLeader) { - const baseDoc = `Tasks! -- [ ] Task 1 -- [ ] Task 2 -- [ ] Task 3 -- [ ] Task 4 -`; - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", baseDoc); - } - await delay(100); - await this.services.replication.replicate(); - await this.services.replication.replicate(); - - if ( - (await this.core.confirm.askYesNoDialog("Ready to test conflict?", { - timeout: 30, - defaultOption: "Yes", - })) == "no" - ) { - return; - } - const mod1Doc = `Tasks! -- [ ] Task 1 -- [v] Task 2 -- [ ] Task 3 -- [ ] Task 4 -`; - - const mod2Doc = `Tasks! -- [ ] Task 1 -- [ ] Task 2 -- [v] Task 3 -- [ ] Task 4 -`; - if (this.isLeader) { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", mod1Doc); - } else { - await this.core.storageAccess.writeHiddenFileAuto(this.testRootPath + "task.md", mod2Doc); - } - - await this.services.replication.replicate(); - await this.services.replication.replicate(); - await delay(1000); - if ( - (await this.core.confirm.askYesNoDialog("Ready to check result?", { timeout: 30, defaultOption: "Yes" })) == - "no" - ) { - return; - } - await this.services.replication.replicate(); - await this.services.replication.replicate(); - const mergedDoc = `Tasks! -- [ ] Task 1 -- [v] Task 2 -- [v] Task 3 -- [ ] Task 4 -`; - return this.__assertStorageContent((this.testRootPath + "task.md") as FilePath, mergedDoc, false, true); - } - - // No longer tested - async checkConflictResolution() { - this._log("Before testing conflicted files, resolve all once", LOG_LEVEL_NOTICE); - await this.services.conflict.resolveAllConflictedFilesByNewerOnes(); - await this.services.conflict.resolveAllConflictedFilesByNewerOnes(); - await this.services.replication.replicate(); - await delay(1000); - if (!(await this.testConflictAutomatic())) { - this._log("Conflict resolution (Auto) failed", LOG_LEVEL_NOTICE); - return false; - } - if (!(await this.testConflictedManually1())) { - this._log("Conflict resolution (Manual1) failed", LOG_LEVEL_NOTICE); - return false; - } - if (!(await this.testConflictedManually2())) { - this._log("Conflict resolution (Manual2) failed", LOG_LEVEL_NOTICE); - return false; - } - return true; - } - - async __assertStorageContent( - fileName: FilePath, - content: string, - inverted = false, - showResult = false - ): Promise { - try { - const fileContent = await this.core.storageAccess.readHiddenFileText(fileName); - let result = fileContent === content; - if (inverted) { - result = !result; - } - if (result) { - return true; - } else { - if (showResult) { - this._log(`Content is not same \n Expected:${content}\n Actual:${fileContent}`, LOG_LEVEL_VERBOSE); - } - return `Content is not same \n Expected:${content}\n Actual:${fileContent}`; - } - } catch (e) { - this._log(`Cannot assert storage content: ${e}`); - return false; - } - } - async performTestManually() { - if (!this.settings.enableDebugTools) return Promise.resolve(true); - await this.checkConflictResolution(); - // await this.collectTestFiles(); - } - - // testResults = writable<[boolean, string, string][]>([]); - // testResults: string[] = []; - - // $$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { - // const logLine = `${name}: ${key} ${summary ?? ""}`; - // this.testResults.update((results) => { - // results.push([result, logLine, message ?? ""]); - // return results; - // }); - // } - private async _everyModuleTestMultiDevice(): Promise { - if (!this.settings.enableDebugTools) return Promise.resolve(true); - // this.core.$$addTestResult("DevModule", "Test", true); - // return Promise.resolve(true); - await this._test("Conflict resolution", async () => await this.checkConflictResolution()); - return this.testDone(); - } - override onBindFunction(core: LiveSyncCore, services: typeof core.services): void { - services.appLifecycle.onSettingLoaded.addHandler(this._everyOnloadAfterLoadSettings.bind(this)); - services.replication.onBeforeReplicate.addHandler(this._everyBeforeReplicate.bind(this)); - services.test.testMultiDevice.addHandler(this._everyModuleTestMultiDevice.bind(this)); - } -} From e14e771bfb6ff37e9fde773446a99e05e46c3c10 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Thu, 28 May 2026 04:42:03 +0100 Subject: [PATCH 04/30] (chore): tidied tsconfig and eslint, and some incorrect imports --- eslint.config.mjs | 71 +++++++++++-------- src/features/ConfigSync/CmdConfigSync.ts | 2 +- src/modules/features/Log/LogPaneView.ts | 2 +- src/modules/features/ModuleLog.ts | 2 +- .../SettingDialogue/LiveSyncSetting.ts | 9 +-- tsconfig.json | 5 +- 6 files changed, 48 insertions(+), 43 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 8109b89..70e5257 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,37 +6,41 @@ import * as sveltePlugin from "eslint-plugin-svelte"; export default defineConfig([ globalIgnores([ - "**/node_modules/*", - "**/jest.config.js", + // Build outputs and legacy files + "**/build", + "**/main.js", + "**/.eslintrc.js.bak", + // Files from linked dependencies (those files should not exist for most people). + "modules/octagonal-wheels/dist/**/*", + + // Sub-projects (Exclude from root linting as they have different environments) + "src/apps/**/*", + + // Specific exclusions from common library (src/lib) "src/lib/coverage", "src/lib/browsertest", - "**/test.ts", - "**/tests.ts", - "**/**test.ts", - "**/**.test.ts", - "**/*.unit.spec.ts", - "**/esbuild.*.mjs", - "**/terser.*.mjs", - "**/node_modules", - "**/build", - "**/.eslintrc.js.bak", - "src/lib/src/patches/pouchdb-utils", - "**/esbuild.config.mjs", - "**/rollup.config.js", - "modules/octagonal-wheels/rollup.config.js", - "modules/octagonal-wheels/dist/**/*", "src/lib/test", "src/lib/_tools", + "src/lib/src/patches/pouchdb-utils", "src/lib/src/cli", - "**/main.js", - "src/apps/**/*", - ".prettierrc.*.mjs", - ".prettierrc.mjs", - "*.config.mjs", - "src/apps/**/*", "src/lib/src/services/implements/browser/**", "src/lib/src/services/implements/headless/**", "src/lib/src/API", + + // Config files and build scripts + "**/jest.config.js", + "**/rollup.config.js", + "**/esbuild.config.mjs", + "**/terser.*.mjs", + ".prettierrc.*.mjs", + ".prettierrc.mjs", + "*.config.mjs", + + // Testing files (Simplified patterns) + "**/*.test.ts", + "**/*.unit.spec.ts", + "**/test.ts", + "**/tests.ts", ]), ...sveltePlugin.configs["flat/base"], ...obsidianmd.configs.recommended, @@ -50,22 +54,29 @@ export default defineConfig([ }, }, rules: { + // Base rules (turned off in favour of TS specific versions or explicitly disabled). "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": ["error", { args: "none" }], "no-unused-labels": "off", - "@typescript-eslint/ban-ts-comment": "off", "no-prototype-builtins": "off", + "require-await": "off", + + // TypeScript specific rules + "@typescript-eslint/no-unused-vars": ["error", { args: "none" }], + "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-empty-function": "off", - "require-await": "error", - "obsidianmd/rule-custom-message": "off", // Temporary - "obsidianmd/ui/sentence-case": "off", // Temporary "@typescript-eslint/require-await": "warn", "@typescript-eslint/no-misused-promises": "warn", "@typescript-eslint/no-floating-promises": "warn", - "no-async-promise-executor": "warn", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unnecessary-type-assertion": "error", + + // General rules + "no-async-promise-executor": "warn", "no-constant-condition": ["error", { checkLoops: false }], + + // Plugin specific overrides (Pending review) + "obsidianmd/rule-custom-message": "off", + "obsidianmd/ui/sentence-case": "off", }, }, { @@ -77,7 +88,7 @@ export default defineConfig([ }, rules: { "no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], - "obsidianmd/no-plugin-as-component": "off", // Temporary + "obsidianmd/no-plugin-as-component": "off", }, } ]); diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index c98142c..8fe59f9 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -68,7 +68,7 @@ import { ConflictResolveModal } from "../../modules/features/InteractiveConflict import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; import { EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, eventHub } from "../../common/events.ts"; import { PluginDialogModal } from "./PluginDialogModal.ts"; -import { $msg } from "src/lib/src/common/i18n.ts"; +import { $msg } from "@/lib/src/common/i18n.ts"; import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; import type { LiveSyncCore } from "../../main.ts"; diff --git a/src/modules/features/Log/LogPaneView.ts b/src/modules/features/Log/LogPaneView.ts index 5af45f6..c032705 100644 --- a/src/modules/features/Log/LogPaneView.ts +++ b/src/modules/features/Log/LogPaneView.ts @@ -2,7 +2,7 @@ import { WorkspaceLeaf } from "@/deps.ts"; import LogPaneComponent from "./LogPane.svelte"; import type ObsidianLiveSyncPlugin from "../../../main.ts"; import { SvelteItemView } from "../../../common/SvelteItemView.ts"; -import { $msg } from "src/lib/src/common/i18n.ts"; +import { $msg } from "@lib/common/i18n.ts"; import { mount } from "svelte"; export const VIEW_TYPE_LOG = "log-log"; //Log view diff --git a/src/modules/features/ModuleLog.ts b/src/modules/features/ModuleLog.ts index 1eb7945..2fba758 100644 --- a/src/modules/features/ModuleLog.ts +++ b/src/modules/features/ModuleLog.ts @@ -29,7 +29,7 @@ import { addIcon, debounce, normalizePath, Notice, stringifyYaml, type Workspace import { LOG_LEVEL_NOTICE, setGlobalLogFunction } from "octagonal-wheels/common/logger"; import { LogPaneView, VIEW_TYPE_LOG } from "./Log/LogPaneView.ts"; import { serialized } from "octagonal-wheels/concurrency/lock"; -import { $msg } from "src/lib/src/common/i18n.ts"; +import { $msg } from "@lib/common/i18n.ts"; import { P2PLogCollector } from "@/lib/src/replication/trystero/P2PLogCollector.ts"; import type { LiveSyncCore } from "../../main.ts"; import { LiveSyncError } from "@lib/common/LSError.ts"; diff --git a/src/modules/features/SettingDialogue/LiveSyncSetting.ts b/src/modules/features/SettingDialogue/LiveSyncSetting.ts index 0a7f90e..894916e 100644 --- a/src/modules/features/SettingDialogue/LiveSyncSetting.ts +++ b/src/modules/features/SettingDialogue/LiveSyncSetting.ts @@ -8,12 +8,7 @@ import { type ValueComponent, } from "@/deps.ts"; import { unique } from "octagonal-wheels/collection"; -import { - LEVEL_ADVANCED, - LEVEL_POWER_USER, - statusDisplay, - type ConfigurationItem, -} from "../../../lib/src/common/types.ts"; +import { LEVEL_ADVANCED, LEVEL_POWER_USER, statusDisplay, type ConfigurationItem } from "@lib/common/types.ts"; import { createStub, type ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import { type AllSettingItemKey, @@ -23,7 +18,7 @@ import { type AllNumericItemKey, type AllBooleanItemKey, } from "./settingConstants.ts"; -import { $msg } from "src/lib/src/common/i18n.ts"; +import { $msg } from "@lib/common/i18n.ts"; import { findAttrFromParent, wrapMemo, type AutoWireOption, type OnUpdateResult } from "./SettingPane.ts"; export class LiveSyncSetting extends Setting { diff --git a/tsconfig.json b/tsconfig.json index 49caa41..4ec4071 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,6 @@ "extends": "@tsconfig/svelte/tsconfig.json", "inlineSourceMap": true, "compilerOptions": { - "baseUrl": ".", "module": "ESNext", "target": "ES2018", "allowJs": true, @@ -20,8 +19,8 @@ "strictBindCallApply": true, "strictFunctionTypes": true, "paths": { - "@/*": ["src/*"], - "@lib/*": ["src/lib/src/*"] + "@/*": ["./src/*"], + "@lib/*": ["./src/lib/src/*"] } }, "include": ["**/*.ts", "test/**/*.test.ts", "**/*.unit.spec.ts"], From b714c00644b5a4b8a802aa202cb43b0c36560230 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 29 May 2026 04:19:58 +0100 Subject: [PATCH 05/30] (chore): Improve type assertion, remove unused imports --- src/lib | 2 +- .../features/SettingDialogue/PaneHatch.ts | 42 +++++++------------ src/modules/services/ObsidianAPIService.ts | 15 +++++-- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/lib b/src/lib index 61741c1..4d5adcd 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 61741c1748a48796a4f8ba0ea83ccde5f4e848fa +Subproject commit 4d5adcdee0a5ce4ace6f10881601071038b645ae diff --git a/src/modules/features/SettingDialogue/PaneHatch.ts b/src/modules/features/SettingDialogue/PaneHatch.ts index d9ca27c..18c878f 100644 --- a/src/modules/features/SettingDialogue/PaneHatch.ts +++ b/src/modules/features/SettingDialogue/PaneHatch.ts @@ -1,29 +1,16 @@ -import { stringifyYaml } from "../../../deps.ts"; import { - type ObsidianLiveSyncSettings, type FilePathWithPrefix, type DocumentID, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, type LoadedEntry, - REMOTE_COUCHDB, - REMOTE_MINIO, type MetaEntry, type FilePath, - DEFAULT_SETTINGS, -} from "../../../lib/src/common/types.ts"; -import { - createBlob, - getFileRegExp, - isDocContentSame, - parseHeaderValues, - readAsBlob, -} from "../../../lib/src/common/utils.ts"; -import { Logger } from "../../../lib/src/common/logger.ts"; -import { isCloudantURI } from "../../../lib/src/pouchdb/utils_couchdb.ts"; -import { requestToCouchDBWithCredentials } from "../../../common/utils.ts"; -import { addPrefix, shouldBeIgnored, stripAllPrefixes } from "../../../lib/src/string_and_binary/path.ts"; -import { $msg } from "../../../lib/src/common/i18n.ts"; +} from "@lib/common/types.ts"; +import { createBlob, getFileRegExp, isDocContentSame, readAsBlob } from "@lib/common/utils.ts"; +import { Logger } from "@lib/common/logger.ts"; +import { addPrefix, shouldBeIgnored, stripAllPrefixes } from "@lib/string_and_binary/path.ts"; +import { $msg } from "@lib/common/i18n.ts"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts"; import { @@ -32,14 +19,13 @@ import { EVENT_REQUEST_RUN_DOCTOR, EVENT_REQUEST_RUN_FIX_INCOMPLETE, eventHub, -} from "../../../common/events.ts"; -import { ICHeader, ICXHeader, PSCHeader } from "../../../common/types.ts"; -import { HiddenFileSync } from "../../../features/HiddenFileSync/CmdHiddenFileSync.ts"; -import { EVENT_REQUEST_SHOW_HISTORY } from "../../../common/obsidianEvents.ts"; -import { generateCredentialObject } from "../../../lib/src/replication/httplib.ts"; +} from "@/common/events.ts"; +import { ICHeader, ICXHeader, PSCHeader } from "@/common/types.ts"; +import { HiddenFileSync } from "@/features/HiddenFileSync/CmdHiddenFileSync.ts"; +import { EVENT_REQUEST_SHOW_HISTORY } from "@/common/obsidianEvents.ts"; import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts"; import type { PageFunctions } from "./SettingPane.ts"; -import { generateReport } from "@/common/reportTool.ts"; +import { isNotFoundError } from "@lib/common/utils.doc.ts"; export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void { // const hatchWarn = this.createEl(paneEl, "div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` }); // hatchWarn.addClass("op-warn-info"); @@ -160,14 +146,14 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, } if (!(await addOn.storeInternalFileToDatabase(file, true))) { Logger( - `Failed to store the file to the database (Hidden file): ${file}`, + `Failed to store the file to the database (Hidden file): ${file.path}`, LOG_LEVEL_NOTICE ); return; } } } else { - if (!(await this.core.fileHandler.storeFileToDB(file as FilePath, true))) { + if (!(await this.core.fileHandler.storeFileToDB(file, true))) { Logger( `Failed to store the file to the database: ${file}`, LOG_LEVEL_NOTICE @@ -406,8 +392,8 @@ export function paneHatch(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, Logger(`Converting ${docName} Failed!`, LOG_LEVEL_NOTICE); Logger(ret, LOG_LEVEL_VERBOSE); } - } catch (ex: any) { - if (ex?.status == 404) { + } catch (ex: unknown) { + if (isNotFoundError(ex)) { // We can perform this safely if ((await this.core.localDatabase.putRaw(newDoc)).ok) { Logger(`${docName} has been converted`, LOG_LEVEL_NOTICE); diff --git a/src/modules/services/ObsidianAPIService.ts b/src/modules/services/ObsidianAPIService.ts index 9f19c2d..2ac8d76 100644 --- a/src/modules/services/ObsidianAPIService.ts +++ b/src/modules/services/ObsidianAPIService.ts @@ -5,9 +5,17 @@ import { ObsHttpHandler } from "../essentialObsidian/APILib/ObsHttpHandler"; import { ObsidianConfirm } from "./ObsidianConfirm"; import type { Confirm } from "@lib/interfaces/Confirm"; import { requestUrl, type RequestUrlParam } from "@/deps"; +import { compatGlobal } from "@/lib/src/common/coreEnvFunctions"; // All Services will be migrated to be based on Plain Services, not Injectable Services. // This is a migration step. +declare module "obsidian" { + interface App { + appId?: string; + isMobile?: boolean; + } +} + export class ObsidianAPIService extends InjectableAPIService { _customHandler: ObsHttpHandler | undefined; _confirmInstance: Confirm; @@ -83,8 +91,7 @@ export class ObsidianAPIService extends InjectableAPIService= 2) { return match[1]; @@ -196,7 +203,7 @@ export class ObsidianAPIService extends InjectableAPIService void, timeout: number): number { - const timerId = globalThis.setInterval(handler, timeout) as unknown as number; + const timerId = compatGlobal.setInterval(handler, timeout); this.context.plugin.registerInterval(timerId); return timerId; } From 7b5876037df37b9cfd1079d1969bdad15684ee48 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 30 May 2026 23:22:17 +0900 Subject: [PATCH 06/30] Add ignores --- eslint.config.mjs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index 70e5257..9360b6b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -8,13 +8,20 @@ export default defineConfig([ globalIgnores([ // Build outputs and legacy files "**/build", + "coverage", "**/main.js", + "main_org.js", + "pouchdb-browser.js", + "version-bump.mjs", + "package.json", + "**/*.json", "**/.eslintrc.js.bak", // Files from linked dependencies (those files should not exist for most people). "modules/octagonal-wheels/dist/**/*", // Sub-projects (Exclude from root linting as they have different environments) "src/apps/**/*", + "utils/**/*", // Specific exclusions from common library (src/lib) "src/lib/coverage", From 547afe9a8663638b6dc9b4857d77e3fcbf069b39 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 30 May 2026 23:32:46 +0900 Subject: [PATCH 07/30] remove unused eslint comment use _fetch instead of fetch add ignore list --- eslint.config.mjs | 1 + src/common/utils.ts | 3 ++- src/lib | 2 +- src/modules/essentialObsidian/APILib/ObsHttpHandler.ts | 1 - 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9360b6b..1583953 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -44,6 +44,7 @@ export default defineConfig([ "*.config.mjs", // Testing files (Simplified patterns) + "test/**", "**/*.test.ts", "**/*.unit.spec.ts", "**/test.ts", diff --git a/src/common/utils.ts b/src/common/utils.ts index b008f2c..15aa19d 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -146,7 +146,7 @@ export const _requestToCouchDBFetch = async ( contentType: "application/json", body: JSON.stringify(body), }; - return await fetch(uri, requestParam); + return await _fetch(uri, requestParam); }; export const _requestToCouchDB = async ( @@ -214,6 +214,7 @@ import { BASE_IS_NEW, EVEN, TARGET_IS_NEW } from "@lib/common/models/shared.cons export { BASE_IS_NEW, EVEN, TARGET_IS_NEW }; // Why 2000? : ZIP FILE Does not have enough resolution. import { compareMTime } from "@lib/common/utils.ts"; +import { _fetch } from "@/lib/src/common/coreEnvFunctions.ts"; export { compareMTime }; function getKey(file: AnyEntry | string | UXFileInfoStub) { const key = typeof file == "string" ? file : stripAllPrefixes(file.path); diff --git a/src/lib b/src/lib index 4d5adcd..da48c09 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 4d5adcdee0a5ce4ace6f10881601071038b645ae +Subproject commit da48c09e783ab9e1df0287ba44a3236c5209562d diff --git a/src/modules/essentialObsidian/APILib/ObsHttpHandler.ts b/src/modules/essentialObsidian/APILib/ObsHttpHandler.ts index 5504918..ef5614c 100644 --- a/src/modules/essentialObsidian/APILib/ObsHttpHandler.ts +++ b/src/modules/essentialObsidian/APILib/ObsHttpHandler.ts @@ -27,7 +27,6 @@ export class ObsHttpHandler extends FetchHttpHandler { this.requestTimeoutInMs = options === undefined ? undefined : options.requestTimeout; this.reverseProxyNoSignUrl = reverseProxyNoSignUrl; } - // eslint-disable-next-line require-await override async handle( request: HttpRequest, { abortSignal }: HttpHandlerOptions = {} From 7189c1c05acaf3ba3ebb4b6ecd98d76121278035 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 30 May 2026 23:42:32 +0900 Subject: [PATCH 08/30] Update dependency set no-deprecated to warn --- eslint.config.mjs | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- src/lib | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 1583953..80bc5b2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -67,8 +67,8 @@ export default defineConfig([ "no-unused-labels": "off", "no-prototype-builtins": "off", "require-await": "off", - // TypeScript specific rules + "@typescript-eslint/no-deprecated": "warn", "@typescript-eslint/no-unused-vars": ["error", { args: "none" }], "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-empty-function": "off", diff --git a/package-lock.json b/package-lock.json index 327d1c5..3912bc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "micromatch": "^4.0.0", "minimatch": "^10.2.2", "obsidian": "^1.12.3", - "octagonal-wheels": "^0.1.45", + "octagonal-wheels": "^0.1.46", "pouchdb-adapter-leveldb": "^9.0.0", "qrcode-generator": "^1.4.4", "werift": "^0.23.0", @@ -11491,9 +11491,9 @@ "license": "MIT" }, "node_modules/octagonal-wheels": { - "version": "0.1.45", - "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.45.tgz", - "integrity": "sha512-gXoCrwoUIXhmu57YN4BxAtBe+JaYNJNaXaZuVjqjopwYKpH5p2mn1om6KjA22rgGPiIJFXkse2U28FFXoT3/0Q==", + "version": "0.1.46", + "resolved": "https://registry.npmjs.org/octagonal-wheels/-/octagonal-wheels-0.1.46.tgz", + "integrity": "sha512-19eB7b/WNNrZ4Xghu93f+NVJsbRiaZaIIzU1rn5shxb6SzwVBoOVkNPJdCAsONl6C1MwjaGDrPUS8CBXvPHjPg==", "license": "MIT", "dependencies": { "idb": "^8.0.3" diff --git a/package.json b/package.json index f74d849..db366fd 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "markdown-it": "^14.1.1", "micromatch": "^4.0.0", "minimatch": "^10.2.2", - "octagonal-wheels": "^0.1.45", + "octagonal-wheels": "^0.1.46", "pouchdb-adapter-leveldb": "^9.0.0", "qrcode-generator": "^1.4.4", "werift": "^0.23.0", diff --git a/src/lib b/src/lib index da48c09..68f1e26 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit da48c09e783ab9e1df0287ba44a3236c5209562d +Subproject commit 68f1e26b3eb2cef2261f9db73dca9afebe316275 From f24d11055265126dfd264598f62ede4a60064978 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 30 May 2026 23:49:32 +0900 Subject: [PATCH 09/30] Change type assertion --- src/features/ConfigSync/CmdConfigSync.ts | 2 +- src/lib | 2 +- src/modules/extras/ModuleDev.ts | 2 +- src/modules/features/SettingDialogue/PanePatches.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index 8fe59f9..e6f4264 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -564,7 +564,7 @@ export class ConfigSync extends LiveSyncCommands { ...data, documentPath: this.getPath(wx), files: xFiles, - } as PluginDataExDisplay; + } satisfies PluginDataExDisplay; } return false; } diff --git a/src/lib b/src/lib index 68f1e26..080bfa2 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 68f1e26b3eb2cef2261f9db73dca9afebe316275 +Subproject commit 080bfa2e14878794f30fcc818621653c3c5e8914 diff --git a/src/modules/extras/ModuleDev.ts b/src/modules/extras/ModuleDev.ts index a69776a..ab48c31 100644 --- a/src/modules/extras/ModuleDev.ts +++ b/src/modules/extras/ModuleDev.ts @@ -111,7 +111,7 @@ export class ModuleDev extends AbstractObsidianModule { const filename = "test-create-conflict.md"; const content = `# Test create conflict\n\n`; const w = await this.core.databaseFileAccess.store({ - name: filename as FilePathWithPrefix, + name: filename, path: filename as FilePathWithPrefix, body: new Blob([content], { type: "text/markdown" }), stat: { diff --git a/src/modules/features/SettingDialogue/PanePatches.ts b/src/modules/features/SettingDialogue/PanePatches.ts index 0631bbd..13d3bf3 100644 --- a/src/modules/features/SettingDialogue/PanePatches.ts +++ b/src/modules/features/SettingDialogue/PanePatches.ts @@ -150,7 +150,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen xxhash64: "xxhash64 (Fastest)", "mixed-purejs": "PureJS fallback (Fast, W/O WebAssembly)", sha1: "Older fallback (Slow, W/O WebAssembly)", - } as Record, + } satisfies Record, }); this.addOnSaved("hashAlg", async () => { await this.core.localDatabase._prepareHashFunctions(); From 24e6c110a3a5c1b101fa8337dc10e39090eccaeb Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 30 May 2026 23:53:19 +0900 Subject: [PATCH 10/30] Remove unused imports --- src/lib | 2 +- src/modules/extras/devUtil/TestPane.svelte | 4 +--- .../features/SetupWizard/dialogs/SelectMethodExisting.svelte | 3 --- .../features/SetupWizard/dialogs/SelectMethodNewUser.svelte | 3 --- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/lib b/src/lib index 080bfa2..8e8944c 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 080bfa2e14878794f30fcc818621653c3c5e8914 +Subproject commit 8e8944ca4172174c8c7db651aa277b5e4c852994 diff --git a/src/modules/extras/devUtil/TestPane.svelte b/src/modules/extras/devUtil/TestPane.svelte index 6ea0eea..2d8f378 100644 --- a/src/modules/extras/devUtil/TestPane.svelte +++ b/src/modules/extras/devUtil/TestPane.svelte @@ -1,17 +1,15 @@
- -
-
- - - + +
+
+ + + -
-
-
- {#each messages as line} -
{line}
- {/each} -
+
+
+
+ {#each messages as line} +
{line}
+ {/each} +
diff --git a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts index 9bdfa56..f6bbd80 100644 --- a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts +++ b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts @@ -62,6 +62,7 @@ import { paneAdvanced } from "./PaneAdvanced.ts"; import { panePowerUsers } from "./PanePowerUsers.ts"; import { panePatches } from "./PanePatches.ts"; import { paneMaintenance } from "./PaneMaintenance.ts"; +import { compatGlobal } from "@lib/common/coreEnvFunctions.ts"; // For creating a document const toc = new Set(); @@ -141,7 +142,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { async saveLocalSetting(key: keyof typeof OnDialogSettingsDefault) { if (key == "configPassphrase") { - localStorage.setItem("ls-setting-passphrase", this.editingSettings?.[key] ?? ""); + compatGlobal.localStorage.setItem("ls-setting-passphrase", this.editingSettings?.[key] ?? ""); return await Promise.resolve(); } if (key == "deviceAndVaultName") { @@ -214,7 +215,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { reloadAllLocalSettings() { const ret = { ...OnDialogSettingsDefault }; - ret.configPassphrase = localStorage.getItem("ls-setting-passphrase") || ""; + ret.configPassphrase = compatGlobal.localStorage.getItem("ls-setting-passphrase") || ""; ret.preset = ""; ret.deviceAndVaultName = this.services.setting.getDeviceAndVaultName(); return ret; diff --git a/src/modules/features/SettingDialogue/PanePatches.ts b/src/modules/features/SettingDialogue/PanePatches.ts index 13d3bf3..839f7b2 100644 --- a/src/modules/features/SettingDialogue/PanePatches.ts +++ b/src/modules/features/SettingDialogue/PanePatches.ts @@ -188,7 +188,7 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen } this.requestUpdate(); }; - text.inputEl.before((dateEl = document.createElement("span"))); + text.inputEl.before((dateEl = activeDocument.createElement("span"))); text.inputEl.type = "datetime-local"; if (this.editingSettings.maxMTimeForReflectEvents > 0) { const date = new Date(this.editingSettings.maxMTimeForReflectEvents); diff --git a/src/modules/features/SettingDialogue/settingUtils.ts b/src/modules/features/SettingDialogue/settingUtils.ts index b965298..847ebc0 100644 --- a/src/modules/features/SettingDialogue/settingUtils.ts +++ b/src/modules/features/SettingDialogue/settingUtils.ts @@ -75,7 +75,7 @@ export function getSummaryFromPartialSettings(setting: Partial Date: Mon, 1 Jun 2026 05:28:03 +0100 Subject: [PATCH 17/30] Aligned to eslint rules and fixed following things: This commit may contains behavioural changes. - Fix for the issue with corrupted log displays - Wrap the activeDocument - Reduced potential type errors and strengthened certain checks - Made error handling more robust (by rewriting the error class) --- eslint.config.mjs | 21 ++- package-lock.json | 16 +-- package.json | 3 +- src/features/ConfigSync/CmdConfigSync.ts | 7 +- .../HiddenFileSync/CmdHiddenFileSync.ts | 5 +- .../CmdLocalDatabaseMainte.ts | 131 +++++++++--------- src/lib | 2 +- src/modules/coreObsidian/UILib/dialogs.ts | 7 +- .../GlobalHistory/GlobalHistory.svelte | 2 +- .../ModuleInteractiveConflictResolver.ts | 4 +- .../ObsidianLiveSyncSettingTab.ts | 6 +- .../SettingDialogue/PaneRemoteConfig.ts | 10 +- src/modules/features/SetupManager.ts | 4 +- src/modules/main/ModuleLiveSyncMain.ts | 7 +- .../services/ObsidianSettingService.ts | 10 +- src/serviceFeatures/redFlag.ts | 2 +- 16 files changed, 130 insertions(+), 107 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 0c47cb2..65f81a3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,7 +4,7 @@ import globals from "globals"; import { defineConfig, globalIgnores } from "eslint/config"; import * as sveltePlugin from "eslint-plugin-svelte"; import svelteParser from "svelte-eslint-parser"; - +const warnWhileDev = "off"; // Change to "warn" to enable warnings for rules that are currently disabled. export default defineConfig([ globalIgnores([ // Build outputs and legacy files @@ -64,6 +64,9 @@ export default defineConfig([ project: "./tsconfig.json", }, }, + linterOptions:{ + reportUnusedDisableDirectives: false, + }, rules: { // -- Base rules (turned off in favour of TS specific versions or explicitly disabled). "no-unused-vars": "off", @@ -81,29 +84,33 @@ export default defineConfig([ "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-unsafe-assignment": "off", // -- Reasonable rules. - "@typescript-eslint/no-deprecated": "warn", + "@typescript-eslint/no-deprecated": warnWhileDev, "@typescript-eslint/no-unused-vars": ["error", { args: "none" }], "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/require-await": "warn", + "@typescript-eslint/require-await": "error", "@typescript-eslint/no-misused-promises": "error", "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-unnecessary-type-assertion": "error", // -- Obsidian rules // obsidianmd/no-unsupported-api: usually this project checks for API support at runtime, so this rule is not critical but can be helpful to catch potential issues. - "obsidianmd/no-unsupported-api": "warn", + "obsidianmd/no-unsupported-api": warnWhileDev, // -- General rules - "no-async-promise-executor": "warn", + "no-async-promise-executor": warnWhileDev, "no-constant-condition": ["error", { checkLoops: false }], - // -- Disabled rules (Pending review) + // -- Disabled rules // no-undef: This option breaks the global declarations for the library files and is not worth the effort to fix at this time. "no-undef": "off", - // -- Plugin specific overrides (Pending review) + // -- Plugin specific overrides "obsidianmd/rule-custom-message": "off", "obsidianmd/ui/sentence-case": "off", + "obsidianmd/no-plugin-as-component": "off", + + // -- Temporary overrides for migration + "obsidianmd/no-static-styles-assignment": "off", }, }, { diff --git a/package-lock.json b/package-lock.json index 3912bc4..eb84601 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@smithy/middleware-apply-body-checksum": "^4.3.9", "@smithy/protocol-http": "^5.3.9", "@smithy/querystring-builder": "^4.2.9", + "@smithy/util-retry": "^4.4.5", "@trystero-p2p/nostr": "^0.24.0", "chokidar": "^4.0.0", "commander": "^14.0.3", @@ -3546,9 +3547,9 @@ } }, "node_modules/@smithy/core": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", - "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.5.tgz", + "integrity": "sha512-Kt8phUg45M15EjhYAbZ+fFikYneijLu9Liugz8ZsYz2i8j0hzGv27LWKpEHYRfvj+LyCOSijpcR/2i8RouV+cA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", @@ -4149,13 +4150,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.12.tgz", - "integrity": "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.4.5.tgz", + "integrity": "sha512-W9Ovy9i02yGqtLlpqZNQuXNxXc5OPfXujnembxN/FxyBtGjJd8vKY0PQYEJ8FNybTOcXG+ZxsSsX23HOb3zQzg==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.12", - "@smithy/types": "^4.13.1", + "@smithy/core": "^3.24.5", "tslib": "^2.6.2" }, "engines": { diff --git a/package.json b/package.json index db366fd..6700e33 100644 --- a/package.json +++ b/package.json @@ -130,16 +130,17 @@ "@smithy/middleware-apply-body-checksum": "^4.3.9", "@smithy/protocol-http": "^5.3.9", "@smithy/querystring-builder": "^4.2.9", + "@smithy/util-retry": "^4.4.5", "@trystero-p2p/nostr": "^0.24.0", "chokidar": "^4.0.0", "commander": "^14.0.3", - "obsidian": "^1.12.3", "diff-match-patch": "^1.0.5", "fflate": "^0.8.2", "idb": "^8.0.3", "markdown-it": "^14.1.1", "micromatch": "^4.0.0", "minimatch": "^10.2.2", + "obsidian": "^1.12.3", "octagonal-wheels": "^0.1.46", "pouchdb-adapter-leveldb": "^9.0.0", "qrcode-generator": "^1.4.4", diff --git a/src/features/ConfigSync/CmdConfigSync.ts b/src/features/ConfigSync/CmdConfigSync.ts index e6f4264..585ef5c 100644 --- a/src/features/ConfigSync/CmdConfigSync.ts +++ b/src/features/ConfigSync/CmdConfigSync.ts @@ -71,6 +71,7 @@ import { PluginDialogModal } from "./PluginDialogModal.ts"; import { $msg } from "@/lib/src/common/i18n.ts"; import type { InjectableServiceHub } from "../../lib/src/services/InjectableServices.ts"; import type { LiveSyncCore } from "../../main.ts"; +import { LiveSyncError } from "@lib/common/LSError.ts"; const d = "\u200b"; const d2 = "\n"; @@ -1069,10 +1070,10 @@ export class ConfigSync extends LiveSyncCommands { } const baseDir = this.configDir; try { - if (!data.documentPath) throw "InternalError: Document path not exist"; + if (!data.documentPath) throw new LiveSyncError("InternalError: Document path not exist"); const dx = await this.localDatabase.getDBEntry(data.documentPath); if (dx == false) { - throw "Not found on database"; + throw new LiveSyncError("Not found on database"); } const loadedData = deserialize(getDocDataAsArray(dx.data), {}) as PluginDataEx; for (const f of loadedData.files) { @@ -1317,7 +1318,7 @@ export class ConfigSync extends LiveSyncCommands { } const docXDoc = await this.localDatabase.getDBEntryFromMeta(old, false, false); if (docXDoc == false) { - throw "Could not load the document"; + throw new LiveSyncError("Could not load the document"); } const dataSrc = getDocData(docXDoc.data); const dataStart = dataSrc.indexOf(DUMMY_END); diff --git a/src/features/HiddenFileSync/CmdHiddenFileSync.ts b/src/features/HiddenFileSync/CmdHiddenFileSync.ts index ed3aa84..3c086d6 100644 --- a/src/features/HiddenFileSync/CmdHiddenFileSync.ts +++ b/src/features/HiddenFileSync/CmdHiddenFileSync.ts @@ -50,6 +50,7 @@ import { hiddenFilesEventCount, hiddenFilesProcessingCount } from "../../lib/src import { EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts"; import { Semaphore } from "octagonal-wheels/concurrency/semaphore"; import type { LiveSyncCore } from "../../main.ts"; +import { tryGetFilePath } from "@lib/common/utils.doc.ts"; type SyncDirection = "push" | "pull" | "safe" | "pullForce" | "pushForce"; declare global { @@ -1047,7 +1048,7 @@ Offline Changed files: ${processFiles.length}`; } notifyProgress(); } catch (ex) { - this._log(`Failed to process storage change file:${file}`, logLevel); + this._log(`Failed to process storage change file:${tryGetFilePath(file)}`, logLevel); this._log(ex, LOG_LEVEL_VERBOSE); } }); @@ -1159,7 +1160,7 @@ Offline Changed files: ${files.length}`; await this.trackDatabaseFileModification(path, "[Scanning]", true, onlyNew, file); notifyProgress(); } catch (ex) { - this._log(`Failed to process database changes:${file}`); + this._log(`Failed to process database changes:${tryGetFilePath(file)}`); this._log(ex, LOG_LEVEL_VERBOSE); } return; diff --git a/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts index df5f921..54eb7aa 100644 --- a/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts +++ b/src/features/LocalDatabaseMainte/CmdLocalDatabaseMainte.ts @@ -16,9 +16,8 @@ import { serialized } from "octagonal-wheels/concurrency/lock_v2"; import { arrayToChunkedArray } from "octagonal-wheels/collection"; import { EVENT_ANALYSE_DB_USAGE, EVENT_REQUEST_PERFORM_GC_V3, eventHub } from "@/common/events"; import type { LiveSyncCouchDBReplicator } from "@/lib/src/replication/couchdb/LiveSyncReplicator"; -import { delay, parseHeaderValues } from "@/lib/src/common/utils"; -import { generateCredentialObject } from "@/lib/src/replication/httplib"; -import { _requestToCouchDB } from "@/common/utils"; +import { delay } from "@/lib/src/common/utils"; +// import { _requestToCouchDB } from "@/common/utils"; const DB_KEY_SEQ = "gc-seq"; const DB_KEY_CHUNK_SET = "chunk-set"; const DB_KEY_DOC_USAGE_MAP = "doc-usage-map"; @@ -533,7 +532,7 @@ Success: ${successCount}, Errored: ${errored}`; const docMap = new Map>(); const info = await db.info(); // Total number of revisions to process (approximate) - const maxSeq = new Number(info.update_seq); + const maxSeq = Number.parseInt(`${info.update_seq ?? 0}`, 10); let processed = 0; let read = 0; let errored = 0; @@ -759,68 +758,68 @@ Success: ${successCount}, Errored: ${errored}`; } } - /** - * Compact the database by temporarily setting the revision limit to 1. - * @returns - */ - async compactDatabaseWithRevLimit() { - // Temporarily set revs_limit to 1, perform compaction, and restore the original revs_limit. - // Very dangerous operation, so now suppressed. - return false; - const replicator = this.core.replicator as LiveSyncCouchDBReplicator; - const remote = await replicator.connectRemoteCouchDBWithSetting(this.settings, false, false, true); - if (!remote) { - this._notice("Failed to connect to remote for compaction."); - return; - } - if (typeof remote == "string") { - this._notice(`Failed to connect to remote for compaction. ${remote}`); - return; - } - const customHeaders = parseHeaderValues(this.settings.couchDB_CustomHeaders); - const credential = generateCredentialObject(this.settings); - const request = async (path: string, method: string = "GET", body: any = undefined) => { - const req = await _requestToCouchDB( - this.settings.couchDB_URI.replace(/\/+$/, "") + - (this.settings.couchDB_DBNAME ? `/${this.settings.couchDB_DBNAME}` : ""), - credential, - window.origin, - path, - body, - method, - customHeaders - ); - return req; - }; - let revsLimit = ""; - const req = await request(`_revs_limit`, "GET"); - if (req.status == 200) { - revsLimit = req.text.trim(); - this._info(`Remote database _revs_limit: ${revsLimit}`); - } else { - this._notice(`Failed to get remote database _revs_limit. Status: ${req.status}`); - return; - } - const req2 = await request(`_revs_limit`, "PUT", 1); - if (req2.status == 200) { - this._info(`Set remote database _revs_limit to 1 for compaction.`); - } - try { - await this.compactDatabase(); - } finally { - // Restore revs_limit - if (revsLimit) { - const req3 = await request(`_revs_limit`, "PUT", parseInt(revsLimit)); - if (req3.status == 200) { - this._info(`Restored remote database _revs_limit to ${revsLimit}.`); - } else { - this._notice( - `Failed to restore remote database _revs_limit. Status: ${req3.status} / ${req3.text}` - ); - } - } - } - } + // /** + // * Compact the database by temporarily setting the revision limit to 1. + // * @returns + // */ + // async compactDatabaseWithRevLimit() { + // // Temporarily set revs_limit to 1, perform compaction, and restore the original revs_limit. + // // Very dangerous operation, so now suppressed. + // return Promise.resolve(false); + // const replicator = this.core.replicator as LiveSyncCouchDBReplicator; + // const remote = await replicator.connectRemoteCouchDBWithSetting(this.settings, false, false, true); + // if (!remote) { + // this._notice("Failed to connect to remote for compaction."); + // return; + // } + // if (typeof remote == "string") { + // this._notice(`Failed to connect to remote for compaction. ${remote}`); + // return; + // } + // const customHeaders = parseHeaderValues(this.settings.couchDB_CustomHeaders); + // const credential = generateCredentialObject(this.settings); + // const request = async (path: string, method: string = "GET", body: any = undefined) => { + // const req = await _requestToCouchDB( + // this.settings.couchDB_URI.replace(/\/+$/, "") + + // (this.settings.couchDB_DBNAME ? `/${this.settings.couchDB_DBNAME}` : ""), + // credential, + // window.origin, + // path, + // body, + // method, + // customHeaders + // ); + // return req; + // }; + // let revsLimit = ""; + // const req = await request(`_revs_limit`, "GET"); + // if (req.status == 200) { + // revsLimit = req.text.trim(); + // this._info(`Remote database _revs_limit: ${revsLimit}`); + // } else { + // this._notice(`Failed to get remote database _revs_limit. Status: ${req.status}`); + // return; + // } + // const req2 = await request(`_revs_limit`, "PUT", 1); + // if (req2.status == 200) { + // this._info(`Set remote database _revs_limit to 1 for compaction.`); + // } + // try { + // await this.compactDatabase(); + // } finally { + // // Restore revs_limit + // if (revsLimit) { + // const req3 = await request(`_revs_limit`, "PUT", parseInt(revsLimit)); + // if (req3.status == 200) { + // this._info(`Restored remote database _revs_limit to ${revsLimit}.`); + // } else { + // this._notice( + // `Failed to restore remote database _revs_limit. Status: ${req3.status} / ${req3.text}` + // ); + // } + // } + // } + // } async gcv3() { if (!this.isAvailable()) return; const replicator = this.core.replicator as LiveSyncCouchDBReplicator; diff --git a/src/lib b/src/lib index af21598..2c6c1df 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit af21598b785e03f08a01a9bcb317ff686c39c76f +Subproject commit 2c6c1dfadb8d2128bae3dda2bf2d8dcf1c36470d diff --git a/src/modules/coreObsidian/UILib/dialogs.ts b/src/modules/coreObsidian/UILib/dialogs.ts index 2d8504a..d84bcab 100644 --- a/src/modules/coreObsidian/UILib/dialogs.ts +++ b/src/modules/coreObsidian/UILib/dialogs.ts @@ -1,10 +1,11 @@ import { ButtonComponent } from "@/deps.ts"; import { App, FuzzySuggestModal, MarkdownRenderer, Modal, Plugin, Setting } from "../../../deps.ts"; import { EVENT_PLUGIN_UNLOADED, eventHub } from "../../../common/events.ts"; -import { compatGlobal } from "@lib/common/coreEnvFunctions.ts"; +import { compatGlobal, type CompatIntervalHandle } from "@lib/common/coreEnvFunctions.ts"; class AutoClosableModal extends Modal { _closeByUnload() { + // eslint-disable-next-line @typescript-eslint/unbound-method eventHub.off(EVENT_PLUGIN_UNLOADED, this._closeByUnload); this.close(); } @@ -12,9 +13,11 @@ class AutoClosableModal extends Modal { constructor(app: App) { super(app); this._closeByUnload = this._closeByUnload.bind(this); + // eslint-disable-next-line @typescript-eslint/unbound-method eventHub.once(EVENT_PLUGIN_UNLOADED, this._closeByUnload); } override onClose() { + // eslint-disable-next-line @typescript-eslint/unbound-method eventHub.off(EVENT_PLUGIN_UNLOADED, this._closeByUnload); } } @@ -140,7 +143,7 @@ export class MessageBox extends AutoClosableModal { isManuallyClosed = false; defaultAction: string | undefined; timeout: number | undefined; - timer: ReturnType | undefined = undefined; + timer: CompatIntervalHandle | undefined = undefined; defaultButtonComponent: ButtonComponent | undefined; wideButton: boolean; diff --git a/src/modules/features/GlobalHistory/GlobalHistory.svelte b/src/modules/features/GlobalHistory/GlobalHistory.svelte index f150e72..4aeb0bf 100644 --- a/src/modules/features/GlobalHistory/GlobalHistory.svelte +++ b/src/modules/features/GlobalHistory/GlobalHistory.svelte @@ -248,7 +248,7 @@
- /{entry.dirname.split("/").join(`​/`)} + /{entry.dirname.split("/").join(`\u200b/`)} diff --git a/src/modules/features/ModuleInteractiveConflictResolver.ts b/src/modules/features/ModuleInteractiveConflictResolver.ts index 0c04092..0ef7c12 100644 --- a/src/modules/features/ModuleInteractiveConflictResolver.ts +++ b/src/modules/features/ModuleInteractiveConflictResolver.ts @@ -88,7 +88,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule { return false; } } else { - this._log(`Merge: Something went wrong: ${filename}, (${toDelete})`, LOG_LEVEL_NOTICE); + this._log(`Merge: Something went wrong: ${filename}, (${toDelete as string})`, LOG_LEVEL_NOTICE); return false; } // In here, some merge has been processed. @@ -163,7 +163,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule { this._log(`There are no conflicting files`, LOG_LEVEL_VERBOSE); } } catch (e) { - this._log(`Error while scanning conflicted files: ${e}`, LOG_LEVEL_NOTICE); + this._log(`Error while scanning conflicted files...`, LOG_LEVEL_NOTICE); this._log(e, LOG_LEVEL_VERBOSE); return false; } diff --git a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts index f6bbd80..c06dcfe 100644 --- a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts +++ b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts @@ -181,7 +181,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { // if (runOnSaved) { const handlers = this.onSavedHandlers .filter((e) => appliedKeys.indexOf(e.key) !== -1) - .map((e) => e.handler(this.editingSettings[e.key as AllSettingItemKey])); + .map((e) => Promise.resolve(e.handler(this.editingSettings[e.key as AllSettingItemKey]))); await Promise.all(handlers); // } keys.forEach((e) => this.refreshSetting(e)); @@ -648,7 +648,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { this.editingSettings.passphrase = ""; } await this.saveAllDirtySettings(); - await this.applyAllSettings(); + await Promise.resolve(this.applyAllSettings()); if (result == OPTION_FETCH) { await this.core.storageAccess.writeFileAuto(FLAGMD_REDFLAG3_HR, ""); this.services.appLifecycle.scheduleRestart(); @@ -739,6 +739,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab { ); } setLevelClass(el, level); + // TODO: Refactor to use Obsidian's recommended way to create heading. + // eslint-disable-next-line obsidianmd/settings-tab/no-manual-html-headings el.createEl("h3", { text: title, cls: "sls-setting-pane-title" }); if (this.menuEl) { this.menuEl.createEl( diff --git a/src/modules/features/SettingDialogue/PaneRemoteConfig.ts b/src/modules/features/SettingDialogue/PaneRemoteConfig.ts index e609e39..8fb94e9 100644 --- a/src/modules/features/SettingDialogue/PaneRemoteConfig.ts +++ b/src/modules/features/SettingDialogue/PaneRemoteConfig.ts @@ -5,6 +5,7 @@ import { DEFAULT_SETTINGS, LOG_LEVEL_NOTICE, type ObsidianLiveSyncSettings, + LOG_LEVEL_VERBOSE, } from "../../../lib/src/common/types.ts"; import { Menu } from "@/deps.ts"; import { $msg } from "../../../lib/src/common/i18n.ts"; @@ -288,7 +289,8 @@ export function paneRemoteConfig( try { parsed = ConnectionStringParser.parse(trimmedURI); } catch (ex) { - this.services.API.addLog(`Failed to import remote configuration: ${ex}`, LOG_LEVEL_NOTICE); + this.services.API.addLog(`Failed to import remote configuration!`, LOG_LEVEL_NOTICE); + this.services.API.addLog(ex, LOG_LEVEL_VERBOSE); return; } @@ -343,9 +345,10 @@ export function paneRemoteConfig( parsed = ConnectionStringParser.parse(config.uri); } catch (ex) { this.services.API.addLog( - `Failed to parse remote configuration '${config.id}' for editing: ${ex}`, + `Failed to parse remote configuration '${config.id}' for editing!`, LOG_LEVEL_NOTICE ); + this.services.API.addLog(ex, LOG_LEVEL_VERBOSE); return; } const workSettings = createBaseRemoteSettings(); @@ -452,9 +455,10 @@ export function paneRemoteConfig( parsed = ConnectionStringParser.parse(config.uri); } catch (ex) { this.services.API.addLog( - `Failed to parse remote configuration '${config.id}': ${ex}`, + `Failed to parse remote configuration '${config.id}' for fetching settings!`, LOG_LEVEL_NOTICE ); + this.services.API.addLog(ex, LOG_LEVEL_VERBOSE); return; } const workSettings = createBaseRemoteSettings(); diff --git a/src/modules/features/SetupManager.ts b/src/modules/features/SetupManager.ts index 3d69968..02f19d6 100644 --- a/src/modules/features/SetupManager.ts +++ b/src/modules/features/SetupManager.ts @@ -227,7 +227,7 @@ export class SetupManager extends AbstractModule { const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, currentSetting); if (e2eeConf === "cancelled") { this._log("E2EE configuration cancelled.", LOG_LEVEL_NOTICE); - return await false; + return false; } const newSetting = { ...currentSetting, @@ -367,7 +367,7 @@ export class SetupManager extends AbstractModule { const qrResult = await this.dialogManager.open(ScanQRCode); this._log("QR Code dialog closed.", LOG_LEVEL_VERBOSE); // Result is not used, but log it for debugging. - this._log(`QR Code result: ${qrResult}`, LOG_LEVEL_VERBOSE); + this._log(qrResult, LOG_LEVEL_VERBOSE); // QR Code instruction dialog never yields settings directly. return false; } diff --git a/src/modules/main/ModuleLiveSyncMain.ts b/src/modules/main/ModuleLiveSyncMain.ts index d6d04bb..4be2f40 100644 --- a/src/modules/main/ModuleLiveSyncMain.ts +++ b/src/modules/main/ModuleLiveSyncMain.ts @@ -14,6 +14,7 @@ import type { InjectableServiceHub } from "@lib/services/implements/injectable/I import type { LiveSyncCore } from "../../main.ts"; import { initialiseWorkerModule } from "@lib/worker/bgWorker.ts"; import { manifestVersion, packageVersion } from "@lib/common/coreEnvVars.ts"; +import { compatGlobal } from "@lib/common/coreEnvFunctions.ts"; export class ModuleLiveSyncMain extends AbstractModule { async _onLiveSyncReady() { @@ -97,7 +98,7 @@ export class ModuleLiveSyncMain extends AbstractModule { return false; } const lsKey = "obsidian-live-sync-ver" + this.services.vault.getVaultName(); - const last_version = localStorage.getItem(lsKey); + const last_version = compatGlobal.localStorage.getItem(lsKey); const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000); if (lastVersion > this.settings.lastReadUpdates && this.settings.isConfigured) { @@ -119,7 +120,7 @@ export class ModuleLiveSyncMain extends AbstractModule { this.settings.versionUpFlash = $msg("moduleLiveSyncMain.logVersionUpdate"); await this.saveSettings(); } - localStorage.setItem(lsKey, `${VER}`); + compatGlobal.localStorage.setItem(lsKey, `${VER}`); await this.services.database.openDatabase({ databaseEvents: this.services.databaseEvents, replicator: this.services.replicator, @@ -129,7 +130,7 @@ export class ModuleLiveSyncMain extends AbstractModule { // this.$$replicate = this.$$replicate.bind(this); // this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this); await this.core.services.appLifecycle.onLoaded(); - await Promise.all(this.core.addOns.map((e) => e.onload())); + await Promise.all(this.core.addOns.map((e) => Promise.resolve(e.onload()))); return true; } diff --git a/src/modules/services/ObsidianSettingService.ts b/src/modules/services/ObsidianSettingService.ts index a9dfbb4..e684ea5 100644 --- a/src/modules/services/ObsidianSettingService.ts +++ b/src/modules/services/ObsidianSettingService.ts @@ -1,3 +1,4 @@ +import { compatGlobal } from "@/lib/src/common/coreEnvFunctions"; import { type ObsidianLiveSyncSettings } from "@lib/common/types"; import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED } from "@lib/events/coreEvents"; import { eventHub } from "@lib/hub/hub"; @@ -17,13 +18,16 @@ export class ObsidianSettingService extends Se }); } protected setItem(key: string, value: string) { - return localStorage.setItem(key, value); + // TODO: Implement nativeLocalStorage. + return compatGlobal.localStorage.setItem(key, value); } protected getItem(key: string): string { - return localStorage.getItem(key) ?? ""; + // TODO: Implement nativeLocalStorage. + return compatGlobal.localStorage.getItem(key) ?? ""; } protected deleteItem(key: string): void { - localStorage.removeItem(key); + // TODO: Implement nativeLocalStorage. + compatGlobal.localStorage.removeItem(key); } protected override async saveData(data: ObsidianLiveSyncSettings): Promise { diff --git a/src/serviceFeatures/redFlag.ts b/src/serviceFeatures/redFlag.ts index fa1dada..00af3b4 100644 --- a/src/serviceFeatures/redFlag.ts +++ b/src/serviceFeatures/redFlag.ts @@ -73,7 +73,7 @@ export function createFetchAllFlagHandler( return false; } const { vault, extra } = method; - const settings = await host.services.setting.currentSettings(); + const settings = await Promise.resolve(host.services.setting.currentSettings()); // If remote is MinIO, makeLocalChunkBeforeSync is not available. (because no-deduplication on sending). const makeLocalChunkBeforeSyncAvailable = settings.remoteType !== REMOTE_MINIO; const mapVaultStateToAction = { From c6697327d504663315d2141b1f41a14438655434 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 1 Jun 2026 06:20:33 +0100 Subject: [PATCH 18/30] Fixed typings Fixed wrong typing for serviceHub, svelte dialog --- src/lib | 2 +- src/modules/extras/devUtil/TestPaneView.ts | 5 +++++ src/modules/features/ModuleLog.ts | 7 ++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/lib b/src/lib index 2c6c1df..6f97753 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 2c6c1dfadb8d2128bae3dda2bf2d8dcf1c36470d +Subproject commit 6f977537f42bafc41f231a61379b613b22f1773b diff --git a/src/modules/extras/devUtil/TestPaneView.ts b/src/modules/extras/devUtil/TestPaneView.ts index 32e29d4..addbe88 100644 --- a/src/modules/extras/devUtil/TestPaneView.ts +++ b/src/modules/extras/devUtil/TestPaneView.ts @@ -3,6 +3,11 @@ import TestPaneComponent from "./TestPane.svelte"; import type ObsidianLiveSyncPlugin from "../../../main.ts"; import type { ModuleDev } from "../ModuleDev.ts"; export const VIEW_TYPE_TEST = "ols-pane-test"; +declare global { + interface LSEvents { + "debug-sync-status": string[]; + } +} //Log view export class TestPaneView extends ItemView { component?: TestPaneComponent; diff --git a/src/modules/features/ModuleLog.ts b/src/modules/features/ModuleLog.ts index 2fba758..81118d2 100644 --- a/src/modules/features/ModuleLog.ts +++ b/src/modules/features/ModuleLog.ts @@ -516,7 +516,12 @@ ${stringifyYaml(info)} let errorInfo = ""; if (message instanceof Error) { if (message instanceof LiveSyncError) { - errorInfo = `${message.cause?.name}:${message.cause?.message}\n[StackTrace]: ${message.stack}\n[CausedBy]: ${message.cause?.stack}`; + if (message.cause && message.cause instanceof Error) { + const causedError = message.cause; + errorInfo = `${causedError?.name}:${causedError?.message}\n[StackTrace]: ${message.stack}\n[CausedBy]: ${causedError?.stack}`; + } else { + errorInfo = `${message.name}:${message.message}\n[StackTrace]: ${message.stack}`; + } } else { const thisStack = new Error().stack; errorInfo = `${message.name}:${message.message}\n[StackTrace]: ${message.stack}\n[LogCallStack]: ${thisStack}`; From 5a280c791974083956072b5d68a9db5718ffa0d8 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 1 Jun 2026 10:41:48 +0100 Subject: [PATCH 19/30] (feat): Bulk database fetching is now work in progress. This feature is expected to speed up rebuilds and setups. (WIP) --- src/lib | 2 +- src/modules/extras/ModuleDev.ts | 80 +++++++++++++++++++++++++++++++-- updates.md | 7 +++ 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/lib b/src/lib index 6f97753..b143cf8 160000 --- a/src/lib +++ b/src/lib @@ -1 +1 @@ -Subproject commit 6f977537f42bafc41f231a61379b613b22f1773b +Subproject commit b143cf887bee591d298f6343fa1601fbc8024ab0 diff --git a/src/modules/extras/ModuleDev.ts b/src/modules/extras/ModuleDev.ts index ab48c31..45ccd26 100644 --- a/src/modules/extras/ModuleDev.ts +++ b/src/modules/extras/ModuleDev.ts @@ -1,17 +1,21 @@ import { delay, fireAndForget } from "octagonal-wheels/promises"; import { __onMissingTranslation } from "../../lib/src/common/i18n"; import { AbstractObsidianModule } from "../AbstractObsidianModule.ts"; -import { LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; +import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, Logger } from "octagonal-wheels/common/logger"; import { eventHub } from "../../common/events"; import { enableTestFunction } from "./devUtil/testUtils.ts"; import { TestPaneView, VIEW_TYPE_TEST } from "./devUtil/TestPaneView.ts"; import { writable } from "svelte/store"; -import type { FilePathWithPrefix } from "../../lib/src/common/types.ts"; +import type { CouchDBCredentials, FilePathWithPrefix } from "../../lib/src/common/types.ts"; import type { LiveSyncCore } from "../../main.ts"; - +import { getConfiguredFunctionsForEncryption } from "@/lib/src/pouchdb/encryption.ts"; +import { AuthorizationHeaderGenerator } from "@/lib/src/replication/httplib.ts"; +import { fetchChangesForInitialSync } from "@/lib/src/pouchdb/StreamingFetch.ts"; +import { PouchDB } from '@lib/pouchdb/pouchdb-browser.ts'; +import { sizeToHumanReadable } from "octagonal-wheels/number"; export class ModuleDev extends AbstractObsidianModule { _everyOnloadStart(): Promise { - __onMissingTranslation(() => {}); + __onMissingTranslation(() => { }); return Promise.resolve(true); } async onMissingTranslation(key: string): Promise { @@ -98,7 +102,75 @@ export class ModuleDev extends AbstractObsidianModule { }); return Promise.resolve(true); } + async _runBulkCopyTest() { + const settings = this.settings; + const dummyLocalDatabaseForDrop = new PouchDB("dummy-local"); + await dummyLocalDatabaseForDrop.destroy(); + const dummyLocalDatabase = new PouchDB("dummy-local"); + const replicator = await this.core.services.replicator.getNewReplicator(); + if (!replicator) { + return; + } + const salt = () => replicator.getReplicationPBKDF2Salt(settings); + const enc = getConfiguredFunctionsForEncryption(settings.passphrase, + false, + false, + salt, + settings.E2EEAlgorithm, + ); + + const auth = ( + settings.useJWT + ? { + jwtAlgorithm: settings.jwtAlgorithm, + jwtKey: settings.jwtKey, + jwtExpDuration: settings.jwtExpDuration, + jwtKid: settings.jwtKid, + jwtSub: settings.jwtSub, + type: "jwt", + } + : { + username: settings.couchDB_USER, + password: settings.couchDB_PASSWORD, + type: "basic", + } + ) satisfies CouchDBCredentials; + const authHeader = await (new AuthorizationHeaderGenerator().getAuthorizationHeader(auth)); + const remote = + settings.couchDB_URI.replace(/\/+$/, "") + + (settings.couchDB_DBNAME == "" ? "" : "/" + settings.couchDB_DBNAME); + // + const ret = fetchChangesForInitialSync( + dummyLocalDatabase, + remote, + authHeader, + enc.outgoing, + "0", + (progress) => { + Logger(`Initial sync progress: ${progress.totalValidFetched} / ${progress.docsToFetch} +Total bytes fetched: ${sizeToHumanReadable(progress.totalBytes)}`, + LOG_LEVEL_NOTICE, "fetch-init-progress" + ); + + } + ); + await ret; + + const allDocs = await dummyLocalDatabase.allDocs({ include_docs: false }); + Logger(`Bulk copy test completed. Total documents in local database: ${allDocs.total_rows}`, LOG_LEVEL_NOTICE, "fetch-init-complete"); + await dummyLocalDatabase.destroy(); + Logger(`Dummy local database has been destroyed after test.`, LOG_LEVEL_NOTICE); + } async _everyOnLayoutReady(): Promise { + + this.addCommand({ + "id": "bulk-copy-test", + "name": "(DEBUG) Bulk copy test", + "callback": async () => { + await this._runBulkCopyTest(); + } + }) + if (!this.settings.enableDebugTools) return Promise.resolve(true); // if (await this.core.storageAccess.isExistsIncludeHidden("_SHOWDIALOGAUTO.md")) { // void this.core.$$showView(VIEW_TYPE_TEST); diff --git a/updates.md b/updates.md index a81c82a..cd54957 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 + +### Under development + +- Bulk database fetching is now work in progress. This feature is expected to speed up rebuilds and setups. + Another feature that is needed is the ability to enforce a specific order during the initial comparison between the storage and the local database. + ## 0.25.70 25th May, 2026 From cd2bff5fc7b406007fa62b4ca0de951764aee13a Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Mon, 1 Jun 2026 11:18:23 +0100 Subject: [PATCH 20/30] Refactor types in svelte components. --- src/modules/features/SetupManager.ts | 36 +++--- .../dialogs/FetchEverything.svelte | 65 ++++------- .../features/SetupWizard/dialogs/Intro.svelte | 26 ++--- .../dialogs/OutroAskUserMode.svelte | 23 ++-- .../dialogs/OutroExistingUser.svelte | 19 ++- .../SetupWizard/dialogs/OutroNewUser.svelte | 9 +- .../dialogs/PanelCouchDBCheck.svelte | 6 +- .../dialogs/RebuildEverything.svelte | 30 ++--- .../SetupWizard/dialogs/ScanQRCode.svelte | 6 +- .../dialogs/SelectMethodExisting.svelte | 17 +-- .../dialogs/SelectMethodNewUser.svelte | 15 ++- .../SetupWizard/dialogs/SetupRemote.svelte | 17 +-- .../dialogs/SetupRemoteBucket.svelte | 11 +- .../dialogs/SetupRemoteCouchDB.svelte | 13 +-- .../dialogs/SetupRemoteE2EE.svelte | 12 +- .../SetupWizard/dialogs/SetupRemoteP2P.svelte | 6 +- .../SetupWizard/dialogs/UseSetupURI.svelte | 14 +-- .../SetupWizard/dialogs/setupDialogTypes.ts | 108 ++++++++++++++++++ src/serviceFeatures/redFlag.ts | 5 +- 19 files changed, 268 insertions(+), 170 deletions(-) create mode 100644 src/modules/features/SetupWizard/dialogs/setupDialogTypes.ts diff --git a/src/modules/features/SetupManager.ts b/src/modules/features/SetupManager.ts index 02f19d6..6bc2132 100644 --- a/src/modules/features/SetupManager.ts +++ b/src/modules/features/SetupManager.ts @@ -1,12 +1,16 @@ import { + type BucketSyncSetting, + type CouchDBConnection, + type EncryptionSettings, type ObsidianLiveSyncSettings, + type P2PSyncSetting, DEFAULT_SETTINGS, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, REMOTE_COUCHDB, REMOTE_MINIO, REMOTE_P2P, -} from "../../lib/src/common/types.ts"; +} from "@lib/common/types.ts"; import { isObjectDifferent } from "@lib/common/utils.ts"; import Intro from "./SetupWizard/dialogs/Intro.svelte"; import SelectMethodNewUser from "./SetupWizard/dialogs/SelectMethodNewUser.svelte"; @@ -21,9 +25,15 @@ import SetupRemoteCouchDB from "./SetupWizard/dialogs/SetupRemoteCouchDB.svelte" import SetupRemoteBucket from "./SetupWizard/dialogs/SetupRemoteBucket.svelte"; import SetupRemoteP2P from "./SetupWizard/dialogs/SetupRemoteP2P.svelte"; import SetupRemoteE2EE from "./SetupWizard/dialogs/SetupRemoteE2EE.svelte"; -import { decodeSettingsFromQRCodeData } from "../../lib/src/API/processSetting.ts"; +import { decodeSettingsFromQRCodeData } from "@lib/API/processSetting.ts"; import { AbstractModule } from "../AbstractModule.ts"; import { ConnectionStringParser } from "@lib/common/ConnectionString.ts"; +import type { + OutroAskUserModeResultType, OutroExistingUserResultType, OutroNewUserResultType, + ScanQRCodeResultType, SetupRemoteBucketResultType, SetupRemoteCouchDBResultType, + SetupRemoteE2EEResultType, SetupRemoteP2PResultType, + SetupRemoteResultType, UseSetupURIResultType +} from "./SetupWizard/dialogs/setupDialogTypes.ts"; /** * User modes for onboarding and setup @@ -118,7 +128,7 @@ export class SetupManager extends AbstractModule { * @returns Promise that resolves to true if onboarding completed successfully, false otherwise */ async onUseSetupURI(userMode: UserMode, setupURI: string = ""): Promise { - const newSetting = await this.dialogManager.openWithExplicitCancel(UseSetupURI, setupURI); + const newSetting = await this.dialogManager.openWithExplicitCancel(UseSetupURI, setupURI); if (newSetting === "cancelled") { this._log("Setup URI dialog cancelled.", LOG_LEVEL_NOTICE); return false; @@ -141,7 +151,7 @@ export class SetupManager extends AbstractModule { ): Promise { const originalSetting = JSON.parse(JSON.stringify(currentSetting)) as ObsidianLiveSyncSettings; const baseSetting = JSON.parse(JSON.stringify(originalSetting)) as ObsidianLiveSyncSettings; - const couchConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteCouchDB, originalSetting); + const couchConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteCouchDB, originalSetting); if (couchConf === "cancelled") { this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE); return await this.onOnboard(userMode); @@ -165,7 +175,7 @@ export class SetupManager extends AbstractModule { currentSetting: ObsidianLiveSyncSettings, activate = true ): Promise { - const bucketConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteBucket, currentSetting); + const bucketConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteBucket, currentSetting); if (bucketConf === "cancelled") { this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE); return await this.onOnboard(userMode); @@ -189,7 +199,7 @@ export class SetupManager extends AbstractModule { currentSetting: ObsidianLiveSyncSettings, activate = true ): Promise { - const p2pConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteP2P, currentSetting); + const p2pConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteP2P, currentSetting); if (p2pConf === "cancelled") { this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE); return await this.onOnboard(userMode); @@ -224,7 +234,7 @@ export class SetupManager extends AbstractModule { * @returns */ async onlyE2EEConfiguration(userMode: UserMode, currentSetting: ObsidianLiveSyncSettings): Promise { - const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, currentSetting); + const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, currentSetting); if (e2eeConf === "cancelled") { this._log("E2EE configuration cancelled.", LOG_LEVEL_NOTICE); return false; @@ -243,7 +253,7 @@ export class SetupManager extends AbstractModule { * @returns */ async onConfigureManually(originalSetting: ObsidianLiveSyncSettings, userMode: UserMode): Promise { - const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, originalSetting); + const e2eeConf = await this.dialogManager.openWithExplicitCancel(SetupRemoteE2EE, originalSetting); if (e2eeConf === "cancelled") { this._log("Manual configuration cancelled.", LOG_LEVEL_NOTICE); return await this.onOnboard(userMode); @@ -262,7 +272,7 @@ export class SetupManager extends AbstractModule { * @returns */ async onSelectServer(currentSetting: ObsidianLiveSyncSettings, userMode: UserMode): Promise { - const method = await this.dialogManager.openWithExplicitCancel(SetupRemote); + const method = await this.dialogManager.openWithExplicitCancel(SetupRemote); if (method === "couchdb") { return await this.onCouchDBManualSetup(userMode, currentSetting, true); } else if (method === "bucket") { @@ -290,7 +300,7 @@ export class SetupManager extends AbstractModule { newConf: ObsidianLiveSyncSettings, _userMode: UserMode, activate: boolean = true, - extra: () => void = () => {} + extra: () => void = () => { } ): Promise { newConf = await this.services.setting.adjustSettings({ ...this.settings, @@ -321,7 +331,7 @@ export class SetupManager extends AbstractModule { this._log("Settings from wizard applied.", LOG_LEVEL_NOTICE); return true; } else { - const userModeResult = await this.dialogManager.openWithExplicitCancel(OutroAskUserMode); + const userModeResult = await this.dialogManager.openWithExplicitCancel(OutroAskUserMode); if (userModeResult === "new-user") { userMode = UserMode.NewUser; } else if (userModeResult === "existing-user") { @@ -338,7 +348,7 @@ export class SetupManager extends AbstractModule { } } const component = userMode === UserMode.NewUser ? OutroNewUser : OutroExistingUser; - const confirm = await this.dialogManager.openWithExplicitCancel(component); + const confirm = await this.dialogManager.openWithExplicitCancel(component); if (confirm === "cancelled") { this._log("User cancelled applying settings from wizard..", LOG_LEVEL_NOTICE); return false; @@ -364,7 +374,7 @@ export class SetupManager extends AbstractModule { */ async onPromptQRCodeInstruction(): Promise { - const qrResult = await this.dialogManager.open(ScanQRCode); + const qrResult = await this.dialogManager.open(ScanQRCode); this._log("QR Code dialog closed.", LOG_LEVEL_VERBOSE); // Result is not used, but log it for debugging. this._log(qrResult, LOG_LEVEL_VERBOSE); diff --git a/src/modules/features/SetupWizard/dialogs/FetchEverything.svelte b/src/modules/features/SetupWizard/dialogs/FetchEverything.svelte index e704782..9bb06d7 100644 --- a/src/modules/features/SetupWizard/dialogs/FetchEverything.svelte +++ b/src/modules/features/SetupWizard/dialogs/FetchEverything.svelte @@ -1,47 +1,30 @@ diff --git a/src/modules/features/SetupWizard/dialogs/OutroNewUser.svelte b/src/modules/features/SetupWizard/dialogs/OutroNewUser.svelte index ff39c62..3818a29 100644 --- a/src/modules/features/SetupWizard/dialogs/OutroNewUser.svelte +++ b/src/modules/features/SetupWizard/dialogs/OutroNewUser.svelte @@ -5,14 +5,13 @@ import Question from "@/lib/src/UI/components/Question.svelte"; import Instruction from "@/lib/src/UI/components/Instruction.svelte"; import UserDecisions from "@/lib/src/UI/components/UserDecisions.svelte"; - const TYPE_APPLY = "apply"; - const TYPE_CANCELLED = "cancelled"; - type ResultType = typeof TYPE_APPLY | typeof TYPE_CANCELLED; + import { TYPE_APPLY, TYPE_CANCELLED, type OutroNewUserResultType } from "./setupDialogTypes"; + type Props = { - setResult: (result: ResultType) => void; + setResult: (result: OutroNewUserResultType) => void; }; const { setResult }: Props = $props(); - // let userType = $state(TYPE_CANCELLED); + // let userType = $state(TYPE_CANCELLED); diff --git a/src/modules/features/SetupWizard/dialogs/PanelCouchDBCheck.svelte b/src/modules/features/SetupWizard/dialogs/PanelCouchDBCheck.svelte index d2cef32..4ac0f85 100644 --- a/src/modules/features/SetupWizard/dialogs/PanelCouchDBCheck.svelte +++ b/src/modules/features/SetupWizard/dialogs/PanelCouchDBCheck.svelte @@ -2,9 +2,9 @@ /** * Panel to check and fix CouchDB configuration issues */ - import type { ObsidianLiveSyncSettings } from "../../../../lib/src/common/types"; - import Decision from "../../../../lib/src/UI/components/Decision.svelte"; - import UserDecisions from "../../../../lib/src/UI/components/UserDecisions.svelte"; + import type { ObsidianLiveSyncSettings } from "@lib/common/types"; + import Decision from "@lib/UI/components/Decision.svelte"; + import UserDecisions from "@lib/UI/components/UserDecisions.svelte"; import { checkConfig, type ConfigCheckResult, type ResultError, type ResultErrorMessage } from "./utilCheckCouchDB"; type Props = { trialRemoteSetting: ObsidianLiveSyncSettings; diff --git a/src/modules/features/SetupWizard/dialogs/RebuildEverything.svelte b/src/modules/features/SetupWizard/dialogs/RebuildEverything.svelte index 93aa834..1fe06e3 100644 --- a/src/modules/features/SetupWizard/dialogs/RebuildEverything.svelte +++ b/src/modules/features/SetupWizard/dialogs/RebuildEverything.svelte @@ -10,29 +10,17 @@ import InfoNote from "@/lib/src/UI/components/InfoNote.svelte"; import ExtraItems from "@/lib/src/UI/components/ExtraItems.svelte"; import Check from "@/lib/src/UI/components/Check.svelte"; - const TYPE_CANCEL = "cancelled"; + import { + TYPE_CANCEL, + TYPE_BACKUP_DONE, + TYPE_BACKUP_SKIPPED, + TYPE_UNABLE_TO_BACKUP, + type RebuildEverythingResult, + type ResultTypeBackup, + } from "./setupDialogTypes"; - const TYPE_BACKUP_DONE = "backup_done"; - const TYPE_BACKUP_SKIPPED = "backup_skipped"; - const TYPE_UNABLE_TO_BACKUP = "unable_to_backup"; - - type ResultTypeBackup = - | typeof TYPE_BACKUP_DONE - | typeof TYPE_BACKUP_SKIPPED - | typeof TYPE_UNABLE_TO_BACKUP - | typeof TYPE_CANCEL; - - type ResultTypeExtra = { - preventFetchingConfig: boolean; - }; - type ResultType = - | { - backup: ResultTypeBackup; - extra: ResultTypeExtra; - } - | typeof TYPE_CANCEL; type Props = { - setResult: (result: ResultType) => void; + setResult: (result: RebuildEverythingResult) => void; }; const { setResult }: Props = $props(); diff --git a/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte b/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte index 57c0621..4a24f46 100644 --- a/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte +++ b/src/modules/features/SetupWizard/dialogs/ScanQRCode.svelte @@ -4,10 +4,10 @@ import Decision from "@/lib/src/UI/components/Decision.svelte"; import Instruction from "@/lib/src/UI/components/Instruction.svelte"; import UserDecisions from "@/lib/src/UI/components/UserDecisions.svelte"; - const TYPE_CLOSE = "close"; - type ResultType = typeof TYPE_CLOSE; + import { TYPE_CLOSE, type ScanQRCodeResultType } from "./setupDialogTypes"; + type Props = { - setResult: (_result: ResultType) => void; + setResult: (_result: ScanQRCodeResultType) => void; }; const { setResult }: Props = $props(); diff --git a/src/modules/features/SetupWizard/dialogs/SelectMethodExisting.svelte b/src/modules/features/SetupWizard/dialogs/SelectMethodExisting.svelte index 8c72ffb..982d60c 100644 --- a/src/modules/features/SetupWizard/dialogs/SelectMethodExisting.svelte +++ b/src/modules/features/SetupWizard/dialogs/SelectMethodExisting.svelte @@ -7,16 +7,19 @@ import Options from "@/lib/src/UI/components/Options.svelte"; import Instruction from "@/lib/src/UI/components/Instruction.svelte"; import UserDecisions from "@/lib/src/UI/components/UserDecisions.svelte"; - const TYPE_USE_SETUP_URI = "use-setup-uri"; - const TYPE_SCAN_QR_CODE = "scan-qr-code"; - const TYPE_CONFIGURE_MANUALLY = "configure-manually"; - const TYPE_CANCELLED = "cancelled"; - type ResultType = typeof TYPE_USE_SETUP_URI | typeof TYPE_SCAN_QR_CODE | typeof TYPE_CONFIGURE_MANUALLY | typeof TYPE_CANCELLED; + import { + TYPE_USE_SETUP_URI, + TYPE_SCAN_QR_CODE, + TYPE_CONFIGURE_MANUALLY, + TYPE_CANCELLED, + type SelectMethodExistingResultType, + } from "./setupDialogTypes"; + type Props = { - setResult: (result: ResultType) => void; + setResult: (result: SelectMethodExistingResultType) => void; }; const { setResult }: Props = $props(); - let userType = $state(TYPE_CANCELLED); + let userType = $state(TYPE_CANCELLED); let proceedTitle = $derived.by(() => { if (userType === TYPE_USE_SETUP_URI) { return "Proceed with Setup URI"; diff --git a/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte b/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte index 88a6aed..36e90fc 100644 --- a/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte +++ b/src/modules/features/SetupWizard/dialogs/SelectMethodNewUser.svelte @@ -7,15 +7,18 @@ import Options from "@/lib/src/UI/components/Options.svelte"; import Instruction from "@/lib/src/UI/components/Instruction.svelte"; import UserDecisions from "@/lib/src/UI/components/UserDecisions.svelte"; - const TYPE_USE_SETUP_URI = "use-setup-uri"; - const TYPE_CONFIGURE_MANUALLY = "configure-manually"; - const TYPE_CANCELLED = "cancelled"; - type ResultType = typeof TYPE_USE_SETUP_URI | typeof TYPE_CONFIGURE_MANUALLY | typeof TYPE_CANCELLED; + import { + TYPE_USE_SETUP_URI, + TYPE_CONFIGURE_MANUALLY, + TYPE_CANCELLED, + type SelectMethodNewUserResultType, + } from "./setupDialogTypes"; + type Props = { - setResult: (result: ResultType) => void; + setResult: (result: SelectMethodNewUserResultType) => void; }; const { setResult }: Props = $props(); - let userType = $state(TYPE_CANCELLED); + let userType = $state(TYPE_CANCELLED); let proceedTitle = $derived.by(() => { if (userType === TYPE_USE_SETUP_URI) { return "Proceed with Setup URI"; diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemote.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemote.svelte index adcb87a..365f117 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemote.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemote.svelte @@ -6,16 +6,19 @@ import Options from "@/lib/src/UI/components/Options.svelte"; import Instruction from "@/lib/src/UI/components/Instruction.svelte"; import UserDecisions from "@/lib/src/UI/components/UserDecisions.svelte"; - const TYPE_COUCHDB = "couchdb"; - const TYPE_BUCKET = "bucket"; - const TYPE_P2P = "p2p"; - const TYPE_CANCELLED = "cancelled"; - type ResultType = typeof TYPE_COUCHDB | typeof TYPE_BUCKET | typeof TYPE_P2P | typeof TYPE_CANCELLED; + import { + TYPE_COUCHDB, + TYPE_BUCKET, + TYPE_P2P, + TYPE_CANCELLED, + type SetupRemoteResultType, + } from "./setupDialogTypes"; + type Props = { - setResult: (result: ResultType) => void; + setResult: (result: SetupRemoteResultType) => void; }; const { setResult }: Props = $props(); - let userType = $state(TYPE_CANCELLED); + let userType = $state(TYPE_CANCELLED); let proceedTitle = $derived.by(() => { if (userType === TYPE_COUCHDB) { return "Continue to CouchDB setup"; diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteBucket.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteBucket.svelte index f2270dc..7c04c38 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemoteBucket.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteBucket.svelte @@ -13,19 +13,18 @@ DEFAULT_SETTINGS, PREFERRED_JOURNAL_SYNC, RemoteTypes, - } from "../../../../lib/src/common/types"; + } from "@lib/common/types"; import { onMount } from "svelte"; - import { getDialogContext, type GuestDialogProps } from "../../../../lib/src/UI/svelteDialog"; - import { copyTo, pickBucketSyncSettings } from "../../../../lib/src/common/utils"; + import { getDialogContext, type GuestDialogProps } from "@lib/UI/svelteDialog"; + import { copyTo, pickBucketSyncSettings } from "@lib/common/utils"; + import { TYPE_CANCELLED, type SetupRemoteBucketResultType } from "./setupDialogTypes"; const default_setting = pickBucketSyncSettings(DEFAULT_SETTINGS); let syncSetting = $state({ ...default_setting }); - type ResultType = typeof TYPE_CANCELLED | BucketSyncSetting; - type Props = GuestDialogProps; - const TYPE_CANCELLED = "cancelled"; + type Props = GuestDialogProps; const { setResult, getInitialData }: Props = $props(); diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte index 671af71..be18b75 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte @@ -14,20 +14,19 @@ RemoteTypes, type CouchDBConnection, type ObsidianLiveSyncSettings, - } from "../../../../lib/src/common/types"; - import { isCloudantURI } from "../../../../lib/src/pouchdb/utils_couchdb"; + } from "@lib/common/types"; + import { isCloudantURI } from "@lib/pouchdb/utils_couchdb"; import { onMount } from "svelte"; - import { getDialogContext, type GuestDialogProps } from "../../../../lib/src/UI/svelteDialog"; - import { copyTo, pickCouchDBSyncSettings } from "../../../../lib/src/common/utils"; + import { getDialogContext, type GuestDialogProps } from "@lib/UI/svelteDialog"; + import { copyTo, pickCouchDBSyncSettings } from "@lib/common/utils"; import PanelCouchDBCheck from "./PanelCouchDBCheck.svelte"; + import { TYPE_CANCELLED, type SetupRemoteCouchDBResultType } from "./setupDialogTypes"; const default_setting = pickCouchDBSyncSettings(DEFAULT_SETTINGS); let syncSetting = $state({ ...default_setting }); - type ResultType = typeof TYPE_CANCELLED | CouchDBConnection; - const TYPE_CANCELLED = "cancelled"; - type Props = GuestDialogProps; + type Props = GuestDialogProps; const { setResult, getInitialData }: Props = $props(); onMount(() => { if (getInitialData) { diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteE2EE.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteE2EE.svelte index 052e97d..f3c40ed 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemoteE2EE.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteE2EE.svelte @@ -12,13 +12,13 @@ E2EEAlgorithmNames, E2EEAlgorithms, type EncryptionSettings, - } from "../../../../lib/src/common/types"; + } from "@lib/common/types"; import { onMount } from "svelte"; - import type { GuestDialogProps } from "../../../../lib/src/UI/svelteDialog"; - import { copyTo, pickEncryptionSettings } from "../../../../lib/src/common/utils"; - const TYPE_CANCELLED = "cancelled"; - type ResultType = typeof TYPE_CANCELLED | EncryptionSettings; - type Props = GuestDialogProps; + import type { GuestDialogProps } from "@lib/UI/svelteDialog"; + import { copyTo, pickEncryptionSettings } from "@lib/common/utils"; + import { TYPE_CANCELLED, type SetupRemoteE2EEResultType } from "./setupDialogTypes"; + + type Props = GuestDialogProps; const { setResult, getInitialData }: Props = $props(); let default_encryption: EncryptionSettings = { encrypt: true, diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte index cd2020e..4e1067a 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteP2P.svelte @@ -26,16 +26,14 @@ import { getDialogContext, type GuestDialogProps } from "@lib/UI/svelteDialog"; import { SETTING_KEY_P2P_DEVICE_NAME } from "@lib/common/types"; import ExtraItems from "@lib/UI/components/ExtraItems.svelte"; + import { TYPE_CANCELLED, type SetupRemoteP2PResultType } from "./setupDialogTypes"; const default_setting = pickP2PSyncSettings(DEFAULT_SETTINGS); let syncSetting = $state({ ...default_setting }); const context = getDialogContext(); let error = $state(""); - const TYPE_CANCELLED = "cancelled"; - type SettingInfo = P2PConnectionInfo; - type ResultType = typeof TYPE_CANCELLED | SettingInfo; - type Props = GuestDialogProps; + type Props = GuestDialogProps; const { setResult, getInitialData }: Props = $props(); onMount(() => { diff --git a/src/modules/features/SetupWizard/dialogs/UseSetupURI.svelte b/src/modules/features/SetupWizard/dialogs/UseSetupURI.svelte index d6152b7..d1ed1ac 100644 --- a/src/modules/features/SetupWizard/dialogs/UseSetupURI.svelte +++ b/src/modules/features/SetupWizard/dialogs/UseSetupURI.svelte @@ -1,6 +1,6 @@