diff --git a/src/apps/cli/commands/p2p.ts b/src/apps/cli/commands/p2p.ts index da83244..2ba76dd 100644 --- a/src/apps/cli/commands/p2p.ts +++ b/src/apps/cli/commands/p2p.ts @@ -3,6 +3,7 @@ import { P2P_DEFAULT_SETTINGS } from "@lib/common/types"; import type { ServiceContext } from "@lib/services/base/ServiceBase"; import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator"; import { compatGlobal } from "@lib/common/coreEnvFunctions.ts"; +import { LiveSyncError } from "@lib/common/LSError"; type CLIP2PPeer = { peerId: string; @@ -107,11 +108,14 @@ export async function syncWithPeer( const pullResult = await replicator.replicateFrom(targetPeer.peerId, false); if (pullResult && "error" in pullResult && pullResult.error) { - throw pullResult.error; + throw pullResult.error instanceof Error ? pullResult.error : LiveSyncError.fromError(pullResult.error); } const pushResult = await replicator.requestSynchroniseToPeer(targetPeer.peerId); if (!pushResult || pushResult.ok !== true) { - throw pushResult?.error ?? new Error("P2P sync failed while requesting remote sync"); + const err = pushResult?.error; + throw err instanceof Error + ? err + : LiveSyncError.fromError(err ?? "P2P sync failed while requesting remote sync"); } return targetPeer; diff --git a/src/apps/cli/commands/runCommand.ts b/src/apps/cli/commands/runCommand.ts index 8614b9e..3468d6b 100644 --- a/src/apps/cli/commands/runCommand.ts +++ b/src/apps/cli/commands/runCommand.ts @@ -159,9 +159,13 @@ export async function runCommand(options: CLIOptions, context: CLICommandContext ); } } - pollTimer = compatGlobal.setTimeout(poll, currentIntervalMs); + pollTimer = compatGlobal.setTimeout(() => { + void poll(); + }, currentIntervalMs); }; - let pollTimer = compatGlobal.setTimeout(poll, currentIntervalMs); + let pollTimer = compatGlobal.setTimeout(() => { + void poll(); + }, currentIntervalMs); core.services.appLifecycle.onUnload.addHandler(async () => { compatGlobal.clearTimeout(pollTimer); return true; diff --git a/src/apps/cli/entrypoint.ts b/src/apps/cli/entrypoint.ts index 0e8de12..5c9ce7f 100644 --- a/src/apps/cli/entrypoint.ts +++ b/src/apps/cli/entrypoint.ts @@ -2,13 +2,16 @@ // eslint-disable -- This is the entry point for the CLI application. import * as polyfill from "werift"; import { main } from "./main"; +import { compatGlobal } from "@lib/common/coreEnvFunctions"; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Polyfill +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Polyfill const rtcPolyfillCtor = (polyfill as any).RTCPeerConnection; -if (typeof (global as any).RTCPeerConnection === "undefined" && typeof rtcPolyfillCtor === "function") { +if ( + typeof (compatGlobal as unknown as Record).RTCPeerConnection === "undefined" && + typeof rtcPolyfillCtor === "function" +) { // Fill only the standard WebRTC global in Node CLI runtime. - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Polyfill - (global as any).RTCPeerConnection = rtcPolyfillCtor; + (compatGlobal as unknown as Record).RTCPeerConnection = rtcPolyfillCtor; } main().catch((error) => { diff --git a/src/apps/cli/services/NodeLocalStorage.ts b/src/apps/cli/services/NodeLocalStorage.ts index 5a2bb45..038ad78 100644 --- a/src/apps/cli/services/NodeLocalStorage.ts +++ b/src/apps/cli/services/NodeLocalStorage.ts @@ -1,4 +1,5 @@ import { fs as nodeFs, path as nodePath } from "@/apps/cli/node-compat"; +import { compatGlobal } from "@lib/common/coreEnvFunctions"; type LocalStorageShape = { getItem(key: string): string | null; @@ -83,10 +84,12 @@ function createNodeLocalStorageShim(): LocalStorageShape { } export function ensureGlobalNodeLocalStorage() { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Node.js global object manipulation - if (!("localStorage" in global) || typeof (global as any).localStorage?.getItem !== "function") { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Node.js global object access - (global as any).localStorage = createNodeLocalStorageShim(); + const g = compatGlobal as unknown as Record; + if ( + !("localStorage" in g) || + typeof (g.localStorage as Record | undefined)?.getItem !== "function" + ) { + g.localStorage = createNodeLocalStorageShim(); } } diff --git a/src/apps/webapp/adapters/FSAPIConversionAdapter.ts b/src/apps/webapp/adapters/FSAPIConversionAdapter.ts index f0136ff..ee40dca 100644 --- a/src/apps/webapp/adapters/FSAPIConversionAdapter.ts +++ b/src/apps/webapp/adapters/FSAPIConversionAdapter.ts @@ -1,4 +1,4 @@ -import type { UXFileInfoStub, UXFolderInfo } from "@lib/common/types"; +import type { FilePath, UXFileInfoStub, UXFolderInfo } from "@lib/common/types"; import type { IConversionAdapter } from "@lib/serviceModules/adapters"; import type { FSAPIFile, FSAPIFolder } from "./FSAPITypes"; @@ -28,7 +28,7 @@ export class FSAPIConversionAdapter implements IConversionAdapter; + } + ).entries()) { const entryPath = relativePath ? `${relativePath}/${name}` : name; if (entry.kind === "directory") { diff --git a/src/apps/webapp/adapters/FSAPIStorageAdapter.ts b/src/apps/webapp/adapters/FSAPIStorageAdapter.ts index 6767410..4cd79d6 100644 --- a/src/apps/webapp/adapters/FSAPIStorageAdapter.ts +++ b/src/apps/webapp/adapters/FSAPIStorageAdapter.ts @@ -195,7 +195,11 @@ export class FSAPIStorageAdapter implements IStorageAdapter { const folders: string[] = []; // Use AsyncIterator instead of .values() for better compatibility - for await (const [name, entry] of (dirHandle as any).entries()) { + for await (const [name, entry] of ( + dirHandle as unknown as { + entries(): AsyncIterable<[string, FileSystemHandle]>; + } + ).entries()) { const entryPath = basePath ? `${basePath}/${name}` : name; if (entry.kind === "directory") { diff --git a/src/apps/webapp/adapters/FSAPITypeGuardAdapter.ts b/src/apps/webapp/adapters/FSAPITypeGuardAdapter.ts index 06fa9b1..a8d1b8d 100644 --- a/src/apps/webapp/adapters/FSAPITypeGuardAdapter.ts +++ b/src/apps/webapp/adapters/FSAPITypeGuardAdapter.ts @@ -5,13 +5,24 @@ import type { FSAPIFile, FSAPIFolder } from "./FSAPITypes"; * Type guard adapter implementation for FileSystem API */ export class FSAPITypeGuardAdapter implements ITypeGuardAdapter { - isFile(file: any): file is FSAPIFile { - return ( - file && typeof file === "object" && "path" in file && "stat" in file && "handle" in file && !file.isFolder + isFile(file: unknown): file is FSAPIFile { + return !!( + file && + typeof file === "object" && + "path" in file && + "stat" in file && + "handle" in file && + !(file as { isFolder?: boolean }).isFolder ); } - isFolder(item: any): item is FSAPIFolder { - return item && typeof item === "object" && "path" in item && item.isFolder === true && "handle" in item; + isFolder(item: unknown): item is FSAPIFolder { + return !!( + item && + typeof item === "object" && + "path" in item && + (item as { isFolder?: boolean }).isFolder === true && + "handle" in item + ); } } diff --git a/src/apps/webapp/adapters/FSAPIVaultAdapter.ts b/src/apps/webapp/adapters/FSAPIVaultAdapter.ts index acc6ebd..4f548ca 100644 --- a/src/apps/webapp/adapters/FSAPIVaultAdapter.ts +++ b/src/apps/webapp/adapters/FSAPIVaultAdapter.ts @@ -116,8 +116,7 @@ export class FSAPIVaultAdapter implements IVaultAdapter { await this.delete(file, force); } - trigger(name: string, ...data: any[]): any { + trigger(name: string, ...data: unknown[]): void { // No-op in webapp version (no event system yet) - return undefined; } } diff --git a/src/apps/webapp/bootstrap.ts b/src/apps/webapp/bootstrap.ts index 15e18cb..41d871c 100644 --- a/src/apps/webapp/bootstrap.ts +++ b/src/apps/webapp/bootstrap.ts @@ -121,13 +121,11 @@ async function initializeVaultSelector(): Promise { await renderHistoryList(); } -compatGlobal.addEventListener("load", async () => { - try { - await initializeVaultSelector(); - } catch (error) { +compatGlobal.addEventListener("load", () => { + initializeVaultSelector().catch((error) => { console.error("Failed to initialize vault selector:", error); setStatus("error", `Initialization failed: ${String(error)}`); - } + }); }); compatGlobal.addEventListener("beforeunload", () => { diff --git a/src/apps/webapp/main.ts b/src/apps/webapp/main.ts index f53861a..46589ee 100644 --- a/src/apps/webapp/main.ts +++ b/src/apps/webapp/main.ts @@ -97,13 +97,15 @@ class LiveSyncWebApp { }); // App lifecycle handlers - this.serviceHub.appLifecycle.scheduleRestart.setHandler(async () => { - console.log("[AppLifecycle] Restart requested"); - await this.shutdown(); - await this.initialize(); - compatGlobal.setTimeout(() => { - compatGlobal.location.reload(); - }, 1000); + this.serviceHub.appLifecycle.scheduleRestart.setHandler(() => { + void (async () => { + console.log("[AppLifecycle] Restart requested"); + await this.shutdown(); + await this.initialize(); + compatGlobal.setTimeout(() => { + compatGlobal.location.reload(); + }, 1000); + })(); }); // Create LiveSync core diff --git a/src/apps/webapp/managers/FSAPIStorageEventManagerAdapter.ts b/src/apps/webapp/managers/FSAPIStorageEventManagerAdapter.ts index 7ae81fb..9a5e99c 100644 --- a/src/apps/webapp/managers/FSAPIStorageEventManagerAdapter.ts +++ b/src/apps/webapp/managers/FSAPIStorageEventManagerAdapter.ts @@ -207,9 +207,9 @@ class FSAPIWatchAdapter implements IStorageEventWatchAdapter { }; if (type === "appeared") { - await handlers.onCreate(fileInfo, undefined); + handlers.onCreate(fileInfo, undefined); } else { - await handlers.onChange(fileInfo, undefined); + handlers.onChange(fileInfo, undefined); } } } else if (type === "disappeared") { @@ -223,7 +223,7 @@ class FSAPIWatchAdapter implements IStorageEventWatchAdapter { }, handle: null as unknown as FileSystemFileHandle, // No handle available for disappeared files }; - await handlers.onDelete(fileInfo, undefined); + handlers.onDelete(fileInfo, undefined); } else if (type === "moved") { // Handle as delete + create // Note: FileSystemObserver provides both old and new paths in some cases @@ -240,7 +240,7 @@ class FSAPIWatchAdapter implements IStorageEventWatchAdapter { }, handle: changedHandle, }; - await handlers.onChange(fileInfo, undefined); + handlers.onChange(fileInfo, undefined); } } } catch (error) { diff --git a/src/apps/webapp/test-entry.ts b/src/apps/webapp/test-entry.ts index ab70ee2..ec47e69 100644 --- a/src/apps/webapp/test-entry.ts +++ b/src/apps/webapp/test-entry.ts @@ -28,11 +28,38 @@ function stripPrefix(raw: string): string { return raw.replace(/^[^:]+:/, ""); } +interface TestCore { + services?: { + replication?: { + databaseQueueCount?: { value: number }; + storageApplyingCount?: { value: number }; + }; + fileProcessing?: { + totalQueued?: { value: number }; + batched?: { value: number }; + processing?: { value: number }; + }; + database?: { + localDatabase: { + findAllNormalDocs(options?: { conflicts?: boolean }): AsyncIterable<{ + _deleted?: boolean; + deleted?: boolean; + path?: string; + _rev?: string; + _conflicts?: string[]; + size?: number; + mtime?: number; + }>; + }; + }; + }; +} + /** * Poll every 300 ms until all known processing queues are drained, or until * the timeout elapses. Mirrors `waitForIdle` in the existing vitest harness. */ -async function waitForIdle(core: any, timeoutMs = 60_000): Promise { +async function waitForIdle(core: TestCore, timeoutMs = 60_000): Promise { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const q = @@ -47,8 +74,8 @@ async function waitForIdle(core: any, timeoutMs = 60_000): Promise { throw new Error(`waitForIdle timed out after ${timeoutMs} ms`); } -function getCore(): any { - const core = (app as any)?.core; +function getCore(): TestCore { + const core = (app as unknown as { core: TestCore | null })?.core; if (!core) throw new Error("Vault not initialised – call livesyncTest.init() first"); return core; } @@ -201,4 +228,4 @@ const livesyncTest: LiveSyncTestAPI = { }; // Expose on window for Playwright page.evaluate() calls. -(compatGlobal as any).livesyncTest = livesyncTest; +(compatGlobal as unknown as Record).livesyncTest = livesyncTest; diff --git a/src/apps/webapp/vaultSelector.ts b/src/apps/webapp/vaultSelector.ts index 104e29c..35fc4f4 100644 --- a/src/apps/webapp/vaultSelector.ts +++ b/src/apps/webapp/vaultSelector.ts @@ -32,7 +32,10 @@ function randomId(): string { } async function hasReadWritePermission(handle: FileSystemDirectoryHandle, requestIfNeeded: boolean): Promise { - const h = handle as any; + const h = handle as unknown as { + queryPermission?: (options: { mode: "readwrite" }) => Promise; + requestPermission?: (options: { mode: "readwrite" }) => Promise; + }; if (typeof h.queryPermission === "function") { const queried = await h.queryPermission({ mode: "readwrite" }); if (queried === "granted") { @@ -172,15 +175,17 @@ export class VaultHistoryStore { } async pickNewVault(): Promise { - const picker = (compatGlobal as any).showDirectoryPicker; + const picker = (compatGlobal as unknown as Record).showDirectoryPicker as + | ((options?: { mode?: "readwrite" | "read"; startIn?: string }) => Promise) + | undefined; if (typeof picker !== "function") { throw new Error("FileSystem API showDirectoryPicker is not supported in this browser"); } - const handle = (await picker({ + const handle = await picker({ mode: "readwrite", startIn: "documents", - })) as FileSystemDirectoryHandle; + }); const granted = await hasReadWritePermission(handle, true); if (!granted) { diff --git a/src/apps/webpeer/src/P2PReplicatorShim.ts b/src/apps/webpeer/src/P2PReplicatorShim.ts index 7c070ab..a4d8d62 100644 --- a/src/apps/webpeer/src/P2PReplicatorShim.ts +++ b/src/apps/webpeer/src/P2PReplicatorShim.ts @@ -82,8 +82,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase { replicator, p2pLogCollector, storeP2PStatusLine: p2pStatusLine, - // eslint-disable-next-line @typescript-eslint/no-unused-vars -- hacky way. //TODO: check it later - } = useP2PReplicator({ services: this.services } as any); + } = useP2PReplicator({ services: this.services } as unknown as Parameters[0]); this._liveSyncReplicator = replicator; this.p2pLogCollector = p2pLogCollector; p2pLogCollector.p2pReplicationLine.onChanged((line) => { @@ -101,7 +100,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase { const repStore = SimpleStoreIDBv2.open("p2p-livesync-web-peer"); this._simpleStore = repStore; let _settings = { ...P2P_DEFAULT_SETTINGS, additionalSuffixOfDatabaseName: "" } as ObsidianLiveSyncSettings; - this.services.setting.settings = _settings as ObsidianLiveSyncSettings; + this.services.setting.settings = _settings; (this.services.setting as InjectableSettingService).saveData.setHandler(async (data) => { await repStore.set("settings", data); eventHub.emitEvent(EVENT_SETTING_SAVED, data); @@ -281,7 +280,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase { } await this.services.setting.applyExternalSettings(remoteConfig, true); if (yn !== DROP) { - await this.plugin.core.services.appLifecycle.scheduleRestart(); + this.plugin.core.services.appLifecycle.scheduleRestart(); } } else { Logger(`Cancelled\nRemote config for ${peer.name} is not applied`, LOG_LEVEL_NOTICE);