Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@suaveplan/api-graphql-ui-graphiql — GraphiQL UI mount for the Suaveplan API plugin system. Exposes createGraphiqlUi() — a factory returning a RouterPlugin that registers GET /graphql/ui (or any configured endpoint) and serves a self-contained HTML shell + bundled assets with a strict CSP. Default GraphQL explorer when operators want GraphiQL; sibling api-graphql-ui-altair covers the Altair alternative.
GraphiQL UI mount for the Suaveplan API plugin system. Stage 7 of the umbrella plan.
createGraphiqlUi(opts) returns a RouterPlugin that registers
GET /graphql/ui (or any configured endpoint) on a host
@suaveplan/api-router Router. The endpoint serves a self-contained
HTML shell plus bundled JS/CSS assets — no CDN fetches.
Documentation tree
- assets — Bundled static-asset registry for the GraphiQL UI plugin. The HTML shell references two assets (
graphiql.js, `graphiq…- assets — Bundled static-asset registry for the GraphiQL UI plugin. The HTML shell references two assets (
graphiql.js, `graphiq…
- assets — Bundled static-asset registry for the GraphiQL UI plugin. The HTML shell references two assets (
- error-codes — Owns the stable
GRAPHQL_UI_GRAPHIQL.*error-code registry consumed by the GraphiQL UI plugin. Every code in the regis…- error-codes — Owns the stable
GRAPHQL_UI_GRAPHIQL.*error-code registry consumed by the GraphiQL UI plugin. Every code in the regis…
- error-codes — Owns the stable
- factory — Public entry point of the package.
createGraphiqlUi(opts)returns aRouterPlugin— a plain value withname, `vers…- create-graphiql-ui — Public entry point of the package.
createGraphiqlUi(opts)returns aRouterPlugin— a plain value withname, `vers…
- create-graphiql-ui — Public entry point of the package.
- headers — Owns the
Content-Security-Policyheader values emitted on the GraphiQL HTML response. The policy is hard-coded to a p…- csp — Owns the
Content-Security-Policyheader values emitted on the GraphiQL HTML response. The policy is hard-coded to a p…
- csp — Owns the
- html — Renders the GraphiQL HTML shell. The output embeds the configured GraphQL endpoint, subscription endpoint, theme, and e…
- render-shell — Renders the GraphiQL HTML shell. The output embeds the configured GraphQL endpoint, subscription endpoint, theme, and e…
- instruments — Owns the OpenTelemetry instrument bundle emitted by the GraphiQL UI plugin. A single
Counter(`graphql.ui.requests_to…- instruments — Owns the OpenTelemetry instrument bundle emitted by the GraphiQL UI plugin. A single
Counter(`graphql.ui.requests_to…
- instruments — Owns the OpenTelemetry instrument bundle emitted by the GraphiQL UI plugin. A single
- options — Zod-validated configuration surface for
createGraphiqlUi. OwnsGraphiqlUiOptions(resolved shape with every default…- options — Zod-validated configuration surface for
createGraphiqlUi. OwnsGraphiqlUiOptions(resolved shape with every default…
- options — Zod-validated configuration surface for
Why opt-in
Per the design doc (docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md
§10.6) the API foundation reserves GET /graphql/ui for an interactive
explorer but does not bundle one by default — UI choice is operator-
chosen so the runtime image stays minimal for production deployments
that don't need it.
Operators wire ONE of:
@suaveplan/api-graphql-ui-graphiql— canonical GraphiQL (this package).@suaveplan/api-graphql-ui-altair— Altair alternative.
The GraphQL engine adapters (api-graphql-yoga, api-graphql-apollo)
return 404 at /graphql/ui when no UI plugin is wired.
Quick start
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 -> the explorer
// GET /graphql/ui/assets/graphiql.<hash>.js -> bundled JS
// GET /graphql/ui/assets/graphiql.<hash>.css -> bundled CSS
Configuration
| Option | Default | Notes |
|---|---|---|
uiEndpoint |
/graphql/ui |
Must start with /. |
graphqlEndpoint |
/graphql |
Embedded into the explorer config. |
subscriptionEndpoint |
/graphql/ws |
Embedded into the explorer config. |
theme |
"system" |
"light" / "dark" / "system". |
defaultQuery |
"" |
HTML-escaped before embedding. |
defaultVariableEditorOpen |
false |
Opens the variables pane on load. |
editorOptions |
{} |
CodeMirror passthroughs. |
meterProvider |
OTel global | OTel MeterProvider for the counter. |
Security posture
- Bundled assets only. Every
src/hrefin the rendered HTML points inside the package's own/<uiEndpoint>/assets/namespace. Cross-checked bysrc/html/render-shell.test.ts. - Strict CSP.
default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self' data:; connect-src 'self'. Inline scripts are NOT permitted. Cross-checked bysrc/headers/csp.test.ts. - Cache-immutable assets. Bundled JS/CSS carries
Cache-Control: public, max-age=31536000, immutable. - Stale-hash tolerance. Hashed filenames resolve any matching stem+ext to the current asset bytes — rolling deploys don't 404 in-flight sessions.
OTel
graphql.ui.requests_total { ui: "graphiql" }— counter incremented on each successful HTML response. Asset hits are NOT counted.
Error codes
GRAPHQL_UI_GRAPHIQL.ASSET_MISSING— asset lookup failure.GRAPHQL_UI_GRAPHIQL.ENDPOINT_COLLISION—<uiEndpoint>already registered by another plugin.
Tests
bunx --bun turbo lint typecheck test:coverage --filter=@suaveplan/api-graphql-ui-graphiql
100% statements / branches / functions / lines required on touched
modules (provider: istanbul).