import { ContextIsolatedElectronDetector } from '@/infrastructure/RuntimeEnvironment/Electron/ContextIsolatedElectronDetector'; import type { ElectronEnvironmentDetector } from '@/infrastructure/RuntimeEnvironment/Electron/ElectronEnvironmentDetector'; import { OperatingSystem } from '@/domain/OperatingSystem'; import { type PropertyKeys, isBoolean, isFunction, isNumber, isPlainObject, } from '@/TypeHelpers'; import type { WindowVariables } from './WindowVariables'; /** * Checks for consistency in runtime environment properties injected by Electron preloader. */ export function validateWindowVariables( variables: Partial, electronDetector: ElectronEnvironmentDetector = new ContextIsolatedElectronDetector(), ) { if (!electronDetector.isRunningInsideElectron() || electronDetector.determineElectronProcessType() !== 'renderer') { return; } if (!isPlainObject(variables)) { throw new Error('window is not an object'); } const errors = [...testEveryProperty(variables)]; if (errors.length > 0) { throw new Error(errors.join('\n')); } } function* testEveryProperty(variables: Partial): Iterable { const tests: Record>, boolean> = { os: testOperatingSystem(variables.os), isRunningAsDesktopApplication: testIsRunningAsDesktopApplication(variables), codeRunner: testCodeRunner(variables), log: testLogger(variables), dialog: testDialog(variables), scriptDiagnosticsCollector: testScriptDiagnosticsCollector(variables), }; for (const [propertyName, testResult] of Object.entries(tests)) { if (!testResult) { const propertyValue = variables[propertyName as keyof WindowVariables]; yield `Unexpected ${propertyName} (${typeof propertyValue})`; } } } function testOperatingSystem(os: unknown): boolean { if (os === undefined) { return true; } if (!isNumber(os)) { return false; } return Object .values(OperatingSystem) .includes(os); } function testLogger(variables: Partial): boolean { return isPlainObject(variables.log) && isFunction(variables.log.debug) && isFunction(variables.log.info) && isFunction(variables.log.error) && isFunction(variables.log.warn); } function testCodeRunner(variables: Partial): boolean { return isPlainObject(variables.codeRunner) && isFunction(variables.codeRunner.runCode); } function testIsRunningAsDesktopApplication(variables: Partial): boolean { return isBoolean(variables.isRunningAsDesktopApplication) && variables.isRunningAsDesktopApplication === true; } function testDialog(variables: Partial): boolean { return isPlainObject(variables.dialog) && isFunction(variables.dialog.saveFile) && isFunction(variables.dialog.showError); } function testScriptDiagnosticsCollector(variables: Partial): boolean { return isPlainObject(variables.scriptDiagnosticsCollector) && isFunction(variables.scriptDiagnosticsCollector.collectDiagnosticInformation); }