Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# namespacing
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The `namespacing` module turns a plugin's structural identity (`name`,
`displayName`) plus a registered route's `(method, path)` tuple into the
deterministic OpenAPI surface fields — tag name, component-schema
prefix, normalised OAS path, and `operationId`. It is the bridge between
the plugin-namespaced runtime and the single merged OpenAPI 3.1
document `@suaveplan/api-openapi` emits.
Determinism matters: clients regenerate SDKs against the document and
expect stable `operationId`s across boots. Collisions — two plugins
producing the same id — would silently overwrite the earlier route in
client toolchains, so the module raises `OPENAPI.OPERATION_ID_COLLISION`
the moment a duplicate appears.
## Features
- `tagFor(plugin)` — prefers `displayName`, falls back to `name`.
- `componentPrefix(plugin)` — `<pluginName>` literal, used by the
emitter as the prefix for `components.schemas` entries.
- `normaliseRoutePath(path)` — rewrites Express-style `:id` parameters
into the OAS `{id}` form. Idempotent.
- `operationIdFor(plugin, method, path)` — full deterministic id:
`<plugin>.<method>.<oasPath>`.
- `assignOperationIds(inputs)` — bulk walk that respects pre-set
`operationId` values and raises a typed error on collision.
## Usage: Basic
```ts
import {
assignOperationIds,
operationIdFor,
tagFor,
} from "@suaveplan/api-openapi/namespacing";
const plugin = { name: "billing", displayName: "Billing" };
operationIdFor(plugin, "GET", "/invoices/:id");
// => "billing.get./invoices/{id}"
tagFor(plugin); // => "Billing"
```
## Usage: Advanced
```ts
import { assignOperationIds } from "@suaveplan/api-openapi/namespacing";
import type { RegisteredRoute } from "@suaveplan/api-openapi/types";
const plugins = [
{ name: "billing", displayName: "Billing" },
{ name: "users" },
];
const routes: ReadonlyArray<{
plugin: (typeof plugins)[number];
route: RegisteredRoute;
}> = [
{ plugin: plugins[0]!, route: { method: "GET", path: "/invoices/:id", tags: [] } },
{ plugin: plugins[1]!, route: { method: "GET", path: "/users/:id", tags: [] } },
];
const namespaced = assignOperationIds(routes);
// Throws OPENAPI.OPERATION_ID_COLLISION if any duplicate appears.
```
## API Reference
- `NamespacePluginInfo` — `{ name, displayName? }`.
- `tagFor(plugin) → string`.
- `componentPrefix(plugin) → string`.
- `normaliseRoutePath(path: string) → string`.
- `operationIdFor(plugin, method, path) → string`.
- `assignOperationIds(inputs) → NamespacedRoute[]`.
- `NamespacedRoute` — `{ pluginName, tag, operationId, method, path, route }`.
## Implementation Notes
- Path normalisation uses the regex `:([A-Za-z_][\w-]*)` so colons
preceded by non-identifier characters (e.g. `:443` in a server URL)
are not rewritten.
- Method is lower-cased to match OAS path-item key casing.
- Pre-set `operationId` overrides the derived one — useful for stable
ids that pre-date the convention. Collisions still raise.
- The module does not know about strictness; the emitter applies
strictness to missing schemas separately.