first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
# @suaveplan/api-graphql-ui-altair — Altair GraphQL UI plugin for the Suaveplan API plugin system. Mounts the Altair shell at GET /graphql/ui via createAltairUi(opts), serves bundled (no-CDN) assets from a hashed static map, embeds graphqlEndpoint / subscriptionEndpoint / initialQuery / initialHeaders / initialEnvironments into the shell, attaches a strict CSP (no script 'unsafe-inline'), increments graphql.ui.requests_total{ ui: "altair" } via an injectable MeterProvider, and refuses endpoint collisions with the GRAPHQL_UI_ALTAIR_ENDPOINT_COLLISION code. Structural sibling of api-graphql-ui-graphiql.
Altair GraphQL UI plugin for the Suaveplan API plugin
system. Structural sibling of `@suaveplan/api-graphql-ui-graphiql`.
<!-- begin:auto-docs -->
## Documentation tree
- [assets](./src/assets/assets.md) — `assets` ships the Altair shell as a static-asset map keyed by logical filename. Every entry carries a content-type, th…
- [assets](./src/assets/assets.md) — `assets` ships the Altair shell as a static-asset map keyed by logical filename. Every entry carries a content-type, th…
- [error-codes](./src/error-codes/error-codes.md) — `error-codes` is the package's single source of truth for the two stable error codes the Altair UI plugin emits. Centra…
- [error-codes](./src/error-codes/error-codes.md) — `error-codes` is the package's single source of truth for the two stable error codes the Altair UI plugin emits. Centra…
- [factory](./src/factory/create-altair-ui.md) — `factory/create-altair-ui` is the package's public entry point. `createAltairUi(opts)` validates the caller's options a…
- [create-altair-ui](./src/factory/create-altair-ui.md) — `factory/create-altair-ui` is the package's public entry point. `createAltairUi(opts)` validates the caller's options a…
- [headers](./src/headers/csp.md) — `headers/csp` produces the Content-Security-Policy and the two ancillary security headers (`X-Content-Type-Options`, `R…
- [csp](./src/headers/csp.md) — `headers/csp` produces the Content-Security-Policy and the two ancillary security headers (`X-Content-Type-Options`, `R…
- [html](./src/html/render-shell.md) — `html/render-shell` produces the single HTML document the Altair UI plugin serves at `<uiEndpoint>`. The shell mounts a…
- [render-shell](./src/html/render-shell.md) — `html/render-shell` produces the single HTML document the Altair UI plugin serves at `<uiEndpoint>`. The shell mounts a…
- [instruments](./src/instruments/instruments.md) — `instruments` owns the package's single OpenTelemetry counter, `graphql.ui.requests_total`, labelled with the constant…
- [instruments](./src/instruments/instruments.md) — `instruments` owns the package's single OpenTelemetry counter, `graphql.ui.requests_total`, labelled with the constant…
- [options](./src/options/options.md) — `options` owns the Zod-validated configuration surface every consumer of the Altair UI plugin passes through. The schem…
- [options](./src/options/options.md) — `options` owns the Zod-validated configuration surface every consumer of the Altair UI plugin passes through. The schem…
<!-- end:auto-docs -->
## What this package is
- **Opt-in.** The host serves nothing at `/graphql/ui` unless this
plugin (or its GraphiQL sibling) is wired against the router.
- **Bundled assets only — no CDN.** Every asset the HTML shell loads
ships inside this package's `dist/` output. The plugin works in
air-gapped, offline-first environments.
- **Alternative to GraphiQL, not co-existing.** Operators wire one or
the other against `/graphql/ui`. Both at once is refused by host
config (the engine adapters return 404 there when neither is wired).
## Quick start
```ts
import { createAltairUi } from "@suaveplan/api-graphql-ui-altair";
import { Router } from "@suaveplan/api-router";
const router = new Router();
const altair = createAltairUi();
altair.mount(router);
// router.match("GET", "/graphql/ui") resolves to the Altair HTML handler.
```
## Configuration
```ts
createAltairUi({
uiEndpoint: "/graphql/ui", // default
graphqlEndpoint: "/graphql", // default
subscriptionEndpoint: "/graphql/ws", // default
theme: "system", // "light" | "dark" | "system"
initialQuery: "",
initialHeaders: { "x-tenant": "acme" },
initialEnvironments: {
production: {
url: "https://api.example.com/graphql",
subscriptionsEndpoint: "wss://api.example.com/graphql",
headers: { "x-env": "prod" },
variables: { region: "us-east-1" },
},
},
});
```
## Security posture
- Strict Content-Security-Policy on every HTML response.
- `script-src 'self'` — no inline scripts.
- `style-src 'self' 'unsafe-inline'` — required for the Altair shell.
- `frame-ancestors 'none'` — clickjacking defence.
- `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`.
- Static assets carry `Cache-Control: public, max-age=31536000, immutable`.
## Observability
Emits one OTel counter: `graphql.ui.requests_total` with label
`ui="altair"`. The metric name is deliberately identical to the
GraphiQL sibling so dashboards can sum across both UIs by dropping
the `ui` label.
## Error codes
- `GRAPHQL_UI_ALTAIR.ASSET_MISSING` — request for a static asset the
bundled registry doesn't recognise (404).
- `GRAPHQL_UI_ALTAIR.ENDPOINT_COLLISION` — `mount()` called against a
router that already serves `GET <uiEndpoint>` (typically the
GraphiQL sibling is wired against the same path).
## License
MIT

View File

@@ -0,0 +1,85 @@
# `assets` — Bundled asset registry, resolver, CDN-clean check
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`assets` ships the Altair shell as a static-asset map keyed by logical
filename. Every entry carries a content-type, the payload bytes, and a
short SHA-256 hash derived once at module init so cache-bust URLs are
stable across deploys with unchanged payloads. The shell is shipped
inline (no FS reads at request time, no CDN fetches) which preserves
the Altair plugin's hermetic-bundle guarantee — the package can boot in
air-gapped, offline-first environments and still serve a usable UI.
## Features
- Default registry: `altair.js` + `altair.css`.
- `defineAsset(name, contentType, body)` returns a frozen `BundledAsset`.
- `resolveAsset(registry, uiEndpointBase, name)` produces a
`ResolvedAsset` with a hash-busted URL.
- Missing names throw `GraphqlUiAltairError` (`ASSET_MISSING`).
- `findExternalHostReferences` is the CDN-clean conformance check.
- Hash derivation is SHA-256, truncated to 16 hex chars — same
cardinality as a `git rev-parse --short=16`.
## Usage: Basic
```ts
import {
buildDefaultAssetRegistry,
resolveAsset,
} from "@suaveplan/api-graphql-ui-altair/assets";
const registry = buildDefaultAssetRegistry();
const jsAsset = resolveAsset(registry, "/graphql/ui", "altair.js");
console.log(jsAsset.url); // /graphql/ui/assets/<hash>/altair.js
```
## Usage: Advanced
```ts
import {
buildDefaultAssetRegistry,
defineAsset,
findExternalHostReferences,
} from "@suaveplan/api-graphql-ui-altair/assets";
// Extend the registry with an operator-specific asset.
const registry = new Map(buildDefaultAssetRegistry());
registry.set(
"logo.svg",
defineAsset(
"logo.svg",
"image/svg+xml",
'<svg xmlns="http://www.w3.org/2000/svg" />'
)
);
const offenders = findExternalHostReferences(registry);
if (offenders.length > 0) {
throw new Error("CDN leak: " + offenders.join(", "));
}
```
## API Reference
- `BundledAsset` — `{ name, contentType, body, hash }`.
- `ResolvedAsset extends BundledAsset` — adds `url`.
- `defineAsset(name, contentType, body): BundledAsset`.
- `buildDefaultAssetRegistry(): ReadonlyMap<string, BundledAsset>`.
- `resolveAsset(registry, uiEndpointBase, name): ResolvedAsset` — throws
`GraphqlUiAltairError` (`ASSET_MISSING`) on misses.
- `findExternalHostReferences(registry): readonly string[]`.
## Implementation Notes
The asset bodies are held as JavaScript template literals so the build
emits them as plain string constants — no FS access at boot, no
`process.cwd()` dependence. The hash is computed via `node:crypto` to
match the same hashing the rest of the project's static-asset
pipeline uses; the truncation to 16 hex chars is intentional — a
collision in a single deploy's asset set is vanishingly unlikely and
the shorter URL is easier on the eyes in production logs.

View File

@@ -0,0 +1,83 @@
# `error-codes` — `GRAPHQL_UI_ALTAIR.*` registry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`error-codes` is the package's single source of truth for the two stable
error codes the Altair UI plugin emits. Centralising the codes here lets
the factory, the asset map, and any downstream observability tooling
import the literal strings from one location — no string-typos, no
divergence between the wire format and the registry. The registry is
frozen at module init and validated against the umbrella `defineCodes`
contract from `@suaveplan/error` so accidental drift is caught at type
check time, not at runtime.
## Features
- Two codes: `GRAPHQL_UI_ALTAIR.ASSET_MISSING`,
`GRAPHQL_UI_ALTAIR.ENDPOINT_COLLISION`.
- Concrete `GraphqlUiAltairError` class extending `SuaveplanError`.
- `altairCode` narrows to `GraphqlUiAltairErrorCode` for exhaustive
switch statements.
- Compatible with the umbrella redaction + i18n + OTel span-event
adapters shipped by `@suaveplan/error`.
## Usage: Basic
```ts
import {
GRAPHQL_UI_ALTAIR_CODES,
GraphqlUiAltairError,
} from "@suaveplan/api-graphql-ui-altair/error-codes";
throw new GraphqlUiAltairError(
GRAPHQL_UI_ALTAIR_CODES.ASSET_MISSING,
"Asset not present in bundled map",
{ context: { path: "/graphql/ui/assets/missing.js" } }
);
```
## Usage: Advanced
```ts
import {
GRAPHQL_UI_ALTAIR_CODES,
GraphqlUiAltairError,
} from "@suaveplan/api-graphql-ui-altair/error-codes";
function handle(err: unknown): number {
if (err instanceof GraphqlUiAltairError) {
switch (err.altairCode) {
case GRAPHQL_UI_ALTAIR_CODES.ASSET_MISSING:
return 404;
case GRAPHQL_UI_ALTAIR_CODES.ENDPOINT_COLLISION:
return 409;
}
}
return 500;
}
```
## API Reference
- `GRAPHQL_UI_ALTAIR_CODES` — frozen record `{ ASSET_MISSING, ENDPOINT_COLLISION }`.
- `GraphqlUiAltairErrorCode` — discriminated union of the two literal
strings.
- `class GraphqlUiAltairError extends SuaveplanError<GraphqlUiAltairErrorCode>`
— owns the `altairCode` accessor.
## Implementation Notes
`defineCodes("GRAPHQL_UI_ALTAIR", ...)` produces the prefixed literal
strings — agents must not hand-build the strings to avoid prefix drift.
The error class delegates entirely to `SuaveplanError` for context
serialisation, cause chains, and the wire-format adapter; the only
local responsibility is preserving `altairCode` for `instanceof`
narrowing in operator-facing error handlers. The two-code surface is
intentionally minimal: the asset path is the only failure mode at HTTP
boundary, and endpoint collision is the only failure mode at mount
boundary. Adding a code requires an OpenSpec change so the registry
shape never drifts silently between the spec and the runtime.

View File

@@ -0,0 +1,88 @@
# `factory/create-altair-ui` — Altair UI plugin factory
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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 <uiEndpoint>`
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.

View File

@@ -0,0 +1,78 @@
# `headers/csp` — Strict CSP for the Altair shell
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`headers/csp` produces the Content-Security-Policy and the two ancillary
security headers (`X-Content-Type-Options`, `Referrer-Policy`) the
Altair plugin attaches to its HTML response. The policy is deliberately
strict — no `'unsafe-inline'` on `script-src` — so a regression that
sneaks an inline script into the shell template breaks loud at policy
enforcement, not at audit time. The same posture is used by the
GraphiQL sibling so dashboards and auditors can compare both packages
on equal terms.
## Features
- Single `Content-Security-Policy` string with eight directives.
- `'unsafe-inline'` is permitted ONLY on `style-src` — required for
Altair's shell styling, never for scripts.
- `frame-ancestors 'none'` denies embedding (clickjacking defence).
- Ancillary headers: `X-Content-Type-Options: nosniff`,
`Referrer-Policy: no-referrer`.
- Header bundle returned frozen so downstream middleware can't mutate
it by accident.
## Usage: Basic
```ts
import { buildAltairResponseHeaders } from "@suaveplan/api-graphql-ui-altair/headers";
const headers = buildAltairResponseHeaders();
// headers["Content-Security-Policy"] === "default-src 'self'; ..."
```
## Usage: Advanced
```ts
import {
ALTAIR_CSP_HEADER_NAME,
ALTAIR_CSP_HEADER_VALUE,
buildAltairResponseHeaders,
} from "@suaveplan/api-graphql-ui-altair/headers";
// Drop onto a Fetch-style Response.
function respond(body: string): Response {
const headers = new Headers(buildAltairResponseHeaders());
headers.set("Content-Type", "text/html; charset=utf-8");
return new Response(body, { status: 200, headers });
}
// Direct access for static header injection (e.g. CDN edge config).
console.log(`${ALTAIR_CSP_HEADER_NAME}: ${ALTAIR_CSP_HEADER_VALUE}`);
```
## API Reference
- `buildAltairResponseHeaders(): Readonly<Record<string,string>>`
- `ALTAIR_CSP_HEADER_NAME: "Content-Security-Policy"` literal.
- `ALTAIR_CSP_HEADER_VALUE: string` — the eight-directive policy.
## Implementation Notes
The CSP value is assembled as a constant string at module init — there
are no parameters today (a nonce'd variant would land here if the
shell template were converted off inline `<style>` and would consume a
caller-supplied nonce). The function return is wrapped in
`Object.freeze` to surface accidental mutation during integration
tests. Eight directives keep the policy short enough to read in a
single browser-devtools row yet rich enough to defeat the standard
opportunistic-injection vectors (mixed content, inline JS, dangling
`<form>` hijack, third-party `<base>` retargeting). Operators wanting
a stricter posture — e.g. `script-src 'none'` for a read-only audit
demo — can wrap the function and substitute their own value before it
hits the response builder; the package never reads the CSP string
back, so there is no integrity-check loop to satisfy.

View File

@@ -0,0 +1,81 @@
# `html/render-shell` — Altair HTML shell builder
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`html/render-shell` produces the single HTML document the Altair UI
plugin serves at `<uiEndpoint>`. The shell mounts a `<div id="altair-root">`,
embeds the resolved JS/CSS asset URLs (hash-busted, so CDN cache
behaviour is correct), and serialises the parsed `AltairUiOptions` as a
`<script type="application/json">` data block — never as executable
inline JavaScript — so the strict CSP attached by `headers/csp` can
forbid `script-src 'unsafe-inline'` outright.
## Features
- One self-contained HTML document — no external host references.
- Configurable endpoints surfaced both as `<meta>` tags AND as `data-*`
attributes on the root container, so consumers integrating Altair
via custom JS can read either layer.
- JSON config block escaped against `</script>` injection per the HTML5
spec recommendation.
- User-supplied endpoint values HTML-escaped before embedding.
- Theme attribute (`data-altair-theme`) on `<html>` for CSS hooks.
## Usage: Basic
```ts
import { renderAltairShell } from "@suaveplan/api-graphql-ui-altair/html";
import { buildDefaultAssetRegistry } from "@suaveplan/api-graphql-ui-altair/assets";
import { parseAltairUiOptions } from "@suaveplan/api-graphql-ui-altair/options";
const html = renderAltairShell({
options: parseAltairUiOptions(),
assetRegistry: buildDefaultAssetRegistry(),
});
```
## Usage: Advanced
```ts
import { renderAltairShell } from "@suaveplan/api-graphql-ui-altair/html";
import {
buildDefaultAssetRegistry,
defineAsset,
} from "@suaveplan/api-graphql-ui-altair/assets";
import { parseAltairUiOptions } from "@suaveplan/api-graphql-ui-altair/options";
const registry = new Map(buildDefaultAssetRegistry());
registry.set(
"logo.svg",
defineAsset("logo.svg", "image/svg+xml", "<svg/>")
);
const html = renderAltairShell({
options: parseAltairUiOptions({
theme: "dark",
initialEnvironments: {
production: { url: "https://api.example.com/graphql" },
},
}),
assetRegistry: registry,
});
```
## API Reference
- `RenderAltairShellInput` — `{ options: AltairUiOptions; assetRegistry: ReadonlyMap<string, BundledAsset> }`.
- `renderAltairShell(input): string` — returns the full HTML document
including the `<!DOCTYPE html>` declaration.
## Implementation Notes
The `</script>` escape inside the JSON config block uses the HTML5
spec's recommended sequence `<\/script` so the JSON itself remains
parseable while the closing-tag-injection vector is closed. All
user-supplied strings flowing into HTML attributes pass through a
four-character HTML escape — the structural goal is "no inline script
ever executes", which the strict CSP then enforces at the browser.

View File

@@ -0,0 +1,67 @@
# `instruments` — OTel counter for Altair UI requests
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`instruments` owns the package's single OpenTelemetry counter,
`graphql.ui.requests_total`, labelled with the constant
`ui="altair"`. The counter name is deliberately identical to the
GraphiQL sibling's so dashboards summing across both UIs can drop the
`ui` label and add the two series. The `MeterProvider` is injectable —
operators can hand in their own provider for in-process testing or rely
on the OTel global the rest of the host has registered at boot.
## Features
- Single counter — `graphql.ui.requests_total`.
- Constant label preset: `{ ui: "altair" }`.
- `recordRequest()` convenience captures the label internally so call
sites never repeat the label literal (no drift risk).
- Meter name pinned to `@suaveplan/api-graphql-ui-altair` for trace
correlation.
- Falls back to the OTel global `MeterProvider` when none is supplied.
## Usage: Basic
```ts
import { createAltairInstruments } from "@suaveplan/api-graphql-ui-altair/instruments";
const inst = createAltairInstruments();
inst.recordRequest();
```
## Usage: Advanced
```ts
import { MeterProvider } from "@opentelemetry/sdk-metrics";
import { createAltairInstruments } from "@suaveplan/api-graphql-ui-altair/instruments";
const meterProvider = new MeterProvider({
/* readers / exporters configured in the host */
});
const inst = createAltairInstruments({ meterProvider });
// Inside the HTML response handler:
inst.recordRequest(); // graphql.ui.requests_total{ui="altair"} += 1
```
## API Reference
- `createAltairInstruments(config?: AltairInstrumentsConfig): AltairInstruments`.
- `AltairInstruments` — `{ meter, requestsTotal, recordRequest }`,
frozen.
- `GRAPHQL_UI_REQUESTS_COUNTER` — literal `"graphql.ui.requests_total"`.
- `ALTAIR_UI_LABEL` — literal `"altair"`.
- `ALTAIR_METER_NAME` — literal `"@suaveplan/api-graphql-ui-altair"`.
## Implementation Notes
The counter description block documents the cross-package label contract
so operators reading the OTel discovery endpoint understand why two
packages emit the same metric name. The `Object.freeze` on the return
record prevents accidental mutation downstream — the bundle is meant to
be a singleton per host instance.

View File

@@ -0,0 +1,74 @@
# `options` — `AltairUiOptions` schema + parser
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`options` owns the Zod-validated configuration surface every consumer of
the Altair UI plugin passes through. The schema enforces structural
invariants — every endpoint string must start with `/`, theme is one of
three literal values, top-level keys are strict so typos surface at the
boundary — while still defaulting every field so the most common call
site (`createAltairUi()`) is zero-config. Centralising defaulting in one
schema means the factory, the HTML renderer, and the asset map all
consume the same shape.
## Features
- Defaults: `uiEndpoint=/graphql/ui`, `graphqlEndpoint=/graphql`,
`subscriptionEndpoint=/graphql/ws`, `theme=system`, `initialQuery=""`,
empty `initialHeaders`, empty `initialEnvironments`.
- Strict mode at every nesting level — unknown keys are rejected.
- Rich per-environment block: `url`, `subscriptionsEndpoint`, `headers`,
`variables`.
- `parseAltairUiOptions(undefined)` is valid and produces the default
bundle.
## Usage: Basic
```ts
import { parseAltairUiOptions } from "@suaveplan/api-graphql-ui-altair/options";
const opts = parseAltairUiOptions();
// opts.uiEndpoint === "/graphql/ui"
```
## Usage: Advanced
```ts
import { parseAltairUiOptions } from "@suaveplan/api-graphql-ui-altair/options";
const opts = parseAltairUiOptions({
uiEndpoint: "/dev/ui",
graphqlEndpoint: "/api/graphql",
theme: "dark",
initialEnvironments: {
production: {
url: "https://api.example.com/graphql",
subscriptionsEndpoint: "wss://api.example.com/graphql",
headers: { "x-tenant": "acme" },
variables: { region: "us-east-1" },
},
},
});
```
## API Reference
- `altairUiOptionsSchema: z.ZodObject` — direct Zod handle (composable
via `.extend()` or `.partial()` for downstream wrappers).
- `parseAltairUiOptions(input?: AltairUiOptionsInput): AltairUiOptions`
— throws `ZodError` on shape violations.
- `AltairUiOptions` — output type (every field defined).
- `AltairUiOptionsInput` — input type (every field optional).
- `AltairEnvironment` — per-environment record shape.
## Implementation Notes
The Zod default record literals (`{}`) feed back into `z.input` as
optional, so the input type stays ergonomic at the caller boundary
while the output type guarantees every field is populated downstream.
The schema uses the Zod 4 default-import (`import z from "zod"`) per
the repo-wide Bun-safe convention.