Add interface-oriented client sessions and headless capability access (WI-07)
This commit is contained in:
+88
-6
@@ -4,6 +4,11 @@ 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(
|
||||
@@ -16,21 +21,83 @@ interface EntryFactory id "interface:entry-factory" revision "interface:entry-fa
|
||||
{ source },
|
||||
);
|
||||
assert.ok(factory.ok && factory.resource.kind === "interface", JSON.stringify(factory.diagnostics));
|
||||
for (const variant of ["base", "added", "gateway", "gateway-next"]) {
|
||||
const gateway = variant.startsWith("gateway"),
|
||||
commit = { base: "a", added: "b", gateway: "c", "gateway-next": "d" }[variant]!.repeat(40);
|
||||
const data = compileCapabilityResourceSource(
|
||||
`interface Data id "interface:data" revision "interface:data@1" {
|
||||
queryable value label id "label" : string {
|
||||
get id "label:get" execution snapshot targets [browser, server];
|
||||
set id "label:set" execution transaction targets [browser, server];
|
||||
}
|
||||
}`,
|
||||
{ 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 targets [browser, server]; }
|
||||
static operation createTwo id "create-two" : unit -> atom-ref<Independent> { call id "create-two:call" execution transaction targets [browser, server]; }
|
||||
}`,
|
||||
{ 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;
|
||||
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(
|
||||
`${gateway ? 'external atom Entry id "atom:entry";' : ""}
|
||||
${session ? 'external atom Independent id "atom:independent"; import interface Data;' : ""}
|
||||
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
|
||||
? `function createOne id "create-one" : string -> atom-ref<Entry> execution transaction targets [browser, server] requires { creation entry id "one:birth" : Entry input unit; };
|
||||
function createTwo id "create-two" : unit -> atom-ref<Independent> execution transaction targets [browser, server] requires { creation entry id "two:birth" : Independent input unit; };
|
||||
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 EditorInputs id "editor-inputs" root Data document "editor.graphql" operation "Editor" { replica; }`
|
||||
: ""
|
||||
}
|
||||
}`,
|
||||
{ source },
|
||||
{
|
||||
source,
|
||||
...(session
|
||||
? { environment: { interfaces: new Map([["Data", dataInterface]]), interfaceClosure: [dataInterface] } }
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
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 { _qx {ref} label } } } } }",
|
||||
"view.graphql":
|
||||
"query View { root { availableObjects(all: 100) { entries { key node { _qx {ref} label } } } } }",
|
||||
"editor.graphql": "query Editor { root { _qx {ref} label } }",
|
||||
})[name]!,
|
||||
),
|
||||
),
|
||||
);
|
||||
const result = compileCapabilitySource(
|
||||
`workspace Managed id "workspace:managed-process" revision "workspace:managed-process@${variant}" commit "${commit}" {
|
||||
import package Owner;
|
||||
${
|
||||
session
|
||||
? `import interface Data; 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; }`).join("\n")}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
${variant !== "base" ? 'atom Independent id "atom:independent";' : ""}
|
||||
${
|
||||
gateway
|
||||
@@ -47,8 +114,16 @@ package Owner id "package:owner" revision "package:owner@1" {
|
||||
packageClosure: [pkg.resource.revision],
|
||||
...(gateway
|
||||
? {
|
||||
interfaces: new Map([["EntryFactory", factory.resource.revision]]),
|
||||
interfaceClosure: [factory.resource.revision],
|
||||
interfaces: new Map([
|
||||
["EntryFactory", factory.resource.revision],
|
||||
...(session
|
||||
? ([
|
||||
["Data", dataInterface],
|
||||
["Factories", factoriesInterface],
|
||||
] as const)
|
||||
: []),
|
||||
]),
|
||||
interfaceClosure: [factory.resource.revision, ...(session ? [dataInterface, factoriesInterface] : [])],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
@@ -57,6 +132,13 @@ package Owner id "package:owner" revision "package:owner@1" {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user