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,75 @@
# `guards/depth-and-complexity` — query-shape rejection
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`depth-and-complexity` walks a parsed `DocumentNode` and computes two
coarse static metrics — the maximum selection-set nesting depth and the
total field-count cost. The engine consults both before handing the
document to Apollo Server; queries that exceed `maxQueryDepth` or
`maxQueryComplexity` are rejected with the corresponding
`GRAPHQL_APOLLO.DEPTH_EXCEEDED` / `COMPLEXITY_EXCEEDED` codes.
The walk follows fragment spreads through their definitions and includes
inline-fragment subselections, so a clever query that hides depth or
cost behind a deeply nested fragment cannot bypass the guards. The
heuristic matches the Yoga sibling so the two engines reject the same
query set under identical limits.
## Features
- Depth counter increments per nested `selectionSet`, never on fragment
spreads or inline fragments themselves (matches conventional
depth-limiter implementations).
- Complexity counter adds 1 per selected field, plus the recursive cost
of its subselection, plus the expansion of fragment spreads. Missing
fragment spreads contribute zero.
- `assertDepthAndComplexity` evaluates depth first; the more localised
failure wins so operators see the rule they triggered.
- Pure-functional — no instance state, no `this`. Safe to call from any
request lifecycle phase.
## Usage: Basic
```ts
import { parse } from "graphql";
import { assertDepthAndComplexity } from "@suaveplan/api-graphql-apollo/guards";
const doc = parse("{ a { b { c } } }");
assertDepthAndComplexity(doc, { maxDepth: 10, maxComplexity: 1000 });
```
## Usage: Advanced
Run the metrics in isolation to feed an OTel histogram before the engine
even routes the document:
```ts
import { parse } from "graphql";
import {
maxQueryDepth,
queryComplexity,
} from "@suaveplan/api-graphql-apollo/guards";
const doc = parse(req.body.query);
metrics.histogram("graphql.depth").record(maxQueryDepth(doc));
metrics.histogram("graphql.complexity").record(queryComplexity(doc));
```
## API Reference
- `maxQueryDepth(doc: DocumentNode): number`
- `queryComplexity(doc: DocumentNode): number`
- `assertDepthAndComplexity(doc, { maxDepth, maxComplexity }): void` —
throws `GraphqlApolloError` on first violation.
## Implementation Notes
Fragment definitions are collected up-front into a `Map` so spread
resolution is O(1). Inline fragments share the parent depth — they do
not add a level. The complexity heuristic intentionally undercounts
list multiplicity; production deployments wire a richer estimator via a
host-supplied `costExtension` (out of scope for the foundation engine).