import { INI_PANEL_ENTRIES, getIniPanelEntryByHref, getIniPanelEntryManifest, } from "./entry-manifest.js"; import { createMachineSessionControlPageController, } from "./control-page-controller.js"; import { renderMachineSessionControlView, } from "./control-view-renderer.js"; import { controlPageStatusText, createControlPageRefreshState, refreshMachineSessionControlPage, } from "./control-page-refresh-workflow.js"; import { createControlPageSessionLoadState, loadMachineSessionForControlPageWorkflow, } from "./control-page-session-workflow.js"; import { createMachineSessionReadonlyStatus, createMachineSessionReadonlyStatusApiManifest, readMachineSessionReadonlyStatus, } from "./readonly-status-api.js"; export { controlPageStatusText, createControlPageRefreshState, refreshMachineSessionControlPage, } from "./control-page-refresh-workflow.js"; export { createControlPageSessionLoadState, loadMachineSessionForControlPageWorkflow, } from "./control-page-session-workflow.js"; export { createMachineSessionReadonlyStatus, createMachineSessionReadonlyStatusApiManifest, readMachineSessionReadonlyStatus, } from "./readonly-status-api.js"; export { INI_PANEL_ENTRIES, getIniPanelEntryByHref, getIniPanelEntryManifest, } from "./entry-manifest.js"; export { createMachineSessionControlPageController, } from "./control-page-controller.js"; export { renderMachineSessionControlView, } from "./control-view-renderer.js"; export function getVisibleIniPanelEntries() { return getIniPanelEntryManifest(); } export function createIniPanelLauncherLinkViewModel(entries = getVisibleIniPanelEntries()) { return entries.map(({ id, href, title }) => ({ entryId: id, href, label: title, })); } export function createIniPanelLaunchApiSchema() { return { apiName: "ini-panel-launch", manifestVersion: 1, fields: [ "apiName", "manifestVersion", "methods", "visibleEntries", "targetPages", "controlPageApiMethods", "persistenceCapabilities", ], }; } export function isSupportedIniPanelLaunchApiManifest(manifest, schema = createIniPanelLaunchApiSchema()) { if (!manifest || typeof manifest !== "object") { return false; } if (manifest.apiName !== schema.apiName || manifest.manifestVersion !== schema.manifestVersion) { return false; } return schema.fields.every((fieldName) => Object.hasOwn(manifest, fieldName)); } export function createControlPageBrowserApiSchema() { return { apiName: "ini-panel-control-page", manifestVersion: 1, fields: [ "apiName", "manifestVersion", "methods", "contract", "readonlyStatusApiManifest", ], }; } export function isSupportedIniPanelControlPageApiManifest( manifest, schema = createControlPageBrowserApiSchema(), ) { if (!manifest || typeof manifest !== "object") { return false; } if (manifest.apiName !== schema.apiName || manifest.manifestVersion !== schema.manifestVersion) { return false; } return schema.fields.every((fieldName) => Object.hasOwn(manifest, fieldName)); } export function createIniPanelLaunchApiManifest(entries = getVisibleIniPanelEntries()) { const schema = createIniPanelLaunchApiSchema(); return { apiName: schema.apiName, manifestVersion: schema.manifestVersion, methods: [ "isSupportedLaunchApiManifest", "getLaunchApiSchema", "getLaunchApiManifest", "getShellIntegrationSchema", "getShellIntegrationManifest", "getShellIntegrationReadiness", "getShellSessionHandoffSummarySchema", "getShellSessionHandoffSummary", "isSupportedShellSessionHandoffSummary", "getShellSessionHandoffCompatibilityReport", "getShellSessionHandoffDisplayViewModel", "getShellSessionHandoffSnapshot", "getShellSessionHandoffRenderState", "getShellSessionHandoffActionPlan", "getShellSessionHandoffChecklist", "getShellSessionHandoffPackageSchema", "getShellSessionHandoffPackage", "getShellSessionHandoffPackageReport", "getShellSessionHandoffPackageDisplayViewModel", "isSupportedShellSessionHandoffPackage", "getLauncherLinks", "getControlPageApiMethods", ], visibleEntries: entries.map(({ id, href, title }) => ({ id, href, title, })), targetPages: entries.map(({ id, href }) => ({ entryId: id, href, })), controlPageApiMethods: createControlPageBrowserApiMethods(), persistenceCapabilities: [ { id: "machine-files", label: "Machine text files", scope: "opfs", }, { id: "session-snapshot", label: "Machine session snapshot", scope: "opfs", }, { id: "readonly-status", label: "Read-only machine session status", scope: "control-page", }, ], }; } export function createIniPanelShellControlPageContract() { const readonlyStatusApiManifest = createMachineSessionReadonlyStatusApiManifest(); return { methodNames: [ "createController", "loadSessionState", "readStatus", "refresh", "renderView", ], readonlyStatusApiManifest, readonlyStatusApiMethodNames: Object.keys(readonlyStatusApiManifest.methods), readonlyStatusApiStatusFields: readonlyStatusApiManifest.statusFields, relatedControlPageMethods: readonlyStatusApiManifest.relatedControlPageMethods, }; } export function createControlPageBrowserApiMethods() { return [ "isSupportedControlPageApiManifest", "getControlPageApiSchema", "getControlPageApiManifest", "getControlPageApiMethods", "getControlPageContract", "getControlView", "getPanelApi", "loadControlPageSessionState", "readControlPageStatus", "refreshControlPage", "renderControlPage", ]; } export function createControlPageBrowserApiManifest() { const schema = createControlPageBrowserApiSchema(); const contract = createIniPanelShellControlPageContract(); return { apiName: schema.apiName, manifestVersion: schema.manifestVersion, methods: createControlPageBrowserApiMethods(), contract, readonlyStatusApiManifest: contract.readonlyStatusApiManifest, }; } export function createIniPanelShellIntegrationSchema() { return { apiName: "ini-panel-shell-integration", manifestVersion: 1, fields: [ "apiName", "manifestVersion", "pages", "launcherLinks", "launchApiSchema", "launchApiManifest", "controlPageApiSchema", "controlPageApiManifest", "compatibility", "persistenceCapabilities", ], }; } export function isSupportedIniPanelShellIntegrationManifest( manifest, schema = createIniPanelShellIntegrationSchema(), ) { if (!manifest || typeof manifest !== "object") { return false; } if (manifest.apiName !== schema.apiName || manifest.manifestVersion !== schema.manifestVersion) { return false; } return schema.fields.every((fieldName) => Object.hasOwn(manifest, fieldName)); } export function createIniPanelShellIntegrationManifest(entries = getVisibleIniPanelEntries()) { const schema = createIniPanelShellIntegrationSchema(); const launchApiSchema = createIniPanelLaunchApiSchema(); const launchApiManifest = createIniPanelLaunchApiManifest(entries); const controlPageApiSchema = createControlPageBrowserApiSchema(); const controlPageApiManifest = createControlPageBrowserApiManifest(); return { apiName: schema.apiName, manifestVersion: schema.manifestVersion, pages: entries.map(({ id, href, title }) => ({ id, href, title, })), launcherLinks: createIniPanelLauncherLinkViewModel(entries), launchApiSchema, launchApiManifest, controlPageApiSchema, controlPageApiManifest, compatibility: { launchApiManifest: isSupportedIniPanelLaunchApiManifest(launchApiManifest, launchApiSchema), controlPageApiManifest: isSupportedIniPanelControlPageApiManifest( controlPageApiManifest, controlPageApiSchema, ), }, persistenceCapabilities: launchApiManifest.persistenceCapabilities, }; } export function createIniPanelShellIntegrationReadiness( manifest = createIniPanelShellIntegrationManifest(), ) { const integrationManifestSupported = isSupportedIniPanelShellIntegrationManifest(manifest); const launchApiManifestSupported = integrationManifestSupported && isSupportedIniPanelLaunchApiManifest(manifest.launchApiManifest, manifest.launchApiSchema); const controlPageApiManifestSupported = integrationManifestSupported && isSupportedIniPanelControlPageApiManifest( manifest.controlPageApiManifest, manifest.controlPageApiSchema, ); const compatibility = integrationManifestSupported && manifest.compatibility ? { launchApiManifest: Boolean(manifest.compatibility.launchApiManifest), controlPageApiManifest: Boolean(manifest.compatibility.controlPageApiManifest), } : { launchApiManifest: false, controlPageApiManifest: false, }; const checks = { integrationManifestSupported, launchApiManifestSupported, controlPageApiManifestSupported, launchCompatibility: compatibility.launchApiManifest, controlPageCompatibility: compatibility.controlPageApiManifest, }; const missing = Object.entries(checks) .filter(([, passed]) => !passed) .map(([name]) => name); const ready = missing.length === 0; return { phase: ready ? "ready" : "blocked", ready, checks, missing, counts: { pages: Array.isArray(manifest?.pages) ? manifest.pages.length : 0, launcherLinks: Array.isArray(manifest?.launcherLinks) ? manifest.launcherLinks.length : 0, launchMethods: Array.isArray(manifest?.launchApiManifest?.methods) ? manifest.launchApiManifest.methods.length : 0, controlPageMethods: Array.isArray(manifest?.controlPageApiManifest?.methods) ? manifest.controlPageApiManifest.methods.length : 0, persistenceCapabilities: Array.isArray(manifest?.persistenceCapabilities) ? manifest.persistenceCapabilities.length : 0, }, apiNames: { integration: manifest?.apiName ?? null, launch: manifest?.launchApiManifest?.apiName ?? null, controlPage: manifest?.controlPageApiManifest?.apiName ?? null, }, }; } export function createIniPanelShellIntegrationWorkflowPlan({ manifest = createIniPanelShellIntegrationManifest(), readiness = createIniPanelShellIntegrationReadiness(manifest), controlEntryId = "control-view", } = {}) { const targetPage = manifest?.launchApiManifest?.targetPages?.find( (page) => page.entryId === controlEntryId, ) ?? null; const requiredLaunchApiMethods = [ "getShellIntegrationManifest", "getShellIntegrationReadiness", ]; const requiredControlPageApiMethods = [ "getControlPageApiManifest", "loadControlPageSessionState", "readControlPageStatus", ]; const launchMethods = Array.isArray(manifest?.launchApiManifest?.methods) ? manifest.launchApiManifest.methods : []; const controlPageMethods = Array.isArray(manifest?.controlPageApiManifest?.methods) ? manifest.controlPageApiManifest.methods : []; const missingLaunchApiMethods = requiredLaunchApiMethods.filter( (methodName) => !launchMethods.includes(methodName), ); const missingControlPageApiMethods = requiredControlPageApiMethods.filter( (methodName) => !controlPageMethods.includes(methodName), ); const missing = [ ...readiness.missing, ...(targetPage ? [] : ["controlTargetPage"]), ...missingLaunchApiMethods.map((methodName) => `launchApi.${methodName}`), ...missingControlPageApiMethods.map((methodName) => `controlPageApi.${methodName}`), ]; const ready = readiness.ready && missing.length === 0; return { phase: ready ? "ready" : "blocked", ready, controlEntryId, targetPage, readiness, requiredLaunchApiMethods, requiredControlPageApiMethods, missingLaunchApiMethods, missingControlPageApiMethods, missing, readonlyStatusHandoff: { loadSessionStateMethod: "loadControlPageSessionState", readStatusMethod: "readControlPageStatus", expectedStatusFields: manifest?.controlPageApiManifest?.readonlyStatusApiManifest?.statusFields ?? [], }, }; } export function createIniPanelShellSessionHandoffSummarySchema() { return { apiName: "ini-panel-shell-session-handoff-summary", summaryVersion: 1, fields: [ "apiName", "summaryVersion", "phase", "ready", "targetPage", "readiness", "persistenceCapabilities", "readonlyStatusFields", "handoffMethods", "blockingReasons", ], }; } export function isSupportedIniPanelShellSessionHandoffSummary( summary, schema = createIniPanelShellSessionHandoffSummarySchema(), ) { if (!summary || typeof summary !== "object") { return false; } if (summary.apiName !== schema.apiName || summary.summaryVersion !== schema.summaryVersion) { return false; } return schema.fields.every((fieldName) => Object.hasOwn(summary, fieldName)); } export function createIniPanelShellSessionHandoffSummary({ manifest = createIniPanelShellIntegrationManifest(), readiness = createIniPanelShellIntegrationReadiness(manifest), workflowPlan = createIniPanelShellIntegrationWorkflowPlan({ manifest, readiness }), } = {}) { const schema = createIniPanelShellSessionHandoffSummarySchema(); const persistenceCapabilities = Array.isArray(manifest?.persistenceCapabilities) ? manifest.persistenceCapabilities.map(({ id, label, scope }) => ({ id, label, scope })) : []; const readonlyStatusFields = Array.isArray(workflowPlan?.readonlyStatusHandoff?.expectedStatusFields) ? [...workflowPlan.readonlyStatusHandoff.expectedStatusFields] : []; return { apiName: schema.apiName, summaryVersion: schema.summaryVersion, phase: workflowPlan.ready ? "ready" : "blocked", ready: Boolean(workflowPlan.ready), targetPage: workflowPlan.targetPage, readiness: { phase: readiness.phase, missing: [...readiness.missing], counts: { ...readiness.counts }, apiNames: { ...readiness.apiNames }, }, persistenceCapabilities, readonlyStatusFields, handoffMethods: { loadSessionState: workflowPlan.readonlyStatusHandoff.loadSessionStateMethod, readStatus: workflowPlan.readonlyStatusHandoff.readStatusMethod, }, blockingReasons: [...workflowPlan.missing], }; } export function createIniPanelShellSessionHandoffCompatibilityReport( summary = createIniPanelShellSessionHandoffSummary(), schema = createIniPanelShellSessionHandoffSummarySchema(), ) { const summaryObject = summary && typeof summary === "object" ? summary : {}; const missingFields = schema.fields.filter((fieldName) => !Object.hasOwn(summaryObject, fieldName)); const mismatchFields = [ ...(summaryObject.apiName === schema.apiName ? [] : ["apiName"]), ...(summaryObject.summaryVersion === schema.summaryVersion ? [] : ["summaryVersion"]), ]; const summarySupported = missingFields.length === 0 && mismatchFields.length === 0; const blockingReasons = Array.isArray(summaryObject.blockingReasons) ? [...summaryObject.blockingReasons] : []; const handoffReady = summarySupported && summaryObject.ready === true && blockingReasons.length === 0; return { apiName: "ini-panel-shell-session-handoff-compatibility-report", reportVersion: 1, phase: handoffReady ? "ready" : "blocked", compatible: handoffReady, summarySupported, handoffReady, schema: { apiName: schema.apiName, summaryVersion: schema.summaryVersion, fields: [...schema.fields], }, summary: { apiName: summaryObject.apiName ?? null, summaryVersion: summaryObject.summaryVersion ?? null, phase: summaryObject.phase ?? null, ready: summaryObject.ready ?? false, }, missingFields, mismatchFields, blockingReasons, }; } export function createIniPanelShellSessionHandoffDisplayViewModel( report = createIniPanelShellSessionHandoffCompatibilityReport(), ) { const missingFields = Array.isArray(report?.missingFields) ? report.missingFields : []; const mismatchFields = Array.isArray(report?.mismatchFields) ? report.mismatchFields : []; const blockingReasons = Array.isArray(report?.blockingReasons) ? report.blockingReasons : []; const problems = [...missingFields, ...mismatchFields, ...blockingReasons]; return { phase: report?.phase === "ready" ? "ready" : "blocked", title: "Shell handoff compatibility", statusText: report?.compatible ? "Ready" : "Blocked", detailText: problems.length === 0 ? "No blocking reasons" : problems.join(", "), rows: [ { id: "summary-supported", label: "Summary schema", value: report?.summarySupported ? "supported" : "blocked", }, { id: "handoff-ready", label: "Handoff readiness", value: report?.handoffReady ? "ready" : "blocked", }, { id: "missing-fields", label: "Missing fields", value: missingFields.length === 0 ? "none" : missingFields.join(", "), }, { id: "mismatch-fields", label: "Mismatched fields", value: mismatchFields.length === 0 ? "none" : mismatchFields.join(", "), }, { id: "blocking-reasons", label: "Blocking reasons", value: blockingReasons.length === 0 ? "none" : blockingReasons.join(", "), }, ], }; } export function createIniPanelShellSessionHandoffSnapshot({ readiness = createIniPanelShellIntegrationReadiness(), summary = createIniPanelShellSessionHandoffSummary(), compatibilityReport = createIniPanelShellSessionHandoffCompatibilityReport(summary), displayViewModel = createIniPanelShellSessionHandoffDisplayViewModel(compatibilityReport), } = {}) { return { apiName: "ini-panel-shell-session-handoff-snapshot", snapshotVersion: 1, phase: compatibilityReport.phase, ready: Boolean(compatibilityReport.compatible), readiness, summary, compatibilityReport, displayViewModel, }; } export function createIniPanelShellSessionHandoffRenderState( snapshot = createIniPanelShellSessionHandoffSnapshot(), ) { const displayViewModel = snapshot?.displayViewModel && typeof snapshot.displayViewModel === "object" ? snapshot.displayViewModel : createIniPanelShellSessionHandoffDisplayViewModel(snapshot?.compatibilityReport); const rows = Array.isArray(displayViewModel.rows) ? displayViewModel.rows.map(({ id, label, value }) => ({ id, label, value: value == null ? "" : String(value), })) : []; const ready = snapshot?.ready === true && displayViewModel.phase === "ready"; const statusText = displayViewModel.statusText ?? (ready ? "Ready" : "Blocked"); const detailText = displayViewModel.detailText ?? "No blocking reasons"; return { apiName: "ini-panel-shell-session-handoff-render-state", renderStateVersion: 1, phase: ready ? "ready" : "blocked", ready, title: displayViewModel.title ?? "Shell handoff compatibility", statusText, detailText, statusLine: `${statusText}: ${detailText}`, rows, dataset: { handoffPhase: ready ? "ready" : "blocked", handoffReady: ready ? "true" : "false", }, }; } export function createIniPanelShellSessionHandoffActionPlan({ snapshot = createIniPanelShellSessionHandoffSnapshot(), renderState = createIniPanelShellSessionHandoffRenderState(snapshot), } = {}) { const summary = snapshot?.summary && typeof snapshot.summary === "object" ? snapshot.summary : {}; const handoffMethods = summary.handoffMethods && typeof summary.handoffMethods === "object" ? summary.handoffMethods : {}; const blockingReasons = Array.isArray(summary.blockingReasons) ? [...summary.blockingReasons] : []; const displayRows = Array.isArray(renderState?.rows) ? renderState.rows.map(({ id, label, value }) => ({ id, label, value, })) : []; const ready = snapshot?.ready === true && renderState?.ready === true && blockingReasons.length === 0; return { apiName: "ini-panel-shell-session-handoff-action-plan", planVersion: 1, phase: ready ? "ready" : "blocked", ready, statusLine: renderState?.statusLine ?? "Blocked: No handoff render state", targetPage: summary.targetPage ?? null, requiredMethods: { loadSessionState: handoffMethods.loadSessionState ?? null, readStatus: handoffMethods.readStatus ?? null, }, blockingReasons, displayRows, nextSteps: ready ? [ { id: "open-control-page", label: "Open control page target", targetPage: summary.targetPage ?? null, }, { id: "read-readonly-status", label: "Read readonly status", methods: { loadSessionState: handoffMethods.loadSessionState ?? null, readStatus: handoffMethods.readStatus ?? null, }, }, ] : [ { id: "resolve-blockers", label: "Resolve handoff blockers", blockingReasons, displayRows, }, ], }; } export function createIniPanelShellSessionHandoffChecklist({ actionPlan = createIniPanelShellSessionHandoffActionPlan(), renderState = createIniPanelShellSessionHandoffRenderState(), } = {}) { const displayRows = Array.isArray(actionPlan?.displayRows) ? actionPlan.displayRows.map(({ id, label, value }) => ({ id, label, value })) : []; const blockingReasons = Array.isArray(actionPlan?.blockingReasons) ? [...actionPlan.blockingReasons] : []; const targetPageReady = Boolean(actionPlan?.targetPage?.href); const loadMethodReady = Boolean(actionPlan?.requiredMethods?.loadSessionState); const readMethodReady = Boolean(actionPlan?.requiredMethods?.readStatus); const ready = actionPlan?.ready === true && renderState?.ready === true && targetPageReady && loadMethodReady && readMethodReady && blockingReasons.length === 0; const checks = [ { id: "render-state", label: "Render state", status: renderState?.ready ? "pass" : "blocked", value: renderState?.statusLine ?? "missing", }, { id: "target-page", label: "Control target", status: targetPageReady ? "pass" : "blocked", value: actionPlan?.targetPage?.href ?? "missing", }, { id: "load-session-method", label: "Load session method", status: loadMethodReady ? "pass" : "blocked", value: actionPlan?.requiredMethods?.loadSessionState ?? "missing", }, { id: "read-status-method", label: "Read status method", status: readMethodReady ? "pass" : "blocked", value: actionPlan?.requiredMethods?.readStatus ?? "missing", }, { id: "blocking-reasons", label: "Blocking reasons", status: blockingReasons.length === 0 ? "pass" : "blocked", value: blockingReasons.length === 0 ? "none" : blockingReasons.join(", "), }, ]; return { apiName: "ini-panel-shell-session-handoff-checklist", checklistVersion: 1, phase: ready ? "ready" : "blocked", ready, statusLine: actionPlan?.statusLine ?? renderState?.statusLine ?? "Blocked: No handoff action plan", checks, displayRows, nextStepIds: Array.isArray(actionPlan?.nextSteps) ? actionPlan.nextSteps.map(({ id }) => id) : [], blockingReasons, }; } export function createIniPanelShellSessionHandoffPackage({ shellIntegrationManifest = createIniPanelShellIntegrationManifest(), launchApiManifest = shellIntegrationManifest.launchApiManifest ?? createIniPanelLaunchApiManifest(), controlPageApiManifest = shellIntegrationManifest.controlPageApiManifest ?? createControlPageBrowserApiManifest(), snapshot = createIniPanelShellSessionHandoffSnapshot(), renderState = createIniPanelShellSessionHandoffRenderState(snapshot), actionPlan = createIniPanelShellSessionHandoffActionPlan({ snapshot, renderState }), checklist = createIniPanelShellSessionHandoffChecklist({ actionPlan, renderState }), } = {}) { return { apiName: "ini-panel-shell-session-handoff-package", packageVersion: 1, phase: checklist.phase, ready: Boolean(checklist.ready), statusLine: checklist.statusLine, manifests: { shellIntegration: shellIntegrationManifest, launchApi: launchApiManifest, controlPageApi: controlPageApiManifest, launchMethods: Array.isArray(launchApiManifest?.methods) ? [...launchApiManifest.methods] : [], controlPageMethods: Array.isArray(controlPageApiManifest?.methods) ? [...controlPageApiManifest.methods] : [], }, snapshot, renderState, actionPlan, checklist, }; } export function createIniPanelShellSessionHandoffPackageSchema() { return { apiName: "ini-panel-shell-session-handoff-package", packageVersion: 1, fields: [ "apiName", "packageVersion", "phase", "ready", "statusLine", "manifests", "snapshot", "renderState", "actionPlan", "checklist", ], manifestFields: [ "shellIntegration", "launchApi", "controlPageApi", "launchMethods", "controlPageMethods", ], }; } export function isSupportedIniPanelShellSessionHandoffPackage( handoffPackage, schema = createIniPanelShellSessionHandoffPackageSchema(), ) { if (!handoffPackage || typeof handoffPackage !== "object") { return false; } if (handoffPackage.apiName !== schema.apiName || handoffPackage.packageVersion !== schema.packageVersion) { return false; } if (!schema.fields.every((fieldName) => Object.hasOwn(handoffPackage, fieldName))) { return false; } const manifests = handoffPackage.manifests; if (!manifests || typeof manifests !== "object") { return false; } return schema.manifestFields.every((fieldName) => Object.hasOwn(manifests, fieldName)); } export function createIniPanelShellSessionHandoffPackageReport( handoffPackage = createIniPanelShellSessionHandoffPackage(), schema = createIniPanelShellSessionHandoffPackageSchema(), ) { const packageObject = handoffPackage && typeof handoffPackage === "object" ? handoffPackage : {}; const manifests = packageObject.manifests && typeof packageObject.manifests === "object" ? packageObject.manifests : {}; const missingPackageFields = schema.fields.filter((fieldName) => !Object.hasOwn(packageObject, fieldName)); const missingManifestFields = schema.manifestFields.filter((fieldName) => !Object.hasOwn(manifests, fieldName)); const mismatchFields = [ ...(packageObject.apiName === schema.apiName ? [] : ["apiName"]), ...(packageObject.packageVersion === schema.packageVersion ? [] : ["packageVersion"]), ]; const blockingReasons = Array.isArray(packageObject?.checklist?.blockingReasons) ? [...packageObject.checklist.blockingReasons] : []; const packageSupported = missingPackageFields.length === 0 && missingManifestFields.length === 0 && mismatchFields.length === 0; const ready = packageSupported && packageObject.ready === true && blockingReasons.length === 0; return { apiName: "ini-panel-shell-session-handoff-package-report", reportVersion: 1, phase: ready ? "ready" : "blocked", ready, packageSupported, packageReady: ready, statusLine: packageObject.statusLine ?? (ready ? "Ready: No blocking reasons" : "Blocked: Package unsupported"), schema: { apiName: schema.apiName, packageVersion: schema.packageVersion, fields: [...schema.fields], manifestFields: [...schema.manifestFields], }, package: { apiName: packageObject.apiName ?? null, packageVersion: packageObject.packageVersion ?? null, phase: packageObject.phase ?? null, ready: packageObject.ready ?? false, }, missingPackageFields, missingManifestFields, mismatchFields, blockingReasons, rows: [ { id: "package-supported", label: "Package schema", value: packageSupported ? "supported" : "blocked", }, { id: "package-ready", label: "Package readiness", value: ready ? "ready" : "blocked", }, { id: "missing-package-fields", label: "Missing package fields", value: missingPackageFields.length === 0 ? "none" : missingPackageFields.join(", "), }, { id: "missing-manifest-fields", label: "Missing manifest fields", value: missingManifestFields.length === 0 ? "none" : missingManifestFields.join(", "), }, { id: "mismatch-fields", label: "Mismatched fields", value: mismatchFields.length === 0 ? "none" : mismatchFields.join(", "), }, { id: "blocking-reasons", label: "Blocking reasons", value: blockingReasons.length === 0 ? "none" : blockingReasons.join(", "), }, ], }; } export function createIniPanelShellSessionHandoffPackageDisplayViewModel( report = createIniPanelShellSessionHandoffPackageReport(), ) { const missingPackageFields = Array.isArray(report?.missingPackageFields) ? report.missingPackageFields : []; const missingManifestFields = Array.isArray(report?.missingManifestFields) ? report.missingManifestFields : []; const mismatchFields = Array.isArray(report?.mismatchFields) ? report.mismatchFields : []; const blockingReasons = Array.isArray(report?.blockingReasons) ? report.blockingReasons : []; const problems = [ ...missingPackageFields, ...missingManifestFields, ...mismatchFields, ...blockingReasons, ]; return { phase: report?.phase === "ready" ? "ready" : "blocked", title: "Shell handoff package", statusText: report?.ready ? "Ready" : "Blocked", detailText: problems.length === 0 ? "No blocking reasons" : problems.join(", "), rows: Array.isArray(report?.rows) ? report.rows.map(({ id, label, value }) => ({ id, label, value, })) : [], }; } export function createIniPanelShellViewModel(entries = getVisibleIniPanelEntries()) { const shellIntegrationManifest = createIniPanelShellIntegrationManifest(entries); const shellIntegrationReadiness = createIniPanelShellIntegrationReadiness(shellIntegrationManifest); const shellIntegrationWorkflowPlan = createIniPanelShellIntegrationWorkflowPlan({ manifest: shellIntegrationManifest, readiness: shellIntegrationReadiness, }); const shellSessionHandoffSummary = createIniPanelShellSessionHandoffSummary({ manifest: shellIntegrationManifest, readiness: shellIntegrationReadiness, workflowPlan: shellIntegrationWorkflowPlan, }); const shellSessionHandoffCompatibilityReport = createIniPanelShellSessionHandoffCompatibilityReport( shellSessionHandoffSummary, ); const shellSessionHandoffDisplayViewModel = createIniPanelShellSessionHandoffDisplayViewModel( shellSessionHandoffCompatibilityReport, ); const shellSessionHandoffSnapshot = createIniPanelShellSessionHandoffSnapshot({ readiness: shellIntegrationReadiness, summary: shellSessionHandoffSummary, compatibilityReport: shellSessionHandoffCompatibilityReport, displayViewModel: shellSessionHandoffDisplayViewModel, }); const shellSessionHandoffRenderState = createIniPanelShellSessionHandoffRenderState( shellSessionHandoffSnapshot, ); const shellSessionHandoffActionPlan = createIniPanelShellSessionHandoffActionPlan({ snapshot: shellSessionHandoffSnapshot, renderState: shellSessionHandoffRenderState, }); const shellSessionHandoffChecklist = createIniPanelShellSessionHandoffChecklist({ actionPlan: shellSessionHandoffActionPlan, renderState: shellSessionHandoffRenderState, }); const shellSessionHandoffPackage = createIniPanelShellSessionHandoffPackage({ shellIntegrationManifest, launchApiManifest: shellIntegrationManifest.launchApiManifest, controlPageApiManifest: shellIntegrationManifest.controlPageApiManifest, snapshot: shellSessionHandoffSnapshot, renderState: shellSessionHandoffRenderState, actionPlan: shellSessionHandoffActionPlan, checklist: shellSessionHandoffChecklist, }); const shellSessionHandoffPackageReport = createIniPanelShellSessionHandoffPackageReport( shellSessionHandoffPackage, ); const shellSessionHandoffPackageDisplayViewModel = createIniPanelShellSessionHandoffPackageDisplayViewModel( shellSessionHandoffPackageReport, ); return { pages: entries.map(({ id, href, title }) => ({ id, href, title, })), launcherLinks: createIniPanelLauncherLinkViewModel(entries), shellIntegrationSchema: createIniPanelShellIntegrationSchema(), shellIntegrationManifest, shellIntegrationReadiness, shellIntegrationWorkflowPlan, shellSessionHandoffSummarySchema: createIniPanelShellSessionHandoffSummarySchema(), shellSessionHandoffSummary, shellSessionHandoffCompatibilityReport, shellSessionHandoffDisplayViewModel, shellSessionHandoffSnapshot, shellSessionHandoffRenderState, shellSessionHandoffActionPlan, shellSessionHandoffChecklist, shellSessionHandoffPackage, shellSessionHandoffPackageSchema: createIniPanelShellSessionHandoffPackageSchema(), shellSessionHandoffPackageReport, shellSessionHandoffPackageDisplayViewModel, launchApiSchema: createIniPanelLaunchApiSchema(), launchApiManifest: createIniPanelLaunchApiManifest(entries), isSupportedShellIntegrationManifest: isSupportedIniPanelShellIntegrationManifest, createShellIntegrationReadiness: createIniPanelShellIntegrationReadiness, createShellIntegrationWorkflowPlan: createIniPanelShellIntegrationWorkflowPlan, isSupportedShellSessionHandoffSummary: isSupportedIniPanelShellSessionHandoffSummary, createShellSessionHandoffSummarySchema: createIniPanelShellSessionHandoffSummarySchema, createShellSessionHandoffSummary: createIniPanelShellSessionHandoffSummary, createShellSessionHandoffCompatibilityReport: createIniPanelShellSessionHandoffCompatibilityReport, createShellSessionHandoffDisplayViewModel: createIniPanelShellSessionHandoffDisplayViewModel, createShellSessionHandoffSnapshot: createIniPanelShellSessionHandoffSnapshot, createShellSessionHandoffRenderState: createIniPanelShellSessionHandoffRenderState, createShellSessionHandoffActionPlan: createIniPanelShellSessionHandoffActionPlan, createShellSessionHandoffChecklist: createIniPanelShellSessionHandoffChecklist, createShellSessionHandoffPackage: createIniPanelShellSessionHandoffPackage, createShellSessionHandoffPackageSchema: createIniPanelShellSessionHandoffPackageSchema, createShellSessionHandoffPackageReport: createIniPanelShellSessionHandoffPackageReport, createShellSessionHandoffPackageDisplayViewModel: createIniPanelShellSessionHandoffPackageDisplayViewModel, isSupportedShellSessionHandoffPackage: isSupportedIniPanelShellSessionHandoffPackage, controlPage: { browserApiSchema: createControlPageBrowserApiSchema(), browserApiManifest: createControlPageBrowserApiManifest(), browserApiMethods: createControlPageBrowserApiMethods(), contract: createIniPanelShellControlPageContract(), createController: createMachineSessionControlPageController, isSupportedBrowserApiManifest: isSupportedIniPanelControlPageApiManifest, loadSessionState: loadMachineSessionForControlPageWorkflow, readonlyStatusApiManifest: createMachineSessionReadonlyStatusApiManifest(), readStatus: readMachineSessionReadonlyStatus, refresh: refreshMachineSessionControlPage, renderView: renderMachineSessionControlView, }, }; }