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,125 @@
# plugin-integration
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`definePluginRoutes` is the single helper plugins call from their `onInit`
to register routes. Every call atomically updates both the Hono
`OpenAPIHono` app (via `app.openapi(route, handler)`) and the
`OpenAPIRegistry` (via `registry.registerRoute(...)`). The two surfaces
cannot drift — there is no path through the helper that touches only
one of them.
Eliminates a class of upstream bugs where a plugin would either
register against Hono but forget the registry (operation served but
not discoverable) or the inverse (discoverable but not served).
## Features
- Atomically registers each route against both Hono and the registry.
- Records `pluginName` on every registry entry for later `registry.getRoutesByPlugin(name)` lookups.
- Carries `operationId`, `summary`, `description`, `deprecated`, and `security` through into the registry record.
- Returns the list of `RegisteredRoute` entries for caller-side post-processing.
- Defensive-copies `tags` and `security` arrays so the route config remains immutable from the registry's perspective.
## Usage: Basic
```ts
import { OpenAPIHono, z } from "@hono/zod-openapi";
import { createOpenAPIRegistry } from "@suaveplan/api-openapi/registry";
import { GET, successResponse } from "@suaveplan/api-openapi/route-factory";
import { definePluginRoutes } from "@suaveplan/api-openapi/plugin-integration";
const app = new OpenAPIHono();
const registry = createOpenAPIRegistry({
info: { title: "X", version: "1.0.0" },
});
const ListUsers = GET("/users")
.tags("users")
.operationId("listUsers")
.response(successResponse(z.array(z.object({ id: z.string() }))))
.build();
definePluginRoutes(
[
{
route: ListUsers,
handler: (c) => c.json({ success: true as const, data: [] }, 200),
},
],
{ app, registry, pluginName: "users" }
);
```
## Usage: Advanced
```ts
import { definePluginRoutes } from "@suaveplan/api-openapi/plugin-integration";
import { definePlugin } from "@suaveplan/api-plugin";
interface MyServices {
app: OpenAPIHono;
registry: OpenAPIRegistry;
}
export const projectsPlugin = definePlugin<{
/* config shape */
}>({
name: "projects",
version: "0.1.0",
onInit(ctx) {
const app = ctx.getService("app") as MyServices["app"];
const registry = ctx.getService("registry") as MyServices["registry"];
const created = definePluginRoutes(
[
/* { route, handler } entries built with RouteBuilder */
],
{ app, registry, pluginName: "projects" }
);
ctx.logger.info(`projects: registered ${created.length} routes`);
},
});
```
## API Reference
- `type RouteHandler = (c: Context) => Response | Promise<Response> | unknown`
- `interface PluginRouteEntry { route: RouteConfig; handler: RouteHandler }`
- `interface DefinePluginRoutesOptions { app: OpenAPIHono; registry: OpenAPIRegistry; pluginName: string }`
- `function definePluginRoutes(entries, options): RegisteredRoute[]`
## Implementation Notes
The helper iterates the entries sequentially — there is no parallelism.
Hono's `app.openapi` is synchronous and side-effecting, so concurrency
would gain nothing. Sequential iteration also gives well-defined
ordering in `registry.getRoutesByPlugin(name)`: the array reflects the
order routes were declared in the plugin source, which is the order
authors expect when consuming the registry for documentation or
introspection.
The `RegisteredRoute` record copies the route's `tags` and `security`
arrays defensively. A plugin could otherwise mutate the array on its
`RouteConfig` post-registration and silently alter the registry's
introspection view.
`route.method` is passed through to the registry as-is. Hono normalises
methods to lowercase, so the registry receives lowercase, matching the
expectations of `OpenAPIRegistry.buildDocument()`.
The helper is intentionally a free function (not a method on the
registry). Decoupling it from the registry class means plugins can
substitute their own "register-both-surfaces" implementation in tests
without touching the registry contract.
The handler type is `(c: Context) => Response | Promise<Response> | unknown`
— Hono's actual handler signature is broader (typed under generics
that depend on the specific RouteConfig). Trying to surface that
inference at the helper boundary would require every caller to thread
the same generics, defeating the helper's purpose. We accept the loose
signature and trust the call site of `app.openapi` to type-check the
handler against the route's contract.