first commit
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# `factory/create-graphiql-ui`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Public entry point of the package. `createGraphiqlUi(opts)` returns a
|
||||
`RouterPlugin` — a plain value with `name`, `version`, parsed `options`,
|
||||
the OTel instrument bundle, and a `register(router)` method that mounts
|
||||
two routes on a host `Router`:
|
||||
|
||||
- `GET <uiEndpoint>` — serves the GraphiQL HTML shell with the configured
|
||||
CSP header.
|
||||
- `GET <uiEndpoint>/assets/:filename` — serves bundled static assets with
|
||||
an `immutable` Cache-Control header.
|
||||
|
||||
The plugin is opt-in: the host's plugin wiring decides whether to call
|
||||
`register(router)`. Mount-time validation runs Zod options parsing AND
|
||||
checks the host router for an existing route at the same path; collision
|
||||
raises `GRAPHQL_UI_GRAPHIQL.ENDPOINT_COLLISION` so operators see it at
|
||||
boot.
|
||||
|
||||
## Features
|
||||
|
||||
- Single-call factory — `createGraphiqlUi(input)` returns a frozen
|
||||
`RouterPlugin`.
|
||||
- Endpoint-collision detection at register time via
|
||||
`router.collidesWith` plus a defence-in-depth catch of
|
||||
`ROUTER.ROUTE_COLLISION` from the `router.get` call itself.
|
||||
- Stale-hash-tolerant asset routing — the same `:filename` param matches
|
||||
every served permutation of the asset name.
|
||||
- Counter increments only on HTML hits, never on asset hits.
|
||||
- Custom OTel `MeterProvider` accepted via the optional `meterProvider`
|
||||
field.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { Router } from "@suaveplan/api-router";
|
||||
import { createGraphiqlUi } from "@suaveplan/api-graphql-ui-graphiql";
|
||||
|
||||
const router = new Router({ plugin: "host" });
|
||||
createGraphiqlUi({}).register(router);
|
||||
// GET /graphql/ui now serves the explorer.
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
||||
import { Router } from "@suaveplan/api-router";
|
||||
import { createGraphiqlUi } from "@suaveplan/api-graphql-ui-graphiql";
|
||||
|
||||
const meterProvider = new MeterProvider({ /* ... */ });
|
||||
const plugin = createGraphiqlUi({
|
||||
uiEndpoint: "/api/explorer",
|
||||
graphqlEndpoint: "/api/gql",
|
||||
subscriptionEndpoint: "/api/gql/ws",
|
||||
theme: "dark",
|
||||
defaultQuery: "{ viewer { id } }",
|
||||
defaultVariableEditorOpen: true,
|
||||
editorOptions: { lineWrapping: true },
|
||||
meterProvider,
|
||||
});
|
||||
|
||||
const router = new Router({ plugin: "host" });
|
||||
plugin.register(router);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface RouterPlugin { name; version; options; instruments;
|
||||
register(router) }`.
|
||||
- `interface CreateGraphiqlUiInput extends GraphiqlUiOptionsInput {
|
||||
meterProvider?: MeterProvider }`.
|
||||
- `interface RouterResponseValue { status; headers; body }` — the shape
|
||||
returned by the route handlers.
|
||||
- `type GraphiqlRouteHandler` — the handler signature consumed by the
|
||||
in-memory `Router`.
|
||||
- `createGraphiqlUi(input?): RouterPlugin`.
|
||||
- `listGraphiqlAssetNames(): readonly string[]`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The handler signature is intentionally adapter-agnostic — it accepts a
|
||||
plain `{ method, path }` request and returns `{ status, headers, body }`.
|
||||
Adapter routers (`api-router-hono`, worker bridges) wrap this with their
|
||||
native request context. The in-memory `Router` from `@suaveplan/api-router`
|
||||
uses the same shape directly, which is what the package's unit tests
|
||||
exercise. The `RouterError` -> `GraphqlUiGraphiqlError` translation in
|
||||
`register()` exists because `router.collidesWith` is best-effort (it
|
||||
only checks the UI endpoint, not the asset sub-route); the second
|
||||
`router.get` for assets may still throw `ROUTE_COLLISION` if some other
|
||||
plugin owns the asset prefix.
|
||||
Reference in New Issue
Block a user