import { readFileSync } from "node:fs"; const main = readFileSync("src/styles/main.css", "utf8"); const unified = readFileSync("src/styles/unified-page.css", "utf8"); const requiredMainTokens = [ "--ctms-primary", "--ctms-text-main", "--ctms-bg-base", "--ctms-radius", "--ctms-shadow", "--ctms-brand-900", "--ctms-brand-700", "--ctms-neutral-100", "--ctms-neutral-300", "--ctms-focus-ring" ]; const requiredUnifiedTokens = [ "--unified-shell-bg", "--unified-shell-radius", ".unified-action-bar", ".unified-section", ".ctms-page-shell", ".ctms-page-header-row", ".ctms-page-content-grid" ]; const missing = [ ...requiredMainTokens.filter((t) => !main.includes(t)), ...requiredUnifiedTokens.filter((t) => !unified.includes(t)) ]; const pageContractChecks = [ { file: "src/views/ia/ProjectMilestones.vue", groups: [ ["ctms-page-shell", "page"], ["unified-action-bar", "table-card-toolbar"], ["ctms-table-card", "table-card"] ] }, { file: "src/views/ia/SubjectManagement.vue", groups: [ ["ctms-page-shell", "page"], ["unified-action-bar", "table-card-toolbar"], ["ctms-table-card", "table-card"], ["subject-table"] ] }, { file: "src/views/ia/RiskIssueSae.vue", groups: [ ["ctms-page-shell", "page"], ["unified-action-bar", "table-card-toolbar"], ["ctms-table-card", "table-card"], ["risk-table"] ] }, { file: "src/views/ia/RiskIssuePd.vue", groups: [ ["ctms-page-shell", "page"], ["unified-action-bar", "table-card-toolbar"], ["ctms-table-card", "table-card"], ["risk-table"] ] }, { file: "src/views/ia/RiskIssueMonitoringVisits.vue", groups: [ ["ctms-page-shell", "page"], ["unified-action-bar", "monitoring-toolbar", "template-toolbar", "toolbar"], ["ctms-table-card", "table-card", "monitoring-table", "issue-table-panel"], ["issue-table"] ] } ]; for (const { file, groups } of pageContractChecks) { const content = readFileSync(file, "utf8"); for (const group of groups) { if (!group.some((className) => content.includes(className))) { missing.push(`${file}:${group.join("|")}`); } } } const overview = readFileSync("src/views/ia/ProjectOverview.vue", "utf8"); const requiredOverviewGroups = [ ["ctms-page-shell", "page"], ["kpi", "overview-card"], ["unified-section", "overview-container"] ]; for (const group of requiredOverviewGroups) { if (!group.some((className) => overview.includes(className))) { missing.push(`src/views/ia/ProjectOverview.vue:${group.join("|")}`); } } const unifiedShellPages = [ "src/views/fees/ContractFees.vue", "src/views/documents/DocumentList.vue" ]; const requiredUnifiedShellClasses = [ "ctms-page-shell", "unified-action-bar", "unified-shell" ]; for (const file of unifiedShellPages) { const content = readFileSync(file, "utf8"); for (const className of requiredUnifiedShellClasses) { if (!content.includes(className)) { missing.push(`${file}:${className}`); } } } const router = readFileSync("src/router/index.ts", "utf8"); const legacyFileVersionRouteIndex = router.indexOf('name: "FileVersionManagement"'); const canonicalDocumentRouteIndex = router.indexOf('path: "trial/:trialId/documents"', legacyFileVersionRouteIndex); const legacyFileVersionRoute = legacyFileVersionRouteIndex >= 0 && canonicalDocumentRouteIndex > legacyFileVersionRouteIndex ? router.slice(legacyFileVersionRouteIndex, canonicalDocumentRouteIndex) : ""; if (router.includes("FileVersionManagement.vue")) { missing.push("src/router/index.ts:legacy FileVersionManagement component import"); } if (!legacyFileVersionRoute.includes("component: DocumentList")) { missing.push("src/router/index.ts:FileVersionManagement->DocumentList"); } if (!router.includes('next({ name: "DocumentList", params: { trialId: studyStore.currentStudy.id }, replace: true });')) { missing.push("src/router/index.ts:legacy file version canonical redirect"); } const adminProjectDetail = readFileSync("src/views/admin/ProjectDetail.vue", "utf8"); const requiredAdminProjectDetailClasses = [ "ctms-page-shell", "unified-action-bar", "unified-section" ]; for (const className of requiredAdminProjectDetailClasses) { if (!adminProjectDetail.includes(className)) { missing.push(`src/views/admin/ProjectDetail.vue:${className}`); } } const stateComponents = [ "src/components/StateLoading.vue", "src/components/StateEmpty.vue", "src/components/StateError.vue" ]; const requiredStateClasses = [ "ctms-state", "ctms-state-title", "ctms-state-desc" ]; for (const file of stateComponents) { const content = readFileSync(file, "utf8"); for (const className of requiredStateClasses) { if (!content.includes(className)) { missing.push(`${file}:${className}`); } } } if (missing.length) { console.error("UI contract check failed. Missing:", missing.join(", ")); process.exit(1); } console.log("UI contract check passed");