V13-FE-011: finalize search list layout slice
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const root = process.cwd()
|
||||
const modulesRoot = path.join(root, 'apps/web/src/modules')
|
||||
const helpText = fs.readFileSync(path.join(root, 'apps/web/src/help/helpRegistry.ts'), 'utf8')
|
||||
const recipeContract = JSON.parse(fs.readFileSync(path.join(root, 'contracts/screens/kbx.screen-recipes.json'), 'utf8'))
|
||||
const recipeByCode = new Map(recipeContract.recipes.map(recipe => [recipe.code, recipe]))
|
||||
|
||||
function walk(dir) {
|
||||
return fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => e.isDirectory() ? walk(path.join(dir,e.name)) : [path.join(dir,e.name)])
|
||||
}
|
||||
|
||||
const errors = []
|
||||
const warnings = []
|
||||
const ids = new Set()
|
||||
for (const file of walk(modulesRoot).filter(f => /definition\.ts$/.test(f))) {
|
||||
const text = fs.readFileSync(file, 'utf8')
|
||||
if (!/defineKbxScreen\s*\(/.test(text)) continue
|
||||
const match = text.match(/defineKbxScreen\s*\(\s*\{([\s\S]*?)\}\s*\)/)
|
||||
const body = match?.[1] ?? text
|
||||
const id = body.match(/\bid:\s*['"]([^'"]+)['"]/)?.[1]
|
||||
if (!id) { errors.push(`${file}: missing screen id`); continue }
|
||||
if (ids.has(id)) errors.push(`${id}: duplicated screen id`)
|
||||
ids.add(id)
|
||||
const type = body.match(/\btype:\s*['"]([^'"]+)['"]/)?.[1]
|
||||
const templateCode = body.match(/\btemplateCode:\s*['"]([^'"]+)['"]/)?.[1]
|
||||
if (!templateCode) errors.push(`${id}: templateCode is required`)
|
||||
else { const recipe=recipeByCode.get(templateCode); if(!recipe) errors.push(`${id}: unknown templateCode ${templateCode}`); else if(recipe.type!==type) errors.push(`${id}: templateCode ${templateCode} expects ${recipe.type}, found ${type}`) }
|
||||
const helpKey = body.match(/helpKey:\s*['"]([^'"]+)['"]/)?.[1]
|
||||
if (helpKey !== id) errors.push(`${id}: helpKey must equal screen id`)
|
||||
if (!new RegExp(`['"]${id.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')}['"]\\s*:`).test(helpText)) errors.push(`${id}: helpRegistry entry missing`)
|
||||
if (!/permissions:\s*\[[^\]]+\]/.test(body)) errors.push(`${id}: at least one screen permission is required`)
|
||||
if (!/telemetry:\s*\{\s*enabled:\s*true\s*\}/.test(body)) warnings.push(`${id}: telemetry enabled is recommended`)
|
||||
if (/group:\s*['"]workflow['"][\s\S]{0,180}?\}/.test(body) && !/group:\s*['"]workflow['"][\s\S]{0,150}?permission:/.test(body)) warnings.push(`${id}: review workflow commands without explicit permission`)
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
console.error('KBX screen governance FAILED')
|
||||
errors.forEach(x => console.error(` - ${x}`))
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`KBX screen governance PASS (${ids.size} screens)`)
|
||||
warnings.forEach(x => console.warn(` warning: ${x}`))
|
||||
Reference in New Issue
Block a user