import { createHash } from 'node:crypto' import { readFile, writeFile } from 'node:fs/promises' import { resolve } from 'node:path' const root = resolve(new URL('..', import.meta.url).pathname) const runtimePath = '.cache/freecad/reference-desktop.json' const oraclePath = 'config/freecad-partdesign-structure-oracle.json' const sourcePaths = [ '.cache/freecad/FreeCAD/src/App/PropertyLinks.h', '.cache/freecad/FreeCAD/src/App/PropertyLinks.cpp', '.cache/freecad/FreeCAD/src/Mod/PartDesign/App/ShapeBinder.h', '.cache/freecad/FreeCAD/src/Mod/PartDesign/App/ShapeBinder.cpp', ] const outputPath = resolve(root, 'config/freecad-property-linksublistglobal-inventory.json') const [runtimeContent, oracleContent, ...sourceContents] = await Promise.all([ readFile(resolve(root, runtimePath)), readFile(resolve(root, oraclePath)), ...sourcePaths.map((path) => readFile(resolve(root, path))), ]) const runtime = JSON.parse(runtimeContent) const oracle = JSON.parse(oracleContent) const fail = (message) => { throw new Error(`FreeCAD PropertyLinkSubListGlobal inventory generation failed: ${message}`) } if (runtime.schemaVersion !== 1 || runtime.freecadVersion !== '1.1.1' || runtime.gitCommit !== '0108fd4b4850cc46e625b60e53cea7a7bbe69f8d') fail('desktop oracle is not the locked FreeCAD baseline') if (oracle.status !== 'pass' || oracle.baselineId !== 'freecad-1.1.1-partdesign-structure-oracle' || oracle.crossDocument?.initial?.shapeBinderSupportType !== 'App::PropertyLinkSubListGlobal') fail('PartDesign structure oracle does not contain the global ShapeBinder Support boundary') const records = [] for (const objectType of runtime.runtimeObjects?.types ?? []) for (const property of objectType.properties ?? []) { if (property.typeId !== 'App::PropertyLinkSubListGlobal') continue records.push({ objectTypeId: objectType.typeId, objectAvailable: objectType.available === true, probeStatus: objectType.probeStatus, propertyName: property.name, group: property.group, statusRaw: property.status, statusNames: property.status, defaultRaw: property.default, valueModel: { kind: 'same-document-object-and-ordered-sub-element-pair-list', inheritedFrom: 'App::PropertyLinkSubList', orderPreserved: true, duplicatesPreserved: true, writable: true }, inputs: { nativeCppSetValues: 'parallel DocumentObject and sub-element lists', emptyListMeansNoReferences: true, nullObjectClears: true, detachedObjectRejected: true, crossDocumentRejectedWithoutLinkAllowExternal: true, invalidSubElementAcceptedAsReference: true }, dependencies: { linkScope: 'Global', createsBacklinks: true, includedByDefaultGetLinks: true, includedByGetLinksAll: true, defaultDagDependency: true }, applicability: { requiredObjectTypeId: objectType.typeId, role: 'ShapeBinder source geometry support', propertyWriteRequiresShape: false, source: 'PartDesign ShapeBinder Support' }, }) } records.sort((left, right) => `${left.objectTypeId}.${left.propertyName}`.localeCompare(`${right.objectTypeId}.${right.propertyName}`)) if (records.length !== 1 || records[0].objectTypeId !== 'PartDesign::ShapeBinder' || records[0].propertyName !== 'Support') fail(`expected one native ShapeBinder Support record, found ${records.length}`) const report = { schemaVersion: 1, status: 'pass', baseline: { freecadVersion: runtime.freecadVersion, commit: runtime.gitCommit }, propertyType: 'App::PropertyLinkSubListGlobal', recordCount: records.length, objectTypeCount: new Set(records.map(({ objectTypeId }) => objectTypeId)).size, writableRecordCount: records.filter(({ valueModel }) => valueModel.writable).length, records, typeContract: { inheritedFrom: 'App::PropertyLinkSubList', linkScope: 'Global', propertyStatus: [], xmlElement: 'LinkSubList', xmlChildElement: 'Link', emptyListMeans: 'no-references', crossDocumentBoundary: 'reject-without-LinkAllowExternal' }, nativeEvidence: { structureOracle: { path: oraclePath, baselineId: oracle.baselineId, crossDocumentShapeBinderSupportType: oracle.crossDocument.initial.shapeBinderSupportType, crossDocumentAccepted: oracle.crossDocument.initial.shapeBinderExternalLink.accepted, crossDocumentError: oracle.crossDocument.initial.shapeBinderExternalLink.error, crossDocumentShapeBinderSupport: oracle.crossDocument.initial.shapeBinderSupport }, runtimeHost: 'PartDesign::ShapeBinder.Support' }, provenance: { runtime: { path: runtimePath, bytes: runtimeContent.length, sha256: createHash('sha256').update(runtimeContent).digest('hex') }, oracle: { path: oraclePath, bytes: oracleContent.length, sha256: createHash('sha256').update(oracleContent).digest('hex') }, sources: sourcePaths.map((path, index) => ({ path, bytes: sourceContents[index].length, sha256: createHash('sha256').update(sourceContents[index]).digest('hex') })), }, classification: 'opaque-fcstd-proxy', nextPhases: ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'], } await writeFile(outputPath, `${JSON.stringify(report, null, 2)}\n`) console.log(JSON.stringify({ status: 'freecad-property-linksublistglobal-inventory-generated', output: 'config/freecad-property-linksublistglobal-inventory.json', propertyType: report.propertyType, recordCount: report.recordCount, objectTypes: report.records.map(({ objectTypeId }) => objectTypeId) }, null, 2))