@molecule/api-webhook-queue
Provider bond · webhook · API (Node) · v1.0.3 · Apache-2.0
Queue-backed reliable webhook provider for molecule.dev
npm install @molecule/api-webhook-queuenpm · Source on GitHub · Implements @molecule/api-webhook
How it works
@molecule/api-webhook-queue is a provider bond on the API (Node) side: it implements the webhook core interface (@molecule/api-webhook) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider } from '@molecule/api-webhook'
import { createProvider } from '@molecule/api-webhook-queue'
// Bond at startup
setProvider(createProvider())
// Or with custom configuration
setProvider(
createProvider({
maxRetries: 10,
baseDelay: 2000,
concurrency: 5,
}),
)Works with: @molecule/api-webhook
Reference
Queue-backed webhook provider for molecule.dev.
Implements the @molecule/api-webhook interface using an internal job queue
with exponential backoff retries and configurable concurrency.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider } from '@molecule/api-webhook'
import { createProvider } from '@molecule/api-webhook-queue'
// Bond at startup
setProvider(createProvider())
// Or with custom configuration
setProvider(
createProvider({
maxRetries: 10,
baseDelay: 2000,
concurrency: 5,
}),
)
Type
provider
Installation
npm install @molecule/api-webhook-queue @molecule/api-webhook undici
API
Interfaces
QueueWebhookConfig
Configuration for the queue-backed webhook provider.
interface QueueWebhookConfig {
/** HTTP request timeout in milliseconds. Defaults to 30000 (30 seconds). */
timeout?: number
/** Maximum number of retry attempts per delivery. Defaults to 5. */
maxRetries?: number
/** Base delay for exponential backoff in milliseconds. Defaults to 1000. */
baseDelay?: number
/** Maximum delay cap for exponential backoff in milliseconds. Defaults to 60000. */
maxDelay?: number
/** Maximum number of concurrent deliveries. Defaults to 10. */
concurrency?: number
/** HTTP header name used to send the HMAC payload signature. Defaults to `'x-webhook-signature'`. */
signatureHeader?: string
}
Types
JobStatus
Internal state of a queued delivery job.
type JobStatus = 'pending' | 'processing' | 'completed' | 'failed'
Functions
createProvider(config)
Creates a queue-backed WebhookProvider.
function createProvider(config?: QueueWebhookConfig): WebhookProvider
config— Queue webhook provider configuration.
Returns: A fully initialised WebhookProvider with queue-based delivery.
isPrivateIPv4(ip)
True for an IPv4 address in any private/loopback/link-local/CGNAT/reserved range.
function isPrivateIPv4(ip: string): boolean
isPrivateIPv6(ip)
True for an IPv6 loopback/unspecified/unique-local/link-local (or mapped-private v4).
function isPrivateIPv6(ip: string): boolean
isPrivateAddress(ip)
True if ip (a literal IPv4/IPv6 string) is private/internal/metadata.
function isPrivateAddress(ip: string): boolean
safeFetch(rawUrl, init)
SSRF-safe replacement for fetch when the URL is (or derives from) untrusted input.
Only http(s) is allowed; the connection is pinned away from private/internal/metadata
addresses (both IP-literal hosts — checked synchronously since they skip DNS — and
hostnames — validated inside the connect lookup). Redirects are not auto-followed so
a 3xx cannot rebind to an internal host.
function safeFetch(rawUrl: string, init?: RequestInit): Promise<Response>
rawUrl— The (untrusted) URL to fetch.init— Standard fetch init.
Returns: The fetch Response.
Core Interface
Implements @molecule/api-webhook interface.
Injection Notes
Requirements
Peer dependencies:
@molecule/api-webhook^1.0.1
Runtime Dependencies
-
@molecule/api-webhook -
undici -
dispatch()returns an ACCEPTANCE receipt, NOT a delivery result. It enqueues one job per matching registration and immediately returns{ status: 202, success: true, deliveryId }per registration:202means QUEUED (accepted for async delivery), NOT delivered, andsuccessmeans "successfully enqueued" — neither claims the receiver was reached. The real per-delivery outcome (the receiver's 2xx/failure) is recorded asynchronously; pollgetDeliveryLog(webhookId)and match on the returneddeliveryIdfor the final status. A delivery that later FAILS is recorded assuccess: falsein the log and is never reported as a success. (Need the delivery outcome synchronously? Use@molecule/api-webhook-http, which delivers inline and returns the real result.) -
WebhookOptions.retryCountis HONORED, per registration. Each webhook retries up to its ownretryCounton failure (captured at register time; defaults to the provider-levelmaxRetries, default 5) with exponential backoff betweenbaseDelayandmaxDelay. The providermaxRetriesis only the default for registrations that omitretryCount. -
All state is in-memory — NOT durable. registrations, delivery logs, and the job queue itself are plain in-memory structures. A restart LOSES registrations AND any pending/in-flight deliveries; this bond adds async delivery + backoff over the http bond, not durability. Do NOT build a delivery-reliability guarantee on it — persist registrations yourself and re-register at boot, and back it with a durable queue/store if you need at-least-once delivery across restarts.
-
Same delivery format and connect-time SSRF guard as
@molecule/api-webhook-http: JSON POST withx-webhook-event+ hex HMAC-SHA256 of the body inx-webhook-signature(configurable), private/metadata destinations refused at connect (failed deliveries in the log, never a throw).
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- Registering a webhook endpoint through the app's UI/API succeeds, and
an event the app dispatches actually produces a delivery. The sandbox
CAPTURES outbound deliveries instead of POSTing — read them with the
read_activitytool (filter type 'webhook'); never mock the dispatch or modify production code to expose the payload. - The captured delivery carries the signature header (derived from the registration's secret), a stable delivery-id header the receiver can dedup on (at-least-once), and an event payload free of secrets/unrelated PII.
- A registration targeting a private/link-local/metadata destination
(
localhost,10.…,169.254.169.254) is REJECTED before any dispatch.