Skip to content

@lastshotlabs/slingshot-mail

npm install @lastshotlabs/slingshot-mail

Creates a durable, Redis-backed mail queue powered by BullMQ.

Requires bullmq and ioredis as installed dependencies — both are optional peers. The queue tests the Redis connection eagerly on start() to surface misconfiguration at startup rather than at first delivery attempt.

Non-retryable failures (e.g. provider-rejected messages) are wrapped in BullMQ.UnrecoverableError so they bypass BullMQ’s built-in retry backoff and are dead-lettered immediately. The original MailSendError context (statusCode, providerError) is preserved and forwarded to onDeadLetter.

function createBullMQMailQueue(config: BullMQMailQueueConfig): MailQueue

Source: packages/slingshot-mail/src/queues/bullmq.ts

Creates the slingshot-mail plugin for event-driven transactional email delivery.

Validates the config and adapter shapes at construction time (fail-fast). Mail delivery is entirely event-driven — subscribe to SlingshotEventMap keys via config.subscriptions and the plugin dispatches templated emails automatically when those events fire.

function createMailPlugin(rawConfig: MailPluginConfig): SlingshotPlugin

Source: packages/slingshot-mail/src/plugin.ts

Creates an in-process, non-durable mail queue for development and testing.

Jobs are held in memory and processed inline - no external dependencies required. This queue is not suitable for production: all pending jobs are lost on process restart. Use createBullMQMailQueue for durable, Redis-backed delivery.

Remarks: Prints a startup warning to console.warn to make the non-durable nature visible. The warning is intentional and should not be suppressed.

function createMemoryQueue(config?: MailQueueConfig): MailQueue

Source: packages/slingshot-mail/src/queues/memory.ts

Creates a MailProvider backed by the Postmark API.

Uses the POST /email endpoint directly via fetch — no SDK dependency required. Rate-limit (429) and server errors (5xx) are marked retryable; all other failures are treated as permanent.

function createPostmarkProvider(config: PostmarkConfig): MailProvider

Source: packages/slingshot-mail/src/providers/postmark.ts

Creates a MailRenderer that interpolates simple {{variable}} placeholders into static HTML string templates.

No templating engine or build step required - templates are plain strings defined inline in code. For rich component-based templates, use createReactEmailRenderer instead.

function createRawHtmlRenderer(config: RawHtmlRendererConfig): MailRenderer

Source: packages/slingshot-mail/src/renderers/rawHtml.ts

Creates a MailRenderer backed by @react-email/render.

Components are plain React functional components that receive the dataMapper output as props. @react-email/render is loaded lazily on first render - install it as a peer dependency. Plain-text extraction is attempted via render(..., { plainText: true }); if it fails the text body is omitted with a warning.

Remarks: Components must be synchronous. Async server components are not supported by @react-email/render.

function createReactEmailRenderer(config: ReactEmailRendererConfig): MailRenderer

Source: packages/slingshot-mail/src/renderers/reactEmail.ts

Creates a MailProvider backed by the Resend API.

Uses the POST /emails endpoint directly via fetch — no SDK dependency required. Rate-limit (429) and server errors (5xx) are marked retryable; all other failures are treated as permanent.

function createResendProvider(config: ResendConfig): MailProvider

Source: packages/slingshot-mail/src/providers/resend.ts

Creates a MailProvider backed by the SendGrid Mail Send API v3.

Uses the POST /v3/mail/send endpoint directly via fetch — no SDK dependency required. SendGrid returns HTTP 202 Accepted for queued messages, so SendResult.status is always 'sent' (delivery is async on SendGrid’s end). Rate-limit (429) and server errors (5xx) are marked retryable.

function createSendgridProvider(config: SendgridConfig): MailProvider

Source: packages/slingshot-mail/src/providers/sendgrid.ts

Creates a MailProvider backed by AWS SES v2 (@aws-sdk/client-sesv2).

The AWS SDK is loaded lazily on first send — install @aws-sdk/client-sesv2 as a peer dependency. Credentials can be provided explicitly or resolved from the environment (IAM roles, ~/.aws/credentials, etc.).

function createSesProvider(config: SesConfig): MailProvider

Source: packages/slingshot-mail/src/providers/ses.ts

Zod schema for validating MailPluginConfig at runtime. Used internally by createMailPlugin — call validatePluginConfig against this.

Source: packages/slingshot-mail/src/types/config.ts

Validates that every subscription’s template is known to the configured renderer. Throws a MailTemplateNotFoundError listing the first missing template so callers can surface the misconfiguration at startup instead of at first event delivery.

async function validateSubscriptionTemplates(config: MailPluginConfig): Promise<void>

Source: packages/slingshot-mail/src/lib/subscriptionWiring.ts

Thrown when the breaker is open and refuses to invoke the provider.

retryAfterMs is the time remaining until the breaker enters half-open state. Workers can surface this as a backoff hint instead of treating the rejection as a generic transient failure.

Source: packages/slingshot-mail/src/lib/circuitBreaker.ts

Thrown by MailProvider.send() when delivery fails.

The retryable flag controls queue behaviour: non-retryable errors (e.g. invalid recipient, API auth failure) are dead-lettered immediately; retryable errors (e.g. rate limit, 5xx) are re-enqueued up to config.queueConfig.maxAttempts.

Source: packages/slingshot-mail/src/types/provider.ts

Thrown at plugin startup when a subscription’s template cannot be found in the configured renderer. Lets callers fail fast instead of waiting for the first matching event to fire at runtime.

Source: packages/slingshot-mail/src/lib/subscriptionWiring.ts

Runtime circuit breaker guarding outbound mail provider calls.

Source: packages/slingshot-mail/src/lib/circuitBreaker.ts

Snapshot of breaker state — useful for health endpoints and metrics.

Source: packages/slingshot-mail/src/lib/circuitBreaker.ts

Tunable options used to construct a mail provider circuit breaker.

Source: packages/slingshot-mail/src/lib/circuitBreaker.ts

A single mail delivery job tracked by the queue.

Exposed to onDeadLetter callbacks so callers can inspect what failed and why.

Source: packages/slingshot-mail/src/types/queue.ts

A fully-resolved email message ready to be handed to a MailProvider.

The html field is required; providers that support a text fallback will use text when present. from overrides the plugin-level default when set.

Source: packages/slingshot-mail/src/types/provider.ts

Interface that every mail transport adapter must implement.

Implementations are provided for Resend, SES, Postmark, and SendGrid. Implement this interface directly to support a custom transport.

Remarks: send() must throw MailSendError with retryable set correctly so the queue can make an informed retry decision. Non-MailSendError rejections are treated as retryable.

Source: packages/slingshot-mail/src/types/provider.ts

Interface that every mail queue implementation must satisfy.

Extend QueueLifecycle (start, stop, depth, drain) with mail-specific enqueue and start(provider). The in-process MemoryQueue and BullMQ-backed queue both implement this interface.

Remarks: Implement this interface to integrate a custom queue backend (e.g. SQS, RabbitMQ).

Source: packages/slingshot-mail/src/types/queue.ts

Configuration shared by all queue implementations.

Used when config.queue is omitted from MailPluginConfig so the plugin can create a default MemoryQueue; also accepted directly by createBullMQMailQueue.

Source: packages/slingshot-mail/src/types/queue.ts

Declarative binding from a bus event to a mail template.

When the bus emits event, the plugin resolves the recipient via recipientMapper, renders template with data from dataMapper, and enqueues a delivery.

Source: packages/slingshot-mail/src/types/config.ts

A static HTML template with optional {{variable}} interpolation placeholders.

Variable syntax: {{key}} - all values are coerced to strings safely. Missing keys render as empty strings rather than throwing.

Source: packages/slingshot-mail/src/renderers/rawHtml.ts

The result returned by a MailProvider after attempting to send a message.

  • sent — provider accepted and transmitted the message.
  • queued_by_provider — provider accepted and will deliver asynchronously (e.g. SendGrid).
  • rejected — provider accepted the request but explicitly rejected the message (e.g. hard bounce rules). The message should be dead-lettered, not retried.

Source: packages/slingshot-mail/src/types/provider.ts

An email address, expressed either as a bare address string or a structured object.

Source: packages/slingshot-mail/src/types/provider.ts

Configuration object accepted by createMailPlugin. Inferred from mailPluginConfigSchema — use the schema for runtime validation.

Source: packages/slingshot-mail/src/types/config.ts