import assert from "node:assert/strict"; import test from "node:test"; import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/index.js"; import { compileWorkspaceRevision, resolveOperationPlan } from "../src/capability-model/validation.js"; import { capabilityId, type InterfaceRevision } from "../src/capability-model/types.js"; import { compileQuery } from "../src/query/compile.js"; import { linkQueries } from "../src/query/link.js"; import { compileCapabilityFixture, fixtureId } from "./fixtures/capability-model.js"; const source = { repository: "https://example.test/forwarding.git", commit: "a".repeat(40) }; function fixture(transform = (s: string) => s, generic = false) { const interfaces = new Map(); for (const name of ["Owner", "Alias", "Relay"]) { const result = compileCapabilityResourceSource( `interface ${name}${generic && name === "Owner" ? "" : ""} id "${name}" revision "${name}@1" { queryable value title id "${name}:title" : ${generic && name === "Owner" ? "T" : "string"} { get id "${name}:get"; set id "${name}:set"; watch start id "${name}:start" stop id "${name}:stop"; } }`, { source }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); if (result.resource.kind !== "interface") throw new Error("interface expected"); interfaces.set(name, result.resource.revision); } const forward = (name: string, target: string) => `conform Item as ${name} id "${name}-conformance" { bind title.get to interface ${target}.title.get; bind title.set to interface ${target}.title.set; bind title.watch-start to interface ${target}.title.watch-start; bind title.watch-stop to interface ${target}.title.watch-stop; }`; const text = `workspace Test id "ws" revision "ws@1" commit "${source.commit}" { import interface Owner; import interface Alias; import interface Relay; atom Item id "item"; ${forward("Relay", "Alias")} ${forward("Alias", generic ? "Owner" : "Owner")} conform Item as Owner${generic ? "" : ""} id "owner-conformance" { private state Title id "title" on Item : string policy register default ""; bind title to state Title; } }`; return { result: compileCapabilitySource(transform(text), "workspace.qx", { interfaces }), interfaces }; } test("bind chains forward native reads, writes and watches without changing storage ownership", async () => { const { result, interfaces } = fixture(); assert.ok(result.ok, JSON.stringify(result.diagnostics)); const compiled = compileWorkspaceRevision(result.workspace); assert.ok(compiled.ok); for (const [operation, primitive] of [ ["get", "read"], ["set", "write"], ["start", "watch-start"], ["stop", "watch-stop"], ]) { const resolved = resolveOperationPlan( compiled.plan, capabilityId.atom("item"), capabilityId.interfaceRevision("Relay@1"), capabilityId.operation(`Relay:${operation}`), ); assert.equal(resolved?.binding.kind, "state"); if (resolved?.kind !== "state") throw new Error("state expected"); assert.equal(resolved.binding.primitive, primitive); assert.deepEqual(resolved.attachment.owner, { kind: "conformance", atomId: "item", interfaceRevisionId: "Owner@1", }); } const pkg = compileCapabilityResourceSource( `import interface Relay; package Views id "views" revision "views@1" { query Rows id "rows" root workspace Relay document "rows.graphql" operation "Rows" {max rows 10; watch;} }`, { source, environment: { interfaces } }, ); assert.ok(pkg.ok, JSON.stringify(pkg.diagnostics)); if (pkg.resource.kind !== "package") throw new Error("package expected"); const revision = pkg.resource.revision; revision.checkedQueries = [ await compileQuery( revision.queries![0]!, [...interfaces.values()], async () => `query Rows {root {objects(first: 10) {entries {node {title @live}}}}}`, ), ]; result.workspace.packageImports.push(revision); const linked = linkQueries(result.workspace)[0]!; assert.equal(linked.fields[0]!.binding.kind, "state"); assert.equal(linked.attachments[0]!.id, "title"); }); test("forwarding rejects cycles, wrong signatures, missing targets, and direct private access", () => { for (const transform of [ (s: string) => s.replaceAll("interface Owner.title", "interface Relay.title"), (s: string) => s.replace("interface Owner.title.get", "interface Owner.title.set"), (s: string) => s .replace("conform Item as Owner", "conform Other as Owner") .replace('atom Item id "item";', 'atom Item id "item"; atom Other id "other";') .replace("on Item :", "on Other :"), (s: string) => s.replace("interface Owner.title.get", "state Title.read"), (s: string) => s.replace("interface Owner.title.watch-stop", "interface Relay.title.watch-stop"), ]) assert.equal(fixture(transform).result.ok, false); }); test("forwarding closes generic targets and rejects incompatible applications", () => { const good = fixture(undefined, true).result; assert.ok(good.ok, JSON.stringify(good.diagnostics)); const bad = fixture( (s) => s.replace("interface Owner.title.get", "interface Owner.title.get"), true, ).result; assert.equal(bad.ok, false); }); test("forwarded package actions retain the provider and its dependency ports", () => { const fixture = compileCapabilityFixture(); assert.ok(fixture.ok, JSON.stringify(fixture.diagnostics)); const ws = fixture.workspace; const target = ws.interfaceImports.find((i) => i.revisionId === fixtureId.summaryV1)!; const alias = { ...structuredClone(target), interfaceId: capabilityId.interface("alias"), revisionId: capabilityId.interfaceRevision("alias@1"), }; ws.interfaceImports.push(alias); ws.conformances.push({ atomId: fixtureId.project, interfaceRevisionId: alias.revisionId, id: capabilityId.conformance("alias-owner"), privateAttachments: [], relationshipMaterializations: [], operationBindings: [ { operationId: fixtureId.summaryGet, binding: { kind: "interface", interfaceRevisionId: fixtureId.summaryV1, operationId: fixtureId.summaryGet }, }, ], }); const result = compileWorkspaceRevision(ws); assert.ok(result.ok, JSON.stringify(result)); const plan = resolveOperationPlan(result.plan, fixtureId.project, alias.revisionId, fixtureId.summaryGet); assert.equal(plan?.kind, "package"); if (plan?.kind !== "package") throw new Error("package expected"); assert.equal(plan.binding.exportId, fixtureId.summaryGetExport); assert.ok(plan.dependencies.length > 0); });