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,84 @@
# `directives`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Host-supplied GraphQL directive descriptors that engine adapters install at
schema-build time. The contract layer ships two: `@auth(scope: String!)` for
per-field authorisation and `@rateLimit(rpm: Int!)` for per-field rate
limiting. Plugins reference them in SDL; engines wire the descriptor's
`handler` into the resolver pipeline.
Keeping the descriptors here (rather than in each engine adapter) means a
plugin author writes one piece of SDL and gets the same enforcement
semantics from every concrete engine. Engine adapters install the SDL via
the directive `sdl` string and dispatch to the descriptor's `handler` from
their resolver wrapper.
## Features
- `authDirective` — checks `ctx.principal.scopes` against the declared scope
and throws when missing. Locations: `FIELD_DEFINITION`.
- `rateLimitDirective` — validates the `rpm` integer bound; the actual
rate-limit accounting is the engine adapter's responsibility (the
contract body asserts the argument shape).
- `HOST_DIRECTIVES` — frozen map keyed by directive name for engine
adapter consumption.
## Usage: Basic
```ts
import { authDirective } from "@suaveplan/api-graphql/directives";
// SDL declaration:
// directive @auth(scope: String!) on FIELD_DEFINITION
//
// Used in a plugin's typeDefs:
// type Query { invoices: [Invoice!]! @auth(scope: "billing:read") }
console.log(authDirective.sdl);
```
## Usage: Advanced
Engine adapters install every host directive in one pass:
```ts
import { HOST_DIRECTIVES } from "@suaveplan/api-graphql/directives";
for (const directive of Object.values(HOST_DIRECTIVES)) {
schemaBuilder.addDirective({
name: directive.name,
sdl: directive.sdl,
run: directive.handler,
});
}
```
The `handler` is the contract-layer enforcement body. The `@auth` handler
reads `ctx.principal.scopes` (the typed `readonly Scope[]`) and raises a
plain `Error` when the required scope is absent — engine adapters wrap that
in the engine's GraphQL error type before returning it on the wire.
## API Reference
- `authDirective: DirectiveDescriptor<{ scope: string }>` — descriptor for
`@auth`.
- `rateLimitDirective: DirectiveDescriptor<{ rpm: number }>` — descriptor
for `@rateLimit`.
- `HOST_DIRECTIVES` — frozen `{ auth, rateLimit }` lookup.
## Implementation Notes
The descriptors carry **contract bodies**, not full implementations. A
production engine adapter wraps each resolver in a check that runs the
handler before invoking the resolver. For `@rateLimit`, the engine adapter
also wires the per-tenant counter; the contract-layer handler only enforces
the `rpm > 0` invariant so a misconfigured directive surfaces early.
Adding a new host directive: add a `DirectiveDescriptor`, append it to
`HOST_DIRECTIVES`, and document the SDL in the package README. The
`api-graphql-yoga` and `api-graphql-apollo` packages pick up new
descriptors automatically through the frozen map.