import{readFileSync as e,writeFileSync as t,mkdirSync as s,readdirSync as a,rmSync as o}from"node:fs";import n from"node:path";import{validateFormDefinition as i}from"../packages/core/dist/index.js";const l=new URL("..",import.meta.url).pathname,c=n.join(l,"docs/examples/invalid"),r=process.argv.includes("--check"),d=()=>JSON.parse(JSON.stringify({$schema:"https://schemas.rasd.dev/form/v1.json",rasd:"1.0",id:"invalid-fixture",version:"2",meta:{title:"Negative fixture"},settings:{defaultLocale:"en",locales:["en"]},pages:[{id:"p1",elements:[{type:"text",name:"q1",label:"Question one"}]}]})),m=(e,...t)=>e.pages[0].elements.push(...t),u=[{code:"E_SCHEMA",why:"meta.title is required (docs/04 \xa73)",mutate:e=>{delete e.meta.title}},{code:"E_UNSUPPORTED_RASD_MAJOR",why:"a 1.x client must refuse a 2.x document rather than degrade (docs/04 \xa717)",mutate:e=>{e.rasd="2.0"}},{code:"E_REQUIRES_UNMET",why:"requires.features names a capability the consumer did not declare (docs/04 \xa72)",mutate:e=>{e.requires={features:["cap:encryption"]}},options:{features:["type:text"]}},{code:"E_PROTO_KEY",why:"__proto__ anywhere in the document is a prototype-pollution vector (docs/16)",mutate:e=>{e.ext=JSON.parse('{"__proto__":{"polluted":true}}')}},{code:"E_DEFINITION_TOO_LARGE",why:"serialised definition exceeds the byte limit (docs/04 \xa716)",mutate:e=>{e.meta.description="x".repeat(400)},options:{limits:{definitionBytes:300}}},{code:"E_DEFAULT_LOCALE_NOT_IN_LOCALES",why:"defaultLocale must be a member of locales (docs/04 \xa74)",mutate:e=>{e.settings.defaultLocale="fr"}},{code:"E_LOCALE_INVALID",why:"the same locale is listed twice (docs/04 \xa74)",mutate:e=>{e.settings.locales=["en","en"]}},{code:"E_ENCRYPTION_KEY_MISSING",why:'encryption.mode "field" without publicKeyId (docs/04 \xa74.3)',mutate:e=>{e.settings.encryption={mode:"field"}}},{code:"E_NO_PAGES",why:"a form must have at least one page (docs/04 \xa77)",mutate:e=>{e.pages=[]}},{code:"E_PAGE_ID_DUPLICATE",why:"page ids are the skipTo target namespace and must be unique (docs/04 \xa77)",mutate:e=>{e.pages.push({id:"p1",elements:[{type:"text",name:"q2",label:"Question two"}]})}},{code:"E_TOO_MANY_PAGES",why:"page count exceeds the limit (docs/04 \xa716)",mutate:e=>{e.pages.push({id:"p2",elements:[{type:"text",name:"q2",label:"Question two"}]})},options:{limits:{pages:1}}},{code:"E_TOO_MANY_ELEMENTS",why:"element count exceeds the limit (docs/04 \xa716)",mutate:e=>{m(e,{type:"text",name:"q2",label:"Question two"})},options:{limits:{elements:1}}},{code:"E_INVALID_NAME",why:"a name may not start with a digit or contain a hyphen (docs/04 \xa71.4)",mutate:e=>{e.pages[0].elements[0].name="1hh-id"}},{code:"E_RESERVED_NAME",why:'"today" is a reserved runtime name (docs/04 \xa713)',mutate:e=>{e.pages[0].elements[0].name="today"}},{code:"E_DUPLICATE_NAME",why:"two elements share a storage key in the same scope (docs/04 \xa71.4)",mutate:e=>{m(e,{type:"number",name:"q1",label:"Also q1"})}},{code:"E_NAME_SHADOWED",why:"a repeat child reuses a name from the enclosing scope (docs/04 \xa71.4)",mutate:e=>{m(e,{type:"repeat",name:"r",label:"Roster",elements:[{type:"text",name:"q1",label:"Shadows q1"}]})}},{code:"E_IMPLICIT_KEY_COLLISION",why:"an authored name collides with the _other key a select creates (docs/04 \xa710)",mutate:e=>{m(e,{type:"select_one",name:"s",label:"Pick one",props:{choices:[{value:"a",label:"A"}],other:{enabled:!0}}},{type:"text",name:"s_other",label:"Collides with the implicit key"})}},{code:"E_UNKNOWN_TYPE",why:"a built-in-looking type that is not in the catalogue (docs/04 \xa710)",mutate:e=>{e.pages[0].elements[0].type="textt"}},{code:"E_CUSTOM_TYPE_NAME",why:"an x: type name must be a slug (docs/04 \xa710)",mutate:e=>{m(e,{type:"x:Bad Name",name:"cx",label:"Custom"})}},{code:"E_ELEMENTS_ON_LEAF",why:"only group and repeat may carry elements (docs/04 \xa79)",mutate:e=>{e.pages[0].elements[0].elements=[]}},{code:"E_TYPE_PROPS_INVALID",why:"number.props.min must be a number (docs/04 \xa710)",mutate:e=>{m(e,{type:"number",name:"n",label:"Number",props:{min:"not-a-number"}})}},{code:"E_NOTE_WITH_VALUE_PROPS",why:"a note holds no value, so it cannot be required (docs/04 \xa710)",mutate:e=>{m(e,{type:"note",name:"nt",label:"Read this",required:!0})}},{code:"E_CONSENT_TEXT_MISSING",why:"a consent element must carry the text being consented to (docs/04 \xa710)",mutate:e=>{m(e,{type:"consent",name:"cons",label:"Consent",props:{textVersion:"2026-08"}})}},{code:"E_MATRIX_ROWS_EMPTY",why:"a matrix with no rows renders nothing (docs/04 \xa710)",mutate:e=>{m(e,{type:"matrix",name:"m",label:"Matrix",props:{rows:[],columns:{type:"select_one",choices:[{value:"a",label:"A"}]}}})}},{code:"E_DEFAULT_ON_CALCULATE",why:"a calculated value cannot also have a default (docs/04 \xa79)",mutate:e=>{m(e,{type:"calculate",name:"c",calculate:"1 + 1",default:{value:3}})}},{code:"E_CALCULATE_MISSING",why:"a calculate element without an expression computes nothing (docs/04 \xa710)",mutate:e=>{m(e,{type:"calculate",name:"c"})}},{code:"E_TOO_MANY_VALIDATORS",why:"validator count per element exceeds the limit (docs/04 \xa716)",mutate:e=>{e.pages[0].elements[0].validators=[{type:"length",max:10},{type:"length",max:11}]},options:{limits:{validatorsPerElement:1}}},{code:"E_NESTING_TOO_DEEP",why:"container nesting exceeds the depth limit (docs/04 \xa716)",mutate:e=>{m(e,{type:"group",name:"g1",label:"Outer",elements:[{type:"group",name:"g2",label:"Inner",elements:[{type:"text",name:"deep",label:"Deep"}]}]})},options:{limits:{containerDepth:1}}},{code:"E_REPEAT_NESTING",why:"repeat nesting exceeds the limit (docs/04 \xa716)",mutate:e=>{m(e,{type:"repeat",name:"r1",label:"Outer",elements:[{type:"repeat",name:"r2",label:"Inner",elements:[{type:"text",name:"rq",label:"Q"}]}]})},options:{limits:{repeatNesting:1}}},{code:"E_REPEAT_MIN_GT_MAX",why:"a repeat that requires more instances than it allows can never be satisfied (docs/04 \xa710)",mutate:e=>{m(e,{type:"repeat",name:"r",label:"Roster",props:{min:5,max:2},elements:[{type:"text",name:"rq",label:"Q"}]})}},{code:"E_LIST_NOT_FOUND",why:"select_one names a choice list that does not exist (docs/04 \xa75)",mutate:e=>{m(e,{type:"select_one",name:"s",label:"Pick",props:{list:"nonexistent"}})}},{code:"E_SELECT_SOURCE",why:"a select must have exactly one source: list, choices or dataset (docs/04 \xa75)",mutate:e=>{m(e,{type:"select_one",name:"s",label:"Pick",props:{}})}},{code:"E_CHOICE_DUPLICATE",why:"two choices share a value, so the stored answer is ambiguous (docs/04 \xa75)",mutate:e=>{e.choiceLists={yesno:{choices:[{value:"y",label:"Yes"},{value:"y",label:"Also yes"}]}},m(e,{type:"select_one",name:"s",label:"Pick",props:{list:"yesno"}})}},{code:"E_CHOICELIST_SOURCE",why:"a choice list must have exactly one source (docs/04 \xa75)",mutate:e=>{e.datasets=[{name:"people",source:"inline",keyField:"id",columns:[{name:"id",type:"string"},{name:"name",type:"string"}],inline:[{id:"1",name:"Amina"}]}],e.choiceLists={l:{choices:[{value:"a",label:"A"}],source:{type:"dataset",dataset:"people"},valueKey:"id",labelKey:"name"}},m(e,{type:"select_one",name:"s",label:"Pick",props:{list:"l"}})}},{code:"E_INLINE_CHOICES_TOO_MANY",why:"inline choice count exceeds the limit; use a dataset (docs/04 \xa716)",mutate:e=>{e.choiceLists={l:{choices:[{value:"a",label:"A"},{value:"b",label:"B"}]}},m(e,{type:"select_one",name:"s",label:"Pick",props:{list:"l"}})},options:{limits:{inlineChoicesPerList:1}}},{code:"E_DATASET_NOT_FOUND",why:"a select sources from a dataset that is not declared (docs/04 \xa76)",mutate:e=>{e.choiceLists={l:{source:{type:"dataset",dataset:"nonexistent"},valueKey:"id",labelKey:"name"}},m(e,{type:"select_one",name:"s",label:"Pick",props:{list:"l"}})}},{code:"E_DATASET_COLUMN_UNKNOWN",why:"a select names a dataset column the dataset does not declare (docs/04 \xa76)",mutate:e=>{e.datasets=[{name:"people",source:"inline",keyField:"id",columns:[{name:"id",type:"string"},{name:"name",type:"string"}],inline:[{id:"1",name:"Amina"}]}],e.choiceLists={l:{source:{type:"dataset",dataset:"people"},valueKey:"id",labelKey:"no_such_column"}},m(e,{type:"select_one",name:"s",label:"Pick",props:{list:"l"}})}},{code:"E_DATASET_INLINE_TOO_MANY",why:"inline dataset rows exceed the limit; large tables belong on the server (docs/04 \xa716)",mutate:e=>{e.datasets=[{name:"people",source:"inline",keyField:"id",columns:[{name:"id",type:"string"},{name:"name",type:"string"}],inline:[{id:"1",name:"Amina"},{id:"2",name:"Bilal"}]}],e.choiceLists={l:{source:{type:"dataset",dataset:"people"},valueKey:"id",labelKey:"name"}},m(e,{type:"select_one",name:"s",label:"Pick",props:{list:"l"}})},options:{limits:{inlineDatasetRows:1}}},{code:"E_EXPR_PARSE",why:"a syntactically invalid REL expression (docs/05 \xa76)",mutate:e=>{e.pages[0].elements[0].relevant="1 +"}},{code:"E_EXPR_UNKNOWN_FUNCTION",why:"a function outside the v1 catalogue and not host-registered (docs/05 \xa78)",mutate:e=>{e.pages[0].elements[0].relevant="no_such_function() = 1"}},{code:"E_EXPR_UNKNOWN_REF",why:"an expression references a name no element declares (docs/05 \xa74)",mutate:e=>{e.pages[0].elements[0].relevant="${missing_question} = 1"}},{code:"E_EXPR_TOO_LARGE",why:"expression length exceeds the limit (docs/04 \xa716)",mutate:e=>{e.pages[0].elements[0].relevant=`'${"x".repeat(60)}' != ''`},options:{limits:{exprChars:40}}},{code:"E_CALC_CYCLE",why:"two calculated values depend on each other, so neither can be computed (docs/05 \xa75)",mutate:e=>{m(e,{type:"calculate",name:"a",calculate:"${b} + 1"},{type:"calculate",name:"b",calculate:"${a} + 1"})}},{code:"E_TRIGGER_TARGET_UNKNOWN",why:"a trigger writes to a name no element declares (docs/04 \xa78)",mutate:e=>{e.logic={triggers:[{id:"t1",when:"true()",actions:[{type:"setValue",target:"nonexistent",value:1}]}]}}},{code:"E_TRIGGER_TARGET_CALCULATED",why:"a trigger cannot write to a calculated value; the calculation would overwrite it (docs/04 \xa78)",mutate:e=>{m(e,{type:"calculate",name:"c",calculate:"1 + 1"}),e.logic={triggers:[{id:"t1",when:"true()",actions:[{type:"setValue",target:"c",value:1}]}]}}},{code:"E_SKIPTO_UNKNOWN_PAGE",why:"a skipTo action names a page that does not exist (docs/04 \xa78)",mutate:e=>{e.logic={triggers:[{id:"t1",when:"true()",actions:[{type:"skipTo",page:"p9"}]}]}}},{code:"E_VERSION_REUSED",why:"a published (id, version) is immutable (docs/00 \xa74.3b)",mutate:e=>{e.version="1",m(e,{type:"text",name:"q2",label:"Added after publish"})},previous:e=>{e.version="1"}},{code:"E_VERSION_NOT_MONOTONIC",why:"version must increase against the last published one (docs/00 \xa74.3b)",mutate:e=>{e.version="2",m(e,{type:"text",name:"q2",label:"Added"})},previous:e=>{e.version="5"}},{code:"E_PUBLISH_UNCHANGED",why:"a new version whose definition is byte-identical to the last one (docs/00 \xa74.3b)",mutate:e=>{e.version="3"},previous:e=>{e.version="2"}},{code:"E_BREAKING_CHANGE",why:"an existing name changed to a type that cannot hold its published data (docs/04 \xa714.2)",mutate:e=>{e.version="3",e.pages[0].elements[0].type="number"},previous:e=>{e.version="2"}}],p=new Map,h={$comment:"Generated by tools/build-invalid-corpus.mjs \u2014 do not edit by hand.",fixtures:[]},_=[],y=[];for(const E of u){const e=d();let t;E.mutate(e),E.previous&&(t=d(),E.previous(t));const s=i(e,{...E.options??{},...t?{previous:t}:{}}),a=[...s.errors,...s.warnings].map(e=>e.code);a.includes(E.code)||_.push(`${E.code}: fixture produced [${[...new Set(a)].join(", ")||"nothing"}]`);const o=[...new Set(s.errors.map(e=>e.code))].filter(e=>e!==E.code);o.length>0&&y.push(`${E.code}: also raises ${o.join(", ")}`);const n=`${E.code}.form.json`;p.set(n,JSON.stringify(e,null,2)+"\n"),t&&p.set(`${E.code}.previous.json`,JSON.stringify(t,null,2)+"\n"),h.fixtures.push({code:E.code,file:n,why:E.why,...t?{previous:`${E.code}.previous.json`}:{},...E.options?.limits?{limits:E.options.limits}:{},...E.options?.features?{features:E.options.features}:{}})}if(h.fixtures.sort((e,t)=>e.code.localeCompare(t.code)),p.set("manifest.json",JSON.stringify(h,null,2)+"\n"),y.length>0&&(console.error("Fixtures that raise more than their own error:\n "+y.join("\n ")),process.exit(1)),_.length>0&&(console.error("These fixtures do not produce the code they are named for:\n "+_.join("\n ")),process.exit(1)),r){const t=new Set(a(c).filter(e=>e.endsWith(".json")));let s=0;for(const[a,o]of p)t.has(a)?(t.delete(a),e(n.join(c,a),"utf8")!==o&&(console.error(`out of date: ${a}`),s++)):(console.error(`missing: ${a}`),s++);for(const e of t)console.error(`stale (not in the table): ${e}`),s++;s>0&&(console.error(`\n${s} file(s) differ. Run: node tools/build-invalid-corpus.mjs`),process.exit(1)),console.log(`invalid corpus: ${h.fixtures.length} fixtures, all up to date`)}else{s(c,{recursive:!0});for(const e of a(c))e.endsWith(".json")&&o(n.join(c,e));for(const[e,s]of p)t(n.join(c,e),s);console.log(`wrote ${p.size} files to docs/examples/invalid/ (${h.fixtures.length} fixtures)`)}