45 lines
2.5 KiB
JavaScript
45 lines
2.5 KiB
JavaScript
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}`))
|