# `yoga-engine.ts` ## Purpose Implements the abstract `GraphqlEngine` contract from `@suaveplan/api-graphql` over the Yoga / `graphql-js` stack. The factory `createYogaGraphqlEngine(config)` returns an engine that hosts can wire into `api-host`'s adapter slot — it is the default GraphQL engine per design doc §10.5. The engine handles the full pipeline for every inbound request: persisted- query resolution, document parse, introspection guard, depth+complexity guards, validate, execute (with wall-clock timeout), and OTel publish. `buildSchema` federates plugin contributions via `@graphql-tools/schema`'s `makeExecutableSchema`, increments `graphql.schema_rebuilds_total`, and caches the executable schema. `executeSubscription` parses + validates and returns the GraphQL subscription's `AsyncIterable`, tracking the live count via `activeSubscriptions()` so the observable gauge can sample it. ## Features - Schema federation across multiple plugin registries, with `GRAPHQL.SCHEMA_COLLISION` raised at the abstract layer. - 10 `GRAPHQL_YOGA.*` codes wired into the production paths. - Masked-errors and introspection-toggle behaviour controllable per host. - Per-request OTel counter + histogram emission with masked / unmasked `outcome` labels. - Persisted-query lookup via injectable `Map` so the host can hot-update the registry without rebuilding the engine. - Observable `subscriptions_active` gauge that reflects the live count of `executeSubscription` iterators. ## Usage: Basic ```ts import { createGraphqlRegistry } from "@suaveplan/api-graphql"; import { createYogaGraphqlEngine } from "@suaveplan/api-graphql-yoga"; const engine = createYogaGraphqlEngine(); const registry = createGraphqlRegistry(); registry.addTypes("type Query { ping: String! }"); registry.addResolvers({ Query: { ping: () => "pong" } }); engine.buildSchema(registry); const result = await engine.executeQuery( { query: "{ ping }" }, {} as never ); // result.data.ping === "pong" await engine.dispose(); ``` ## Usage: Advanced ```ts import { createYogaGraphqlEngine, federatePlugins, } from "@suaveplan/api-graphql-yoga"; import { createGraphqlRegistry } from "@suaveplan/api-graphql"; import { createInMemoryWsBroker } from "@suaveplan/api-ws-broker-in-memory"; const broker = createInMemoryWsBroker(); const billing = createGraphqlRegistry(); billing.addTypes("extend type Query { invoices: [String!]! }"); billing.addResolvers({ Query: { invoices: () => ["i_1"] } }); const engine = createYogaGraphqlEngine({ options: { maxQueryDepth: 8, maxQueryComplexity: 250 }, entries: [{ plugin: "billing", registry: billing as never }], wsBroker: broker, persistedQueries: new Map([["pq_1", "{ invoices }"]]), }); const inline = createGraphqlRegistry(); inline.addTypes("type Query { ping: String! }"); inline.addResolvers({ Query: { ping: () => "pong" } }); engine.buildSchema(inline); ``` ## API Reference - `YogaEngineConfig` — `{ options?, entries?, wsBroker?, persistedQueries? }`. - `YogaGraphqlEngine` — engine handle extending `GraphqlEngine`. - `createYogaGraphqlEngine(config?)` — factory. ## Implementation Notes `buildSchema` accepts a runtime-supplied registry alongside the pre- registered `config.entries`; the runtime registry is appended as a final `__inline__` plugin so callers that don't manage entries up-front (e.g. the conformance harness) still drive the engine through the same path. The introspection guard runs against the raw query text rather than the parsed AST so that pre-parse, pre-guard rejection paths still produce a deterministic `GRAPHQL_YOGA.INTROSPECTION_DENIED` instead of leaking parse errors. The depth/complexity guard ALWAYS runs before `validate()` so denial-of-service queries cost a parse + a walk, not a parse + validate + execute.