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,85 @@
# schema-builder
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Declares `SchemaBuilder`, the fluent builder for GraphQL schema definitions. Consumers describe types, queries, mutations, and subscriptions via chained method calls; `build()` returns a fully resolved `SchemaDefinition` consumed by `defineGraphQLPlugin` (or by any future executor).
`SchemaBuilder` is the canonical authoring surface — callers should not construct `SchemaDefinition` literals by hand. The builder enforces sensible defaults (`required: false`, `isList: false`), maintains insertion order for downstream determinism, and round-trips description/interfaces metadata correctly.
## Features
- `addType` / `addInputType` / `addEnumType` / `addInterfaceType` / `addUnionType` — every GraphQL type kind except `scalar` (consumers register scalars as plain strings on field types).
- `addQuery` / `addMutation` / `addSubscription` — root-field declarations with optional `required`, `isList`, `description`, `args`.
- `hasType` / `getType` — lookup helpers.
- `typeCount` / `queryCount` / `mutationCount` — read-only counters.
- `build()` — snapshot into an independent `SchemaDefinition`.
## Usage: Basic
```ts
import { SchemaBuilder } from "@suaveplan/api-plugin-graphql";
const schema = new SchemaBuilder()
.addType("User", (t) =>
t.field("id", "ID", { required: true })
.field("name", "String", { required: true })
)
.addQuery("user", "User", {
args: [{ name: "id", type: "ID", required: true, isList: false }],
})
.build();
```
## Usage: Advanced
```ts
import { SchemaBuilder } from "@suaveplan/api-plugin-graphql";
const schema = new SchemaBuilder()
.addEnumType("Role", ["ADMIN", "USER", "GUEST"], "Authorization role")
.addInterfaceType("Node", (t) => t.field("id", "ID", { required: true }))
.addType("Account", (t) =>
t.field("id", "ID", { required: true }).field("role", "Role", { required: true }),
{ description: "User account", interfaces: ["Node"] }
)
.addUnionType("SearchHit", ["Account", "Post"], "Polymorphic search result")
.addInputType("CreateAccountInput", (t) =>
t.field("name", "String", { required: true })
.field("role", "Role", { required: true })
)
.addMutation("createAccount", "Account", {
required: true,
args: [{ name: "input", type: "CreateAccountInput", required: true, isList: false }],
})
.addSubscription("onAccountCreated", "Account", { required: true })
.build();
```
## API Reference
| Method | Returns | Notes |
|---|---|---|
| `addType(name, builderFn, options?)` | `this` | `options.description`, `options.interfaces`. |
| `addInputType(name, builderFn, description?)` | `this` | |
| `addEnumType(name, values, description?)` | `this` | `values` is copied. |
| `addInterfaceType(name, builderFn, description?)` | `this` | |
| `addUnionType(name, memberTypes, description?)` | `this` | `memberTypes` stored on `enumValues`. |
| `addQuery(name, returnType, options?)` | `this` | `required`/`isList`/`description`/`args`. |
| `addMutation(name, returnType, options?)` | `this` | Same options as queries. |
| `addSubscription(name, returnType, options?)` | `this` | Same options as queries. |
| `hasType(name)` | `boolean` | |
| `getType(name)` | `TypeDefinition \| undefined` | |
| `typeCount` / `queryCount` / `mutationCount` | `number` | |
| `build()` | `SchemaDefinition` | Returns an independent snapshot. |
## Implementation Notes
`addType` / `addInterfaceType` / `addInputType` re-construct the `TypeDefinition` from the `TypeBuilder` output to merge `description` and `interfaces` cleanly under `exactOptionalPropertyTypes` — TypeScript rejects assigning `undefined` to an optional property when that strict flag is on, so each merge spreads conditionally.
Union types reuse `enumValues` to carry their member-type name list. This matches upstream and keeps `TypeDefinition` flat — adding a separate `memberTypes` field would have introduced a new public type with no consumer benefit. Downstream code distinguishes via `kind === "union"`.
`build()` is the only method that exposes structure to callers; until then, the builder owns its internal `Map` / arrays. Each `build()` call returns fresh arrays so callers may mutate them safely or invoke `build()` multiple times during incremental construction.