import assert from "node:assert/strict"; import test from "node:test"; import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/parser.js"; import { generateTypeScriptBindings } from "../src/bindings/index.js"; import { generateClientContracts } from "../src/bindings/client.js"; import { TypeSubstitution, appliedInterfaceId, bindTypeParameters, canonicalTypeArgument, instantiateInterface, capabilityId, isStorableType, valueType, computeCapabilityClosure, compileWorkspaceRevision, type ClosedTypeArgument, type GenericTypeEnvironment, type ValueTypeExpression, } from "../src/capability-model/index.js"; test("unused generic definitions prove symbolic bounds and storable aliases", () => { const source = { repository: "https://example.test/bounds.git", commit: "a".repeat(40) }; const interfaces = new Map(); for (const text of [ 'interface Named id "named" revision "named@1" {}', 'import interface Named; interface Detailed id "detailed" revision "detailed@1" requires Named {}', 'import interface Named; interface Requires id "requires" revision "requires@1" {}', 'interface Stored id "stored" revision "stored@1" {}', ]) { const result = compileCapabilityResourceSource(text, { source, environment: { interfaces } }); assert.ok(result.ok, JSON.stringify(result.diagnostics)); assert.equal(result.resource.kind, "interface"); interfaces.set(result.resource.revision.displayName, result.resource.revision); } const compile = (body: string) => compileCapabilityResourceSource(body, { source, environment: { interfaces } }); const bad = compile( 'import interface Requires; interface Bad id "bad" revision "bad@1" requires Requires {}', ); assert.equal(bad.ok, false); assert.match(JSON.stringify(bad.diagnostics), /does not prove/); const good = compile( 'import interface Detailed; import interface Requires; interface Good id "good" revision "good@1" requires Requires {}', ); assert.ok(good.ok, JSON.stringify(good.diagnostics)); const alias = compile( 'import interface Stored; type Values = list>; interface Good id "good" revision "good@1" requires Stored> {}', ); assert.ok(alias.ok, JSON.stringify(alias.diagnostics)); const nonstorable = compile( 'import interface Stored; interface Bad id "bad" revision "bad@1" requires Stored {}', ); assert.equal(nonstorable.ok, false); }); const note = capabilityId.atom("atom:note"); test("Self remains contextual through local aliases", () => { const result = compileCapabilityResourceSource( 'type Me = ref; type Mine = optional; interface Identity id "identity" revision "identity@1" {value mine id "mine" : Mine {get id "mine:get";}}', { source: { repository: "https://example.test/self.git", commit: "a".repeat(40) } }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); if (result.resource.kind !== "interface") throw new Error("expected interface"); assert.equal(result.resource.revision.template?.usesSelf, true); const closed = instantiateInterface(result.resource.revision, [], { ...environment(), self: note }); assert.deepEqual(closed.members[0].operations[0].outputType, valueType.optional(valueType.atomRef(note))); }); const named = capabilityId.interfaceRevision("interface:named@1"); const environment = (arguments_: [string, ClosedTypeArgument][] = []): GenericTypeEnvironment => ({ arguments: new Map(arguments_), applyInterface: (id, args) => { assert.equal(args.length, 0); return id; }, implementsInterface: (target, required) => target.kind === "atom" && target.atomId === note && required === named, }); test("substitution reaches nested records, lists, optionals and object references", () => { const substitute = new TypeSubstitution( environment([ ["scope/value", { kind: "value", type: valueType.int64 }], ["scope/object", { kind: "object", target: { kind: "atom", atomId: note } }], ]), ); const result = substitute.value({ kind: "record", fields: { values: { kind: "list", value: { kind: "optional", value: { kind: "parameter", parameterId: "scope/value" } } }, target: { kind: "object-ref", expectation: { kind: "parameter", parameterId: "scope/object" } }, }, }); assert.deepEqual(result, { kind: "record", fields: { values: valueType.list(valueType.optional(valueType.int64)), target: valueType.atomRef(note), }, }); assert.equal(isStorableType(result), false); }); test("Self is the exact implementing atom and cannot be unbound", () => { const expression = { kind: "object-ref", expectation: { kind: "self" } } as const; assert.throws(() => new TypeSubstitution(environment()).value(expression), /Self requires an implementing atom/); assert.deepEqual(new TypeSubstitution({ ...environment(), self: note }).value(expression), valueType.atomRef(note)); }); test("parameter kinds and missing parameters fail closed", () => { const substitute = new TypeSubstitution( environment([["T", { kind: "object", target: { kind: "atom", atomId: note } }]]), ); assert.throws(() => substitute.value({ kind: "parameter", parameterId: "T" }), /use ref/); assert.throws(() => substitute.value({ kind: "parameter", parameterId: "unknown" }), /Unbound parameter/); assert.throws( () => bindTypeParameters([{ id: "T", name: "T", kind: "value" }], [], environment()), /Expected 1 type arguments/, ); assert.throws( () => bindTypeParameters( [{ id: "T", name: "T", kind: "value" }], [{ kind: "object", target: { kind: "atom", atomId: note } }], environment(), ), /Expected value, received object/, ); }); test("bounds require evidence and storable constraints inspect nested values", () => { const parameters = [ { id: "T", name: "T", kind: "object", implements: [{ definitionId: named, arguments: [] }] }, ] as const; const mutableParameters = parameters.map((p) => ({ ...p, implements: p.implements.map((b) => ({ ...b, arguments: [] })), })); assert.equal( bindTypeParameters(mutableParameters, [{ kind: "object", target: { kind: "atom", atomId: note } }], environment()) .size, 1, ); assert.throws( () => bindTypeParameters( mutableParameters, [{ kind: "object", target: { kind: "atom", atomId: capabilityId.atom("person") } }], environment(), ), /does not implement/, ); assert.throws( () => bindTypeParameters( [{ kind: "value", id: "V", name: "V", storable: true }], [{ kind: "value", type: valueType.list(valueType.optional(valueType.atomRef(note))) }], environment(), ), /cannot contain managed references/, ); assert.equal(isStorableType(valueType.message("opaque")), false); assert.equal(isStorableType(valueType.watchHandle), false); assert.equal( isStorableType(valueType.message("checked"), (id) => id === "checked"), true, ); }); test("closed applications canonicalize records without erasing nominal identity or provenance", () => { const a: ClosedTypeArgument = { kind: "value", type: { kind: "record", fields: { b: valueType.int64, a: valueType.string } }, }; const b: ClosedTypeArgument = { kind: "value", type: { kind: "record", fields: { a: valueType.string, b: valueType.int64 } }, }; assert.equal(canonicalTypeArgument(a), canonicalTypeArgument(b)); const application = { definitionId: named, source: { repository: "https://example.test/named.git", commit: "a".repeat(40) }, arguments: [a], }; assert.equal(appliedInterfaceId(application), appliedInterfaceId({ ...application, arguments: [b] })); assert.notEqual(appliedInterfaceId(application), appliedInterfaceId({ ...application, self: note })); assert.notEqual( appliedInterfaceId(application), appliedInterfaceId({ ...application, source: { ...application.source, commit: "b".repeat(40) } }), ); assert.throws( () => canonicalTypeArgument({ kind: "value", type: { kind: "parameter", parameterId: "T" }, } as unknown as ClosedTypeArgument), /Expected a closed value type/, ); }); test("aliases use lexical parameter identities, preserve outer bindings and reject cycles", () => { const env = environment([["outer/T", { kind: "value", type: valueType.string }]]); env.aliases = new Map([ [ "Box", { id: "Box", parameters: [{ id: "box/T", name: "T", kind: "value" }], body: { kind: "list", value: { kind: "parameter", parameterId: "box/T" } }, }, ], ["Loop", { id: "Loop", parameters: [], body: { kind: "alias", definitionId: "Loop", arguments: [] } }], ]); const substitute = new TypeSubstitution(env); assert.deepEqual( substitute.value({ kind: "alias", definitionId: "Box", arguments: [{ kind: "value", type: { kind: "parameter", parameterId: "outer/T" } }], }), valueType.list(valueType.string), ); assert.deepEqual(substitute.value({ kind: "parameter", parameterId: "outer/T" }), valueType.string); assert.throws(() => substitute.value({ kind: "alias", definitionId: "Loop", arguments: [] }), /Loop -> Loop/); }); test("excessively deep types produce a bounded diagnostic", () => { let expression: ValueTypeExpression = valueType.string; for (let index = 0; index < 200; index++) expression = { kind: "list", value: expression }; assert.throws(() => new TypeSubstitution(environment()).value(expression), /depth or expansion budget/); }); const source = { repository: "https://example.test/contracts.git", commit: "a".repeat(40) }; const reader = () => { const result = compileCapabilityResourceSource( `interface Reader id "reader" revision "reader@1" { value values id "values" : list> { get id "values:get"; watch start id "values:watch" stop id "values:stop"; } }`, { source }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); assert.equal(result.resource.kind, "interface"); if (result.resource.kind !== "interface") throw new Error("expected interface"); return result.resource.revision; }; test("generic interface source retains its template and produces closed package/codegen contracts", () => { const definition = reader(); assert.equal(definition.template?.parameters[0].name, "V"); const result = compileCapabilityResourceSource( `import interface Reader; package Client id "client" revision "client@1" { function run id "run" : unit -> list> requires { interface reader id "reader-port" : Reader; }; }`, { source, environment: { interfaces: new Map([["Reader", definition]]) } }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); if (result.resource.kind !== "package") throw new Error("expected package"); assert.equal(result.resource.specializations?.length, 1); const closed = result.resource.specializations![0]; assert.deepEqual(closed.members[0].operations[0].outputType, valueType.list(valueType.optional(valueType.string))); assert.deepEqual(closed.members[0].operations[1].eventType, closed.members[0].operations[0].outputType); const generated = generateTypeScriptBindings( { format: "quixos-bindings", version: 1, interfaces: [closed], packages: [result.resource.revision] }, "client@1", ); assert.match(generated, /Array<\(string \| null\)>/); assert.ok(generated.includes(closed.revisionId)); }); test("generic conformances bind state against specialized signatures", () => { const result = compileCapabilitySource( `workspace Demo id "ws" revision "ws@1" commit "${source.commit}" { atom Document id "document"; import interface Reader; conform Document as Reader id "reader-conformance" { private state Values id "values-slot" on Document : list> policy optimistic-register; bind values.get to state Values.read; bind values.watch-start to state Values.watch-start; bind values.watch-stop to state Values.watch-stop; } }`, "workspace.qx", { interfaces: new Map([["Reader", reader()]]) }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); assert.equal(result.workspace.interfaceImports.length, 1); assert.equal(result.workspace.interfaceImports[0].template, undefined); assert.equal(result.workspace.conformances[0].interfaceRevisionId, result.workspace.interfaceImports[0].revisionId); }); test("source rejects wrong generic arity and an unapplied interface", () => { for (const input of [ "interface-ref", "interface-ref>", "interface-ref>", ]) { const result = compileCapabilityResourceSource( `import interface Reader; external atom Note id "note"; package P id "p" revision "p@1" { function f id "f" : ${input} -> unit; }`, { source, environment: { interfaces: new Map([["Reader", reader()]]) } }, ); assert.equal(result.ok, false, input); assert.match(result.diagnostics[0].code, /type-arity|parameter-kind/); } }); test("package receivers and injected dependencies select exact applications", () => { const definition = reader(); const summary = compileCapabilityResourceSource( `interface Summary id "summary" revision "summary@1" { value summary id "summary-value" : list> { get id "summary:get"; } }`, { source }, ); assert.ok(summary.ok); if (summary.resource.kind !== "interface") throw new Error("expected interface"); const pkg = compileCapabilityResourceSource( `import interface Reader; package P id "p" revision "p@1" { operation summarize id "summarize" : unit -> list> mode call receiver interfaces [Reader] requires { interface reader id "reader-port" : Reader; }; }`, { source, environment: { interfaces: new Map([["Reader", definition]]) } }, ); assert.ok(pkg.ok, JSON.stringify(pkg.diagnostics)); if (pkg.resource.kind !== "package") throw new Error("expected package"); const result = compileCapabilitySource( `workspace W id "w" revision "w@1" commit "${source.commit}" { import interface Reader; import interface Summary; import package P; atom Note id "note"; conform Note as Reader id "reader-conformance" { private state Values id "values" on Note : list> policy optimistic-register; bind values.get to state Values.read; bind values.watch-start to state Values.watch-start; bind values.watch-stop to state Values.watch-stop; } conform Note as Summary id "summary-conformance" { bind summary.get to package P.summarize with { reader to interface Reader; }; } }`, "workspace.qx", { interfaces: new Map([ ["Reader", definition], ["Summary", summary.resource.revision], ]), interfaceClosure: pkg.resource.specializations, packages: new Map([["P", pkg.resource.revision]]), }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); }); test("named generic value aliases elaborate through nested lists", () => { const result = compileCapabilityResourceSource( `type Page = record { items: list; next: optional; }; package P id "p" revision "p@1" { function f id "f" : unit -> Page; }`, { source }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); if (result.resource.kind !== "package") throw new Error("expected package"); assert.deepEqual(result.resource.revision.exports[0].outputType, { kind: "record", fields: { items: valueType.list(valueType.int64), next: valueType.optional(valueType.string) }, }); }); test("Self specializes to the implementing atom rather than an erased interface", () => { const definition = compileCapabilityResourceSource( `interface Identity id "identity" revision "identity@1" { operation identity id "identity-member" : ref -> ref { call id "identity-call"; } }`, { source }, ); assert.ok(definition.ok, JSON.stringify(definition.diagnostics)); if (definition.resource.kind !== "interface") throw new Error("expected interface"); assert.equal(definition.resource.revision.template?.usesSelf, true); const { revision } = definition.resource; const first = instantiateInterface(revision, [], { ...environment(), self: note }); const second = instantiateInterface(revision, [], { ...environment(), self: capabilityId.atom("other") }); assert.deepEqual(first.members[0].operations[0].outputType, valueType.atomRef(note)); assert.notEqual(first.revisionId, second.revisionId); }); test("package Self comes from an exact receiver, never a previous export", () => { const valid = compileCapabilityResourceSource( `type Owned = ref; external atom Note id "note"; package P id "p" revision "p@1" { operation identity id "identity" : ref -> Owned mode call receiver atom Note; }`, { source }, ); assert.ok(valid.ok, JSON.stringify(valid.diagnostics)); if (valid.resource.kind !== "package") throw new Error("expected package"); assert.deepEqual(valid.resource.revision.exports[0].outputType, valueType.atomRef(capabilityId.atom("note"))); const invalid = compileCapabilityResourceSource( `external atom Note id "note"; package P id "p" revision "p@1" { operation identity id "identity" : ref -> ref mode call receiver atom Note; function bad id "bad" : unit -> ref; }`, { source }, ); assert.equal(invalid.ok, false); assert.equal(invalid.diagnostics[0].code, "unbound-self"); }); test("host generation rejects unresolved definitions and operation-only dispatch ambiguity", () => { const definition = reader(); assert.throws(() => generateClientContracts([definition], {}), /closed interface/); const first = instantiateInterface(definition, [{ kind: "value", type: valueType.string }], environment()); const second = instantiateInterface(definition, [{ kind: "value", type: valueType.int32 }], environment()); assert.throws(() => generateClientContracts([first, second], {}), /ambiguous/); }); test("finite recursive generic interface references share the same closed application", () => { const definition = compileCapabilityResourceSource( `interface Node id "node" revision "node@1" { value next id "next" : optional>> { get id "next:get"; } }`, { source }, ); assert.ok(definition.ok, JSON.stringify(definition.diagnostics)); if (definition.resource.kind !== "interface") throw new Error("expected interface"); const result = compileCapabilityResourceSource( `import interface Node; package P id "p" revision "p@1" { function f id "f" : interface-ref> -> unit; } `, { source, environment: { interfaces: new Map([["Node", definition.resource.revision]]) } }, ); assert.ok(result.ok, JSON.stringify(result.diagnostics)); const [closed] = result.resource.specializations!; assert.equal(result.resource.specializations!.length, 1); assert.deepEqual( closed.members[0].operations[0].outputType, valueType.optional(valueType.interfaceRef(closed.revisionId)), ); }); test("expanding recursive generic interfaces fail with a bounded diagnostic", () => { const definition = compileCapabilityResourceSource( `interface Node id "node" revision "node@1" { value next id "next" : interface-ref>> { get id "next:get"; } }`, { source }, ); assert.ok(definition.ok, JSON.stringify(definition.diagnostics)); if (definition.resource.kind !== "interface") throw new Error("expected interface"); const result = compileCapabilityResourceSource( `import interface Node; package P id "p" revision "p@1" { function f id "f" : interface-ref> -> unit; } `, { source, environment: { interfaces: new Map([["Node", definition.resource.revision]]) } }, ); assert.equal(result.ok, false); assert.equal(result.diagnostics[0].code, "recursive-application"); }); test("storable parameters do not imply support for RPC-only records", () => { assert.equal(isStorableType({ kind: "record", fields: { name: valueType.string } }), false); assert.equal(isStorableType(valueType.list(valueType.optional(valueType.string))), true); }); test("generic declarations reject duplicate members before specialization", () => { const result = compileCapabilityResourceSource( `interface Bad id "bad" revision "bad@1" { value first id "duplicate" : V { get id "first:get"; } value second id "duplicate" : V { get id "second:get"; } }`, { source }, ); assert.equal(result.ok, false); assert.equal(result.diagnostics[0].code, "duplicate-interface-member"); }); test("unused templates still check nested application arity and alias cycles", () => { const malformed = compileCapabilityResourceSource( `import interface Reader; interface Bad id "bad" revision "bad@1" { value item id "item" : interface-ref> { get id "get"; } }`, { source, environment: { interfaces: new Map([["Reader", reader()]]) } }, ); assert.equal(malformed.ok, false); assert.equal(malformed.diagnostics[0].code, "type-arity"); const cycle = compileCapabilityResourceSource( `type Loop = list>; interface Bad id "bad" revision "bad@1" { value item id "item" : Loop { get id "get"; } }`, { source }, ); assert.equal(cycle.ok, false); assert.equal(cycle.diagnostics[0].code, "recursive-alias"); }); test("Self in a prerequisite contributes to the outer application identity", () => { const identity = compileCapabilityResourceSource( `interface Identity id "identity" revision "identity@1" { value self id "self" : ref { get id "self:get"; } }`, { source }, ); assert.ok(identity.ok); if (identity.resource.kind !== "interface") throw new Error("expected interface"); const outer = compileCapabilityResourceSource( `import interface Identity; interface Outer id "outer" revision "outer@1" requires Identity {}`, { source, environment: { interfaces: new Map([["Identity", identity.resource.revision]]) } }, ); assert.ok(outer.ok, JSON.stringify(outer.diagnostics)); if (outer.resource.kind !== "interface") throw new Error("expected interface"); assert.equal(outer.resource.revision.template?.usesSelf, true); }); test("object bounds are discharged against the complete candidate, not source order", () => { const namedResult = compileCapabilityResourceSource(`interface Named id "named" revision "named@1" {}`, { source }); assert.ok(namedResult.ok); if (namedResult.resource.kind !== "interface") throw new Error("expected interface"); const named = namedResult.resource.revision; const bounded = compileCapabilityResourceSource( `import interface Named; interface Container id "container" revision "container@1" {}`, { source, environment: { interfaces: new Map([["Named", named]]) } }, ); assert.ok(bounded.ok, JSON.stringify(bounded.diagnostics)); if (bounded.resource.kind !== "interface") throw new Error("expected interface"); const container = bounded.resource.revision; const compile = (evidence: string) => compileCapabilitySource( `workspace W id "w" revision "w@1" commit "${source.commit}" { import interface Named; import interface Container; atom Note id "note"; atom Index id "index"; conform Index as Container id "index-container" {} ${evidence} }`, "workspace.qx", { interfaces: new Map([ ["Named", named], ["Container", container], ]), }, ); const missing = compile(""); assert.equal(missing.ok, false); assert.ok(missing.diagnostics.some((issue) => issue.code === "unsatisfied-interface")); const valid = compile('conform Note as Named id "note-named" {}'); assert.ok(valid.ok, JSON.stringify(valid.diagnostics)); }); test("prerequisites require explicit conformances and remain in the capability closure", () => { const baseResult = compileCapabilityResourceSource(`interface Base id "base" revision "base@1" {}`, { source }); assert.ok(baseResult.ok); if (baseResult.resource.kind !== "interface") throw new Error("expected interface"); const base = baseResult.resource.revision; const derivedResult = compileCapabilityResourceSource( `import interface Base; interface Derived id "derived" revision "derived@1" requires Base {}`, { source, environment: { interfaces: new Map([["Base", base]]) }, }, ); assert.ok(derivedResult.ok, JSON.stringify(derivedResult.diagnostics)); if (derivedResult.resource.kind !== "interface") throw new Error("expected interface"); const derived = derivedResult.resource.revision; const compile = (evidence: string) => compileCapabilitySource( `workspace W id "w" revision "w@1" commit "${source.commit}" { import interface Base; import interface Derived; atom Note id "note"; conform Note as Derived id "derived-conformance" {} ${evidence} }`, "workspace.qx", { interfaces: new Map([ ["Base", base], ["Derived", derived], ]), }, ); assert.equal(compile("").ok, false); const result = compile('conform Note as Base id "base-conformance" {}'); assert.ok(result.ok, JSON.stringify(result.diagnostics)); const root = result.workspace.conformances[0]; const closure = computeCapabilityClosure(result.plan, [root]); assert.equal(closure.conformances.length, 2); const unresolved = structuredClone(result.workspace); unresolved.interfaceImports[0].members.push({ kind: "value", id: capabilityId.member("unresolved"), displayName: "unresolved", operations: [], valueType: { kind: "parameter", parameterId: "T" } as unknown as typeof valueType.string, }); assert.equal(compileWorkspaceRevision(unresolved).ok, false); const forged = structuredClone(result.workspace); const applied = forged.interfaceImports.find((entry) => entry.application)!; applied.application!.arguments = [{ kind: "value", type: valueType.int64 }]; assert.equal(compileWorkspaceRevision(forged).ok, false); });