# plugin-integration _(no submodules)_ ## 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 | 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 | 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.