Files
suaveplan/openspec/archive/2026-05-26-api-graphql-ui-altair/proposal.md
2026-05-27 23:29:53 +01:00

51 lines
3.0 KiB
Markdown

# `@suaveplan/api-graphql-ui-altair` — Altair UI mount at `/graphql/ui`
**Package**: `@suaveplan/api-graphql-ui-altair`
**Status**: PROPOSED
**Type**: new-package
**Stage**: 7 (GraphQL + WebSocket / SSE engines) — see umbrella `openspec/changes/api-plugin-foundation/`
## Why
Per design doc §10.6 Altair is the explicit alternative GraphQL UI alongside GraphiQL: "GET /graphql/ui — GraphiQL (or Altair via `api-graphql-ui-altair`)". Altair has a richer feature set — environment management, response history, query-saving collections — preferred by teams who do heavy in-browser API development. Operators wire one OR the other; the engine adapters (`api-graphql-yoga`, `api-graphql-apollo`) return 404 at `/graphql/ui` when neither is wired.
This package is the structural sibling of `api-graphql-ui-graphiql` — same plugin shape, same mount semantics, different bundled UI.
## What changes
Net-new package depending on `@suaveplan/api-types`, `@suaveplan/api-router`, `@suaveplan/api-graphql`, `@suaveplan/error`, `@suaveplan/logger`, and `altair-static` (peer; bundled assets).
- ADDED: `createAltairUi(opts)` — factory returning a `RouterPlugin` value mounting `GET <uiEndpoint>`.
- ADDED: `AltairUiOptions` — Zod-validated config: `uiEndpoint` (default `/graphql/ui`), `graphqlEndpoint` (default `/graphql`), `subscriptionEndpoint` (default `/graphql/ws`), `theme` (`"light"` | `"dark"` | `"system"`), `initialQuery`, `initialHeaders`, `initialEnvironments` (Altair-specific feature).
- ADDED: Static-asset serving from package's own `dist/assets/` (no CDN).
- ADDED: CSP headers — same posture as the GraphiQL variant.
- ADDED: `GRAPHQL_UI_ALTAIR_*` error codes: `GRAPHQL_UI_ALTAIR_ASSET_MISSING`, `GRAPHQL_UI_ALTAIR_ENDPOINT_COLLISION`.
- ADDED: OTel instrument: `graphql.ui.requests_total { ui: "altair" }` — same metric name as the GraphiQL variant, only the label differs, so dashboards can sum across both UIs.
## Out of scope
- A CDN-fetched Altair variant.
- The GraphiQL alternative.
- Co-mounting both UIs on the same path — host config refuses if both are wired against the same `uiEndpoint`.
## Source mapping
Genesis-original. Source of design intent: `docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md`.
| Requirement | Design doc section |
|---|---|
| Altair as alternative at `/graphql/ui` | §10.6 |
| UI as opt-in, separate package | §10.6 |
| Bundled assets only | §10.6 + foundation security posture |
## Verification approach
- `src/factory/create-altair-ui.test.ts` — factory returns `RouterPlugin`; HTML response embeds configured endpoints; CSP header.
- `src/factory/create-altair-ui.spec-conformance.test.ts` — same conformance shape as the GraphiQL spec — endpoint mount + embedded-config.
- `src/assets/assets.test.ts` — no external host references; missing asset raises `GRAPHQL_UI_ALTAIR_ASSET_MISSING`.
- `tests/integration/altair-end-to-end.test.ts` (30000ms) — boots `api-graphql-yoga` + this UI plugin, Playwright opens `/graphql/ui`, runs a query, asserts results render.
## Migration impact
None — additive only.