feat: advance FreeCAD parity and native OCCT history
This commit is contained in:
83
scripts/check-bitbybit-history-surface.mjs
Normal file
83
scripts/check-bitbybit-history-surface.mjs
Normal file
@@ -0,0 +1,83 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { execFile } from 'node:child_process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { promisify } from 'node:util'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
const root = resolve(fileURLToPath(new URL('..', import.meta.url)))
|
||||
const run = promisify(execFile)
|
||||
const packageJson = JSON.parse(await readFile(resolve(root, 'node_modules/@bitbybit-dev/occt/package.json'), 'utf8'))
|
||||
const matrix = JSON.parse(await readFile(resolve(root, 'config/compatibility-matrix.json'), 'utf8'))
|
||||
const sourcePath = resolve(root, matrix.bitbybitSource.localSnapshot)
|
||||
const { stdout: sourceCommitOutput } = await run('git', ['-C', sourcePath, 'rev-parse', 'HEAD'])
|
||||
const sourceCommit = sourceCommitOutput.trim()
|
||||
if (sourceCommit !== matrix.bitbybitSource.commit) throw new Error(`Bitbybit source commit mismatch: expected ${matrix.bitbybitSource.commit}, got ${sourceCommit}.`)
|
||||
if (packageJson.version !== matrix.bitbybitSource.version) throw new Error(`Bitbybit package version mismatch: expected ${matrix.bitbybitSource.version}, got ${packageJson.version}.`)
|
||||
const sourceCppFiles = (await run('git', ['-C', sourcePath, 'ls-tree', '-r', '--name-only', 'HEAD'])).stdout
|
||||
.split('\n').filter((file) => /\.(c|cc|cpp|cxx|h|hh|hpp)$/.test(file))
|
||||
if (sourceCppFiles.length !== 0) throw new Error(`Bitbybit source snapshot unexpectedly contains native source files (${sourceCppFiles.length}); review the custom binding build inputs.`)
|
||||
const declarationPath = resolve(root, 'node_modules/@bitbybit-dev/occt/bitbybit-dev-occt/bitbybit-dev-occt.d.ts')
|
||||
const declarations = await readFile(declarationPath, 'utf8')
|
||||
|
||||
const interfaceBody = (name) => {
|
||||
const match = declarations.match(new RegExp(`export interface ${name} extends ClassHandle \\{([\\s\\S]*?)\\n\\}`))
|
||||
if (!match) throw new Error(`Bitbybit declaration is missing interface ${name}.`)
|
||||
return match[1]
|
||||
}
|
||||
const hasMethod = (body, name) => new RegExp(`\\n\\s*${name}\\(`).test(body)
|
||||
const assert = (condition, message) => {
|
||||
if (!condition) throw new Error(message)
|
||||
}
|
||||
|
||||
const booleanInterfaces = ['BRepAlgoAPI_Fuse', 'BRepAlgoAPI_Cut', 'BRepAlgoAPI_Common']
|
||||
const booleanHistorySurface = Object.fromEntries(booleanInterfaces.map((name) => {
|
||||
const body = interfaceBody(name)
|
||||
return [name, {
|
||||
generated: hasMethod(body, 'Generated'),
|
||||
modified: hasMethod(body, 'Modified'),
|
||||
deleted: hasMethod(body, 'IsDeleted'),
|
||||
hasGenerated: hasMethod(body, 'HasGenerated')
|
||||
}]
|
||||
}))
|
||||
const builderBody = interfaceBody('BOPAlgo_Builder')
|
||||
const listBody = interfaceBody('TopTools_ListOfShape')
|
||||
|
||||
for (const [name, surface] of Object.entries(booleanHistorySurface)) {
|
||||
assert(!surface.generated && !surface.modified && !surface.deleted, `${name} unexpectedly exposes native Generated/Modified/IsDeleted; review the adapter contract.`)
|
||||
assert(surface.hasGenerated, `${name} must retain HasGenerated for capability reporting.`)
|
||||
}
|
||||
assert(hasMethod(builderBody, 'Modified'), 'BOPAlgo_Builder.Modified(shape) is required for the partial history surface.')
|
||||
for (const method of ['Size', 'IsEmpty', 'Append', 'First']) {
|
||||
assert(hasMethod(listBody, method), `TopTools_ListOfShape.${method}() is required for the partial history surface.`)
|
||||
}
|
||||
|
||||
console.log(JSON.stringify({
|
||||
provider: 'bitbybit-occt',
|
||||
version: packageJson.version,
|
||||
sourceCommit,
|
||||
sourceForm: matrix.bitbybitSource.sourceForm,
|
||||
missingBuildInputs: matrix.bitbybitSource.missingBuildInputs,
|
||||
declarationPath: 'node_modules/@bitbybit-dev/occt/bitbybit-dev-occt/bitbybit-dev-occt.d.ts',
|
||||
nativeBooleanHistory: { generated: false, modified: false, deleted: false },
|
||||
partialBuilderHistory: { modified: true, listIndexAccess: false },
|
||||
nativeExtension: await (async () => {
|
||||
const result = { status: 'not-built' }
|
||||
for (const [key, relativePath, expectedHash] of [
|
||||
['javascript', matrix.nativeOcctHistory.artifact.javascript, matrix.nativeOcctHistory.artifact.javascriptSha256],
|
||||
['wasm', matrix.nativeOcctHistory.artifact.wasm, matrix.nativeOcctHistory.artifact.wasmSha256]
|
||||
]) {
|
||||
try {
|
||||
const bytes = await readFile(resolve(root, relativePath))
|
||||
const hash = createHash('sha256').update(bytes).digest('hex')
|
||||
if (hash !== expectedHash) throw new Error(`${key} hash mismatch: expected ${expectedHash}, got ${hash}.`)
|
||||
result[key] = 'hash-ok'
|
||||
} catch (error) {
|
||||
if (error?.code !== 'ENOENT') throw error
|
||||
}
|
||||
}
|
||||
if (result.javascript === 'hash-ok' && result.wasm === 'hash-ok') result.status = 'built-hash-ok'
|
||||
return result
|
||||
})(),
|
||||
status: 'audit-pass'
|
||||
}, null, 2))
|
||||
Reference in New Issue
Block a user