import { normalizeColorSchemePreference } from "./policies/color-scheme.js"; import type { ApplicationApi, ColorSchemePreference, RenderFailureReport, RouteChangedReport, } from "./ports/in/application-api.js"; import type { ApplicationOutputPorts } from "./ports/out/application-output-ports.js"; import { decideChunkRecovery } from "./use-cases/decide-chunk-recovery.js"; export type { ApplicationApi, ApplicationOutputPorts }; /** * Builds the driving API consumed by inbound adapters. Concrete output ports * remain inside these closures and are never returned to React. */ export function createApplication( outputPorts: ApplicationOutputPorts, featureInputs: Readonly> = {}, ): ApplicationApi { const session = Object.freeze({ getSnapshot: () => outputPorts.session.getState(), subscribe: (listener: () => void) => outputPorts.session.subscribe(listener), beginSignIn: (returnTo?: string) => outputPorts.session.beginSignIn(returnTo), signOut: () => outputPorts.session.signOut(), recover: () => outputPorts.session.recover(), }); const preferences = Object.freeze({ getColorScheme(): ColorSchemePreference { const result = outputPorts.preferences.read("COLOR_SCHEME"); return normalizeColorSchemePreference( result.ok ? result.value : undefined, ); }, setColorScheme(preference: ColorSchemePreference) { const normalized = normalizeColorSchemePreference(preference); return outputPorts.preferences.write("COLOR_SCHEME", normalized); }, }); const diagnostics = Object.freeze({ reportRenderFailure(report: RenderFailureReport) { try { outputPorts.diagnostics.record({ level: "error", eventId: "ui.render.failed", context: { route_id: report.routeId, build_id: report.buildId, component_boundary: report.boundaryName, }, }); } catch { // Diagnostics are best-effort and cannot become an application failure. } try { outputPorts.telemetry.emit("ui.render.failed", { route_id: report.routeId, build_id: report.buildId, component_boundary: report.boundaryName, }); } catch { // Diagnostics are best-effort and cannot become an application failure. } }, reportRouteChanged(report: RouteChangedReport) { try { outputPorts.diagnostics.record({ level: "info", eventId: "route.changed", context: { route_id: report.routeId, build_id: report.buildId, }, }); } catch { // Diagnostics are best-effort and cannot become navigation failure. } }, }); const runtime = Object.freeze({ async getReleaseSummary() { const release = await outputPorts.releaseInfo.getCurrent(); return Object.freeze({ buildId: release.buildId, releaseId: release.releaseId, configSchemaVersion: release.configSchemaVersion, apiContractVersion: release.apiContractVersion, }); }, }); const recovery = Object.freeze({ async recoverChunk(input: { chunkId: string; failureKind: "CHUNK_LOAD_FAILURE" | "DEPLOY_MISMATCH"; }) { try { const current = await outputPorts.releaseInfo.getCurrent(); const active = await outputPorts.releaseInfo.refresh(); if ( current.buildId !== active.buildId || current.releaseId !== active.releaseId ) { const mismatchKind = current.buildId !== active.buildId ? "BUILD_MISMATCH" : "RELEASE_MISMATCH"; try { outputPorts.diagnostics.record({ level: "warn", eventId: "release.mismatch.detected", context: { build_id: current.buildId, active_release_id: active.releaseId, mismatch_kind: mismatchKind, }, }); } catch { // Recovery remains independent from diagnostics. } try { outputPorts.telemetry.emit("release.mismatch.detected", { build_id: current.buildId, active_release_id: active.releaseId, mismatch_kind: mismatchKind, }); } catch { // Recovery remains independent from telemetry. } } if (!active.routeChunks[input.chunkId]) { return { action: "support" as const, reason: "active-chunk-unknown", }; } const decision = decideChunkRecovery({ failureKind: input.failureKind, manifestLoaded: true, currentBuildId: current.buildId, currentReleaseId: current.releaseId, activeBuildId: active.buildId, activeReleaseId: active.releaseId, storage: outputPorts.preferences, }); if (decision.action === "reload-once") { try { outputPorts.navigation.reload(); } catch { return { action: "support" as const, reason: "reload-failed", }; } } return decision; } catch { return { action: "support" as const, reason: "manifest-unavailable", }; } }, }); const installedFeatureInputs = Object.freeze({ ...featureInputs }); const features = Object.freeze({ has(featureId: string) { return Object.hasOwn(installedFeatureInputs, featureId); }, get(featureId: string) { if (!Object.hasOwn(installedFeatureInputs, featureId)) { throw new Error(`Application feature is not installed: ${featureId}`); } return installedFeatureInputs[featureId]; }, }); return Object.freeze({ session, preferences, diagnostics, runtime, recovery, features, }); }