refactor: align platform APIs with community review

This commit is contained in:
vorotamoroz
2026-07-17 14:47:27 +00:00
parent 298738fc67
commit 326bf77183
39 changed files with 444 additions and 294 deletions
+10 -2
View File
@@ -5,6 +5,15 @@ import { compatGlobal, _activeDocument } from "@vrtmrz/livesync-commonlib/compat
const historyStore = new VaultHistoryStore();
let app: LiveSyncWebApp | null = null;
type LiveSyncWebAppDebugApi = {
getApp: () => LiveSyncWebApp | null;
historyStore: VaultHistoryStore;
};
type LiveSyncWebAppGlobal = typeof compatGlobal & {
livesyncApp?: LiveSyncWebAppDebugApi;
};
function getRequiredElement<T extends HTMLElement>(id: string): T {
const element = _activeDocument.getElementById(id);
if (!element) {
@@ -131,8 +140,7 @@ compatGlobal.addEventListener("load", () => {
compatGlobal.addEventListener("beforeunload", () => {
void app?.shutdown();
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- patching
(compatGlobal as any).livesyncApp = {
(compatGlobal as LiveSyncWebAppGlobal).livesyncApp = {
getApp: () => app,
historyStore,
};
+10 -8
View File
@@ -6,7 +6,7 @@
import type { BrowserServiceHub } from "@vrtmrz/livesync-commonlib/compat/services/BrowserServices";
import { LiveSyncBaseCore } from "@/LiveSyncBaseCore";
import { ServiceContext } from "@vrtmrz/livesync-commonlib/compat/services/base/ServiceBase";
import { initialiseServiceModulesFSAPI } from "./serviceModules/FSAPIServiceModules";
import { initialiseServiceModulesFSAPI, type FSAPIServiceModules } from "./serviceModules/FSAPIServiceModules";
import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";
import type { BrowserAPIService } from "@vrtmrz/livesync-commonlib/compat/services/implements/browser/BrowserAPIService";
import type { InjectableSettingService } from "@vrtmrz/livesync-commonlib/compat/services/implements/injectable/InjectableSettingService";
@@ -53,6 +53,7 @@ class LiveSyncWebApp {
private rootHandle: FileSystemDirectoryHandle;
private core: LiveSyncBaseCore<ServiceContext, never> | null = null;
private serviceHub: BrowserServiceHub<ServiceContext> | null = null;
private platformServiceModules: FSAPIServiceModules | null = null;
constructor(rootHandle: FileSystemDirectoryHandle) {
this.rootHandle = rootHandle;
@@ -113,7 +114,9 @@ class LiveSyncWebApp {
this.core = new LiveSyncBaseCore<ServiceContext, never>(
this.serviceHub,
(core, serviceHub) => {
return initialiseServiceModulesFSAPI(this.rootHandle, core, serviceHub);
const serviceModules = initialiseServiceModulesFSAPI(this.rootHandle, core, serviceHub);
this.platformServiceModules = serviceModules;
return serviceModules;
},
(core) => [
// new ModuleObsidianEvents(this, core),
@@ -208,9 +211,8 @@ class LiveSyncWebApp {
}
// Scan the directory to populate file cache
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Accessing private service modules
const fileAccess = (this.core as any)._serviceModules?.storageAccess?.vaultAccess;
if (fileAccess?.fsapiAdapter) {
const fileAccess = this.platformServiceModules?.vaultAccess;
if (fileAccess) {
console.log("[Scanning] Scanning vault directory...");
await fileAccess.fsapiAdapter.scanDirectory();
const files = await fileAccess.fsapiAdapter.getFiles();
@@ -227,13 +229,13 @@ class LiveSyncWebApp {
console.log("[Shutdown] Shutting down...");
// Stop file watching
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Accessing private service modules
const storageEventManager = (this.core as any)._serviceModules?.storageAccess?.storageEventManager;
if (storageEventManager?.cleanup) {
const storageEventManager = this.platformServiceModules?.storageEventManager;
if (storageEventManager) {
await storageEventManager.cleanup();
}
await this.core.services.control.onUnload();
this.platformServiceModules = null;
console.log("[Shutdown] Complete");
}
}
@@ -13,6 +13,25 @@ import type { FileEventItemSentinel } from "@vrtmrz/livesync-commonlib/compat/ma
import type { FSAPIFile, FSAPIFolder } from "@/apps/webapp/adapters/FSAPITypes";
import { compatGlobal } from "@vrtmrz/livesync-commonlib/compat/common/coreEnvFunctions";
type FileSystemObserverRecord = {
changedHandle?: FileSystemFileHandle | FileSystemDirectoryHandle;
relativePathComponents?: readonly string[];
type: "appeared" | "disappeared" | "modified" | "moved" | "unknown" | "errored";
};
type FileSystemObserverInstance = {
observe(handle: FileSystemDirectoryHandle, options: { recursive: boolean }): Promise<void>;
disconnect(): void;
};
type FileSystemObserverConstructor = new (
callback: (records: readonly FileSystemObserverRecord[]) => void | Promise<void>
) => FileSystemObserverInstance;
type GlobalWithFileSystemObserver = typeof compatGlobal & {
FileSystemObserver?: FileSystemObserverConstructor;
};
/**
* FileSystem API-specific type guard adapter
*/
@@ -89,7 +108,8 @@ class FSAPIPersistenceAdapter implements IStorageEventPersistenceAdapter {
const result = await new Promise<(FileEventItem | FileEventItemSentinel)[] | null>((resolve, reject) => {
const request = store.get(this.snapshotKey);
request.onsuccess = () => resolve(request.result || null);
request.onsuccess = () =>
resolve((request.result as (FileEventItem | FileEventItemSentinel)[] | undefined) ?? null);
request.onerror = () => reject(request.error);
});
@@ -155,26 +175,21 @@ class FSAPIConverterAdapter implements IStorageEventConverterAdapter<FSAPIFile>
* FileSystem API-specific watch adapter using FileSystemObserver (Chrome only)
*/
class FSAPIWatchAdapter implements IStorageEventWatchAdapter {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Accessing private service modules
private observer: any = null; // FileSystemObserver type
private observer: FileSystemObserverInstance | null = null;
constructor(private rootHandle: FileSystemDirectoryHandle) {}
async beginWatch(handlers: IStorageEventWatchHandlers): Promise<void> {
// Use FileSystemObserver if available (Chrome 124+)
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Accessing global FileSystemObserver
if (typeof (compatGlobal as any).FileSystemObserver === "undefined") {
const FileSystemObserver = (compatGlobal as GlobalWithFileSystemObserver).FileSystemObserver;
if (!FileSystemObserver) {
console.log("[FSAPIWatchAdapter] FileSystemObserver not available, file watching disabled");
console.log("[FSAPIWatchAdapter] Consider using Chrome 124+ for real-time file watching");
return Promise.resolve();
}
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Accessing private service modules
const FileSystemObserver = (compatGlobal as any).FileSystemObserver;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Accessing private service modules
this.observer = new FileSystemObserver(async (records: any[]) => {
this.observer = new FileSystemObserver(async (records) => {
for (const record of records) {
const changedHandle = record.changedHandle;
const relativePathComponents = record.relativePathComponents;
@@ -11,6 +11,11 @@ import { StorageEventManagerFSAPI } from "@/apps/webapp/managers/StorageEventMan
import type { ServiceModules } from "@vrtmrz/livesync-commonlib/compat/interfaces/ServiceModule";
import { ServiceFileHandler } from "@/serviceModules/FileHandler";
export interface FSAPIServiceModules extends ServiceModules {
vaultAccess: FileAccessFSAPI;
storageEventManager: StorageEventManagerFSAPI;
}
/**
* Initialize service modules for FileSystem API webapp version
* This is the webapp equivalent of ObsidianLiveSyncPlugin.initialiseServiceModules
@@ -24,7 +29,7 @@ export function initialiseServiceModulesFSAPI(
rootHandle: FileSystemDirectoryHandle,
core: LiveSyncBaseCore<ServiceContext, never>,
services: InjectableServiceHub<ServiceContext>
): ServiceModules {
): FSAPIServiceModules {
const storageAccessManager = new StorageAccessManager();
// FileSystem API-specific file access
@@ -104,5 +109,7 @@ export function initialiseServiceModulesFSAPI(
fileHandler,
databaseFileAccess,
storageAccess,
vaultAccess,
storageEventManager,
};
}