@webpieces/rules-config 0.4.483 → 0.4.484
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/checklist-config.d.ts +4 -22
- package/src/checklist-config.js +23 -64
- package/src/checklist-config.js.map +1 -1
- package/src/checklist-docs-validator.d.ts +6 -5
- package/src/checklist-docs-validator.js +6 -5
- package/src/checklist-docs-validator.js.map +1 -1
- package/src/checklist-validator.d.ts +25 -0
- package/src/checklist-validator.js +78 -0
- package/src/checklist-validator.js.map +1 -0
- package/src/index.d.ts +2 -2
- package/src/index.js +6 -7
- package/src/index.js.map +1 -1
- package/src/pr-gate-config.d.ts +3 -3
- package/src/pr-gate-config.js +11 -21
- package/src/pr-gate-config.js.map +1 -1
- package/src/pr-gate-section-validators.d.ts +11 -8
- package/src/pr-gate-section-validators.js +48 -26
- package/src/pr-gate-section-validators.js.map +1 -1
- package/src/review-json.js +2 -2
- package/src/review-json.js.map +1 -1
- package/src/validate-config.js +2 -2
- package/src/validate-config.js.map +1 -1
- package/templates/webpieces.review-checklists.md +8 -7
- package/src/checklist-manifest.d.ts +0 -36
- package/src/checklist-manifest.js +0 -180
- package/src/checklist-manifest.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/validate-config.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { FieldDef } from './field-def';\nimport { sectionForRule, isHookGuard } from './sections';\nimport { MODIFIED_CODE_MODES } from './rule-configs';\nimport {\n FeatureBranchGuardConfig,\n ReadStaleGuardConfig,\n MergedBranchBashGuardConfig,\n StaleMainBashGuardConfig,\n} from './main-sync-guard-configs';\nimport { validateChecklistsSection, validateNoGateSaltRationale } from './pr-gate-section-validators';\n\n// Re-exported so the isolated validate-checklist-docs target keeps importing it from here.\nexport { validateChecklistsSection };\nimport { DEFAULT_MATCH_RULES } from './match-rules-config';\nimport { toError } from './to-error';\nimport {\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n NoUnmanagedExceptionsConfig,\n CatchErrorPatternConfig,\n ThrowCauseRequiredConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoCustomCssConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n BranchCreationGuardConfig,\n PrCreationOrPushGuardConfig,\n MergeInProgressGuardConfig,\n PrMergeGuardConfig,\n RedirectHowToMergeMainConfig,\n NoFileImportCyclesConfig,\n RuntimeArchitectureConfig,\n NxWiringConfig,\n DiGraphConfig,\n MissingDesignAnnotationConfig,\n NoJsFilesConfig,\n ValidateTsInSrcConfig,\n ValidateArchitectureUnchangedConfig,\n ValidateNoArchitectureCyclesConfig,\n ValidatePackageJsonConfig,\n ValidateVersionsLockedConfig,\n ValidateEslintSyncConfig,\n} from './rule-configs';\nimport { NoClientCreationOutsideServerOrClientConfig } from './no-client-creation-config';\n\n// Thin lookup table — each entry delegates to the class's own SCHEMA.\n// No field lists here; all schemas live with their config class.\nconst RULE_SCHEMAS: Record<string, Record<string, FieldDef>> = {\n 'max-method-lines': MaxMethodLinesConfig.SCHEMA,\n 'max-file-lines': MaxFileLinesConfig.SCHEMA,\n 'require-return-type': RequireReturnTypeConfig.SCHEMA,\n 'no-inline-type-literals': NoInlineTypeLiteralsConfig.SCHEMA,\n 'no-any-unknown': NoAnyUnknownConfig.SCHEMA,\n 'no-implicit-any': NoImplicitAnyConfig.SCHEMA,\n 'prisma-validate-dtos': PrismaValidateDtosConfig.SCHEMA,\n 'prisma-converter': PrismaConverterConfig.SCHEMA,\n 'no-destructure': NoDestructureConfig.SCHEMA,\n 'no-unmanaged-exceptions': NoUnmanagedExceptionsConfig.SCHEMA,\n 'catch-error-pattern': CatchErrorPatternConfig.SCHEMA,\n 'throw-cause-required': ThrowCauseRequiredConfig.SCHEMA,\n 'angular-no-direct-api-in-resolver': AngularNoDirectApiInResolverConfig.SCHEMA,\n 'no-symbol-di-tokens': NoSymbolDiTokensConfig.SCHEMA,\n 'no-client-creation-outside-server-or-client': NoClientCreationOutsideServerOrClientConfig.SCHEMA,\n 'no-custom-css': NoCustomCssConfig.SCHEMA,\n 'no-process-exit-outside-main': NoProcessExitOutsideMainConfig.SCHEMA,\n 'no-function-outside-class': NoFunctionOutsideClassConfig.SCHEMA,\n 'inject-annotation-not-needed-for-concrete-class': InjectAnnotationNotNeededForConcreteClassConfig.SCHEMA,\n 'framework-tag': FrameworkTagConfig.SCHEMA,\n 'role-tag': RoleTagConfig.SCHEMA,\n 'branch-creation-guard': BranchCreationGuardConfig.SCHEMA,\n 'pr-creation-or-push-guard': PrCreationOrPushGuardConfig.SCHEMA,\n 'merge-in-progress-guard': MergeInProgressGuardConfig.SCHEMA,\n 'pr-merge-guard': PrMergeGuardConfig.SCHEMA,\n 'redirect-how-to-merge-main': RedirectHowToMergeMainConfig.SCHEMA,\n 'feature-branch-guard': FeatureBranchGuardConfig.SCHEMA,\n 'read-stale-guard': ReadStaleGuardConfig.SCHEMA,\n 'merged-branch-bash-guard': MergedBranchBashGuardConfig.SCHEMA,\n 'stale-main-bash-guard': StaleMainBashGuardConfig.SCHEMA,\n 'no-file-import-cycles': NoFileImportCyclesConfig.SCHEMA,\n 'runtime-architecture': RuntimeArchitectureConfig.SCHEMA,\n 'nx-wiring': NxWiringConfig.SCHEMA,\n 'di-graph': DiGraphConfig.SCHEMA,\n 'missing-design-annotation': MissingDesignAnnotationConfig.SCHEMA,\n 'no-js-files': NoJsFilesConfig.SCHEMA,\n 'validate-ts-in-src': ValidateTsInSrcConfig.SCHEMA,\n 'validate-architecture-unchanged': ValidateArchitectureUnchangedConfig.SCHEMA,\n 'validate-no-architecture-cycles': ValidateNoArchitectureCyclesConfig.SCHEMA,\n 'validate-packagejson': ValidatePackageJsonConfig.SCHEMA,\n 'validate-versions-locked': ValidateVersionsLockedConfig.SCHEMA,\n 'validate-eslint-sync': ValidateEslintSyncConfig.SCHEMA,\n};\n\n// Every built-in rule name that has a typed schema (code rules + bash guards). The installer uses\n// this (with sectionForRule) to seed a fresh webpieces.config.json with every rule in its section.\nexport function allRuleNames(): readonly string[] {\n return Object.keys(RULE_SCHEMAS);\n}\n\nfunction valueHint(def: FieldDef, key?: string): string {\n // ignoreModifiedUntilEpoch is required on every rule; 0 keeps the rule active (epoch in the\n // past), a future unix epoch (seconds) temporarily disables it. Spell that out for the AI.\n if (key === 'ignoreModifiedUntilEpoch') return '0 (0 = active; future unix-epoch seconds = temporarily off)';\n return def.enumValues\n ? `\"${def.enumValues.join(' | ')}\"`\n : def.type === 'string[]' ? '[\"<string>\", ...]'\n : def.type === 'number' ? '<number>'\n : def.type === 'boolean' ? '<boolean>'\n : '\"<string>\"';\n}\n\n// Scoped modes (narrowest → broadest) that enforce ONLY on what changed, so a rule can be\n// adopted gradually instead of all-at-once. When a rule offers one, recommend the first it\n// supports so a fresh config opts into a low-friction rollout rather than reflexively OFF.\nconst GRADUAL_MODE_PREFERENCE = [\n 'MODIFIED_PROJECTS',\n 'NEW_AND_MODIFIED_CODE',\n 'NEW_AND_MODIFIED_METHODS',\n 'MODIFIED_CLASS',\n 'NEW_METHODS',\n 'NEW_AND_MODIFIED_FILES',\n];\n\n/** A rollout hint for the copy-paste snippet: recommend the narrowest gradual mode the rule supports. */\nfunction rolloutTip(schema: Record<string, FieldDef>): string {\n const modes = schema['mode']?.enumValues ?? [];\n const recommended = GRADUAL_MODE_PREFERENCE.find((m: string) => modes.includes(m));\n if (!recommended) return '';\n const optOut = modes.includes('OFF') ? ' Set \"mode\": \"OFF\" to opt out entirely.' : '';\n return (\n `\\n\\n💡 Recommended: start with \"mode\": \"${recommended}\" — it enforces only on what you ` +\n `actually change, so the rule rolls out gradually (existing code stays grandfathered until ` +\n `you next touch that project/file/method).${optOut}`\n );\n}\n\nfunction missingRuleSnippet(ruleName: string, schema: Record<string, FieldDef>): string {\n // Only required fields go in the copy-paste entry. Optional fields (e.g. the\n // universal escape hatches ignoreRuleWhileOnBranch / ignoreModifiedUntilEpoch)\n // are listed separately so the snippet doesn't over-state what's mandatory.\n const fields = Object.keys(schema);\n const required = fields.filter(f => !schema[f].optional);\n const optional = fields.filter(f => schema[f].optional);\n\n const requiredLines = required.map(f => ` \"${f}\": ${valueHint(schema[f], f)}`);\n const section = sectionForRule(ruleName);\n let out =\n `[${ruleName}] Not configured in webpieces.config.json. Add this entry to the \"${section}\" section\\n` +\n `(choose values appropriate for your project):\\n\\n` +\n ` \"${ruleName}\": {\\n${requiredLines.join(',\\n')}\\n }`;\n\n if (optional.length > 0) {\n const optionalLines = optional.map(f => ` \"${f}\": ${valueHint(schema[f], f)}`);\n out +=\n `\\n\\nOptional fields you may add to this rule (omit if not needed):\\n` +\n `${optionalLines.join(',\\n')}`;\n }\n out += rolloutTip(schema);\n return out;\n}\n\n// A config key under rules/hookGuards that the RUNNING validator has no schema for (and no rulesDir is\n// set to supply custom rules). Two very different causes, so the message leads with the common one:\n// 1. Version skew (most common — happens right after a dep bump): webpieces.config.json references a\n// rule a NEWER @webpieces release added, but the installed guard is OLDER and doesn't know it yet.\n// Fix = `pnpm install` (NOT deleting the key — the key is valid, the validator is just stale).\n// 2. A genuinely removed/renamed rule left behind, or a typo. Fix = remove the key.\n// Do NOT tell the AI to delete first — that destroys valid config in case 1 (the trap that made the AI\n// gut a working config instead of running `pnpm install`). The banner in load-config.ts spells out the\n// ordered \"run pnpm install, THEN edit only if it persists\" fix.\nfunction unknownRuleError(ruleName: string): string {\n return (\n `[${ruleName}] Unknown rule — the running @webpieces validator has no schema for it, and no ` +\n `\"rulesDir\" is configured to supply custom rules. Most often this means your installed guard is a ` +\n `release BEHIND this webpieces.config.json: run \\`pnpm install\\` first (see the fix steps below) so ` +\n `the validator learns \"${ruleName}\". Only if it is STILL unknown after a fresh install is it a ` +\n `removed/renamed rule or a typo — then remove the \"${ruleName}\" key from webpieces.config.json.`\n );\n}\n\n// Fields we DELETED from a rule's schema, keyed by \"<rule>.<field>\". The generic unknown-field error\n// (\"Unknown field ... Valid fields: [...]\") is correct but doesn't say WHY the field vanished, so an AI\n// might re-add it. These hints explain the removal so the only action left is to delete the key.\nconst RETIRED_FIELD_HINTS: Record<string, string> = {\n 'runtime-architecture.servicePaths':\n 'This field was removed — it was never read. The runtime graph is derived automatically from ' +\n 'architecture/dependencies.json (apiRelations + project roles). Delete it.',\n 'runtime-architecture.apiProjectPaths':\n 'This field was removed — it was never read. The runtime graph is derived automatically from ' +\n 'architecture/dependencies.json, so there is NO list of api libs to maintain. Delete it (do not ' +\n 'enumerate api libs and do not replace it with a glob).',\n};\n\n// webpieces-disable no-any-unknown -- rawRules values are opaque JSON; each field is validated individually\nexport function validateWebpiecesConfig(\n rawRules: Record<string, Record<string, unknown>>,\n hasCustomRulesDir: boolean = false,\n): string[] {\n const errors: string[] = [];\n\n // Check field-level correctness for rules that are present\n for (const [ruleName, entry] of Object.entries(rawRules)) {\n const schema = RULE_SCHEMAS[ruleName];\n if (!schema) {\n // No built-in schema. With no rulesDir there are no custom rules, so this key is a\n // dead/typo'd entry — tell the AI to remove it (a removed rule like no-shell-substitution\n // lingers here otherwise). With a rulesDir it may be a legitimate custom rule → skip.\n if (!hasCustomRulesDir) errors.push(unknownRuleError(ruleName));\n continue;\n }\n for (const [key, value] of Object.entries(entry)) {\n const fieldDef = schema[key];\n if (!fieldDef) {\n const retiredHint = RETIRED_FIELD_HINTS[`${ruleName}.${key}`];\n const suffix = retiredHint ? ` ${retiredHint}` : '';\n errors.push(`[${ruleName}] Unknown field \"${key}\". Valid fields: [${Object.keys(schema).join(', ')}].${suffix}`);\n continue;\n }\n if (fieldDef.type === 'string[]') {\n if (!Array.isArray(value) || !value.every(v => typeof v === 'string'))\n errors.push(`[${ruleName}] \"${key}\" must be string[], got ${typeof value}.`);\n } else if (typeof value !== fieldDef.type) {\n errors.push(`[${ruleName}] \"${key}\" must be ${fieldDef.type}, got ${typeof value}.`);\n } else if (fieldDef.enumValues && !fieldDef.enumValues.includes(value as string)) {\n errors.push(`[${ruleName}] \"${key}\" = \"${value}\" is not valid. Must be one of: ${fieldDef.enumValues.join(', ')}.`);\n }\n }\n // Required fields must actually be present. Until now the loop above only checked\n // fields that WERE present, so an entry like `{}` (or one missing `mode` /\n // `ignoreModifiedUntilEpoch`) slipped through. Every non-optional schema field is mandatory.\n for (const [key, fieldDef] of Object.entries(schema)) {\n if (!fieldDef.optional && !(key in entry)) {\n errors.push(`[${ruleName}] Missing required field \"${key}\". Add ${key}: ${valueHint(fieldDef, key)}.`);\n }\n }\n }\n\n // Every built-in rule must be explicitly configured — no silent defaults.\n // When a new rule is added to the framework, this check surfaces it immediately\n // with a ready-to-copy snippet so AI can configure it in one pass.\n for (const [ruleName, schema] of Object.entries(RULE_SCHEMAS)) {\n if (!(ruleName in rawRules)) {\n errors.push(missingRuleSnippet(ruleName, schema));\n }\n }\n\n return errors;\n}\n\nconst PR_GATE_MODES = ['ON', 'OFF'] as const;\n// Optional — omitted means DETECT. Kept inline (like prGateExample) to avoid a load-config ↔\n// pr-gate-config import cycle; the canonical list + semantics live in pr-gate-config.ts.\nconst PR_GATE_MERGE_MODES = ['AUTO', 'NONE'] as const;\n\n// Spelled out because the choice is a POLICY decision with a consequence the chooser cannot see:\n// only the AUTO path can put the compact risk/flags body in main's history, because a UI merge is\n// limited to the repo's squash_merge_commit_title/message settings and no setting produces it.\nconst MERGE_MODE_HELP = (\n `Must be one of: ${PR_GATE_MERGE_MODES.join(', ')}.\\n` +\n ` \"AUTO\" — wp-finish-upsert-pr lands the PR: it squash-merges when mergeable, else enables\\n` +\n ` GitHub auto-merge, both with an explicit --subject/--body-file so main's history\\n` +\n ` gets the PR title + the compact risk/flags body. Needs allow_auto_merge on the repo\\n` +\n ` (gh api repos/{owner}/{repo} --jq .allow_auto_merge).\\n` +\n ` \"NONE\" — wp-finish-upsert-pr only opens/updates the PR; a human merges it. The compact body\\n` +\n ` is then impossible, so set the repo's squash_merge_commit_title to PR_TITLE or\\n` +\n ` commits land as the internal \"Squash merge of <branch>\" subject.`\n);\n\n// Copy-paste example for the top-level `pr-gate` block (sibling of `rules`). Kept inline rather\n// than imported from pr-gate-config.ts to avoid a load-config ↔ pr-gate-config import cycle.\nfunction prGateExample(): string {\n return (\n ` \"pr-gate\": {\\n` +\n ` \"mode\": \"ON\",\\n` +\n ` \"buildCommand\": \"<command CI runs to validate a PR, e.g. pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)>\",\\n` +\n ` \"gates\": [\\n` +\n ` { \"name\": \"API Changed\", \"patterns\": [\"libraries/apis/**\", \"**/*Api.ts\"], \"warningColor\": \"yellow\" }\\n` +\n ` ],\\n` +\n ` \"checklists\": [ // OPTIONAL — per-area review, one distinct reviewer subagent each\\n` +\n ` { \"subagent\": \"db-migration-reviewer\", \"doc\": \".claude/review/db-migrations.md\", \"patterns\": [\"**/*.sql\"] }\\n` +\n ` ]\\n` +\n ` }`\n );\n}\n\n// The `gates` array: dashboard-only warning flags. Extracted from validatePrGateSection to keep that\n// method inside the length limit.\n// webpieces-disable no-any-unknown -- `value` is the opaque consumer `gates` value until narrowed here\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this file\nfunction validateGatesSection(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [`[pr-gate] \"gates\" must be an array of { name, patterns, warningColor, disabled? }.`];\n }\n const errors: string[] = [];\n for (let i = 0; i < value.length; i += 1) {\n errors.push(...validateGate(value[i], i));\n }\n return errors;\n}\n\n// webpieces-disable no-any-unknown -- one gate entry from opaque consumer JSON, validated field-by-field\nfunction validateGate(gate: unknown, index: number): string[] {\n if (typeof gate !== 'object' || gate === null) {\n return [`[pr-gate] gates[${index}] must be an object { name, patterns, warningColor, disabled? }.`];\n }\n // webpieces-disable no-any-unknown -- narrowing one opaque gate object from consumer JSON\n const g = gate as Record<string, unknown>;\n const errors: string[] = [];\n if (typeof g['name'] !== 'string') errors.push(`[pr-gate] gates[${index}].name must be a string.`);\n if (!Array.isArray(g['patterns']) || !g['patterns'].every(p => typeof p === 'string'))\n errors.push(`[pr-gate] gates[${index}].patterns must be string[].`);\n if (g['warningColor'] === undefined)\n errors.push(`[pr-gate] gates[${index}].warningColor is required — set it to \"yellow\" or \"red\" (green is implicit when nothing matches).`);\n else if (g['warningColor'] !== 'yellow' && g['warningColor'] !== 'red')\n errors.push(`[pr-gate] gates[${index}].warningColor must be \"yellow\" or \"red\" (green is implicit when nothing matches).`);\n if (g['disabled'] !== undefined && typeof g['disabled'] !== 'boolean')\n errors.push(`[pr-gate] gates[${index}].disabled must be a boolean (example/inactive gate kept in the file).`);\n return errors;\n}\n\n/**\n * Validate the top-level `pr-gate` section. It is REQUIRED (a client that opts out sets mode \"OFF\").\n * `buildCommand` is required unless mode is \"OFF\". Returns human-readable, copy-paste-friendly errors\n * — never throws. The pr-gate block lives outside the FieldDef-driven `rules` schema because its\n * nested `gates`/`checklists` arrays can't be expressed there, so they get structural validation here.\n * `repoRoot` (when known) lets the `checklists[].docs` existence check run.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed below\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this file\nexport function validatePrGateSection(section: unknown, repoRoot?: string): string[] {\n if (section === undefined || section === null) {\n return [\n `[pr-gate] Not configured in webpieces.config.json. Add this block under the \"commands\" ` +\n `section (set \"mode\": \"OFF\" to opt out):\\n\\n${prGateExample()}`,\n ];\n }\n if (typeof section !== 'object' || Array.isArray(section)) {\n return [`[pr-gate] Must be an object. Example:\\n\\n${prGateExample()}`];\n }\n // webpieces-disable no-any-unknown -- narrowing the opaque pr-gate section from consumer JSON\n const s = section as Record<string, unknown>;\n const errors: string[] = [];\n\n if (!('mode' in s)) {\n errors.push(`[pr-gate] Missing required field \"mode\". Must be one of: ${PR_GATE_MODES.join(', ')}.`);\n } else if (typeof s['mode'] !== 'string' || !PR_GATE_MODES.includes(s['mode'] as typeof PR_GATE_MODES[number])) {\n errors.push(`[pr-gate] \"mode\" = \"${String(s['mode'])}\" is not valid. Must be one of: ${PR_GATE_MODES.join(', ')}.`);\n }\n\n // buildCommand and mergeMode are both required whenever the gate is active (mode !== OFF). There\n // is deliberately NO default for mergeMode: whether the tooling may land your PRs is a policy\n // decision, and either guess is wrong for somebody.\n if (s['mode'] !== 'OFF') {\n const cmd = s['buildCommand'];\n if (typeof cmd !== 'string' || cmd.trim() === '') {\n errors.push(\n `[pr-gate] Missing required field \"buildCommand\" — the command CI runs to validate a PR. ` +\n `Add e.g. \"buildCommand\": \"pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)\".`,\n );\n }\n const mm = s['mergeMode'];\n if (!('mergeMode' in s)) {\n errors.push(`[pr-gate] Missing required field \"mergeMode\". ${MERGE_MODE_HELP}`);\n } else if (typeof mm !== 'string' || !PR_GATE_MERGE_MODES.includes(mm as typeof PR_GATE_MERGE_MODES[number])) {\n errors.push(`[pr-gate] \"mergeMode\" = \"${String(mm)}\" is not valid. ${MERGE_MODE_HELP}`);\n }\n }\n\n if ('gates' in s) errors.push(...validateGatesSection(s['gates']));\n\n // Optional extension point: company review checklists. The config only points at ONE manifest doc —\n // { \"doc\": \"...\" } — and the checklist SET lives in that doc. Absent ⇒ no checklists.\n if ('checklists' in s) errors.push(...validateChecklistsSection(s['checklists'], repoRoot));\n\n // Optional server-token salt. Absent ⇒ no token minted, CI enforcement is a no-op. Present ⇒ must be\n // a non-empty string (an empty salt would mint a token anyone can forge from a known-empty secret).\n if ('gateSalt' in s) {\n const salt = s['gateSalt'];\n if (typeof salt !== 'string' || salt.trim() === '') {\n errors.push(`[pr-gate] \"gateSalt\" must be a non-empty string — it is the shared secret the gate token is HMAC'd with. Omit the key entirely to disable server-side token enforcement.`);\n }\n }\n\n errors.push(...validateNoGateSaltRationale(s));\n\n // Optional: publish reviewer output as a PR comment (defaults true). Must be a boolean when present.\n if ('checklistComments' in s && typeof s['checklistComments'] !== 'boolean') {\n errors.push(`[pr-gate] \"checklistComments\" must be a boolean (defaults to true; set false to keep the PR body-only).`);\n }\n\n return errors;\n}\n\nfunction excludePathsExample(): string {\n return '\"excludePaths\": {\\n' +\n ' \"rules\": [\"repositories/**\"],\\n' +\n ' \"guards\": []\\n' +\n ' }';\n}\n\n// One of the two excludePaths lists: required, must be a string[] (may be empty).\n// webpieces-disable no-any-unknown -- `value` is opaque consumer JSON until narrowed here\nfunction validateExcludeList(value: unknown, key: string): string[] {\n if (!(Array.isArray(value) && value.every(p => typeof p === 'string'))) {\n return [`[excludePaths] \"${key}\" must be a string[] of glob paths (use [] for none).`];\n }\n return [];\n}\n\n/**\n * Validate the REQUIRED top-level `excludePaths` block: two independent glob lists (`rules`,\n * `guards`) that suppress hook enforcement per file path. Required so every client upgrading is\n * forced to declare it (as [] to keep today's behavior, or with real paths). Returns copy-paste\n * friendly errors and never throws — same contract as validatePrGateSection.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed below\nexport function validateExcludePaths(section: unknown): string[] {\n if (section === undefined || section === null) {\n return [\n `[excludePaths] Not configured in webpieces.config.json. Add this REQUIRED block ` +\n `(use empty arrays to keep enforcing everywhere):\\n\\n ${excludePathsExample()}`,\n ];\n }\n if (typeof section !== 'object' || Array.isArray(section)) {\n return [`[excludePaths] Must be an object with \"rules\" and \"guards\" string arrays. Example:\\n\\n ${excludePathsExample()}`];\n }\n // webpieces-disable no-any-unknown -- narrowing the opaque excludePaths section from consumer JSON\n const s = section as Record<string, unknown>;\n return [\n ...validateExcludeList(s['rules'], 'rules'),\n ...validateExcludeList(s['guards'], 'guards'),\n ];\n}\n\n// ---------------------------------------------------------------------------\n// match-rules — a new top-level ARRAY section (parallel to pr-gate/excludePaths). Each entry is a\n// client-authored content guard (raw-regex patterns + message + scoping). Validated structurally here\n// the same way pr-gate's `gates` are, because an array of objects can't be expressed in FieldDef schema.\n// ---------------------------------------------------------------------------\n\nfunction matchRulesExample(): string {\n return `\"match-rules\": ${JSON.stringify(DEFAULT_MATCH_RULES, null, 4)}`;\n}\n\n// webpieces-disable no-any-unknown -- generic type guard over an opaque JSON value\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(v => typeof v === 'string');\n}\n\n// Compile a pattern to validate it; returns the error message, or undefined when it compiles.\nfunction regexError(pattern: string): string | undefined {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Constructed only to validate the syntax; the object is intentionally discarded.\n void new RegExp(pattern);\n return undefined;\n } catch (err: unknown) {\n const error = toError(err);\n return error.message;\n }\n}\n\n// One entry of the match-rules array, validated field-by-field (see validateGate for the pattern).\n// webpieces-disable no-any-unknown -- one match-rule entry from opaque consumer JSON, validated field-by-field\nfunction validateMatchRule(entry: unknown, index: number): string[] {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n return [`[match-rules] entry[${index}] must be an object { name, patterns, mainMessage, mode, ignoreModifiedUntilEpoch, ... }.`];\n }\n // webpieces-disable no-any-unknown -- narrowing one opaque match-rule entry from consumer JSON\n const e = entry as Record<string, unknown>;\n const label = typeof e['name'] === 'string' ? `\"${e['name']}\"` : `entry[${index}]`;\n const errors: string[] = [];\n\n if (typeof e['name'] !== 'string' || e['name'].trim() === '')\n errors.push(`[match-rules] entry[${index}].name must be a non-empty string (it is the disable token and report label).`);\n\n if (!isStringArray(e['patterns']) || e['patterns'].length === 0) {\n errors.push(`[match-rules] ${label}.patterns must be a non-empty string[] of regexes.`);\n } else {\n e['patterns'].forEach((p: string, pi: number) => {\n const rxErr = regexError(p);\n if (rxErr) errors.push(`[match-rules] ${label}.patterns[${pi}] is not a valid regex: ${rxErr}`);\n });\n }\n\n if (typeof e['mainMessage'] !== 'string' || e['mainMessage'].trim() === '')\n errors.push(`[match-rules] ${label}.mainMessage must be a non-empty string.`);\n\n if (typeof e['mode'] !== 'string' || !MODIFIED_CODE_MODES.includes(e['mode'] as typeof MODIFIED_CODE_MODES[number]))\n errors.push(`[match-rules] ${label}.mode must be one of: ${MODIFIED_CODE_MODES.join(', ')}.`);\n\n if (typeof e['ignoreModifiedUntilEpoch'] !== 'number')\n errors.push(`[match-rules] ${label}.ignoreModifiedUntilEpoch must be a number (0 = active; future unix-epoch seconds = temporarily off).`);\n\n if (e['options'] !== undefined && !isStringArray(e['options']))\n errors.push(`[match-rules] ${label}.options must be a string[] (omit if not needed).`);\n if (e['allowedPaths'] !== undefined && !isStringArray(e['allowedPaths']))\n errors.push(`[match-rules] ${label}.allowedPaths must be a string[] of globs (omit if not needed).`);\n if (e['disableAllowed'] !== undefined && typeof e['disableAllowed'] !== 'boolean')\n errors.push(`[match-rules] ${label}.disableAllowed must be a boolean.`);\n if (e['ignoreRuleWhileOnBranch'] !== undefined && typeof e['ignoreRuleWhileOnBranch'] !== 'string')\n errors.push(`[match-rules] ${label}.ignoreRuleWhileOnBranch must be a string.`);\n\n return errors;\n}\n\n/**\n * Validate the REQUIRED top-level `match-rules` array (client-authored content guards). MISSING →\n * one error printing the ready-to-paste `no-fetch` example (add at least this; more can follow).\n * Present-but-`[]` → allowed (a conscious opt-out, matching pr-gate mode:OFF / excludePaths []).\n * Otherwise every entry is validated field-by-field (each regex compile-checked) plus name\n * uniqueness. Copy-paste-friendly errors; never throws — same contract as validatePrGateSection.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed below\nexport function validateMatchRulesSection(section: unknown): string[] {\n if (section === undefined || section === null) {\n return [\n `[match-rules] Not configured in webpieces.config.json. Add this REQUIRED top-level array — ` +\n `seed it with the no-fetch guard below (you can add more entries: no-moment, no-lodash-chain, …):\\n\\n${matchRulesExample()}`,\n ];\n }\n if (!Array.isArray(section)) {\n return [`[match-rules] Must be an array of content-guard objects. Example:\\n\\n${matchRulesExample()}`];\n }\n\n const errors: string[] = [];\n const seen = new Set<string>();\n for (let i = 0; i < section.length; i += 1) {\n errors.push(...validateMatchRule(section[i], i));\n // webpieces-disable no-any-unknown -- reading the name off an opaque entry only to dedupe\n const name = (section[i] as Record<string, unknown> | null)?.['name'];\n if (typeof name === 'string') {\n if (seen.has(name)) errors.push(`[match-rules] duplicate entry name \"${name}\" — each match-rule name must be unique.`);\n seen.add(name);\n }\n }\n return errors;\n}\n\n/**\n * Enforce that each built-in lives in its correct section: code rules under `rules`, bash guards\n * under `hookGuards`. A guard left in `rules` (or a rule placed in `hookGuards`) is reported with a\n * \"move it\" message so the split stays clean. Unknown/custom names are ignored (they may be custom\n * rules from rulesDir). Presence (\"every built-in must be configured\") is checked separately by\n * validateWebpiecesConfig against the merged map.\n */\n// webpieces-disable no-any-unknown -- section maps are opaque consumer JSON\nexport function validateSectionPlacement(\n rulesSection: Record<string, Record<string, unknown>>,\n hookGuardsSection: Record<string, Record<string, unknown>>,\n): string[] {\n const errors: string[] = [];\n for (const name of Object.keys(rulesSection)) {\n if (isHookGuard(name)) {\n errors.push(\n `[${name}] is a hook guard and belongs in the \"hookGuards\" section, not \"rules\". ` +\n `Move it (or run \\`wp-install-ai-hooks --sync\\` to migrate automatically).`,\n );\n }\n }\n for (const name of Object.keys(hookGuardsSection)) {\n // Only flag KNOWN code rules misplaced into hookGuards; unknown names may be custom rules.\n if (!isHookGuard(name) && RULE_SCHEMAS[name]) {\n errors.push(\n `[${name}] is a code rule and belongs in the \"rules\" section, not \"hookGuards\". ` +\n `Move it (or run \\`wp-install-ai-hooks --sync\\` to migrate automatically).`,\n );\n }\n }\n return errors;\n}\n\n/**\n * Validate the `commands` section: its `pr-gate` block (delegated to validatePrGateSection) plus the\n * optional command-string fields. Also surfaces a migration error if a DEPRECATED top-level `pr-gate`\n * block is still present, telling the consumer to move it under `commands`.\n */\n// webpieces-disable no-any-unknown -- `commands`/`legacyPrGate` are opaque consumer JSON\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this file\nexport function validateCommandsSection(commands: unknown, legacyPrGate: unknown, repoRoot?: string): string[] {\n const errors: string[] = [];\n\n if (legacyPrGate !== undefined) {\n errors.push(\n `[pr-gate] The top-level \"pr-gate\" block is deprecated. Move it under the \"commands\" ` +\n `section as commands[\"pr-gate\"] (run \\`wp-install-ai-hooks --sync\\` to migrate automatically).`,\n );\n }\n\n if (commands !== undefined && (typeof commands !== 'object' || commands === null || Array.isArray(commands))) {\n errors.push(`[commands] Must be an object { \"pr-gate\": {...}, \"upsertPr\": \"...\", \"mergeComplete\": \"...\" }.`);\n return errors;\n }\n\n // webpieces-disable no-any-unknown -- narrowing the opaque commands section from consumer JSON\n const c = (commands ?? {}) as Record<string, unknown>;\n\n // pr-gate is required (set mode OFF to opt out). Prefer commands[\"pr-gate\"]; fall back to the\n // legacy top-level block so an un-migrated file still validates its gate config.\n errors.push(...validatePrGateSection(c['pr-gate'] ?? legacyPrGate, repoRoot));\n\n // Canonical guard-hint strings live under commands.guardHints; each value, when present, must be a\n // non-empty command string. The two names bind to the two guards that surface them in fix hints.\n const hints = c['guardHints'];\n if (hints !== undefined) {\n if (typeof hints !== 'object' || hints === null || Array.isArray(hints)) {\n errors.push(`[commands] \"guardHints\" must be an object { \"prCreationOrPush\": \"...\", \"mergeInProgress\": \"...\" }.`);\n } else {\n // webpieces-disable no-any-unknown -- narrowing the opaque guardHints object from consumer JSON\n const h = hints as Record<string, unknown>;\n for (const field of ['prCreationOrPush', 'mergeInProgress']) {\n if (field in h && (typeof h[field] !== 'string' || (h[field] as string).trim() === '')) {\n errors.push(`[commands] \"guardHints.${field}\" must be a non-empty string (the gated command to run).`);\n }\n }\n }\n }\n\n // Legacy FLAT command strings (pre-guardHints). Still accepted for back-compat; a migrated file\n // uses commands.guardHints instead.\n for (const field of ['upsertPr', 'mergeComplete']) {\n if (field in c && typeof c[field] !== 'string') {\n errors.push(`[commands] \"${field}\" must be a string (the gated command to run).`);\n }\n }\n\n return errors;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"validate-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/validate-config.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { FieldDef } from './field-def';\nimport { sectionForRule, isHookGuard } from './sections';\nimport { MODIFIED_CODE_MODES } from './rule-configs';\nimport {\n FeatureBranchGuardConfig,\n ReadStaleGuardConfig,\n MergedBranchBashGuardConfig,\n StaleMainBashGuardConfig,\n} from './main-sync-guard-configs';\nimport { validateChecklistsSection, validateNoGateSaltRationale } from './pr-gate-section-validators';\n\n// Re-exported so the isolated validate-checklist-docs target keeps importing it from here.\nexport { validateChecklistsSection };\nimport { DEFAULT_MATCH_RULES } from './match-rules-config';\nimport { toError } from './to-error';\nimport {\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n NoUnmanagedExceptionsConfig,\n CatchErrorPatternConfig,\n ThrowCauseRequiredConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoCustomCssConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n BranchCreationGuardConfig,\n PrCreationOrPushGuardConfig,\n MergeInProgressGuardConfig,\n PrMergeGuardConfig,\n RedirectHowToMergeMainConfig,\n NoFileImportCyclesConfig,\n RuntimeArchitectureConfig,\n NxWiringConfig,\n DiGraphConfig,\n MissingDesignAnnotationConfig,\n NoJsFilesConfig,\n ValidateTsInSrcConfig,\n ValidateArchitectureUnchangedConfig,\n ValidateNoArchitectureCyclesConfig,\n ValidatePackageJsonConfig,\n ValidateVersionsLockedConfig,\n ValidateEslintSyncConfig,\n} from './rule-configs';\nimport { NoClientCreationOutsideServerOrClientConfig } from './no-client-creation-config';\n\n// Thin lookup table — each entry delegates to the class's own SCHEMA.\n// No field lists here; all schemas live with their config class.\nconst RULE_SCHEMAS: Record<string, Record<string, FieldDef>> = {\n 'max-method-lines': MaxMethodLinesConfig.SCHEMA,\n 'max-file-lines': MaxFileLinesConfig.SCHEMA,\n 'require-return-type': RequireReturnTypeConfig.SCHEMA,\n 'no-inline-type-literals': NoInlineTypeLiteralsConfig.SCHEMA,\n 'no-any-unknown': NoAnyUnknownConfig.SCHEMA,\n 'no-implicit-any': NoImplicitAnyConfig.SCHEMA,\n 'prisma-validate-dtos': PrismaValidateDtosConfig.SCHEMA,\n 'prisma-converter': PrismaConverterConfig.SCHEMA,\n 'no-destructure': NoDestructureConfig.SCHEMA,\n 'no-unmanaged-exceptions': NoUnmanagedExceptionsConfig.SCHEMA,\n 'catch-error-pattern': CatchErrorPatternConfig.SCHEMA,\n 'throw-cause-required': ThrowCauseRequiredConfig.SCHEMA,\n 'angular-no-direct-api-in-resolver': AngularNoDirectApiInResolverConfig.SCHEMA,\n 'no-symbol-di-tokens': NoSymbolDiTokensConfig.SCHEMA,\n 'no-client-creation-outside-server-or-client': NoClientCreationOutsideServerOrClientConfig.SCHEMA,\n 'no-custom-css': NoCustomCssConfig.SCHEMA,\n 'no-process-exit-outside-main': NoProcessExitOutsideMainConfig.SCHEMA,\n 'no-function-outside-class': NoFunctionOutsideClassConfig.SCHEMA,\n 'inject-annotation-not-needed-for-concrete-class': InjectAnnotationNotNeededForConcreteClassConfig.SCHEMA,\n 'framework-tag': FrameworkTagConfig.SCHEMA,\n 'role-tag': RoleTagConfig.SCHEMA,\n 'branch-creation-guard': BranchCreationGuardConfig.SCHEMA,\n 'pr-creation-or-push-guard': PrCreationOrPushGuardConfig.SCHEMA,\n 'merge-in-progress-guard': MergeInProgressGuardConfig.SCHEMA,\n 'pr-merge-guard': PrMergeGuardConfig.SCHEMA,\n 'redirect-how-to-merge-main': RedirectHowToMergeMainConfig.SCHEMA,\n 'feature-branch-guard': FeatureBranchGuardConfig.SCHEMA,\n 'read-stale-guard': ReadStaleGuardConfig.SCHEMA,\n 'merged-branch-bash-guard': MergedBranchBashGuardConfig.SCHEMA,\n 'stale-main-bash-guard': StaleMainBashGuardConfig.SCHEMA,\n 'no-file-import-cycles': NoFileImportCyclesConfig.SCHEMA,\n 'runtime-architecture': RuntimeArchitectureConfig.SCHEMA,\n 'nx-wiring': NxWiringConfig.SCHEMA,\n 'di-graph': DiGraphConfig.SCHEMA,\n 'missing-design-annotation': MissingDesignAnnotationConfig.SCHEMA,\n 'no-js-files': NoJsFilesConfig.SCHEMA,\n 'validate-ts-in-src': ValidateTsInSrcConfig.SCHEMA,\n 'validate-architecture-unchanged': ValidateArchitectureUnchangedConfig.SCHEMA,\n 'validate-no-architecture-cycles': ValidateNoArchitectureCyclesConfig.SCHEMA,\n 'validate-packagejson': ValidatePackageJsonConfig.SCHEMA,\n 'validate-versions-locked': ValidateVersionsLockedConfig.SCHEMA,\n 'validate-eslint-sync': ValidateEslintSyncConfig.SCHEMA,\n};\n\n// Every built-in rule name that has a typed schema (code rules + bash guards). The installer uses\n// this (with sectionForRule) to seed a fresh webpieces.config.json with every rule in its section.\nexport function allRuleNames(): readonly string[] {\n return Object.keys(RULE_SCHEMAS);\n}\n\nfunction valueHint(def: FieldDef, key?: string): string {\n // ignoreModifiedUntilEpoch is required on every rule; 0 keeps the rule active (epoch in the\n // past), a future unix epoch (seconds) temporarily disables it. Spell that out for the AI.\n if (key === 'ignoreModifiedUntilEpoch') return '0 (0 = active; future unix-epoch seconds = temporarily off)';\n return def.enumValues\n ? `\"${def.enumValues.join(' | ')}\"`\n : def.type === 'string[]' ? '[\"<string>\", ...]'\n : def.type === 'number' ? '<number>'\n : def.type === 'boolean' ? '<boolean>'\n : '\"<string>\"';\n}\n\n// Scoped modes (narrowest → broadest) that enforce ONLY on what changed, so a rule can be\n// adopted gradually instead of all-at-once. When a rule offers one, recommend the first it\n// supports so a fresh config opts into a low-friction rollout rather than reflexively OFF.\nconst GRADUAL_MODE_PREFERENCE = [\n 'MODIFIED_PROJECTS',\n 'NEW_AND_MODIFIED_CODE',\n 'NEW_AND_MODIFIED_METHODS',\n 'MODIFIED_CLASS',\n 'NEW_METHODS',\n 'NEW_AND_MODIFIED_FILES',\n];\n\n/** A rollout hint for the copy-paste snippet: recommend the narrowest gradual mode the rule supports. */\nfunction rolloutTip(schema: Record<string, FieldDef>): string {\n const modes = schema['mode']?.enumValues ?? [];\n const recommended = GRADUAL_MODE_PREFERENCE.find((m: string) => modes.includes(m));\n if (!recommended) return '';\n const optOut = modes.includes('OFF') ? ' Set \"mode\": \"OFF\" to opt out entirely.' : '';\n return (\n `\\n\\n💡 Recommended: start with \"mode\": \"${recommended}\" — it enforces only on what you ` +\n `actually change, so the rule rolls out gradually (existing code stays grandfathered until ` +\n `you next touch that project/file/method).${optOut}`\n );\n}\n\nfunction missingRuleSnippet(ruleName: string, schema: Record<string, FieldDef>): string {\n // Only required fields go in the copy-paste entry. Optional fields (e.g. the\n // universal escape hatches ignoreRuleWhileOnBranch / ignoreModifiedUntilEpoch)\n // are listed separately so the snippet doesn't over-state what's mandatory.\n const fields = Object.keys(schema);\n const required = fields.filter(f => !schema[f].optional);\n const optional = fields.filter(f => schema[f].optional);\n\n const requiredLines = required.map(f => ` \"${f}\": ${valueHint(schema[f], f)}`);\n const section = sectionForRule(ruleName);\n let out =\n `[${ruleName}] Not configured in webpieces.config.json. Add this entry to the \"${section}\" section\\n` +\n `(choose values appropriate for your project):\\n\\n` +\n ` \"${ruleName}\": {\\n${requiredLines.join(',\\n')}\\n }`;\n\n if (optional.length > 0) {\n const optionalLines = optional.map(f => ` \"${f}\": ${valueHint(schema[f], f)}`);\n out +=\n `\\n\\nOptional fields you may add to this rule (omit if not needed):\\n` +\n `${optionalLines.join(',\\n')}`;\n }\n out += rolloutTip(schema);\n return out;\n}\n\n// A config key under rules/hookGuards that the RUNNING validator has no schema for (and no rulesDir is\n// set to supply custom rules). Two very different causes, so the message leads with the common one:\n// 1. Version skew (most common — happens right after a dep bump): webpieces.config.json references a\n// rule a NEWER @webpieces release added, but the installed guard is OLDER and doesn't know it yet.\n// Fix = `pnpm install` (NOT deleting the key — the key is valid, the validator is just stale).\n// 2. A genuinely removed/renamed rule left behind, or a typo. Fix = remove the key.\n// Do NOT tell the AI to delete first — that destroys valid config in case 1 (the trap that made the AI\n// gut a working config instead of running `pnpm install`). The banner in load-config.ts spells out the\n// ordered \"run pnpm install, THEN edit only if it persists\" fix.\nfunction unknownRuleError(ruleName: string): string {\n return (\n `[${ruleName}] Unknown rule — the running @webpieces validator has no schema for it, and no ` +\n `\"rulesDir\" is configured to supply custom rules. Most often this means your installed guard is a ` +\n `release BEHIND this webpieces.config.json: run \\`pnpm install\\` first (see the fix steps below) so ` +\n `the validator learns \"${ruleName}\". Only if it is STILL unknown after a fresh install is it a ` +\n `removed/renamed rule or a typo — then remove the \"${ruleName}\" key from webpieces.config.json.`\n );\n}\n\n// Fields we DELETED from a rule's schema, keyed by \"<rule>.<field>\". The generic unknown-field error\n// (\"Unknown field ... Valid fields: [...]\") is correct but doesn't say WHY the field vanished, so an AI\n// might re-add it. These hints explain the removal so the only action left is to delete the key.\nconst RETIRED_FIELD_HINTS: Record<string, string> = {\n 'runtime-architecture.servicePaths':\n 'This field was removed — it was never read. The runtime graph is derived automatically from ' +\n 'architecture/dependencies.json (apiRelations + project roles). Delete it.',\n 'runtime-architecture.apiProjectPaths':\n 'This field was removed — it was never read. The runtime graph is derived automatically from ' +\n 'architecture/dependencies.json, so there is NO list of api libs to maintain. Delete it (do not ' +\n 'enumerate api libs and do not replace it with a glob).',\n};\n\n// webpieces-disable no-any-unknown -- rawRules values are opaque JSON; each field is validated individually\nexport function validateWebpiecesConfig(\n rawRules: Record<string, Record<string, unknown>>,\n hasCustomRulesDir: boolean = false,\n): string[] {\n const errors: string[] = [];\n\n // Check field-level correctness for rules that are present\n for (const [ruleName, entry] of Object.entries(rawRules)) {\n const schema = RULE_SCHEMAS[ruleName];\n if (!schema) {\n // No built-in schema. With no rulesDir there are no custom rules, so this key is a\n // dead/typo'd entry — tell the AI to remove it (a removed rule like no-shell-substitution\n // lingers here otherwise). With a rulesDir it may be a legitimate custom rule → skip.\n if (!hasCustomRulesDir) errors.push(unknownRuleError(ruleName));\n continue;\n }\n for (const [key, value] of Object.entries(entry)) {\n const fieldDef = schema[key];\n if (!fieldDef) {\n const retiredHint = RETIRED_FIELD_HINTS[`${ruleName}.${key}`];\n const suffix = retiredHint ? ` ${retiredHint}` : '';\n errors.push(`[${ruleName}] Unknown field \"${key}\". Valid fields: [${Object.keys(schema).join(', ')}].${suffix}`);\n continue;\n }\n if (fieldDef.type === 'string[]') {\n if (!Array.isArray(value) || !value.every(v => typeof v === 'string'))\n errors.push(`[${ruleName}] \"${key}\" must be string[], got ${typeof value}.`);\n } else if (typeof value !== fieldDef.type) {\n errors.push(`[${ruleName}] \"${key}\" must be ${fieldDef.type}, got ${typeof value}.`);\n } else if (fieldDef.enumValues && !fieldDef.enumValues.includes(value as string)) {\n errors.push(`[${ruleName}] \"${key}\" = \"${value}\" is not valid. Must be one of: ${fieldDef.enumValues.join(', ')}.`);\n }\n }\n // Required fields must actually be present. Until now the loop above only checked\n // fields that WERE present, so an entry like `{}` (or one missing `mode` /\n // `ignoreModifiedUntilEpoch`) slipped through. Every non-optional schema field is mandatory.\n for (const [key, fieldDef] of Object.entries(schema)) {\n if (!fieldDef.optional && !(key in entry)) {\n errors.push(`[${ruleName}] Missing required field \"${key}\". Add ${key}: ${valueHint(fieldDef, key)}.`);\n }\n }\n }\n\n // Every built-in rule must be explicitly configured — no silent defaults.\n // When a new rule is added to the framework, this check surfaces it immediately\n // with a ready-to-copy snippet so AI can configure it in one pass.\n for (const [ruleName, schema] of Object.entries(RULE_SCHEMAS)) {\n if (!(ruleName in rawRules)) {\n errors.push(missingRuleSnippet(ruleName, schema));\n }\n }\n\n return errors;\n}\n\nconst PR_GATE_MODES = ['ON', 'OFF'] as const;\n// Optional — omitted means DETECT. Kept inline (like prGateExample) to avoid a load-config ↔\n// pr-gate-config import cycle; the canonical list + semantics live in pr-gate-config.ts.\nconst PR_GATE_MERGE_MODES = ['AUTO', 'NONE'] as const;\n\n// Spelled out because the choice is a POLICY decision with a consequence the chooser cannot see:\n// only the AUTO path can put the compact risk/flags body in main's history, because a UI merge is\n// limited to the repo's squash_merge_commit_title/message settings and no setting produces it.\nconst MERGE_MODE_HELP = (\n `Must be one of: ${PR_GATE_MERGE_MODES.join(', ')}.\\n` +\n ` \"AUTO\" — wp-finish-upsert-pr lands the PR: it squash-merges when mergeable, else enables\\n` +\n ` GitHub auto-merge, both with an explicit --subject/--body-file so main's history\\n` +\n ` gets the PR title + the compact risk/flags body. Needs allow_auto_merge on the repo\\n` +\n ` (gh api repos/{owner}/{repo} --jq .allow_auto_merge).\\n` +\n ` \"NONE\" — wp-finish-upsert-pr only opens/updates the PR; a human merges it. The compact body\\n` +\n ` is then impossible, so set the repo's squash_merge_commit_title to PR_TITLE or\\n` +\n ` commits land as the internal \"Squash merge of <branch>\" subject.`\n);\n\n// Copy-paste example for the top-level `pr-gate` block (sibling of `rules`). Kept inline rather\n// than imported from pr-gate-config.ts to avoid a load-config ↔ pr-gate-config import cycle.\nfunction prGateExample(): string {\n return (\n ` \"pr-gate\": {\\n` +\n ` \"mode\": \"ON\",\\n` +\n ` \"buildCommand\": \"<command CI runs to validate a PR, e.g. pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)>\",\\n` +\n ` \"gates\": [\\n` +\n ` { \"name\": \"API Changed\", \"patterns\": [\"libraries/apis/**\", \"**/*Api.ts\"], \"warningColor\": \"yellow\" }\\n` +\n ` ],\\n` +\n ` \"checklists\": [ // OPTIONAL — per-area review, one distinct reviewer subagent each\\n` +\n ` { \"subagent\": \"db-migration-reviewer\", \"doc\": \".claude/review/db-migrations.md\", \"patterns\": [\"**/*.sql\"] }\\n` +\n ` ]\\n` +\n ` }`\n );\n}\n\n// The `gates` array: dashboard-only warning flags. Extracted from validatePrGateSection to keep that\n// method inside the length limit.\n// webpieces-disable no-any-unknown -- `value` is the opaque consumer `gates` value until narrowed here\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this file\nfunction validateGatesSection(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [`[pr-gate] \"gates\" must be an array of { name, patterns, warningColor, disabled? }.`];\n }\n const errors: string[] = [];\n for (let i = 0; i < value.length; i += 1) {\n errors.push(...validateGate(value[i], i));\n }\n return errors;\n}\n\n// webpieces-disable no-any-unknown -- one gate entry from opaque consumer JSON, validated field-by-field\nfunction validateGate(gate: unknown, index: number): string[] {\n if (typeof gate !== 'object' || gate === null) {\n return [`[pr-gate] gates[${index}] must be an object { name, patterns, warningColor, disabled? }.`];\n }\n // webpieces-disable no-any-unknown -- narrowing one opaque gate object from consumer JSON\n const g = gate as Record<string, unknown>;\n const errors: string[] = [];\n if (typeof g['name'] !== 'string') errors.push(`[pr-gate] gates[${index}].name must be a string.`);\n if (!Array.isArray(g['patterns']) || !g['patterns'].every(p => typeof p === 'string'))\n errors.push(`[pr-gate] gates[${index}].patterns must be string[].`);\n if (g['warningColor'] === undefined)\n errors.push(`[pr-gate] gates[${index}].warningColor is required — set it to \"yellow\" or \"red\" (green is implicit when nothing matches).`);\n else if (g['warningColor'] !== 'yellow' && g['warningColor'] !== 'red')\n errors.push(`[pr-gate] gates[${index}].warningColor must be \"yellow\" or \"red\" (green is implicit when nothing matches).`);\n if (g['disabled'] !== undefined && typeof g['disabled'] !== 'boolean')\n errors.push(`[pr-gate] gates[${index}].disabled must be a boolean (example/inactive gate kept in the file).`);\n return errors;\n}\n\n/**\n * Validate the top-level `pr-gate` section. It is REQUIRED (a client that opts out sets mode \"OFF\").\n * `buildCommand` is required unless mode is \"OFF\". Returns human-readable, copy-paste-friendly errors\n * — never throws. The pr-gate block lives outside the FieldDef-driven `rules` schema because its\n * nested `gates`/`checklists` arrays can't be expressed there, so they get structural validation here.\n * `repoRoot` (when known) lets the `checklists[].docs` existence check run.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed below\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this file\nexport function validatePrGateSection(section: unknown, repoRoot?: string): string[] {\n if (section === undefined || section === null) {\n return [\n `[pr-gate] Not configured in webpieces.config.json. Add this block under the \"commands\" ` +\n `section (set \"mode\": \"OFF\" to opt out):\\n\\n${prGateExample()}`,\n ];\n }\n if (typeof section !== 'object' || Array.isArray(section)) {\n return [`[pr-gate] Must be an object. Example:\\n\\n${prGateExample()}`];\n }\n // webpieces-disable no-any-unknown -- narrowing the opaque pr-gate section from consumer JSON\n const s = section as Record<string, unknown>;\n const errors: string[] = [];\n\n if (!('mode' in s)) {\n errors.push(`[pr-gate] Missing required field \"mode\". Must be one of: ${PR_GATE_MODES.join(', ')}.`);\n } else if (typeof s['mode'] !== 'string' || !PR_GATE_MODES.includes(s['mode'] as typeof PR_GATE_MODES[number])) {\n errors.push(`[pr-gate] \"mode\" = \"${String(s['mode'])}\" is not valid. Must be one of: ${PR_GATE_MODES.join(', ')}.`);\n }\n\n // buildCommand and mergeMode are both required whenever the gate is active (mode !== OFF). There\n // is deliberately NO default for mergeMode: whether the tooling may land your PRs is a policy\n // decision, and either guess is wrong for somebody.\n if (s['mode'] !== 'OFF') {\n const cmd = s['buildCommand'];\n if (typeof cmd !== 'string' || cmd.trim() === '') {\n errors.push(\n `[pr-gate] Missing required field \"buildCommand\" — the command CI runs to validate a PR. ` +\n `Add e.g. \"buildCommand\": \"pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)\".`,\n );\n }\n const mm = s['mergeMode'];\n if (!('mergeMode' in s)) {\n errors.push(`[pr-gate] Missing required field \"mergeMode\". ${MERGE_MODE_HELP}`);\n } else if (typeof mm !== 'string' || !PR_GATE_MERGE_MODES.includes(mm as typeof PR_GATE_MERGE_MODES[number])) {\n errors.push(`[pr-gate] \"mergeMode\" = \"${String(mm)}\" is not valid. ${MERGE_MODE_HELP}`);\n }\n }\n\n if ('gates' in s) errors.push(...validateGatesSection(s['gates']));\n\n // Optional extension point: company review checklists, as an ARRAY right here in the config. Absent ⇒\n // none. The removed { doc } manifest shape is rejected with the exact migration edit.\n if ('checklists' in s) errors.push(...validateChecklistsSection(s['checklists'], repoRoot));\n\n // Optional server-token salt. Absent ⇒ no token minted, CI enforcement is a no-op. Present ⇒ must be\n // a non-empty string (an empty salt would mint a token anyone can forge from a known-empty secret).\n if ('gateSalt' in s) {\n const salt = s['gateSalt'];\n if (typeof salt !== 'string' || salt.trim() === '') {\n errors.push(`[pr-gate] \"gateSalt\" must be a non-empty string — it is the shared secret the gate token is HMAC'd with. Omit the key entirely to disable server-side token enforcement.`);\n }\n }\n\n errors.push(...validateNoGateSaltRationale(s));\n\n // Optional: publish reviewer output as a PR comment (defaults true). Must be a boolean when present.\n if ('checklistComments' in s && typeof s['checklistComments'] !== 'boolean') {\n errors.push(`[pr-gate] \"checklistComments\" must be a boolean (defaults to true; set false to keep the PR body-only).`);\n }\n\n return errors;\n}\n\nfunction excludePathsExample(): string {\n return '\"excludePaths\": {\\n' +\n ' \"rules\": [\"repositories/**\"],\\n' +\n ' \"guards\": []\\n' +\n ' }';\n}\n\n// One of the two excludePaths lists: required, must be a string[] (may be empty).\n// webpieces-disable no-any-unknown -- `value` is opaque consumer JSON until narrowed here\nfunction validateExcludeList(value: unknown, key: string): string[] {\n if (!(Array.isArray(value) && value.every(p => typeof p === 'string'))) {\n return [`[excludePaths] \"${key}\" must be a string[] of glob paths (use [] for none).`];\n }\n return [];\n}\n\n/**\n * Validate the REQUIRED top-level `excludePaths` block: two independent glob lists (`rules`,\n * `guards`) that suppress hook enforcement per file path. Required so every client upgrading is\n * forced to declare it (as [] to keep today's behavior, or with real paths). Returns copy-paste\n * friendly errors and never throws — same contract as validatePrGateSection.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed below\nexport function validateExcludePaths(section: unknown): string[] {\n if (section === undefined || section === null) {\n return [\n `[excludePaths] Not configured in webpieces.config.json. Add this REQUIRED block ` +\n `(use empty arrays to keep enforcing everywhere):\\n\\n ${excludePathsExample()}`,\n ];\n }\n if (typeof section !== 'object' || Array.isArray(section)) {\n return [`[excludePaths] Must be an object with \"rules\" and \"guards\" string arrays. Example:\\n\\n ${excludePathsExample()}`];\n }\n // webpieces-disable no-any-unknown -- narrowing the opaque excludePaths section from consumer JSON\n const s = section as Record<string, unknown>;\n return [\n ...validateExcludeList(s['rules'], 'rules'),\n ...validateExcludeList(s['guards'], 'guards'),\n ];\n}\n\n// ---------------------------------------------------------------------------\n// match-rules — a new top-level ARRAY section (parallel to pr-gate/excludePaths). Each entry is a\n// client-authored content guard (raw-regex patterns + message + scoping). Validated structurally here\n// the same way pr-gate's `gates` are, because an array of objects can't be expressed in FieldDef schema.\n// ---------------------------------------------------------------------------\n\nfunction matchRulesExample(): string {\n return `\"match-rules\": ${JSON.stringify(DEFAULT_MATCH_RULES, null, 4)}`;\n}\n\n// webpieces-disable no-any-unknown -- generic type guard over an opaque JSON value\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(v => typeof v === 'string');\n}\n\n// Compile a pattern to validate it; returns the error message, or undefined when it compiles.\nfunction regexError(pattern: string): string | undefined {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Constructed only to validate the syntax; the object is intentionally discarded.\n void new RegExp(pattern);\n return undefined;\n } catch (err: unknown) {\n const error = toError(err);\n return error.message;\n }\n}\n\n// One entry of the match-rules array, validated field-by-field (see validateGate for the pattern).\n// webpieces-disable no-any-unknown -- one match-rule entry from opaque consumer JSON, validated field-by-field\nfunction validateMatchRule(entry: unknown, index: number): string[] {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n return [`[match-rules] entry[${index}] must be an object { name, patterns, mainMessage, mode, ignoreModifiedUntilEpoch, ... }.`];\n }\n // webpieces-disable no-any-unknown -- narrowing one opaque match-rule entry from consumer JSON\n const e = entry as Record<string, unknown>;\n const label = typeof e['name'] === 'string' ? `\"${e['name']}\"` : `entry[${index}]`;\n const errors: string[] = [];\n\n if (typeof e['name'] !== 'string' || e['name'].trim() === '')\n errors.push(`[match-rules] entry[${index}].name must be a non-empty string (it is the disable token and report label).`);\n\n if (!isStringArray(e['patterns']) || e['patterns'].length === 0) {\n errors.push(`[match-rules] ${label}.patterns must be a non-empty string[] of regexes.`);\n } else {\n e['patterns'].forEach((p: string, pi: number) => {\n const rxErr = regexError(p);\n if (rxErr) errors.push(`[match-rules] ${label}.patterns[${pi}] is not a valid regex: ${rxErr}`);\n });\n }\n\n if (typeof e['mainMessage'] !== 'string' || e['mainMessage'].trim() === '')\n errors.push(`[match-rules] ${label}.mainMessage must be a non-empty string.`);\n\n if (typeof e['mode'] !== 'string' || !MODIFIED_CODE_MODES.includes(e['mode'] as typeof MODIFIED_CODE_MODES[number]))\n errors.push(`[match-rules] ${label}.mode must be one of: ${MODIFIED_CODE_MODES.join(', ')}.`);\n\n if (typeof e['ignoreModifiedUntilEpoch'] !== 'number')\n errors.push(`[match-rules] ${label}.ignoreModifiedUntilEpoch must be a number (0 = active; future unix-epoch seconds = temporarily off).`);\n\n if (e['options'] !== undefined && !isStringArray(e['options']))\n errors.push(`[match-rules] ${label}.options must be a string[] (omit if not needed).`);\n if (e['allowedPaths'] !== undefined && !isStringArray(e['allowedPaths']))\n errors.push(`[match-rules] ${label}.allowedPaths must be a string[] of globs (omit if not needed).`);\n if (e['disableAllowed'] !== undefined && typeof e['disableAllowed'] !== 'boolean')\n errors.push(`[match-rules] ${label}.disableAllowed must be a boolean.`);\n if (e['ignoreRuleWhileOnBranch'] !== undefined && typeof e['ignoreRuleWhileOnBranch'] !== 'string')\n errors.push(`[match-rules] ${label}.ignoreRuleWhileOnBranch must be a string.`);\n\n return errors;\n}\n\n/**\n * Validate the REQUIRED top-level `match-rules` array (client-authored content guards). MISSING →\n * one error printing the ready-to-paste `no-fetch` example (add at least this; more can follow).\n * Present-but-`[]` → allowed (a conscious opt-out, matching pr-gate mode:OFF / excludePaths []).\n * Otherwise every entry is validated field-by-field (each regex compile-checked) plus name\n * uniqueness. Copy-paste-friendly errors; never throws — same contract as validatePrGateSection.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed below\nexport function validateMatchRulesSection(section: unknown): string[] {\n if (section === undefined || section === null) {\n return [\n `[match-rules] Not configured in webpieces.config.json. Add this REQUIRED top-level array — ` +\n `seed it with the no-fetch guard below (you can add more entries: no-moment, no-lodash-chain, …):\\n\\n${matchRulesExample()}`,\n ];\n }\n if (!Array.isArray(section)) {\n return [`[match-rules] Must be an array of content-guard objects. Example:\\n\\n${matchRulesExample()}`];\n }\n\n const errors: string[] = [];\n const seen = new Set<string>();\n for (let i = 0; i < section.length; i += 1) {\n errors.push(...validateMatchRule(section[i], i));\n // webpieces-disable no-any-unknown -- reading the name off an opaque entry only to dedupe\n const name = (section[i] as Record<string, unknown> | null)?.['name'];\n if (typeof name === 'string') {\n if (seen.has(name)) errors.push(`[match-rules] duplicate entry name \"${name}\" — each match-rule name must be unique.`);\n seen.add(name);\n }\n }\n return errors;\n}\n\n/**\n * Enforce that each built-in lives in its correct section: code rules under `rules`, bash guards\n * under `hookGuards`. A guard left in `rules` (or a rule placed in `hookGuards`) is reported with a\n * \"move it\" message so the split stays clean. Unknown/custom names are ignored (they may be custom\n * rules from rulesDir). Presence (\"every built-in must be configured\") is checked separately by\n * validateWebpiecesConfig against the merged map.\n */\n// webpieces-disable no-any-unknown -- section maps are opaque consumer JSON\nexport function validateSectionPlacement(\n rulesSection: Record<string, Record<string, unknown>>,\n hookGuardsSection: Record<string, Record<string, unknown>>,\n): string[] {\n const errors: string[] = [];\n for (const name of Object.keys(rulesSection)) {\n if (isHookGuard(name)) {\n errors.push(\n `[${name}] is a hook guard and belongs in the \"hookGuards\" section, not \"rules\". ` +\n `Move it (or run \\`wp-install-ai-hooks --sync\\` to migrate automatically).`,\n );\n }\n }\n for (const name of Object.keys(hookGuardsSection)) {\n // Only flag KNOWN code rules misplaced into hookGuards; unknown names may be custom rules.\n if (!isHookGuard(name) && RULE_SCHEMAS[name]) {\n errors.push(\n `[${name}] is a code rule and belongs in the \"rules\" section, not \"hookGuards\". ` +\n `Move it (or run \\`wp-install-ai-hooks --sync\\` to migrate automatically).`,\n );\n }\n }\n return errors;\n}\n\n/**\n * Validate the `commands` section: its `pr-gate` block (delegated to validatePrGateSection) plus the\n * optional command-string fields. Also surfaces a migration error if a DEPRECATED top-level `pr-gate`\n * block is still present, telling the consumer to move it under `commands`.\n */\n// webpieces-disable no-any-unknown -- `commands`/`legacyPrGate` are opaque consumer JSON\n// webpieces-disable no-function-outside-class -- module-level config validator, matches the rest of this file\nexport function validateCommandsSection(commands: unknown, legacyPrGate: unknown, repoRoot?: string): string[] {\n const errors: string[] = [];\n\n if (legacyPrGate !== undefined) {\n errors.push(\n `[pr-gate] The top-level \"pr-gate\" block is deprecated. Move it under the \"commands\" ` +\n `section as commands[\"pr-gate\"] (run \\`wp-install-ai-hooks --sync\\` to migrate automatically).`,\n );\n }\n\n if (commands !== undefined && (typeof commands !== 'object' || commands === null || Array.isArray(commands))) {\n errors.push(`[commands] Must be an object { \"pr-gate\": {...}, \"upsertPr\": \"...\", \"mergeComplete\": \"...\" }.`);\n return errors;\n }\n\n // webpieces-disable no-any-unknown -- narrowing the opaque commands section from consumer JSON\n const c = (commands ?? {}) as Record<string, unknown>;\n\n // pr-gate is required (set mode OFF to opt out). Prefer commands[\"pr-gate\"]; fall back to the\n // legacy top-level block so an un-migrated file still validates its gate config.\n errors.push(...validatePrGateSection(c['pr-gate'] ?? legacyPrGate, repoRoot));\n\n // Canonical guard-hint strings live under commands.guardHints; each value, when present, must be a\n // non-empty command string. The two names bind to the two guards that surface them in fix hints.\n const hints = c['guardHints'];\n if (hints !== undefined) {\n if (typeof hints !== 'object' || hints === null || Array.isArray(hints)) {\n errors.push(`[commands] \"guardHints\" must be an object { \"prCreationOrPush\": \"...\", \"mergeInProgress\": \"...\" }.`);\n } else {\n // webpieces-disable no-any-unknown -- narrowing the opaque guardHints object from consumer JSON\n const h = hints as Record<string, unknown>;\n for (const field of ['prCreationOrPush', 'mergeInProgress']) {\n if (field in h && (typeof h[field] !== 'string' || (h[field] as string).trim() === '')) {\n errors.push(`[commands] \"guardHints.${field}\" must be a non-empty string (the gated command to run).`);\n }\n }\n }\n }\n\n // Legacy FLAT command strings (pre-guardHints). Still accepted for back-compat; a migrated file\n // uses commands.guardHints instead.\n for (const field of ['upsertPr', 'mergeComplete']) {\n if (field in c && typeof c[field] !== 'string') {\n errors.push(`[commands] \"${field}\" must be a string (the gated command to run).`);\n }\n }\n\n return errors;\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Webpieces review checklists — how to satisfy them
|
|
2
2
|
|
|
3
3
|
Your repo defines company review checklists in `pr-gate.checklists` in `webpieces.config.json` — an
|
|
4
|
-
array of `{ subagent, doc?, patterns? }
|
|
5
|
-
`.claude/agents/<subagent>.md`, which webpieces verifies exists) and, optionally, a
|
|
6
|
-
detail doc and path `patterns`:
|
|
4
|
+
array of `{ subagent, doc?, patterns? }`, and that is the **only** accepted shape. Each names a reviewer
|
|
5
|
+
**subagent** (a `.claude/agents/<subagent>.md`, which webpieces verifies exists) and, optionally, a
|
|
6
|
+
**repo-relative** detail doc and path `patterns`:
|
|
7
7
|
|
|
8
8
|
```jsonc
|
|
9
9
|
"commands": { "pr-gate": { "checklists": [
|
|
@@ -13,10 +13,11 @@ detail doc and path `patterns`:
|
|
|
13
13
|
] } }
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
>
|
|
17
|
-
> `<!-- webpieces:checklists [...] -->` HTML comment inside
|
|
18
|
-
>
|
|
19
|
-
>
|
|
16
|
+
> **The old `checklists: { "doc": "..." }` shape is REMOVED.** It hid this array in a
|
|
17
|
+
> `<!-- webpieces:checklists [...] -->` HTML comment inside a markdown doc, where no schema, editor, or `jq`
|
|
18
|
+
> could reach it. If your config still has it, **config validation fails** and prints the exact edit: move
|
|
19
|
+
> the array into `webpieces.config.json` and make each entry's `doc` repo-relative (they used to resolve
|
|
20
|
+
> relative to the index doc). There is no compatibility mode — fix the config and move on.
|
|
20
21
|
|
|
21
22
|
## The one command you run
|
|
22
23
|
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { ChecklistDefinition, ChecklistSource } from './checklist-config';
|
|
2
|
-
/**
|
|
3
|
-
* Loads + validates a repo's review checklists from EITHER shape (see {@link ChecklistSource}): the
|
|
4
|
-
* `pr-gate.checklists` array in webpieces.config.json (primary) or the `<!-- webpieces:checklists -->`
|
|
5
|
-
* manifest embedded in the doc that `pr-gate.checklists.doc` points at (legacy).
|
|
6
|
-
*
|
|
7
|
-
* `@injectable(bindingScopeValues.Singleton)` so it is injected by type + drawn in the DI design.
|
|
8
|
-
*/
|
|
9
|
-
export declare class ChecklistManifestService {
|
|
10
|
-
manifestDocPath(repoRoot: string, docRel: string): string;
|
|
11
|
-
/**
|
|
12
|
-
* The repo's checklists, or [] when there are none / the manifest doc is missing / malformed. Callers
|
|
13
|
-
* that need to REPORT those problems use validate(); this one is the tolerant runtime read.
|
|
14
|
-
*/
|
|
15
|
-
load(repoRoot: string, source: ChecklistSource): ChecklistDefinition[];
|
|
16
|
-
validate(repoRoot: string, source: ChecklistSource): string[];
|
|
17
|
-
private validateItems;
|
|
18
|
-
/**
|
|
19
|
-
* The checks that are the SAME for both config shapes, run against already-narrowed definitions:
|
|
20
|
-
* subagent present + distinct + actually resolves to a spawnable agent, and the guidance doc exists.
|
|
21
|
-
*/
|
|
22
|
-
private validateResolved;
|
|
23
|
-
/**
|
|
24
|
-
* The check this validator was missing: `subagent` is the ONE required field and the whole
|
|
25
|
-
* distinct-reviewer guarantee rests on it, yet nothing confirmed it names a real agent. A typo used to
|
|
26
|
-
* validate clean, then get printed to the coding agent as "spawn this" — and since wp-finish blocks on
|
|
27
|
-
* `review-<that-typo>.json`, the path of least resistance became writing the reviewer's verdict itself,
|
|
28
|
-
* which is the exact self-certification the distinct-subagent rule exists to prevent. Reject at load.
|
|
29
|
-
*/
|
|
30
|
-
private validateSubagentExists;
|
|
31
|
-
private usable;
|
|
32
|
-
private readItems;
|
|
33
|
-
private extractManifest;
|
|
34
|
-
private parse;
|
|
35
|
-
private isStringArray;
|
|
36
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ChecklistManifestService = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
-
const path = tslib_1.__importStar(require("path"));
|
|
7
|
-
const inversify_1 = require("inversify");
|
|
8
|
-
const checklist_config_1 = require("./checklist-config");
|
|
9
|
-
const to_error_1 = require("./to-error");
|
|
10
|
-
// The delimited JSON manifest embedded in the review doc — the LEGACY shape, kept working because it is
|
|
11
|
-
// shipped. An HTML comment so it does NOT render in the markdown a human reads, but is trivially +
|
|
12
|
-
// robustly parseable (JSON.parse — no YAML dependency, no hand-rolled frontmatter parser). Example, at the
|
|
13
|
-
// top of `.claude/review/index.md`:
|
|
14
|
-
//
|
|
15
|
-
// <!-- webpieces:checklists
|
|
16
|
-
// [ { "subagent": "morpheus-envvars-reviewer", "doc": "morpheus-envvars.md",
|
|
17
|
-
// "patterns": ["**/.env*", "**/Dockerfile*"] } ]
|
|
18
|
-
// -->
|
|
19
|
-
//
|
|
20
|
-
// New repos should put the SAME array directly in `pr-gate.checklists` in webpieces.config.json instead —
|
|
21
|
-
// see ChecklistSource. A JSON array inside an HTML comment is reachable only by this regex: no JSON Schema
|
|
22
|
-
// covers it, no editor completes it, no `jq` reads it.
|
|
23
|
-
const MANIFEST_RE = /<!--\s*webpieces:checklists\s*([\s\S]*?)-->/;
|
|
24
|
-
// Where a reviewer subagent's definition must live for Claude Code to be able to spawn it.
|
|
25
|
-
const AGENTS_DIR = path.join('.claude', 'agents');
|
|
26
|
-
/**
|
|
27
|
-
* Loads + validates a repo's review checklists from EITHER shape (see {@link ChecklistSource}): the
|
|
28
|
-
* `pr-gate.checklists` array in webpieces.config.json (primary) or the `<!-- webpieces:checklists -->`
|
|
29
|
-
* manifest embedded in the doc that `pr-gate.checklists.doc` points at (legacy).
|
|
30
|
-
*
|
|
31
|
-
* `@injectable(bindingScopeValues.Singleton)` so it is injected by type + drawn in the DI design.
|
|
32
|
-
*/
|
|
33
|
-
let ChecklistManifestService = class ChecklistManifestService {
|
|
34
|
-
// Absolute path of the manifest doc under repoRoot.
|
|
35
|
-
manifestDocPath(repoRoot, docRel) {
|
|
36
|
-
return path.join(repoRoot, docRel);
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* The repo's checklists, or [] when there are none / the manifest doc is missing / malformed. Callers
|
|
40
|
-
* that need to REPORT those problems use validate(); this one is the tolerant runtime read.
|
|
41
|
-
*/
|
|
42
|
-
load(repoRoot, source) {
|
|
43
|
-
if (source.inline.length > 0)
|
|
44
|
-
return this.usable(source.inline);
|
|
45
|
-
if (source.doc.trim() === '')
|
|
46
|
-
return [];
|
|
47
|
-
const items = this.readItems(this.manifestDocPath(repoRoot, source.doc));
|
|
48
|
-
if (items === null)
|
|
49
|
-
return [];
|
|
50
|
-
return this.usable(items.map((raw) => (0, checklist_config_1.toChecklist)(raw, path.posix.dirname(source.doc))));
|
|
51
|
-
}
|
|
52
|
-
// Human-readable errors for the repo's checklist config, or [] when valid. Never throws.
|
|
53
|
-
validate(repoRoot, source) {
|
|
54
|
-
if (source.inline.length > 0)
|
|
55
|
-
return this.validateResolved(source.inline, repoRoot, source.describe());
|
|
56
|
-
if (source.doc.trim() === '')
|
|
57
|
-
return [];
|
|
58
|
-
const docRel = source.doc;
|
|
59
|
-
const docPath = this.manifestDocPath(repoRoot, docRel);
|
|
60
|
-
if (!fs.existsSync(docPath)) {
|
|
61
|
-
return [`[pr-gate] checklists.doc "${docRel}" does not exist — it must be a markdown doc carrying a <!-- webpieces:checklists [...] --> manifest.`];
|
|
62
|
-
}
|
|
63
|
-
const raw = this.extractManifest(fs.readFileSync(docPath, 'utf8'));
|
|
64
|
-
if (raw === '') {
|
|
65
|
-
return [`[pr-gate] "${docRel}" has no <!-- webpieces:checklists [...] --> block. Add a JSON array of { subagent, doc?, patterns? } inside that HTML comment.`];
|
|
66
|
-
}
|
|
67
|
-
const parsed = this.parse(raw);
|
|
68
|
-
if (parsed === null) {
|
|
69
|
-
return [`[pr-gate] the <!-- webpieces:checklists --> block in "${docRel}" is not a valid JSON array.`];
|
|
70
|
-
}
|
|
71
|
-
return this.validateItems(parsed, repoRoot, docRel);
|
|
72
|
-
}
|
|
73
|
-
// Validate raw manifest entries (legacy shape): narrow each to a ChecklistDefinition with its doc
|
|
74
|
-
// resolved against the manifest doc's directory, then run the SAME checks the array form gets.
|
|
75
|
-
validateItems(items, repoRoot, docRel) {
|
|
76
|
-
const errors = [];
|
|
77
|
-
const docBase = path.posix.dirname(docRel);
|
|
78
|
-
items.forEach((item, i) => {
|
|
79
|
-
const label = typeof item.subagent === 'string' && item.subagent !== '' ? `"${item.subagent}"` : `checklists[${i}]`;
|
|
80
|
-
if (item.patterns !== undefined && !this.isStringArray(item.patterns)) {
|
|
81
|
-
errors.push(`[pr-gate] ${docRel} ${label}.patterns must be a string[] of path globs (omit or [] to run on every PR).`);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
const defs = items.map((item) => (0, checklist_config_1.toChecklist)(item, docBase));
|
|
85
|
-
return [...errors, ...this.validateResolved(defs, repoRoot, docRel)];
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* The checks that are the SAME for both config shapes, run against already-narrowed definitions:
|
|
89
|
-
* subagent present + distinct + actually resolves to a spawnable agent, and the guidance doc exists.
|
|
90
|
-
*/
|
|
91
|
-
validateResolved(defs, repoRoot, sourceLabel) {
|
|
92
|
-
const errors = [];
|
|
93
|
-
const seen = new Set();
|
|
94
|
-
// Only enforce the reviewer-agent file when this repo HAS an agents dir — a non-Claude-Code consumer
|
|
95
|
-
// that drives the gate some other way must not be broken by a check for a directory it never has.
|
|
96
|
-
const agentsDir = path.join(repoRoot, AGENTS_DIR);
|
|
97
|
-
const checkAgents = fs.existsSync(agentsDir);
|
|
98
|
-
defs.forEach((def, i) => {
|
|
99
|
-
const label = def.subagent !== '' ? `"${def.subagent}"` : `checklists[${i}]`;
|
|
100
|
-
if (def.subagent.trim() === '') {
|
|
101
|
-
errors.push(`[pr-gate] ${sourceLabel} checklists[${i}].subagent must be a non-empty string (the reviewer agent name, matching .claude/agents/<subagent>.md).`);
|
|
102
|
-
}
|
|
103
|
-
else if (seen.has(def.subagent)) {
|
|
104
|
-
errors.push(`[pr-gate] ${sourceLabel} duplicate subagent "${def.subagent}" — each checklist must use a DISTINCT reviewer subagent (that is how independent review is enforced).`);
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
seen.add(def.subagent);
|
|
108
|
-
errors.push(...this.validateSubagentExists(def.subagent, label, checkAgents, agentsDir, sourceLabel));
|
|
109
|
-
}
|
|
110
|
-
if (def.doc !== '' && !fs.existsSync(path.join(repoRoot, def.doc))) {
|
|
111
|
-
errors.push(`[pr-gate] ${sourceLabel} ${label}.doc "${def.doc}" does not exist (resolved repo-relative).`);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
return errors;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* The check this validator was missing: `subagent` is the ONE required field and the whole
|
|
118
|
-
* distinct-reviewer guarantee rests on it, yet nothing confirmed it names a real agent. A typo used to
|
|
119
|
-
* validate clean, then get printed to the coding agent as "spawn this" — and since wp-finish blocks on
|
|
120
|
-
* `review-<that-typo>.json`, the path of least resistance became writing the reviewer's verdict itself,
|
|
121
|
-
* which is the exact self-certification the distinct-subagent rule exists to prevent. Reject at load.
|
|
122
|
-
*/
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/max-params
|
|
124
|
-
validateSubagentExists(subagent, label, checkAgents, agentsDir, sourceLabel) {
|
|
125
|
-
if (!checkAgents)
|
|
126
|
-
return [];
|
|
127
|
-
if (fs.existsSync(path.join(agentsDir, `${subagent}.md`)))
|
|
128
|
-
return [];
|
|
129
|
-
return [
|
|
130
|
-
`[pr-gate] ${sourceLabel} ${label}.subagent names no reviewer — ${AGENTS_DIR}/${subagent}.md does not exist, ` +
|
|
131
|
-
`so nothing can spawn it and wp-finish-upsert-pr would block forever on a review-${subagent}.json that no reviewer can write. ` +
|
|
132
|
-
`Create that agent file or fix the name.`,
|
|
133
|
-
];
|
|
134
|
-
}
|
|
135
|
-
// Drop entries with no subagent — they have no id, so they can key neither review-<id>.json nor a
|
|
136
|
-
// dashboard row. validate() reports them; the tolerant load() just skips them.
|
|
137
|
-
usable(defs) {
|
|
138
|
-
return defs.filter((d) => d.subagent !== '');
|
|
139
|
-
}
|
|
140
|
-
// Read the manifest doc and return its parsed items, or null on any problem (missing/no block/bad JSON).
|
|
141
|
-
readItems(docPath) {
|
|
142
|
-
if (!fs.existsSync(docPath))
|
|
143
|
-
return null;
|
|
144
|
-
const raw = this.extractManifest(fs.readFileSync(docPath, 'utf8'));
|
|
145
|
-
if (raw === '')
|
|
146
|
-
return null;
|
|
147
|
-
return this.parse(raw);
|
|
148
|
-
}
|
|
149
|
-
extractManifest(content) {
|
|
150
|
-
const m = MANIFEST_RE.exec(content);
|
|
151
|
-
return m ? m[1].trim() : '';
|
|
152
|
-
}
|
|
153
|
-
// webpieces-disable no-any-unknown -- opaque parsed JSON, narrowed to an item array by the caller
|
|
154
|
-
parse(json) {
|
|
155
|
-
// webpieces-disable no-unmanaged-exceptions -- chokepoint: a malformed manifest is reported as one error / ignored
|
|
156
|
-
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
157
|
-
try {
|
|
158
|
-
// webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed below
|
|
159
|
-
const value = JSON.parse(json);
|
|
160
|
-
if (!Array.isArray(value))
|
|
161
|
-
return null;
|
|
162
|
-
// webpieces-disable no-any-unknown -- each opaque entry is narrowed field-by-field downstream
|
|
163
|
-
return value;
|
|
164
|
-
}
|
|
165
|
-
catch (err) {
|
|
166
|
-
const error = (0, to_error_1.toError)(err);
|
|
167
|
-
void error;
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here
|
|
172
|
-
isStringArray(value) {
|
|
173
|
-
return Array.isArray(value) && value.every((v) => typeof v === 'string');
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
exports.ChecklistManifestService = ChecklistManifestService;
|
|
177
|
-
exports.ChecklistManifestService = ChecklistManifestService = tslib_1.__decorate([
|
|
178
|
-
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
|
|
179
|
-
], ChecklistManifestService);
|
|
180
|
-
//# sourceMappingURL=checklist-manifest.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"checklist-manifest.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/checklist-manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { ChecklistDefinition, ChecklistSource, RawChecklistItem, toChecklist } from './checklist-config';\nimport { toError } from './to-error';\n\n// The delimited JSON manifest embedded in the review doc — the LEGACY shape, kept working because it is\n// shipped. An HTML comment so it does NOT render in the markdown a human reads, but is trivially +\n// robustly parseable (JSON.parse — no YAML dependency, no hand-rolled frontmatter parser). Example, at the\n// top of `.claude/review/index.md`:\n//\n// <!-- webpieces:checklists\n// [ { \"subagent\": \"morpheus-envvars-reviewer\", \"doc\": \"morpheus-envvars.md\",\n// \"patterns\": [\"**/.env*\", \"**/Dockerfile*\"] } ]\n// -->\n//\n// New repos should put the SAME array directly in `pr-gate.checklists` in webpieces.config.json instead —\n// see ChecklistSource. A JSON array inside an HTML comment is reachable only by this regex: no JSON Schema\n// covers it, no editor completes it, no `jq` reads it.\nconst MANIFEST_RE = /<!--\\s*webpieces:checklists\\s*([\\s\\S]*?)-->/;\n\n// Where a reviewer subagent's definition must live for Claude Code to be able to spawn it.\nconst AGENTS_DIR = path.join('.claude', 'agents');\n\n/**\n * Loads + validates a repo's review checklists from EITHER shape (see {@link ChecklistSource}): the\n * `pr-gate.checklists` array in webpieces.config.json (primary) or the `<!-- webpieces:checklists -->`\n * manifest embedded in the doc that `pr-gate.checklists.doc` points at (legacy).\n *\n * `@injectable(bindingScopeValues.Singleton)` so it is injected by type + drawn in the DI design.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class ChecklistManifestService {\n // Absolute path of the manifest doc under repoRoot.\n manifestDocPath(repoRoot: string, docRel: string): string {\n return path.join(repoRoot, docRel);\n }\n\n /**\n * The repo's checklists, or [] when there are none / the manifest doc is missing / malformed. Callers\n * that need to REPORT those problems use validate(); this one is the tolerant runtime read.\n */\n load(repoRoot: string, source: ChecklistSource): ChecklistDefinition[] {\n if (source.inline.length > 0) return this.usable(source.inline);\n if (source.doc.trim() === '') return [];\n const items = this.readItems(this.manifestDocPath(repoRoot, source.doc));\n if (items === null) return [];\n return this.usable(items.map((raw: RawChecklistItem): ChecklistDefinition => toChecklist(raw, path.posix.dirname(source.doc))));\n }\n\n // Human-readable errors for the repo's checklist config, or [] when valid. Never throws.\n validate(repoRoot: string, source: ChecklistSource): string[] {\n if (source.inline.length > 0) return this.validateResolved(source.inline, repoRoot, source.describe());\n if (source.doc.trim() === '') return [];\n const docRel = source.doc;\n const docPath = this.manifestDocPath(repoRoot, docRel);\n if (!fs.existsSync(docPath)) {\n return [`[pr-gate] checklists.doc \"${docRel}\" does not exist — it must be a markdown doc carrying a <!-- webpieces:checklists [...] --> manifest.`];\n }\n const raw = this.extractManifest(fs.readFileSync(docPath, 'utf8'));\n if (raw === '') {\n return [`[pr-gate] \"${docRel}\" has no <!-- webpieces:checklists [...] --> block. Add a JSON array of { subagent, doc?, patterns? } inside that HTML comment.`];\n }\n const parsed = this.parse(raw);\n if (parsed === null) {\n return [`[pr-gate] the <!-- webpieces:checklists --> block in \"${docRel}\" is not a valid JSON array.`];\n }\n return this.validateItems(parsed, repoRoot, docRel);\n }\n\n // Validate raw manifest entries (legacy shape): narrow each to a ChecklistDefinition with its doc\n // resolved against the manifest doc's directory, then run the SAME checks the array form gets.\n private validateItems(items: readonly RawChecklistItem[], repoRoot: string, docRel: string): string[] {\n const errors: string[] = [];\n const docBase = path.posix.dirname(docRel);\n items.forEach((item: RawChecklistItem, i: number): void => {\n const label = typeof item.subagent === 'string' && item.subagent !== '' ? `\"${item.subagent}\"` : `checklists[${i}]`;\n if (item.patterns !== undefined && !this.isStringArray(item.patterns)) {\n errors.push(`[pr-gate] ${docRel} ${label}.patterns must be a string[] of path globs (omit or [] to run on every PR).`);\n }\n });\n const defs = items.map((item: RawChecklistItem): ChecklistDefinition => toChecklist(item, docBase));\n return [...errors, ...this.validateResolved(defs, repoRoot, docRel)];\n }\n\n /**\n * The checks that are the SAME for both config shapes, run against already-narrowed definitions:\n * subagent present + distinct + actually resolves to a spawnable agent, and the guidance doc exists.\n */\n private validateResolved(defs: readonly ChecklistDefinition[], repoRoot: string, sourceLabel: string): string[] {\n const errors: string[] = [];\n const seen = new Set<string>();\n // Only enforce the reviewer-agent file when this repo HAS an agents dir — a non-Claude-Code consumer\n // that drives the gate some other way must not be broken by a check for a directory it never has.\n const agentsDir = path.join(repoRoot, AGENTS_DIR);\n const checkAgents = fs.existsSync(agentsDir);\n defs.forEach((def: ChecklistDefinition, i: number): void => {\n const label = def.subagent !== '' ? `\"${def.subagent}\"` : `checklists[${i}]`;\n if (def.subagent.trim() === '') {\n errors.push(`[pr-gate] ${sourceLabel} checklists[${i}].subagent must be a non-empty string (the reviewer agent name, matching .claude/agents/<subagent>.md).`);\n } else if (seen.has(def.subagent)) {\n errors.push(`[pr-gate] ${sourceLabel} duplicate subagent \"${def.subagent}\" — each checklist must use a DISTINCT reviewer subagent (that is how independent review is enforced).`);\n } else {\n seen.add(def.subagent);\n errors.push(...this.validateSubagentExists(def.subagent, label, checkAgents, agentsDir, sourceLabel));\n }\n if (def.doc !== '' && !fs.existsSync(path.join(repoRoot, def.doc))) {\n errors.push(`[pr-gate] ${sourceLabel} ${label}.doc \"${def.doc}\" does not exist (resolved repo-relative).`);\n }\n });\n return errors;\n }\n\n /**\n * The check this validator was missing: `subagent` is the ONE required field and the whole\n * distinct-reviewer guarantee rests on it, yet nothing confirmed it names a real agent. A typo used to\n * validate clean, then get printed to the coding agent as \"spawn this\" — and since wp-finish blocks on\n * `review-<that-typo>.json`, the path of least resistance became writing the reviewer's verdict itself,\n * which is the exact self-certification the distinct-subagent rule exists to prevent. Reject at load.\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private validateSubagentExists(subagent: string, label: string, checkAgents: boolean, agentsDir: string, sourceLabel: string): string[] {\n if (!checkAgents) return [];\n if (fs.existsSync(path.join(agentsDir, `${subagent}.md`))) return [];\n return [\n `[pr-gate] ${sourceLabel} ${label}.subagent names no reviewer — ${AGENTS_DIR}/${subagent}.md does not exist, ` +\n `so nothing can spawn it and wp-finish-upsert-pr would block forever on a review-${subagent}.json that no reviewer can write. ` +\n `Create that agent file or fix the name.`,\n ];\n }\n\n // Drop entries with no subagent — they have no id, so they can key neither review-<id>.json nor a\n // dashboard row. validate() reports them; the tolerant load() just skips them.\n private usable(defs: readonly ChecklistDefinition[]): ChecklistDefinition[] {\n return defs.filter((d: ChecklistDefinition): boolean => d.subagent !== '');\n }\n\n // Read the manifest doc and return its parsed items, or null on any problem (missing/no block/bad JSON).\n private readItems(docPath: string): RawChecklistItem[] | null {\n if (!fs.existsSync(docPath)) return null;\n const raw = this.extractManifest(fs.readFileSync(docPath, 'utf8'));\n if (raw === '') return null;\n return this.parse(raw);\n }\n\n private extractManifest(content: string): string {\n const m = MANIFEST_RE.exec(content);\n return m ? m[1].trim() : '';\n }\n\n // webpieces-disable no-any-unknown -- opaque parsed JSON, narrowed to an item array by the caller\n private parse(json: string): RawChecklistItem[] | null {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: a malformed manifest is reported as one error / ignored\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed below\n const value = JSON.parse(json) as unknown;\n if (!Array.isArray(value)) return null;\n // webpieces-disable no-any-unknown -- each opaque entry is narrowed field-by-field downstream\n return value as RawChecklistItem[];\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n return null;\n }\n }\n\n // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here\n private isStringArray(value: unknown): boolean {\n return Array.isArray(value) && value.every((v: unknown): boolean => typeof v === 'string');\n }\n}\n"]}
|