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

@odla-ai/email

v0.3.1

Published

Send and receive email from odla apps and agents — structured messages for Cloudflare Email Service's modern send() API (multi-recipient, cc/bcc, attachments, messageId receipts), invite/onboarding rendering, inbound parsing with DMARC verification, and t

Readme

@odla-ai/email

Send and receive email from odla apps and agents. A small, dependency-free, isomorphic library (Node 20+, Cloudflare Workers, the browser) that:

  • shapes structured outbound messages (EmailPayload) for Cloudflare Email Service's modern send() API — html/text bodies, multiple recipients, cc/bcc/replyTo, custom headers, and attachments (EmailAttachment) — validated fail-closed by validatePayload;
  • renders invite / onboarding emails (renderInvite) whose default body sends people to the Quickstart and starts agents with npx @odla-ai/cli setup;
  • parses inbound mail (parseInbound, verifyInbound) and threads replies (buildReply).

It never imports cloudflare:email. Transport is a seam: a Worker implements the one-method EmailSender interface over its send_email binding and returns the binding's receipt (EmailSendReceipt, carrying the transport's messageId); tests implement it as a spy. That keeps this package pure and portable, and confines the Cloudflare runtime to the @odla/email-router Worker that consumes it.

Sending an invite

import { renderInvite, sendMessage, type EmailSender } from "@odla-ai/email";

// In a Worker, `sender` adapts env.EMAIL (the send_email binding); see
// @odla/email-router's cfSender for the reference adapter:
//   const sender = { async send(p) {
//     const { messageId } = await env.EMAIL.send({ ...p });  // structured API
//     return { messageId };
//   }};

const invite = renderInvite({
  from: { name: "odla", email: "[email protected]" },
  to: "[email protected]",
  acceptUrl: "https://odla.ai/accept?__clerk_ticket=…", // from Clerk
});
const { messageId } = await sendMessage(sender, invite);

renderInvite returns a validated EmailPayload — the structured shape Cloudflare Email Service's send() accepts ({ from, to, subject, html, text, cc, bcc, replyTo, headers, attachments }). Pass instructions to override the default onboarding block, or appName / recipientName to personalise.

A failed send surfaces as EmailError with the transport's machine-readable code preserved — Cloudflare's E_SENDER_NOT_VERIFIED, E_RATE_LIMIT_EXCEEDED, and E_TOO_MANY_RECIPIENTS.

Payload safety

validatePayload rejects CR/LF in every caller-controlled header component (subject, addresses, display names, custom header names/values, attachment metadata), checks address syntax on from/to/cc/bcc/replyTo, and enforces Cloudflare's limits, exported as MAX_RECIPIENTS (50 combined to/cc/bcc; toAddressList normalizes the single-or-array Recipients forms) and MAX_ATTACHMENTS (32). renderInvite and buildReply validate before returning, so an unsafe reflected inbound Message-ID fails closed. These checks protect message structure; they do not sanitize HTML bodies.

Receiving mail

import { parseInbound, verifyInbound, buildReply } from "@odla-ai/email";

// In a Worker's email() handler:
const raw = await new Response(message.raw).text();
const mail = parseInbound(raw);
if (!verifyInbound(mail).ok) return message.setReject("DMARC failed");
const reply = buildReply(mail, { from: "[email protected]", text: "Got it." });
await sender.send(reply); // threaded via In-Reply-To / References headers

parseInbound handles single-part bodies (base64 / quoted-printable). Rich multipart + attachment parsing is intentionally left to postal-mime in the receiving Worker so this library stays dependency-free.

Local development

wrangler dev simulates the send_email binding: the message is logged and written to a temp file, and send() returns a synthesized messageId — nothing is delivered. Add "remote": true to the binding in wrangler.jsonc to send real email from local dev (requires the onboarded sending domain). The local simulator cannot serialize ArrayBuffer attachment content — use "remote": true or string content when exercising attachments locally. The inbound email() handler is also drivable locally: POST /cdn-cgi/handler/email?from=…&to=… with a raw RFC 5322 body.

API

Inspect the installed package's exported TypeScript declarations/JSDoc, or the rendered reference at https://odla.ai/docs/packages/email. Key exports: sendMessage, validatePayload, toAddressList, renderInvite / inviteContent, parseInbound, verifyInbound, buildReply, EmailError; the EmailPayload, EmailAttachment, Recipients, EmailSender, and EmailSendReceipt types; the MAX_RECIPIENTS / MAX_ATTACHMENTS limits; and the decode primitives (decodeBase64, decodeHeaderWord, …).