@xtandard/webhooks 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +4 -4
- package/dist/cli.mjs +4 -4
- package/dist/{core-CMpnmI5Q.mjs → core-C9mPi4iw.mjs} +20 -3
- package/dist/core-C9mPi4iw.mjs.map +1 -0
- package/dist/{core-ZGhH6Vs2.cjs → core-CF8U0hgi.cjs} +20 -3
- package/dist/core-CF8U0hgi.cjs.map +1 -0
- package/dist/core.cjs +1 -1
- package/dist/core.mjs +1 -1
- package/dist/{create-fetch-handler-CmooujQo.cjs → create-fetch-handler-BILaZ2Z7.cjs} +6 -5
- package/dist/{create-fetch-handler-CmooujQo.cjs.map → create-fetch-handler-BILaZ2Z7.cjs.map} +1 -1
- package/dist/{create-fetch-handler-jy3hy5nZ.d.mts → create-fetch-handler-BN9vXbgW.d.mts} +7 -3
- package/dist/{create-fetch-handler-BIdk9P30.mjs → create-fetch-handler-D0F3cjoq.mjs} +6 -5
- package/dist/{create-fetch-handler-BIdk9P30.mjs.map → create-fetch-handler-D0F3cjoq.mjs.map} +1 -1
- package/dist/{create-fetch-handler-Dlkhustu.d.cts → create-fetch-handler-D9ZRfrY6.d.cts} +7 -3
- package/dist/{dispatcher-B0xTEHt1.cjs → dispatcher-DOOCJJxM.cjs} +3 -3
- package/dist/{dispatcher-B0xTEHt1.cjs.map → dispatcher-DOOCJJxM.cjs.map} +1 -1
- package/dist/{dispatcher-Coubwrka.mjs → dispatcher-DuBrQL46.mjs} +3 -3
- package/dist/{dispatcher-Coubwrka.mjs.map → dispatcher-DuBrQL46.mjs.map} +1 -1
- package/dist/entry-bun.cjs +1 -1
- package/dist/entry-bun.d.cts +1 -1
- package/dist/entry-bun.d.mts +1 -1
- package/dist/entry-bun.mjs +1 -1
- package/dist/entry-elysia.cjs +1 -1
- package/dist/entry-elysia.d.cts +1 -1
- package/dist/entry-elysia.d.mts +1 -1
- package/dist/entry-elysia.mjs +1 -1
- package/dist/entry-express.cjs +1 -1
- package/dist/entry-express.d.cts +1 -1
- package/dist/entry-express.d.mts +1 -1
- package/dist/entry-express.mjs +1 -1
- package/dist/entry-hono.cjs +1 -1
- package/dist/entry-hono.d.cts +1 -1
- package/dist/entry-hono.d.mts +1 -1
- package/dist/entry-hono.mjs +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- package/dist/testing.cjs +2 -2
- package/dist/testing.mjs +2 -2
- package/package.json +1 -1
- package/dist/core-CMpnmI5Q.mjs.map +0 -1
- package/dist/core-ZGhH6Vs2.cjs.map +0 -1
package/dist/{create-fetch-handler-CmooujQo.cjs.map → create-fetch-handler-BILaZ2Z7.cjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-fetch-handler-CmooujQo.cjs","names":["durationToMs","HookDeniedError","ReadonlyError","NotFoundError","IdempotencyConflictError","ConflictError","PayloadTooLargeError","ValidationError","createWebhooksCore","hasDeliveryQueue","isCompareAndSwap","createDispatcher"],"sources":["../src/portal.ts","../src/server/base-path.ts","../src/server/cors.ts","../src/server/openapi.ts","../src/server/render-index-html.ts","../src/server/routes.ts","../src/server/static-assets.ts","../src/server/create-fetch-handler.ts"],"sourcesContent":["/**\n * Portal tokens — the credential behind the embeddable consumer portal.\n *\n * A portal token is a compact HMAC-signed grant scoping its bearer to exactly\n * **one application** for a limited time. The host mints tokens server-side\n * with {@link createPortalToken} (the portal secret never reaches a browser)\n * and hands them to its frontend; the panel handler verifies them with\n * {@link verifyPortalToken} and force-scopes authorization to the token's\n * application (see the `portal` panel option).\n *\n * Wire format:\n *\n * ```txt\n * whpt_<base64url(JSON { app, exp })>.<base64url(HMAC-SHA256(secret, payloadPart))>\n * ```\n *\n * Zero dependencies (Web Crypto only). Unlike Standard Webhooks signing\n * secrets, the portal secret is an **arbitrary string** — it is fed to the\n * HMAC as raw UTF-8 bytes, not base64-decoded.\n *\n * @module\n */\n\nimport { durationToMs } from \"./duration.ts\";\nimport type { WebhookDuration } from \"./schema.ts\";\n\n/** Prefix of every portal token. */\nexport const PORTAL_TOKEN_PREFIX = \"whpt_\";\n\nconst DEFAULT_EXPIRES_IN: WebhookDuration = \"7d\";\n\n/**\n * Thrown by {@link verifyPortalToken} for any invalid token — malformed,\n * bad signature, or expired. Maps to HTTP 401 at the API layer.\n */\nexport class PortalTokenError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PortalTokenError\";\n }\n}\n\n/** Options for {@link createPortalToken}. */\nexport interface PortalTokenOptions {\n /** Token lifetime. Default `\"7d\"`. */\n expiresIn?: WebhookDuration;\n}\n\n/** The claims carried inside a portal token. */\ninterface PortalTokenClaims {\n /** The application the token grants access to. */\n app: string;\n /** Expiry, unix epoch milliseconds. */\n exp: number;\n}\n\nconst base64urlEncode = (bytes: Uint8Array): string => {\n let binary = \"\";\n for (const b of bytes) binary += String.fromCharCode(b);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n};\n\nconst base64urlDecode = (value: string): Uint8Array => {\n const padded = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded + \"=\".repeat((4 - (padded.length % 4)) % 4));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n};\n\n/** HMAC-SHA256 keyed by the raw UTF-8 bytes of the (arbitrary-string) secret. */\nasync function hmac(secret: string, content: string): Promise<Uint8Array> {\n const key = await crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(secret) as BufferSource,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const digest = await crypto.subtle.sign(\"HMAC\", key, new TextEncoder().encode(content));\n return new Uint8Array(digest);\n}\n\n/** Constant-time byte comparison (XOR accumulate — no early exit on content). */\nfunction timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= (a[i] as number) ^ (b[i] as number);\n return diff === 0;\n}\n\n/**\n * Mint a portal token granting access to `applicationKey` until `expiresIn`\n * elapses (default 7 days). Call this **server-side** — anyone holding the\n * portal secret can mint tokens for any application.\n *\n * @example\n * ```ts\n * import { createPortalToken } from \"@xtandard/webhooks\";\n *\n * // In the host app's session-guarded route:\n * const token = await createPortalToken(process.env.PORTAL_SECRET!, \"acme\", {\n * expiresIn: \"1h\",\n * });\n * // Hand `token` to the frontend for <WebhooksPortal token={token} />.\n * ```\n */\nexport async function createPortalToken(\n secret: string,\n applicationKey: string,\n options: PortalTokenOptions = {},\n): Promise<string> {\n const expiresInMs = durationToMs(options.expiresIn ?? DEFAULT_EXPIRES_IN);\n const claims: PortalTokenClaims = { app: applicationKey, exp: Date.now() + expiresInMs };\n const payloadPart = base64urlEncode(new TextEncoder().encode(JSON.stringify(claims)));\n const signature = base64urlEncode(await hmac(secret, payloadPart));\n return `${PORTAL_TOKEN_PREFIX}${payloadPart}.${signature}`;\n}\n\n/**\n * Verify a portal token: format, signature (constant time), then expiry.\n * Returns the granted application key on success; throws\n * {@link PortalTokenError} otherwise.\n *\n * @example\n * ```ts\n * import { verifyPortalToken } from \"@xtandard/webhooks\";\n *\n * const { applicationKey } = await verifyPortalToken(secret, token);\n * ```\n */\nexport async function verifyPortalToken(\n secret: string,\n token: string,\n): Promise<{ applicationKey: string }> {\n if (!token.startsWith(PORTAL_TOKEN_PREFIX)) {\n throw new PortalTokenError(\"Invalid portal token: missing whpt_ prefix\");\n }\n const parts = token.slice(PORTAL_TOKEN_PREFIX.length).split(\".\");\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n throw new PortalTokenError(\"Invalid portal token: malformed\");\n }\n const [payloadPart, signaturePart] = parts as [string, string];\n\n // Signature first: never interpret unauthenticated claims.\n let candidate: Uint8Array;\n try {\n candidate = base64urlDecode(signaturePart);\n } catch {\n throw new PortalTokenError(\"Invalid portal token: malformed signature\");\n }\n const expected = await hmac(secret, payloadPart);\n if (!timingSafeEqual(expected, candidate)) {\n throw new PortalTokenError(\"Invalid portal token: signature mismatch\");\n }\n\n let claims: PortalTokenClaims;\n try {\n claims = JSON.parse(\n new TextDecoder().decode(base64urlDecode(payloadPart)),\n ) as PortalTokenClaims;\n } catch {\n throw new PortalTokenError(\"Invalid portal token: malformed payload\");\n }\n if (typeof claims.app !== \"string\" || claims.app.length === 0) {\n throw new PortalTokenError(\"Invalid portal token: missing application\");\n }\n if (typeof claims.exp !== \"number\" || !Number.isFinite(claims.exp)) {\n throw new PortalTokenError(\"Invalid portal token: missing expiry\");\n }\n if (Date.now() >= claims.exp) {\n throw new PortalTokenError(\"Portal token has expired\");\n }\n return { applicationKey: claims.app };\n}\n","/**\n * Base-path helpers. The panel can be mounted under any prefix (`/webhooks`,\n * `/admin/webhooks`, ...). These normalize the configured base path and strip it\n * from incoming request paths so routing is prefix-agnostic.\n *\n * @module\n */\n\n/** Normalize a base path to either `\"\"` (root) or `/segment` with no trailing slash. */\nexport function normalizeBasePath(basePath: string | undefined): string {\n if (!basePath || basePath === \"/\") return \"\";\n let p = basePath.trim();\n if (!p.startsWith(\"/\")) p = \"/\" + p;\n if (p.endsWith(\"/\")) p = p.slice(0, -1);\n return p;\n}\n\n/**\n * Strip the (normalized) base path from a pathname. Returns a path that always\n * starts with `/`. If the pathname does not start with the base path it is\n * returned unchanged (the host framework may already have stripped the mount).\n */\nexport function stripBasePath(pathname: string, basePath: string): string {\n if (basePath === \"\") return pathname || \"/\";\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(basePath + \"/\")) {\n const rest = pathname.slice(basePath.length);\n return rest || \"/\";\n }\n return pathname || \"/\";\n}\n","/**\n * Framework-independent CORS for the mounted panel. When `webhooksPanel` /\n * `createFetchHandler` is given a `cors` option, the handler answers `OPTIONS`\n * preflights itself and attaches `Access-Control-*` headers to every response —\n * so a **cross-origin embed** (portal on `app.example.com`, panel on\n * `api.example.com`) works even if the host framework's CORS middleware does not\n * wrap the mounted sub-handler.\n *\n * @module\n */\n\n/** CORS configuration for the panel handler. */\nexport interface WebhooksCorsOptions {\n /**\n * Allowed origin(s): a single origin (`\"https://app.example.com\"`), `\"*\"`, a\n * list, or a predicate `(origin) => boolean`. With `credentials: true`, `\"*\"`\n * is echoed back as the caller's exact origin (the browser forbids `*` +\n * credentials).\n */\n origin: string | string[] | ((origin: string) => boolean);\n /** Send `Access-Control-Allow-Credentials: true` (needed for cookie auth). Default `false`. */\n credentials?: boolean;\n /** Allowed methods advertised in preflight. Default `GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS`. */\n methods?: string[];\n /**\n * Allowed request headers advertised in preflight. Default: reflect the\n * request's `Access-Control-Request-Headers`, falling back to\n * `Content-Type, Authorization`.\n */\n headers?: string[];\n /** `Access-Control-Max-Age` (seconds) for the preflight cache. */\n maxAge?: number;\n}\n\nconst DEFAULT_METHODS = \"GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS\";\n\n/**\n * Resolve the value for `Access-Control-Allow-Origin` for this request, or\n * `null` when the origin is not allowed (no CORS headers → the browser blocks).\n */\nfunction resolveOrigin(cors: WebhooksCorsOptions, requestOrigin: string | null): string | null {\n const { origin } = cors;\n if (origin === \"*\") {\n // `*` + credentials is invalid; echo the caller's origin instead.\n return cors.credentials ? (requestOrigin ?? null) : \"*\";\n }\n if (requestOrigin === null) return null;\n const allowed =\n typeof origin === \"string\"\n ? origin === requestOrigin\n : Array.isArray(origin)\n ? origin.includes(requestOrigin)\n : origin(requestOrigin);\n return allowed ? requestOrigin : null;\n}\n\n/** Attach `Access-Control-*` headers to `response` for an allowed cross-origin request. */\nexport function applyCorsHeaders(\n request: Request,\n response: Response,\n cors: WebhooksCorsOptions,\n): Response {\n const requestOrigin = request.headers.get(\"origin\");\n const allowOrigin = resolveOrigin(cors, requestOrigin);\n if (allowOrigin === null) return response;\n response.headers.set(\"access-control-allow-origin\", allowOrigin);\n if (cors.credentials) response.headers.set(\"access-control-allow-credentials\", \"true\");\n // The response varies by Origin unless we blanket-allow `*` — tell caches.\n if (allowOrigin !== \"*\") response.headers.append(\"vary\", \"Origin\");\n return response;\n}\n\n/** Build the `204` response for a CORS preflight (`OPTIONS`). */\nexport function preflightResponse(request: Request, cors: WebhooksCorsOptions): Response {\n const response = new Response(null, { status: 204 });\n applyCorsHeaders(request, response, cors);\n response.headers.set(\n \"access-control-allow-methods\",\n (cors.methods ?? []).join(\",\") || DEFAULT_METHODS,\n );\n const requestedHeaders = request.headers.get(\"access-control-request-headers\");\n response.headers.set(\n \"access-control-allow-headers\",\n cors.headers?.join(\",\") || requestedHeaders || \"Content-Type, Authorization\",\n );\n if (cors.maxAge !== undefined) {\n response.headers.set(\"access-control-max-age\", String(cors.maxAge));\n }\n return response;\n}\n","/**\n * OpenAPI 3.1 description of the admin JSON API.\n *\n * Exposed two ways, mirroring the flags panel:\n * - served at `GET {basePath}/api/openapi.json` for standalone tools (Scalar,\n * Swagger UI, Postman, codegen);\n * - returned by `createFetchHandler(...).openapi()` so you can MERGE it into\n * your host app's OpenAPI document (e.g. Elysia `@elysiajs/openapi`\n * `references`, or Hono's OpenAPI).\n *\n * Hand-authored — no generation dependency (flags precedent).\n *\n * @module\n */\n\n/** Options for {@link buildOpenApiDocument}. */\nexport interface OpenApiOptions {\n /** Mount prefix used in the `servers` url (e.g. `\"/webhooks\"`). */\n basePath?: string;\n /** Document title. */\n title?: string;\n /** Document version (defaults to the package's API version). */\n version?: string;\n}\n\nconst schemas = {\n Application: {\n type: \"object\",\n required: [\"key\"],\n properties: {\n key: { type: \"string\", pattern: \"^[a-zA-Z0-9._-]+$\" },\n name: { type: \"string\" },\n metadata: {},\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n },\n },\n EventType: {\n type: \"object\",\n required: [\"name\"],\n properties: {\n name: { type: \"string\", pattern: \"^[a-zA-Z0-9._-]+$\" },\n description: { type: \"string\" },\n groupName: { type: \"string\" },\n schema: { description: \"JSON Schema documenting the payload.\" },\n deprecated: { type: \"boolean\" },\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n },\n },\n EndpointSecret: {\n type: \"object\",\n required: [\"secret\", \"createdAt\"],\n properties: {\n secret: { type: \"string\", description: '\"whsec_\" + base64 key material.' },\n createdAt: { type: \"string\", format: \"date-time\" },\n expiresAt: {\n type: \"string\",\n format: \"date-time\",\n description: \"Set on rotation; the secret stops signing after this instant.\",\n },\n },\n },\n Endpoint: {\n type: \"object\",\n required: [\"id\", \"url\"],\n description:\n \"Read routes strip `secrets`; only the create/rotate responses and the dedicated /secret route carry them.\",\n properties: {\n id: { type: \"string\" },\n url: { type: \"string\" },\n description: { type: \"string\" },\n eventTypes: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Subscribed event types; empty/absent = all.\",\n },\n disabled: { type: \"boolean\" },\n disabledReason: { type: \"string\", enum: [\"manual\", \"auto\"] },\n headers: { type: \"object\", additionalProperties: { type: \"string\" } },\n secrets: { type: \"array\", items: { $ref: \"#/components/schemas/EndpointSecret\" } },\n metadata: {},\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n firstFailingAt: { type: \"string\", format: \"date-time\", nullable: true },\n },\n },\n Message: {\n type: \"object\",\n required: [\"id\", \"eventType\", \"payload\", \"timestamp\", \"createdAt\"],\n properties: {\n id: { type: \"string\", description: \"Sent as the webhook-id header.\" },\n eventType: { type: \"string\" },\n payload: {},\n timestamp: { type: \"string\", format: \"date-time\" },\n idempotencyKey: { type: \"string\" },\n envelope: { type: \"string\", description: \"The serialized wire envelope.\" },\n createdAt: { type: \"string\", format: \"date-time\" },\n },\n },\n Delivery: {\n type: \"object\",\n required: [\"id\", \"applicationKey\", \"messageId\", \"endpointId\", \"status\", \"attemptCount\"],\n properties: {\n id: { type: \"string\" },\n applicationKey: { type: \"string\" },\n messageId: { type: \"string\" },\n endpointId: { type: \"string\" },\n status: { type: \"string\", enum: [\"pending\", \"delivering\", \"succeeded\", \"failed\"] },\n attemptCount: { type: \"integer\" },\n nextAttemptAt: { type: \"string\", format: \"date-time\", nullable: true },\n leaseUntil: { type: \"string\", format: \"date-time\", nullable: true },\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n },\n },\n DeliveryAttempt: {\n type: \"object\",\n required: [\"id\", \"deliveryId\", \"attemptNumber\", \"at\", \"durationMs\", \"ok\", \"trigger\"],\n properties: {\n id: { type: \"string\" },\n deliveryId: { type: \"string\" },\n attemptNumber: { type: \"integer\" },\n at: { type: \"string\", format: \"date-time\" },\n durationMs: { type: \"number\" },\n ok: { type: \"boolean\" },\n httpStatus: { type: \"integer\" },\n error: { type: \"string\" },\n responseBody: { type: \"string\" },\n trigger: { type: \"string\", enum: [\"schedule\", \"manual\", \"test\"] },\n },\n },\n PublishResult: {\n type: \"object\",\n required: [\"message\", \"deliveries\", \"deduplicated\"],\n properties: {\n message: { $ref: \"#/components/schemas/Message\" },\n deliveries: { type: \"array\", items: { $ref: \"#/components/schemas/Delivery\" } },\n deduplicated: {\n type: \"boolean\",\n description: \"True when the idempotency key matched an existing message.\",\n },\n },\n },\n RecoverResult: {\n type: \"object\",\n required: [\"deliveryIds\"],\n properties: { deliveryIds: { type: \"array\", items: { type: \"string\" } } },\n },\n AuditEntry: {\n type: \"object\",\n required: [\"action\", \"at\"],\n properties: {\n action: { type: \"string\" },\n at: { type: \"string\", format: \"date-time\" },\n by: { type: \"object\", nullable: true },\n applicationKey: { type: \"string\" },\n subjectId: { type: \"string\" },\n message: { type: \"string\" },\n },\n },\n Config: {\n type: \"object\",\n properties: {\n title: { type: \"string\" },\n basePath: { type: \"string\" },\n readonly: { type: \"boolean\" },\n authenticated: { type: \"boolean\" },\n principal: { type: \"object\", nullable: true },\n portal: {\n type: \"boolean\",\n description: \"True when the request carried a valid portal token.\",\n },\n logoUrl: { type: \"string\" },\n },\n },\n Error: {\n type: \"object\",\n required: [\"error\"],\n properties: {\n error: { type: \"string\" },\n code: { type: \"string\" },\n errors: { type: \"array\", items: { type: \"object\" } },\n },\n },\n} as const;\n\nconst APP = { name: \"app\", in: \"path\", required: true, schema: { type: \"string\" } } as const;\nconst ID = { name: \"id\", in: \"path\", required: true, schema: { type: \"string\" } } as const;\n\nconst jsonBody = (schema: object, required = true) => ({\n required,\n content: { \"application/json\": { schema } },\n});\nconst jsonRes = (desc: string, schema: object) => ({\n description: desc,\n content: { \"application/json\": { schema } },\n});\nconst ref = (name: string) => ({ $ref: `#/components/schemas/${name}` });\nconst errorRes = (desc: string) => jsonRes(desc, ref(\"Error\"));\nconst okRef = (name: string, desc = name) => jsonRes(desc, ref(name));\nconst arrayOf = (name: string) => ({ type: \"array\", items: ref(name) });\n\n/** Build the OpenAPI 3.1 document for the admin API. Pure — safe to call anywhere. */\nexport function buildOpenApiDocument(options: OpenApiOptions = {}): Record<string, unknown> {\n const base = options.basePath && options.basePath !== \"/\" ? options.basePath : \"\";\n const endpointPath = \"/api/applications/{app}/endpoints/{id}\";\n\n return {\n openapi: \"3.1.0\",\n info: {\n title: options.title ?? \"@xtandard/webhooks Admin API\",\n version: options.version ?? \"0.1.0\",\n description:\n \"Admin/control-plane API for @xtandard/webhooks: applications, the global event-type catalog, endpoints (with Standard Webhooks signing secrets), message publishing, and delivery observability. Portal tokens (Authorization: Bearer whpt_…) hit the same surface scoped to one application.\",\n },\n servers: [{ url: base || \"/\" }],\n tags: [\n { name: \"meta\" },\n { name: \"applications\" },\n { name: \"event-types\" },\n { name: \"endpoints\" },\n { name: \"messages\" },\n { name: \"deliveries\" },\n { name: \"audit\" },\n ],\n security: [{ basicAuth: [] }, { bearerAuth: [] }],\n paths: {\n \"/config\": {\n get: {\n tags: [\"meta\"],\n security: [],\n summary: \"Bootstrap config (title, basePath, readonly, auth state, portal flag)\",\n responses: { \"200\": okRef(\"Config\") },\n },\n },\n \"/api/openapi.json\": {\n get: {\n tags: [\"meta\"],\n security: [],\n summary: \"This OpenAPI document\",\n responses: { \"200\": jsonRes(\"OpenAPI 3.1 document\", { type: \"object\" }) },\n },\n },\n \"/api/event-types.json\": {\n get: {\n tags: [\"meta\"],\n security: [],\n summary: \"Public event-type catalog (unauthenticated, CORS-open)\",\n responses: { \"200\": jsonRes(\"Event types\", arrayOf(\"EventType\")) },\n },\n },\n \"/api/applications\": {\n get: {\n tags: [\"applications\"],\n summary: \"List applications\",\n responses: { \"200\": jsonRes(\"Applications\", arrayOf(\"Application\")) },\n },\n post: {\n tags: [\"applications\"],\n summary: \"Create an application\",\n requestBody: jsonBody(ref(\"Application\")),\n responses: {\n \"201\": okRef(\"Application\", \"Created\"),\n \"409\": errorRes(\"Key already exists\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n \"/api/applications/{app}\": {\n parameters: [APP],\n get: {\n tags: [\"applications\"],\n summary: \"Get an application\",\n responses: { \"200\": okRef(\"Application\"), \"404\": errorRes(\"Not found\") },\n },\n put: {\n tags: [\"applications\"],\n summary: \"Update an application\",\n requestBody: jsonBody({\n type: \"object\",\n properties: { name: { type: \"string\" }, metadata: {} },\n }),\n responses: { \"200\": okRef(\"Application\", \"Updated\"), \"404\": errorRes(\"Not found\") },\n },\n delete: {\n tags: [\"applications\"],\n summary: \"Delete an application and everything under it\",\n responses: {\n \"200\": jsonRes(\"Deleted\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/event-types\": {\n get: {\n tags: [\"event-types\"],\n summary: \"List event types\",\n responses: { \"200\": jsonRes(\"Event types\", arrayOf(\"EventType\")) },\n },\n post: {\n tags: [\"event-types\"],\n summary: \"Create (upsert) an event type\",\n requestBody: jsonBody(ref(\"EventType\")),\n responses: {\n \"201\": okRef(\"EventType\", \"Created\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n \"/api/event-types/{name}\": {\n parameters: [{ name: \"name\", in: \"path\", required: true, schema: { type: \"string\" } }],\n get: {\n tags: [\"event-types\"],\n summary: \"Get an event type\",\n responses: { \"200\": okRef(\"EventType\"), \"404\": errorRes(\"Not found\") },\n },\n put: {\n tags: [\"event-types\"],\n summary: \"Update an event type\",\n requestBody: jsonBody(ref(\"EventType\")),\n responses: { \"200\": okRef(\"EventType\", \"Updated\"), \"422\": errorRes(\"Validation error\") },\n },\n delete: {\n tags: [\"event-types\"],\n summary: \"Delete an event type (endpoints referencing it stop matching)\",\n responses: {\n \"200\": jsonRes(\"Deleted\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/endpoints\": {\n parameters: [APP],\n get: {\n tags: [\"endpoints\"],\n summary: \"List endpoints (secrets stripped)\",\n responses: { \"200\": jsonRes(\"Endpoints\", arrayOf(\"Endpoint\")) },\n },\n post: {\n tags: [\"endpoints\"],\n summary: \"Create an endpoint — the response includes the signing secret ONCE\",\n requestBody: jsonBody({\n type: \"object\",\n required: [\"url\"],\n properties: {\n url: { type: \"string\" },\n description: { type: \"string\" },\n eventTypes: { type: \"array\", items: { type: \"string\" } },\n headers: { type: \"object\", additionalProperties: { type: \"string\" } },\n metadata: {},\n disabled: { type: \"boolean\" },\n },\n }),\n responses: {\n \"201\": okRef(\"Endpoint\", \"Created (includes secrets)\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n [endpointPath]: {\n parameters: [APP, ID],\n get: {\n tags: [\"endpoints\"],\n summary: \"Get an endpoint (secrets stripped)\",\n responses: { \"200\": okRef(\"Endpoint\"), \"404\": errorRes(\"Not found\") },\n },\n put: {\n tags: [\"endpoints\"],\n summary: \"Update an endpoint\",\n requestBody: jsonBody({\n type: \"object\",\n properties: {\n url: { type: \"string\" },\n description: { type: \"string\" },\n eventTypes: { type: \"array\", items: { type: \"string\" } },\n headers: { type: \"object\", additionalProperties: { type: \"string\" } },\n metadata: {},\n },\n }),\n responses: {\n \"200\": okRef(\"Endpoint\", \"Updated\"),\n \"404\": errorRes(\"Not found\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n delete: {\n tags: [\"endpoints\"],\n summary: \"Delete an endpoint\",\n responses: {\n \"200\": jsonRes(\"Deleted\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/secret`]: {\n parameters: [APP, ID],\n get: {\n tags: [\"endpoints\"],\n summary: \"Read the endpoint's signing secrets (current first)\",\n responses: {\n \"200\": jsonRes(\"Secrets\", arrayOf(\"EndpointSecret\")),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/rotate-secret`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Mint a new secret; the previous one keeps signing through the grace window\",\n responses: {\n \"200\": okRef(\"Endpoint\", \"Rotated (includes secrets)\"),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/enable`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Enable a disabled endpoint (delivery resumes)\",\n responses: { \"200\": okRef(\"Endpoint\", \"Enabled\"), \"404\": errorRes(\"Not found\") },\n },\n },\n [`${endpointPath}/disable`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Disable an endpoint (pending deliveries are held, not failed)\",\n responses: { \"200\": okRef(\"Endpoint\", \"Disabled\"), \"404\": errorRes(\"Not found\") },\n },\n },\n [`${endpointPath}/test`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Fire a one-off signed example delivery (not retained as a message)\",\n requestBody: jsonBody({\n type: \"object\",\n required: [\"eventType\"],\n properties: { eventType: { type: \"string\" }, payload: {} },\n }),\n responses: {\n \"200\": jsonRes(\"Attempt outcome\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/recover`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"deliveries\"],\n summary: \"Re-queue every failed delivery for this endpoint since a timestamp\",\n requestBody: jsonBody({\n type: \"object\",\n required: [\"since\"],\n properties: { since: { type: \"string\", format: \"date-time\" } },\n }),\n responses: {\n \"200\": okRef(\"RecoverResult\", \"Re-queued delivery ids\"),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/messages\": {\n parameters: [APP],\n get: {\n tags: [\"messages\"],\n summary: \"List messages (newest first)\",\n parameters: [\n { name: \"eventType\", in: \"query\", required: false, schema: { type: \"string\" } },\n { name: \"limit\", in: \"query\", required: false, schema: { type: \"integer\" } },\n { name: \"before\", in: \"query\", required: false, schema: { type: \"string\" } },\n ],\n responses: { \"200\": jsonRes(\"Messages\", arrayOf(\"Message\")) },\n },\n post: {\n tags: [\"messages\"],\n summary: \"Publish a message (fan-out to matching endpoints; the ingest route)\",\n description:\n \"Honors an `idempotency-key` header OR a body `idempotencyKey` field (the header wins). A deduplicated publish answers 200 with the original message.\",\n parameters: [\n { name: \"idempotency-key\", in: \"header\", required: false, schema: { type: \"string\" } },\n ],\n requestBody: jsonBody({\n type: \"object\",\n required: [\"eventType\", \"payload\"],\n properties: {\n eventType: { type: \"string\" },\n payload: {},\n timestamp: { type: \"string\", format: \"date-time\" },\n idempotencyKey: { type: \"string\" },\n },\n }),\n responses: {\n \"200\": okRef(\"PublishResult\", \"Deduplicated (existing message)\"),\n \"201\": okRef(\"PublishResult\", \"Published\"),\n \"409\": errorRes(\"Idempotency key reused with a different payload\"),\n \"413\": errorRes(\"Payload too large\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n \"/api/applications/{app}/messages/{id}\": {\n parameters: [APP, ID],\n get: {\n tags: [\"messages\"],\n summary: \"Get a message with its deliveries\",\n responses: {\n \"200\": jsonRes(\"Message + deliveries\", {\n allOf: [ref(\"Message\")],\n properties: { deliveries: arrayOf(\"Delivery\") },\n }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/deliveries\": {\n parameters: [APP],\n get: {\n tags: [\"deliveries\"],\n summary: \"List deliveries (newest first)\",\n parameters: [\n {\n name: \"status\",\n in: \"query\",\n required: false,\n schema: {\n type: \"string\",\n enum: [\"pending\", \"delivering\", \"succeeded\", \"failed\"],\n },\n },\n { name: \"endpoint\", in: \"query\", required: false, schema: { type: \"string\" } },\n { name: \"limit\", in: \"query\", required: false, schema: { type: \"integer\" } },\n { name: \"before\", in: \"query\", required: false, schema: { type: \"string\" } },\n ],\n responses: { \"200\": jsonRes(\"Deliveries\", arrayOf(\"Delivery\")) },\n },\n },\n \"/api/applications/{app}/deliveries/{id}\": {\n parameters: [APP, ID],\n get: {\n tags: [\"deliveries\"],\n summary: \"Get a delivery with its attempt timeline\",\n responses: {\n \"200\": jsonRes(\"Delivery + attempts\", {\n allOf: [ref(\"Delivery\")],\n properties: { attempts: arrayOf(\"DeliveryAttempt\") },\n }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/deliveries/{id}/retry\": {\n parameters: [APP, ID],\n post: {\n tags: [\"deliveries\"],\n summary: \"Re-queue a dead-lettered delivery\",\n responses: {\n \"200\": okRef(\"Delivery\", \"Re-queued\"),\n \"404\": errorRes(\"Not found\"),\n \"422\": errorRes(\"Delivery is not in the failed state\"),\n },\n },\n },\n \"/api/applications/{app}/audit\": {\n parameters: [APP],\n get: {\n tags: [\"audit\"],\n summary: \"List audit entries (newest first)\",\n responses: { \"200\": jsonRes(\"Audit\", arrayOf(\"AuditEntry\")) },\n },\n },\n },\n components: {\n schemas,\n securitySchemes: {\n basicAuth: { type: \"http\", scheme: \"basic\" },\n bearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n description: \"Portal tokens (whpt_…) or host-issued bearer credentials.\",\n },\n },\n },\n };\n}\n","/**\n * Renders the SPA `index.html`: injects a `<base>` tag so relative asset URLs\n * resolve under any mount path, and a bootstrap `window.__WEBHOOKS_CONFIG__`\n * blob. Falls back to a minimal built-in page when the UI bundle is absent\n * (e.g. before `bun run build:ui`, or in headless tests).\n *\n * @module\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/** Bootstrap config injected into the page and exposed at `/config`. */\nexport interface BootstrapConfig {\n title: string;\n basePath: string;\n readonly: boolean;\n /** Optional logo image URL shown in the navbar in place of the title wordmark. */\n logoUrl?: string;\n}\n\nconst escapeJson = (value: unknown): string =>\n JSON.stringify(value).replace(/</g, \"\\\\u003c\").replace(/>/g, \"\\\\u003e\");\n\n/** Build the `<base href>` value (always ends in `/`). */\nfunction baseHref(basePath: string): string {\n return basePath === \"\" ? \"/\" : `${basePath}/`;\n}\n\nfunction injectInto(html: string, config: BootstrapConfig): string {\n const tags =\n `<base href=\"${baseHref(config.basePath)}\">` +\n `<script>window.__WEBHOOKS_CONFIG__=${escapeJson(config)}</script>`;\n if (html.includes(\"<head>\")) return html.replace(\"<head>\", `<head>${tags}`);\n return tags + html;\n}\n\n/** Minimal page served when no built UI is present. */\nfunction fallbackHtml(config: BootstrapConfig): string {\n return `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><base href=\"${baseHref(\n config.basePath,\n )}\"><title>${config.title}</title><script>window.__WEBHOOKS_CONFIG__=${escapeJson(\n config,\n )}</script></head><body style=\"font-family:system-ui;margin:0;padding:3rem;background:#0a0a0a;color:#e5e5e5\"><h1 style=\"margin:0 0 .5rem\">${\n config.title\n }</h1><p style=\"color:#a3a3a3\">The admin UI bundle is not built. Run <code style=\"background:#1a1a1a;padding:2px 6px;border-radius:4px\">bun run build:ui</code>. The JSON API at <code style=\"background:#1a1a1a;padding:2px 6px;border-radius:4px\">api/</code> is fully available.</p></body></html>`;\n}\n\n/** Read, inject into, and return the SPA index HTML (or a fallback). */\nexport async function renderIndexHtml(uiDir: string, config: BootstrapConfig): Promise<string> {\n try {\n const html = await readFile(join(uiDir, \"index.html\"), \"utf8\");\n return injectInto(html, config);\n } catch {\n return fallbackHtml(config);\n }\n}\n","/**\n * JSON admin API. A tiny method+pattern router over {@link WebhooksCore}, wired\n * to authentication, authorization, and portal-token scoping. Returns `null`\n * for non-API paths so the caller can fall through to static-asset / SPA\n * handling.\n *\n * The consumer **portal** is not a separate route set — a request bearing a\n * valid `Authorization: Bearer whpt_…` token hits this same API with its\n * authorization force-scoped to the token's application and allowed actions\n * (the host's own auth/authorization providers are bypassed for portal\n * principals; portal scoping wins).\n *\n * @module\n */\n\nimport type { AuthProvider, Principal } from \"../auth/contract.ts\";\nimport type {\n AuthorizationProvider,\n WebhooksAction,\n WebhooksResource,\n} from \"../authorization/contract.ts\";\nimport {\n ConflictError,\n IdempotencyConflictError,\n NotFoundError,\n PayloadTooLargeError,\n ReadonlyError,\n type WebhooksCore,\n} from \"../core.ts\";\nimport { HookDeniedError } from \"../hooks/contract.ts\";\nimport { PORTAL_TOKEN_PREFIX, PortalTokenError, verifyPortalToken } from \"../portal.ts\";\nimport type { Actor, DeliveryStatus, Endpoint, EventType, JsonValue } from \"../schema.ts\";\nimport { ValidationError } from \"../validation.ts\";\nimport { buildOpenApiDocument } from \"./openapi.ts\";\n\n/**\n * Portal composition for the panel: when set, requests bearing a valid\n * `whpt_…` token act as a portal principal scoped to the token's application.\n */\nexport interface WebhooksPortalOptions {\n /** The HMAC secret portal tokens are minted with (`createPortalToken`). */\n secret: string;\n /**\n * Actions a portal principal may perform (always confined to the token's\n * application). Defaults to {@link DEFAULT_PORTAL_ACTIONS}.\n */\n allow?: WebhooksAction[];\n}\n\n/**\n * The default portal grant: manage own endpoints (including secrets), inspect\n * own messages/deliveries, retry, and read the event-type catalog.\n */\nexport const DEFAULT_PORTAL_ACTIONS: readonly WebhooksAction[] = [\n \"endpoint:read\",\n \"endpoint:create\",\n \"endpoint:update\",\n \"endpoint:delete\",\n \"endpoint:rotate-secret\",\n \"endpoint:read-secret\",\n \"message:read\",\n \"delivery:read\",\n \"delivery:retry\",\n \"event-type:read\",\n];\n\n/** Everything the API router needs. */\nexport interface ApiContext {\n core: WebhooksCore;\n auth: AuthProvider;\n authorization: AuthorizationProvider;\n title: string;\n readonly: boolean;\n basePath: string;\n logoUrl?: string;\n /** Portal-token composition (see {@link WebhooksPortalOptions}). */\n portal?: WebhooksPortalOptions;\n}\n\nconst json = (data: unknown, status = 200): Response =>\n new Response(JSON.stringify(data), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n\nconst error = (status: number, message: string, extra?: Record<string, unknown>): Response =>\n json({ error: message, ...extra }, status);\n\ninterface Matched {\n params: Record<string, string>;\n}\n\n/** Match `pattern` (with `:name` segments) against `path`. */\nfunction match(pattern: string, path: string): Matched | null {\n const pp = pattern.split(\"/\").filter(Boolean);\n const ap = path.split(\"/\").filter(Boolean);\n if (pp.length !== ap.length) return null;\n const params: Record<string, string> = {};\n for (let i = 0; i < pp.length; i++) {\n const seg = pp[i]!;\n const val = ap[i]!;\n if (seg.startsWith(\":\")) params[seg.slice(1)] = decodeURIComponent(val);\n else if (seg !== val) return null;\n }\n return { params };\n}\n\n/**\n * An endpoint as served by read routes: secrets stripped. The secret is\n * returned exactly once at mint time (create / rotate); afterwards it is only\n * reachable through the dedicated `/secret` route gated by\n * `endpoint:read-secret`.\n */\nfunction withoutSecrets(endpoint: Endpoint): Omit<Endpoint, \"secrets\"> {\n const { secrets: _secrets, ...rest } = endpoint;\n return rest;\n}\n\n/** The per-request scope a valid portal token establishes. */\ninterface PortalScope {\n applicationKey: string;\n allow: ReadonlySet<WebhooksAction>;\n}\n\n/** Build the audit {@link Actor} for a principal. */\nfunction actorFor(principal: Principal): Actor {\n return {\n id: principal.id,\n ...(principal.email !== undefined ? { email: principal.email } : {}),\n ...(principal.name !== undefined ? { name: principal.name } : {}),\n };\n}\n\n/** Parse a positive-integer query param, or `undefined`. */\nfunction intParam(value: string | null): number | undefined {\n if (value === null) return undefined;\n const n = Number(value);\n return Number.isFinite(n) && n > 0 ? Math.floor(n) : undefined;\n}\n\n/**\n * Handle an API request. `path` is already base-path-stripped. Returns a\n * `Response` for API/config routes, or `null` if the path is not an API route.\n */\nexport async function handleApiRequest(\n request: Request,\n path: string,\n ctx: ApiContext,\n): Promise<Response | null> {\n const isApi =\n path === \"/config\" ||\n path === \"/api/config\" ||\n path === \"/openapi.json\" ||\n path.startsWith(\"/api/\");\n if (!isApi) return null;\n\n const method = request.method.toUpperCase();\n\n // Public OpenAPI document (no auth) — for docs tooling and host-app merging.\n if (path === \"/api/openapi.json\" || path === \"/openapi.json\") {\n return json(buildOpenApiDocument({ basePath: ctx.basePath, title: ctx.title }));\n }\n\n // Public event catalog (no auth, CORS-open) — so receiver teams can read\n // which event types exist without panel credentials.\n if (path === \"/api/event-types.json\") {\n return new Response(JSON.stringify(await ctx.core.listEventTypes()), {\n status: 200,\n headers: {\n \"content-type\": \"application/json; charset=utf-8\",\n \"access-control-allow-origin\": \"*\",\n },\n });\n }\n\n // --- Authentication ---\n // A portal token short-circuits the host's providers entirely: valid →\n // portal-scoped principal; invalid/expired → 401 (never fall back to the\n // host's auth with an explicitly presented, rejected credential).\n let principal: Principal | null = null;\n let portalScope: PortalScope | null = null;\n const bearer = request.headers.get(\"authorization\");\n if (ctx.portal && bearer?.startsWith(`Bearer ${PORTAL_TOKEN_PREFIX}`)) {\n try {\n const { applicationKey } = await verifyPortalToken(\n ctx.portal.secret,\n bearer.slice(\"Bearer \".length),\n );\n principal = {\n id: `portal:${applicationKey}`,\n metadata: { portal: true, applicationKey },\n };\n portalScope = {\n applicationKey,\n allow: new Set(ctx.portal.allow ?? DEFAULT_PORTAL_ACTIONS),\n };\n } catch (err) {\n return mapError(err);\n }\n } else {\n try {\n principal = await ctx.auth.authenticate(request);\n } catch (err) {\n // A throwing auth provider is a backend failure (IdP down, DB error) —\n // surface it as 500, not a misleading 401. `null` means \"no valid\n // credentials\"; an exception means \"couldn't decide\".\n return mapError(err);\n }\n }\n\n // Public bootstrap config (whether the client should show a login, whether\n // the SPA renders the reduced portal chrome, etc.).\n if (path === \"/config\" || path === \"/api/config\") {\n return json({\n title: ctx.title,\n basePath: ctx.basePath,\n readonly: ctx.readonly,\n authenticated: principal !== null,\n principal: principal\n ? { id: principal.id, email: principal.email, name: principal.name, roles: principal.roles }\n : null,\n portal: portalScope !== null,\n logoUrl: ctx.logoUrl,\n });\n }\n\n if (principal === null) {\n const challenge = ctx.auth.challenge?.(request);\n return challenge ?? error(401, \"Unauthorized\");\n }\n\n const authorize = async (\n action: WebhooksAction,\n resource: WebhooksResource,\n ): Promise<Response | null> => {\n if (portalScope) {\n // Defense in depth: the host's authorization provider is NOT consulted\n // for portal principals — the token's scope is the whole grant.\n if (!portalScope.allow.has(action)) return error(403, \"Forbidden\", { action });\n // Event types are a single GLOBAL catalog shared by every application, so\n // a token scoped to one application may only ever READ them — never\n // mutate the catalog other tenants depend on, even if the host widened\n // `portal.allow` to include event-type writes.\n if (resource.type === \"event-type\") {\n if (action !== \"event-type:read\") return error(403, \"Forbidden\", { action });\n return null;\n }\n if (resource.applicationKey !== portalScope.applicationKey) {\n return error(403, \"Forbidden\", { action });\n }\n return null;\n }\n const ok = await ctx.authorization.authorize({ principal, action, resource, request });\n return ok ? null : error(403, \"Forbidden\", { action });\n };\n\n const actor = actorFor(principal);\n const body = async <T>(): Promise<T> => (await request.json()) as T;\n\n try {\n // --- Applications ---\n if (path === \"/api/applications\") {\n if (method === \"GET\") {\n const denied = await authorize(\"application:read\", {\n type: \"application\",\n applicationKey: \"*\",\n });\n if (denied) return denied;\n return json(await ctx.core.listApplications());\n }\n if (method === \"POST\") {\n const input = await body<{ key: string; name?: string; metadata?: JsonValue }>();\n const denied = await authorize(\"application:create\", {\n type: \"application\",\n applicationKey: input.key,\n });\n if (denied) return denied;\n return json(await ctx.core.createApplication(input, { actor }), 201);\n }\n }\n\n let m = match(\"/api/applications/:app\", path);\n if (m) {\n const app = m.params.app!;\n const resource: WebhooksResource = { type: \"application\", applicationKey: app };\n if (method === \"GET\") {\n const denied = await authorize(\"application:read\", resource);\n if (denied) return denied;\n const application = await ctx.core.getApplication(app);\n return application ? json(application) : error(404, `application \"${app}\" not found`);\n }\n if (method === \"PUT\") {\n const patch = await body<{ name?: string; metadata?: JsonValue }>();\n const denied = await authorize(\"application:update\", resource);\n if (denied) return denied;\n return json(await ctx.core.updateApplication(app, patch, { actor }));\n }\n if (method === \"DELETE\") {\n const denied = await authorize(\"application:delete\", resource);\n if (denied) return denied;\n await ctx.core.deleteApplication(app, { actor });\n return json({ ok: true });\n }\n }\n\n // --- Event types (global catalog) ---\n if (path === \"/api/event-types\") {\n if (method === \"GET\") {\n const denied = await authorize(\"event-type:read\", { type: \"event-type\", name: \"*\" });\n if (denied) return denied;\n return json(await ctx.core.listEventTypes());\n }\n if (method === \"POST\") {\n const input = await body<EventType>();\n const denied = await authorize(\"event-type:create\", {\n type: \"event-type\",\n name: input.name,\n });\n if (denied) return denied;\n return json(await ctx.core.upsertEventType(input, { actor }), 201);\n }\n }\n\n m = match(\"/api/event-types/:name\", path);\n if (m) {\n const name = m.params.name!;\n const resource: WebhooksResource = { type: \"event-type\", name };\n if (method === \"GET\") {\n const denied = await authorize(\"event-type:read\", resource);\n if (denied) return denied;\n const eventType = await ctx.core.getEventType(name);\n return eventType ? json(eventType) : error(404, `event type \"${name}\" not found`);\n }\n if (method === \"PUT\") {\n const input = await body<EventType>();\n const denied = await authorize(\"event-type:update\", resource);\n if (denied) return denied;\n return json(await ctx.core.upsertEventType({ ...input, name }, { actor }));\n }\n if (method === \"DELETE\") {\n const denied = await authorize(\"event-type:delete\", resource);\n if (denied) return denied;\n await ctx.core.deleteEventType(name, { actor });\n return json({ ok: true });\n }\n }\n\n // --- Endpoints ---\n m = match(\"/api/applications/:app/endpoints\", path);\n if (m) {\n const app = m.params.app!;\n if (method === \"GET\") {\n const denied = await authorize(\"endpoint:read\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: \"*\",\n });\n if (denied) return denied;\n return json((await ctx.core.listEndpoints(app)).map(withoutSecrets));\n }\n if (method === \"POST\") {\n const input = await body<{\n url: string;\n description?: string;\n eventTypes?: string[];\n headers?: Record<string, string>;\n metadata?: JsonValue;\n disabled?: boolean;\n }>();\n const denied = await authorize(\"endpoint:create\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: \"*\",\n });\n if (denied) return denied;\n // The one response that carries the signing secret — capture it now.\n return json(await ctx.core.createEndpoint(app, input, { actor }), 201);\n }\n }\n\n m = match(\"/api/applications/:app/endpoints/:id\", path);\n if (m) {\n const app = m.params.app!;\n const id = m.params.id!;\n const resource: WebhooksResource = { type: \"endpoint\", applicationKey: app, endpointId: id };\n if (method === \"GET\") {\n const denied = await authorize(\"endpoint:read\", resource);\n if (denied) return denied;\n const endpoint = await ctx.core.getEndpoint(app, id);\n return endpoint ? json(withoutSecrets(endpoint)) : error(404, `endpoint \"${id}\" not found`);\n }\n if (method === \"PUT\") {\n const patch = await body<{\n url?: string;\n description?: string;\n eventTypes?: string[];\n headers?: Record<string, string>;\n metadata?: JsonValue;\n }>();\n const denied = await authorize(\"endpoint:update\", resource);\n if (denied) return denied;\n return json(withoutSecrets(await ctx.core.updateEndpoint(app, id, patch, { actor })));\n }\n if (method === \"DELETE\") {\n const denied = await authorize(\"endpoint:delete\", resource);\n if (denied) return denied;\n await ctx.core.deleteEndpoint(app, id, { actor });\n return json({ ok: true });\n }\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/secret\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:read-secret\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n return json(await ctx.core.getSecrets(app, id));\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/rotate-secret\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:rotate-secret\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n // Like create, rotation mints a secret the caller must capture — the\n // response includes `secrets` (new current + graced predecessors).\n return json(await ctx.core.rotateSecret(app, id, { actor }));\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/enable\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:update\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n return json(withoutSecrets(await ctx.core.enableEndpoint(app, id, { actor })));\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/disable\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:update\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n return json(withoutSecrets(await ctx.core.disableEndpoint(app, id, { actor })));\n }\n\n // --- Send-example test delivery ---\n m = match(\"/api/applications/:app/endpoints/:id/test\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:update\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n const input = await body<{ eventType: string; payload?: JsonValue }>();\n return json(await ctx.core.sendExample(app, id, input, { actor }));\n }\n\n // --- Recover (redrive failed deliveries since a timestamp) ---\n m = match(\"/api/applications/:app/endpoints/:id/recover\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:retry\", {\n type: \"delivery\",\n applicationKey: app,\n });\n if (denied) return denied;\n const input = await body<{ since: string }>();\n return json(await ctx.core.recoverEndpoint(app, id, input, { actor }));\n }\n\n // --- Messages ---\n m = match(\"/api/applications/:app/messages\", path);\n if (m) {\n const app = m.params.app!;\n if (method === \"GET\") {\n const denied = await authorize(\"message:read\", { type: \"message\", applicationKey: app });\n if (denied) return denied;\n const url = new URL(request.url);\n const eventType = url.searchParams.get(\"eventType\") ?? undefined;\n const before = url.searchParams.get(\"before\") ?? undefined;\n const limit = intParam(url.searchParams.get(\"limit\"));\n return json(\n await ctx.core.listMessages(app, {\n ...(eventType !== undefined ? { eventType } : {}),\n ...(before !== undefined ? { before } : {}),\n ...(limit !== undefined ? { limit } : {}),\n }),\n );\n }\n if (method === \"POST\") {\n const input = await body<{\n eventType: string;\n payload: JsonValue;\n timestamp?: string;\n idempotencyKey?: string;\n }>();\n const denied = await authorize(\"message:publish\", { type: \"message\", applicationKey: app });\n if (denied) return denied;\n // The `idempotency-key` header wins over the body field.\n const idempotencyKey = request.headers.get(\"idempotency-key\") ?? input.idempotencyKey;\n const result = await ctx.core.publish(\n app,\n {\n eventType: input.eventType,\n payload: input.payload,\n ...(input.timestamp !== undefined ? { timestamp: input.timestamp } : {}),\n ...(idempotencyKey !== undefined ? { idempotencyKey } : {}),\n },\n { actor },\n );\n return json(result, result.deduplicated ? 200 : 201);\n }\n }\n\n m = match(\"/api/applications/:app/messages/:id\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"message:read\", {\n type: \"message\",\n applicationKey: app,\n messageId: id,\n });\n if (denied) return denied;\n const message = await ctx.core.getMessage(app, id);\n if (!message) return error(404, `message \"${id}\" not found`);\n const deliveries = await ctx.core.listDeliveries(app, { messageId: id });\n return json({ ...message, deliveries });\n }\n\n // --- Deliveries ---\n m = match(\"/api/applications/:app/deliveries\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const denied = await authorize(\"delivery:read\", { type: \"delivery\", applicationKey: app });\n if (denied) return denied;\n const url = new URL(request.url);\n const status = url.searchParams.get(\"status\") ?? undefined;\n const endpointId = url.searchParams.get(\"endpoint\") ?? undefined;\n const before = url.searchParams.get(\"before\") ?? undefined;\n const limit = intParam(url.searchParams.get(\"limit\"));\n return json(\n await ctx.core.listDeliveries(app, {\n ...(status !== undefined ? { status: status as DeliveryStatus } : {}),\n ...(endpointId !== undefined ? { endpointId } : {}),\n ...(before !== undefined ? { before } : {}),\n ...(limit !== undefined ? { limit } : {}),\n }),\n );\n }\n\n m = match(\"/api/applications/:app/deliveries/:id\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:read\", {\n type: \"delivery\",\n applicationKey: app,\n deliveryId: id,\n });\n if (denied) return denied;\n const found = await ctx.core.getDelivery(app, id);\n if (!found) return error(404, `delivery \"${id}\" not found`);\n return json({ ...found.delivery, attempts: found.attempts });\n }\n\n m = match(\"/api/applications/:app/deliveries/:id/request\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:read\", {\n type: \"delivery\",\n applicationKey: app,\n deliveryId: id,\n });\n if (denied) return denied;\n const preview = await ctx.core.previewDeliveryRequest(app, id);\n if (!preview) return error(404, `delivery \"${id}\" not found (or its endpoint was deleted)`);\n return json(preview);\n }\n\n m = match(\"/api/applications/:app/deliveries/:id/retry\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:retry\", {\n type: \"delivery\",\n applicationKey: app,\n deliveryId: id,\n });\n if (denied) return denied;\n return json(await ctx.core.retryDelivery(app, id, { actor }));\n }\n\n // --- Audit ---\n m = match(\"/api/applications/:app/audit\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const denied = await authorize(\"audit:read\", { type: \"audit\", applicationKey: app });\n if (denied) return denied;\n return json(await ctx.core.listAudit(app));\n }\n\n return error(404, \"Not found\");\n } catch (err) {\n return mapError(err);\n }\n}\n\n/**\n * Cross-bundle error detection: `instanceof` first, `err.name` fallback. An\n * error thrown from a separate subpath bundle carries its own copy of the\n * class, so `instanceof` alone would mis-map it to 500.\n */\nfunction isNamed(err: unknown, ctor: new (...args: never[]) => Error, name: string): boolean {\n return err instanceof ctor || (err instanceof Error && err.name === name);\n}\n\n/**\n * A hook denial, detected by `name` rather than `instanceof`: a hook thrown from\n * a separate subpath bundle carries its own copy of the `HookDeniedError` class,\n * so `instanceof` would miss it and mis-map the denial to 500. Returns the\n * status to respond with, or `null` if not a denial.\n */\nfunction hookDeniedStatus(err: unknown): number | null {\n if (err instanceof HookDeniedError) return err.status;\n if (err instanceof Error && err.name === \"HookDeniedError\") {\n const status = (err as { status?: unknown }).status;\n return typeof status === \"number\" ? status : 403;\n }\n return null;\n}\n\n/** Map domain errors to HTTP responses. */\nfunction mapError(err: unknown): Response {\n if (isNamed(err, ReadonlyError, \"ReadonlyError\")) {\n return error(403, (err as Error).message, { code: \"READONLY\" });\n }\n const denied = hookDeniedStatus(err);\n if (denied !== null) return error(denied, (err as Error).message, { code: \"HOOK_DENIED\" });\n if (isNamed(err, PortalTokenError, \"PortalTokenError\")) {\n return error(401, (err as Error).message, { code: \"PORTAL_TOKEN\" });\n }\n if (isNamed(err, NotFoundError, \"NotFoundError\")) return error(404, (err as Error).message);\n if (isNamed(err, IdempotencyConflictError, \"IdempotencyConflictError\")) {\n return error(409, (err as Error).message, { code: \"IDEMPOTENCY_CONFLICT\" });\n }\n if (isNamed(err, ConflictError, \"ConflictError\")) {\n return error(409, (err as Error).message, { code: \"CONFLICT\" });\n }\n if (isNamed(err, PayloadTooLargeError, \"PayloadTooLargeError\")) {\n return error(413, (err as Error).message, { code: \"PAYLOAD_TOO_LARGE\" });\n }\n if (isNamed(err, ValidationError, \"ValidationError\")) {\n const errors = (err as { errors?: unknown }).errors;\n return error(422, (err as Error).message, {\n code: \"VALIDATION\",\n errors: Array.isArray(errors) ? errors : [],\n });\n }\n if (err instanceof SyntaxError) return error(400, \"Invalid JSON body\");\n const message = err instanceof Error ? err.message : \"Internal error\";\n return error(500, message);\n}\n","/**\n * Serves the bundled SPA's static assets from the UI output directory. Maps file\n * extensions to content types and guards against path traversal.\n *\n * @module\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, normalize } from \"node:path\";\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".mjs\": \"text/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".map\": \"application/json; charset=utf-8\",\n \".txt\": \"text/plain; charset=utf-8\",\n};\n\nfunction contentType(path: string): string {\n const dot = path.lastIndexOf(\".\");\n const ext = dot >= 0 ? path.slice(dot).toLowerCase() : \"\";\n return CONTENT_TYPES[ext] ?? \"application/octet-stream\";\n}\n\n/** True if the path has a file extension (so a miss should 404 rather than fall through to the SPA). */\nexport function looksLikeAsset(path: string): boolean {\n const last = path.split(\"/\").pop() ?? \"\";\n return last.includes(\".\");\n}\n\n/**\n * Try to serve `path` (base-path-stripped, leading `/`) as a static file from\n * `uiDir`. Returns a `Response`, or `null` if the file does not exist.\n */\nexport async function serveStaticAsset(uiDir: string, path: string): Promise<Response | null> {\n // Resolve within uiDir and refuse anything that escapes it.\n const rel = normalize(path).replace(/^(\\.\\.(\\/|\\\\|$))+/, \"\");\n const full = join(uiDir, rel);\n if (!full.startsWith(normalize(uiDir))) return null;\n try {\n const data = await readFile(full);\n return new Response(new Uint8Array(data), {\n status: 200,\n headers: {\n \"content-type\": contentType(full),\n \"cache-control\": rel.includes(\"/assets/\")\n ? \"public, max-age=31536000, immutable\"\n : \"no-cache\",\n },\n });\n } catch {\n return null;\n }\n}\n","/**\n * The web-standard fetch handler at the heart of every framework adapter and the\n * standalone app. Composes auth/authorization (plus portal-token scoping), the\n * JSON admin API, static-asset serving, and SPA fallback into a single\n * `(request: Request) => Promise<Response>` — and, by default, starts the\n * in-process delivery dispatcher so mounting the panel is all it takes to\n * deliver webhooks.\n *\n * @module\n */\n\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AuthProvider } from \"../auth/contract.ts\";\nimport type { AuthorizationProvider } from \"../authorization/contract.ts\";\nimport { createWebhooksCore, type RetentionOptions, type WebhooksCore } from \"../core.ts\";\nimport type { DeliveryErrorReporter, DeliveryListener } from \"../delivery-sink.ts\";\nimport { createDispatcher, type Dispatcher, type DispatcherOptions } from \"../dispatcher.ts\";\nimport type { HookErrorReporter, WebhooksHooksInput } from \"../hooks/contract.ts\";\nimport { hasDeliveryQueue, isCompareAndSwap, type WebhooksStorage } from \"../storage/contract.ts\";\nimport { normalizeBasePath, stripBasePath } from \"./base-path.ts\";\nimport { applyCorsHeaders, preflightResponse, type WebhooksCorsOptions } from \"./cors.ts\";\nimport { buildOpenApiDocument } from \"./openapi.ts\";\nimport { renderIndexHtml } from \"./render-index-html.ts\";\nimport { handleApiRequest, type ApiContext, type WebhooksPortalOptions } from \"./routes.ts\";\nimport { looksLikeAsset, serveStaticAsset } from \"./static-assets.ts\";\n\n/** Options for the panel handler (shared by every framework adapter). */\nexport interface WebhooksPanelOptions {\n /** Control-plane store: applications, event types, endpoints, messages, audit. */\n storage: WebhooksStorage;\n /** Store for deliveries, attempts, and the due index. Defaults to `storage`. */\n queueStorage?: WebhooksStorage;\n /** Mount prefix, e.g. `\"/webhooks\"`. Default `\"\"` (root). */\n basePath?: string;\n /** Authentication provider. Default: anonymous (no auth). */\n auth?: AuthProvider;\n /** Authorization provider. Default: allow all. */\n authorization?: AuthorizationProvider;\n /**\n * Portal-token composition: requests bearing a valid `whpt_…` token act as a\n * portal principal scoped to the token's application. Mint tokens\n * server-side with `createPortalToken`. See\n * {@link ./routes.WebhooksPortalOptions}.\n */\n portal?: WebhooksPortalOptions;\n /** Block all mutating operations when true. */\n readonly?: boolean;\n /** UI title shown in the page and bootstrap config. */\n title?: string;\n /** Logo image URL shown in the navbar in place of the title wordmark. */\n logoUrl?: string;\n /** Override the directory the bundled UI is served from (defaults to `./ui` beside this module). */\n uiDir?: string;\n /** Reuse an existing core instead of constructing one. */\n core?: WebhooksCore;\n /**\n * Control-plane hooks fired around admin mutations (see\n * {@link ../hooks/contract.WebhooksHooks}). Ignored when `core` is supplied —\n * configure hooks on that core instead.\n */\n hooks?: WebhooksHooksInput;\n /** Reporter for errors thrown by `after` hooks. Default: `console.warn`. */\n onHookError?: HookErrorReporter;\n /**\n * Message/audit retention policy. Ignored when a prebuilt `core` is supplied —\n * configure it on that core instead. See {@link ../core.RetentionOptions}.\n */\n retention?: RetentionOptions;\n /**\n * Fire-and-forget sink invoked for **every** delivery attempt (metrics tap).\n * Ignored when `core` is supplied. See {@link ../delivery-sink.DeliveryListener}.\n */\n onDelivery?: DeliveryListener;\n /** Reporter for errors thrown by `onDelivery`. Default: `console.warn`. */\n onDeliveryError?: DeliveryErrorReporter;\n /**\n * Enable CORS on the handler itself — answers `OPTIONS` preflights and attaches\n * `Access-Control-*` headers to every response, so a **cross-origin** embed\n * works regardless of the host framework. See {@link ./cors.WebhooksCorsOptions}.\n */\n cors?: WebhooksCorsOptions;\n /**\n * Delivery-engine configuration. By default the panel creates **and starts**\n * a dispatcher so deliveries flow the moment the panel is mounted. Pass\n * `false` to skip it entirely (split-worker deployments where a separate\n * process runs `xtandard-webhooks dispatch` against the same storage).\n */\n dispatcher?: DispatcherOptions | false;\n}\n\n/** Return shape of {@link createFetchHandler}. */\nexport interface CreateFetchHandlerResult {\n /** Web-standard request handler. */\n fetch(request: Request): Promise<Response>;\n /** The underlying core (handy for `publish()`, tests, CLI, and standalone wiring). */\n core: WebhooksCore;\n /** The started dispatcher, or `null` when `dispatcher: false`. */\n dispatcher: Dispatcher | null;\n /**\n * The admin API as an OpenAPI 3.1 document (also served at `{basePath}/api/openapi.json`).\n * Merge it into your host app's docs — e.g. Elysia `@elysiajs/openapi` `references`.\n */\n openapi(): Record<string, unknown>;\n}\n\n// Anonymous defaults keep embedded usage zero-config; harden via auth/authorization.\nconst defaultAuth: AuthProvider = { authenticate: async () => ({ id: \"anonymous\" }) };\nconst defaultAuthorization: AuthorizationProvider = { authorize: async () => true };\n\n/**\n * Locate the built admin SPA (`dist/ui`). When this module runs compiled (the\n * normal npm-consumer case) it lives in `dist/` and the bundle is the sibling\n * `./ui`. When it runs from TypeScript source (examples / dev against a\n * `file:`-linked checkout, where the runtime executes `src/server/*.ts`\n * directly), the bundle is instead at `<repo>/dist/ui` — i.e. `../../dist/ui`\n * relative to `src/server/`. Try the candidates and return the first that\n * exists, falling back to the compiled-layout path so the \"build the UI\" hint\n * still fires when nothing is built yet.\n */\nfunction defaultUiDir(): string {\n try {\n const candidates = [\n new URL(\"./ui\", import.meta.url), // compiled: dist/<chunk>.mjs → dist/ui\n new URL(\"../../dist/ui\", import.meta.url), // source: src/server/*.ts → <repo>/dist/ui\n ].map((u) => fileURLToPath(u));\n return candidates.find((p) => existsSync(p)) ?? candidates[0]!;\n } catch {\n return \"./ui\";\n }\n}\n\n/**\n * Build the panel fetch handler.\n *\n * @example\n * ```ts\n * import { createFetchHandler } from \"@xtandard/webhooks\";\n * import { createFileStorage } from \"@xtandard/webhooks/storage/file\";\n *\n * const storage = createFileStorage({ dir: \"./data/webhooks\" });\n * const { fetch, core } = createFetchHandler({\n * storage,\n * basePath: \"/webhooks\",\n * title: \"Acme Webhooks\",\n * });\n *\n * Bun.serve({ port: 3000, fetch });\n * // Elsewhere in the app:\n * await core.publish(\"acme\", { eventType: \"invoice.paid\", payload: { id: \"inv_1\" } });\n * ```\n */\nexport function createFetchHandler(options: WebhooksPanelOptions): CreateFetchHandlerResult {\n const basePath = normalizeBasePath(options.basePath);\n const readonly = options.readonly ?? false;\n const title = options.title ?? \"@xtandard/webhooks\";\n const uiDir = options.uiDir ?? defaultUiDir();\n const dispatcherOptions = options.dispatcher === false ? undefined : options.dispatcher;\n\n const core =\n options.core ??\n createWebhooksCore({\n storage: options.storage,\n ...(options.queueStorage ? { queueStorage: options.queueStorage } : {}),\n readonly,\n ...(options.hooks !== undefined ? { hooks: options.hooks } : {}),\n ...(options.onHookError ? { onHookError: options.onHookError } : {}),\n ...(options.retention ? { retention: options.retention } : {}),\n ...(options.onDelivery ? { onDelivery: options.onDelivery } : {}),\n ...(options.onDeliveryError ? { onDeliveryError: options.onDeliveryError } : {}),\n ...(dispatcherOptions ? { dispatcher: dispatcherOptions } : {}),\n });\n\n // The panel owns the default embedded deployment shape: dispatcher created\n // AND started (timers are unref()ed — it never keeps the process alive).\n let dispatcher: Dispatcher | null = null;\n if (options.dispatcher !== false) {\n // Claiming is only exclusive across processes when the queue storage has\n // native claimDue (redis/memory) or compare-and-swap. On plainer backends\n // (postgres/file/mongodb/…) two dispatchers can claim the same delivery and\n // double-send. Since the panel auto-starts one on EVERY mount, warn loudly\n // so a horizontally-scaled deployment sets `dispatcher: false` on all but\n // one instance (or runs a single split worker). See docs/DELIVERY.md.\n const queue = options.queueStorage ?? options.storage;\n if (queue && !hasDeliveryQueue(queue) && !isCompareAndSwap(queue)) {\n // eslint-disable-next-line no-console\n console.warn(\n \"[@xtandard/webhooks] The panel started an in-process dispatcher over storage \" +\n \"without atomic claiming (no claimDue/compareAndSwap). If you run more than one \" +\n \"instance, deliveries WILL be sent multiple times — set `dispatcher: false` on all \" +\n \"but one instance, or use redis/memory storage. See docs/DELIVERY.md.\",\n );\n }\n dispatcher = createDispatcher(core, dispatcherOptions ?? {});\n dispatcher.start();\n }\n\n const apiCtx: ApiContext = {\n core,\n auth: options.auth ?? defaultAuth,\n authorization: options.authorization ?? defaultAuthorization,\n title,\n readonly,\n basePath,\n ...(options.logoUrl !== undefined ? { logoUrl: options.logoUrl } : {}),\n ...(options.portal ? { portal: options.portal } : {}),\n };\n\n const cors = options.cors;\n\n async function fetch(request: Request): Promise<Response> {\n // CORS preflight: answer OPTIONS directly so it works for any path,\n // independent of the host framework wrapping this handler.\n if (cors && request.method === \"OPTIONS\") return preflightResponse(request, cors);\n const response = await respond(request);\n return cors ? applyCorsHeaders(request, response, cors) : response;\n }\n\n async function respond(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const path = stripBasePath(url.pathname, basePath);\n\n // 1. JSON API + bootstrap config.\n const api = await handleApiRequest(request, path, apiCtx);\n if (api) return api;\n\n // 2. Only GET/HEAD reach the static UI.\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n return new Response(\"Method Not Allowed\", { status: 405 });\n }\n\n // 3. Static asset.\n const asset = await serveStaticAsset(uiDir, path);\n if (asset) return asset;\n\n // 4. A path that looks like a file but was not found → 404 (don't mask with the SPA).\n if (path !== \"/\" && looksLikeAsset(path)) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n // 5. SPA fallback.\n const html = await renderIndexHtml(uiDir, {\n title,\n basePath,\n readonly,\n ...(options.logoUrl !== undefined ? { logoUrl: options.logoUrl } : {}),\n });\n return new Response(html, {\n status: 200,\n headers: { \"content-type\": \"text/html; charset=utf-8\" },\n });\n }\n\n return { fetch, core, dispatcher, openapi: () => buildOpenApiDocument({ basePath, title }) };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"create-fetch-handler-BILaZ2Z7.cjs","names":["durationToMs","HookDeniedError","ReadonlyError","NotFoundError","IdempotencyConflictError","ConflictError","PayloadTooLargeError","ValidationError","createWebhooksCore","hasDeliveryQueue","isCompareAndSwap","createDispatcher"],"sources":["../src/portal.ts","../src/server/base-path.ts","../src/server/cors.ts","../src/server/openapi.ts","../src/server/render-index-html.ts","../src/server/routes.ts","../src/server/static-assets.ts","../src/server/create-fetch-handler.ts"],"sourcesContent":["/**\n * Portal tokens — the credential behind the embeddable consumer portal.\n *\n * A portal token is a compact HMAC-signed grant scoping its bearer to exactly\n * **one application** for a limited time. The host mints tokens server-side\n * with {@link createPortalToken} (the portal secret never reaches a browser)\n * and hands them to its frontend; the panel handler verifies them with\n * {@link verifyPortalToken} and force-scopes authorization to the token's\n * application (see the `portal` panel option).\n *\n * Wire format:\n *\n * ```txt\n * whpt_<base64url(JSON { app, exp })>.<base64url(HMAC-SHA256(secret, payloadPart))>\n * ```\n *\n * Zero dependencies (Web Crypto only). Unlike Standard Webhooks signing\n * secrets, the portal secret is an **arbitrary string** — it is fed to the\n * HMAC as raw UTF-8 bytes, not base64-decoded.\n *\n * @module\n */\n\nimport { durationToMs } from \"./duration.ts\";\nimport type { WebhookDuration } from \"./schema.ts\";\n\n/** Prefix of every portal token. */\nexport const PORTAL_TOKEN_PREFIX = \"whpt_\";\n\nconst DEFAULT_EXPIRES_IN: WebhookDuration = \"7d\";\n\n/**\n * Thrown by {@link verifyPortalToken} for any invalid token — malformed,\n * bad signature, or expired. Maps to HTTP 401 at the API layer.\n */\nexport class PortalTokenError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PortalTokenError\";\n }\n}\n\n/** Options for {@link createPortalToken}. */\nexport interface PortalTokenOptions {\n /** Token lifetime. Default `\"7d\"`. */\n expiresIn?: WebhookDuration;\n}\n\n/** The claims carried inside a portal token. */\ninterface PortalTokenClaims {\n /** The application the token grants access to. */\n app: string;\n /** Expiry, unix epoch milliseconds. */\n exp: number;\n}\n\nconst base64urlEncode = (bytes: Uint8Array): string => {\n let binary = \"\";\n for (const b of bytes) binary += String.fromCharCode(b);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n};\n\nconst base64urlDecode = (value: string): Uint8Array => {\n const padded = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded + \"=\".repeat((4 - (padded.length % 4)) % 4));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n};\n\n/** HMAC-SHA256 keyed by the raw UTF-8 bytes of the (arbitrary-string) secret. */\nasync function hmac(secret: string, content: string): Promise<Uint8Array> {\n const key = await crypto.subtle.importKey(\n \"raw\",\n new TextEncoder().encode(secret) as BufferSource,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const digest = await crypto.subtle.sign(\"HMAC\", key, new TextEncoder().encode(content));\n return new Uint8Array(digest);\n}\n\n/** Constant-time byte comparison (XOR accumulate — no early exit on content). */\nfunction timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= (a[i] as number) ^ (b[i] as number);\n return diff === 0;\n}\n\n/**\n * Mint a portal token granting access to `applicationKey` until `expiresIn`\n * elapses (default 7 days). Call this **server-side** — anyone holding the\n * portal secret can mint tokens for any application.\n *\n * @example\n * ```ts\n * import { createPortalToken } from \"@xtandard/webhooks\";\n *\n * // In the host app's session-guarded route:\n * const token = await createPortalToken(process.env.PORTAL_SECRET!, \"acme\", {\n * expiresIn: \"1h\",\n * });\n * // Hand `token` to the frontend for <WebhooksPortal token={token} />.\n * ```\n */\nexport async function createPortalToken(\n secret: string,\n applicationKey: string,\n options: PortalTokenOptions = {},\n): Promise<string> {\n const expiresInMs = durationToMs(options.expiresIn ?? DEFAULT_EXPIRES_IN);\n const claims: PortalTokenClaims = { app: applicationKey, exp: Date.now() + expiresInMs };\n const payloadPart = base64urlEncode(new TextEncoder().encode(JSON.stringify(claims)));\n const signature = base64urlEncode(await hmac(secret, payloadPart));\n return `${PORTAL_TOKEN_PREFIX}${payloadPart}.${signature}`;\n}\n\n/**\n * Verify a portal token: format, signature (constant time), then expiry.\n * Returns the granted application key on success; throws\n * {@link PortalTokenError} otherwise.\n *\n * @example\n * ```ts\n * import { verifyPortalToken } from \"@xtandard/webhooks\";\n *\n * const { applicationKey } = await verifyPortalToken(secret, token);\n * ```\n */\nexport async function verifyPortalToken(\n secret: string,\n token: string,\n): Promise<{ applicationKey: string }> {\n if (!token.startsWith(PORTAL_TOKEN_PREFIX)) {\n throw new PortalTokenError(\"Invalid portal token: missing whpt_ prefix\");\n }\n const parts = token.slice(PORTAL_TOKEN_PREFIX.length).split(\".\");\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n throw new PortalTokenError(\"Invalid portal token: malformed\");\n }\n const [payloadPart, signaturePart] = parts as [string, string];\n\n // Signature first: never interpret unauthenticated claims.\n let candidate: Uint8Array;\n try {\n candidate = base64urlDecode(signaturePart);\n } catch {\n throw new PortalTokenError(\"Invalid portal token: malformed signature\");\n }\n const expected = await hmac(secret, payloadPart);\n if (!timingSafeEqual(expected, candidate)) {\n throw new PortalTokenError(\"Invalid portal token: signature mismatch\");\n }\n\n let claims: PortalTokenClaims;\n try {\n claims = JSON.parse(\n new TextDecoder().decode(base64urlDecode(payloadPart)),\n ) as PortalTokenClaims;\n } catch {\n throw new PortalTokenError(\"Invalid portal token: malformed payload\");\n }\n if (typeof claims.app !== \"string\" || claims.app.length === 0) {\n throw new PortalTokenError(\"Invalid portal token: missing application\");\n }\n if (typeof claims.exp !== \"number\" || !Number.isFinite(claims.exp)) {\n throw new PortalTokenError(\"Invalid portal token: missing expiry\");\n }\n if (Date.now() >= claims.exp) {\n throw new PortalTokenError(\"Portal token has expired\");\n }\n return { applicationKey: claims.app };\n}\n","/**\n * Base-path helpers. The panel can be mounted under any prefix (`/webhooks`,\n * `/admin/webhooks`, ...). These normalize the configured base path and strip it\n * from incoming request paths so routing is prefix-agnostic.\n *\n * @module\n */\n\n/** Normalize a base path to either `\"\"` (root) or `/segment` with no trailing slash. */\nexport function normalizeBasePath(basePath: string | undefined): string {\n if (!basePath || basePath === \"/\") return \"\";\n let p = basePath.trim();\n if (!p.startsWith(\"/\")) p = \"/\" + p;\n if (p.endsWith(\"/\")) p = p.slice(0, -1);\n return p;\n}\n\n/**\n * Strip the (normalized) base path from a pathname. Returns a path that always\n * starts with `/`. If the pathname does not start with the base path it is\n * returned unchanged (the host framework may already have stripped the mount).\n */\nexport function stripBasePath(pathname: string, basePath: string): string {\n if (basePath === \"\") return pathname || \"/\";\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(basePath + \"/\")) {\n const rest = pathname.slice(basePath.length);\n return rest || \"/\";\n }\n return pathname || \"/\";\n}\n","/**\n * Framework-independent CORS for the mounted panel. When `webhooksPanel` /\n * `createFetchHandler` is given a `cors` option, the handler answers `OPTIONS`\n * preflights itself and attaches `Access-Control-*` headers to every response —\n * so a **cross-origin embed** (portal on `app.example.com`, panel on\n * `api.example.com`) works even if the host framework's CORS middleware does not\n * wrap the mounted sub-handler.\n *\n * @module\n */\n\n/** CORS configuration for the panel handler. */\nexport interface WebhooksCorsOptions {\n /**\n * Allowed origin(s): a single origin (`\"https://app.example.com\"`), `\"*\"`, a\n * list, or a predicate `(origin) => boolean`. With `credentials: true`, `\"*\"`\n * is echoed back as the caller's exact origin (the browser forbids `*` +\n * credentials).\n */\n origin: string | string[] | ((origin: string) => boolean);\n /** Send `Access-Control-Allow-Credentials: true` (needed for cookie auth). Default `false`. */\n credentials?: boolean;\n /** Allowed methods advertised in preflight. Default `GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS`. */\n methods?: string[];\n /**\n * Allowed request headers advertised in preflight. Default: reflect the\n * request's `Access-Control-Request-Headers`, falling back to\n * `Content-Type, Authorization`.\n */\n headers?: string[];\n /** `Access-Control-Max-Age` (seconds) for the preflight cache. */\n maxAge?: number;\n}\n\nconst DEFAULT_METHODS = \"GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS\";\n\n/**\n * Resolve the value for `Access-Control-Allow-Origin` for this request, or\n * `null` when the origin is not allowed (no CORS headers → the browser blocks).\n */\nfunction resolveOrigin(cors: WebhooksCorsOptions, requestOrigin: string | null): string | null {\n const { origin } = cors;\n if (origin === \"*\") {\n // `*` + credentials is invalid; echo the caller's origin instead.\n return cors.credentials ? (requestOrigin ?? null) : \"*\";\n }\n if (requestOrigin === null) return null;\n const allowed =\n typeof origin === \"string\"\n ? origin === requestOrigin\n : Array.isArray(origin)\n ? origin.includes(requestOrigin)\n : origin(requestOrigin);\n return allowed ? requestOrigin : null;\n}\n\n/** Attach `Access-Control-*` headers to `response` for an allowed cross-origin request. */\nexport function applyCorsHeaders(\n request: Request,\n response: Response,\n cors: WebhooksCorsOptions,\n): Response {\n const requestOrigin = request.headers.get(\"origin\");\n const allowOrigin = resolveOrigin(cors, requestOrigin);\n if (allowOrigin === null) return response;\n response.headers.set(\"access-control-allow-origin\", allowOrigin);\n if (cors.credentials) response.headers.set(\"access-control-allow-credentials\", \"true\");\n // The response varies by Origin unless we blanket-allow `*` — tell caches.\n if (allowOrigin !== \"*\") response.headers.append(\"vary\", \"Origin\");\n return response;\n}\n\n/** Build the `204` response for a CORS preflight (`OPTIONS`). */\nexport function preflightResponse(request: Request, cors: WebhooksCorsOptions): Response {\n const response = new Response(null, { status: 204 });\n applyCorsHeaders(request, response, cors);\n response.headers.set(\n \"access-control-allow-methods\",\n (cors.methods ?? []).join(\",\") || DEFAULT_METHODS,\n );\n const requestedHeaders = request.headers.get(\"access-control-request-headers\");\n response.headers.set(\n \"access-control-allow-headers\",\n cors.headers?.join(\",\") || requestedHeaders || \"Content-Type, Authorization\",\n );\n if (cors.maxAge !== undefined) {\n response.headers.set(\"access-control-max-age\", String(cors.maxAge));\n }\n return response;\n}\n","/**\n * OpenAPI 3.1 description of the admin JSON API.\n *\n * Exposed two ways, mirroring the flags panel:\n * - served at `GET {basePath}/api/openapi.json` for standalone tools (Scalar,\n * Swagger UI, Postman, codegen);\n * - returned by `createFetchHandler(...).openapi()` so you can MERGE it into\n * your host app's OpenAPI document (e.g. Elysia `@elysiajs/openapi`\n * `references`, or Hono's OpenAPI).\n *\n * Hand-authored — no generation dependency (flags precedent).\n *\n * @module\n */\n\n/** Options for {@link buildOpenApiDocument}. */\nexport interface OpenApiOptions {\n /** Mount prefix used in the `servers` url (e.g. `\"/webhooks\"`). */\n basePath?: string;\n /** Document title. */\n title?: string;\n /** Document version (defaults to the package's API version). */\n version?: string;\n}\n\nconst schemas = {\n Application: {\n type: \"object\",\n required: [\"key\"],\n properties: {\n key: { type: \"string\", pattern: \"^[a-zA-Z0-9._-]+$\" },\n name: { type: \"string\" },\n metadata: {},\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n },\n },\n EventType: {\n type: \"object\",\n required: [\"name\"],\n properties: {\n name: { type: \"string\", pattern: \"^[a-zA-Z0-9._-]+$\" },\n description: { type: \"string\" },\n groupName: { type: \"string\" },\n schema: { description: \"JSON Schema documenting the payload.\" },\n deprecated: { type: \"boolean\" },\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n },\n },\n EndpointSecret: {\n type: \"object\",\n required: [\"secret\", \"createdAt\"],\n properties: {\n secret: { type: \"string\", description: '\"whsec_\" + base64 key material.' },\n createdAt: { type: \"string\", format: \"date-time\" },\n expiresAt: {\n type: \"string\",\n format: \"date-time\",\n description: \"Set on rotation; the secret stops signing after this instant.\",\n },\n },\n },\n Endpoint: {\n type: \"object\",\n required: [\"id\", \"url\"],\n description:\n \"Read routes strip `secrets`; only the create/rotate responses and the dedicated /secret route carry them.\",\n properties: {\n id: { type: \"string\" },\n url: { type: \"string\" },\n description: { type: \"string\" },\n eventTypes: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Subscribed event types; empty/absent = all.\",\n },\n disabled: { type: \"boolean\" },\n disabledReason: { type: \"string\", enum: [\"manual\", \"auto\"] },\n headers: { type: \"object\", additionalProperties: { type: \"string\" } },\n secrets: { type: \"array\", items: { $ref: \"#/components/schemas/EndpointSecret\" } },\n metadata: {},\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n firstFailingAt: { type: \"string\", format: \"date-time\", nullable: true },\n },\n },\n Message: {\n type: \"object\",\n required: [\"id\", \"eventType\", \"payload\", \"timestamp\", \"createdAt\"],\n properties: {\n id: { type: \"string\", description: \"Sent as the webhook-id header.\" },\n eventType: { type: \"string\" },\n payload: {},\n timestamp: { type: \"string\", format: \"date-time\" },\n idempotencyKey: { type: \"string\" },\n envelope: { type: \"string\", description: \"The serialized wire envelope.\" },\n createdAt: { type: \"string\", format: \"date-time\" },\n },\n },\n Delivery: {\n type: \"object\",\n required: [\"id\", \"applicationKey\", \"messageId\", \"endpointId\", \"status\", \"attemptCount\"],\n properties: {\n id: { type: \"string\" },\n applicationKey: { type: \"string\" },\n messageId: { type: \"string\" },\n endpointId: { type: \"string\" },\n status: { type: \"string\", enum: [\"pending\", \"delivering\", \"succeeded\", \"failed\"] },\n attemptCount: { type: \"integer\" },\n nextAttemptAt: { type: \"string\", format: \"date-time\", nullable: true },\n leaseUntil: { type: \"string\", format: \"date-time\", nullable: true },\n createdAt: { type: \"string\", format: \"date-time\" },\n updatedAt: { type: \"string\", format: \"date-time\" },\n },\n },\n DeliveryAttempt: {\n type: \"object\",\n required: [\"id\", \"deliveryId\", \"attemptNumber\", \"at\", \"durationMs\", \"ok\", \"trigger\"],\n properties: {\n id: { type: \"string\" },\n deliveryId: { type: \"string\" },\n attemptNumber: { type: \"integer\" },\n at: { type: \"string\", format: \"date-time\" },\n durationMs: { type: \"number\" },\n ok: { type: \"boolean\" },\n httpStatus: { type: \"integer\" },\n error: { type: \"string\" },\n responseBody: { type: \"string\" },\n trigger: { type: \"string\", enum: [\"schedule\", \"manual\", \"test\"] },\n },\n },\n PublishResult: {\n type: \"object\",\n required: [\"message\", \"deliveries\", \"deduplicated\"],\n properties: {\n message: { $ref: \"#/components/schemas/Message\" },\n deliveries: { type: \"array\", items: { $ref: \"#/components/schemas/Delivery\" } },\n deduplicated: {\n type: \"boolean\",\n description: \"True when the idempotency key matched an existing message.\",\n },\n },\n },\n RecoverResult: {\n type: \"object\",\n required: [\"deliveryIds\"],\n properties: { deliveryIds: { type: \"array\", items: { type: \"string\" } } },\n },\n AuditEntry: {\n type: \"object\",\n required: [\"action\", \"at\"],\n properties: {\n action: { type: \"string\" },\n at: { type: \"string\", format: \"date-time\" },\n by: { type: \"object\", nullable: true },\n applicationKey: { type: \"string\" },\n subjectId: { type: \"string\" },\n message: { type: \"string\" },\n },\n },\n Config: {\n type: \"object\",\n properties: {\n title: { type: \"string\" },\n basePath: { type: \"string\" },\n readonly: { type: \"boolean\" },\n authenticated: { type: \"boolean\" },\n principal: { type: \"object\", nullable: true },\n portal: {\n type: \"boolean\",\n description: \"True when the request carried a valid portal token.\",\n },\n logoUrl: { type: \"string\" },\n },\n },\n Error: {\n type: \"object\",\n required: [\"error\"],\n properties: {\n error: { type: \"string\" },\n code: { type: \"string\" },\n errors: { type: \"array\", items: { type: \"object\" } },\n },\n },\n} as const;\n\nconst APP = { name: \"app\", in: \"path\", required: true, schema: { type: \"string\" } } as const;\nconst ID = { name: \"id\", in: \"path\", required: true, schema: { type: \"string\" } } as const;\n\nconst jsonBody = (schema: object, required = true) => ({\n required,\n content: { \"application/json\": { schema } },\n});\nconst jsonRes = (desc: string, schema: object) => ({\n description: desc,\n content: { \"application/json\": { schema } },\n});\nconst ref = (name: string) => ({ $ref: `#/components/schemas/${name}` });\nconst errorRes = (desc: string) => jsonRes(desc, ref(\"Error\"));\nconst okRef = (name: string, desc = name) => jsonRes(desc, ref(name));\nconst arrayOf = (name: string) => ({ type: \"array\", items: ref(name) });\n\n/** Build the OpenAPI 3.1 document for the admin API. Pure — safe to call anywhere. */\nexport function buildOpenApiDocument(options: OpenApiOptions = {}): Record<string, unknown> {\n const base = options.basePath && options.basePath !== \"/\" ? options.basePath : \"\";\n const endpointPath = \"/api/applications/{app}/endpoints/{id}\";\n\n return {\n openapi: \"3.1.0\",\n info: {\n title: options.title ?? \"@xtandard/webhooks Admin API\",\n version: options.version ?? \"0.1.0\",\n description:\n \"Admin/control-plane API for @xtandard/webhooks: applications, the global event-type catalog, endpoints (with Standard Webhooks signing secrets), message publishing, and delivery observability. Portal tokens (Authorization: Bearer whpt_…) hit the same surface scoped to one application.\",\n },\n servers: [{ url: base || \"/\" }],\n tags: [\n { name: \"meta\" },\n { name: \"applications\" },\n { name: \"event-types\" },\n { name: \"endpoints\" },\n { name: \"messages\" },\n { name: \"deliveries\" },\n { name: \"audit\" },\n ],\n security: [{ basicAuth: [] }, { bearerAuth: [] }],\n paths: {\n \"/config\": {\n get: {\n tags: [\"meta\"],\n security: [],\n summary: \"Bootstrap config (title, basePath, readonly, auth state, portal flag)\",\n responses: { \"200\": okRef(\"Config\") },\n },\n },\n \"/api/openapi.json\": {\n get: {\n tags: [\"meta\"],\n security: [],\n summary: \"This OpenAPI document\",\n responses: { \"200\": jsonRes(\"OpenAPI 3.1 document\", { type: \"object\" }) },\n },\n },\n \"/api/event-types.json\": {\n get: {\n tags: [\"meta\"],\n security: [],\n summary: \"Public event-type catalog (unauthenticated, CORS-open)\",\n responses: { \"200\": jsonRes(\"Event types\", arrayOf(\"EventType\")) },\n },\n },\n \"/api/applications\": {\n get: {\n tags: [\"applications\"],\n summary: \"List applications\",\n responses: { \"200\": jsonRes(\"Applications\", arrayOf(\"Application\")) },\n },\n post: {\n tags: [\"applications\"],\n summary: \"Create an application\",\n requestBody: jsonBody(ref(\"Application\")),\n responses: {\n \"201\": okRef(\"Application\", \"Created\"),\n \"409\": errorRes(\"Key already exists\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n \"/api/applications/{app}\": {\n parameters: [APP],\n get: {\n tags: [\"applications\"],\n summary: \"Get an application\",\n responses: { \"200\": okRef(\"Application\"), \"404\": errorRes(\"Not found\") },\n },\n put: {\n tags: [\"applications\"],\n summary: \"Update an application\",\n requestBody: jsonBody({\n type: \"object\",\n properties: { name: { type: \"string\" }, metadata: {} },\n }),\n responses: { \"200\": okRef(\"Application\", \"Updated\"), \"404\": errorRes(\"Not found\") },\n },\n delete: {\n tags: [\"applications\"],\n summary: \"Delete an application and everything under it\",\n responses: {\n \"200\": jsonRes(\"Deleted\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/event-types\": {\n get: {\n tags: [\"event-types\"],\n summary: \"List event types\",\n responses: { \"200\": jsonRes(\"Event types\", arrayOf(\"EventType\")) },\n },\n post: {\n tags: [\"event-types\"],\n summary: \"Create (upsert) an event type\",\n requestBody: jsonBody(ref(\"EventType\")),\n responses: {\n \"201\": okRef(\"EventType\", \"Created\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n \"/api/event-types/{name}\": {\n parameters: [{ name: \"name\", in: \"path\", required: true, schema: { type: \"string\" } }],\n get: {\n tags: [\"event-types\"],\n summary: \"Get an event type\",\n responses: { \"200\": okRef(\"EventType\"), \"404\": errorRes(\"Not found\") },\n },\n put: {\n tags: [\"event-types\"],\n summary: \"Update an event type\",\n requestBody: jsonBody(ref(\"EventType\")),\n responses: { \"200\": okRef(\"EventType\", \"Updated\"), \"422\": errorRes(\"Validation error\") },\n },\n delete: {\n tags: [\"event-types\"],\n summary: \"Delete an event type (endpoints referencing it stop matching)\",\n responses: {\n \"200\": jsonRes(\"Deleted\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/endpoints\": {\n parameters: [APP],\n get: {\n tags: [\"endpoints\"],\n summary: \"List endpoints (secrets stripped)\",\n responses: { \"200\": jsonRes(\"Endpoints\", arrayOf(\"Endpoint\")) },\n },\n post: {\n tags: [\"endpoints\"],\n summary: \"Create an endpoint — the response includes the signing secret ONCE\",\n requestBody: jsonBody({\n type: \"object\",\n required: [\"url\"],\n properties: {\n url: { type: \"string\" },\n description: { type: \"string\" },\n eventTypes: { type: \"array\", items: { type: \"string\" } },\n headers: { type: \"object\", additionalProperties: { type: \"string\" } },\n metadata: {},\n disabled: { type: \"boolean\" },\n },\n }),\n responses: {\n \"201\": okRef(\"Endpoint\", \"Created (includes secrets)\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n [endpointPath]: {\n parameters: [APP, ID],\n get: {\n tags: [\"endpoints\"],\n summary: \"Get an endpoint (secrets stripped)\",\n responses: { \"200\": okRef(\"Endpoint\"), \"404\": errorRes(\"Not found\") },\n },\n put: {\n tags: [\"endpoints\"],\n summary: \"Update an endpoint\",\n requestBody: jsonBody({\n type: \"object\",\n properties: {\n url: { type: \"string\" },\n description: { type: \"string\" },\n eventTypes: { type: \"array\", items: { type: \"string\" } },\n headers: { type: \"object\", additionalProperties: { type: \"string\" } },\n metadata: {},\n },\n }),\n responses: {\n \"200\": okRef(\"Endpoint\", \"Updated\"),\n \"404\": errorRes(\"Not found\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n delete: {\n tags: [\"endpoints\"],\n summary: \"Delete an endpoint\",\n responses: {\n \"200\": jsonRes(\"Deleted\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/secret`]: {\n parameters: [APP, ID],\n get: {\n tags: [\"endpoints\"],\n summary: \"Read the endpoint's signing secrets (current first)\",\n responses: {\n \"200\": jsonRes(\"Secrets\", arrayOf(\"EndpointSecret\")),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/rotate-secret`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Mint a new secret; the previous one keeps signing through the grace window\",\n responses: {\n \"200\": okRef(\"Endpoint\", \"Rotated (includes secrets)\"),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/enable`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Enable a disabled endpoint (delivery resumes)\",\n responses: { \"200\": okRef(\"Endpoint\", \"Enabled\"), \"404\": errorRes(\"Not found\") },\n },\n },\n [`${endpointPath}/disable`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Disable an endpoint (pending deliveries are held, not failed)\",\n responses: { \"200\": okRef(\"Endpoint\", \"Disabled\"), \"404\": errorRes(\"Not found\") },\n },\n },\n [`${endpointPath}/test`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"endpoints\"],\n summary: \"Fire a one-off signed example delivery (not retained as a message)\",\n requestBody: jsonBody({\n type: \"object\",\n required: [\"eventType\"],\n properties: { eventType: { type: \"string\" }, payload: {} },\n }),\n responses: {\n \"200\": jsonRes(\"Attempt outcome\", { type: \"object\" }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n [`${endpointPath}/recover`]: {\n parameters: [APP, ID],\n post: {\n tags: [\"deliveries\"],\n summary: \"Re-queue every failed delivery for this endpoint since a timestamp\",\n requestBody: jsonBody({\n type: \"object\",\n required: [\"since\"],\n properties: { since: { type: \"string\", format: \"date-time\" } },\n }),\n responses: {\n \"200\": okRef(\"RecoverResult\", \"Re-queued delivery ids\"),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/messages\": {\n parameters: [APP],\n get: {\n tags: [\"messages\"],\n summary: \"List messages (newest first)\",\n parameters: [\n { name: \"eventType\", in: \"query\", required: false, schema: { type: \"string\" } },\n { name: \"limit\", in: \"query\", required: false, schema: { type: \"integer\" } },\n { name: \"before\", in: \"query\", required: false, schema: { type: \"string\" } },\n ],\n responses: { \"200\": jsonRes(\"Messages\", arrayOf(\"Message\")) },\n },\n post: {\n tags: [\"messages\"],\n summary: \"Publish a message (fan-out to matching endpoints; the ingest route)\",\n description:\n \"Honors an `idempotency-key` header OR a body `idempotencyKey` field (the header wins). A deduplicated publish answers 200 with the original message.\",\n parameters: [\n { name: \"idempotency-key\", in: \"header\", required: false, schema: { type: \"string\" } },\n ],\n requestBody: jsonBody({\n type: \"object\",\n required: [\"eventType\", \"payload\"],\n properties: {\n eventType: { type: \"string\" },\n payload: {},\n timestamp: { type: \"string\", format: \"date-time\" },\n idempotencyKey: { type: \"string\" },\n },\n }),\n responses: {\n \"200\": okRef(\"PublishResult\", \"Deduplicated (existing message)\"),\n \"201\": okRef(\"PublishResult\", \"Published\"),\n \"409\": errorRes(\"Idempotency key reused with a different payload\"),\n \"413\": errorRes(\"Payload too large\"),\n \"422\": errorRes(\"Validation error\"),\n },\n },\n },\n \"/api/applications/{app}/messages/{id}\": {\n parameters: [APP, ID],\n get: {\n tags: [\"messages\"],\n summary: \"Get a message with its deliveries\",\n responses: {\n \"200\": jsonRes(\"Message + deliveries\", {\n allOf: [ref(\"Message\")],\n properties: { deliveries: arrayOf(\"Delivery\") },\n }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/deliveries\": {\n parameters: [APP],\n get: {\n tags: [\"deliveries\"],\n summary: \"List deliveries (newest first)\",\n parameters: [\n {\n name: \"status\",\n in: \"query\",\n required: false,\n schema: {\n type: \"string\",\n enum: [\"pending\", \"delivering\", \"succeeded\", \"failed\"],\n },\n },\n { name: \"endpoint\", in: \"query\", required: false, schema: { type: \"string\" } },\n { name: \"limit\", in: \"query\", required: false, schema: { type: \"integer\" } },\n { name: \"before\", in: \"query\", required: false, schema: { type: \"string\" } },\n ],\n responses: { \"200\": jsonRes(\"Deliveries\", arrayOf(\"Delivery\")) },\n },\n },\n \"/api/applications/{app}/deliveries/{id}\": {\n parameters: [APP, ID],\n get: {\n tags: [\"deliveries\"],\n summary: \"Get a delivery with its attempt timeline\",\n responses: {\n \"200\": jsonRes(\"Delivery + attempts\", {\n allOf: [ref(\"Delivery\")],\n properties: { attempts: arrayOf(\"DeliveryAttempt\") },\n }),\n \"404\": errorRes(\"Not found\"),\n },\n },\n },\n \"/api/applications/{app}/deliveries/{id}/retry\": {\n parameters: [APP, ID],\n post: {\n tags: [\"deliveries\"],\n summary: \"Re-queue a dead-lettered delivery\",\n responses: {\n \"200\": okRef(\"Delivery\", \"Re-queued\"),\n \"404\": errorRes(\"Not found\"),\n \"422\": errorRes(\"Delivery is not in the failed state\"),\n },\n },\n },\n \"/api/applications/{app}/audit\": {\n parameters: [APP],\n get: {\n tags: [\"audit\"],\n summary: \"List audit entries (newest first)\",\n responses: { \"200\": jsonRes(\"Audit\", arrayOf(\"AuditEntry\")) },\n },\n },\n },\n components: {\n schemas,\n securitySchemes: {\n basicAuth: { type: \"http\", scheme: \"basic\" },\n bearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n description: \"Portal tokens (whpt_…) or host-issued bearer credentials.\",\n },\n },\n },\n };\n}\n","/**\n * Renders the SPA `index.html`: injects a `<base>` tag so relative asset URLs\n * resolve under any mount path, and a bootstrap `window.__WEBHOOKS_CONFIG__`\n * blob. Falls back to a minimal built-in page when the UI bundle is absent\n * (e.g. before `bun run build:ui`, or in headless tests).\n *\n * @module\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/** Bootstrap config injected into the page and exposed at `/config`. */\nexport interface BootstrapConfig {\n title: string;\n basePath: string;\n readonly: boolean;\n /** Optional logo image URL shown in the navbar in place of the title wordmark. */\n logoUrl?: string;\n}\n\nconst escapeJson = (value: unknown): string =>\n JSON.stringify(value).replace(/</g, \"\\\\u003c\").replace(/>/g, \"\\\\u003e\");\n\n/** Build the `<base href>` value (always ends in `/`). */\nfunction baseHref(basePath: string): string {\n return basePath === \"\" ? \"/\" : `${basePath}/`;\n}\n\nfunction injectInto(html: string, config: BootstrapConfig): string {\n const tags =\n `<base href=\"${baseHref(config.basePath)}\">` +\n `<script>window.__WEBHOOKS_CONFIG__=${escapeJson(config)}</script>`;\n if (html.includes(\"<head>\")) return html.replace(\"<head>\", `<head>${tags}`);\n return tags + html;\n}\n\n/** Minimal page served when no built UI is present. */\nfunction fallbackHtml(config: BootstrapConfig): string {\n return `<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><base href=\"${baseHref(\n config.basePath,\n )}\"><title>${config.title}</title><script>window.__WEBHOOKS_CONFIG__=${escapeJson(\n config,\n )}</script></head><body style=\"font-family:system-ui;margin:0;padding:3rem;background:#0a0a0a;color:#e5e5e5\"><h1 style=\"margin:0 0 .5rem\">${\n config.title\n }</h1><p style=\"color:#a3a3a3\">The admin UI bundle is not built. Run <code style=\"background:#1a1a1a;padding:2px 6px;border-radius:4px\">bun run build:ui</code>. The JSON API at <code style=\"background:#1a1a1a;padding:2px 6px;border-radius:4px\">api/</code> is fully available.</p></body></html>`;\n}\n\n/** Read, inject into, and return the SPA index HTML (or a fallback). */\nexport async function renderIndexHtml(uiDir: string, config: BootstrapConfig): Promise<string> {\n try {\n const html = await readFile(join(uiDir, \"index.html\"), \"utf8\");\n return injectInto(html, config);\n } catch {\n return fallbackHtml(config);\n }\n}\n","/**\n * JSON admin API. A tiny method+pattern router over {@link WebhooksCore}, wired\n * to authentication, authorization, and portal-token scoping. Returns `null`\n * for non-API paths so the caller can fall through to static-asset / SPA\n * handling.\n *\n * The consumer **portal** is not a separate route set — a request bearing a\n * valid `Authorization: Bearer whpt_…` token hits this same API with its\n * authorization force-scoped to the token's application and allowed actions\n * (the host's own auth/authorization providers are bypassed for portal\n * principals; portal scoping wins).\n *\n * @module\n */\n\nimport type { AuthProvider, Principal } from \"../auth/contract.ts\";\nimport type {\n AuthorizationProvider,\n WebhooksAction,\n WebhooksResource,\n} from \"../authorization/contract.ts\";\nimport {\n ConflictError,\n IdempotencyConflictError,\n NotFoundError,\n PayloadTooLargeError,\n ReadonlyError,\n type WebhooksCore,\n} from \"../core.ts\";\nimport { HookDeniedError } from \"../hooks/contract.ts\";\nimport { PORTAL_TOKEN_PREFIX, PortalTokenError, verifyPortalToken } from \"../portal.ts\";\nimport type { Actor, DeliveryStatus, Endpoint, EventType, JsonValue } from \"../schema.ts\";\nimport { ValidationError } from \"../validation.ts\";\nimport { buildOpenApiDocument } from \"./openapi.ts\";\n\n/**\n * Portal composition for the panel: when set, requests bearing a valid\n * `whpt_…` token act as a portal principal scoped to the token's application.\n */\nexport interface WebhooksPortalOptions {\n /** The HMAC secret portal tokens are minted with (`createPortalToken`). */\n secret: string;\n /**\n * Actions a portal principal may perform (always confined to the token's\n * application). Defaults to {@link DEFAULT_PORTAL_ACTIONS}.\n */\n allow?: WebhooksAction[];\n}\n\n/**\n * The default portal grant: manage own endpoints (including secrets), inspect\n * own messages/deliveries, retry, and read the event-type catalog.\n */\nexport const DEFAULT_PORTAL_ACTIONS: readonly WebhooksAction[] = [\n \"endpoint:read\",\n \"endpoint:create\",\n \"endpoint:update\",\n \"endpoint:delete\",\n \"endpoint:rotate-secret\",\n \"endpoint:read-secret\",\n \"message:read\",\n \"delivery:read\",\n \"delivery:retry\",\n \"event-type:read\",\n];\n\n/** Everything the API router needs. */\nexport interface ApiContext {\n core: WebhooksCore;\n auth: AuthProvider;\n authorization: AuthorizationProvider;\n title: string;\n readonly: boolean;\n basePath: string;\n logoUrl?: string;\n /** Portal-token composition (see {@link WebhooksPortalOptions}). */\n portal?: WebhooksPortalOptions;\n}\n\nconst json = (data: unknown, status = 200): Response =>\n new Response(JSON.stringify(data), {\n status,\n headers: { \"content-type\": \"application/json; charset=utf-8\" },\n });\n\nconst error = (status: number, message: string, extra?: Record<string, unknown>): Response =>\n json({ error: message, ...extra }, status);\n\ninterface Matched {\n params: Record<string, string>;\n}\n\n/** Match `pattern` (with `:name` segments) against `path`. */\nfunction match(pattern: string, path: string): Matched | null {\n const pp = pattern.split(\"/\").filter(Boolean);\n const ap = path.split(\"/\").filter(Boolean);\n if (pp.length !== ap.length) return null;\n const params: Record<string, string> = {};\n for (let i = 0; i < pp.length; i++) {\n const seg = pp[i]!;\n const val = ap[i]!;\n if (seg.startsWith(\":\")) params[seg.slice(1)] = decodeURIComponent(val);\n else if (seg !== val) return null;\n }\n return { params };\n}\n\n/**\n * An endpoint as served by read routes: secrets stripped. The secret is\n * returned exactly once at mint time (create / rotate); afterwards it is only\n * reachable through the dedicated `/secret` route gated by\n * `endpoint:read-secret`.\n */\nfunction withoutSecrets(endpoint: Endpoint): Omit<Endpoint, \"secrets\"> {\n const { secrets: _secrets, ...rest } = endpoint;\n return rest;\n}\n\n/** The per-request scope a valid portal token establishes. */\ninterface PortalScope {\n applicationKey: string;\n allow: ReadonlySet<WebhooksAction>;\n}\n\n/** Build the audit {@link Actor} for a principal. */\nfunction actorFor(principal: Principal): Actor {\n return {\n id: principal.id,\n ...(principal.email !== undefined ? { email: principal.email } : {}),\n ...(principal.name !== undefined ? { name: principal.name } : {}),\n };\n}\n\n/** Parse a positive-integer query param, or `undefined`. */\nfunction intParam(value: string | null): number | undefined {\n if (value === null) return undefined;\n const n = Number(value);\n return Number.isFinite(n) && n > 0 ? Math.floor(n) : undefined;\n}\n\n/**\n * Handle an API request. `path` is already base-path-stripped. Returns a\n * `Response` for API/config routes, or `null` if the path is not an API route.\n */\nexport async function handleApiRequest(\n request: Request,\n path: string,\n ctx: ApiContext,\n): Promise<Response | null> {\n const isApi =\n path === \"/config\" ||\n path === \"/api/config\" ||\n path === \"/openapi.json\" ||\n path.startsWith(\"/api/\");\n if (!isApi) return null;\n\n const method = request.method.toUpperCase();\n\n // Public OpenAPI document (no auth) — for docs tooling and host-app merging.\n if (path === \"/api/openapi.json\" || path === \"/openapi.json\") {\n return json(buildOpenApiDocument({ basePath: ctx.basePath, title: ctx.title }));\n }\n\n // Public event catalog (no auth, CORS-open) — so receiver teams can read\n // which event types exist without panel credentials.\n if (path === \"/api/event-types.json\") {\n return new Response(JSON.stringify(await ctx.core.listEventTypes()), {\n status: 200,\n headers: {\n \"content-type\": \"application/json; charset=utf-8\",\n \"access-control-allow-origin\": \"*\",\n },\n });\n }\n\n // --- Authentication ---\n // A portal token short-circuits the host's providers entirely: valid →\n // portal-scoped principal; invalid/expired → 401 (never fall back to the\n // host's auth with an explicitly presented, rejected credential).\n let principal: Principal | null = null;\n let portalScope: PortalScope | null = null;\n const bearer = request.headers.get(\"authorization\");\n if (ctx.portal && bearer?.startsWith(`Bearer ${PORTAL_TOKEN_PREFIX}`)) {\n try {\n const { applicationKey } = await verifyPortalToken(\n ctx.portal.secret,\n bearer.slice(\"Bearer \".length),\n );\n principal = {\n id: `portal:${applicationKey}`,\n metadata: { portal: true, applicationKey },\n };\n portalScope = {\n applicationKey,\n allow: new Set(ctx.portal.allow ?? DEFAULT_PORTAL_ACTIONS),\n };\n } catch (err) {\n return mapError(err);\n }\n } else {\n try {\n principal = await ctx.auth.authenticate(request);\n } catch (err) {\n // A throwing auth provider is a backend failure (IdP down, DB error) —\n // surface it as 500, not a misleading 401. `null` means \"no valid\n // credentials\"; an exception means \"couldn't decide\".\n return mapError(err);\n }\n }\n\n // Public bootstrap config (whether the client should show a login, whether\n // the SPA renders the reduced portal chrome, etc.).\n if (path === \"/config\" || path === \"/api/config\") {\n return json({\n title: ctx.title,\n basePath: ctx.basePath,\n readonly: ctx.readonly,\n authenticated: principal !== null,\n principal: principal\n ? { id: principal.id, email: principal.email, name: principal.name, roles: principal.roles }\n : null,\n portal: portalScope !== null,\n logoUrl: ctx.logoUrl,\n });\n }\n\n if (principal === null) {\n const challenge = ctx.auth.challenge?.(request);\n return challenge ?? error(401, \"Unauthorized\");\n }\n\n const authorize = async (\n action: WebhooksAction,\n resource: WebhooksResource,\n ): Promise<Response | null> => {\n if (portalScope) {\n // Defense in depth: the host's authorization provider is NOT consulted\n // for portal principals — the token's scope is the whole grant.\n if (!portalScope.allow.has(action)) return error(403, \"Forbidden\", { action });\n // Event types are a single GLOBAL catalog shared by every application, so\n // a token scoped to one application may only ever READ them — never\n // mutate the catalog other tenants depend on, even if the host widened\n // `portal.allow` to include event-type writes.\n if (resource.type === \"event-type\") {\n if (action !== \"event-type:read\") return error(403, \"Forbidden\", { action });\n return null;\n }\n if (resource.applicationKey !== portalScope.applicationKey) {\n return error(403, \"Forbidden\", { action });\n }\n return null;\n }\n const ok = await ctx.authorization.authorize({ principal, action, resource, request });\n return ok ? null : error(403, \"Forbidden\", { action });\n };\n\n const actor = actorFor(principal);\n const body = async <T>(): Promise<T> => (await request.json()) as T;\n\n try {\n // --- Applications ---\n if (path === \"/api/applications\") {\n if (method === \"GET\") {\n const denied = await authorize(\"application:read\", {\n type: \"application\",\n applicationKey: \"*\",\n });\n if (denied) return denied;\n return json(await ctx.core.listApplications());\n }\n if (method === \"POST\") {\n const input = await body<{ key: string; name?: string; metadata?: JsonValue }>();\n const denied = await authorize(\"application:create\", {\n type: \"application\",\n applicationKey: input.key,\n });\n if (denied) return denied;\n return json(await ctx.core.createApplication(input, { actor }), 201);\n }\n }\n\n let m = match(\"/api/applications/:app\", path);\n if (m) {\n const app = m.params.app!;\n const resource: WebhooksResource = { type: \"application\", applicationKey: app };\n if (method === \"GET\") {\n const denied = await authorize(\"application:read\", resource);\n if (denied) return denied;\n const application = await ctx.core.getApplication(app);\n return application ? json(application) : error(404, `application \"${app}\" not found`);\n }\n if (method === \"PUT\") {\n const patch = await body<{ name?: string; metadata?: JsonValue }>();\n const denied = await authorize(\"application:update\", resource);\n if (denied) return denied;\n return json(await ctx.core.updateApplication(app, patch, { actor }));\n }\n if (method === \"DELETE\") {\n const denied = await authorize(\"application:delete\", resource);\n if (denied) return denied;\n await ctx.core.deleteApplication(app, { actor });\n return json({ ok: true });\n }\n }\n\n // --- Event types (global catalog) ---\n if (path === \"/api/event-types\") {\n if (method === \"GET\") {\n const denied = await authorize(\"event-type:read\", { type: \"event-type\", name: \"*\" });\n if (denied) return denied;\n return json(await ctx.core.listEventTypes());\n }\n if (method === \"POST\") {\n const input = await body<EventType>();\n const denied = await authorize(\"event-type:create\", {\n type: \"event-type\",\n name: input.name,\n });\n if (denied) return denied;\n return json(await ctx.core.upsertEventType(input, { actor }), 201);\n }\n }\n\n m = match(\"/api/event-types/:name\", path);\n if (m) {\n const name = m.params.name!;\n const resource: WebhooksResource = { type: \"event-type\", name };\n if (method === \"GET\") {\n const denied = await authorize(\"event-type:read\", resource);\n if (denied) return denied;\n const eventType = await ctx.core.getEventType(name);\n return eventType ? json(eventType) : error(404, `event type \"${name}\" not found`);\n }\n if (method === \"PUT\") {\n const input = await body<EventType>();\n const denied = await authorize(\"event-type:update\", resource);\n if (denied) return denied;\n return json(await ctx.core.upsertEventType({ ...input, name }, { actor }));\n }\n if (method === \"DELETE\") {\n const denied = await authorize(\"event-type:delete\", resource);\n if (denied) return denied;\n await ctx.core.deleteEventType(name, { actor });\n return json({ ok: true });\n }\n }\n\n // --- Endpoints ---\n m = match(\"/api/applications/:app/endpoints\", path);\n if (m) {\n const app = m.params.app!;\n if (method === \"GET\") {\n const denied = await authorize(\"endpoint:read\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: \"*\",\n });\n if (denied) return denied;\n return json((await ctx.core.listEndpoints(app)).map(withoutSecrets));\n }\n if (method === \"POST\") {\n const input = await body<{\n url: string;\n description?: string;\n eventTypes?: string[];\n headers?: Record<string, string>;\n metadata?: JsonValue;\n disabled?: boolean;\n }>();\n const denied = await authorize(\"endpoint:create\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: \"*\",\n });\n if (denied) return denied;\n // The one response that carries the signing secret — capture it now.\n return json(await ctx.core.createEndpoint(app, input, { actor }), 201);\n }\n }\n\n m = match(\"/api/applications/:app/endpoints/:id\", path);\n if (m) {\n const app = m.params.app!;\n const id = m.params.id!;\n const resource: WebhooksResource = { type: \"endpoint\", applicationKey: app, endpointId: id };\n if (method === \"GET\") {\n const denied = await authorize(\"endpoint:read\", resource);\n if (denied) return denied;\n const endpoint = await ctx.core.getEndpoint(app, id);\n return endpoint ? json(withoutSecrets(endpoint)) : error(404, `endpoint \"${id}\" not found`);\n }\n if (method === \"PUT\") {\n const patch = await body<{\n url?: string;\n description?: string;\n eventTypes?: string[];\n headers?: Record<string, string>;\n metadata?: JsonValue;\n }>();\n const denied = await authorize(\"endpoint:update\", resource);\n if (denied) return denied;\n return json(withoutSecrets(await ctx.core.updateEndpoint(app, id, patch, { actor })));\n }\n if (method === \"DELETE\") {\n const denied = await authorize(\"endpoint:delete\", resource);\n if (denied) return denied;\n await ctx.core.deleteEndpoint(app, id, { actor });\n return json({ ok: true });\n }\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/secret\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:read-secret\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n return json(await ctx.core.getSecrets(app, id));\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/rotate-secret\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:rotate-secret\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n // Like create, rotation mints a secret the caller must capture — the\n // response includes `secrets` (new current + graced predecessors).\n return json(await ctx.core.rotateSecret(app, id, { actor }));\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/enable\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:update\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n return json(withoutSecrets(await ctx.core.enableEndpoint(app, id, { actor })));\n }\n\n m = match(\"/api/applications/:app/endpoints/:id/disable\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:update\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n return json(withoutSecrets(await ctx.core.disableEndpoint(app, id, { actor })));\n }\n\n // --- Send-example test delivery ---\n m = match(\"/api/applications/:app/endpoints/:id/test\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"endpoint:update\", {\n type: \"endpoint\",\n applicationKey: app,\n endpointId: id,\n });\n if (denied) return denied;\n const input = await body<{ eventType: string; payload?: JsonValue }>();\n return json(await ctx.core.sendExample(app, id, input, { actor }));\n }\n\n // --- Recover (redrive failed deliveries since a timestamp) ---\n m = match(\"/api/applications/:app/endpoints/:id/recover\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:retry\", {\n type: \"delivery\",\n applicationKey: app,\n });\n if (denied) return denied;\n const input = await body<{ since: string }>();\n return json(await ctx.core.recoverEndpoint(app, id, input, { actor }));\n }\n\n // --- Messages ---\n m = match(\"/api/applications/:app/messages\", path);\n if (m) {\n const app = m.params.app!;\n if (method === \"GET\") {\n const denied = await authorize(\"message:read\", { type: \"message\", applicationKey: app });\n if (denied) return denied;\n const url = new URL(request.url);\n const eventType = url.searchParams.get(\"eventType\") ?? undefined;\n const before = url.searchParams.get(\"before\") ?? undefined;\n const limit = intParam(url.searchParams.get(\"limit\"));\n return json(\n await ctx.core.listMessages(app, {\n ...(eventType !== undefined ? { eventType } : {}),\n ...(before !== undefined ? { before } : {}),\n ...(limit !== undefined ? { limit } : {}),\n }),\n );\n }\n if (method === \"POST\") {\n const input = await body<{\n eventType: string;\n payload: JsonValue;\n timestamp?: string;\n idempotencyKey?: string;\n }>();\n const denied = await authorize(\"message:publish\", { type: \"message\", applicationKey: app });\n if (denied) return denied;\n // The `idempotency-key` header wins over the body field.\n const idempotencyKey = request.headers.get(\"idempotency-key\") ?? input.idempotencyKey;\n const result = await ctx.core.publish(\n app,\n {\n eventType: input.eventType,\n payload: input.payload,\n ...(input.timestamp !== undefined ? { timestamp: input.timestamp } : {}),\n ...(idempotencyKey !== undefined ? { idempotencyKey } : {}),\n },\n { actor },\n );\n return json(result, result.deduplicated ? 200 : 201);\n }\n }\n\n m = match(\"/api/applications/:app/messages/:id\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"message:read\", {\n type: \"message\",\n applicationKey: app,\n messageId: id,\n });\n if (denied) return denied;\n const message = await ctx.core.getMessage(app, id);\n if (!message) return error(404, `message \"${id}\" not found`);\n const deliveries = await ctx.core.listDeliveries(app, { messageId: id });\n return json({ ...message, deliveries });\n }\n\n // --- Deliveries ---\n m = match(\"/api/applications/:app/deliveries\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const denied = await authorize(\"delivery:read\", { type: \"delivery\", applicationKey: app });\n if (denied) return denied;\n const url = new URL(request.url);\n const status = url.searchParams.get(\"status\") ?? undefined;\n const endpointId = url.searchParams.get(\"endpoint\") ?? undefined;\n const before = url.searchParams.get(\"before\") ?? undefined;\n const limit = intParam(url.searchParams.get(\"limit\"));\n return json(\n await ctx.core.listDeliveries(app, {\n ...(status !== undefined ? { status: status as DeliveryStatus } : {}),\n ...(endpointId !== undefined ? { endpointId } : {}),\n ...(before !== undefined ? { before } : {}),\n ...(limit !== undefined ? { limit } : {}),\n }),\n );\n }\n\n m = match(\"/api/applications/:app/deliveries/:id\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:read\", {\n type: \"delivery\",\n applicationKey: app,\n deliveryId: id,\n });\n if (denied) return denied;\n const found = await ctx.core.getDelivery(app, id);\n if (!found) return error(404, `delivery \"${id}\" not found`);\n return json({ ...found.delivery, attempts: found.attempts });\n }\n\n m = match(\"/api/applications/:app/deliveries/:id/request\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:read\", {\n type: \"delivery\",\n applicationKey: app,\n deliveryId: id,\n });\n if (denied) return denied;\n const preview = await ctx.core.previewDeliveryRequest(app, id);\n if (!preview) return error(404, `delivery \"${id}\" not found (or its endpoint was deleted)`);\n return json(preview);\n }\n\n m = match(\"/api/applications/:app/deliveries/:id/retry\", path);\n if (m && method === \"POST\") {\n const app = m.params.app!;\n const id = m.params.id!;\n const denied = await authorize(\"delivery:retry\", {\n type: \"delivery\",\n applicationKey: app,\n deliveryId: id,\n });\n if (denied) return denied;\n return json(await ctx.core.retryDelivery(app, id, { actor }));\n }\n\n // --- Audit ---\n m = match(\"/api/applications/:app/audit\", path);\n if (m && method === \"GET\") {\n const app = m.params.app!;\n const denied = await authorize(\"audit:read\", { type: \"audit\", applicationKey: app });\n if (denied) return denied;\n return json(await ctx.core.listAudit(app));\n }\n\n return error(404, \"Not found\");\n } catch (err) {\n return mapError(err);\n }\n}\n\n/**\n * Cross-bundle error detection: `instanceof` first, `err.name` fallback. An\n * error thrown from a separate subpath bundle carries its own copy of the\n * class, so `instanceof` alone would mis-map it to 500.\n */\nfunction isNamed(err: unknown, ctor: new (...args: never[]) => Error, name: string): boolean {\n return err instanceof ctor || (err instanceof Error && err.name === name);\n}\n\n/**\n * A hook denial, detected by `name` rather than `instanceof`: a hook thrown from\n * a separate subpath bundle carries its own copy of the `HookDeniedError` class,\n * so `instanceof` would miss it and mis-map the denial to 500. Returns the\n * status to respond with, or `null` if not a denial.\n */\nfunction hookDeniedStatus(err: unknown): number | null {\n if (err instanceof HookDeniedError) return err.status;\n if (err instanceof Error && err.name === \"HookDeniedError\") {\n const status = (err as { status?: unknown }).status;\n return typeof status === \"number\" ? status : 403;\n }\n return null;\n}\n\n/** Map domain errors to HTTP responses. */\nfunction mapError(err: unknown): Response {\n if (isNamed(err, ReadonlyError, \"ReadonlyError\")) {\n return error(403, (err as Error).message, { code: \"READONLY\" });\n }\n const denied = hookDeniedStatus(err);\n if (denied !== null) return error(denied, (err as Error).message, { code: \"HOOK_DENIED\" });\n if (isNamed(err, PortalTokenError, \"PortalTokenError\")) {\n return error(401, (err as Error).message, { code: \"PORTAL_TOKEN\" });\n }\n if (isNamed(err, NotFoundError, \"NotFoundError\")) return error(404, (err as Error).message);\n if (isNamed(err, IdempotencyConflictError, \"IdempotencyConflictError\")) {\n return error(409, (err as Error).message, { code: \"IDEMPOTENCY_CONFLICT\" });\n }\n if (isNamed(err, ConflictError, \"ConflictError\")) {\n return error(409, (err as Error).message, { code: \"CONFLICT\" });\n }\n if (isNamed(err, PayloadTooLargeError, \"PayloadTooLargeError\")) {\n return error(413, (err as Error).message, { code: \"PAYLOAD_TOO_LARGE\" });\n }\n if (isNamed(err, ValidationError, \"ValidationError\")) {\n const errors = (err as { errors?: unknown }).errors;\n return error(422, (err as Error).message, {\n code: \"VALIDATION\",\n errors: Array.isArray(errors) ? errors : [],\n });\n }\n if (err instanceof SyntaxError) return error(400, \"Invalid JSON body\");\n const message = err instanceof Error ? err.message : \"Internal error\";\n return error(500, message);\n}\n","/**\n * Serves the bundled SPA's static assets from the UI output directory. Maps file\n * extensions to content types and guards against path traversal.\n *\n * @module\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, normalize } from \"node:path\";\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".mjs\": \"text/javascript; charset=utf-8\",\n \".css\": \"text/css; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".map\": \"application/json; charset=utf-8\",\n \".txt\": \"text/plain; charset=utf-8\",\n};\n\nfunction contentType(path: string): string {\n const dot = path.lastIndexOf(\".\");\n const ext = dot >= 0 ? path.slice(dot).toLowerCase() : \"\";\n return CONTENT_TYPES[ext] ?? \"application/octet-stream\";\n}\n\n/** True if the path has a file extension (so a miss should 404 rather than fall through to the SPA). */\nexport function looksLikeAsset(path: string): boolean {\n const last = path.split(\"/\").pop() ?? \"\";\n return last.includes(\".\");\n}\n\n/**\n * Try to serve `path` (base-path-stripped, leading `/`) as a static file from\n * `uiDir`. Returns a `Response`, or `null` if the file does not exist.\n */\nexport async function serveStaticAsset(uiDir: string, path: string): Promise<Response | null> {\n // Resolve within uiDir and refuse anything that escapes it.\n const rel = normalize(path).replace(/^(\\.\\.(\\/|\\\\|$))+/, \"\");\n const full = join(uiDir, rel);\n if (!full.startsWith(normalize(uiDir))) return null;\n try {\n const data = await readFile(full);\n return new Response(new Uint8Array(data), {\n status: 200,\n headers: {\n \"content-type\": contentType(full),\n \"cache-control\": rel.includes(\"/assets/\")\n ? \"public, max-age=31536000, immutable\"\n : \"no-cache\",\n },\n });\n } catch {\n return null;\n }\n}\n","/**\n * The web-standard fetch handler at the heart of every framework adapter and the\n * standalone app. Composes auth/authorization (plus portal-token scoping), the\n * JSON admin API, static-asset serving, and SPA fallback into a single\n * `(request: Request) => Promise<Response>` — and, by default, starts the\n * in-process delivery dispatcher so mounting the panel is all it takes to\n * deliver webhooks.\n *\n * @module\n */\n\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AuthProvider } from \"../auth/contract.ts\";\nimport type { AuthorizationProvider } from \"../authorization/contract.ts\";\nimport { createWebhooksCore, type RetentionOptions, type WebhooksCore } from \"../core.ts\";\nimport type { DeliveryErrorReporter, DeliveryListener } from \"../delivery-sink.ts\";\nimport { createDispatcher, type Dispatcher, type DispatcherOptions } from \"../dispatcher.ts\";\nimport type { HookErrorReporter, WebhooksHooksInput } from \"../hooks/contract.ts\";\nimport { hasDeliveryQueue, isCompareAndSwap, type WebhooksStorage } from \"../storage/contract.ts\";\nimport { normalizeBasePath, stripBasePath } from \"./base-path.ts\";\nimport { applyCorsHeaders, preflightResponse, type WebhooksCorsOptions } from \"./cors.ts\";\nimport { buildOpenApiDocument } from \"./openapi.ts\";\nimport { renderIndexHtml } from \"./render-index-html.ts\";\nimport { handleApiRequest, type ApiContext, type WebhooksPortalOptions } from \"./routes.ts\";\nimport { looksLikeAsset, serveStaticAsset } from \"./static-assets.ts\";\n\n/** Options for the panel handler (shared by every framework adapter). */\nexport interface WebhooksPanelOptions {\n /**\n * Control-plane store: applications, event types, endpoints, messages, audit.\n * Required unless a prebuilt `core` is supplied (which carries its own\n * storage, and its `queueStorage`, as the source of truth).\n */\n storage?: WebhooksStorage;\n /** Store for deliveries, attempts, and the due index. Defaults to `storage`. */\n queueStorage?: WebhooksStorage;\n /** Mount prefix, e.g. `\"/webhooks\"`. Default `\"\"` (root). */\n basePath?: string;\n /** Authentication provider. Default: anonymous (no auth). */\n auth?: AuthProvider;\n /** Authorization provider. Default: allow all. */\n authorization?: AuthorizationProvider;\n /**\n * Portal-token composition: requests bearing a valid `whpt_…` token act as a\n * portal principal scoped to the token's application. Mint tokens\n * server-side with `createPortalToken`. See\n * {@link ./routes.WebhooksPortalOptions}.\n */\n portal?: WebhooksPortalOptions;\n /** Block all mutating operations when true. */\n readonly?: boolean;\n /** UI title shown in the page and bootstrap config. */\n title?: string;\n /** Logo image URL shown in the navbar in place of the title wordmark. */\n logoUrl?: string;\n /** Override the directory the bundled UI is served from (defaults to `./ui` beside this module). */\n uiDir?: string;\n /** Reuse an existing core instead of constructing one. */\n core?: WebhooksCore;\n /**\n * Control-plane hooks fired around admin mutations (see\n * {@link ../hooks/contract.WebhooksHooks}). Ignored when `core` is supplied —\n * configure hooks on that core instead.\n */\n hooks?: WebhooksHooksInput;\n /** Reporter for errors thrown by `after` hooks. Default: `console.warn`. */\n onHookError?: HookErrorReporter;\n /**\n * Message/audit retention policy. Ignored when a prebuilt `core` is supplied —\n * configure it on that core instead. See {@link ../core.RetentionOptions}.\n */\n retention?: RetentionOptions;\n /**\n * Fire-and-forget sink invoked for **every** delivery attempt (metrics tap).\n * Ignored when `core` is supplied. See {@link ../delivery-sink.DeliveryListener}.\n */\n onDelivery?: DeliveryListener;\n /** Reporter for errors thrown by `onDelivery`. Default: `console.warn`. */\n onDeliveryError?: DeliveryErrorReporter;\n /**\n * Enable CORS on the handler itself — answers `OPTIONS` preflights and attaches\n * `Access-Control-*` headers to every response, so a **cross-origin** embed\n * works regardless of the host framework. See {@link ./cors.WebhooksCorsOptions}.\n */\n cors?: WebhooksCorsOptions;\n /**\n * Delivery-engine configuration. By default the panel creates **and starts**\n * a dispatcher so deliveries flow the moment the panel is mounted. Pass\n * `false` to skip it entirely (split-worker deployments where a separate\n * process runs `xtandard-webhooks dispatch` against the same storage).\n */\n dispatcher?: DispatcherOptions | false;\n}\n\n/** Return shape of {@link createFetchHandler}. */\nexport interface CreateFetchHandlerResult {\n /** Web-standard request handler. */\n fetch(request: Request): Promise<Response>;\n /** The underlying core (handy for `publish()`, tests, CLI, and standalone wiring). */\n core: WebhooksCore;\n /** The started dispatcher, or `null` when `dispatcher: false`. */\n dispatcher: Dispatcher | null;\n /**\n * The admin API as an OpenAPI 3.1 document (also served at `{basePath}/api/openapi.json`).\n * Merge it into your host app's docs — e.g. Elysia `@elysiajs/openapi` `references`.\n */\n openapi(): Record<string, unknown>;\n}\n\n// Anonymous defaults keep embedded usage zero-config; harden via auth/authorization.\nconst defaultAuth: AuthProvider = { authenticate: async () => ({ id: \"anonymous\" }) };\nconst defaultAuthorization: AuthorizationProvider = { authorize: async () => true };\n\n/**\n * Locate the built admin SPA (`dist/ui`). When this module runs compiled (the\n * normal npm-consumer case) it lives in `dist/` and the bundle is the sibling\n * `./ui`. When it runs from TypeScript source (examples / dev against a\n * `file:`-linked checkout, where the runtime executes `src/server/*.ts`\n * directly), the bundle is instead at `<repo>/dist/ui` — i.e. `../../dist/ui`\n * relative to `src/server/`. Try the candidates and return the first that\n * exists, falling back to the compiled-layout path so the \"build the UI\" hint\n * still fires when nothing is built yet.\n */\nfunction defaultUiDir(): string {\n try {\n const candidates = [\n new URL(\"./ui\", import.meta.url), // compiled: dist/<chunk>.mjs → dist/ui\n new URL(\"../../dist/ui\", import.meta.url), // source: src/server/*.ts → <repo>/dist/ui\n ].map((u) => fileURLToPath(u));\n return candidates.find((p) => existsSync(p)) ?? candidates[0]!;\n } catch {\n return \"./ui\";\n }\n}\n\n/**\n * Build the panel fetch handler.\n *\n * @example\n * ```ts\n * import { createFetchHandler } from \"@xtandard/webhooks\";\n * import { createFileStorage } from \"@xtandard/webhooks/storage/file\";\n *\n * const storage = createFileStorage({ dir: \"./data/webhooks\" });\n * const { fetch, core } = createFetchHandler({\n * storage,\n * basePath: \"/webhooks\",\n * title: \"Acme Webhooks\",\n * });\n *\n * Bun.serve({ port: 3000, fetch });\n * // Elsewhere in the app:\n * await core.publish(\"acme\", { eventType: \"invoice.paid\", payload: { id: \"inv_1\" } });\n * ```\n */\nexport function createFetchHandler(options: WebhooksPanelOptions): CreateFetchHandlerResult {\n const basePath = normalizeBasePath(options.basePath);\n const readonly = options.readonly ?? false;\n const title = options.title ?? \"@xtandard/webhooks\";\n const uiDir = options.uiDir ?? defaultUiDir();\n const dispatcherOptions = options.dispatcher === false ? undefined : options.dispatcher;\n\n // Either a prebuilt `core` (its own source of truth for storage) or a\n // `storage` to build one from is required.\n if (!options.core && !options.storage) {\n throw new Error(\"createFetchHandler requires either `storage` or a prebuilt `core`. Pass one.\");\n }\n\n const core =\n options.core ??\n createWebhooksCore({\n storage: options.storage!,\n ...(options.queueStorage ? { queueStorage: options.queueStorage } : {}),\n readonly,\n ...(options.hooks !== undefined ? { hooks: options.hooks } : {}),\n ...(options.onHookError ? { onHookError: options.onHookError } : {}),\n ...(options.retention ? { retention: options.retention } : {}),\n ...(options.onDelivery ? { onDelivery: options.onDelivery } : {}),\n ...(options.onDeliveryError ? { onDeliveryError: options.onDeliveryError } : {}),\n ...(dispatcherOptions ? { dispatcher: dispatcherOptions } : {}),\n });\n\n // The panel owns the default embedded deployment shape: dispatcher created\n // AND started (timers are unref()ed — it never keeps the process alive).\n let dispatcher: Dispatcher | null = null;\n if (options.dispatcher !== false) {\n // Claiming is only exclusive across processes when the queue storage has\n // native claimDue (redis/memory) or compare-and-swap. On plainer backends\n // (postgres/file/mongodb/…) two dispatchers can claim the same delivery and\n // double-send. Since the panel auto-starts one on EVERY mount, warn loudly\n // so a horizontally-scaled deployment sets `dispatcher: false` on all but\n // one instance (or runs a single split worker). See docs/DELIVERY.md.\n //\n // Check the CORE's queue storage — that is what the dispatcher actually\n // claims over (`core.claimDueDeliveries`). It is the source of truth\n // whether the core was built here or supplied prebuilt with its own\n // split `queueStorage`.\n const queue = core.options.queueStorage;\n if (!hasDeliveryQueue(queue) && !isCompareAndSwap(queue)) {\n // eslint-disable-next-line no-console\n console.warn(\n \"[@xtandard/webhooks] The panel started an in-process dispatcher over storage \" +\n \"without atomic claiming (no claimDue/compareAndSwap). If you run more than one \" +\n \"instance, deliveries WILL be sent multiple times — set `dispatcher: false` on all \" +\n \"but one instance, or use redis/memory storage. See docs/DELIVERY.md.\",\n );\n }\n dispatcher = createDispatcher(core, dispatcherOptions ?? {});\n dispatcher.start();\n }\n\n const apiCtx: ApiContext = {\n core,\n auth: options.auth ?? defaultAuth,\n authorization: options.authorization ?? defaultAuthorization,\n title,\n readonly,\n basePath,\n ...(options.logoUrl !== undefined ? { logoUrl: options.logoUrl } : {}),\n ...(options.portal ? { portal: options.portal } : {}),\n };\n\n const cors = options.cors;\n\n async function fetch(request: Request): Promise<Response> {\n // CORS preflight: answer OPTIONS directly so it works for any path,\n // independent of the host framework wrapping this handler.\n if (cors && request.method === \"OPTIONS\") return preflightResponse(request, cors);\n const response = await respond(request);\n return cors ? applyCorsHeaders(request, response, cors) : response;\n }\n\n async function respond(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const path = stripBasePath(url.pathname, basePath);\n\n // 1. JSON API + bootstrap config.\n const api = await handleApiRequest(request, path, apiCtx);\n if (api) return api;\n\n // 2. Only GET/HEAD reach the static UI.\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n return new Response(\"Method Not Allowed\", { status: 405 });\n }\n\n // 3. Static asset.\n const asset = await serveStaticAsset(uiDir, path);\n if (asset) return asset;\n\n // 4. A path that looks like a file but was not found → 404 (don't mask with the SPA).\n if (path !== \"/\" && looksLikeAsset(path)) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n // 5. SPA fallback.\n const html = await renderIndexHtml(uiDir, {\n title,\n basePath,\n readonly,\n ...(options.logoUrl !== undefined ? { logoUrl: options.logoUrl } : {}),\n });\n return new Response(html, {\n status: 200,\n headers: { \"content-type\": \"text/html; charset=utf-8\" },\n });\n }\n\n return { fetch, core, dispatcher, openapi: () => buildOpenApiDocument({ basePath, title }) };\n}\n"],"mappings":"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"}
|
|
@@ -61,8 +61,12 @@ declare const DEFAULT_PORTAL_ACTIONS: readonly WebhooksAction[];
|
|
|
61
61
|
//#region src/server/create-fetch-handler.d.ts
|
|
62
62
|
/** Options for the panel handler (shared by every framework adapter). */
|
|
63
63
|
interface WebhooksPanelOptions {
|
|
64
|
-
/**
|
|
65
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Control-plane store: applications, event types, endpoints, messages, audit.
|
|
66
|
+
* Required unless a prebuilt `core` is supplied (which carries its own
|
|
67
|
+
* storage, and its `queueStorage`, as the source of truth).
|
|
68
|
+
*/
|
|
69
|
+
storage?: WebhooksStorage;
|
|
66
70
|
/** Store for deliveries, attempts, and the due index. Defaults to `storage`. */
|
|
67
71
|
queueStorage?: WebhooksStorage;
|
|
68
72
|
/** Mount prefix, e.g. `"/webhooks"`. Default `""` (root). */
|
|
@@ -159,4 +163,4 @@ interface CreateFetchHandlerResult {
|
|
|
159
163
|
declare function createFetchHandler(options: WebhooksPanelOptions): CreateFetchHandlerResult;
|
|
160
164
|
//#endregion
|
|
161
165
|
export { WebhooksPortalOptions as a, DEFAULT_PORTAL_ACTIONS as i, WebhooksPanelOptions as n, WebhooksCorsOptions as o, createFetchHandler as r, CreateFetchHandlerResult as t };
|
|
162
|
-
//# sourceMappingURL=create-fetch-handler-
|
|
166
|
+
//# sourceMappingURL=create-fetch-handler-BN9vXbgW.d.mts.map
|