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,59 @@
# `plugin.ts` — `createWebhookPlugin` factory
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Top-level `definePlugin` consumer for the webhook plugin. Returns a typed `PluginDescriptor<WebhookPluginConfig>` 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<WebhookPluginConfig>`.
- `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.