Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/young-deserts-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opencoredev/email-sdk": minor
---

Allow CommonJS applications on Node.js 20.19+ to load every SDK entry point with `require()`.
11 changes: 5 additions & 6 deletions apps/fumadocs/content/docs/getting-started/install.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Install
description: Install the ESM-only SDK for Node.js 20+ or Bun 1.1+.
description: Install Email SDK for Node.js or Bun using ESM imports or CommonJS require.
---

<Steps>
Expand All @@ -19,12 +19,11 @@ The public package is `@opencoredev/email-sdk`. The unscoped `email-sdk` package

### Confirm your runtime

Use Node.js 20+ or Bun 1.1+. The package is ESM-only, so use `import` syntax or dynamic `import()` from a CommonJS application.
Use Node.js 20+ or Bun 1.1+. ESM imports work across the supported range. CommonJS applications running Node.js 20.19+ can use `require()` for the main package and every adapter entry point.

```json title="package.json"
{
"type": "module"
}
```js title="email.cjs"
const { createEmailClient } = require("@opencoredev/email-sdk");
const { resend } = require("@opencoredev/email-sdk/resend");
```

</Step>
Expand Down
4 changes: 2 additions & 2 deletions apps/fumadocs/content/docs/guides/migrate/v0-to-v1.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ v1 has a closed error-code union: `validation_error`, `adapter_not_found`, `adap

`EmailRouteError.failures` replaces arbitrary route details. Middleware exceptions become `EmailMiddlewareError`.

## 14. Confirm ESM and telemetry
## 14. Confirm runtime and telemetry

v1 is ESM-only on Node.js 20+ or Bun 1.1+. Telemetry remains enabled by default with the existing opt-outs: `telemetry: false`, `EMAIL_SDK_TELEMETRY=0`, or `DO_NOT_TRACK=1`.
v1 supports ESM imports on Node.js 20+ or Bun 1.1+, plus `require()` on Node.js 20.19+. Telemetry remains enabled by default with the existing opt-outs: `telemetry: false`, `EMAIL_SDK_TELEMETRY=0`, or `DO_NOT_TRACK=1`.

## 15. Use the compat bridge only temporarily

Expand Down
2 changes: 1 addition & 1 deletion apps/fumadocs/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Now call `email.send({ from, to, subject, text })`. Add retries, fallback, plugi

## Runtime boundary

Email SDK is ESM-only and server-side. It supports Node.js 20+ and Bun 1.1+. Keep API keys and SMTP credentials out of browser bundles.
Email SDK is server-side and supports Node.js 20+ or Bun 1.1+. Use ESM imports on any supported runtime, or `require()` from CommonJS applications on Node.js 20.19+. Keep API keys and SMTP credentials out of browser bundles.

<Card title="Install" href="/docs/getting-started/install" description="Install the SDK and confirm your Node.js or Bun version." />
<Card title="Schedule an email" href="/docs/guides/schedule-email" description="Use sendAt for provider scheduling, or keep long-running jobs in your own queue." />
Expand Down
6 changes: 3 additions & 3 deletions apps/fumadocs/src/lib/docs-lastmod.generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"concepts/sending-modes.mdx": "2026-07-22",
"getting-started/agent-skill.mdx": "2026-07-22",
"getting-started/credentials.mdx": "2026-07-22",
"getting-started/install.mdx": "2026-07-22",
"getting-started/install.mdx": "2026-07-30",
"getting-started/quickstart.mdx": "2026-07-22",
"guides/authoring/create-adapter.mdx": "2026-07-22",
"guides/authoring/create-first-plugin.mdx": "2026-07-22",
Expand All @@ -44,12 +44,12 @@
"guides/migrate/nodemailer.mdx": "2026-07-22",
"guides/migrate/resend.mdx": "2026-07-22",
"guides/migrate/sendgrid.mdx": "2026-07-22",
"guides/migrate/v0-to-v1.mdx": "2026-07-22",
"guides/migrate/v0-to-v1.mdx": "2026-07-30",
"guides/production-send-pipeline.mdx": "2026-07-22",
"guides/schedule-email.mdx": "2026-07-22",
"guides/test-email-behavior.mdx": "2026-07-22",
"guides/troubleshoot-failed-sends.mdx": "2026-07-22",
"index.mdx": "2026-07-22",
"index.mdx": "2026-07-30",
"integrations/ai-sdk.mdx": "2026-07-22",
"integrations/chat-sdk.mdx": "2026-07-22",
"integrations/convex/index.mdx": "2026-07-22",
Expand Down
2 changes: 1 addition & 1 deletion packages/email-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pnpm add @opencoredev/email-sdk
bun add @opencoredev/email-sdk
```

The public npm package is `@opencoredev/email-sdk`. The CLI binary it installs is `email-sdk`. Use both from server-side runtimes such as Node.js 20+ or Bun 1.1+, and never expose provider API keys in browser code.
The public npm package is `@opencoredev/email-sdk`. The CLI binary it installs is `email-sdk`. Use both from server-side runtimes such as Node.js 20+ or Bun 1.1+, and never expose provider API keys in browser code. CommonJS applications running Node.js 20.19+ can load the package and its adapter entry points with `require()`.

## Quickstart

Expand Down
68 changes: 34 additions & 34 deletions packages/email-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,139 +43,139 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"default": "./dist/index.js"
},
"./resend": {
"types": "./dist/resend.d.ts",
"import": "./dist/resend.js"
"default": "./dist/resend.js"
},
"./postmark": {
"types": "./dist/postmark.d.ts",
"import": "./dist/postmark.js"
"default": "./dist/postmark.js"
},
"./sendgrid": {
"types": "./dist/sendgrid.d.ts",
"import": "./dist/sendgrid.js"
"default": "./dist/sendgrid.js"
},
"./cloudflare": {
"types": "./dist/cloudflare.d.ts",
"import": "./dist/cloudflare.js"
"default": "./dist/cloudflare.js"
},
"./unosend": {
"types": "./dist/unosend.d.ts",
"import": "./dist/unosend.js"
"default": "./dist/unosend.js"
},
"./iterable": {
"types": "./dist/iterable.d.ts",
"import": "./dist/iterable.js"
"default": "./dist/iterable.js"
},
"./ses": {
"types": "./dist/ses.d.ts",
"import": "./dist/ses.js"
"default": "./dist/ses.js"
},
"./mailgun": {
"types": "./dist/mailgun.d.ts",
"import": "./dist/mailgun.js"
"default": "./dist/mailgun.js"
},
"./mailersend": {
"types": "./dist/mailersend.d.ts",
"import": "./dist/mailersend.js"
"default": "./dist/mailersend.js"
},
"./brevo": {
"types": "./dist/brevo.d.ts",
"import": "./dist/brevo.js"
"default": "./dist/brevo.js"
},
"./mailchimp": {
"types": "./dist/mailchimp.d.ts",
"import": "./dist/mailchimp.js"
"default": "./dist/mailchimp.js"
},
"./sparkpost": {
"types": "./dist/sparkpost.d.ts",
"import": "./dist/sparkpost.js"
"default": "./dist/sparkpost.js"
},
"./loops": {
"types": "./dist/loops.d.ts",
"import": "./dist/loops.js"
"default": "./dist/loops.js"
},
"./sequenzy": {
"types": "./dist/sequenzy.d.ts",
"import": "./dist/sequenzy.js"
"default": "./dist/sequenzy.js"
},
"./jetemail": {
"types": "./dist/jetemail.d.ts",
"import": "./dist/jetemail.js"
"default": "./dist/jetemail.js"
},
"./lettermint": {
"types": "./dist/lettermint.d.ts",
"import": "./dist/lettermint.js"
"default": "./dist/lettermint.js"
},
"./primitive": {
"types": "./dist/primitive.d.ts",
"import": "./dist/primitive.js"
"default": "./dist/primitive.js"
},
"./plunk": {
"types": "./dist/plunk.d.ts",
"import": "./dist/plunk.js"
"default": "./dist/plunk.js"
},
"./mailtrap": {
"types": "./dist/mailtrap.d.ts",
"import": "./dist/mailtrap.js"
"default": "./dist/mailtrap.js"
},
"./scaleway": {
"types": "./dist/scaleway.d.ts",
"import": "./dist/scaleway.js"
"default": "./dist/scaleway.js"
},
"./zeptomail": {
"types": "./dist/zeptomail.d.ts",
"import": "./dist/zeptomail.js"
"default": "./dist/zeptomail.js"
},
"./mailpace": {
"types": "./dist/mailpace.d.ts",
"import": "./dist/mailpace.js"
"default": "./dist/mailpace.js"
},
"./smtp": {
"types": "./dist/smtp.d.ts",
"import": "./dist/smtp.js"
"default": "./dist/smtp.js"
},
"./testing": {
"types": "./dist/testing.d.ts",
"import": "./dist/testing.js"
"default": "./dist/testing.js"
},
"./compat": {
"types": "./dist/compat.d.ts",
"import": "./dist/compat.js"
"default": "./dist/compat.js"
},
"./agent-tools": {
"types": "./dist/agent-tools.d.ts",
"import": "./dist/agent-tools.js"
"default": "./dist/agent-tools.js"
},
"./ai": {
"types": "./dist/ai.d.ts",
"import": "./dist/ai.js"
"default": "./dist/ai.js"
},
"./react": {
"types": "./dist/react.d.ts",
"import": "./dist/react.js"
"default": "./dist/react.js"
},
"./plugins/defaults": {
"types": "./dist/plugins-defaults.d.ts",
"import": "./dist/plugins-defaults.js"
"default": "./dist/plugins-defaults.js"
},
"./plugins/observability": {
"types": "./dist/plugins-observability.d.ts",
"import": "./dist/plugins-observability.js"
"default": "./dist/plugins-observability.js"
},
"./plugins/capture": {
"types": "./dist/plugins-capture.d.ts",
"import": "./dist/plugins-capture.js"
"default": "./dist/plugins-capture.js"
},
"./plugins/routing": {
"types": "./dist/plugins-routing.d.ts",
"import": "./dist/plugins-routing.js"
"default": "./dist/plugins-routing.js"
},
"./plugins/timeout": {
"types": "./dist/plugins-timeout.d.ts",
"import": "./dist/plugins-timeout.js"
"default": "./dist/plugins-timeout.js"
}
},
"publishConfig": {
Expand Down
18 changes: 18 additions & 0 deletions scripts/check-package-tarballs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,15 @@ try {

const smoke = join(install, "smoke.mjs");
await writeFile(smoke, smokeProgram());
const commonJsSmoke = join(install, "smoke.cjs");
await writeFile(commonJsSmoke, commonJsSmokeProgram());

console.log("Running installed-package smoke tests under Node...");
await run(["node", smoke], install);
await run(["node", commonJsSmoke], install);
console.log("Running installed-package smoke tests under Bun...");
await run(["bun", smoke], install);
await run(["bun", commonJsSmoke], install);
console.log("Installed package smoke tests passed.");
} finally {
await rm(scratch, { recursive: true, force: true });
Expand Down Expand Up @@ -196,3 +200,17 @@ async function declarationMaps(directory) {
}
`;
}

function commonJsSmokeProgram(): string {
return String.raw`
const { readFileSync } = require("node:fs");
const { join } = require("node:path");

const coreDir = join(__dirname, "node_modules/@opencoredev/email-sdk");
const manifest = JSON.parse(readFileSync(join(coreDir, "package.json"), "utf8"));

for (const subpath of Object.keys(manifest.exports ?? {})) {
require(subpath === "." ? manifest.name : manifest.name + subpath.slice(1));
}
`;
}