import { ensureAGLHomeDataRoot, preflightAGLHomeDataRootMigration, type IAGLHomeDataRootMigrationOptions, } from '../ts_migration/v23_aglhome.js'; import type { IControllerDataRootMigrationResult } from '../ts_migration/v2_controllerdataroot.js'; export interface IEnsureControllerDataRootOptions extends IAGLHomeDataRootMigrationOptions {} export const preflightAGLHomeMigration = async ( optionsArg: IEnsureControllerDataRootOptions = {}, ): Promise => await preflightAGLHomeDataRootMigration(optionsArg); export const ensureControllerDataRoot = async ( optionsArg: IEnsureControllerDataRootOptions = {}, ): Promise => await ensureAGLHomeDataRoot(optionsArg); export interface IEnsureControllerDataRootForStartupOptions extends IEnsureControllerDataRootOptions { retainOnFailure: boolean; onBlocked?: (errorArg: Error) => Promise; retryDelayMs?: number; ensureDataRoot?: typeof ensureControllerDataRoot; reportBlocked?: (errorArg: Error) => void; } export const ensureControllerDataRootForStartup = async ( optionsArg: IEnsureControllerDataRootForStartupOptions, ): Promise => { const ensureDataRoot = optionsArg.ensureDataRoot ?? ensureControllerDataRoot; let lastFailureLogAt = 0; while (true) { try { return await ensureDataRoot(optionsArg); } catch (errorArg) { if (!optionsArg.retainOnFailure) throw errorArg; const error = errorArg instanceof Error ? errorArg : new Error(String(errorArg)); if (Date.now() - lastFailureLogAt >= 30_000) { lastFailureLogAt = Date.now(); try { (optionsArg.reportBlocked ?? ((reportedErrorArg) => console.error( 'Controller data-root migration is blocked; startup remains fail-closed.', reportedErrorArg, )))(error); } catch (reportErrorArg) { console.error('Reporting the blocked controller startup failed.', reportErrorArg); } } try { await optionsArg.onBlocked?.(error); } catch (reportErrorArg) { console.error('Publishing the blocked controller startup state failed.', reportErrorArg); } await new Promise((resolve, reject) => { const signal = optionsArg.signal; const timeout = setTimeout(finish, optionsArg.retryDelayMs ?? 5_000); const onAbort = () => finish(signal?.reason instanceof Error ? signal.reason : new Error('Controller startup was cancelled by shutdown.')); function finish(errorArg?: Error): void { clearTimeout(timeout); signal?.removeEventListener('abort', onAbort); if (errorArg) reject(errorArg); else resolve(); } signal?.addEventListener('abort', onAbort, { once: true }); if (signal?.aborted) onAbort(); }); } } };