# `factory/create-altair-ui` — Altair UI plugin factory _(no submodules)_ ## Purpose `factory/create-altair-ui` is the package's public entry point. `createAltairUi(opts)` validates the caller's options against the Zod schema, builds the asset registry and OTel instruments, and returns a `RouterPlugin` whose `mount(router)` method registers `GET ` plus the hashed-asset routes against any `Router` instance. Endpoint collisions are detected up-front via `router.collidesWith` and reject the mount with a typed `GRAPHQL_UI_ALTAIR.ENDPOINT_COLLISION` error before any route is written — partial mounts are impossible. ## Features - Zero-config call: `createAltairUi()` works out of the box. - Returns a frozen `RouterPlugin` with `name`, `options`, `mount`, `renderHtml`, and `renderAsset` members. - HTML response always carries the strict CSP + ancillary security headers. - Asset response carries `Cache-Control: public, max-age=31536000, immutable`. - Asset misses (wrong hash, wrong name) throw `GraphqlUiAltairError` with code `ASSET_MISSING`. - Endpoint collisions throw `GraphqlUiAltairError` with code `ENDPOINT_COLLISION` before any route is registered. - Injectable internals (`assetRegistry`, `instruments`) for integration tests and operators wanting custom asset bundles. ## Usage: Basic ```ts import { createAltairUi } from "@suaveplan/api-graphql-ui-altair/factory"; import { Router } from "@suaveplan/api-router"; const router = new Router(); const altair = createAltairUi(); altair.mount(router); // router.match("GET", "/graphql/ui") now resolves to the HTML handler. ``` ## Usage: Advanced ```ts import { createAltairUi } from "@suaveplan/api-graphql-ui-altair/factory"; import { createAltairInstruments } from "@suaveplan/api-graphql-ui-altair/instruments"; import { MeterProvider } from "@opentelemetry/sdk-metrics"; import { Router } from "@suaveplan/api-router"; const meterProvider = new MeterProvider(); const instruments = createAltairInstruments({ meterProvider }); const router = new Router(); const altair = createAltairUi( { uiEndpoint: "/dev/altair", graphqlEndpoint: "/api/graphql", theme: "dark", initialEnvironments: { production: { url: "https://api.example.com/graphql", headers: { "x-tenant": "acme" }, }, }, }, { instruments } ); altair.mount(router); ``` ## API Reference - `createAltairUi(input?: AltairUiOptionsInput, internals?: CreateAltairUiInternals): RouterPlugin`. - `RouterPlugin` — `{ name, options, mount(router), renderHtml(), renderAsset(hash, name) }`. - `CreateAltairUiInternals` — `{ assetRegistry?, instruments? }`. ## Implementation Notes The factory composes five lower-level modules — options, assets, html, headers, instruments — and exposes their behaviour behind one ergonomic entry point. The `RouterPlugin.options` field is the parsed, defaulted bundle (not the caller's raw input) so downstream code reads the same value the renderer wrote. Frozen-everything posture means operators cannot accidentally mutate the plugin between `mount` calls.