Files
cnc_wams/wasm-port/runtime/ui/ini-panel/ui-shell.js

980 lines
34 KiB
JavaScript

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",
"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 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,
);
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,
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,
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,
},
};
}