import { readFile } from 'node:fs/promises' import { FREECAD_PRIVATE_NAMING_ABI_VERSION, FREECAD_SKETCHER_CONSTRAINT_TYPES, FREECAD_SKETCHER_GEOMETRY_TYPES, FREECAD_SKETCHER_INTERNAL_ALIGNMENT_TYPES, PARTDESIGN_PARAMETER_SPACE, createFacadeRuntimeProfile, partDesignParameterSpaceCoverage, } from '../src/facade/index.ts' const report = JSON.parse(await readFile(new URL('../config/freecad-sketcher-partdesign-abi-contract.json', import.meta.url), 'utf8')) const failures = [] const check = (condition, message) => { if (!condition) failures.push(message) } const same = (actual, expected) => JSON.stringify(actual) === JSON.stringify(expected) check(report.schemaVersion === 1, 'contract schemaVersion must be 1') check(report.baseline?.version === '1.1.1', 'contract baseline must lock FreeCAD 1.1.1') check(report.baseline?.sourceCommit === '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d', 'contract baseline source commit changed') check(report.claim?.scope === 'supported-web-facade' && report.claim?.exactFreeCadParity === false, 'contract must remain scoped and fail-closed for system exact parity') check(same(FREECAD_SKETCHER_GEOMETRY_TYPES, report.sketcher?.geometryTypes), 'Sketcher geometry type report is stale') check(same(FREECAD_SKETCHER_CONSTRAINT_TYPES, report.sketcher?.constraintTypes), 'Sketcher constraint type report is stale') check(same(FREECAD_SKETCHER_INTERNAL_ALIGNMENT_TYPES, report.sketcher?.internalAlignmentTypes), 'Sketcher internal alignment report is stale') const coverage = partDesignParameterSpaceCoverage() check(coverage.duplicateTypeIds === 0, 'PartDesign parameter space has duplicate TypeIds') check(coverage.familiesWithoutPartitions.length === 0, 'every PartDesign family must have at least three semantic partitions') check(coverage.families === report.partDesign?.families, 'PartDesign family count report is stale') check(coverage.properties === report.partDesign?.propertySlots, 'PartDesign property-slot count report is stale') check(coverage.partitions === report.partDesign?.semanticPartitions, 'PartDesign semantic-partition count report is stale') check(same(PARTDESIGN_PARAMETER_SPACE.map((family) => family.typeId), report.partDesign?.typeIds), 'PartDesign TypeId report is stale') for (const family of PARTDESIGN_PARAMETER_SPACE) { check(new Set(family.properties).size === family.properties.length, `${family.typeId} has duplicate property declarations`) check(new Set(family.partitions.map((partition) => partition.id)).size === family.partitions.length, `${family.typeId} has duplicate partition IDs`) for (const partition of family.partitions) for (const propertyName of Object.keys(partition.values)) check(family.properties.includes(propertyName), `${family.typeId}.${partition.id} uses undeclared property ${propertyName}`) } check(FREECAD_PRIVATE_NAMING_ABI_VERSION === report.privateNamingAbi?.abiVersion, 'FreeCAD private naming ABI version report is stale') check(createFacadeRuntimeProfile('production').naming.nativeAbi === report.privateNamingAbi?.runtimeProfile, 'production runtime profile does not advertise the optional private ABI') check(createFacadeRuntimeProfile('mock').naming.nativeAbi === 'not-exposed', 'mock runtime must not advertise the private naming ABI') check(report.privateNamingAbi?.shippedWorkerImplementation === 'freecad-linked', 'the production contract must identify the verified FreeCAD-linked Worker') check(report.privateNamingAbi?.syntheticTokenGeneration === 'forbidden', 'synthetic FreeCAD token generation must remain forbidden') if (failures.length) { console.error('FreeCAD Sketcher/PartDesign/private-ABI contract failed:') failures.forEach((failure) => console.error(`- ${failure}`)) process.exit(1) } console.log(`freecad-sketcher-partdesign-abi-contract-pass geometry=${FREECAD_SKETCHER_GEOMETRY_TYPES.length} constraints=${FREECAD_SKETCHER_CONSTRAINT_TYPES.length} alignments=${FREECAD_SKETCHER_INTERNAL_ALIGNMENT_TYPES.length} families=${coverage.families} properties=${coverage.properties} partitions=${coverage.partitions} nativeWorker=${report.privateNamingAbi.shippedWorkerImplementation}`)