import fs from 'node:fs' const failures=[] const read=p=>fs.readFileSync(p,'utf8') const ui=read('packages/kbx-contracts/src/ui.ts') for(const token of ["'changed'","'warning'","'ai-suggested'","'application'","'page'","'grid'","'dialog'","'editor'"])if(!ui.includes(token))failures.push(`ui interaction contract missing ${token}`) const stateful=[ 'KbxInput.vue','KbxNumberField.vue','KbxDateField.vue','KbxDateRange.vue','KbxSelect.vue','KbxTextarea.vue','KbxLookup.vue','KbxCheckbox.vue','KbxRadio.vue','KbxTextField.vue','KbxBarcodeField.vue','KbxMoneyField.vue','KbxQuantityField.vue', ] for(const file of stateful){ const text=read(`packages/kbx-ui/src/components/${file}`) if(!text.includes('KbxFieldState'))failures.push(`${file} must consume KbxFieldState`) } const searchContract=read('packages/kbx-contracts/src/search.ts') if(!searchContract.includes("'checkbox'"))failures.push('KbxSearchFieldType must include checkbox') const search=read('packages/kbx-ui/src/components/KbxSearchPanel.vue') for(const token of ['rememberChecked','savedSearchRequested',"field.type==='checkbox'",'field.defaultValue'])if(!search.includes(token))failures.push(`KbxSearchPanel missing ${token}`) const grid=read('packages/kbx-ui/src/components/KbxDataGrid.vue') for(const token of ['KbxDataState','changedCells','personalization','savedPreference','preferenceChanged','exportable','emptyText','errorText','savePreference','KbxSummaryBar'])if(!grid.includes(token))failures.push(`KbxDataGrid missing ${token}`) const keyboard=read('packages/kbx-ui/src/keyboard/manager.ts') for(const token of ['scopeRank','protectedBrowserShortcuts','F5','CTRL+L','CTRL+T','CTRL+W','CTRL+R','registerKbxShortcuts'])if(!keyboard.includes(token))failures.push(`keyboard manager missing ${token}`) const composable=read('packages/kbx-ui/src/composables/useKbxShortcuts.ts') if(!composable.includes('registerKbxShortcuts'))failures.push('useKbxShortcuts must delegate to central keyboard manager') for(const file of ['apps/web/src/shell/KbxAppFrame.vue','packages/kbx-ui/src/components/KbxExceptionDetailDrawer.vue']){ if(read(file).includes("addEventListener('keydown'"))failures.push(`${file} bypasses central keyboard manager`) } const tabs=read('packages/kbx-ui/src/components/KbxTabs.vue') for(const token of ['ArrowRight','ArrowLeft','Home','End','aria-controls','tabpanel'])if(!tabs.includes(token))failures.push(`KbxTabs missing ${token}`) const index=read('packages/kbx-ui/src/index.ts') if(!index.includes('KbxDataState'))failures.push('public API missing KbxDataState') const manifest=read('packages/kbx-ui/src/registry/componentManifest.ts') if(!manifest.includes("name: 'KbxDataState'"))failures.push('component manifest missing KbxDataState') const catalog=read('packages/kbx-ui/src/catalog/componentCatalog.ts') if(!catalog.includes("component:'KbxDataState'"))failures.push('component catalog missing KbxDataState') for(const state of ["state:'changed'","state:'warning'","state:'ai-suggested'","state:'empty'"])if(!catalog.includes(state))failures.push(`component catalog missing ${state}`) if(failures.length){console.error('KBX interaction contract validation failed:\n- '+failures.join('\n- '));process.exit(1)} console.log(`Interaction contracts PASS: ${stateful.length} stateful inputs, SearchPanel, DataGrid runtime states, scoped keyboard manager, accessible Tabs.`)