84 lines
5.1 KiB
TypeScript
84 lines
5.1 KiB
TypeScript
import { FacadeHandlerError, FacadeRequest, FacadeResponse, FacadeSerialQueue, createRequest, isFacadeRequest } from "./facade-envelope.js";
|
|
import type { CloudKernelModule } from "./cloud-facade.js";
|
|
|
|
export const CLOUD_PYTHON_FAMILY_SCHEMA = "freecad-web-cloud/python-family-batch/v1" as const;
|
|
export const CLOUD_PYTHON_FAMILY_COMMAND = "cloud.pythonFamilyBatch" as const;
|
|
export const CLOUD_PYTHON_FAMILY_TIER = "tier-1-readonly" as const;
|
|
|
|
export interface CloudPythonFamilyRecord { apiId: string; name: string; family: string; }
|
|
export interface CloudPythonFamilyPayload {
|
|
schema: typeof CLOUD_PYTHON_FAMILY_SCHEMA;
|
|
module: "Cloud";
|
|
tier: typeof CLOUD_PYTHON_FAMILY_TIER;
|
|
records: CloudPythonFamilyRecord[];
|
|
}
|
|
export interface CloudPythonFamilyNativeRecord extends CloudPythonFamilyRecord {
|
|
status: "exposed" | "restricted" | "nonportable" | "not_implemented";
|
|
code: string;
|
|
message: string;
|
|
native: true;
|
|
sourceEntry?: string;
|
|
sourceRevision?: string;
|
|
}
|
|
export interface CloudPythonFamilyResult {
|
|
schema: typeof CLOUD_PYTHON_FAMILY_SCHEMA;
|
|
module: "Cloud";
|
|
tier: typeof CLOUD_PYTHON_FAMILY_TIER;
|
|
source: "freecad-source";
|
|
records: CloudPythonFamilyNativeRecord[];
|
|
count: number;
|
|
resourcesReleased: true;
|
|
}
|
|
export type CloudPythonFamilyRequest = FacadeRequest<CloudPythonFamilyPayload>;
|
|
export type CloudPythonFamilyResponse = FacadeResponse<CloudPythonFamilyResult>;
|
|
|
|
export function createCloudPythonFamilyRequest(requestId: string, records: CloudPythonFamilyRecord[]): CloudPythonFamilyRequest {
|
|
return createRequest(requestId, CLOUD_PYTHON_FAMILY_COMMAND, {
|
|
schema: CLOUD_PYTHON_FAMILY_SCHEMA,
|
|
module: "Cloud",
|
|
tier: CLOUD_PYTHON_FAMILY_TIER,
|
|
records,
|
|
}, "cloud-python-family");
|
|
}
|
|
|
|
export function isCloudPythonFamilyRequest(value: unknown): value is CloudPythonFamilyRequest {
|
|
if (!isFacadeRequest(value)) return false;
|
|
const request = value as CloudPythonFamilyRequest;
|
|
const payload = request.payload;
|
|
return request.command === CLOUD_PYTHON_FAMILY_COMMAND
|
|
&& payload?.schema === CLOUD_PYTHON_FAMILY_SCHEMA
|
|
&& payload?.module === "Cloud"
|
|
&& payload?.tier === CLOUD_PYTHON_FAMILY_TIER
|
|
&& Array.isArray(payload.records)
|
|
&& payload.records.length > 0
|
|
&& payload.records.every((record) => record && typeof record.apiId === "string" && typeof record.name === "string" && typeof record.family === "string");
|
|
}
|
|
|
|
function dispatchJson(kernel: CloudKernelModule, payload: CloudPythonFamilyPayload): CloudPythonFamilyResult {
|
|
const raw = String(kernel.ccall("fc_web_dispatch_json", "string", ["string"], [JSON.stringify({
|
|
cmd: CLOUD_PYTHON_FAMILY_COMMAND,
|
|
args: { tier: payload.tier, records: payload.records },
|
|
})]));
|
|
let reply: unknown;
|
|
try { reply = JSON.parse(raw); } catch { throw new FacadeHandlerError("native_protocol_error", "Cloud native dispatcher returned invalid JSON"); }
|
|
if (!reply || typeof reply !== "object") throw new FacadeHandlerError("native_protocol_error", "Cloud native dispatcher returned a non-object result");
|
|
const result = reply as Record<string, unknown>;
|
|
if (result.ok !== true) throw new FacadeHandlerError(String(result.code || "native_error"), String(result.error || "Cloud Python family dispatch failed"));
|
|
if (result.schema !== CLOUD_PYTHON_FAMILY_SCHEMA || result.module !== "Cloud" || result.tier !== CLOUD_PYTHON_FAMILY_TIER || result.source !== "freecad-source") throw new FacadeHandlerError("native_protocol_error", "Cloud native dispatcher returned an invalid batch schema");
|
|
if (!Array.isArray(result.records) || result.records.length !== payload.records.length || result.count !== payload.records.length || result.resourcesReleased !== true) throw new FacadeHandlerError("native_protocol_error", "Cloud native dispatcher returned an incomplete record set");
|
|
if (result.records.some((record) => !record || typeof record !== "object" || !["exposed", "restricted", "nonportable", "not_implemented"].includes(String((record as Record<string, unknown>).status)) || (record as Record<string, unknown>).native !== true)) throw new FacadeHandlerError("native_protocol_error", "Cloud native dispatcher returned an invalid record status");
|
|
return result as unknown as CloudPythonFamilyResult;
|
|
}
|
|
|
|
export async function handleCloudPythonFamily(payload: CloudPythonFamilyPayload, signal: AbortSignal, source: CloudKernelModule | Promise<CloudKernelModule>): Promise<CloudPythonFamilyResult> {
|
|
if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled");
|
|
if (!isCloudPythonFamilyRequest(createCloudPythonFamilyRequest("validation", payload.records))) throw new FacadeHandlerError("invalid_fixture", "Cloud Python family payload is invalid");
|
|
const kernel = await source;
|
|
if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled");
|
|
return dispatchJson(kernel, payload);
|
|
}
|
|
|
|
export function dispatchCloudPythonFamily(queue: FacadeSerialQueue, request: CloudPythonFamilyRequest, source: CloudKernelModule | Promise<CloudKernelModule>): Promise<CloudPythonFamilyResponse> {
|
|
return queue.dispatch(request, (payload, signal) => handleCloudPythonFamily(payload, signal, source));
|
|
}
|