63 lines
6.3 KiB
JavaScript
63 lines
6.3 KiB
JavaScript
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
const root=process.cwd();const failures=[]
|
|
const read=p=>fs.readFileSync(path.join(root,p),'utf8')
|
|
const must=(text,needles,scope)=>needles.forEach(needle=>{if(!text.includes(needle))failures.push(`${scope} missing ${needle}`)})
|
|
|
|
const lookupContract=read('packages/kbx-contracts/src/lookup.ts')
|
|
must(lookupContract,['KbxLookupColumnDefinition','metadata.${string}','columns?: KbxLookupColumnDefinition[]','pageSize?: number'],'lookup presentation contract')
|
|
const lookup=read('packages/kbx-ui/src/components/KbxLookup.vue')
|
|
must(lookup,['resolveSequence','조회 공급자가 구성되지 않았습니다.','선택 정보를 불러오지 못했습니다.','코드를 확인하지 못했습니다.',':columns="columns"',':page-size="pageSize"'],'lookup async guard')
|
|
const lookupDialog=read('packages/kbx-ui/src/components/KbxLookupDialog.vue')
|
|
must(lookupDialog,["import KbxDialog from './KbxDialog.vue'",'requestSequence','pageCount','조회하지 못했습니다.','조회된 항목이 없습니다.','aria-selected','scrollSelectedIntoView'],'lookup dialog maturity')
|
|
if(/primevue\//.test(lookupDialog))failures.push('KbxLookupDialog must compose KBX primitives instead of importing PrimeVue directly')
|
|
|
|
const excelContract=read('packages/kbx-contracts/src/excel.ts')
|
|
must(excelContract,['export interface KbxImportFailure','failure?: KbxImportFailure'],'import failure contract')
|
|
const importGuard=read('packages/kbx-ui/src/excel/importGuard.ts')
|
|
must(importGuard,['validateKbxImportMappings','duplicate-target','required-missing','validateKbxImportFileCandidate',"endsWith('.xlsx')"],'import guard')
|
|
const excel=read('packages/kbx-ui/src/components/KbxExcelImport.vue')
|
|
must(excel,['localMapping = ref<KbxImportMapping[] | null>(null)','watch(() => props.session?.id','mappingIssues','KbxConfirm','requestCommit','isFailed','isCancelled','isPartial','새 파일로 다시 시작'],'Excel import recovery/mapping maturity')
|
|
|
|
const operations=read('packages/kbx-contracts/src/operations.ts')
|
|
must(operations,["permissionMode?: 'hide' | 'disable'"],'exception permission UX contract')
|
|
const exceptionDrawer=read('packages/kbx-ui/src/components/KbxExceptionDetailDrawer.vue')
|
|
must(exceptionDrawer,['KbxPermissionHostKey','KbxDrawer','permissionMode===\'disable\'','이 작업을 실행할 권한이 없습니다.','data-kbx-component="exception-detail-drawer"'],'exception drawer hardening')
|
|
|
|
const aiContract=read('packages/kbx-contracts/src/ai.ts')
|
|
must(aiContract,['KbxAiProposalValidationState','validation?: KbxAiProposalValidation','KbxAiAnswerAction','requiredCapability?: KbxAiCapability','requiredPermission?: string'],'AI action/validation contract')
|
|
const assistant=read('packages/kbx-ui/src/components/KbxAiAssistant.vue')
|
|
must(assistant,['KbxPermissionHostKey','actionAllowed','proposalAllowed','lastSubmitted','다시 질문','currentScreenLabel','현재 사용자 권한 또는 AI 허용 범위를 벗어난'],'AI assistant guard/recovery')
|
|
if(assistant.includes('{{ context.screenId }}'))failures.push('AI assistant must not expose ScreenId as the default user-facing current-screen label')
|
|
const proposal=read('packages/kbx-ui/src/components/KbxProposalPanel.vue')
|
|
must(proposal,['KbxPermissionHostKey','permissionAllowed','validationAllowed','data-validation','서버에서 대상·권한·업무규칙','대상 {{proposal.targets.length.toLocaleString()}}건'],'AI proposal enforcement')
|
|
const appFrame=read('apps/web/src/shell/KbxAppFrame.vue')
|
|
must(appFrame,['utilityAiError','네트워크 상태를 확인한 후 다시 질문하세요.',':ai-error="utilityAiError"',':ai-current-screen-label="utilityScreen?.title"'],'AI shell error recovery')
|
|
|
|
const barcodeGuard=read('packages/kbx-ui/src/wms/barcodeGuard.ts')
|
|
must(barcodeGuard,['normalizeKbxBarcode','isKbxBarcodeDuplicate'],'barcode guard')
|
|
const barcode=read('packages/kbx-ui/src/wms/KbxBarcodeCapture.vue')
|
|
must(barcode,['maxLength?: number','duplicateDebounceMs?: number','duplicateIgnored','isKbxBarcodeDuplicate','defineExpose({submitCamera,reset})','visibilitychange'],'barcode capture hardening')
|
|
|
|
const home=read('packages/kbx-ui/src/shell/KbxHomePage.vue')
|
|
must(home,['regularOpenCount','open:regularOpenCount.value','watch(modules','visibleScreenCount','aria-live="polite"'],'Home navigation resilience')
|
|
|
|
const templateManifest=read('packages/kbx-ui/src/registry/templateManifest.ts')
|
|
const componentManifest=read('packages/kbx-ui/src/registry/componentManifest.ts')
|
|
const catalog=read('packages/kbx-ui/src/catalog/componentCatalog.ts')
|
|
const coreGroups=[...templateManifest.matchAll(/coreComponents:\[([^\]]+)\]/g)]
|
|
const coreComponents=new Set(coreGroups.flatMap(match=>[...match[1].matchAll(/'([^']+)'/g)].map(item=>item[1])))
|
|
const manifestNames=new Set([...componentManifest.matchAll(/name: '([^']+)'/g)].map(match=>match[1]))
|
|
const catalogNames=new Set([...catalog.matchAll(/component:'([^']+)'/g)].map(match=>match[1]))
|
|
for(const component of coreComponents){
|
|
if(!manifestNames.has(component))failures.push(`template core component missing manifest: ${component}`)
|
|
if(!catalogNames.has(component))failures.push(`template core component missing catalog scenarios: ${component}`)
|
|
}
|
|
for(const component of ['KbxLookupDialog','KbxValidationSummary','KbxExceptionSummary','KbxExceptionDetailDrawer','KbxRecordLifecycle','KbxBarcodeCapture','KbxNetworkIndicator','KbxWmsActionButton']){
|
|
if(!catalogNames.has(component))failures.push(`v34 required catalog entry missing: ${component}`)
|
|
}
|
|
must(componentManifest,["KbxLookup', category: 'business', purpose: '비동기 race/error", "KbxLookupDialog', category: 'business', purpose: 'Provider 오류", "KbxExcelImport', category: 'business', purpose: '파일 preflight", "KbxExceptionDetailDrawer', category: 'business', purpose: '표준 Drawer", "KbxAiAssistant', category: 'business', purpose: 'Capability/Permission guard", "KbxBarcodeCapture', category: 'wms', purpose: 'Keyboard-wedge/Camera", "KbxHomePage', category: 'shell', purpose: '미저장·실패 작업"],'v34 component versions/descriptions')
|
|
|
|
if(failures.length){console.error(failures.join('\n'));process.exit(1)}
|
|
console.log(`v34 template/component/home validation passed: ${coreComponents.size} template core components have manifest+catalog coverage; Lookup/Import/Exception/AI/Barcode recovery and permission boundaries hardened; Home counts/filter recovery corrected.`)
|