# `plugin.ts` — `createWebhookPlugin` factory _(no submodules)_ ## Purpose Top-level `definePlugin` consumer for the webhook plugin. Returns a typed `PluginDescriptor` whose `onInit` executes the canonical five-step lifecycle: resolve `app` / `openAPIRegistry` from the host `ServiceRegistry`, construct the `WebhookService` wired to the configured (or default) collaborators, register the service under the `webhookService` slot, mount the six CRUD + test routes under `routePrefix` (gated by `requireAuth()`), and register the routes with the `OpenAPIRegistry` when one is present so `/openapi.json` exposes them. ## Features - Zod-validates options at the factory boundary; invalid input raises `WebhookError(WEBHOOK_INVALID_INPUT)` carrying the `ZodError` as `cause`. - Wires defaults for every optional collaborator (`MemoryEndpointStore`, `MemoryDeadLetterSink`, system clock, global `fetch`, noop logger) so `createWebhookPlugin()` works zero-config. - Raises `WebhookError(WEBHOOK_SERVICE_NOT_AVAILABLE)` if the required `app` slot is absent. - Raises `WebhookError(WEBHOOK_ROUTE_PREFIX_COLLISION)` when the `routePrefix` collides with previously-registered routes. - Falls back to a manual Hono sub-router mount when no `OpenAPIRegistry` is present so the runtime surface still works even when the OpenAPI doc is not assembled. ## Usage: Basic ```ts import { defineApp } from "@suaveplan/api"; import { createWebhookPlugin } from "@suaveplan/api-plugin-webhook"; const app = defineApp().use(createWebhookPlugin()); ``` ## Usage: Advanced ```ts import { defineApp } from "@suaveplan/api"; import { createAuthPlugin } from "@suaveplan/api-plugin-auth"; import { createWebhookPlugin } from "@suaveplan/api-plugin-webhook"; import { MemoryEndpointStore } from "@suaveplan/api-plugin-webhook/store/memory"; import { MemoryDeadLetterSink } from "@suaveplan/api-plugin-webhook/dead-letter/memory"; const app = defineApp() .use(createAuthPlugin({ betterAuth: {} })) .use( createWebhookPlugin({ routePrefix: "/api/v1/webhooks", endpointStore: new MemoryEndpointStore(), deadLetterSink: new MemoryDeadLetterSink(), maxAttempts: 8, initialRetryDelayMs: 250, maxRetryDelayMs: 60_000, }) ); ``` ## API Reference - `createWebhookPlugin(options?)` — factory. Returns a `PluginDescriptor`. - `WebhookPluginOptions` — every field optional; defaults supplied for omitted fields. - `WebhookPluginConfig` — post-default-merge shape carried on the descriptor's `defaults`. ## Implementation Notes The plugin name `webhook` is singular (not `webhooks`) so dependent plugins reference one stable identifier when declaring `dependencies: ["webhook"]`. The lifecycle is one-shot — there is no `onClose` hook because the service does not hold network resources (the in-flight dedup map drains naturally as deliveries resolve). The route mount path joins `routePrefix` + entry path so the generated OpenAPI paths read as `/webhooks`, `/webhooks/{id}`, `/webhooks/{id}/test`; the special-case `routePrefix === "/"` keeps the root namespace clean for apps that want webhook CRUD at the top level.