feat: prepare FreeCAD private naming worker linkage
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

This commit is contained in:
2026-08-11 23:16:22 -04:00
parent aa607451ad
commit 97967041e2
30 changed files with 505 additions and 84 deletions

View File

@@ -52,6 +52,7 @@ export type FcstdObjectSummary = {
name: string
label: string
typeId: string
nativeObjectTag?: number
propertyCount: number
properties: FcstdPropertySummary[]
support: FcstdObjectSupport
@@ -1103,16 +1104,32 @@ export const serializeFcstdMetadataArchive = (document: DocumentSnapshot, option
const objectIds = document.objects.map((object) => object.id.trim())
if (objectIds.some((objectId) => !objectId)) throw new TypeError('FCStd metadata writer requires non-empty object ids.')
if (new Set(objectIds).size !== objectIds.length) throw new TypeError('FCStd metadata writer requires unique object ids.')
const explicitObjectTags = new Set<number>()
for (const object of document.objects) {
if (object.nativeObjectTag === undefined) continue
if (!Number.isSafeInteger(object.nativeObjectTag) || object.nativeObjectTag <= 0) throw new RangeError(`FCStd object ${object.id} nativeObjectTag must be a positive safe integer.`)
if (explicitObjectTags.has(object.nativeObjectTag)) throw new RangeError(`FCStd nativeObjectTag ${object.nativeObjectTag} is assigned to more than one object.`)
explicitObjectTags.add(object.nativeObjectTag)
}
let nextObjectTag = 1
const objectTags = new Map(document.objects.map((object) => {
if (object.nativeObjectTag !== undefined) return [object.id, object.nativeObjectTag] as const
while (explicitObjectTags.has(nextObjectTag)) nextObjectTag += 1
const tag = nextObjectTag
explicitObjectTags.add(tag)
nextObjectTag += 1
return [object.id, tag] as const
}))
const objectIdSet = new Set(objectIds)
const dependencies = new Map(objectIds.map((objectId) => [objectId, [] as string[]]))
for (const edge of document.dependencies ?? []) {
if (!objectIdSet.has(edge.sourceId) || !objectIdSet.has(edge.targetId)) throw new Error(`FCStd dependency ${edge.sourceId} -> ${edge.targetId} references an undeclared object.`)
if (edge.relation !== 'view') dependencies.get(edge.sourceId)?.push(edge.targetId)
}
const objectDeclarations = document.objects.map((object, objectIndex) => {
const objectDeclarations = document.objects.map((object) => {
const targets = dependencies.get(object.id) ?? []
const dependencyXml = targets.length === 0 ? `<ObjectDeps Name="${xmlEscape(object.id)}" Count="0"/>` : `<ObjectDeps Name="${xmlEscape(object.id)}" Count="${targets.length}">${targets.map((target) => `<Dep Name="${xmlEscape(target)}"/>`).join('')}</ObjectDeps>`
return `${dependencyXml}<Object name="${xmlEscape(object.id)}" type="${xmlEscape(nativeFcstdTypeId(object.typeId))}" id="${objectIndex + 1}" Touched="1"/>`
return `${dependencyXml}<Object name="${xmlEscape(object.id)}" type="${xmlEscape(nativeFcstdTypeId(object.typeId))}" id="${objectTags.get(object.id)}" Touched="1"/>`
}).join('')
const objectData = document.objects.map((object) => {
const sketchGeneratedPropertyNames = object.sketch ? new Set(['Geometry', 'Constraints', 'ExternalGeo', 'ExternalGeometry', 'ExternalTypes', 'WebGeometryIds', 'WebSyntheticConstraintIds', 'WebExternalIds', 'WebExternalProjectionIds', 'WebExternalSources']) : new Set<string>()
@@ -2014,9 +2031,18 @@ const parseDocumentXml = (bytes: Uint8Array, limits: FcstdArchiveLimits) => {
const dataByName = new Map(objectData.map((data) => [attribute(data, 'name'), data]))
const documentProperties = asArray((((root.Properties as Record<string, unknown> | undefined)?.Property) as Record<string, unknown> | Record<string, unknown>[] | undefined))
const labelProperty = documentProperties.find((property) => attribute(property, 'name') === 'Label')
const nativeObjectTags = new Set<number>()
const objects = objectDeclarations.map((declaration): FcstdObjectSummary => {
const name = attribute(declaration, 'name') || '<unnamed>'
const typeId = attribute(declaration, 'type') || 'App::DocumentObject'
const objectTagText = attribute(declaration, 'id')
let nativeObjectTag: number | undefined
if (objectTagText) {
if (!/^\d+$/.test(objectTagText) || !Number.isSafeInteger(Number(objectTagText)) || Number(objectTagText) <= 0) throw new Error(`FCStd object ${name} has an invalid native id.`)
nativeObjectTag = Number(objectTagText)
if (nativeObjectTags.has(nativeObjectTag)) throw new Error(`FCStd object native id ${nativeObjectTag} is duplicated.`)
nativeObjectTags.add(nativeObjectTag)
}
const data = dataByName.get(name)
const propertiesContainer = data?.Properties as Record<string, unknown> | undefined
const properties = asArray(((propertiesContainer?.Property) as Record<string, unknown> | Record<string, unknown>[] | undefined))
@@ -2029,7 +2055,7 @@ const parseDocumentXml = (bytes: Uint8Array, limits: FcstdArchiveLimits) => {
const support: FcstdObjectSupport = blockedTypeId(typeId) ? 'blocked' : recognizedTypeIds.has(typeId) ? 'recognized' : 'proxy'
const summaries = propertySummaries(properties).map((property) => expressions.has(property.name) ? { ...property, expression: expressions.get(property.name) } : property)
const sketch = typeId === 'Sketcher::SketchObject' ? sketchFromPropertySummaries(name, summaries) : undefined
return { name, label: objectLabelProperty ? propertyValue(objectLabelProperty) || name : name, typeId, propertyCount: properties.length, properties: summaries, support, extensions, ...(sketch ? { sketch } : {}) }
return { name, label: objectLabelProperty ? propertyValue(objectLabelProperty) || name : name, typeId, ...(nativeObjectTag === undefined ? {} : { nativeObjectTag }), propertyCount: properties.length, properties: summaries, support, extensions, ...(sketch ? { sketch } : {}) }
})
return {
schemaVersion: attribute(root, 'SchemaVersion') || attribute(root, 'schemaVersion') || 'unknown',
@@ -2348,6 +2374,7 @@ const createProxyDocument = (inspection: Omit<FcstdInspection, 'proxyDocument'>)
return {
id: object.name,
typeId: object.typeId,
...(object.nativeObjectTag === undefined ? {} : { nativeObjectTag: object.nativeObjectTag }),
properties: [
createProxyProperty('Label', 'Label', object.label),
createProxyProperty('TypeId', 'Type', object.typeId),