import { contextBridge, ipcRenderer, type IpcRendererEvent } from 'electron' import { BRIDGE_GLOBAL_NAME, type BridgeApi, type StreamListener } from '../shared/contracts/BridgeApi' import { IPC_CHANNELS } from '../shared/contracts/IpcChannels' import type { AccountDto, SignInFinishedDto, SignInPromptDto } from '../shared/contracts/dto/AccountDto' import type { AppStateDto } from '../shared/contracts/dto/AppStateDto' import type { CatalogListingDto } from '../shared/contracts/dto/CatalogListingDto' import type { InstalledStoreDto } from '../shared/contracts/dto/InstalledStoreDto' import type { LocaleSelectionDto } from '../shared/contracts/dto/LocaleSelectionDto' import type { RegistryResultDto } from '../shared/contracts/dto/RegistryResultDto' import type { RegistryStoreDto } from '../shared/contracts/dto/RegistryStoreDto' import type { StorePathsDto } from '../shared/contracts/dto/StorePathsDto' import type { StoreSelectionDto } from '../shared/contracts/dto/StoreSelectionDto' import type { SyncEventDto } from '../shared/contracts/dto/SyncEventDto' /** * The bridge, and nothing else. * * This file is the whole surface the window gets: no Node, no filesystem, no child * processes. It is bundled into a single script on purpose — a sandboxed preload * cannot require its own modules — and it implements `BridgeApi`, so the renderer * and the main process are compiled against the same contract. */ const bridge: BridgeApi = { readState: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.appReadState) as Promise, updateLocale: async (locale: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.appUpdateLocale, locale) as Promise, updateNavOpen: async (open: boolean): Promise => ipcRenderer.invoke(IPC_CHANNELS.appUpdateNavOpen, open) as Promise, listGames: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.catalogListGames) as Promise, readPaths: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.catalogReadPaths) as Promise, syncGames: async (names: readonly string[]): Promise => ipcRenderer.invoke(IPC_CHANNELS.catalogSyncGames, names) as Promise, removeGame: async (name: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.catalogRemoveGame, name) as Promise, launchGame: async (name: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.catalogLaunchGame, name) as Promise, readAccount: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.accountRead) as Promise, beginSignIn: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.accountBeginSignIn) as Promise, cancelSignIn: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.accountCancelSignIn) as Promise, signOut: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.accountSignOut) as Promise, listRegistryStores: async (): Promise => ipcRenderer.invoke(IPC_CHANNELS.storeListRegistry) as Promise, installStore: async (store: RegistryStoreDto): Promise => ipcRenderer.invoke(IPC_CHANNELS.storeInstallStore, store) as Promise, installCatalog: async (catalogUrl: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.storeInstallCatalog, catalogUrl) as Promise, selectStore: async (home: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.storeSelectStore, home) as Promise, removeStore: async (home: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.storeRemoveStore, home) as Promise, openFolder: async (directory: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.appOpenFolder, directory) as Promise, openUrl: async (url: string): Promise => ipcRenderer.invoke(IPC_CHANNELS.appOpenUrl, url) as Promise, onLog: (listener: StreamListener): void => { ipcRenderer.on(IPC_CHANNELS.streamLog, (_event: IpcRendererEvent, line: string): void => { listener(line) }) }, onSyncEvent: (listener: StreamListener): void => { ipcRenderer.on(IPC_CHANNELS.streamSyncEvent, (_event: IpcRendererEvent, payload: SyncEventDto): void => { listener(payload) }) }, onBusyChanged: (listener: StreamListener): void => { ipcRenderer.on(IPC_CHANNELS.streamBusyChanged, (_event: IpcRendererEvent, busy: boolean): void => { listener(busy) }) }, onSignInFinished: (listener: StreamListener): void => { ipcRenderer.on( IPC_CHANNELS.streamSignInFinished, (_event: IpcRendererEvent, payload: SignInFinishedDto): void => { listener(payload) } ) } } contextBridge.exposeInMainWorld(BRIDGE_GLOBAL_NAME, bridge)