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 sourcePaths = [ '.cache/freecad/FreeCAD/src/App/PropertyLinks.h', '.cache/freecad/FreeCAD/src/App/PropertyLinks.cpp', '.cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.h', '.cache/freecad/FreeCAD/src/Mod/Sketcher/App/SketchObject.cpp', ] const outputPath = resolve(root, 'config/freecad-property-linklisthidden-inventory.json') const [runtimeContent, ...sourceContents] = await Promise.all([ readFile(resolve(root, runtimePath)), ...sourcePaths.map((path) => readFile(resolve(root, path))), ]) const runtime = JSON.parse(runtimeContent) const fail = (message) => { throw new Error(`FreeCAD PropertyLinkListHidden 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') const records = [] for (const objectType of runtime.runtimeObjects?.types ?? []) { for (const property of objectType.properties ?? []) { if (property.typeId !== 'App::PropertyLinkListHidden') 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.map((status) => status === 26 ? 'PropHidden' : `UnknownBit${status}`), defaultRaw: property.default, valueModel: { kind: 'hidden-document-object-list', inheritedFrom: 'App::PropertyLinkList', elementType: 'same-document App::DocumentObject reference', orderPreserved: true, duplicatesPreserved: true, writable: !property.status.some((status) => status === 'ReadOnly' || status === 'Immutable' || status === 24), }, inputs: { nativeCppSetValues: 'attached same-document DocumentObject list', emptyListClears: true, singleNullListClears: true, detachedObjectRejected: true, crossDocumentRejectedWithoutLinkAllowExternal: true, }, dependencies: { linkScope: 'Hidden', createsBacklinks: false, includedByDefaultGetLinks: false, includedByGetLinksAll: true, defaultDagDependency: false, }, applicability: { requiredObjectTypeId: objectType.typeId, role: 'Sketch exported child geometry lookup by object name', propertyWriteRequiresShape: false, source: 'Document.supportedTypes runtime inventory and SketchObject::getSubObject', }, }) } } records.sort((left, right) => `${left.objectTypeId}.${left.propertyName}`.localeCompare(`${right.objectTypeId}.${right.propertyName}`)) if (records.length !== 2 || new Set(records.map(({ objectTypeId }) => objectTypeId)).size !== 2) fail(`expected two native App::PropertyLinkListHidden records, found ${records.length}`) const report = { schemaVersion: 1, status: 'pass', baseline: { freecadVersion: runtime.freecadVersion, commit: runtime.gitCommit }, propertyType: 'App::PropertyLinkListHidden', recordCount: records.length, objectTypeCount: new Set(records.map(({ objectTypeId }) => objectTypeId)).size, writableRecordCount: records.filter(({ valueModel }) => valueModel.writable).length, records, typeContract: { inheritedFrom: 'App::PropertyLinkList', linkScope: 'Hidden', propertyStatus: 'PropHidden', propertyStatusPurpose: 'hide the Exports property from the ordinary editor surface', linkScopePurpose: 'exclude Exports from ordinary backlink and dependency traversal', xmlElement: 'LinkList', xmlChildElement: 'Link', legacyRestoreType: 'App::PropertyLinkList', }, provenance: { runtime: { path: runtimePath, bytes: runtimeContent.length, sha256: createHash('sha256').update(runtimeContent).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-linklisthidden-inventory-generated', output: 'config/freecad-property-linklisthidden-inventory.json', propertyType: report.propertyType, recordCount: report.recordCount, writableRecordCount: report.writableRecordCount }, null, 2))