92 lines
3.1 KiB
Markdown
92 lines
3.1 KiB
Markdown
# Per-plugin Scalar UI handler
|
|
|
|
<!-- begin:auto-docs -->
|
|
_(no submodules)_
|
|
<!-- end:auto-docs -->
|
|
|
|
## Purpose
|
|
|
|
The per-plugin module exposes `createPerPluginScalarUiHandler`, the
|
|
variant the host mounts under `/docs/ui/scalar/<plugin>` so each plugin
|
|
gets its own Scalar shell pointing at a filtered OpenAPI document
|
|
(typically `/openapi.json?plugin=<name>`). It composes the standard
|
|
`createScalarUiHandler` factory but rewrites the OpenAPI URL passthrough
|
|
and fans out the OTel observation with a `{ plugin }` attribute so
|
|
dashboards summing per-plugin mounts get distinct buckets without
|
|
losing the top-line UI total.
|
|
|
|
## Features
|
|
|
|
- Defaults `uiEndpoint` to `/docs/ui/scalar/<plugin>` when the caller
|
|
does not override it — keeps the host's `/docs/ui/scalar` namespace
|
|
collision-free across N plugins.
|
|
- Embeds the host-computed filtered URL verbatim — the host is
|
|
responsible for query-string composition so this package never has to
|
|
re-implement the OpenAPI filter semantics.
|
|
- Same CSP + asset + Zod validation guarantees as
|
|
`createScalarUiHandler`.
|
|
- Per-plugin OTel attribute fan-out — every render emits both the
|
|
baseline `{ ui: "scalar" }` and `{ ui: "scalar", plugin }` observations
|
|
so dashboards can sum either way without losing the top-line total.
|
|
|
|
## Usage: Basic
|
|
|
|
```ts
|
|
import { createPerPluginScalarUiHandler } from "@suaveplan/api-openapi-ui-scalar/per-plugin";
|
|
|
|
const { handler } = createPerPluginScalarUiHandler({
|
|
plugin: "billing",
|
|
openapiUrlForPlugin: "/openapi.json?plugin=billing",
|
|
});
|
|
|
|
const response = handler({
|
|
method: "GET",
|
|
path: "/docs/ui/scalar/billing",
|
|
});
|
|
console.log(response.status); // 200
|
|
```
|
|
|
|
## Usage: Advanced
|
|
|
|
```ts
|
|
import { createPerPluginScalarUiHandler } from "@suaveplan/api-openapi-ui-scalar/per-plugin";
|
|
|
|
const bundle = createPerPluginScalarUiHandler({
|
|
plugin: "billing",
|
|
openapiUrlForPlugin: "/openapi.json?plugin=billing",
|
|
theme: "deepSpace",
|
|
assetMode: "bundled",
|
|
uiEndpoint: "/docs/billing", // override the default mount path
|
|
});
|
|
|
|
console.log(bundle.plugin); // "billing"
|
|
console.log(bundle.options.uiEndpoint); // "/docs/billing"
|
|
|
|
// The shell embeds the filtered URL exactly once.
|
|
const html = bundle.handler({
|
|
method: "GET",
|
|
path: "/docs/billing",
|
|
}).body;
|
|
console.assert(html.includes("/openapi.json?plugin=billing"));
|
|
```
|
|
|
|
## API Reference
|
|
|
|
- `CreatePerPluginScalarUiHandlerInput` — `Omit<ScalarUiOptionsInput,
|
|
"openapiUrl">` plus `{ plugin, openapiUrlForPlugin, meterProvider?,
|
|
bundledAssetProbe? }`.
|
|
- `createPerPluginScalarUiHandler(input)` —
|
|
`PerPluginScalarUiHandlerBundle`.
|
|
- `PerPluginScalarUiHandlerBundle` extends `ScalarUiHandlerBundle` with
|
|
the resolved `plugin` field.
|
|
|
|
## Implementation Notes
|
|
|
|
The per-plugin factory deliberately delegates to the base
|
|
`createScalarUiHandler` so the per-plugin path inherits every
|
|
validation + CSP + asset-resolution guarantee. The only divergences are
|
|
the rewritten OpenAPI URL, the default `uiEndpoint`, and the extra
|
|
counter observation for the per-plugin attribute. Doubling the counter
|
|
is intentional — the bare `{ ui: "scalar" }` total stays correct while
|
|
the per-plugin breakdown remains queryable from the same series.
|