import { readFile } from 'node:fs/promises' import { resolve } from 'node:path' import { spawnSync } from 'node:child_process' const root = resolve(new URL('..', import.meta.url).pathname) const load = async (path) => JSON.parse(await readFile(resolve(root, path), 'utf8')) const native = await load('config/freecad-fcstd-roundtrip-verification.json') const semantic = await load('config/chrome-fcstd-semantic-verification.json') const chromeRoundTrip = await load('config/chrome-fcstd-roundtrip-verification.json') const golden = await load('config/chrome-fcstd-golden-verification.json') const secondary = await load('config/chrome-secondary-formats-verification.json') const fail = (message) => { throw new Error(`FCStd closure: ${message}`) } const nativeScenarioCount = native.scenarioCount if (native.status !== 'verified' || native.baselineId !== 'freecad-1.1.1' || native.freecadCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d' || !Number.isSafeInteger(nativeScenarioCount) || nativeScenarioCount < 23 || native.scenarios?.length !== nativeScenarioCount || native.unknownDifferencesFail !== true || JSON.stringify(native.directions) !== JSON.stringify(['freecad-web-freecad', 'web-freecad-web'])) fail('native round-trip baseline or direction matrix is incomplete.') if (native.scenarios.some((scenario) => scenario.status !== 'pass' || scenario.differences?.length !== 0 || scenario.domains?.length === 0 || scenario.evidence === undefined)) fail('native round-trip contains an unknown difference or missing evidence.') const proxy = native.scenarios.find((scenario) => scenario.id === 'proxy-byte-preservation')?.evidence if (!proxy || proxy.byteIdentical !== true || proxy.sourceArchiveBytes !== proxy.preservedArchiveBytes || proxy.sourceSha256 !== proxy.preservedSha256 || proxy.proxyObjectCount !== 8 || proxy.resavedProxyObjectCount !== 8) fail('unknown/proxy archive bytes were not preserved.') const shape = native.scenarios.find((scenario) => scenario.id === 'locked-partdesign-shape')?.evidence if (!shape || shape.solidCount !== 1 || shape.faceCount !== 23 || shape.elementMapCount !== 2 || shape.elementMapPostfixCount !== 63 || !(shape.stringHasherBytes > 0)) fail('BRep/ElementMap/StringHasher evidence is incomplete.') if (semantic.status !== 'pass' || semantic.objectNames?.length !== 4 || semantic.archiveBytes <= 0 || semantic.expression !== 'Spreadsheet.Width * 2' || semantic.link !== 'Source' || semantic.linkSub?.subElement !== 'Face1' || semantic.linkSubList?.length !== 2 || semantic.guiViews?.length !== 2 || semantic.shapeResource?.byteLength <= 0 || semantic.opfs?.markerRemoved !== true) fail('Chrome Expression/Link/TopoRef/Sketcher/GuiDocument semantic evidence is incomplete.') if (chromeRoundTrip.status !== 'pass' || chromeRoundTrip.directions?.length !== 2 || chromeRoundTrip.scenarios?.some((scenario) => scenario.status !== 'pass' || scenario.differences?.length !== 0) || chromeRoundTrip.persistence?.roundTrip !== true || chromeRoundTrip.persistence?.released !== true || chromeRoundTrip.opfs?.markerRemoved !== true || chromeRoundTrip.afterRelease?.shapeCount !== 0 || chromeRoundTrip.afterRelease?.kernelReferenceCount !== 0) fail('Chrome bidirectional FCStd round-trip evidence is incomplete.') if (golden.status !== 'pass' || golden.scenarioCount !== 100 || golden.summary?.passed !== 100 || golden.summary?.unknownDifferences !== 0 || golden.summary?.formatRoundTrips?.step?.passed !== 100 || golden.summary?.formatRoundTrips?.iges?.passed !== 100 || golden.summary?.formatRoundTrips?.brep?.passed !== 100 || golden.summary?.formatRoundTrips?.step?.maximumVolumeDelta > 1e-6 || golden.summary?.formatRoundTrips?.iges?.maximumVolumeDelta > 1e-6 || golden.summary?.formatRoundTrips?.brep?.maximumVolumeDelta > 1e-6 || golden.afterRelease?.shapeCount !== 0 || golden.afterRelease?.kernelReferenceCount !== 0) fail('100-model Shape/STEP/IGES/BREP quality and tolerance golden is incomplete.') const formats = ['DXF', 'SVG', 'OBJ', 'PLY', 'STL', 'PDF', 'IFC', 'CSV'] if (secondary.status !== 'pass' || secondary.formats?.descriptors !== formats.length || secondary.formats.records !== formats.length || JSON.stringify(secondary.formats.categories) !== JSON.stringify(['2d', 'bim', 'data', 'mesh']) || secondary.formats.matrix?.length !== formats.length || secondary.formats.matrix.some((entry) => !entry.imported || !entry.rejectedInvalid || !entry.byteExactRoundTrip || !entry.deterministic) || secondary.persistence?.roundTrip !== true || secondary.persistence?.released !== true || secondary.opfs?.markerRemoved !== true || secondary.afterRelease?.shapeCount !== 0 || secondary.afterRelease?.kernelReferenceCount !== 0) fail('secondary 2D/Mesh/BIM/data format matrix is incomplete.') const hardening = spawnSync('./npmw', ['run', 'check:fcstd-hardening'], { cwd: root, encoding: 'utf8', timeout: 120_000 }) if (hardening.status !== 0) fail(`FC-11 hardening suite failed: ${(hardening.stdout || '') + (hardening.stderr || '')}`) console.log(JSON.stringify({ status: 'fcstd-closure-pass', nativeScenarios: nativeScenarioCount, semanticObjects: semantic.objectNames.length, goldenModels: golden.scenarioCount, formatMatrix: secondary.formats.matrix, chromeDirections: chromeRoundTrip.directions, hardening: 'pass' }, null, 2))