Make local graph execution optional across composed packages

This commit is contained in:
Timothy J. Aveni
2026-09-28 21:05:42 -07:00
parent 1e72713ae2
commit e9c15fe40f
87 changed files with 3703 additions and 3400 deletions
+4 -3
View File
@@ -2,12 +2,13 @@ import assert from "node:assert/strict";
import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/parser.js";
const contract = `interface Limit id "limit" revision "limit@1" {
operation change id "change" : int32 -> unit {
call id "change:call" execution transaction targets [browser, server];
call id "change:call" execution transaction;
}
}`;
export const runtime = `external atom Item id "item";
export const runtime = `symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom Item id "item";
package Runtime id "runtime" revision "runtime@1" {
operation change id "runtime:change" : int32 -> unit mode call execution transaction targets [browser, server]
operation change id "runtime:change" : int32 -> unit mode call execution transaction targets [JavaScript, server]
receiver atom Item requires { state level id "write:value" : int32 [write]; } accept using check;
operation check id "runtime:check" : int32 -> unit mode call execution snapshot targets [server]
receiver atom Item requires {
+4 -4
View File
@@ -28,9 +28,9 @@ test("acceptance syntax retains separate final-state ports through formatting, p
]);
assert.equal(kernel.packages[0]!.exports.find((e) => e.id === "runtime:change")!.acceptance, "runtime:check");
assert.ok(
!generatePortableTypeScriptBindings(plan.definition.bindingSchema, "runtime@1", { target: "browser" }).includes(
'"runtime:check"',
),
!generatePortableTypeScriptBindings(plan.definition.bindingSchema, "runtime@1", {
target: "target:org.quixos.execution.javascript@1",
}).includes('"runtime:check"'),
);
assert.ok(
generatePortableTypeScriptBindings(plan.definition.bindingSchema, "runtime@1", { target: "server" }).includes(
@@ -43,7 +43,7 @@ test("acceptance syntax retains separate final-state ports through formatting, p
test("acceptance declarations reject wrong effects/signatures and missing or mutable ports", () => {
for (const [pkg, ws] of [
[runtime.replace("snapshot targets [server]", "transaction targets [server]"), workspace],
[runtime.replace("snapshot targets [server]", "snapshot targets [browser, server]"), workspace],
[runtime.replace("snapshot targets [server]", "snapshot targets [JavaScript, server]"), workspace],
[
runtime.replace('check id "runtime:check" : int32 -> unit', 'check id "runtime:check" : string -> unit'),
workspace,
+3 -1
View File
@@ -46,7 +46,9 @@ test("gateway serves only declared browser assets with correct Wasm MIME and con
const plan = compileExecutionPlan(await loadReaderWorkspace()),
fixture = buildArtifactFixture(plan);
const wasm = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0]);
const browser = fixture.manifest.implementations.find((i) => i.target === "browser")!;
const browser = fixture.manifest.implementations.find(
(i) => i.target === "target:org.quixos.execution.javascript@1",
)!;
browser.files.push({
path: "portable/browser/assets/café.wasm",
bytes: wasm.length,
+2 -2
View File
@@ -13,7 +13,7 @@ export const buildArtifactFixture = (plan: ExecutionPlan, revision = plan.defini
target: string,
compatibility: ArtifactImplementation["compatibility"],
): ArtifactImplementation => {
const path = compatibility === "quixos-service-v1" ? "bin/service" : `portable/${target}/module.mjs`;
const path = compatibility === "quixos-service-v1" ? "bin/service" : `portable/${id}/module.mjs`;
const value = {
id,
target,
@@ -44,7 +44,7 @@ export const buildArtifactFixture = (plan: ExecutionPlan, revision = plan.defini
implementations: [
implementation("service", "server", "quixos-service-v1"),
implementation("server", "server", "quixos-js-module-v1"),
implementation("browser", "browser", "quixos-js-module-v1"),
implementation("browser", "target:org.quixos.execution.javascript@1", "quixos-js-module-v1"),
],
};
return { manifest, bytes, read: async () => bytes };
+1 -1
View File
@@ -229,7 +229,7 @@ test("resource-owned fragment imports compile through the locked package closure
await writeFile(path.join(dirs.item!, "quixos.lock"), lock());
await writeFile(
path.join(dirs.item!, "interface.qx"),
`interface Item id "item" revision "item@1" {queryable value title id "title" : string {get id "title:get" execution snapshot targets [browser,server];}}`,
`interface Item id "item" revision "item@1" {queryable value title id "title" : string {get id "title:get" execution snapshot;}}`,
);
await writeFile(path.join(dirs.rows!, "quixos.lock"), lock(dependency("interface", "Item")));
await writeFile(
+9 -8
View File
@@ -14,7 +14,7 @@ import { parseControllerCatalog, validateControllers } from "../src/capability-m
const source = { repository: "https://example.test/contracts.git", commit: "a".repeat(40) };
const parsed = compileCapabilityResourceSource(
`interface Data id "data" revision "data@1" {
queryable portable value label id "label" : string { get id "label:get" execution snapshot targets [browser, server]; }
queryable portable value label id "label" : string { get id "label:get" execution snapshot; }
operation publish id "publish" : string -> unit { call id "publish:call"; }
}`,
{ source },
@@ -26,20 +26,20 @@ test("native fields certify one storage slot; only browser writes get live descr
const parsed = compileCapabilityResourceSource(
`interface Fields id "fields" revision "fields@1" {
queryable field position id "position" : string {
get id "position:get" execution snapshot targets [browser, server];
set id "position:set" execution transaction targets [browser, server];
get id "position:get" execution snapshot;
set id "position:set" execution transaction;
}
field remote id "remote" : string {
get id "remote:get";
set id "remote:set";
}
value arbitrary id "arbitrary" : string {
get id "arbitrary:get" execution snapshot targets [browser,server];
set id "arbitrary:set" execution transaction targets [browser, server];
get id "arbitrary:get" execution snapshot;
set id "arbitrary:set" execution transaction;
}
value text id "text" : string {
get id "text:get";
edit id "text:edit" execution transaction targets [browser, server];
edit id "text:edit" execution transaction;
}
}`,
{ source },
@@ -92,8 +92,9 @@ test("native fields certify one storage slot; only browser writes get live descr
const goodForward = compileCapabilitySource(forwarded, "workspace.qx", env);
assert.ok(goodForward.ok, JSON.stringify(goodForward.diagnostics));
const pkg = compileCapabilityResourceSource(
`package Setter id "setter" revision "setter@1" {
operation replaceValue id "write" : string -> unit mode call execution transaction targets [browser,server] receiver any;
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
package Setter id "setter" revision "setter@1" {
operation replaceValue id "write" : string -> unit mode call execution transaction targets [JavaScript,server] receiver any;
}`,
{ source },
);
+1 -1
View File
@@ -52,7 +52,7 @@ test("checked component scaffolding composes with execution handlers without rep
await apply("function", {
name: "echo",
id: "echo",
declaration: 'function echo id "echo" : string -> string execution snapshot targets [browser,server];',
declaration: 'function echo id "echo" : string -> string execution snapshot targets [JavaScript,server];',
});
await apply("component", {
name: "Details",
+4 -3
View File
@@ -24,7 +24,7 @@ test("compound captured changes survive planning, transport lowering and typed e
]);
assert.deepEqual(paths.find((op) => op.operation === "compound:content:edit")!.inputChanges, [[]]);
const code = generatePortableTypeScriptBindings(plan.definition.bindingSchema, "compound:runtime@1", {
target: "browser",
target: "target:org.quixos.execution.javascript@1",
});
assert.ok(code.includes("PreparedChange<"));
assert.ok(code.includes('"edit"'));
@@ -33,8 +33,9 @@ test("compound captured changes survive planning, transport lowering and typed e
test("prepared changes require one storable materialized type", () => {
for (const shape of ["change<string, bool>", "change<atom N>", "change<atom-ref<N>>", "change<change<string>>"]) {
const result = compileCapabilityResourceSource(
`external atom N id "n"; package P id "p" revision "p@1" {
function f id "f" : ${shape} -> unit execution transaction targets [browser, server];
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom N id "n"; package P id "p" revision "p@1" {
function f id "f" : ${shape} -> unit execution transaction targets [JavaScript, server];
}`,
{ source },
);
+3 -3
View File
@@ -192,9 +192,9 @@ test("rule private edges substitute Self and captured endpoints with stable proj
test("rule creation ports close captured atom parameters and retain initialization checking", () => {
const compile = fixture([
'interface Factory<object T> id "factory" revision "factory@1" {operation create id "create" : unit -> ref<T> {call id "call" execution transaction targets [browser,server];}}',
'interface Factory<object T> id "factory" revision "factory@1" {operation create id "create" : unit -> ref<T> {call id "call" execution transaction;}}',
'interface Creates<object T> id "creates" revision "creates@1" {}',
'package Runtime id "runtime" revision "runtime@1" {operation create<object T> id "create" : unit -> ref<T> mode call execution transaction targets [browser,server] receiver any requires {creation result id "result" : T input unit;};}',
'symbol JavaScript id "target:org.quixos.execution.javascript@1";\npackage Runtime id "runtime" revision "runtime@1" {operation create<object T> id "create" : unit -> ref<T> mode call execution transaction targets [JavaScript,server] receiver any requires {creation result id "result" : T input unit;};}',
]);
const rule = `conform<object T> Creates<T> as Factory<T> id "factory-rule" {
bind create.call to package Runtime.create<T> with {result to creation T with {};};
@@ -324,7 +324,7 @@ test("rule inverse reads retain the concrete conformance ownership checks", () =
const compile = fixture([
'interface Parent id "parent" revision "parent@1" {}',
'interface Child id "child" revision "child@1" {}',
'import interface Parent; interface Owned id "owned" revision "owned@1" {queryable relation parent id "parent-ref" : optional-one interface Parent {resolve id "parent-read" execution snapshot targets [browser,server];}}',
'import interface Parent; interface Owned id "owned" revision "owned@1" {queryable relation parent id "parent-ref" : optional-one interface Parent {resolve id "parent-read" execution snapshot;}}',
]);
const source = `
atom P id "p"; atom C id "c";
+3 -3
View File
@@ -6,8 +6,8 @@ const content = "record { text:string; tags:list<string>; }";
const iface = compileCapabilityResourceSource(
`interface Comment id "comment" revision "comment@1" {
value content id "content" : ${content} {
get id "content:get" execution snapshot targets [browser, server];
edit id "content:edit" execution transaction targets [browser, server];
get id "content:get" execution snapshot;
edit id "content:edit" execution transaction;
}
}`,
{ source },
@@ -68,7 +68,7 @@ test("authored prepared changes retain their materialized type as transport valu
test("generic edit retains its materialized type through specialization", () => {
const generic = compileCapabilityResourceSource(
`interface Cell<value T : storable> id "cell" revision "cell@1" {
value content id "content" : T { get id "get"; edit id "edit" execution transaction targets [browser, server]; }
value content id "content" : T { get id "get"; edit id "edit" execution transaction; }
}`,
{ source },
);
+11 -9
View File
@@ -14,23 +14,24 @@ const iface = (text: string): InterfaceRevision => {
return result.resource.revision;
};
const data = iface(`interface Data id "data" revision "data@1" {
value title id "title" : string { get id "title:get" execution snapshot targets [browser, server]; }
value title id "title" : string { get id "title:get" execution snapshot; }
}`);
const tagged = iface(`interface Tagged id "tagged" revision "tagged@1" {
value tags id "tags" : list<string> { get id "tags:get" execution snapshot targets [browser, server]; }
value tags id "tags" : list<string> { get id "tags:get" execution snapshot; }
}`);
const factory = iface(`external atom Note id "note";
interface Factory id "factory" revision "factory@1" {
static operation create id "create" : ${seed} -> atom-ref<Note> { call id "create:call" execution transaction targets [browser, server]; }
static operation importNote id "import" : ${seed} -> atom-ref<Note> { call id "import:call" execution transaction targets [browser, server]; }
static operation create id "create" : ${seed} -> atom-ref<Note> { call id "create:call" execution transaction; }
static operation importNote id "import" : ${seed} -> atom-ref<Note> { call id "import:call" execution transaction; }
}`);
const runtime = compileCapabilityResourceSource(
`external atom Note id "note";
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom Note id "note";
package Runtime id "runtime" revision "runtime@1" {
function createNote id "create-note" : ${seed} -> atom-ref<Note> execution transaction targets [browser, server] requires {
function createNote id "create-note" : ${seed} -> atom-ref<Note> execution transaction targets [JavaScript, server] requires {
creation note id "new-note" : Note input ${seed};
};
function importNote id "import-note" : ${seed} -> atom-ref<Note> execution transaction targets [browser, server] requires {
function importNote id "import-note" : ${seed} -> atom-ref<Note> execution transaction targets [JavaScript, server] requires {
creation note id "new-note" : Note input ${seed};
};
}`,
@@ -137,9 +138,10 @@ test("defaults supply unmapped state and seeds cannot invoke an undeclared owner
test("creation ports cannot be used in snapshot execution", () => {
const result = compileCapabilityResourceSource(
`external atom Note id "note";
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom Note id "note";
package P id "p" revision "p@1" {
function createNote id "create" : unit -> atom-ref<Note> execution snapshot targets [browser, server] requires {
function createNote id "create" : unit -> atom-ref<Note> execution snapshot targets [JavaScript, server] requires {
creation note id "note" : Note input record { title:string; };
};
}`,
+16 -14
View File
@@ -15,7 +15,7 @@ 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 targets [server];
call id "entry:create:call" execution transaction;
}
}`,
{ source },
@@ -23,12 +23,12 @@ interface EntryFactory id "interface:entry-factory" revision "interface:entry-fa
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 targets [browser, server]; edit id "body:edit" execution transaction targets [browser, server]; }
queryable value notes id "notes" : string { get id "notes:get" execution snapshot targets [browser, server]; edit id "notes:edit" execution transaction targets [browser, server]; }
operation revise id "revise" : record {body:change<string>; notes:change<string>; reject:bool;} -> unit {call id "revise:call" execution transaction targets [browser, server];}
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 targets [browser, server];
set id "label:set" execution transaction targets [browser, server];
get id "label:get" execution snapshot;
set id "label:set" execution transaction;
}
}`,
{ source },
@@ -36,8 +36,8 @@ const data = compileCapabilityResourceSource(
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]; }
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 },
);
@@ -70,7 +70,8 @@ for (const variant of ["base", "added", "gateway", "gateway-next", "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";' : ""}
`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;
@@ -78,9 +79,9 @@ package Owner id "package:owner" revision "package:owner@1" {
${
session
? `module Editor id "component:owner-editor" implements View.render;
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; };
operation revise id "revise" : record {body:change<string>; notes:change<string>; reject:bool;} -> unit mode call execution transaction targets [browser, server] receiver any requires {
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; }
@@ -202,9 +203,10 @@ 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(
`package Owner id "package:owner" revision "package:owner@1" {
`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 [browser,server]" : "remote"};
execution ${kind === "portable" ? "snapshot targets [JavaScript,server]" : "remote"};
}`,
{ source },
);
+13 -5
View File
@@ -7,17 +7,21 @@ import { compileExecutionPlan } from "../src/execution-world/plan.js";
import { generatePortableTypeScriptBindings } from "../src/bindings/portable.js";
import { generatePortableClientBindings } from "../src/bindings/portable-client.js";
import { loadCompositeWorkspace } from "./composite-fixture.js";
import { javascriptTarget } from "../src/execution-world/native-backends.js";
const executionTarget = (lane: string) => (lane === "browser" ? javascriptTarget : lane);
const plan = compileExecutionPlan(await loadReaderWorkspace());
await mkdir("dist/portable-reader", { recursive: true });
for (const target of ["browser", "server"])
await writeFile(
`dist/portable-reader/${target}.ts`,
generatePortableTypeScriptBindings(plan.definition.bindingSchema, "package:reader-runtime@1", { target }),
generatePortableTypeScriptBindings(plan.definition.bindingSchema, "package:reader-runtime@1", {
target: executionTarget(target),
}),
);
await writeFile("dist/portable-reader/plan.json", JSON.stringify(plan));
await writeFile("dist/portable-reader/kernel.json", JSON.stringify(compileKernelProgram(plan)));
await writeFile("dist/portable-reader/queries.json", JSON.stringify(compileQueryProgram(plan)));
await writeFile("dist/portable-reader/client.ts", generatePortableClientBindings(plan, { target: "browser" }));
await writeFile("dist/portable-reader/client.ts", generatePortableClientBindings(plan, { target: javascriptTarget }));
const serviceWorkspace = structuredClone(plan.definition.workspace);
serviceWorkspace.atoms = [];
serviceWorkspace.conformances = [];
@@ -49,7 +53,9 @@ for (const target of ["browser", "server"])
] as const)
await writeFile(
`dist/portable-reader/composite/${name}-${target}.ts`,
generatePortableTypeScriptBindings(composite.definition.bindingSchema, revision, { target }),
generatePortableTypeScriptBindings(composite.definition.bindingSchema, revision, {
target: executionTarget(target),
}),
);
const { portableGenericPlan } = await import("./portable-generic-fixture.js");
@@ -59,7 +65,9 @@ await mkdir("dist/portable-reader/generic", { recursive: true });
for (const target of ["browser", "server"]) {
await writeFile(
`dist/portable-reader/generic/${target}.ts`,
generatePortableTypeScriptBindings(generic.definition.bindingSchema, genericPackage.revisionId, { target }),
generatePortableTypeScriptBindings(generic.definition.bindingSchema, genericPackage.revisionId, {
target: executionTarget(target),
}),
);
}
const stringEntry = genericPackage.exports.find((e) => e.inputType.kind === "scalar")!;
@@ -141,7 +149,7 @@ await writeFile(
await writeFile(
"dist/portable-reader/presentation-mount/browser.ts",
generatePortableTypeScriptBindings(presentationMount.definition.bindingSchema, "presentation-runtime@1", {
target: "browser",
target: javascriptTarget,
}),
);
+43 -19
View File
@@ -3,7 +3,7 @@ import { test } from "node:test";
import { compileCapabilityResourceSource } from "../src/capability-language/index.js";
const source = { repository: "https://example.test/execution.git", commit: "a".repeat(40) };
const data = (execution = "snapshot targets [browser, server]") =>
const data = (execution = "snapshot") =>
compileCapabilityResourceSource(
`
interface Data id "interface:data" revision "interface:data@1" {
@@ -14,13 +14,14 @@ interface Data id "interface:data" revision "interface:data@1" {
}`,
{ source },
);
const program = (selection: string, execution = "snapshot targets [browser, server]") => {
const program = (selection: string, execution = "snapshot targets [JavaScript, server]") => {
const contract = data();
assert.ok(contract.ok, JSON.stringify(contract.diagnostics));
assert.equal(contract.resource.kind, "interface");
if (contract.resource.kind !== "interface") throw new Error("Expected interface");
return compileCapabilityResourceSource(
`
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
import interface Data;
package Reader id "package:reader" revision "package:reader@1" {
function label id "export:label" : unit -> string execution ${execution} requires {
@@ -43,7 +44,10 @@ test("portable contracts preserve exact selected operations and targets", () =>
assert.equal(result.resource.kind, "package");
if (result.resource.kind !== "package") return;
const entry = result.resource.revision.exports[0]!;
assert.deepEqual(entry.execution, { kind: "snapshot", targets: ["browser", "server"] });
assert.deepEqual(entry.execution, {
kind: "snapshot",
targets: ["target:org.quixos.execution.javascript@1", "server"],
});
assert.deepEqual(entry.dependencyPorts[0]!.requirement, {
kind: "interface",
interfaceRevisionId: "interface:data@1",
@@ -53,10 +57,10 @@ test("portable contracts preserve exact selected operations and targets", () =>
test("portable dependency diagnostics reject remote defaults and broad acquisition", () => {
for (const [selection, execution, message] of [
["using [refresh.call]", "snapshot targets [browser]", "remote-only"],
["", "snapshot targets [browser]", "select"],
["using [label.get, label.get]", "snapshot targets [browser]", "duplicates"],
["using [label.set]", "snapshot targets [browser]", "Unknown"],
["using [refresh.call]", "snapshot targets [JavaScript, server]", "remote-only"],
["", "snapshot targets [JavaScript, server]", "select"],
["using [label.get, label.get]", "snapshot targets [JavaScript, server]", "duplicates"],
["using [label.set]", "snapshot targets [JavaScript, server]", "Unknown"],
]) {
const result = program(selection!, execution);
assert.equal(result.ok, false);
@@ -64,11 +68,26 @@ test("portable dependency diagnostics reject remote defaults and broad acquisiti
}
});
test("package code targets may exceed a dependency's public minimum", () => {
test("execution targets must resolve nominal declarations", () => {
const result = program("using [label.get]", "snapshot targets [arduino]");
assert.ok(result.ok, JSON.stringify(result.diagnostics));
// A workspace still has to provide the full installed closure before an
// operation can promise this target; package code alone is insufficient.
assert.equal(result.ok, false);
assert.match(JSON.stringify(result.diagnostics), /Unknown nominal execution target arduino/);
});
test("nominal targets are identified by symbols rather than platform spellings or interfaces", () => {
const compile = (alias: string, identity: string) =>
compileCapabilityResourceSource(
`symbol ${alias} id "${identity}"; package P id "p" revision "p@1" { function f id "f" : unit -> unit execution transaction targets [${alias},server]; }`,
{ source },
);
const device = compile("ESP32", "target:example.device@1");
const renamed = compile("MyController", "target:example.device@1");
assert.ok(device.ok && device.resource.kind === "package");
assert.ok(renamed.ok && renamed.resource.kind === "package");
assert.deepEqual(device.resource.revision.exports[0]!.execution, renamed.resource.revision.exports[0]!.execution);
assert.deepEqual(device.resource.externalInterfaces, [], "A target identity has no interface definition dependency");
assert.equal(compile("server", "target:example.device@1").ok, false);
assert.equal(compile("Alias", "server").ok, false);
});
test("portable query getters and target declarations are checked", () => {
@@ -80,9 +99,10 @@ test("portable query getters and target declarations are checked", () => {
test("snapshot exports reject mutable state ports but transactions admit them", () => {
const compile = (effect: string) =>
compileCapabilityResourceSource(
`
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
package Reader id "package:reader" revision "package:reader@1" {
function update id "export:update" : string -> unit execution ${effect} targets [browser, server] requires {
function update id "export:update" : string -> unit execution ${effect} targets [JavaScript, server] requires {
state storage id "port:value" : string [read, write];
};
}`,
@@ -137,7 +157,7 @@ package Reader id "package:reader" revision "package:reader@1" {
delete remote.members[0]!.operations[0]!.execution;
await assert.rejects(
compileQuery(local, [remote], async () => document("Presentation", "availableObjects")),
/browser snapshot getter/,
/graph snapshot getter/,
);
const { capabilityId } = await import("../src/capability-model/index.js");
const linked = linkQueries({
@@ -200,15 +220,16 @@ test("generic selected ports preserve operation identities and execution through
const { specializePackageExport, capabilityId } = await import("../src/capability-model/index.js");
const iface = compileCapabilityResourceSource(
`interface Cell<value T> id "cell" revision "cell@1" {
value content id "content" : T { get id "content:get" execution snapshot targets [browser, server]; }
value content id "content" : T { get id "content:get" execution snapshot; }
}`,
{ source },
);
assert.ok(iface.ok && iface.resource.kind === "interface", JSON.stringify(iface.diagnostics));
const pkg = compileCapabilityResourceSource(
`import interface Cell;
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
import interface Cell;
package P id "p" revision "p@1" {
function load<value T> id "read" : unit -> T execution snapshot targets [browser, server] requires {
function load<value T> id "read" : unit -> T execution snapshot targets [JavaScript, server] requires {
interface cell id "cell" : Cell<T> using [content.get];
};
}`,
@@ -236,7 +257,10 @@ test("generic selected ports preserve operation identities and execution through
implementsInterface: () => false,
},
);
assert.deepEqual(specialized.execution, { kind: "snapshot", targets: ["browser", "server"] });
assert.deepEqual(specialized.execution, {
kind: "snapshot",
targets: ["target:org.quixos.execution.javascript@1", "server"],
});
assert.deepEqual(specialized.dependencyPorts[0]!.requirement.kind, "interface");
const requirement = specialized.dependencyPorts[0]!.requirement;
if (requirement.kind === "interface")
+1 -1
View File
@@ -88,7 +88,7 @@ test("outbound provider changes invalidate consumers while caller changes preser
mode: "call",
inputType: { kind: "builtin", name: "unit" },
outputType: { kind: "scalar", name: "string" },
execution: { kind: "snapshot", targets: ["browser", "server"] },
execution: { kind: "snapshot", targets: ["target:org.quixos.execution.javascript@1", "server"] },
receiverRequirement: { kind: "exact-atom", atomId: id.atom("atom:reader:document") },
dependencyPorts: [
{
+1 -1
View File
@@ -24,7 +24,7 @@ export async function emitExecutionScaffold(root: string) {
tools: { quixos: source, protocol: source, helpers: source, sdk: source },
});
for (const [name, execution] of [
["echo", "snapshot targets [browser, server]"],
["echo", "snapshot targets [JavaScript, server]"],
["serverEcho", "snapshot targets [server]"],
["remoteEcho", "remote"],
]) {
+54 -15
View File
@@ -17,23 +17,63 @@ import {
import { buildArtifactFixture as buildFixture } from "./artifact-fixture.js";
import { compileKernelProgram } from "../src/execution-world/kernel.js";
import { loadCompositeWorkspace } from "./composite-fixture.js";
import { standardNativeBackends } from "../src/execution-world/native-backends.js";
test("optional targets close over calls into another package", async () => {
const workspace = await loadCompositeWorkspace();
const before = compileExecutionPlan(workspace).definition.operations.find(
(op) => op.operationId === "document:annotate:call",
)!;
assert.ok(before, "fixture exposes the composed add-highlight operation");
assert.equal(before.availableTargets.length, 2);
workspace.packageImports.find((pkg) => pkg.revisionId === "package:comment-runtime@1")!.exports[0]!.execution = {
kind: "transaction",
targets: ["server"],
};
const after = compileExecutionPlan(workspace).definition.operations.find((op) => op.key === before.key)!;
assert.deepEqual(after.availableTargets, ["server"]);
assert.deepEqual(after.execution, before.execution);
});
test("native support can be supplied for a nominal device target without platform syntax", async () => {
const workspace = await loadReaderWorkspace();
const target = "interface:example.esp32@1";
for (const pkg of workspace.packageImports)
for (const entry of pkg.exports) {
if (entry.execution && entry.execution.kind !== "remote") entry.execution.targets.push(target);
}
const plan = compileExecutionPlan(workspace, [
...standardNativeBackends,
{ target, supports: (binding) => binding.kind === "state" || binding.kind === "edge" },
]);
assert.ok(
plan.definition.operations
.filter((op) => op.binding.kind === "state")
.every((op) => op.availableTargets.includes(target)),
);
assert.ok(
plan.definition.operations.some((op) => op.binding.kind === "package" && op.availableTargets.includes(target)),
);
});
const resign = (implementation: ArtifactImplementation) => {
const { contentDigest, ...body } = implementation;
implementation.contentDigest = implementationDigest(body);
};
test("installed targets can exceed the public requirement without changing that contract", async () => {
const workspace = await loadReaderWorkspace((name, text) =>
name === "DocumentFactory.qx" ? text.replaceAll("targets [browser, server]", "targets [server]") : text,
);
test("installed targets are implementation properties; authoritative fallback preserves the interface", async () => {
const workspace = await loadReaderWorkspace();
const plan = compileExecutionPlan(workspace);
const factory = plan.definition.operations.find(
(op) => op.binding.kind === "package" && op.binding.exportId === "reader:create-document",
)!;
assert.deepEqual(factory.execution, { kind: "transaction", targets: ["server"] });
assert.deepEqual(factory.availableTargets, ["browser", "server"]);
assert.deepEqual(factory.availableTargets, ["server", "target:org.quixos.execution.javascript@1"]);
const kernel = compileKernelProgram(plan) as { operations: { key: string; targets: string[] }[] };
assert.deepEqual(kernel.operations.find((op) => op.key === factory.key)!.targets, ["browser", "server"]);
assert.deepEqual(kernel.operations.find((op) => op.key === factory.key)!.targets, [
"server",
"target:org.quixos.execution.javascript@1",
]);
const provider = workspace.packageImports
.flatMap((pkg) => pkg.exports)
@@ -102,8 +142,8 @@ test("artifact closure checks bytes, exact source/schema, ABI, exports and targe
(m.versions as unknown as Record<string, string>).value = "v2";
}, /Incompatible runtime codecs/);
await negative((m) => {
m.implementations = m.implementations.filter((i) => i.target !== "browser");
}, /Missing browser implementation/);
m.implementations = m.implementations.filter((i) => i.target !== "target:org.quixos.execution.javascript@1");
}, /Missing target:org.quixos.execution.javascript@1 implementation/);
await negative((m) => {
m.implementations = m.implementations.filter((i) => i.compatibility !== "quixos-service-v1");
}, /Missing service fallback/);
@@ -211,7 +251,7 @@ test("closed generic applications retain distinct identities and codec shapes",
const source = { repository: "https://example.test/generic.git", commit: "a".repeat(40) };
const definition = compileCapabilityResourceSource(
`interface Cell<value V : storable> id "cell" revision "cell@1" {
value content id "value" : V { get id "get" execution snapshot targets [browser, server]; }
value content id "value" : V { get id "get" execution snapshot; }
}`,
{ source },
);
@@ -269,11 +309,10 @@ test("query dependencies propagate target requirements through selected getters"
binding: { kind: "query", packageRevisionId: pkg.revisionId, queryId: query.id, scope: "workspace" },
});
compileExecutionPlan(workspace);
const field = workspace.interfaceImports
.find((i) => i.displayName === "HighlightData")!
.members.find((m) => m.displayName === "color")!;
field.operations.find((o) => o.displayName === "get")!.execution = { kind: "snapshot", targets: ["browser"] };
assert.throws(() => compileExecutionPlan(workspace), /MISSING_EXECUTION_TARGET.*test:selector-query.*server/);
// Package artifact support is checked over the composed graph; an interface
// declaration cannot manufacture an authoritative implementation.
entry.execution = { kind: "snapshot", targets: ["target:org.quixos.execution.javascript@1"] };
assert.throws(() => compileExecutionPlan(workspace), /MISSING_SERVER_IMPLEMENTATION/);
});
test("implementation selection is explicit, target checked and independent of manifest order", async () => {
@@ -281,7 +320,7 @@ test("implementation selection is explicit, target checked and independent of ma
const plan = compileExecutionPlan(await loadReaderWorkspace());
const { manifest, read } = buildFixture(plan);
const selection = {
target: "browser",
target: "target:org.quixos.execution.javascript@1",
compatibility: "quixos-js-module-v1" as const,
exports: [manifest.implementations[0]!.exports[0]!],
};
+6 -6
View File
@@ -1,17 +1,17 @@
type Summary = record { text: string; basedOn: string; };
interface DocumentData id "async:document-data" revision "async:document-data@1" {
queryable value content id "async:content" : string {
get id "async:content:get" execution snapshot targets [browser, server];
get id "async:content:get" execution snapshot;
}
queryable value summary id "async:summary" : Summary {
get id "async:summary:get" execution snapshot targets [browser, server];
set id "async:summary:set" execution transaction targets [browser, server];
get id "async:summary:get" execution snapshot;
set id "async:summary:set" execution transaction;
}
queryable value pendingJob id "async:pending-job" : optional<record { operationId: string; }> {
get id "async:pending-job:get" execution snapshot targets [server];
set id "async:pending-job:set" execution transaction targets [server];
get id "async:pending-job:get" execution snapshot;
set id "async:pending-job:set" execution transaction;
}
operation recordSummary id "async:record-summary" : record { operationId: string; summary: Summary; } -> unit {
call id "async:record-summary:call" execution transaction targets [server];
call id "async:record-summary:call" execution transaction;
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ type Entry = variant id "canvas:entry@1" {
};
interface CanvasContent id "canvas" revision "canvas@1" {
queryable value entries id "entries" : list<Entry> {
get id "entries:get" execution snapshot targets [browser,server];
edit id "entries:edit" execution transaction targets [browser,server];
get id "entries:get" execution snapshot;
edit id "entries:edit" execution transaction;
}
}
+4 -3
View File
@@ -1,17 +1,18 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
type Replacement = record { expected: list<string>; targets: list<atom-ref<Node>>; };
type Seed = record { label: string; };
external atom Node id "collection:node";
package CollectionRuntime id "collection:runtime" revision "collection:runtime@1" {
operation version id "collection:runtime:version" : unit -> list<string>
mode call execution snapshot targets [browser, server] receiver atom Node requires {
mode call execution snapshot targets [JavaScript, server] receiver atom Node requires {
edge items id "collection:version:items" : many atom Node [resolve];
};
operation replace id "collection:runtime:replace" : Replacement -> unit
mode call execution transaction targets [browser, server] receiver atom Node requires {
mode call execution transaction targets [JavaScript, server] receiver atom Node requires {
edge items id "collection:replace:items" : many atom Node [resolve, connect, disconnect];
};
operation ensure id "collection:runtime:ensure" : unit -> atom-ref<Node>
mode call execution transaction targets [browser, server] receiver atom Node requires {
mode call execution transaction targets [JavaScript, server] receiver atom Node requires {
edge detail id "collection:ensure:detail" : optional-one atom Node [resolve, connect];
creation node id "collection:ensure:node" : Node input Seed;
};
+8 -8
View File
@@ -2,25 +2,25 @@ type Replacement = record { expected: list<string>; targets: list<atom-ref<Node>
external atom Node id "collection:node";
interface NodeData id "collection:data" revision "collection:data@1" {
queryable portable value version id "collection:version" : list<string> {
get id "collection:version:get" execution snapshot targets [browser, server];
get id "collection:version:get" execution snapshot;
}
operation replace id "collection:replace" : Replacement -> unit {
call id "collection:replace:call" execution transaction targets [browser, server];
call id "collection:replace:call" execution transaction;
}
operation ensure id "collection:ensure" : unit -> atom-ref<Node> {
call id "collection:ensure:call" execution transaction targets [browser, server];
call id "collection:ensure:call" execution transaction;
}
queryable value label id "collection:label" : string {
get id "collection:label:get" execution snapshot targets [browser, server];
set id "collection:label:set" execution transaction targets [browser, server];
get id "collection:label:get" execution snapshot;
set id "collection:label:set" execution transaction;
}
queryable relation items id "collection:items" : many atom Node {
resolve id "collection:items:resolve" execution snapshot targets [browser, server];
resolve id "collection:items:resolve" execution snapshot;
}
queryable relation containers id "collection:containers" : many atom Node {
resolve id "collection:containers:resolve" execution snapshot targets [browser, server];
resolve id "collection:containers:resolve" execution snapshot;
}
queryable relation detail id "collection:detail" : optional-one atom Node {
resolve id "collection:detail:resolve" execution snapshot targets [browser, server];
resolve id "collection:detail:resolve" execution snapshot;
}
}
+3 -2
View File
@@ -1,9 +1,10 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom Note id "compound:note";
type Content = record { text: string; tags: list<string>; };
type Edit = record { change: change<Content>; resolved: bool; };
package EditRuntime id "compound:runtime" revision "compound:runtime@1" {
operation batch id "compound:runtime:batch" : record { edits: list<Edit>; } -> unit
mode call execution transaction targets [browser, server] receiver atom Note requires {
mode call execution transaction targets [JavaScript, server] receiver atom Note requires {
state content id "compound:batch:content" : Content [edit];
state resolved id "compound:batch:resolved" : bool [write];
} accept using checkBatch;
@@ -12,7 +13,7 @@ package EditRuntime id "compound:runtime" revision "compound:runtime@1" {
state allowed id "compound:batch:allowed" : bool [read];
};
operation update id "compound:runtime:update" : Edit -> unit
mode call execution transaction targets [browser, server] receiver atom Note requires {
mode call execution transaction targets [JavaScript, server] receiver atom Note requires {
state content id "compound:edit:content" : Content [edit];
state resolved id "compound:edit:resolved" : bool [write];
} accept using check;
+7 -7
View File
@@ -2,20 +2,20 @@ type Content = record { text: string; tags: list<string>; };
type Edit = record { change: change<Content>; resolved: bool; };
interface NoteData id "compound:data" revision "compound:data@1" {
operation batch id "compound:batch" : record { edits: list<Edit>; } -> unit {
call id "compound:batch:call" execution transaction targets [browser, server];
call id "compound:batch:call" execution transaction;
}
operation update id "compound:update" : Edit -> unit {
call id "compound:update:call" execution transaction targets [browser, server];
call id "compound:update:call" execution transaction;
}
queryable value content id "compound:content" : Content {
get id "compound:content:get" execution snapshot targets [browser, server];
edit id "compound:content:edit" execution transaction targets [browser, server];
get id "compound:content:get" execution snapshot;
edit id "compound:content:edit" execution transaction;
}
queryable value resolved id "compound:resolved" : bool {
get id "compound:resolved:get" execution snapshot targets [browser, server];
get id "compound:resolved:get" execution snapshot;
}
queryable value title id "compound:title" : string {
get id "compound:title:get" execution snapshot targets [browser, server];
set id "compound:title:set" execution transaction targets [browser, server];
get id "compound:title:get" execution snapshot;
set id "compound:title:set" execution transaction;
}
}
+7 -7
View File
@@ -1,11 +1,11 @@
external atom Display id "device:display";
type Request=record {url:string;text:string;version:int32;};
interface Device id "device" revision "device@1" {
static operation create id "device:create" : string -> atom-ref<Display> {call id "device:create:call" execution transaction targets [browser,server];}
queryable value enabled id "device:enabled" : bool {get id "device:enabled:get" execution snapshot targets [browser,server];set id "device:enabled:set" execution transaction targets [browser,server];}
queryable value requested id "device:requested" : Request {get id "device:requested:get" execution snapshot targets [browser,server];}
queryable value observed id "device:observed" : optional<Request> {get id "device:observed:get" execution snapshot targets [browser,server];}
operation requestText id "device:request" : string -> unit {call id "device:request:call" execution transaction targets [browser,server];}
operation recordObservation id "device:record" : Request -> unit {call id "device:record:call" execution transaction targets [server];}
operation remove id "device:remove" : unit -> unit {call id "device:remove:call" execution transaction targets [server];}
static operation create id "device:create" : string -> atom-ref<Display> {call id "device:create:call" execution transaction;}
queryable value enabled id "device:enabled" : bool {get id "device:enabled:get" execution snapshot;set id "device:enabled:set" execution transaction;}
queryable value requested id "device:requested" : Request {get id "device:requested:get" execution snapshot;}
queryable value observed id "device:observed" : optional<Request> {get id "device:observed:get" execution snapshot;}
operation requestText id "device:request" : string -> unit {call id "device:request:call" execution transaction;}
operation recordObservation id "device:record" : Request -> unit {call id "device:record:call" execution transaction;}
operation remove id "device:remove" : unit -> unit {call id "device:remove:call" execution transaction;}
}
+2 -2
View File
@@ -1,6 +1,6 @@
type Request=record {url:string;text:string;version:int32;};
interface DeviceController id "device-controller" revision "device-controller@1" {
queryable portable value enabled id "controller:enabled" : bool {get id "controller:enabled:get" execution snapshot targets [server];}
operation snapshot id "device:snapshot" : unit -> optional<Request> {call id "device:snapshot:call" execution snapshot targets [server];}
queryable portable value enabled id "controller:enabled" : bool {get id "controller:enabled:get" execution snapshot;}
operation snapshot id "device:snapshot" : unit -> optional<Request> {call id "device:snapshot:call" execution snapshot;}
operation apply id "device:apply" : optional<Request> -> unit {call id "device:apply:call" execution remote;}
}
+3 -2
View File
@@ -1,10 +1,11 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
import interface Device;
import interface DeviceController;
external atom Display id "device:display";
type Request=record {url:string;text:string;version:int32;};
package Driver id "driver" revision "driver@1" {
function create id "driver:create" : string -> atom-ref<Display> execution transaction targets [browser,server] requires {creation device id "device" : Display input record {enabled:bool;requested:Request;observed:optional<Request>;};};
operation request id "driver:request" : string -> unit mode call execution transaction targets [browser,server] receiver any requires {state requested id "requested" : Request [read,write];};
function create id "driver:create" : string -> atom-ref<Display> execution transaction targets [JavaScript,server] requires {creation device id "device" : Display input record {enabled:bool;requested:Request;observed:optional<Request>;};};
operation request id "driver:request" : string -> unit mode call execution transaction targets [JavaScript,server] receiver any requires {state requested id "requested" : Request [read,write];};
operation snapshot id "driver:snapshot" : unit -> optional<Request> mode call execution snapshot targets [server] receiver any requires {interface data id "data" : Device using [enabled.get,requested.get,observed.get];};
operation recordObservation id "driver:record" : Request -> unit mode call execution transaction targets [server] receiver any requires {state requested id "requested" : Request [read];state observed id "observed" : optional<Request> [write];};
operation apply id "driver:apply" : optional<Request> -> unit mode call execution remote receiver any requires {interface device id "device" : Device using [recordObservation.call];};
+1 -1
View File
@@ -1,5 +1,5 @@
interface EventHandler<value Payload,value Parameters> id "interface:org.quixos.events:handler" revision "interface:org.quixos.events:handler@1" {
operation handle id "events:handle" : record {event:Payload;parameters:Parameters;} -> unit {
call id "events:handle:call" execution transaction targets [server];
call id "events:handle:call" execution transaction;
}
}
+3 -3
View File
@@ -1,13 +1,13 @@
// Required server support; a checked installed implementation may additionally predict locally.
interface EventStream<value Payload> id "interface:org.quixos.events:stream" revision "interface:org.quixos.events:stream@1" {
operation publish id "events:publish" : Payload -> record {publication:string;} {
call id "events:publish:call" execution transaction targets [server];
call id "events:publish:call" execution transaction;
}
queryable value head id "events:head" : optional<uint64> {
get id "events:head:get" execution snapshot targets [server];
get id "events:head:get" execution snapshot;
}
operation readPage id "events:read-page" : record {after:optional<string>;limit:uint32;}
-> record {entries:list<record {publication:string;position:uint64;payload:Payload;}>;next:optional<string>;asOfHead:optional<uint64>;} {
call id "events:read-page:call" execution snapshot targets [server];
call id "events:read-page:call" execution snapshot;
}
}
+5 -5
View File
@@ -7,12 +7,12 @@ type StartPosition = variant id "org.quixos.events:start@1" {
};
interface EventSubscription<value Payload,value Parameters> id "interface:org.quixos.events:subscription" revision "interface:org.quixos.events:subscription@1" {
operation attach id "events:attach" : record {source:interface-ref<EventStream<Payload>>;handler:interface-ref<EventHandler<Payload,Parameters>>;parameters:Parameters;start:StartPosition;} -> unit {
call id "events:attach:call" execution transaction targets [server];
call id "events:attach:call" execution transaction;
}
queryable value status id "events:status" : record {status:string;generation:uint64;cursor:uint64;failure:optional<record {publication:string;code:string;message:string;}>;} {
get id "events:status:get" execution snapshot targets [server];
get id "events:status:get" execution snapshot;
}
operation pause id "events:pause" : unit -> unit {call id "events:pause:call" execution transaction targets [server];}
operation resume id "events:resume" : unit -> unit {call id "events:resume:call" execution transaction targets [server];}
operation retire id "events:retire" : unit -> unit {call id "events:retire:call" execution transaction targets [server];}
operation pause id "events:pause" : unit -> unit {call id "events:pause:call" execution transaction;}
operation resume id "events:resume" : unit -> unit {call id "events:resume:call" execution transaction;}
operation retire id "events:retire" : unit -> unit {call id "events:retire:call" execution transaction;}
}
+1 -1
View File
@@ -2,6 +2,6 @@ external atom Comment id "atom:reader:comment";
interface CommentFactory id "interface:reader:comment-factory"
revision "interface:reader:comment-factory@1" {
operation prepareComment id "document:prepare-comment" : bool -> atom-ref<Comment> {
call id "document:prepare-comment:call" execution transaction targets [browser, server];
call id "document:prepare-comment:call" execution transaction;
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom Document id "atom:reader:document";
external atom Comment id "atom:reader:comment";
import interface CommentData;
type CommentSeed = record { content: record { text: string; tags: list<string>; }; };
package CommentRuntime id "package:comment-runtime" revision "package:comment-runtime@1" {
operation prepareComment id "comment:prepare"
: bool -> atom-ref<Comment> mode call execution transaction targets [browser, server]
: bool -> atom-ref<Comment> mode call execution transaction targets [JavaScript, server]
receiver atom Document requires {
creation comment id "comment:prepare:comment" : Comment input CommentSeed;
};
+1 -1
View File
@@ -20,6 +20,6 @@ interface DocumentFactory id "interface:reader:document-factory"
revision "interface:reader:document-factory@1" {
static operation create id "document:create" : DocumentSeed -> atom-ref<Document> {
call id "document:create:call"
execution transaction targets [browser, server];
execution transaction;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
interface DocumentLabel id "interface:reader:document-label"
revision "interface:reader:document-label@1" {
queryable value title id "document:view-title" : string {
get id "document:view-title:get" execution snapshot targets [browser, server];
get id "document:view-title:get" execution snapshot;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
interface DocumentMetadata id "interface:reader:document-metadata"
revision "interface:reader:document-metadata@1" {
queryable value label id "document:label" : string {
get id "document:label:get" execution snapshot targets [browser, server];
get id "document:label:get" execution snapshot;
}
}
+5 -4
View File
@@ -1,3 +1,4 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
// Shared record definitions, repeated explicitly in independently locked resources.
external atom Document id "atom:reader:document";
external atom Highlight id "atom:reader:highlight";
@@ -30,7 +31,7 @@ package ReaderRuntime id "package:reader-runtime"
revision "package:reader-runtime@1" {
operation addHighlight id "reader:add-highlight"
: SourceRange -> atom-ref<Highlight>
mode call execution transaction targets [browser, server]
mode call execution transaction targets [JavaScript, server]
receiver atom Document requires {
interface document id "reader:add:document" : DocumentData
using [sourceRevision.get, content.get, highlights.connect];
@@ -39,7 +40,7 @@ revision "package:reader-runtime@1" {
};
operation excerpt id "reader:excerpt" : unit -> string
mode call execution snapshot targets [browser, server]
mode call execution snapshot targets [JavaScript, server]
receiver atom Highlight requires {
state range id "reader:excerpt:range" : SourceRange [read];
interface document id "reader:excerpt:document" : DocumentData
@@ -49,11 +50,11 @@ revision "package:reader-runtime@1" {
// A receiverless function needs no synthetic service object.
function validateRange id "reader:validate-range"
: record { range: SourceRange; content: string; } -> bool
execution snapshot targets [browser, server];
execution snapshot targets [JavaScript, server];
function createDocument id "reader:create-document"
: DocumentSeed -> atom-ref<Document>
execution transaction targets [browser, server] requires {
execution transaction targets [JavaScript, server] requires {
creation document id "reader:create:document" : Document input DocumentSeed;
};
query ReplicaInputs id "reader:replica-inputs" root DocumentData
+2 -2
View File
@@ -20,8 +20,8 @@ interface CommentData id "interface:reader:comment-data"
revision "interface:reader:comment-data@1" {
queryable value content id "comment:content" : CommentContent {
get id "comment:content:get"
execution snapshot targets [browser, server];
execution snapshot;
edit id "comment:content:edit"
execution transaction targets [browser, server];
execution transaction;
}
}
+1 -1
View File
@@ -20,6 +20,6 @@ interface DocumentActions id "interface:reader:document-actions"
revision "interface:reader:document-actions@1" {
operation annotate id "document:annotate" : SourceRange -> atom-ref<Highlight> {
call id "document:annotate:call"
execution transaction targets [browser, server];
execution transaction;
}
}
+4 -4
View File
@@ -20,17 +20,17 @@ interface DocumentData id "interface:reader:document-data"
revision "interface:reader:document-data@1" {
queryable value sourceRevision id "document:source-revision" : string {
get id "document:source-revision:get"
execution snapshot targets [browser, server];
execution snapshot;
}
queryable value content id "document:content" : string {
get id "document:content:get"
execution snapshot targets [browser, server];
execution snapshot;
}
queryable relation highlights id "document:highlights"
: many-unique atom Highlight {
resolve id "document:highlights:resolve"
execution snapshot targets [browser, server];
execution snapshot;
connect id "document:highlights:connect"
execution transaction targets [browser, server];
execution transaction;
}
}
+1 -1
View File
@@ -20,6 +20,6 @@ interface DocumentFactory id "interface:reader:document-factory"
revision "interface:reader:document-factory@1" {
static operation create id "document:create" : DocumentSeed -> atom-ref<Document> {
call id "document:create:call"
execution transaction targets [browser, server];
execution transaction;
}
}
+5 -5
View File
@@ -20,20 +20,20 @@ interface HighlightData id "interface:reader:highlight-data"
revision "interface:reader:highlight-data@1" {
queryable value range id "highlight:range" : SourceRange {
get id "highlight:range:get"
execution snapshot targets [browser, server];
execution snapshot;
}
queryable value color id "highlight:color" : string {
get id "highlight:color:get"
execution snapshot targets [browser, server];
execution snapshot;
set id "highlight:color:set"
execution transaction targets [browser, server];
execution transaction;
}
queryable portable value excerpt id "highlight:excerpt" : string {
get id "highlight:excerpt:get"
execution snapshot targets [browser, server];
execution snapshot;
}
queryable relation comments id "highlight:comments" : many-unique atom Comment {
resolve id "highlight:comments:resolve"
execution snapshot targets [browser, server];
execution snapshot;
}
}
+5 -4
View File
@@ -1,3 +1,4 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
// Shared record definitions, repeated explicitly in independently locked resources.
external atom Document id "atom:reader:document";
external atom Highlight id "atom:reader:highlight";
@@ -29,7 +30,7 @@ package ReaderRuntime id "package:reader-runtime"
revision "package:reader-runtime@1" {
operation addHighlight id "reader:add-highlight"
: SourceRange -> atom-ref<Highlight>
mode call execution transaction targets [browser, server]
mode call execution transaction targets [JavaScript, server]
receiver atom Document requires {
interface document id "reader:add:document" : DocumentData
using [sourceRevision.get, content.get, highlights.connect];
@@ -38,7 +39,7 @@ revision "package:reader-runtime@1" {
};
operation excerpt id "reader:excerpt" : unit -> string
mode call execution snapshot targets [browser, server]
mode call execution snapshot targets [JavaScript, server]
receiver atom Highlight requires {
state range id "reader:excerpt:range" : SourceRange [read];
interface document id "reader:excerpt:document" : DocumentData
@@ -48,11 +49,11 @@ revision "package:reader-runtime@1" {
// A receiverless function needs no synthetic service object.
function validateRange id "reader:validate-range"
: record { range: SourceRange; content: string; } -> bool
execution snapshot targets [browser, server];
execution snapshot targets [JavaScript, server];
function createDocument id "reader:create-document"
: DocumentSeed -> atom-ref<Document>
execution transaction targets [browser, server] requires {
execution transaction targets [JavaScript, server] requires {
creation document id "reader:create:document" : Document input DocumentSeed;
};
query ReplicaInputs id "reader:replica-inputs" root DocumentData
+2 -2
View File
@@ -1,7 +1,7 @@
import interface PresentationState;
interface DocumentNavigation id "document-navigation" revision "document-navigation@1" requires PresentationState {
queryable value compact id "document-navigation:compact" : bool {
get id "document-navigation:compact:get" execution snapshot targets [browser,server];
set id "document-navigation:compact:set" execution transaction targets [browser,server];
get id "document-navigation:compact:get" execution snapshot;
set id "document-navigation:compact:set" execution transaction;
}
}
+4 -4
View File
@@ -1,15 +1,15 @@
import interface PresentationState;
interface PresentationMount id "presentation-mount" revision "presentation-mount@1" {
static operation create id "presentation-mount:create" : unit -> interface-ref<PresentationMount> {
call id "presentation-mount:create:call" execution transaction targets [browser,server];
call id "presentation-mount:create:call" execution transaction;
}
queryable relation members id "presentation-mount:members" : many-unique interface PresentationState {
resolve id "presentation-mount:members:resolve" execution snapshot targets [browser,server];
resolve id "presentation-mount:members:resolve" execution snapshot;
}
queryable relation root id "presentation-mount:root" : optional-one interface PresentationState {
resolve id "presentation-mount:root:resolve" execution snapshot targets [browser,server];
resolve id "presentation-mount:root:resolve" execution snapshot;
}
operation attach id "presentation-mount:attach" : interface-ref<PresentationState> -> unit {
call id "presentation-mount:attach:call" execution transaction targets [browser,server];
call id "presentation-mount:attach:call" execution transaction;
}
}
+6 -5
View File
@@ -1,3 +1,4 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
import interface PresentationStates;
import interface ReaderNavigation;
external atom ReaderNavigationRecord id "reader-navigation-record";
@@ -6,24 +7,24 @@ import interface PresentationMount;
import interface StateFactory;
external atom PresentationMountRecord id "presentation-mount-record";
package PresentationRuntime id "presentation-runtime" revision "presentation-runtime@1" {
function createReaderState id "presentation-runtime:create-reader-state" : record {occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> interface-ref<ReaderNavigation> execution transaction targets [browser,server] requires {
function createReaderState id "presentation-runtime:create-reader-state" : record {occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> interface-ref<ReaderNavigation> execution transaction targets [JavaScript,server] requires {
creation navigation id "navigation" : ReaderNavigationRecord input record {occurrence:string;parent:optional<interface-ref<PresentationState>>;};
};
function createPresentationMount id "presentation-runtime:create" : unit -> interface-ref<PresentationMount> execution transaction targets [browser,server] requires {
function createPresentationMount id "presentation-runtime:create" : unit -> interface-ref<PresentationMount> execution transaction targets [JavaScript,server] requires {
creation presentationMount id "presentationMount" : PresentationMountRecord input unit;
};
operation attach id "presentation-runtime:attach" : interface-ref<PresentationState> -> unit mode call execution transaction targets [browser,server] receiver any requires {
operation attach id "presentation-runtime:attach" : interface-ref<PresentationState> -> unit mode call execution transaction targets [JavaScript,server] receiver any requires {
edge members id "members" : many-unique interface PresentationState [resolve,connect];
edge root id "root" : optional-one interface PresentationState [resolve,connect];
interface metadata id "state" : PresentationState using [presentationMount.resolve,parent.resolve] from input;
};
operation ensure<object Navigation implements PresentationState> id "presentation-runtime:ensure" : record {presentationMount:interface-ref<PresentationMount>;occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> ref<Navigation> mode call execution transaction targets [browser,server] receiver any requires {
operation ensure<object Navigation implements PresentationState> id "presentation-runtime:ensure" : record {presentationMount:interface-ref<PresentationMount>;occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> ref<Navigation> mode call execution transaction targets [JavaScript,server] receiver any requires {
edge states id "states" : many-unique object Navigation keyed "string" [resolve,connect];
interface metadata id "state" : PresentationState using [occurrence.get,presentationMount.resolve,parent.resolve] from input;
interface presentationMount id "presentationMount" : PresentationMount using [attach.call] from input;
interface factory id "factory" : StateFactory<Navigation> using [create.call];
};
function createState<object Navigation> id "presentation-runtime:create-state" : record {occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> ref<Navigation> execution transaction targets [browser,server] requires {
function createState<object Navigation> id "presentation-runtime:create-state" : record {occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> ref<Navigation> execution transaction targets [JavaScript,server] requires {
creation navigation id "navigation" : Navigation input record {occurrence:string;parent:optional<interface-ref<PresentationState>>;};
};
query ReaderStateInputs id "reader-state:inputs" root PresentationStates<atom ReaderNavigationRecord> document "queries/reader-inputs.graphql" operation "ReaderState" { replica; view ReaderNavigationRecord as ReaderNavigation; max rows 100; }
+3 -3
View File
@@ -1,12 +1,12 @@
external interface PresentationMount revision "presentation-mount@1";
interface PresentationState id "presentation-state" revision "presentation-state@1" {
queryable value occurrence id "state:occurrence" : string {
get id "state:occurrence:get" execution snapshot targets [browser,server];
get id "state:occurrence:get" execution snapshot;
}
queryable relation presentationMount id "state:presentation-mount" : optional-one interface PresentationMount {
resolve id "state:presentation-mount:resolve" execution snapshot targets [browser,server];
resolve id "state:presentation-mount:resolve" execution snapshot;
}
queryable relation parent id "state:parent" : optional-one interface PresentationState {
resolve id "state:parent:resolve" execution snapshot targets [browser,server];
resolve id "state:parent:resolve" execution snapshot;
}
}
+2 -2
View File
@@ -2,9 +2,9 @@ import interface PresentationState;
import interface PresentationMount;
interface PresentationStates<object Navigation implements PresentationState> id "presentation-states" revision "presentation-states@1" {
queryable relation states id "presentation-states:states" : many-unique object Navigation keyed "string" {
resolve id "presentation-states:states:resolve" execution snapshot targets [browser,server];
resolve id "presentation-states:states:resolve" execution snapshot;
}
operation ensure id "presentation-states:ensure" : record {presentationMount:interface-ref<PresentationMount>;occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> ref<Navigation> {
call id "presentation-states:ensure:call" execution transaction targets [browser,server];
call id "presentation-states:ensure:call" execution transaction;
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
import interface PresentationState;
interface ReaderNavigation id "reader-navigation" revision "reader-navigation@1" requires PresentationState {
queryable value page id "reader-navigation:mode" : string {
get id "reader-navigation:mode:get" execution snapshot targets [browser,server];
set id "reader-navigation:mode:set" execution transaction targets [browser,server];
get id "reader-navigation:mode:get" execution snapshot;
set id "reader-navigation:mode:set" execution transaction;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import interface PresentationState;
interface StateFactory<object Navigation implements PresentationState> id "state-factory" revision "state-factory@1" {
static operation create id "state-factory:create" : record {occurrence:string;parent:optional<interface-ref<PresentationState>>;} -> ref<Navigation> {
call id "state-factory:create:call" execution transaction targets [browser,server];
call id "state-factory:create:call" execution transaction;
}
}
+5 -5
View File
@@ -1,15 +1,15 @@
interface Task id "recurring:task-data" revision "recurring:task-data@1" {
queryable value occurrence id "recurring:occurrence" : uint64 {
get id "recurring:occurrence:get" execution snapshot targets [browser, server];
get id "recurring:occurrence:get" execution snapshot;
}
queryable value due id "recurring:due" : int64 {
get id "recurring:due:get" execution snapshot targets [browser, server];
get id "recurring:due:get" execution snapshot;
}
queryable value title id "recurring:title" : string {
get id "recurring:title:get" execution snapshot targets [browser, server];
set id "recurring:title:set" execution transaction targets [browser, server];
get id "recurring:title:get" execution snapshot;
set id "recurring:title:set" execution transaction;
}
operation complete id "recurring:complete" : uint64 -> unit {
call id "recurring:complete:call" execution transaction targets [browser, server];
call id "recurring:complete:call" execution transaction;
}
}
+2 -1
View File
@@ -1,7 +1,8 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom TaskObject id "recurring:task";
package TaskRuntime id "recurring:runtime" revision "recurring:runtime@1" {
operation complete id "recurring:runtime:complete" : uint64 -> unit
mode call execution transaction targets [browser, server] receiver atom TaskObject requires {
mode call execution transaction targets [JavaScript, server] receiver atom TaskObject requires {
state occurrence id "recurring:port:occurrence" : uint64 [read, write];
state due id "recurring:port:due" : int64 [read, write];
};
+3 -3
View File
@@ -1,11 +1,11 @@
interface TreeData id "tree:data" revision "tree:data@1" {
operation reparent id "tree:reparent" : optional<interface-ref<TreeData>> -> unit {
call id "tree:reparent:call" execution transaction targets [browser, server];
call id "tree:reparent:call" execution transaction;
}
queryable relation parent id "tree:parent" : optional-one interface TreeData {
resolve id "tree:parent:resolve" execution snapshot targets [browser, server];
resolve id "tree:parent:resolve" execution snapshot;
}
queryable relation children id "tree:children" : many interface TreeData {
resolve id "tree:children:resolve" execution snapshot targets [browser, server];
resolve id "tree:children:resolve" execution snapshot;
}
}
+2 -1
View File
@@ -1,8 +1,9 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom Node id "tree:node";
import interface TreeData;
package TreeRuntime id "tree:runtime" revision "tree:runtime@1" {
operation reparent id "tree:runtime:reparent" : optional<interface-ref<TreeData>> -> unit
mode call execution transaction targets [browser, server] receiver atom Node requires {
mode call execution transaction targets [JavaScript, server] receiver atom Node requires {
edge parent id "tree:move:parent" : optional-one interface TreeData [resolve, connect, disconnect];
} accept using check;
operation check id "tree:runtime:check" : optional<interface-ref<TreeData>> -> unit
+1 -1
View File
@@ -609,7 +609,7 @@ test("edge read targets honor declared interface prerequisites without inferring
for (const text of [
'interface Base id "base" revision "base@1" {}',
'import interface Base; interface Detailed id "detailed" revision "detailed@1" requires Base {}',
'import interface Base; interface Holder id "holder" revision "holder@1" {queryable relation item id "item" : optional-one interface Base {resolve id "item:resolve" execution snapshot targets [browser,server];}}',
'import interface Base; interface Holder id "holder" revision "holder@1" {queryable relation item id "item" : optional-one interface Base {resolve id "item:resolve" execution snapshot;}}',
]) {
const parsed = compileCapabilityResourceSource(text, { source, environment: { interfaces } });
assert.ok(parsed.ok, JSON.stringify(parsed.diagnostics));
+7 -7
View File
@@ -58,12 +58,12 @@ const contract = `external atom Document id "document";
type Event = record { document: atom-ref<Document>; title: string; };
interface Stream id "stream" revision "stream@1" {
operation publish id "publish" : Event -> record { publication: string; } {
call id "publish:call" execution transaction targets [server];
call id "publish:call" execution transaction;
}
queryable value head id "head" : optional<uint64> { get id "head:get" execution snapshot targets [server]; }
queryable value head id "head" : optional<uint64> { get id "head:get" execution snapshot; }
operation readPage id "page" : record { after: optional<string>; limit: uint32; }
-> record { entries: list<record { publication: string; position: uint64; payload: Event; }>; next: optional<string>; asOfHead: optional<uint64>; } {
call id "page:call" execution snapshot targets [server];
call id "page:call" execution snapshot;
}
}`;
const assembly = `workspace Journals id "journals" revision "journals@1" commit "${source.commit}" {
@@ -127,7 +127,7 @@ test("subscription storage infers its typed handler and retains configuration re
`external atom Document id "document";
interface Receiver id "receiver" revision "receiver@1" {
operation receive id "receive" : record { event: record { document: atom-ref<Document>; title: string; }; parameters: string; } -> unit {
call id "receive:call" execution transaction targets [server];
call id "receive:call" execution transaction;
}
}`,
);
@@ -139,12 +139,12 @@ import interface Receiver;
type Start = variant id "start" { future id "future" : unit; retained id "retained" : unit; afterCursor id "after" : string; };
interface Subscription id "subscription" revision "subscription@1" {
operation attach id "attach" : record { source: interface-ref<Stream>; handler: interface-ref<Receiver>; parameters: string; start: Start; } -> unit {
call id "attach:call" execution transaction targets [server];
call id "attach:call" execution transaction;
}
queryable value status id "status" : record { status: string; generation: uint64; cursor: uint64; failure: optional<record { publication: string; code: string; message: string; }>; } {
get id "status:get" execution snapshot targets [server];
get id "status:get" execution snapshot;
}
operation retire id "retire" : unit -> unit { call id "retire:call" execution transaction targets [server]; }
operation retire id "retire" : unit -> unit { call id "retire:call" execution transaction; }
}`,
);
const ws = assembly
+7 -6
View File
@@ -12,19 +12,20 @@ const source = { repository: "https://example.test/keyed.git", commit: "a".repea
const iface = compileCapabilityResourceSource(
`interface Index<object Item> id "index" revision "index@1" {
queryable relation entries id "entries" : many object Item keyed "string" {
resolve id "resolve" execution snapshot targets [browser,server];
connect id "connect" execution transaction targets [browser,server];
resolve id "resolve" execution snapshot;
connect id "connect" execution transaction;
}
operation add id "add" : record {mapKey:string;target:ref<Item>;} -> unit {
call id "add-call" execution transaction targets [browser,server];
call id "add-call" execution transaction;
}
}`,
{ source },
);
assert.ok(iface.ok && iface.resource.kind === "interface", JSON.stringify(iface.diagnostics));
const pkg = compileCapabilityResourceSource(
`package Runtime id "runtime" revision "runtime@1" {
operation add<object Item> id "add" : record {mapKey:string;target:ref<Item>;} -> unit mode call execution transaction targets [browser,server] receiver any requires {
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
package Runtime id "runtime" revision "runtime@1" {
operation add<object Item> id "add" : record {mapKey:string;target:ref<Item>;} -> unit mode call execution transaction targets [JavaScript,server] receiver any requires {
edge entries id "entries" : many object Item keyed "string" [resolve,connect];
};
}`,
@@ -61,7 +62,7 @@ test("generic keyed relationships and portable ports have matching checked shape
const plan = compileExecutionPlan(result.workspace);
assert.doesNotThrow(() => compileKernelProgram(plan));
const bindings = generatePortableTypeScriptBindings(plan.definition.bindingSchema, "runtime@1", {
target: "browser",
target: "target:org.quixos.execution.javascript@1",
});
assert.match(bindings, /readonly "mapKey": string/);
assert.match(bindings, /readonly "connect": \(input: \{ readonly "mapKey": string;/);
+4 -5
View File
@@ -5,7 +5,7 @@ import { formatQx } from "../src/capability-language/source.js";
import { compileExecutionPlan, compileKernelProgram } from "../src/execution-world/index.js";
const contract = `interface Item id "item" revision "item@1" {
operation remove id "remove" : unit -> unit {
call id "remove:call" execution transaction targets [server];
call id "remove:call" execution transaction;
}
}`;
const workspace = `workspace Lifecycle id "workspace:lifecycle" revision "workspace:lifecycle@1" commit "${"a".repeat(40)}" {
@@ -45,10 +45,9 @@ test("lifecycle binding and endpoint policies survive formatting and kernel comp
});
test("deletion rejects browser, snapshot, remote, class, payload and unknown primitive promises", () => {
for (const [iface, ws] of [
[contract.replace("targets [server]", "targets [browser, server]"), workspace],
[contract.replace("transaction targets", "snapshot targets"), workspace],
[contract.replace("execution transaction targets [server]", "execution remote"), workspace],
[contract.replace("execution transaction targets [server]", ""), workspace],
[contract.replace("execution transaction", "execution snapshot"), workspace],
[contract.replace("execution transaction", "execution remote"), workspace],
[contract.replace("execution transaction", ""), workspace],
[contract.replace("operation remove", "static operation remove"), workspace],
[contract.replace("unit -> unit", "string -> unit"), workspace],
[contract.replace("unit -> unit", "unit -> string"), workspace],
+14 -9
View File
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
import { test } from "node:test";
import { compileQuery } from "../src/query/compile.js";
import { linkQueries } from "../src/query/link.js";
import { compileExecutionPlan } from "../src/execution-world/plan.js";
import { loadReaderWorkspace as load, readReaderFixture as read } from "./reader-fixture.js";
test("complete reader contracts compose factories, portable selectors, CRDT edits, and bounded query scopes", async () => {
const workspace = await load();
@@ -23,15 +24,19 @@ test("complete reader contracts compose factories, portable selectors, CRDT edit
);
});
test("reader composition reports the dependency that weakens a portable promise", async () => {
await assert.rejects(
load((name, text) =>
name === "ReaderRuntime.qx"
? text.replace(/(operation excerpt[\s\S]*?targets )\[browser, server\]/, "$1[server]")
: text,
),
/does not provide execution target browser/,
test("ordinary operations allow server fallback; explicitly local projections require local code", async () => {
const serverExcerpt = await load((name, text) =>
name === "ReaderRuntime.qx"
? text.replace(/(operation excerpt[\s\S]*?targets )\[JavaScript, server\]/, "$1[server]")
: text,
);
assert.throws(() => compileExecutionPlan(serverExcerpt), /NONPORTABLE_QUERY_FIELD/);
for (const pkg of serverExcerpt.packageImports)
pkg.queries = pkg.queries?.filter((q) => q.placement?.kind !== "local");
const fallback = compileExecutionPlan(serverExcerpt).definition.operations.find(
(op) => op.operationId === "highlight:excerpt:get",
)!;
assert.deepEqual(fallback.availableTargets, ["server"]);
await assert.rejects(
load((name, text) =>
name === "ReaderRuntime.qx"
@@ -56,6 +61,6 @@ test("a field can remain server-queryable while failing local query placement",
await compileQuery(admission, workspace.interfaceImports, read);
await assert.rejects(
compileQuery(local, workspace.interfaceImports, read),
/browser snapshot getter for HighlightData.color/,
/graph snapshot getter for HighlightData.color/,
);
});
+29 -12
View File
@@ -22,7 +22,9 @@ test("service bindings depend on their consumed contract, not workspace or sourc
test("reader portable bindings have selected facets, creation inputs and stable keyed calls", async () => {
const schema = compileExecutionPlan(await loadReaderWorkspace()).definition.bindingSchema;
const source = generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", { target: "browser" });
const source = generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", {
target: "target:org.quixos.execution.javascript@1",
});
assert.match(source, /readonly "sourceRevision.get": \(\) => Promise<string>/);
assert.match(
source,
@@ -32,22 +34,30 @@ test("reader portable bindings have selected facets, creation inputs and stable
assert.match(source, /readonly "create":/);
assert.match(source, /readonly "comments": ReadonlyArray/);
assert.doesNotMatch(source, /"highlights.resolve"|context\.conform|objectId|"live"/);
assert.equal(source, generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", { target: "browser" }));
assert.equal(
source,
generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", {
target: "target:org.quixos.execution.javascript@1",
}),
);
const server = generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", { target: "server" });
assert.equal(source.replace('target = "browser"', 'target = "server"'), server);
assert.equal(source.replace('target = "target:org.quixos.execution.javascript@1"', 'target = "server"'), server);
});
test("generation rejects remote dependencies, wrong effects and unsupported shapes explicitly", async () => {
const schema = compileExecutionPlan(await loadReaderWorkspace()).definition.bindingSchema;
const generate = () => generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", { target: "browser" });
const generate = () =>
generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", {
target: "target:org.quixos.execution.javascript@1",
});
const entry = schema.packages[0]!.exports.find((e) => e.displayName === "addHighlight")!;
entry.execution = { kind: "snapshot", targets: ["browser"] };
entry.execution = { kind: "snapshot", targets: ["target:org.quixos.execution.javascript@1"] };
assert.throws(generate, /Snapshot context cannot expose mutation ports/);
entry.execution = { kind: "transaction", targets: ["browser"] };
entry.execution = { kind: "transaction", targets: ["target:org.quixos.execution.javascript@1"] };
const iface = schema.interfaces.find((i) => i.revisionId === "interface:reader:document-data@1")!;
const getter = iface.members[0]!.operations[0]!;
getter.execution = { kind: "remote" };
assert.throws(generate, /not an object call available in portable target browser/);
assert.throws(generate, /not an object call available in portable target target:org.quixos.execution.javascript@1/);
getter.execution = { kind: "snapshot", targets: ["browser", "server"] };
entry.dependencyPorts[0]!.requirement = { kind: "reflection", selectors: ["active"] };
assert.throws(generate, /Only checked presentation metadata is portable/);
@@ -59,7 +69,9 @@ test("closed generic portable handlers have distinct concrete codecs and no univ
const schema = plan.definition.bindingSchema;
const pkg = schema.packages[0]!;
assert.equal(pkg.exports.length, 2);
const generated = generatePortableTypeScriptBindings(schema, pkg.revisionId, { target: "browser" });
const generated = generatePortableTypeScriptBindings(schema, pkg.revisionId, {
target: "target:org.quixos.execution.javascript@1",
});
assert.match(generated, /readonly input: string/);
assert.match(generated, /readonly input: ReadonlyArray<bigint>/);
for (const entry of pkg.exports) {
@@ -69,7 +81,7 @@ test("closed generic portable handlers have distinct concrete codecs and no univ
}
assert.notEqual(pkg.exports[0]!.displayName, pkg.exports[1]!.displayName);
assert.equal(
generated.replace('target = "browser"', 'target = "server"'),
generated.replace('target = "target:org.quixos.execution.javascript@1"', 'target = "server"'),
generatePortableTypeScriptBindings(schema, pkg.revisionId, { target: "server" }),
);
});
@@ -90,7 +102,10 @@ test("a portable subset of a mixed interface excludes remote dependencies", asyn
displayName: "remote",
operations: [operation],
});
const generate = () => generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", { target: "browser" });
const generate = () =>
generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", {
target: "target:org.quixos.execution.javascript@1",
});
assert.doesNotMatch(generate(), /readonly "remote.get"/);
selected.operations!.push(operation.id);
assert.throws(generate, /select a portable subset/);
@@ -99,7 +114,7 @@ test("a portable subset of a mixed interface excludes remote dependencies", asyn
test("server projection descriptors are distinct from canonical inputs and have result decoders", async () => {
const { generatePortableClientBindings } = await import("../src/bindings/portable-client.js");
const plan = compileExecutionPlan(await loadReaderWorkspace());
const source = generatePortableClientBindings(plan, { target: "browser" });
const source = generatePortableClientBindings(plan, { target: "target:org.quixos.execution.javascript@1" });
const projections = source.slice(
source.indexOf("export const serverProjections"),
source.indexOf("export const results"),
@@ -127,7 +142,9 @@ test("static interface ports keep unit arguments explicit for generic factory co
op.inputType = { kind: "builtin", name: "unit" };
port.requirement.operations = [op.id];
delete port.requirement.receiver;
const generated = generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", { target: "browser" });
const generated = generatePortableTypeScriptBindings(schema, "package:reader-runtime@1", {
target: "target:org.quixos.execution.javascript@1",
});
assert.match(generated, /"explicitInput": true/);
assert.match(generated, /input: null/);
});
+4 -3
View File
@@ -9,17 +9,18 @@ export function portableGenericPlan() {
`
interface Echo<value T> id "echo-interface" revision "echo-interface@2" {
operation echo id "echo-member" : T -> T {
call id "echo-call" execution snapshot targets [browser, server];
call id "echo-call" execution snapshot;
}
}`,
{ source },
);
assert.ok(iface.ok && iface.resource.kind === "interface", JSON.stringify(iface.diagnostics));
const pkg = compileCapabilityResourceSource(
`
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
package Generic id "generic" revision "generic@3" {
operation echo<value T> id "echo" : T -> T mode call
execution snapshot targets [browser, server] receiver any;
execution snapshot targets [JavaScript, server] receiver any;
}`,
{ source: { ...source, commit: "b".repeat(40) } },
);
+2 -2
View File
@@ -24,8 +24,8 @@ async function fixture(replacement = "First", change?: (artifact: ReactPreparati
return result.resource;
};
for (const text of [
'interface Row id "row" revision "row@1" { queryable value title id "title" : string {get id "title:get" execution snapshot targets [browser, server];} queryable value detail id "detail" : string {get id "detail:get" execution snapshot targets [browser, server];} }',
'import interface Row; interface Library id "library" revision "library@1" { queryable relation selected id "selected" : optional-one interface Row {resolve id "selected:get" execution snapshot targets [browser, server];} }',
'interface Row id "row" revision "row@1" { queryable value title id "title" : string {get id "title:get" execution snapshot;} queryable value detail id "detail" : string {get id "detail:get" execution snapshot;} }',
'import interface Row; interface Library id "library" revision "library@1" { queryable relation selected id "selected" : optional-one interface Row {resolve id "selected:get" execution snapshot;} }',
'import interface Row; interface RowRole id "row-role" revision "row-role@1" { module render id "row-render" contract "org.quixos.react.subject-only/1" subject interface Row; }',
'import interface Library; interface LibraryRole id "library-role" revision "library-role@1" { module render id "library-render" contract "org.quixos.react.subject-only/1" subject interface Library; }',
]) {
+5 -5
View File
@@ -437,17 +437,17 @@ test("React tooling generates checked owner queries from imported closed navigat
const state = iface(
`import interface PresentationMount; import interface Subject;
interface PresentationState id "state" revision "state@1" {
queryable value occurrence id "occurrence" : string {get id "occurrence:get" execution snapshot targets [browser,server];}
queryable relation presentationMount id "mount" : optional-one interface PresentationMount {resolve id "mount:resolve" execution snapshot targets [browser,server];}
queryable relation parent id "parent" : optional-one interface PresentationState {resolve id "parent:resolve" execution snapshot targets [browser,server];}
queryable relation source id "source" : exactly-one interface Subject {resolve id "source:resolve" execution snapshot targets [browser,server];}
queryable value occurrence id "occurrence" : string {get id "occurrence:get" execution snapshot;}
queryable relation presentationMount id "mount" : optional-one interface PresentationMount {resolve id "mount:resolve" execution snapshot;}
queryable relation parent id "parent" : optional-one interface PresentationState {resolve id "parent:resolve" execution snapshot;}
queryable relation source id "source" : exactly-one interface Subject {resolve id "source:resolve" execution snapshot;}
}`,
[mount, subject],
);
const navigation = iface(
`import interface PresentationState;
interface Navigation<value T> id "navigation" revision "navigation@1" requires PresentationState {
queryable value custom id "custom" : T {get id "custom:get" execution snapshot targets [browser,server];}
queryable value custom id "custom" : T {get id "custom:get" execution snapshot;}
}`,
[mount, subject, state],
);
+1 -1
View File
@@ -6,7 +6,7 @@ import type { PackageRevision } from "../src/capability-model/types.js";
const source = { repository: "https://example.test/fragments", commit: "a".repeat(40) };
const compiled = compileCapabilityResourceSource(
`interface Item id "item" revision "item@1" {
queryable value title id "title" : string {get id "title:get" execution snapshot targets [browser,server];}
queryable value title id "title" : string {get id "title:get" execution snapshot;}
queryable rpc value remote id "remote" : string {get id "remote:get";}
}`,
{ source },
+2 -2
View File
@@ -7,8 +7,8 @@ import { defaultQueryBudgets, type QueryDeclaration } from "../src/query/types.j
const source = { repository: "https://example.test/structured.git", commit: "a".repeat(40) };
const result = compileCapabilityResourceSource(
`interface Highlight id "highlight" revision "highlight@1" {
queryable value range id "range" : record { sourceRevision:string; start:int32; end:int32; } { get id "range:get" execution snapshot targets [browser, server]; }
queryable value content id "content" : record { text:string; tags:list<string>; metadata:optional<record { score:int32; }>; } { get id "content:get" execution snapshot targets [browser, server]; }
queryable value range id "range" : record { sourceRevision:string; start:int32; end:int32; } { get id "range:get" execution snapshot; }
queryable value content id "content" : record { text:string; tags:list<string>; metadata:optional<record { score:int32; }>; } { get id "content:get" execution snapshot; }
}`,
{ source },
);
+9 -8
View File
@@ -6,10 +6,11 @@ import { generatePortableTypeScriptBindings } from "../src/bindings/portable.js"
import { formatQx } from "../src/capability-language/source.js";
const source = { repository: "https://example.test/reference.git", commit: "a".repeat(40) };
const iface = `interface Data id "data" revision "data@1" {
value content id "content" : string { get id "content:get" execution snapshot targets [server]; }
operation peek id "peek" : interface-ref<Data> -> string { call id "peek:call" execution snapshot targets [server]; }
value content id "content" : string { get id "content:get" execution snapshot; }
operation peek id "peek" : interface-ref<Data> -> string { call id "peek:call" execution snapshot; }
}`;
const pkg = `external atom Item id "item";
const pkg = `symbol JavaScript id "target:org.quixos.execution.javascript@1";
external atom Item id "item";
import interface Data;
package Runtime id "runtime" revision "runtime@1" {
operation peek id "runtime:peek" : interface-ref<Data> -> string mode call execution snapshot targets [server]
@@ -83,15 +84,15 @@ test("closed generic specialization preserves reference-selected interface ports
});
test("portable input-selected reads use installed support beyond the public minimum", () => {
const portable = pkg.replaceAll("targets [server]", "targets [browser, server]");
const portable = pkg.replaceAll("targets [server]", "targets [JavaScript, server]");
const optional = compile(portable);
assert.ok(optional.ok, JSON.stringify(optional.diagnostics));
const plan = compileExecutionPlan(optional.workspace);
assert.deepEqual(plan.definition.operations.find((op) => op.operationId === "peek:call")!.availableTargets, [
"browser",
"server",
"target:org.quixos.execution.javascript@1",
]);
const result = compile(portable, workspace, iface.replaceAll("targets [server]", "targets [browser, server]"));
const result = compile(portable, workspace, iface);
assert.ok(result.ok, JSON.stringify(result.diagnostics));
});
@@ -102,8 +103,8 @@ test("input-selected transactions compose only within a compatible transaction c
const txPackage = pkg.replace("execution snapshot targets [server]", "execution transaction targets [server]");
// Use an operation for the selected write; value getters themselves must remain snapshots.
const contract = txInterface.replace(
'value content id "content" : string { get id "content:get" execution transaction targets [server]; }',
'operation update id "content" : unit -> string { call id "content:get" execution transaction targets [server]; }',
'value content id "content" : string { get id "content:get" execution transaction; }',
'operation update id "content" : unit -> string { call id "content:get" execution transaction; }',
);
const implementation = txPackage.replace("using [content.get]", "using [update.call]");
const model = workspace.replace(
+3 -3
View File
@@ -35,7 +35,7 @@ test("execution scaffolds route typed handlers by declared effect and target wit
id: "export:" + name,
declaration: `function ${name} id "export:${name}" : unit -> unit execution ${execution};`,
});
await add("both", "transaction targets [browser, server]");
await add("both", "transaction targets [JavaScript, server]");
await add("serverOnly", "snapshot targets [server]");
await add("asyncWork", "remote");
const read = (file: string) => fs.readFile(path.join(root, file), "utf8");
@@ -48,8 +48,8 @@ test("execution scaffolds route typed handlers by declared effect and target wit
assert.match(await read("src/impl/both.ts"), /PortableImplementation/);
assert.match(await read("flake.nix"), /checkedArtifacts/);
assert.match(await read("flake.nix"), /entries.browser/);
await assert.rejects(add("both", "transaction targets [browser, server]"), /unique/);
await assert.rejects(add("invalid", "snapshot targets [remote]"), /browser\/server/);
await assert.rejects(add("both", "transaction targets [JavaScript, server]"), /unique/);
await assert.rejects(add("invalid", "snapshot targets [remote]"), /TypeScript scaffold supports/);
await assert.rejects(apply("function", { name: "missing", id: "export:missing" }), /explicit execution/);
await assert.rejects(apply("migration", { name: "migration", id: "export:migration" }), /retired/);
});