2026-09-21 17:38:45 -07:00
|
|
|
import assert from "node:assert/strict";
|
|
|
|
|
import { readFile, mkdir, writeFile } from "node:fs/promises";
|
|
|
|
|
import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/index.js";
|
|
|
|
|
import { compileQuery } from "../src/query/compile.js";
|
|
|
|
|
import { parseControllerCatalog } from "../src/capability-model/controllers.js";
|
|
|
|
|
import {
|
|
|
|
|
compileExecutionPlan,
|
|
|
|
|
generateInterfaceClientBindings,
|
|
|
|
|
generatePackageQueryClientBindings,
|
|
|
|
|
} from "../src/execution-world/index.js";
|
|
|
|
|
const read = (file: string) => readFile("test/fixtures/controller-device/" + file, "utf8");
|
|
|
|
|
const source = { repository: "https://example.test/device.git", commit: "a".repeat(40) };
|
|
|
|
|
const device = compileCapabilityResourceSource(await read("Device.qx"), { source });
|
|
|
|
|
assert.ok(device.ok && device.resource.kind === "interface", JSON.stringify(device.diagnostics));
|
|
|
|
|
const control = compileCapabilityResourceSource(await read("DeviceController.qx"), { source });
|
|
|
|
|
assert.ok(control.ok && control.resource.kind === "interface", JSON.stringify(control.diagnostics));
|
|
|
|
|
const iface = device.resource.revision,
|
|
|
|
|
controllerInterface = control.resource.revision,
|
|
|
|
|
environment = {
|
|
|
|
|
interfaces: new Map([
|
|
|
|
|
["Device", iface],
|
|
|
|
|
["DeviceController", controllerInterface],
|
|
|
|
|
]),
|
|
|
|
|
interfaceClosure: [iface, controllerInterface],
|
|
|
|
|
};
|
|
|
|
|
const driver = compileCapabilityResourceSource(await read("Driver.qx"), { source, environment });
|
|
|
|
|
assert.ok(driver.ok && driver.resource.kind === "package", JSON.stringify(driver.diagnostics));
|
|
|
|
|
const pkg = driver.resource.revision;
|
|
|
|
|
pkg.checkedQueries = await Promise.all(
|
|
|
|
|
pkg.queries!.map((query) => compileQuery(query, [iface, controllerInterface], read)),
|
|
|
|
|
);
|
|
|
|
|
pkg.controllers = parseControllerCatalog(
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
schemaVersion: 1,
|
|
|
|
|
controllers: [
|
|
|
|
|
{
|
|
|
|
|
id: "device-driver",
|
|
|
|
|
registry: "enabled",
|
|
|
|
|
subject: "device-controller@1",
|
|
|
|
|
snapshot: "device:snapshot:call",
|
|
|
|
|
remote: "device:apply:call",
|
|
|
|
|
input: "value",
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}),
|
|
|
|
|
[iface, controllerInterface],
|
|
|
|
|
pkg.checkedQueries,
|
|
|
|
|
);
|
2026-09-21 18:31:43 -07:00
|
|
|
const viewer = compileCapabilityResourceSource(await read("Viewer.qx"), {
|
|
|
|
|
source: { ...source, repository: "https://example.test/device-viewer.git" },
|
|
|
|
|
environment,
|
|
|
|
|
});
|
|
|
|
|
assert.ok(viewer.ok && viewer.resource.kind === "package", JSON.stringify(viewer.diagnostics));
|
|
|
|
|
const viewerPackage = viewer.resource.revision;
|
|
|
|
|
viewerPackage.checkedQueries = await Promise.all(
|
|
|
|
|
viewerPackage.queries!.map((query) => compileQuery(query, [iface], read)),
|
|
|
|
|
);
|
2026-09-21 17:38:45 -07:00
|
|
|
for (const variant of ["controller", "controller-next"]) {
|
2026-09-21 18:31:43 -07:00
|
|
|
const extra = variant.endsWith("next")
|
|
|
|
|
? 'atom Dashboard id "dashboard"; import package DeviceViewer; presentation DeviceStatus id "device:status" for Device using DeviceViewer.Status;'
|
|
|
|
|
: "";
|
2026-09-21 17:38:45 -07:00
|
|
|
const result = compileCapabilitySource((await read("workspace.qx")).replace(/}\s*$/, extra + "\n}"), "workspace.qx", {
|
|
|
|
|
...environment,
|
2026-09-21 18:31:43 -07:00
|
|
|
packages: new Map([
|
|
|
|
|
["Driver", pkg],
|
|
|
|
|
...(variant.endsWith("next") ? [["DeviceViewer", viewerPackage] as const] : []),
|
|
|
|
|
]),
|
2026-09-21 17:38:45 -07:00
|
|
|
});
|
|
|
|
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
|
|
|
|
const plan = compileExecutionPlan(result.workspace),
|
|
|
|
|
root = "dist/managed/" + variant;
|
|
|
|
|
await mkdir(root, { recursive: true });
|
|
|
|
|
await writeFile(root + "/client.ts", generateInterfaceClientBindings({ Device: iface }));
|
|
|
|
|
await writeFile(root + "/queries.ts", generatePackageQueryClientBindings(pkg));
|
|
|
|
|
for (const [file, value] of Object.entries({
|
|
|
|
|
"candidate.json": result.workspace,
|
|
|
|
|
"execution-plan.json": plan,
|
|
|
|
|
"entry.json": null,
|
|
|
|
|
"source.json": { kind: "workspace", ...source },
|
|
|
|
|
}))
|
|
|
|
|
await writeFile(root + "/" + file, JSON.stringify(value));
|
|
|
|
|
}
|