import { compileKernelProgram } from "../execution-world/kernel.js"; import { hexDigest, type ExecutionPlan } from "../execution-world/plan.js"; import { resultInterfaces } from "../execution-world/projections.js"; import { portableValueSchema, tsType, PortableBindingError } from "./portable.js"; import type { QuerySelection } from "../query/types.js"; const conditional = (selections: readonly QuerySelection[]): boolean => selections.some((selection) => selection.conditions.length > 0 || conditional(selection.selection)); /** Closed-world app descriptors. Handler bindings and application acquisition share the same public identities. */ export function generatePortableClientBindings( plan: ExecutionPlan, options: { target: string; runtimeModule?: string }, ): string { if (hexDigest(plan.definition) !== plan.digest) throw new PortableBindingError("client", "Invalid plan digest"); const executionPlan = (compileKernelProgram(plan) as { plan: string }).plan; const workspace = plan.definition.workspace, atoms = new Map(), unavailable: { key: string; reason: string }[] = []; const seen = new Set(); for (const operation of plan.definition.operations) { if (operation.execution.kind === "remote" || !operation.availableTargets.includes(options.target)) { unavailable.push({ key: operation.key, reason: "OPERATION_NOT_PORTABLE" }); continue; } let input: string, output: string; try { input = tsType(operation.input); output = tsType(operation.output); } catch (error) { if (!(error instanceof PortableBindingError)) throw error; unavailable.push({ key: operation.key, reason: "UNSUPPORTED_OPERATION_CODEC" }); continue; } const atom = workspace.atoms.find((a) => a.id === operation.atomId)!; const iface = workspace.interfaceImports.find((i) => i.revisionId === operation.interfaceRevisionId)!; const member = iface.members.find((m) => m.operations.some((o) => o.id === operation.operationId))!; const facet = member.operations.find((o) => o.id === operation.operationId)!; const name = `${iface.displayName}.${member.displayName}.${facet.displayName}`; const alias = JSON.stringify([atom.displayName, name]); if (seen.has(alias)) throw new PortableBindingError(alias, "Ambiguous application descriptor alias"); seen.add(alias); const roots = resultInterfaces(plan, operation.key); const descriptor = { key: operation.key, plan: executionPlan, effect: operation.execution.kind, input: portableValueSchema(operation.input), output: portableValueSchema(operation.output), resultInterfaces: roots, }; const list = atoms.get(atom.displayName) ?? []; list.push( `${JSON.stringify(name)}: defineOperationDescriptor<${input}, ${output}, ${roots.map((r) => JSON.stringify(r)).join(" | ") || "never"}, ${JSON.stringify(operation.execution.kind)}>(${JSON.stringify(descriptor)})`, ); atoms.set(atom.displayName, list); } const queries = new Map(); const views = new Map(); const serverProjections = new Map(); const results = new Map(); for (const query of plan.definition.queries) { const declaration = query.query.checked.declaration; const pkg = workspace.packageImports.find((p) => p.revisionId === query.query.packageRevisionId)!; const alias = JSON.stringify([pkg.displayName, declaration.displayName]); if (seen.has(`query:${alias}`)) throw new PortableBindingError(alias, "Ambiguous query alias"); seen.add(`query:${alias}`); const addResult = () => { if (conditional(query.query.checked.selection)) { unavailable.push({ key: query.id, reason: "CONDITIONAL_RESULT_DECODER_UNSUPPORTED" }); return; } const decoders = results.get(pkg.displayName) ?? []; decoders.push( `${JSON.stringify(declaration.displayName)}: (data: Json): ${tsType(query.query.checked.output)} => decodeArgument(${JSON.stringify(portableValueSchema(query.query.checked.output))}, { codec: "camino-value-v1", usage: "argument", value: data }, { workspace: workspaceId(${JSON.stringify(workspace.workspaceId)}), limits: { bytes: ${declaration.budgets.resultBytes + 128}, nodes: 100000, depth: 64 } }) as ${tsType(query.query.checked.output)}`, ); results.set(pkg.displayName, decoders); }; if (!declaration.placement) { const operations = query.operations.map( (key) => plan.definition.operations.find((operation) => operation.key === key)!, ); if ( operations.some( (operation) => operation.execution.kind !== "snapshot" || !operation.execution.targets.includes("server"), ) ) { unavailable.push({ key: query.id, reason: "QUERY_NOT_SNAPSHOT_PROJECTION" }); continue; } const list = serverProjections.get(pkg.displayName) ?? []; list.push( JSON.stringify(declaration.displayName) + ": " + JSON.stringify({ kind: "server-projection", id: query.id, plan: executionPlan, root: declaration.root.interface, rootKind: declaration.root.kind, }) + " as const satisfies ServerProjection<" + JSON.stringify(declaration.root.interface) + ", " + JSON.stringify(declaration.root.kind) + ">", ); serverProjections.set(pkg.displayName, list); addResult(); continue; } if (declaration.placement.kind === "local") { const list = views.get(pkg.displayName) ?? []; list.push( `${JSON.stringify(declaration.displayName)}: ${JSON.stringify({ id: query.id, plan: executionPlan, source: JSON.stringify([query.query.packageRevisionId, declaration.placement.sourceQueryId]) })} as const satisfies LocalQueryDescriptor`, ); views.set(pkg.displayName, list); addResult(); continue; } const list = queries.get(pkg.displayName) ?? []; list.push( `${JSON.stringify(declaration.displayName)}: { id: ${JSON.stringify(query.id)}, plan: ${JSON.stringify(executionPlan)}, root: ${JSON.stringify(declaration.root.interface)}, rootKind: ${JSON.stringify(declaration.root.kind)} } as const satisfies ReplicaProjection<${JSON.stringify(declaration.root.interface)}, ${JSON.stringify(declaration.root.kind)}>`, ); queries.set(pkg.displayName, list); } const groups = (entries: Map) => [...entries].map(([name, values]) => `${JSON.stringify(name)}: {${values.join(",\n")}}`).join(",\n"); return `// Generated application descriptors. Regenerate; do not edit.\nimport { defineOperationDescriptor, decodeArgument, workspaceId, type Json, type PreparedChange, type ManagedReference, type ReplicaProjection, type ServerProjection, type LocalQueryDescriptor } from ${JSON.stringify(options.runtimeModule ?? "@quixos/camino-replica-core")};\nexport const planDigest = ${JSON.stringify(executionPlan)};\nexport const operations = {${groups(atoms)}} as const;\nexport const queries = {${groups(queries)}} as const;\nexport const views = {${groups(views)}} as const;\nexport const serverProjections = {${groups(serverProjections)}} as const;\nexport const results = {${groups(results)}} as const;\nexport const unavailable = ${JSON.stringify(unavailable)} as const;\n`; }