224 lines
12 KiB
TypeScript
224 lines
12 KiB
TypeScript
/** Protocol/process qualification only; no application fixture enters runtime code. */
|
|
import assert from "node:assert/strict";
|
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/index.js";
|
|
import { compileExecutionPlan } from "../src/execution-world/plan.js";
|
|
import { compileKernelProgram } from "../src/execution-world/kernel.js";
|
|
import { compileQuery } from "../src/query/compile.js";
|
|
import {
|
|
generateInterfaceClientBindings,
|
|
generatePackageQueryClientBindings,
|
|
} from "../src/execution-world/client-contracts.js";
|
|
import { compileQueryProgram } from "../src/execution-world/queries.js";
|
|
const source = { repository: "https://example.test/device-owner.git", commit: "a".repeat(40) };
|
|
const factory = compileCapabilityResourceSource(
|
|
`external atom Entry id "atom:entry";
|
|
interface EntryFactory id "interface:entry-factory" revision "interface:entry-factory@1" {
|
|
static operation create id "entry:create" : unit -> atom-ref<Entry> {
|
|
call id "entry:create:call" execution transaction;
|
|
}
|
|
}`,
|
|
{ source },
|
|
);
|
|
assert.ok(factory.ok && factory.resource.kind === "interface", JSON.stringify(factory.diagnostics));
|
|
const data = compileCapabilityResourceSource(
|
|
`interface Data id "interface:data" revision "interface:data@1" {
|
|
queryable value body id "body" : string { get id "body:get" execution snapshot; edit id "body:edit" execution transaction; }
|
|
queryable value notes id "notes" : string { get id "notes:get" execution snapshot; edit id "notes:edit" execution transaction; }
|
|
operation revise id "revise" : record {body:change<string>; notes:change<string>; reject:bool;} -> unit {call id "revise:call" execution transaction;}
|
|
queryable value label id "label" : string {
|
|
get id "label:get" execution snapshot;
|
|
set id "label:set" execution transaction;
|
|
}
|
|
}`,
|
|
{ source },
|
|
);
|
|
const constructors = compileCapabilityResourceSource(
|
|
`external atom Entry id "atom:entry"; external atom Independent id "atom:independent";
|
|
interface Factories id "interface:factories" revision "interface:factories@1" {
|
|
static operation createOne id "create-one" : string -> atom-ref<Entry> { call id "create-one:call" execution transaction; }
|
|
static operation createTwo id "create-two" : unit -> atom-ref<Independent> { call id "create-two:call" execution transaction; }
|
|
}`,
|
|
{ source },
|
|
);
|
|
assert.ok(data.ok && data.resource.kind === "interface", JSON.stringify(data.diagnostics));
|
|
assert.ok(constructors.ok && constructors.resource.kind === "interface", JSON.stringify(constructors.diagnostics));
|
|
const dataInterface = data.resource.revision,
|
|
factoriesInterface = constructors.resource.revision;
|
|
const view = compileCapabilityResourceSource(
|
|
'import interface Data; interface View id "interface:device-view" revision "interface:device-view@1" { module render id "member:device-view:render" contract "org.quixos.react.subject-only/1" subject interface Data; }',
|
|
{ source, environment: { interfaces: new Map([["Data", dataInterface]]), interfaceClosure: [dataInterface] } },
|
|
);
|
|
assert.ok(view.ok && view.resource.kind === "interface", JSON.stringify(view.diagnostics));
|
|
const viewInterface = view.resource.revision;
|
|
const ui = compileCapabilityResourceSource(
|
|
'import interface Data; import interface View; package UI id "package:ui" revision "package:ui@1" { module Card id "component:card" implements View.render; }',
|
|
{
|
|
source,
|
|
environment: {
|
|
interfaces: new Map([
|
|
["Data", dataInterface],
|
|
["View", viewInterface],
|
|
]),
|
|
interfaceClosure: [dataInterface, viewInterface],
|
|
},
|
|
},
|
|
);
|
|
assert.ok(ui.ok && ui.resource.kind === "package", JSON.stringify(ui.diagnostics));
|
|
for (const variant of ["base", "added", "gateway", "gateway-next", "session"]) {
|
|
const session = variant === "session",
|
|
gateway = session || variant.startsWith("gateway"),
|
|
commit = { base: "a", added: "b", gateway: "c", "gateway-next": "d", session: "e" }[variant]!.repeat(40);
|
|
const pkg = compileCapabilityResourceSource(
|
|
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
|
|
${gateway ? 'external atom Entry id "atom:entry";' : ""}
|
|
${session ? 'external atom Independent id "atom:independent"; import interface Data; import interface View;' : ""}
|
|
package Owner id "package:owner" revision "package:owner@1" {
|
|
function identity id "identity" : int32 -> record { pid: int32; child: int32; } execution remote;
|
|
${gateway ? 'function create id "create" : unit -> atom-ref<Entry> execution transaction targets [server] requires { creation entry id "entry:birth" : Entry input unit; };' : ""}
|
|
${
|
|
session
|
|
? `module Editor id "component:owner-editor" implements View.render;
|
|
function createOne id "create-one" : string -> atom-ref<Entry> execution transaction targets [JavaScript, server] requires { creation entry id "one:birth" : Entry input unit; };
|
|
function createTwo id "create-two" : unit -> atom-ref<Independent> execution transaction targets [JavaScript, server] requires { creation entry id "two:birth" : Independent input unit; };
|
|
operation revise id "revise" : record {body:change<string>; notes:change<string>; reject:bool;} -> unit mode call execution transaction targets [JavaScript, server] receiver any requires {
|
|
state body id "revise:body" : string [edit]; state notes id "revise:notes" : string [edit];
|
|
};
|
|
query Inputs id "inputs" root workspace Data document "inputs.graphql" operation "Inputs" { replica; }
|
|
query View id "view" root workspace Data document "view.graphql" operation "View" { local from Inputs; watch; }
|
|
query EditorView id "editor-view" root Data document "editor-view.graphql" operation "EditorView" { local from EditorInputs; watch; }
|
|
query EditorInputs id "editor-inputs" root Data document "editor.graphql" operation "Editor" { replica; }`
|
|
: ""
|
|
}
|
|
}`,
|
|
{
|
|
source,
|
|
...(session
|
|
? {
|
|
environment: {
|
|
interfaces: new Map([
|
|
["Data", dataInterface],
|
|
["View", viewInterface],
|
|
]),
|
|
interfaceClosure: [dataInterface, viewInterface],
|
|
},
|
|
}
|
|
: {}),
|
|
},
|
|
);
|
|
assert.ok(pkg.ok && pkg.resource.kind === "package", JSON.stringify(pkg.diagnostics));
|
|
if (session)
|
|
pkg.resource.revision.checkedQueries = await Promise.all(
|
|
pkg.resource.revision.queries!.map((query) =>
|
|
compileQuery(
|
|
query,
|
|
[dataInterface],
|
|
async (name) =>
|
|
({
|
|
"inputs.graphql":
|
|
"query Inputs($label: String!) { root { objects(all: 100, where: {label: {eq: $label}}) { entries { key node { _camino {ref} label } } } } }",
|
|
"view.graphql":
|
|
"query View { root { availableObjects(all: 100) { entries { key node { _camino {ref} label } } } } }",
|
|
"editor.graphql": "query Editor { root { _camino {ref} label body notes } }",
|
|
"editor-view.graphql": "query EditorView { root { label body notes } }",
|
|
})[name]!,
|
|
),
|
|
),
|
|
);
|
|
const result = compileCapabilitySource(
|
|
`workspace Managed id "workspace:managed-process" revision "workspace:managed-process@${variant}" commit "${commit}" {
|
|
import package Owner;
|
|
${session ? "import package UI;" : ""}
|
|
${
|
|
session
|
|
? `import interface Data; import interface View; import interface Factories;
|
|
conform Entry as Factories id "owner:factories" { bind createOne.call to package Owner.createOne with { entry to creation Entry with {}; }; bind createTwo.call to package Owner.createTwo with { entry to creation Independent with {}; }; }
|
|
${["Entry", "Independent"].map((atom) => `conform ${atom} as Data id "owner:${atom}:data" { private state ${atom}Label id "label:${atom}" on ${atom} : string policy register default "${atom}"; bind label.get to state ${atom}Label.read; bind label.set to state ${atom}Label.write; private state ${atom}Body id "body:${atom}" on ${atom} : string policy crdt(string) default "hello 🌍"; private state ${atom}Notes id "notes:${atom}" on ${atom} : string policy crdt(string) default "notes"; bind body.get to state ${atom}Body.read; bind body.edit to state ${atom}Body.edit; bind notes.get to state ${atom}Notes.read; bind notes.edit to state ${atom}Notes.edit; bind revise.call to package Owner.revise with {body to state ${atom}Body; notes to state ${atom}Notes;}; }`).join("\n")}
|
|
`
|
|
: ""
|
|
}
|
|
${variant !== "base" ? 'atom Independent id "atom:independent";' : ""}
|
|
${
|
|
gateway
|
|
? `import interface EntryFactory; atom Entry id "atom:entry";
|
|
conform Entry as EntryFactory id "owner:entry-factory" {
|
|
bind create.call to package Owner.create with { entry to creation Entry with {}; };
|
|
}`
|
|
: ""
|
|
}
|
|
}`,
|
|
"workspace.qx",
|
|
{
|
|
packages: new Map([
|
|
["Owner", pkg.resource.revision],
|
|
...(session ? [["UI", ui.resource.revision] as const] : []),
|
|
]),
|
|
packageClosure: [pkg.resource.revision, ...(session ? [ui.resource.revision] : [])],
|
|
...(gateway
|
|
? {
|
|
interfaces: new Map([
|
|
["EntryFactory", factory.resource.revision],
|
|
...(session
|
|
? ([
|
|
["Data", dataInterface],
|
|
["View", viewInterface],
|
|
["Factories", factoriesInterface],
|
|
] as const)
|
|
: []),
|
|
]),
|
|
interfaceClosure: [
|
|
factory.resource.revision,
|
|
...(session ? [dataInterface, viewInterface, factoriesInterface] : []),
|
|
],
|
|
}
|
|
: {}),
|
|
},
|
|
);
|
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
|
const plan = compileExecutionPlan(result.workspace),
|
|
root = `dist/managed/${variant}`;
|
|
await mkdir(root, { recursive: true });
|
|
if (session) {
|
|
await writeFile(
|
|
`${root}/client.ts`,
|
|
generateInterfaceClientBindings({ Data: dataInterface, Factories: factoriesInterface }),
|
|
);
|
|
await writeFile(`${root}/queries.ts`, generatePackageQueryClientBindings(pkg.resource.revision));
|
|
}
|
|
for (const [file, value] of Object.entries({
|
|
"candidate.json": result.workspace,
|
|
"entry.json": gateway
|
|
? { atom: "atom:entry", interface: "interface:entry-factory@1", operation: "entry:create:call" }
|
|
: null,
|
|
"execution-plan.json": plan,
|
|
"kernel.json": compileKernelProgram(plan),
|
|
"queries.json": compileQueryProgram(plan),
|
|
"source.json": { kind: "workspace", repository: "https://example.test/managed.git", commit },
|
|
}))
|
|
await writeFile(`${root}/${file}`, JSON.stringify(value));
|
|
}
|
|
|
|
await import("./emit-controllers.js");
|
|
|
|
// Standalone public builder roots must work without opting into the other modes.
|
|
for (const kind of ["portable", "service"]) {
|
|
const pkg = compileCapabilityResourceSource(
|
|
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
|
|
package Owner id "package:owner" revision "package:owner@1" {
|
|
function identity id "identity" : int32 -> record {pid:int32;child:int32;}
|
|
execution ${kind === "portable" ? "snapshot targets [JavaScript,server]" : "remote"};
|
|
}`,
|
|
{ source },
|
|
);
|
|
assert.ok(pkg.ok && pkg.resource.kind === "package", JSON.stringify(pkg.diagnostics));
|
|
const workspace = compileCapabilitySource(
|
|
`workspace Simple id "workspace:simple" revision "workspace:simple@1" commit "${source.commit}" {import package Owner;}`,
|
|
"workspace.qx",
|
|
{ packages: new Map([["Owner", pkg.resource.revision]]) },
|
|
);
|
|
assert.ok(workspace.ok, JSON.stringify(workspace.diagnostics));
|
|
const directory = `dist/managed/standalone-${kind}`;
|
|
await mkdir(directory, { recursive: true });
|
|
await writeFile(`${directory}/execution-plan.json`, JSON.stringify(compileExecutionPlan(workspace.workspace)));
|
|
}
|