Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user