import fs from 'node:fs' import path from 'node:path' const requiredCore=[ 'KbxButton','KbxInput','KbxNumberField','KbxMoneyField','KbxQuantityField','KbxDateField','KbxDateRange','KbxSelect','KbxLookup', 'KbxPageHeader','KbxCommandBar','KbxSearchPanel','KbxFormSection','KbxDataGrid','KbxBulkActionBar','KbxStatus', 'KbxDialog','KbxDrawer','KbxToast','KbxConfirm','KbxExcelMenu','KbxExcelImport','KbxJobProgress','KbxAuditTrail','KbxHelpPanel','KbxAiPanel','KbxProposalPanel', 'KbxListPage','KbxMasterPage','KbxTransactionPage','KbxMasterDetailPage','KbxQueuePage','KbxReconcilePage','KbxWmsMobilePage', ] const manifest=fs.readFileSync('packages/kbx-ui/src/registry/componentManifest.ts','utf8') const index=fs.readFileSync('packages/kbx-ui/src/index.ts','utf8') const catalog=JSON.parse(fs.readFileSync('generated/catalog-manifest.json','utf8')) const generatedComponents=JSON.parse(fs.readFileSync('generated/component-manifest.json','utf8')) const failures=[] for(const entry of generatedComponents){ if(!/^\d+\.\d+\.\d+$/.test(entry.version)) failures.push(`invalid component semver ${entry.name}: ${entry.version}`) } for(const name of requiredCore){ if(!manifest.includes(`name: '${name}'`)) failures.push(`component manifest missing ${name}`) if(!generatedComponents.some(x=>x.name===name)) failures.push(`generated component manifest missing ${name}`) if(!index.includes(`as ${name} }`) && !index.includes(`as ${name} } from`) && !index.includes(`default as ${name}`)) failures.push(`public API missing ${name}`) } for(const name of ['KbxInput','KbxLookup','KbxDataGrid','KbxCommandBar','KbxExcelImport','KbxWmsMobilePage','KbxApplicationShell']){ if(!catalog.some(x=>x.component===name)) failures.push(`catalog missing critical component ${name}`) } const css=fs.readFileSync('packages/kbx-ui/src/tokens/kbx.css','utf8') if(!css.includes(':focus-visible')) failures.push('global focus-visible contract missing') const uiFiles=[] function walk(dir){for(const e of fs.readdirSync(dir,{withFileTypes:true})){const f=path.join(dir,e.name);e.isDirectory()?walk(f):uiFiles.push(f)}} walk('packages/kbx-ui/src') for(const file of uiFiles.filter(x=>x.endsWith('.vue'))){ const text=fs.readFileSync(file,'utf8') if(/outline\s*:\s*none/i.test(text)) failures.push(`${file}: outline:none is forbidden`) } const visual=fs.readFileSync('tests/e2e/kbx-component-catalog.visual.spec.ts','utf8') for(const d of ['compact','comfortable','touch']) if(!visual.includes(`density:'${d}'`)) failures.push(`visual regression missing ${d} density`) const keyboard=fs.readFileSync('tests/e2e/kbx-keyboard-regression.spec.ts','utf8') for(const key of ['F2','F3','F8','Tab','Shift+Tab','Enter','Escape']) if(!keyboard.includes(`'${key}'`) && !keyboard.includes(`('${key}')`)) failures.push(`keyboard regression missing ${key}`) if(failures.length){console.error('KBX component catalog validation failed:\n- '+failures.join('\n- '));process.exit(1)} console.log(`KBX component catalog validation passed: ${requiredCore.length} core APIs, ${catalog.length} catalog entries.`)