import { contextBridge, ipcRenderer } from 'electron' import type { MySqlConfig, SqlServerConfig } from '../main/types/ipc-api.types' import type { ExtractorInput, ExtractionProgress } from '../main/types/extractor.types' import type { CleanerInput, CleanerProgress, ExportResultItem } from '../main/types/cleaner.types' import type { ResolverInput } from '../main/types/resolver-ipc.types' import type { LoginRequest } from '../main/types/auth-ipc.types' import type { UserInfo } from '../main/types/user.types' import type { ValidationRequest, MaterialTypeRecord, MaterialTypeBatchRequest } from '../main/types/validation.types' import type { IpcResult } from '../main/types/ipc.types' import { IPC_CHANNELS, type LogLevel } from '../shared/ipc-channels' import type { CleanerConfig } from '../main/types/config.schema' import type { DownloadReleaseRequest, UpdateStatus } from '../main/types/update.types' import type { UpdateDialogCatalog } from '../main/types/update.types' type ErpSettingsPayload = { erp?: { username?: string password?: string } } const processApi = { versions: { electron: process.versions.electron, chrome: process.versions.chrome, node: process.versions.node } } function invokeIpc(channel: string, ...args: unknown[]): Promise> { return ipcRenderer.invoke(channel, ...args).then((result: unknown) => { if (result && typeof result === 'object' && 'success' in (result as Record)) { const typed = result as Record const hasIpcShape = 'data' in typed || 'code' in typed || 'error' in typed if (hasIpcShape) { return typed as unknown as IpcResult } if (typed.success === true) { return { success: true, data: result as T } } return { success: false, error: typeof typed.error === 'string' ? typed.error : 'IPC operation failed' } } return { success: true, data: result as T } }) } const api = { process: processApi, file: { readFile: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_READ, filePath), writeFile: (filePath: string, content: string) => invokeIpc(IPC_CHANNELS.FILE_WRITE, filePath, content), fileExists: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_EXISTS, filePath), listFiles: (dirPath: string) => invokeIpc(IPC_CHANNELS.FILE_LIST, dirPath), openPath: (filePath: string) => invokeIpc(IPC_CHANNELS.FILE_OPEN_PATH, filePath) }, extractor: { runExtractor: (input: ExtractorInput): Promise => invokeIpc(IPC_CHANNELS.EXTRACTOR_RUN, input), onProgress: (callback: (data: ExtractionProgress) => void) => { const subscription = (_event: Electron.IpcRendererEvent, data: ExtractionProgress) => callback(data) ipcRenderer.on(IPC_CHANNELS.EXTRACTOR_PROGRESS, subscription) return () => ipcRenderer.removeListener(IPC_CHANNELS.EXTRACTOR_PROGRESS, subscription) }, onLog: (callback: (data: { level: string; message: string }) => void) => { const subscription = ( _event: Electron.IpcRendererEvent, data: { level: string; message: string } ) => callback(data) ipcRenderer.on(IPC_CHANNELS.EXTRACTOR_LOG, subscription) return () => ipcRenderer.removeListener(IPC_CHANNELS.EXTRACTOR_LOG, subscription) } }, cleaner: { runCleaner: (input: CleanerInput): Promise => invokeIpc(IPC_CHANNELS.CLEANER_RUN, input), exportResults: (items: ExportResultItem[]): Promise => invokeIpc(IPC_CHANNELS.CLEANER_EXPORT_RESULTS, items), onProgress: (callback: (data: CleanerProgress) => void) => { const subscription = (_event: Electron.IpcRendererEvent, data: CleanerProgress) => callback(data) ipcRenderer.on(IPC_CHANNELS.CLEANER_PROGRESS, subscription) return () => ipcRenderer.removeListener(IPC_CHANNELS.CLEANER_PROGRESS, subscription) } }, resolver: { resolve: (input: ResolverInput): Promise => invokeIpc(IPC_CHANNELS.RESOLVER_RESOLVE, input), validateFormat: (inputs: string[]): Promise => invokeIpc(IPC_CHANNELS.RESOLVER_VALIDATE_FORMAT, inputs) }, auth: { getComputerName: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_GET_COMPUTER_NAME), silentLogin: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_SILENT_LOGIN), login: (request: LoginRequest): Promise => invokeIpc(IPC_CHANNELS.AUTH_LOGIN, request), logout: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_LOGOUT), getCurrentUser: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_GET_CURRENT_USER), getAllUsers: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_GET_ALL_USERS), switchUser: (userInfo: UserInfo): Promise => invokeIpc(IPC_CHANNELS.AUTH_SWITCH_USER, userInfo), isAdmin: (): Promise => invokeIpc(IPC_CHANNELS.AUTH_IS_ADMIN) }, database: { connectMySql: (config: MySqlConfig): Promise => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_CONNECT, config), disconnectMySql: (): Promise => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_DISCONNECT), isMySqlConnected: (): Promise => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_IS_CONNECTED), queryMySql: (sql: string, params?: any[]): Promise => invokeIpc(IPC_CHANNELS.DATABASE_MYSQL_QUERY, sql, params), connectSqlServer: (config: SqlServerConfig): Promise => invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_CONNECT, config), disconnectSqlServer: (): Promise => invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_DISCONNECT), isSqlServerConnected: (): Promise => invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_IS_CONNECTED), querySqlServer: (sql: string, params?: Record): Promise => invokeIpc(IPC_CHANNELS.DATABASE_SQLSERVER_QUERY, sql, params) }, validation: { validate: (request: ValidationRequest): Promise => invokeIpc(IPC_CHANNELS.VALIDATION_VALIDATE, request), setSharedProductionIds: (productionIds: string[]): Promise => invokeIpc(IPC_CHANNELS.VALIDATION_SET_SHARED_PRODUCTION_IDS, productionIds), getSharedProductionIds: (): Promise => invokeIpc(IPC_CHANNELS.VALIDATION_GET_SHARED_PRODUCTION_IDS), clearSharedProductionIds: (): Promise => invokeIpc(IPC_CHANNELS.VALIDATION_CLEAR_SHARED_PRODUCTION_IDS), getCleanerData: (): Promise => invokeIpc(IPC_CHANNELS.VALIDATION_GET_CLEANER_DATA) }, materials: { upsertBatch: (materials: { materialCode: string; managerName: string }[]): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_UPSERT_BATCH, { materials }), delete: (materialCodes: string[]): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_DELETE, { materialCodes }), getManagers: (): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_GET_MANAGERS), getByManager: (managerName: string): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_GET_BY_MANAGER, managerName), getAll: (): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_GET_ALL), getStatistics: (): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_GET_STATISTICS), updateManager: (materialCode: string, managerName: string): Promise => invokeIpc(IPC_CHANNELS.MATERIALS_UPDATE_MANAGER, { materialCode, managerName }) }, settings: { getUserType: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_GET_USER_TYPE), getSettings: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_GET_SETTINGS), saveSettings: (settings: ErpSettingsPayload): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_SAVE_SETTINGS, settings), resetDefaults: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_RESET_DEFAULTS), testDbConnection: (): Promise => invokeIpc(IPC_CHANNELS.SETTINGS_TEST_DB_CONNECTION) }, materialType: { getAll: (): Promise> => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_ALL), getByManager: (managerName: string): Promise> => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_BY_MANAGER, managerName), getManagers: (): Promise> => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_GET_MANAGERS), upsert: (materialName: string, managerName: string): Promise => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_UPSERT, { materialName, managerName }), delete: (materialName: string, managerName: string): Promise => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_DELETE, { materialName, managerName }), upsertBatch: (request: MaterialTypeBatchRequest): Promise => invokeIpc(IPC_CHANNELS.MATERIAL_TYPE_UPSERT_BATCH, request) }, userErpConfig: { getCurrent: (): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_CURRENT), update: (config: { url: string; username: string; password: string }): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_UPDATE, config), testConnection: (config: { url: string username: string password: string }): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_TEST_CONNECTION, config), getAll: (): Promise => invokeIpc(IPC_CHANNELS.USER_ERP_CONFIG_GET_ALL) }, config: { getCleaner: (): Promise> => invokeIpc(IPC_CHANNELS.CONFIG_GET_CLEANER), updateCleaner: (updates: Partial): Promise> => invokeIpc(IPC_CHANNELS.CONFIG_UPDATE_CLEANER, updates) }, logger: { log: (level: LogLevel, message: string, context?: Record): void => { ipcRenderer.send(IPC_CHANNELS.LOGGER_FORWARD, { level, message, context, timestamp: Date.now() }) } }, report: { listAll: (): Promise => invokeIpc(IPC_CHANNELS.REPORT_LIST_ALL), listByUser: (username: string): Promise => invokeIpc(IPC_CHANNELS.REPORT_LIST_BY_USER, username), download: (key: string): Promise => invokeIpc(IPC_CHANNELS.REPORT_DOWNLOAD, key) }, update: { getStatus: (): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_GET_STATUS), checkNow: (): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_CHECK_NOW), getCatalog: (): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_GET_CATALOG), getChangelog: (release: DownloadReleaseRequest): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_GET_CHANGELOG, release), downloadRelease: (release: DownloadReleaseRequest): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_DOWNLOAD_RELEASE, release), installDownloaded: (): Promise> => invokeIpc(IPC_CHANNELS.UPDATE_INSTALL_DOWNLOADED), onStatusChanged: (callback: (data: UpdateStatus) => void) => { const subscription = (_event: Electron.IpcRendererEvent, data: UpdateStatus) => callback(data) ipcRenderer.on(IPC_CHANNELS.UPDATE_STATUS_CHANGED, subscription) return () => ipcRenderer.removeListener(IPC_CHANNELS.UPDATE_STATUS_CHANGED, subscription) } } } as const if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', api) contextBridge.exposeInMainWorld('api', api) } catch (error) { console.error(error) } } else { // @ts-ignore (define in dts) window.electron = api // @ts-ignore (define in dts) window.api = api }