import { createHash } from 'node:crypto' import { readFile } from 'node:fs/promises' import { resolve } from 'node:path' const root = resolve(new URL('..', import.meta.url).pathname) const readBytes = (relativePath) => readFile(resolve(root, relativePath)) const readJson = async (relativePath) => JSON.parse(await readFile(resolve(root, relativePath), 'utf8')) const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex') const assertEqual = (actual, expected, label) => { if (actual !== expected) throw new Error(`${label} drifted: expected ${expected}, got ${actual}.`) } const baseline = await readJson('config/occt-upstream-drift-baseline.json') const matrix = await readJson('config/compatibility-matrix.json') const packageJson = await readJson('node_modules/@bitbybit-dev/occt/package.json') const goldenManifestBytes = await readBytes('fixtures/freecad-golden/manifest.json') const goldenManifest = JSON.parse(goldenManifestBytes) const threeWayBytes = await readBytes('config/freecad-bitbybit-native-threeway.json') const threeWay = JSON.parse(threeWayBytes) if (baseline.schemaVersion !== 1) throw new Error('Unsupported OCCT upstream drift baseline schema.') assertEqual(packageJson.version, baseline.bitbybit.version, 'Bitbybit package version') assertEqual(matrix.bitbybitSource.commit, baseline.bitbybit.sourceCommit, 'Bitbybit source commit') assertEqual(matrix.bitbybitSource.wasmArtifact.sha256, baseline.bitbybit.wasmSha256, 'Bitbybit declared WASM hash') assertEqual(sha256(await readBytes(matrix.bitbybitSource.wasmArtifact.path)), baseline.bitbybit.wasmSha256, 'Bitbybit installed WASM hash') const declarations = await readFile(resolve(root, baseline.bitbybit.declarationPath), 'utf8') const canonicalSurface = baseline.bitbybit.surfaceInterfaces.map((name) => { const match = declarations.match(new RegExp(`export interface ${name} extends ClassHandle \\{([\\s\\S]*?)\\n\\}`)) if (!match) throw new Error(`Bitbybit ABI surface is missing ${name}.`) return `${name}:${match[1].replace(/\s+/g, ' ').trim()}` }).join('\n') assertEqual(sha256(canonicalSurface), baseline.bitbybit.surfaceSha256, 'Bitbybit declaration surface') assertEqual(matrix.nativeOcctHistory.occtVersion, baseline.nativeOcct.version, 'Native OCCT version') assertEqual(matrix.nativeOcctHistory.sourceCommit, baseline.nativeOcct.sourceCommit, 'Native OCCT source commit') assertEqual(matrix.nativeOcctHistory.freecadNamingBuild.status, baseline.nativeOcct.buildStatus, 'Native OCCT naming build status') for (const [kind, relativePath, expectedHash] of [ ['JavaScript', matrix.nativeOcctHistory.artifact.javascript, baseline.nativeOcct.javascriptSha256], ['WASM', matrix.nativeOcctHistory.artifact.wasm, baseline.nativeOcct.wasmSha256], ['DATA', matrix.nativeOcctHistory.artifact.data, baseline.nativeOcct.dataSha256], ]) { assertEqual(sha256(await readBytes(relativePath)), expectedHash, `Native OCCT ${kind} artifact`) } assertEqual(goldenManifest.scenarios.length, baseline.golden.manifestScenarioCount, 'Golden manifest scenario count') assertEqual(sha256(goldenManifestBytes), baseline.golden.manifestSha256, 'Golden manifest') assertEqual(threeWay.scenarioCount, baseline.golden.threeWayScenarioCount, 'Three-way scenario count') assertEqual(sha256(threeWayBytes), baseline.golden.threeWayReportSha256, 'Three-way golden report') const operationCounts = Object.fromEntries(['cut', 'fuse', 'common'].map((operation) => [operation, threeWay.reports.filter((entry) => entry.operation === operation).length])) for (const [operation, expectedCount] of Object.entries(baseline.golden.operationCounts)) assertEqual(operationCounts[operation], expectedCount, `${operation} scenario count`) for (const entry of threeWay.reports) { if (entry.bitbybit.differences.differences.length || entry.native.differences.differences.length) throw new Error(`Unknown golden drift in ${entry.id}.`) } console.log(JSON.stringify({ status: 'occt-upstream-drift-pass', bitbybit: { version: packageJson.version, sourceCommit: baseline.bitbybit.sourceCommit, surfaceSha256: baseline.bitbybit.surfaceSha256 }, nativeOcct: { version: baseline.nativeOcct.version, sourceCommit: baseline.nativeOcct.sourceCommit, buildStatus: baseline.nativeOcct.buildStatus }, golden: { manifestScenarios: goldenManifest.scenarios.length, threeWayScenarios: threeWay.scenarioCount, operationCounts } }, null, 2))