[verified] align CRM frontend contracts
CI / compose (push) Successful in 11m0s

This commit is contained in:
Marco0300
2026-09-03 20:07:02 +02:00
parent 725ef0db9f
commit bf5b54679a
4 changed files with 21 additions and 10 deletions
+11
View File
@@ -82,6 +82,17 @@ check('routes.contracts', 'all critical API route contracts are referenced by th
check('routes.authenticated', 'protected API requests include cookie credentials', js.includes("credentials:'include'") && js.includes('jsonRequest'));
check('routes.no-arbitrary-target-fetch', 'browser does not fetch arbitrary target URLs directly', !/fetch\s*\(\s*(?:url|target|website|href)\b/i.test(js));
const canonicalStages = ['new', 'contacted', 'qualified', 'proposal', 'negotiation', 'won', 'lost'];
const crmStagesMatch = js.match(/const crmStages = \[([^\]]+)\]/);
const crmStages = crmStagesMatch ? [...crmStagesMatch[1].matchAll(/["']([^"']+)["']/g)].map(match => match[1]) : [];
check('crm.stage-contract', 'CRM stage definitions exactly match the canonical pipeline', JSON.stringify(crmStages) === JSON.stringify(canonicalStages), `actual=${JSON.stringify(crmStages)}`);
const pipelineControl = js.match(/<select name="stage"[\s\S]*?<\/select>/)?.[0] || '';
check('crm.stage-control', 'detail stage control exposes only canonical stages', canonicalStages.every(stage => pipelineControl.includes(`value="${stage}"`)) && !pipelineControl.includes('value="review"') && !pipelineControl.includes('value="suppressed"'));
const interactionControl = html.match(/<form id="interactionForm"[\s\S]*?<\/form>/)?.[0] || '';
const canonicalOutcomes = ['connected', 'no_answer', 'left_message', 'meeting_booked', 'meeting_held', 'qualified', 'disqualified', 'won', 'lost', 'other'];
check('crm.interaction-form-contract', 'interaction form uses kind/body and canonical outcomes', interactionControl.includes('name="kind"') && interactionControl.includes('name="body"') && canonicalOutcomes.every(outcome => interactionControl.includes(`value="${outcome}"`)) && !interactionControl.includes('name="type"') && !interactionControl.includes('name="summary"'));
check('crm.interaction-payload-contract', 'interaction writes use the backend kind/body payload contract', /JSON\.stringify\(\{\s*kind:\s*data\.kind,\s*body:\s*data\.body/.test(js) && !/JSON\.stringify\(\{\.\.\.data,outreach:false\}\)/.test(js));
const manifestAssets = manifest && [...new Set([...(manifest.entrypoints || []), ...(manifest.publicAssets || [])])];
check('manifest.schema', 'asset manifest has a supported schema and asset lists', Boolean(manifest && manifest.schema === 1 && Array.isArray(manifest.entrypoints) && Array.isArray(manifest.publicAssets) && manifest.integrity && manifestAssets.length), manifest ? '' : 'manifest unavailable');
if (manifestAssets) {