diff --git a/test/collections-fixture.ts b/test/collections-fixture.ts new file mode 100644 index 0000000..921acda --- /dev/null +++ b/test/collections-fixture.ts @@ -0,0 +1,23 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/parser.js"; +import { compileExecutionPlan } from "../src/execution-world/plan.js"; +import type { InterfaceRevision } from "../src/capability-model/types.js"; +export async function collectionsPlan() { + const read = (name: string) => readFile(`test/fixtures/collections/${name}.qx`, "utf8"); + const source = { repository: "https://example.test/collections.git", commit: "a".repeat(40) }; + const interfaces = new Map(); + for (const name of ["NodeData"]) { + const result = compileCapabilityResourceSource(await read(name), { source, environment: { interfaces } }); + assert.ok(result.ok && result.resource.kind === "interface", JSON.stringify(result.diagnostics)); + interfaces.set(name, result.resource.revision); + } + const pkg = compileCapabilityResourceSource(await read("CollectionRuntime"), { source, environment: { interfaces } }); + assert.ok(pkg.ok && pkg.resource.kind === "package", JSON.stringify(pkg.diagnostics)); + const result = compileCapabilitySource(await read("workspace"), "workspace.qx", { + interfaces, + packages: new Map([["CollectionRuntime", pkg.resource.revision]]), + }); + assert.ok(result.ok, JSON.stringify(result.diagnostics)); + return compileExecutionPlan(result.workspace); +} diff --git a/test/emit-portable-reader.ts b/test/emit-portable-reader.ts index 90a413f..dba500f 100644 --- a/test/emit-portable-reader.ts +++ b/test/emit-portable-reader.ts @@ -119,3 +119,9 @@ for (const [name, revision] of [ `dist/portable-reader/async/${name}.ts`, generateRemoteTypeScriptBindings(asyncPlan.definition.bindingSchema, revision), ); + +const { collectionsPlan } = await import("./collections-fixture.js"); +const collections = await collectionsPlan(); +await mkdir("dist/portable-reader/collections", { recursive: true }); +await writeFile("dist/portable-reader/collections/plan.json", JSON.stringify(collections)); +await writeFile("dist/portable-reader/collections/kernel.json", JSON.stringify(compileKernelProgram(collections))); diff --git a/test/fixtures/collections/CollectionRuntime.qx b/test/fixtures/collections/CollectionRuntime.qx new file mode 100644 index 0000000..160601b --- /dev/null +++ b/test/fixtures/collections/CollectionRuntime.qx @@ -0,0 +1,18 @@ +type Replacement = record { expected: list; targets: list>; }; +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 + mode call execution snapshot targets [browser, 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 { + edge items id "collection:replace:items" : many atom Node [resolve, connect, disconnect]; + }; + operation ensure id "collection:runtime:ensure" : unit -> atom-ref + mode call execution transaction targets [browser, 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; + }; +} diff --git a/test/fixtures/collections/NodeData.qx b/test/fixtures/collections/NodeData.qx new file mode 100644 index 0000000..21eb3ce --- /dev/null +++ b/test/fixtures/collections/NodeData.qx @@ -0,0 +1,26 @@ +type Replacement = record { expected: list; targets: list>; }; +external atom Node id "collection:node"; +interface NodeData id "collection:data" revision "collection:data@1" { + queryable portable value version id "collection:version" : list { + get id "collection:version:get" execution snapshot targets [browser, server]; + } + operation replace id "collection:replace" : Replacement -> unit { + call id "collection:replace:call" execution transaction targets [browser, server]; + } + operation ensure id "collection:ensure" : unit -> atom-ref { + call id "collection:ensure:call" execution transaction targets [browser, server]; + } + 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]; + } + queryable relation items id "collection:items" : many atom Node { + resolve id "collection:items:resolve" execution snapshot targets [browser, server]; + } + queryable relation containers id "collection:containers" : many atom Node { + resolve id "collection:containers:resolve" execution snapshot targets [browser, server]; + } + queryable relation detail id "collection:detail" : optional-one atom Node { + resolve id "collection:detail:resolve" execution snapshot targets [browser, server]; + } +} diff --git a/test/fixtures/collections/workspace.qx b/test/fixtures/collections/workspace.qx new file mode 100644 index 0000000..8a726d5 --- /dev/null +++ b/test/fixtures/collections/workspace.qx @@ -0,0 +1,28 @@ +workspace Collections id "workspace:collections" revision "workspace:collections@1" commit "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" { + import interface NodeData; + import package CollectionRuntime; + atom Node id "collection:node"; + conform Node as NodeData id "collection:node:data" { + private state Label id "collection:label-slot" on Node : string policy register; + private edge Items id "collection:items-edge" { + atom Node projection items id "collection:items-projection" many; + atom Node projection containers id "collection:containers-projection" many; + } + private edge Detail id "collection:detail-edge" { + atom Node projection detail id "collection:detail-projection" optional-one; + atom Node projection owner id "collection:owner-projection" optional-one; + } + initialize { label to state Label; }; + bind label.get to state Label.read; + bind label.set to state Label.write; + bind items.resolve to edge Items.items.resolve; + bind containers.resolve to edge Items.containers.resolve; + bind detail.resolve to edge Detail.detail.resolve; + bind version.get to package CollectionRuntime.version with { items to edge Items.items; }; + bind replace.call to package CollectionRuntime.replace with { items to edge Items.items; }; + bind ensure.call to package CollectionRuntime.ensure with { + detail to edge Detail.detail; + node to creation Node with { NodeData from input; }; + }; + } +}