import { QxReject, type ExactReference, type MutationKey } from "@quixos/camino-replica-core"; import { createRegistry, generic, type Contexts } from "./gen/qx.js"; const metadata = (ref: ExactReference) => ({ ...ref, expectation: { kind: "interface" as const, id: "interface:org.quixos.presentation-state@1" as const }, }); const same = (a: ExactReference | null, b: ExactReference | null) => a === null || b === null ? a === b : a.workspace === b.workspace && a.object === b.object; // These source contracts also exist in assemblies with no closed navigation // application. Generated Contexts contains only the applications actually used. type NavigationInput = Contexts["attach"]["input"] & { occurrence: string }; type MetadataRequest = { receiver: ReturnType; input: null }; type NavigationMetadata = { "occurrence.get"(request: MetadataRequest): Promise; "source.resolve"(request: MetadataRequest): Promise; "parent.resolve"(request: MetadataRequest): Promise; "attach.call"( request: { receiver: ReturnType; input: Contexts["attach"]["input"] }, key: MutationKey, ): Promise; }; type CreateContext = { receiver: ExactReference; input: NavigationInput; ports: { metadata: NavigationMetadata; factory: { "create.call"(input: { occurrence: string }, key: MutationKey): Promise }; }; }; type EnsureContext = CreateContext & { ports: { presentationMount: { "find.call"(request: { receiver: NavigationInput["presentationMount"]; input: string; }): Promise | null>; }; }; }; async function attach({ receiver, input, ports }: Contexts["attach"]) { const owner = (await ports.owner.resolve())[0]?.target ?? null; const parent = (await ports.parent.resolve())[0]?.target ?? null; if (owner) { if (!same(owner, input.presentationMount)) throw new QxReject("PRESENTATION_STATE_ALREADY_ATTACHED"); if (!same(parent, input.parent)) throw new QxReject("OCCURRENCE_PARENT_MISMATCH"); return null; } if (parent) throw new QxReject("UNATTACHED_PRESENTATION_PARENT"); if (input.parent) { const parentOwner = await ports.metadata["presentationMount.resolve"]({ receiver: input.parent, input: null }); if (!same(parentOwner, input.presentationMount)) throw new QxReject("PRESENTATION_PARENT_MISMATCH"); await ports.parent.connect(input.parent, { key: "parent" }); } else { if (await ports.presentationMount["root.resolve"]({ receiver: input.presentationMount, input: null })) throw new QxReject("PRESENTATION_ROOT_EXISTS"); await ports.rootOf.connect(input.presentationMount, { key: "root" }); } // The parent must already be attached. No operation reparents attached state, // so attachment cannot introduce a cycle, including through pending births. await ports.presentationMount["attach.call"]( { receiver: input.presentationMount, input: metadata(receiver) }, { key: "owner" }, ); return null; } async function create({ receiver, input, ports, }: CreateContext): Promise { if (!input.occurrence || input.occurrence.length > 128) throw new QxReject("INVALID_OCCURRENCE"); const navigation = await ports.factory["create.call"]({ occurrence: input.occurrence }, { key: "navigation" }); if ((await ports.metadata["occurrence.get"]({ receiver: metadata(navigation), input: null })) !== input.occurrence) throw new QxReject("PRESENTATION_OCCURRENCE_MISMATCH"); if (!same(await ports.metadata["source.resolve"]({ receiver: metadata(navigation), input: null }), receiver)) throw new QxReject("PRESENTATION_SOURCE_MISMATCH"); await ports.metadata["attach.call"]( { receiver: metadata(navigation), input: { presentationMount: input.presentationMount, parent: input.parent } }, { key: "attach" }, ); return navigation; } export const registry = createRegistry({ copyEmpty: async ({ input, ports }) => { if (input !== null) throw new QxReject("PRESENTATION_HAS_NO_STATE"); return { ...(await ports.presentationMount.create({ key: "copy" })), expectation: { kind: "interface", id: "interface:org.quixos.presentation-mount@1" }, }; }, findEmpty: async () => null, attachEmpty: async () => { throw new QxReject("PRESENTATION_STATE_NOT_INSTALLED"); }, ...generic.allocate(allocate), copy, ...generic.create(create), find: async ({ input, ports }) => (await ports.members.resolve()).find((entry) => entry.mapKey === input)?.target ?? null, ...generic.ensure(async ({ receiver, input, ports }: EnsureContext) => { const existing = await ports.presentationMount["find.call"]({ receiver: input.presentationMount, input: input.occurrence, }); if (existing) { if (!same(await ports.metadata["source.resolve"]({ receiver: existing, input: null }), receiver)) throw new QxReject("PRESENTATION_SOURCE_MISMATCH"); if (!same(await ports.metadata["parent.resolve"]({ receiver: existing, input: null }), input.parent)) throw new QxReject("OCCURRENCE_PARENT_MISMATCH"); } else { await create({ receiver, input, ports }); } return null; }), attach, claim: async ({ receiver, input, ports }) => { const request = { receiver: input, input: null }; const owner = await ports.metadata["presentationMount.resolve"](request); if (owner) throw new QxReject("PRESENTATION_STATE_ALREADY_ATTACHED"); const parent = await ports.metadata["parent.resolve"](request); if (parent) { if (!same(await ports.metadata["presentationMount.resolve"]({ receiver: parent, input: null }), receiver)) throw new QxReject("PRESENTATION_PARENT_MISMATCH"); } else if (!same((await ports.root.resolve())[0]?.target ?? null, input)) { throw new QxReject("PRESENTATION_ROOT_MISMATCH"); } const occurrence = await ports.metadata["occurrence.get"](request); if (!occurrence || occurrence.length > 128) throw new QxReject("INVALID_OCCURRENCE"); await ports.members.connect({ target: input, mapKey: occurrence }, { key: "member" }); return null; }, createPresentationMount: async ({ ports }) => ({ ...(await ports.presentationMount.create({ key: "presentationMount" })), expectation: { kind: "interface", id: "interface:org.quixos.presentation-mount@1" }, }), }); async function allocate({ input, ports, }: { input: NavigationInput; ports: { source: { "create.call"(input: NavigationInput, key: MutationKey): Promise } }; }) { return metadata(await ports.source["create.call"](input, { key: "allocate" })); } async function copy({ receiver, input, ports }: Contexts["copy"]) { const members = (await ports.members.resolve()).map((entry) => entry.target); if (members.length > 512) throw new QxReject("PRESENTATION_COPY_LIMIT"); const originalRoot = (await ports.root.resolve())[0]?.target ?? null; const requestedRoot = input ?? originalRoot; if (!requestedRoot && members.length) throw new QxReject("PRESENTATION_ROOT_MISSING"); if (requestedRoot && !members.some((member) => same(member, requestedRoot))) throw new QxReject("PRESENTATION_COPY_ROOT_MISMATCH"); const byId = new Map(members.map((member) => [member.object, member])); const children = new Map(); for (const member of members) { const owner = await ports.metadata["presentationMount.resolve"]({ receiver: member, input: null }); if (!same(owner, receiver)) throw new QxReject("PRESENTATION_OWNER_MISMATCH"); const parent = await ports.metadata["parent.resolve"]({ receiver: member, input: null }); if (parent) { if (!byId.has(parent.object)) throw new QxReject("PRESENTATION_PARENT_MISMATCH"); const siblings = children.get(parent.object) ?? []; siblings.push(member); children.set(parent.object, siblings); } else if (!same(member, originalRoot)) throw new QxReject("PRESENTATION_ROOT_MISMATCH"); } const queue = requestedRoot ? [requestedRoot] : []; const visited = new Set(); for (let index = 0; index < queue.length; index++) { const item = queue[index]!; if (visited.has(item.object)) throw new QxReject("PRESENTATION_CYCLE"); visited.add(item.object); queue.push(...(children.get(item.object) ?? []).sort((a, b) => a.object.localeCompare(b.object))); } if (!input && queue.length !== members.length) throw new QxReject("PRESENTATION_DISCONNECTED_STATE"); const presentationMount = { ...(await ports.presentationMount.create({ key: "presentationMount" })), expectation: { kind: "interface" as const, id: "interface:org.quixos.presentation-mount@1" as const }, }; const mapping: { source: (typeof members)[number]; target: (typeof members)[number] }[] = []; const copied = new Map(); for (const [index, source] of queue.entries()) { const originalParent = await ports.metadata["parent.resolve"]({ receiver: source, input: null }); const parent = same(source, requestedRoot) ? null : copied.get(originalParent!.object)!; if (!same(source, requestedRoot) && !parent) throw new QxReject("PRESENTATION_COPY_PARENT_MISSING"); const occurrence = await ports.metadata["occurrence.get"]({ receiver: source, input: null }); const target = await ports.metadata["allocate.call"]( { receiver: source, input: { presentationMount, occurrence, parent } }, { key: "allocate:" + index }, ); copied.set(source.object, target); mapping.push({ source, target }); } for (const [index, item] of mapping.entries()) await ports.metadata["copyValues.call"]( { receiver: item.source, input: { target: item.target, mapping } }, { key: "values:" + index }, ); return presentationMount; }