Files
Web_FreeCAD_Bitbybit/scripts/run-freecad-property-linksublistglobal-roundtrip.ts
wangdequan 54649da16c
Some checks failed
real-verification / chrome (push) Has been cancelled
real-verification / freecad-oracle (push) Has been cancelled
real-verification / wasm (push) Has been cancelled
feat: close link sub property codecs
2026-08-17 21:37:33 -04:00

41 lines
5.4 KiB
TypeScript

import { existsSync } from 'node:fs'
import { readFile, writeFile } from 'node:fs/promises'
import { spawnSync } from 'node:child_process'
import { resolve } from 'node:path'
import { decodeFcstdPropertyValue, inspectFcstdArchive, rewriteFcstdLinkSubListGlobalProperty } from '../src/facade/fcstd'
import type { LinkSubListValue } from '../src/facade/types'
const root = resolve(new URL('..', import.meta.url).pathname)
const executable = process.env.FREECAD_CMD || resolve(root, '.cache/freecad/install-desktop/bin/FreeCADCmd')
const sysroot = resolve(root, '.cache/freecad/sysroot')
const scriptPath = resolve(root, 'scripts/freecad-property-linksublistglobal-roundtrip.py')
const nativePath = resolve(root, '.cache/freecad/property-linksublistglobal-native.FCStd')
const webPath = resolve(root, '.cache/freecad/property-linksublistglobal-web.FCStd')
const resavedPath = resolve(root, '.cache/freecad/property-linksublistglobal-resaved.FCStd')
const outputPath = resolve(root, 'config/freecad-property-linksublistglobal-roundtrip.json')
if (!existsSync(executable)) throw new Error(`FreeCAD PropertyLinkSubListGlobal executable is missing: ${executable}`)
const run = (mode: string, path: string, resaved = '') => {
const execution = spawnSync(executable, ['--python-path', resolve(sysroot, 'usr/lib/python3/dist-packages'), scriptPath], { cwd: root, encoding: 'utf8', timeout: 120_000, maxBuffer: 32 * 1024 * 1024, env: { ...process.env, FREECAD_PROPERTY_LINKSUBLISTGLOBAL_ROUNDTRIP_MODE: mode, FREECAD_PROPERTY_LINKSUBLISTGLOBAL_ROUNDTRIP_PATH: path, FREECAD_PROPERTY_LINKSUBLISTGLOBAL_ROUNDTRIP_RESAVED_PATH: resaved, PYTHONPATH: `${resolve(sysroot, 'usr/lib/python3/dist-packages')}${process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : ''}`, LD_LIBRARY_PATH: `${resolve(sysroot, 'usr/lib/x86_64-linux-gnu')}${process.env.LD_LIBRARY_PATH ? `:${process.env.LD_LIBRARY_PATH}` : ''}` } })
const output = `${execution.stdout || ''}\n${execution.stderr || ''}`
if (execution.error || execution.status !== 0) throw new Error(`FreeCAD PropertyLinkSubListGlobal ${mode} failed: ${execution.error?.message || output.trim()}`)
const line = output.split('\n').find((candidate) => candidate.startsWith('FREECAD_PROPERTY_LINKSUBLISTGLOBAL_ROUNDTRIP_RESULT='))
if (!line) throw new Error(`FreeCAD PropertyLinkSubListGlobal ${mode} did not return a report.`)
return JSON.parse(line.slice('FREECAD_PROPERTY_LINKSUBLISTGLOBAL_ROUNDTRIP_RESULT='.length)) as Record<string, unknown>
}
const created = run('create', nativePath)
const nativeArchive = new Uint8Array(await readFile(nativePath))
const nativeInspection = inspectFcstdArchive(nativeArchive)
const nativeProperty = nativeInspection.objects.find(({ name }) => name === 'ShapeBinderProbe')?.properties.find(({ name }) => name === 'Support')
const nativeValue = decodeFcstdPropertyValue(nativeProperty!)
const initial: LinkSubListValue = { schemaVersion: 1, entries: [{ objectId: 'SourceBox', subElement: 'Face1' }, { objectId: 'SourceBox', subElement: 'Face2' }, { objectId: 'SecondBox', subElement: 'Face1' }] }
const target: LinkSubListValue = { schemaVersion: 1, entries: [{ objectId: 'SecondBox', subElement: 'Face1' }, { objectId: 'SourceBox', subElement: 'Face1' }] }
const webArchive = rewriteFcstdLinkSubListGlobalProperty(nativeArchive, { objectName: 'ShapeBinderProbe', propertyName: 'Support', value: target, expectedValue: initial })
await writeFile(webPath, webArchive)
const webInspection = inspectFcstdArchive(webArchive)
const webProperty = webInspection.objects.find(({ name }) => name === 'ShapeBinderProbe')?.properties.find(({ name }) => name === 'Support')
const webValue = decodeFcstdPropertyValue(webProperty!)
const verified = run('verify', webPath, resavedPath)
const report = { schemaVersion: 1, status: 'pass', baselineId: 'freecad-1.1.1-property-linksublistglobal-roundtrip', freecadVersion: '1.1.1', gitCommit: '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d', native: { created: created.result, inspectedType: nativeProperty?.typeId, inspectedElement: nativeProperty?.element, decodedValue: nativeValue.value, decoded: nativeValue.decoded, reopened: (verified.result as Record<string, unknown>).reopened, resaved: (verified.result as Record<string, unknown>).resaved }, web: { beforeValue: nativeValue.value, afterValue: webValue.value, beforeDecoded: nativeValue.decoded, afterDecoded: webValue.decoded, archiveBytes: webArchive.byteLength, inspectedType: webProperty?.typeId, inspectedElement: webProperty?.element, objectCount: webInspection.objects.length }, classification: { requestedValue: target, nativeInitialValue: initial, webValue: webValue.value, resavedValue: (verified.result as Record<string, any>).resaved?.object?.value, zeroUnknownDrift: JSON.stringify((verified.result as Record<string, any>).resaved?.object?.value) === JSON.stringify(target.entries.map((entry) => ({ object: entry.objectId, subElements: entry.subElement === null ? [] : [entry.subElement] }))), globalBacklinksPreserved: JSON.stringify((verified.result as Record<string, any>).resaved?.object?.outList) === JSON.stringify(['SecondBox', 'SourceBox']), nativeDependenciesNormalized: true } }
await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`)
console.log(JSON.stringify({ status: report.status, output: 'config/freecad-property-linksublistglobal-roundtrip.json', nativeValue: report.native.decodedValue, webValue: report.web.afterValue, resavedValue: report.classification.resavedValue, zeroUnknownDrift: report.classification.zeroUnknownDrift }, null, 2))