npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@render-lab/tasks-resend

v0.1.2

Published

Durable Resend email tasks for Render Workflows: resend.send, resend.template, resend.awaitReply (Resend via fetch, console fallback).

Readme

@render-lab/tasks-resend

⚠️ Experimental: proof of concept. This package is part of the Render Tasks POC and is published for testing only. It is not fully tested or production ready. Task names, inputs, outputs, and behavior can change or break in any release. Pin exact versions and expect breaking changes.

Durable Resend email tasks for Render Workflows. Email is vendor-first (ADR-0016): this is the Resend pack, not a generic email capability. Other providers (SendGrid, Postmark) get their own packs.

import { send } from "@render-lab/tasks-resend";

| Task | Input | Output | Transport | | ------------------------ | ----------------------------------------------------------- | ------------------------------------- | --------------------------------- | | resend.send | { to, from, subject, text?, html?, replyTo?, headers? } | { id, delivered } | Resend (console fallback) | | resend.sendBatch | { messages: SendInput[] } | { ids } | Resend (console fallback) | | resend.template | { subject, body, data? } | { subject, text, html } | pure (no I/O) | | resend.awaitReply | { toAddress, correlationId, sinceISO? } | { replied, from, text, receivedAt } | injected inbox port | | resend.createBroadcast | { audienceId, from, subject, html?, text?, name?, replyTo? } | { id } | Resend (key required) | | resend.sendBroadcast | { id, scheduledAt? } | { id } | Resend (key required) | | resend.addContact | { audienceId, email, firstName?, lastName?, unsubscribed? } | { id } | Resend (key required) | | resend.listAudiences | {} | { id, name }[] | Resend (key required) | | resend.getDeliveryStatus | { id } | { id, status, lastEvent? } | Resend (key required) | | resend.verifyWebhook | { payload, headers, secret? } | { id, type, verified, data } | pure (Svix HMAC, no I/O) |

send posts to Resend (https://api.resend.com/emails) via the built-in fetch. If RESEND_API_KEY is unset, the task logs to the console and returns { id: null, delivered: false }, so a demo workflow runs end-to-end with zero email setup — only "real delivery vs. not" changes when you add the secret (mirrors the Slack webhook fallback). A non-ok Resend response throws, so the durable task retry handles transient failures.

sendBatch POSTs an array of messages to /emails/batch and mirrors send's console fallback (logs each message, returns { ids: [] }) when no key is set. The management/read tasks — createBroadcast, sendBroadcast, addContact, listAudiences, getDeliveryStatus — have no meaningful console equivalent (they must return real provider data), so they fail close with a clear RESEND_API_KEY required for … error when the key is unset rather than fall back. All map Resend responses to plain DTOs — raw provider payloads never cross the task boundary.

template renders {{var}} placeholders in the subject and body into a plain-text text and a minimal, HTML-escaped html (one <p> per non-empty line). It is pure — no network, no retry.

Verifying webhooks: resend.verifyWebhook

verifyWebhook authenticates a Resend webhook. Resend signs with Svix: the signed content is ${svix-id}.${svix-timestamp}.${rawBody}, HMAC-SHA256'd with the secret (the base64-decoded portion after the whsec_ prefix), base64-encoded; the svix-signature header is a space-separated list of v1,<base64> entries. The task recomputes the HMAC and constant-time-compares it against each v1, entry, accepting if any matches. It is pure (no port, no retry — a bad signature is a permanent failure) and reads its secret from the secret input or RESEND_WEBHOOK_SECRET. Pass the raw request body verbatim (do not re-serialize) or the signature will not match. On success it returns the JSON-parsed { id, type, verified: true, data }; it throws on a bad or missing signature so the caller can reject the request.

Durable human-in-the-loop: resend.awaitReply

awaitReply is a poll-with-retry task. SDK 0.6.0 has no native sleep/await, so the task throws when no matching reply is present yet, and the durable AWAIT_REPLY_RETRY policy (poll ~every 15s for up to ~1h) re-runs it until a human replies. The run parks on this task across restarts — the durable wait, made concrete.

POC limitation: there is no default inbox provider. awaitReply requires an injected deps.inbox (an InboxPort) and throws a clear "no inbox configured" error until one is supplied. Wire your IMAP/webhook/provider inbox to the InboxPort.findReply seam.

Install

pnpm add @render-lab/tasks-resend @renderinc/sdk

@renderinc/sdk is a peer dependency. This package has no vendor SDK — it calls Resend with the built-in fetch.

Environment contract

| Variable | Required | Purpose | | ----------------------- | -------- | ---------------------------------------------------------------------------------------- | | RESEND_API_KEY | optional | Resend API key for resend.send/resend.sendBatch (console fallback if unset) and required for the broadcast/contact/audience/status tasks. Read lazily on first call. | | RESEND_WEBHOOK_SECRET | optional | Svix signing secret (whsec_…) for resend.verifyWebhook. Only needed when you verify webhooks and don't pass secret in the input. Read lazily on first call. |

Extending & testing

send exports its raw sendImpl, which depends on an EmailPort. resendPort accepts an injected fetchImpl and env, so both the impl and the transport unit-test without network access:

import { resendPort } from "@render-lab/tasks-resend";

const port = resendPort({ env: {} });           // no key -> console fallback
await port.send({ to: "[email protected]", from: "[email protected]", subject: "hi" }); // { id: null, delivered: false }

Run the tests with pnpm -C packages/tasks-resend test.