3.1 KiB
Per-plugin Scalar UI handler
(no submodules)
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
uiEndpointto/docs/ui/scalar/<plugin>when the caller does not override it — keeps the host's/docs/ui/scalarnamespace 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
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
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.PerPluginScalarUiHandlerBundleextendsScalarUiHandlerBundlewith the resolvedpluginfield.
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.