import { FacadeHandlerError, FacadeRequest, FacadeResponse, FacadeSerialQueue, createRequest, isFacadeRequest } from "./facade-envelope.js"; export const BIM_PYTHON_FAMILY_SCHEMA = "freecad-web-bim/python-family/v1" as const; export const BIM_PYTHON_FAMILY_COMMAND = "bim.pythonFamily" as const; export const BIM_PYTHON_FAMILY_SOURCE_REVISION = "4f4e9244e613ea2df06e70027074a562695048cb" as const; export const BIM_PYTHON_FAMILY_APIS = ["getStringList", "getDefaultColor", "is_type_or_link", "getAllChildren", "pruneIncluded", "get_architectural_contents", "check", "mergeCells", "survey", "toggleIfcBrepFlag", "makeCompoundFromSelected", "cleanArchSplitter", "getExtrusionData", "uncamel", "getCleanName", "getValueAndDirection", "BoundBoxValid", "isOriented", "update_svg_cache", "getSVG", "getHost", "getPropertyNames", "getVector", "getDocumentMaterials", "makeMaterial", "getMaterialContainer", "makeMultiMaterial", "makeGrid", "makeBuildingPart", "makeFloor", "makeBuilding", "make2DDrawing", "makeProject", "makeSite", "makePipe", "makeProfile", "getCutVolume", "closeHole", "makeFace", "projectToVector", "get_freecad_type", "get_ifc_type", "getParents", "getProfileCenterPoint", "face_from_points", "placeAlongEdge", "getUnit", "getUnsigned", "getPlacement", "getSqlKeywords", "getPropertyData", "getEdgesAngle", "getSqlApiDocumentation", "adjust_list_len", "getCameraData", "getLengthOfRebar", "find_inters", "getSectionData", "hex2rgb", "set_shininess", "percent_sh2fc", "cross_product"] as const; export type BimPythonFamilyApi = typeof BIM_PYTHON_FAMILY_APIS[number]; export interface BimPythonFamilyPayload { schema: typeof BIM_PYTHON_FAMILY_SCHEMA; module: "BIM"; sourceRevision: string; apiId: `py:BIM:function:${BimPythonFamilyApi}`; name: BimPythonFamilyApi; } export interface BimPythonFamilyResult { schema: typeof BIM_PYTHON_FAMILY_SCHEMA; module: "BIM"; action: "python-family"; sourceRevision: string; apiId: BimPythonFamilyPayload["apiId"]; name: BimPythonFamilyApi; result: string; count: number; source: "freecad-source"; nativeProbe: true; } export type BimPythonFamilyRequest = FacadeRequest; export type BimPythonFamilyResponse = FacadeResponse; export interface BimPythonFamilyKernel { ccall(name: string, returnType: "number" | "string", argTypes?: string[], args?: unknown[]): number | string } function dispatchJson(kernel: BimPythonFamilyKernel, request: Record): Record { const raw = String(kernel.ccall("fc_web_dispatch_json", "string", ["string"], [JSON.stringify(request)])); let reply: unknown; try { reply = JSON.parse(raw); } catch { throw new FacadeHandlerError("native_protocol_error", "BIM native dispatcher returned invalid JSON"); } if (!reply || typeof reply !== "object") throw new FacadeHandlerError("native_protocol_error", "BIM native dispatcher returned a non-object result"); const result = reply as Record; if (result.ok !== true) throw new FacadeHandlerError(String(result.code || "native_error"), String(result.error || "BIM native dispatch failed")); return result; } export function createBimPythonFamilyRequest(requestId: string, name: BimPythonFamilyApi, sourceRevision: string = BIM_PYTHON_FAMILY_SOURCE_REVISION): BimPythonFamilyRequest { return createRequest(requestId, BIM_PYTHON_FAMILY_COMMAND, { schema: BIM_PYTHON_FAMILY_SCHEMA, module: "BIM", sourceRevision, apiId: `py:BIM:function:${name}`, name }, "bim-python-family"); } export function isBimPythonFamilyRequest(value: unknown): value is BimPythonFamilyRequest { if (!isFacadeRequest(value)) return false; const p = (value as BimPythonFamilyRequest).payload; return (value as BimPythonFamilyRequest).command === BIM_PYTHON_FAMILY_COMMAND && p?.schema === BIM_PYTHON_FAMILY_SCHEMA && p?.module === "BIM" && p?.sourceRevision === BIM_PYTHON_FAMILY_SOURCE_REVISION && BIM_PYTHON_FAMILY_APIS.includes(p?.name) && p?.apiId === `py:BIM:function:${p?.name}`; } export async function handleBimPythonFamily(payload: BimPythonFamilyPayload, signal: AbortSignal, source: BimPythonFamilyKernel | Promise): Promise { if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled"); if (!isBimPythonFamilyRequest(createBimPythonFamilyRequest("validation", payload.name, payload.sourceRevision))) throw new FacadeHandlerError("invalid_api_id", "BIM Python family API identity is invalid", { apiId: payload.apiId }); const kernel = await source; if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled"); const native = dispatchJson(kernel, { cmd: BIM_PYTHON_FAMILY_COMMAND, args: { name: payload.name } }); const name = String(native.name) as BimPythonFamilyApi; if (name !== payload.name) throw new FacadeHandlerError("native_result_mismatch", "BIM Python family returned a different API name", { expected: payload.name, actual: name }); return { schema: BIM_PYTHON_FAMILY_SCHEMA, module: "BIM", action: "python-family", sourceRevision: payload.sourceRevision, apiId: payload.apiId, name, result: String(native.result || ""), count: Number(native.count || 0), source: "freecad-source", nativeProbe: true }; } export function dispatchBimPythonFamily(queue: FacadeSerialQueue, request: BimPythonFamilyRequest, source: BimPythonFamilyKernel | Promise): Promise { return queue.dispatch(request, (payload, signal) => handleBimPythonFamily(payload, signal, source)); }