import { CliArgs, CliCommandSource } from '../../cli/args'; import { createLogger } from '../../logger'; const logger = createLogger('main:app-lifecycle'); export interface AppLifecycleServiceDeps { shouldStartApp: (args: CliArgs) => boolean; parseArgs: (argv: string[]) => CliArgs; requestSingleInstanceLock: () => boolean; quitApp: () => void; onSecondInstance: (handler: (_event: unknown, argv: string[]) => void) => void; handleCliCommand: (args: CliArgs, source: CliCommandSource) => void; printHelp: () => void; logNoRunningInstance: () => void; whenReady: (handler: () => Promise) => void; onWindowAllClosed: (handler: () => void) => void; onWillQuit: (handler: () => void) => void; onActivate: (handler: () => void) => void; isDarwinPlatform: () => boolean; onReady: () => Promise; onWillQuitCleanup: () => void; shouldRestoreWindowsOnActivate: () => boolean; restoreWindowsOnActivate: () => void; shouldQuitOnWindowAllClosed: () => boolean; } interface AppLike { requestSingleInstanceLock: () => boolean; quit: () => void; on: (...args: any[]) => unknown; whenReady: () => Promise; } export interface AppLifecycleDepsRuntimeOptions { app: AppLike; platform: NodeJS.Platform; shouldStartApp: (args: CliArgs) => boolean; parseArgs: (argv: string[]) => CliArgs; handleCliCommand: (args: CliArgs, source: CliCommandSource) => void; printHelp: () => void; logNoRunningInstance: () => void; onReady: () => Promise; onWillQuitCleanup: () => void; shouldRestoreWindowsOnActivate: () => boolean; restoreWindowsOnActivate: () => void; shouldQuitOnWindowAllClosed: () => boolean; } export function createAppLifecycleDepsRuntime( options: AppLifecycleDepsRuntimeOptions, ): AppLifecycleServiceDeps { return { shouldStartApp: options.shouldStartApp, parseArgs: options.parseArgs, requestSingleInstanceLock: () => options.app.requestSingleInstanceLock(), quitApp: () => options.app.quit(), onSecondInstance: (handler) => { options.app.on('second-instance', handler as (...args: unknown[]) => void); }, handleCliCommand: options.handleCliCommand, printHelp: options.printHelp, logNoRunningInstance: options.logNoRunningInstance, whenReady: (handler) => { options.app .whenReady() .then(handler) .catch((error) => { logger.error('App ready handler failed:', error); }); }, onWindowAllClosed: (handler) => { options.app.on('window-all-closed', handler as (...args: unknown[]) => void); }, onWillQuit: (handler) => { options.app.on('will-quit', handler as (...args: unknown[]) => void); }, onActivate: (handler) => { options.app.on('activate', handler as (...args: unknown[]) => void); }, isDarwinPlatform: () => options.platform === 'darwin', onReady: options.onReady, onWillQuitCleanup: options.onWillQuitCleanup, shouldRestoreWindowsOnActivate: options.shouldRestoreWindowsOnActivate, restoreWindowsOnActivate: options.restoreWindowsOnActivate, shouldQuitOnWindowAllClosed: options.shouldQuitOnWindowAllClosed, }; } export function startAppLifecycle(initialArgs: CliArgs, deps: AppLifecycleServiceDeps): void { if (initialArgs.help && !deps.shouldStartApp(initialArgs)) { deps.printHelp(); deps.quitApp(); return; } const gotTheLock = deps.requestSingleInstanceLock(); if (!gotTheLock) { deps.quitApp(); return; } deps.onSecondInstance((_event, argv) => { try { deps.handleCliCommand(deps.parseArgs(argv), 'second-instance'); } catch (error) { logger.error('Failed to handle second-instance CLI command:', error); } }); if (!deps.shouldStartApp(initialArgs)) { if (initialArgs.stop && !initialArgs.start) { deps.quitApp(); } else { deps.logNoRunningInstance(); deps.quitApp(); } return; } deps.whenReady(async () => { await deps.onReady(); }); deps.onWindowAllClosed(() => { if (!deps.isDarwinPlatform() && deps.shouldQuitOnWindowAllClosed()) { deps.quitApp(); } }); deps.onWillQuit(() => { deps.onWillQuitCleanup(); }); deps.onActivate(() => { if (deps.shouldRestoreWindowsOnActivate()) { deps.restoreWindowsOnActivate(); } }); }