Fix more typings

This commit is contained in:
vorotamoroz committed 2026-06-19 08:43:36 +01:00
1 parent 42954fcf68
commit 874164ecf5
15 files changed
+114 -51

No files matched your search

+6 -2
View File
@@ -3,6 +3,7 @@ import { P2P_DEFAULT_SETTINGS } from "@lib/common/types";
import type { ServiceContext } from "@lib/services/base/ServiceBase"; import type { ServiceContext } from "@lib/services/base/ServiceBase";
import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator"; import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts"; import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
import { LiveSyncError } from "@lib/common/LSError";
type CLIP2PPeer = { type CLIP2PPeer = {
peerId: string; peerId: string;
@@ -107,11 +108,14 @@ export async function syncWithPeer(
const pullResult = await replicator.replicateFrom(targetPeer.peerId, false); const pullResult = await replicator.replicateFrom(targetPeer.peerId, false);
if (pullResult && "error" in pullResult && pullResult.error) { 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); const pushResult = await replicator.requestSynchroniseToPeer(targetPeer.peerId);
if (!pushResult || pushResult.ok !== true) { 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; return targetPeer;
+6 -2
View File
@@ -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 () => { core.services.appLifecycle.onUnload.addHandler(async () => {
compatGlobal.clearTimeout(pollTimer); compatGlobal.clearTimeout(pollTimer);
return true; return true;
+7 -4
View File
@@ -2,13 +2,16 @@
// eslint-disable -- This is the entry point for the CLI application. // eslint-disable -- This is the entry point for the CLI application.
import * as polyfill from "werift"; import * as polyfill from "werift";
import { main } from "./main"; 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; const rtcPolyfillCtor = (polyfill as any).RTCPeerConnection;
if (typeof (global as any).RTCPeerConnection === "undefined" && typeof rtcPolyfillCtor === "function") { if (
typeof (compatGlobal as unknown as Record<string, unknown>).RTCPeerConnection === "undefined" &&
typeof rtcPolyfillCtor === "function"
) {
// Fill only the standard WebRTC global in Node CLI runtime. // Fill only the standard WebRTC global in Node CLI runtime.
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Polyfill (compatGlobal as unknown as Record<string, unknown>).RTCPeerConnection = rtcPolyfillCtor;
(global as any).RTCPeerConnection = rtcPolyfillCtor;
} }
main().catch((error) => { main().catch((error) => {
+7 -4
View File
@@ -1,4 +1,5 @@
import { fs as nodeFs, path as nodePath } from "@/apps/cli/node-compat"; import { fs as nodeFs, path as nodePath } from "@/apps/cli/node-compat";
import { compatGlobal } from "@lib/common/coreEnvFunctions";
type LocalStorageShape = { type LocalStorageShape = {
getItem(key: string): string | null; getItem(key: string): string | null;
@@ -83,10 +84,12 @@ function createNodeLocalStorageShim(): LocalStorageShape {
} }
export function ensureGlobalNodeLocalStorage() { export function ensureGlobalNodeLocalStorage() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Node.js global object manipulation const g = compatGlobal as unknown as Record<string, unknown>;
if (!("localStorage" in global) || typeof (global as any).localStorage?.getItem !== "function") { if (
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Node.js global object access !("localStorage" in g) ||
(global as any).localStorage = createNodeLocalStorageShim(); typeof (g.localStorage as Record<string, unknown> | undefined)?.getItem !== "function"
) {
g.localStorage = createNodeLocalStorageShim();
} }
} }
@@ -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 { IConversionAdapter } from "@lib/serviceModules/adapters";
import type { FSAPIFile, FSAPIFolder } from "./FSAPITypes"; import type { FSAPIFile, FSAPIFolder } from "./FSAPITypes";
@@ -28,7 +28,7 @@ export class FSAPIConversionAdapter implements IConversionAdapter<FSAPIFile, FSA
path: folder.path, path: folder.path,
isFolder: true, isFolder: true,
children: [], children: [],
parent: parentPath as any, parent: parentPath as FilePath,
}; };
} }
} }
@@ -173,7 +173,11 @@ export class FSAPIFileSystemAdapter implements IFileSystemAdapter<FSAPIFile, FSA
} }
// Use AsyncIterator instead of .values() for better compatibility // Use AsyncIterator instead of .values() for better compatibility
for await (const [name, entry] of (currentHandle as any).entries()) { for await (const [name, entry] of (
currentHandle as unknown as {
entries(): AsyncIterable<[string, FileSystemHandle]>;
}
).entries()) {
const entryPath = relativePath ? `${relativePath}/${name}` : name; const entryPath = relativePath ? `${relativePath}/${name}` : name;
if (entry.kind === "directory") { if (entry.kind === "directory") {
@@ -195,7 +195,11 @@ export class FSAPIStorageAdapter implements IStorageAdapter<FSAPIStat> {
const folders: string[] = []; const folders: string[] = [];
// Use AsyncIterator instead of .values() for better compatibility // 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; const entryPath = basePath ? `${basePath}/${name}` : name;
if (entry.kind === "directory") { if (entry.kind === "directory") {
@@ -5,13 +5,24 @@ import type { FSAPIFile, FSAPIFolder } from "./FSAPITypes";
* Type guard adapter implementation for FileSystem API * Type guard adapter implementation for FileSystem API
*/ */
export class FSAPITypeGuardAdapter implements ITypeGuardAdapter<FSAPIFile, FSAPIFolder> { export class FSAPITypeGuardAdapter implements ITypeGuardAdapter<FSAPIFile, FSAPIFolder> {
isFile(file: any): file is FSAPIFile { isFile(file: unknown): file is FSAPIFile {
return ( return !!(
file && typeof file === "object" && "path" in file && "stat" in file && "handle" in file && !file.isFolder file &&
typeof file === "object" &&
"path" in file &&
"stat" in file &&
"handle" in file &&
!(file as { isFolder?: boolean }).isFolder
); );
} }
isFolder(item: any): item is FSAPIFolder { isFolder(item: unknown): item is FSAPIFolder {
return item && typeof item === "object" && "path" in item && item.isFolder === true && "handle" in item; return !!(
item &&
typeof item === "object" &&
"path" in item &&
(item as { isFolder?: boolean }).isFolder === true &&
"handle" in item
);
} }
} }
@@ -116,8 +116,7 @@ export class FSAPIVaultAdapter implements IVaultAdapter<FSAPIFile> {
await this.delete(file, force); 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) // No-op in webapp version (no event system yet)
return undefined;
} }
} }
+3 -5
View File
@@ -121,13 +121,11 @@ async function initializeVaultSelector(): Promise<void> {
await renderHistoryList(); await renderHistoryList();
} }
compatGlobal.addEventListener("load", async () => { compatGlobal.addEventListener("load", () => {
try { initializeVaultSelector().catch((error) => {
await initializeVaultSelector();
} catch (error) {
console.error("Failed to initialize vault selector:", error); console.error("Failed to initialize vault selector:", error);
setStatus("error", `Initialization failed: ${String(error)}`); setStatus("error", `Initialization failed: ${String(error)}`);
} });
}); });
compatGlobal.addEventListener("beforeunload", () => { compatGlobal.addEventListener("beforeunload", () => {
+9 -7
View File
@@ -97,13 +97,15 @@ class LiveSyncWebApp {
}); });
// App lifecycle handlers // App lifecycle handlers
this.serviceHub.appLifecycle.scheduleRestart.setHandler(async () => { this.serviceHub.appLifecycle.scheduleRestart.setHandler(() => {
console.log("[AppLifecycle] Restart requested"); void (async () => {
await this.shutdown(); console.log("[AppLifecycle] Restart requested");
await this.initialize(); await this.shutdown();
compatGlobal.setTimeout(() => { await this.initialize();
compatGlobal.location.reload(); compatGlobal.setTimeout(() => {
}, 1000); compatGlobal.location.reload();
}, 1000);
})();
}); });
// Create LiveSync core // Create LiveSync core
@@ -207,9 +207,9 @@ class FSAPIWatchAdapter implements IStorageEventWatchAdapter {
}; };
if (type === "appeared") { if (type === "appeared") {
await handlers.onCreate(fileInfo, undefined); handlers.onCreate(fileInfo, undefined);
} else { } else {
await handlers.onChange(fileInfo, undefined); handlers.onChange(fileInfo, undefined);
} }
} }
} else if (type === "disappeared") { } else if (type === "disappeared") {
@@ -223,7 +223,7 @@ class FSAPIWatchAdapter implements IStorageEventWatchAdapter {
}, },
handle: null as unknown as FileSystemFileHandle, // No handle available for disappeared files 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") { } else if (type === "moved") {
// Handle as delete + create // Handle as delete + create
// Note: FileSystemObserver provides both old and new paths in some cases // Note: FileSystemObserver provides both old and new paths in some cases
@@ -240,7 +240,7 @@ class FSAPIWatchAdapter implements IStorageEventWatchAdapter {
}, },
handle: changedHandle, handle: changedHandle,
}; };
await handlers.onChange(fileInfo, undefined); handlers.onChange(fileInfo, undefined);
} }
} }
} catch (error) { } catch (error) {
+31 -4
View File
@@ -28,11 +28,38 @@ function stripPrefix(raw: string): string {
return raw.replace(/^[^:]+:/, ""); 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 * Poll every 300 ms until all known processing queues are drained, or until
* the timeout elapses. Mirrors `waitForIdle` in the existing vitest harness. * the timeout elapses. Mirrors `waitForIdle` in the existing vitest harness.
*/ */
async function waitForIdle(core: any, timeoutMs = 60_000): Promise<void> { async function waitForIdle(core: TestCore, timeoutMs = 60_000): Promise<void> {
const deadline = Date.now() + timeoutMs; const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) { while (Date.now() < deadline) {
const q = const q =
@@ -47,8 +74,8 @@ async function waitForIdle(core: any, timeoutMs = 60_000): Promise<void> {
throw new Error(`waitForIdle timed out after ${timeoutMs} ms`); throw new Error(`waitForIdle timed out after ${timeoutMs} ms`);
} }
function getCore(): any { function getCore(): TestCore {
const core = (app as any)?.core; const core = (app as unknown as { core: TestCore | null })?.core;
if (!core) throw new Error("Vault not initialised – call livesyncTest.init() first"); if (!core) throw new Error("Vault not initialised – call livesyncTest.init() first");
return core; return core;
} }
@@ -201,4 +228,4 @@ const livesyncTest: LiveSyncTestAPI = {
}; };
// Expose on window for Playwright page.evaluate() calls. // Expose on window for Playwright page.evaluate() calls.
(compatGlobal as any).livesyncTest = livesyncTest; (compatGlobal as unknown as Record<string, unknown>).livesyncTest = livesyncTest;
+9 -4
View File
@@ -32,7 +32,10 @@ function randomId(): string {
} }
async function hasReadWritePermission(handle: FileSystemDirectoryHandle, requestIfNeeded: boolean): Promise<boolean> { async function hasReadWritePermission(handle: FileSystemDirectoryHandle, requestIfNeeded: boolean): Promise<boolean> {
const h = handle as any; const h = handle as unknown as {
queryPermission?: (options: { mode: "readwrite" }) => Promise<PermissionState>;
requestPermission?: (options: { mode: "readwrite" }) => Promise<PermissionState>;
};
if (typeof h.queryPermission === "function") { if (typeof h.queryPermission === "function") {
const queried = await h.queryPermission({ mode: "readwrite" }); const queried = await h.queryPermission({ mode: "readwrite" });
if (queried === "granted") { if (queried === "granted") {
@@ -172,15 +175,17 @@ export class VaultHistoryStore {
} }
async pickNewVault(): Promise<FileSystemDirectoryHandle> { async pickNewVault(): Promise<FileSystemDirectoryHandle> {
const picker = (compatGlobal as any).showDirectoryPicker; const picker = (compatGlobal as unknown as Record<string, unknown>).showDirectoryPicker as
| ((options?: { mode?: "readwrite" | "read"; startIn?: string }) => Promise<FileSystemDirectoryHandle>)
| undefined;
if (typeof picker !== "function") { if (typeof picker !== "function") {
throw new Error("FileSystem API showDirectoryPicker is not supported in this browser"); throw new Error("FileSystem API showDirectoryPicker is not supported in this browser");
} }
const handle = (await picker({ const handle = await picker({
mode: "readwrite", mode: "readwrite",
startIn: "documents", startIn: "documents",
})) as FileSystemDirectoryHandle; });
const granted = await hasReadWritePermission(handle, true); const granted = await hasReadWritePermission(handle, true);
if (!granted) { if (!granted) {
+3 -4
View File
@@ -82,8 +82,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase {
replicator, replicator,
p2pLogCollector, p2pLogCollector,
storeP2PStatusLine: p2pStatusLine, storeP2PStatusLine: p2pStatusLine,
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- hacky way. //TODO: check it later } = useP2PReplicator({ services: this.services } as unknown as Parameters<typeof useP2PReplicator>[0]);
} = useP2PReplicator({ services: this.services } as any);
this._liveSyncReplicator = replicator; this._liveSyncReplicator = replicator;
this.p2pLogCollector = p2pLogCollector; this.p2pLogCollector = p2pLogCollector;
p2pLogCollector.p2pReplicationLine.onChanged((line) => { p2pLogCollector.p2pReplicationLine.onChanged((line) => {
@@ -101,7 +100,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase {
const repStore = SimpleStoreIDBv2.open<unknown>("p2p-livesync-web-peer"); const repStore = SimpleStoreIDBv2.open<unknown>("p2p-livesync-web-peer");
this._simpleStore = repStore; this._simpleStore = repStore;
let _settings = { ...P2P_DEFAULT_SETTINGS, additionalSuffixOfDatabaseName: "" } as ObsidianLiveSyncSettings; 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<ServiceContext>).saveData.setHandler(async (data) => { (this.services.setting as InjectableSettingService<ServiceContext>).saveData.setHandler(async (data) => {
await repStore.set("settings", data); await repStore.set("settings", data);
eventHub.emitEvent(EVENT_SETTING_SAVED, data); eventHub.emitEvent(EVENT_SETTING_SAVED, data);
@@ -281,7 +280,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase {
} }
await this.services.setting.applyExternalSettings(remoteConfig, true); await this.services.setting.applyExternalSettings(remoteConfig, true);
if (yn !== DROP) { if (yn !== DROP) {
await this.plugin.core.services.appLifecycle.scheduleRestart(); this.plugin.core.services.appLifecycle.scheduleRestart();
} }
} else { } else {
Logger(`Cancelled\nRemote config for ${peer.name} is not applied`, LOG_LEVEL_NOTICE); Logger(`Cancelled\nRemote config for ${peer.name} is not applied`, LOG_LEVEL_NOTICE);