Files
FreeCADWasmDev/web/src/jtreader-persistence-facade.ts
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

89 lines
4.3 KiB
TypeScript

import { FacadeHandlerError, FacadeRequest, FacadeResponse, FacadeSerialQueue, createRequest, isFacadeRequest } from "./facade-envelope.js";
import type { JtReaderKernelModule } from "./jtreader-facade.js";
export const JTREADER_PERSISTENCE_SCHEMA = "freecad-web-jtreader/save-reopen/v1" as const;
export const JTREADER_PERSISTENCE_COMMAND = "jtreader.saveReopen" as const;
export const JTREADER_PERSISTENCE_PATH = "/tmp/JtReaderKernel.FCStd" as const;
export interface JtReaderPersistencePayload {
schema: typeof JTREADER_PERSISTENCE_SCHEMA;
module: "JtReader";
action: "save/reopen";
path: string;
}
export interface JtReaderPersistenceResult {
schema: typeof JTREADER_PERSISTENCE_SCHEMA;
module: "JtReader";
action: "save/reopen";
format: ".fcstd";
path: typeof JTREADER_PERSISTENCE_PATH;
size: number;
objectCount: 1;
objectType: "App::InventorObject";
roundTrip: true;
resourcesReleased: true;
}
export type JtReaderPersistenceRequest = FacadeRequest<JtReaderPersistencePayload>;
export type JtReaderPersistenceResponse = FacadeResponse<JtReaderPersistenceResult>;
export const JTREADER_PERSISTENCE_FIXTURE: JtReaderPersistencePayload = {
schema: JTREADER_PERSISTENCE_SCHEMA,
module: "JtReader",
action: "save/reopen",
path: JTREADER_PERSISTENCE_PATH,
};
export function createJtReaderPersistenceRequest(requestId: string, payload: JtReaderPersistencePayload = JTREADER_PERSISTENCE_FIXTURE): JtReaderPersistenceRequest {
return createRequest(requestId, JTREADER_PERSISTENCE_COMMAND, payload, "jtreader-persistence-doc");
}
export function isJtReaderPersistenceRequest(value: unknown): value is JtReaderPersistenceRequest {
if (!isFacadeRequest(value)) return false;
const request = value as JtReaderPersistenceRequest;
const payload = request.payload;
return request.command === JTREADER_PERSISTENCE_COMMAND
&& payload?.schema === JTREADER_PERSISTENCE_SCHEMA
&& payload?.module === "JtReader"
&& payload?.action === "save/reopen"
&& typeof payload?.path === "string";
}
const kernelString = (kernel: JtReaderKernelModule, name: string) => kernel.ccall(name, "string") as string;
const kernelNumber = (kernel: JtReaderKernelModule, name: string) => kernel.ccall(name, "number") as number;
export async function handleJtReaderPersistence(payload: JtReaderPersistencePayload, signal: AbortSignal, kernelSource: JtReaderKernelModule | Promise<JtReaderKernelModule>): Promise<JtReaderPersistenceResult> {
if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled");
if (!isJtReaderPersistenceRequest(createJtReaderPersistenceRequest("validation", payload)) || payload.schema !== JTREADER_PERSISTENCE_SCHEMA || payload.module !== "JtReader" || payload.action !== "save/reopen") {
throw new FacadeHandlerError("invalid_fixture", "JtReader persistence fixture is invalid", { field: "action" });
}
if (payload.path !== JTREADER_PERSISTENCE_PATH) {
throw new FacadeHandlerError("invalid_path", "JtReader FCStd path is not writable", { path: payload.path });
}
const kernel = await kernelSource;
if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled");
if (kernelNumber(kernel, "fc_web_jtreader_save_reopen") !== 1) {
throw new FacadeHandlerError(
kernelString(kernel, "fc_web_jtreader_error_code") || "save_reopen_failed",
kernelString(kernel, "fc_web_jtreader_error_message") || "JtReader FCStd save/reopen failed",
);
}
return {
schema: JTREADER_PERSISTENCE_SCHEMA,
module: "JtReader",
action: "save/reopen",
format: ".fcstd",
path: kernelString(kernel, "fc_web_jtreader_persistence_path") as typeof JTREADER_PERSISTENCE_PATH,
size: kernelNumber(kernel, "fc_web_jtreader_persistence_size"),
objectCount: kernelNumber(kernel, "fc_web_jtreader_persistence_object_count") as 1,
objectType: kernelString(kernel, "fc_web_jtreader_persistence_object_type") as "App::InventorObject",
roundTrip: true,
resourcesReleased: true,
};
}
export function dispatchJtReaderPersistence(queue: FacadeSerialQueue, request: JtReaderPersistenceRequest, kernelSource: JtReaderKernelModule | Promise<JtReaderKernelModule>): Promise<JtReaderPersistenceResponse> {
return queue.dispatch(request, (payload, signal) => handleJtReaderPersistence(payload, signal, kernelSource));
}