/** * Copyright (c) 2026, Salesforce, Inc., * All rights reserved. * For full license text, see the LICENSE.txt file */ import { buildOutput } from "./build-output.js"; import { getSchemaWithPriming } from "./get-schema-with-priming.js"; import { type RawSpec, type RawOperation, type ToolOutput } from "./types.js"; import { applyCommand } from "../lib/apply-command.js"; import { assertGraphqlName } from "../lib/graphql-name.js"; import { type PrimeDeps } from "../lib/prime-schema.js"; import { createSession } from "../lib/session.js"; const DEFAULT_OP_NAME: Record = { query: "RawQuery", mutation: "RawMutation", aggregate: "RawAggregate", }; /** * Build an arbitrary UIAPI operation from CLI-style commands. Implements * `sf_gql_raw` — the low-level escape hatch (FR-12) for operations the typed * tools do not model (cross-union selections, custom mutations). * * `operation` selects the session root (query/mutation/aggregate); the agent * hand-drives every selection. No typed-tool sugar (no aggregations[] handling, * no auto-pagination, no Id defaulting). * * Fails fast: the first command that cannot be applied throws * `command (): `. Throws on an invalid `typeName` (must be a valid * GraphQL Name), auth-missing / introspection failure (via getSchemaWithPriming) * and on empty `commands`. Never throws on validation or codegen failure (those * surface as warnings[]). */ export async function buildRaw(spec: RawSpec, deps?: PrimeDeps): Promise { if (!spec.commands || spec.commands.length === 0) { throw new Error("buildRaw: commands must contain at least one command"); } const operation: RawOperation = spec.operation ?? "query"; const { schema, primingNote, instanceUrl } = await getSchemaWithPriming(spec.org, deps); const session = createSession(spec.org, operation, instanceUrl); // `typeName` lands only in the rendered operation-name position (buildOutput // runs codegen without a typeName option), so it carries the GraphQL Name // constraint just like the typed builders' `operationName`. session.operationName = spec.typeName ?? DEFAULT_OP_NAME[operation]; assertGraphqlName(session.operationName, "buildRaw", "typeName"); spec.commands.forEach((cmd, i) => { try { applyCommand(session, schema, cmd); } catch (err) { const msg = err instanceof Error ? err.message : String(err); throw new Error(`command ${i} (${cmd}): ${msg}`); } }); return buildOutput(session, schema, primingNote); }