3.6 KiB
Capability Spec: api-graphql-ui-altair
Package: @suaveplan/api-graphql-ui-altair
Status: SHIPPED 2026-05-26
Every requirement carries a
**Verified by:**line.
Requirements
Requirement: Mounts Altair at the configured UI endpoint
The package SHALL expose createAltairUi(opts) returning a RouterPlugin that registers GET <uiEndpoint> (default /graphql/ui). The response MUST be the Altair HTML shell embedding the configured graphqlEndpoint, subscriptionEndpoint, initialQuery, initialHeaders, and initialEnvironments.
Scenario: default mount serves Altair at /graphql/ui
- WHEN the plugin is mounted with default
opts - THEN
GET /graphql/uireturns 200 AND the body contains the Altair shell AND the embeddedgraphqlEndpointreads/graphql
Scenario: configured initial environments flow into the shell
- WHEN
createAltairUi({ initialEnvironments: { production: { url: "https://api.example.com" } } }) - THEN the rendered HTML embeds those environments
Verified by: src/factory/create-altair-ui.test.ts::"default mount returns Altair HTML at /graphql/ui" AND src/factory/create-altair-ui.spec-conformance.test.ts::"initialEnvironments flow into embedded shell"
Requirement: Bundled assets only — no CDN fetches
Every static asset SHALL be served from this package's dist/assets/. No external host references. Cache-busting hashes embedded. Missing asset returns 404 with GRAPHQL_UI_ALTAIR_ASSET_MISSING.
Scenario: no external hosts in the shell
- WHEN the HTML body is inspected
- THEN no
srcorhrefattribute points at an external host
Scenario: missing asset returns spec'd code
- WHEN a request arrives for
<uiEndpoint>/assets/does-not-exist.js - THEN the response is 404 with
code: "GRAPHQL_UI_ALTAIR_ASSET_MISSING"
Verified by: src/assets/assets.test.ts::"rendered shell references no external hosts" AND src/assets/assets.test.ts::"missing asset returns GRAPHQL_UI_ALTAIR_ASSET_MISSING"
Requirement: CSP header on the HTML response
The response SHALL carry Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'. Inline scripts MUST NOT be permitted.
Scenario: CSP header present, scripts not 'unsafe-inline'
- WHEN the HTML response is returned
- THEN the CSP header is set AND
script-srcdoes NOT include'unsafe-inline'
Verified by: src/headers/csp.test.ts::"CSP header set with 'unsafe-inline' only for style-src"
Requirement: Endpoint-collision detection
If <uiEndpoint> is already mounted, this package SHALL refuse with GRAPHQL_UI_ALTAIR_ENDPOINT_COLLISION.
Scenario: collision refused
- WHEN another plugin has already mounted
GET /graphql/ui - THEN mount fails with
GRAPHQL_UI_ALTAIR_ENDPOINT_COLLISION
Verified by: src/factory/create-altair-ui.test.ts::"endpoint collision refused with GRAPHQL_UI_ALTAIR_ENDPOINT_COLLISION"
Requirement: OTel counter shares the GraphiQL variant's metric name
The package SHALL emit graphql.ui.requests_total counter labelled { ui: "altair" } on each UI HTML request. The metric name is deliberately identical to the GraphiQL variant's so dashboards can sum across both UIs by dropping the ui label.
Scenario: UI HTML request increments the counter with ui="altair"
- WHEN
GET /graphql/uireturns 200 - THEN an in-memory
MeterProviderobservesgraphql.ui.requests_total{ui="altair"}increment by 1
Verified by: src/instruments/instruments.test.ts::"UI HTML increments graphql.ui.requests_total with ui='altair' label"