Files
2026-05-27 23:29:53 +01:00

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/ui returns 200 AND the body contains the Altair shell AND the embedded graphqlEndpoint reads /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 src or href attribute 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-src does 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/ui returns 200
  • THEN an in-memory MeterProvider observes graphql.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"