first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
# `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<string, string>` 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<Caps>` — engine handle extending
`GraphqlEngine<Caps>`.
- `createYogaGraphqlEngine<Caps>(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.