Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
ef6e3d2
feat: get inbound emails v0 (#641)
lucasfcosta Sep 30, 2025
71e8a7d
chore(deps): update dependency @biomejs/biome to v2.2.4 (#537)
renovate[bot] Sep 30, 2025
5a88b17
feat: bump version for inbound release (#642)
lucasfcosta Sep 30, 2025
302e620
chore: bump to 6.2.0-canary.1 (#649)
gabrielmfern Oct 1, 2025
1375e0b
chore(deps): update dependency @types/node to v22.18.8 (#638)
renovate[bot] Oct 3, 2025
f3f8df3
chore(deps): update dependency typescript to v5.9.3 (#645)
renovate[bot] Oct 3, 2025
be7f792
chore(deps): update tj-actions/changed-files digest to d6f020b (#651)
renovate[bot] Oct 3, 2025
4658a96
chore(deps): update pnpm to v10.18.0 (#653)
renovate[bot] Oct 3, 2025
e5bbd56
chore(deps): update dependency @biomejs/biome to v2.2.5 (#652)
renovate[bot] Oct 3, 2025
f5abf15
chore(deps): update dependency @types/react to v19.2.0 (#640)
renovate[bot] Oct 3, 2025
3f9190f
feat: show headers in response (#657)
lucasfcosta Oct 4, 2025
a7ba412
chore: release new canary for headers (#658)
lucasfcosta Oct 4, 2025
f1e7df4
chore: improve PR title check error (#664)
TyMick Oct 9, 2025
ed2e3c3
feat: move emails inbound method to emails.receiving.<method> (#666)
lucasfcosta Oct 9, 2025
06dba00
feat: move attachment inbound methods to be nested (#667)
lucasfcosta Oct 9, 2025
0dae1bc
feat: add inbound listing method (#668)
lucasfcosta Oct 9, 2025
ebc0e95
feat: add pagination for inbound email attachments (#670)
lucasfcosta Oct 10, 2025
177020a
chore(deps): update pnpm to v10.18.2 (#659)
renovate[bot] Oct 10, 2025
6f8a641
chore(deps): update dependency @types/node to v22.18.9 (#669)
renovate[bot] Oct 10, 2025
499ec8e
chore(deps): update pnpm/action-setup digest to 41ff726 (#665)
renovate[bot] Oct 10, 2025
ffa8622
feat: partial (API Keys, Audiences, and Contacts) non-mocked test cov…
TyMick Oct 14, 2025
446fb1e
feat: bump version (#675)
isabellaaquino Oct 14, 2025
feb8fd1
feat: merge project preview branches (#680)
Cisneiros Oct 14, 2025
f98e4c2
feat: download from API's signed URLs instead of proxied routes (#676)
lucasfcosta Oct 15, 2025
1b1ac45
fix: repo urls (#679)
zenorocha Oct 15, 2025
a1ec05f
feat: allow creating domain with capabilities through the api (#682)
lucasfcosta Oct 15, 2025
1b4ad28
feat: add verify webhooks (#636)
bukinoshita Oct 17, 2025
8cde75f
feat: avoid downloading attachments ourselves (#685)
lucasfcosta Oct 17, 2025
2a57b77
chore: bump to canary 6.3.0-canary.1 (#687)
lucasfcosta Oct 17, 2025
d6c46f2
chore: limited variable types (#692)
joaopcm Oct 17, 2025
5e19da5
fix: remove duplicated webhook import
lucasfcosta Oct 17, 2025
d70ba6e
fix: missing `statusCode` in ErrorRespnse type (#567)
gabrielmfern Oct 20, 2025
a83f4db
use try-catch again
gabrielmfern Oct 21, 2025
8cac8bd
set the target to es2020 so the distributed uses async-await
gabrielmfern Oct 21, 2025
40c68e8
use es2017
gabrielmfern Oct 21, 2025
2f650ee
add first version of integration testing with nextjs/esbuild
gabrielmfern Oct 21, 2025
0d32c63
use another package.json
gabrielmfern Oct 21, 2025
85b8635
copy over the integrations to the user's tmp directory
gabrielmfern Oct 21, 2025
2352714
remove unused depdnencies, update lock
gabrielmfern Oct 21, 2025
ea82ac0
ensure that the directory is deleted properly
gabrielmfern Oct 21, 2025
0b02b80
lint
gabrielmfern Oct 21, 2025
f8535e6
fix linting warning
gabrielmfern Oct 21, 2025
84b76ff
use js for config
gabrielmfern Oct 21, 2025
e72ac57
add types
gabrielmfern Oct 21, 2025
e688309
use js for nextjs integration
gabrielmfern Oct 21, 2025
6919b60
use cjs for next.config
gabrielmfern Oct 21, 2025
ef5f40a
dont use sequential for the integration tests
gabrielmfern Oct 21, 2025
ab1d509
feat: get inbound emails v0 (#641)
lucasfcosta Sep 30, 2025
ae5b029
chore(deps): update dependency @biomejs/biome to v2.2.4 (#537)
renovate[bot] Sep 30, 2025
a0b0943
feat: bump version for inbound release (#642)
lucasfcosta Sep 30, 2025
c0b1b4f
chore: bump to 6.2.0-canary.1 (#649)
gabrielmfern Oct 1, 2025
e648bb9
chore(deps): update dependency @types/node to v22.18.8 (#638)
renovate[bot] Oct 3, 2025
34b2267
chore(deps): update dependency typescript to v5.9.3 (#645)
renovate[bot] Oct 3, 2025
98fa90c
chore(deps): update tj-actions/changed-files digest to d6f020b (#651)
renovate[bot] Oct 3, 2025
5d9d306
chore(deps): update pnpm to v10.18.0 (#653)
renovate[bot] Oct 3, 2025
a5df0a5
chore(deps): update dependency @biomejs/biome to v2.2.5 (#652)
renovate[bot] Oct 3, 2025
9e4b04a
chore(deps): update dependency @types/react to v19.2.0 (#640)
renovate[bot] Oct 3, 2025
294aeba
feat: show headers in response (#657)
lucasfcosta Oct 4, 2025
bfb4026
chore: release new canary for headers (#658)
lucasfcosta Oct 4, 2025
2a5fd6d
chore: improve PR title check error (#664)
TyMick Oct 9, 2025
2022031
feat: move emails inbound method to emails.receiving.<method> (#666)
lucasfcosta Oct 9, 2025
54569ba
feat: move attachment inbound methods to be nested (#667)
lucasfcosta Oct 9, 2025
236c533
feat: add inbound listing method (#668)
lucasfcosta Oct 9, 2025
64a674f
feat: add pagination for inbound email attachments (#670)
lucasfcosta Oct 10, 2025
ffe4fce
chore(deps): update pnpm to v10.18.2 (#659)
renovate[bot] Oct 10, 2025
d4e34ee
chore(deps): update dependency @types/node to v22.18.9 (#669)
renovate[bot] Oct 10, 2025
798d80b
chore(deps): update pnpm/action-setup digest to 41ff726 (#665)
renovate[bot] Oct 10, 2025
41b7f7b
feat: partial (API Keys, Audiences, and Contacts) non-mocked test cov…
TyMick Oct 14, 2025
aae3602
feat: bump version (#675)
isabellaaquino Oct 14, 2025
653a6a3
feat: merge project preview branches (#680)
Cisneiros Oct 14, 2025
27a9a6c
feat: download from API's signed URLs instead of proxied routes (#676)
lucasfcosta Oct 15, 2025
4ba8340
fix: repo urls (#679)
zenorocha Oct 15, 2025
afc815a
feat: allow creating domain with capabilities through the api (#682)
lucasfcosta Oct 15, 2025
4747aea
feat: add verify webhooks (#636)
bukinoshita Oct 17, 2025
def00a1
feat: avoid downloading attachments ourselves (#685)
lucasfcosta Oct 17, 2025
de2e0e3
chore: bump to canary 6.3.0-canary.1 (#687)
lucasfcosta Oct 17, 2025
f6b5102
chore: limited variable types (#692)
joaopcm Oct 17, 2025
f0ccb0e
fix: remove duplicated webhook import
lucasfcosta Oct 17, 2025
8d6cb55
fix: missing `statusCode` in ErrorRespnse type (#567)
gabrielmfern Oct 20, 2025
ac6889e
feat: introduce the /segment API endpoints (#689)
Cisneiros Oct 21, 2025
e00acf7
Merge branch 'canary' into feat/integration-tests
gabrielmfern Oct 21, 2025
b4e9697
undo merge-introduced issues D:
gabrielmfern Oct 21, 2025
5858436
remove extra import
gabrielmfern Oct 21, 2025
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
10 changes: 4 additions & 6 deletions .github/scripts/pr-title-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ const isValidType = (title) =>
const validateTitle = (title) => {
if (!isValidType(title)) {
console.error(
`PR title does not follow the required format.
example: "type: My PR Title"

- type: "feat", "fix", "chore", or "refactor"
- First letter of the PR title needs to be lowercased
`,
`PR title does not follow the required format "[type]: [title]".
- Example: "fix: email compatibility issue"
- Allowed types: 'feat', 'fix', 'chore', 'refactor'
- First letter of the title portion (after the colon) must be lowercased`,
);
process.exit(1);
}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
- name: pnpm setup
uses: pnpm/action-setup@f2b2b233b538f500472c7274c7012f57857d8ce0
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
- name: Install packages
run: pnpm install
- name: Run Lint
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/preview-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Find changed files
id: changed_files
if: github.event_name == 'pull_request'
uses: tj-actions/changed-files@212f9a7760ad2b8eb511185b841f3725a62c2ae0
uses: tj-actions/changed-files@d6f020b1d9d7992dcf07f03b14d42832f866b495
with:
files: src/**/*
dir_names: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
- name: pnpm setup
uses: pnpm/action-setup@f2b2b233b538f500472c7274c7012f57857d8ce0
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
- name: Install packages
run: pnpm install
- name: Run Tests
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
dist
build
.env.test
1 change: 1 addition & 0 deletions integrations/esbuild/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
index.js
3 changes: 3 additions & 0 deletions integrations/esbuild/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Resend } from 'resend';

new Resend('');
9 changes: 9 additions & 0 deletions integrations/esbuild/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"dependencies": {
"esbuild": "0.25.11",
"resend": "../.."
},
"scripts": {
"build": "esbuild ./index.ts --bundle --platform=node --target=node18 --outfile=./index.js"
}
}
93 changes: 93 additions & 0 deletions integrations/integrations.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';

describe('integrations', () => {
const sdkPath = path.resolve(__dirname, '..');

beforeAll(() => {
const build = spawnSync('pnpm build', {
stdio: 'inherit',
cwd: path.resolve(__dirname, '..'),
shell: true,
});
if (build.status !== 0) {
throw new Error('SDK build failed');
}
});

/**
* Create an extra temporary copy of the given integration so that there's @react-email/render module resolution from ../node_modules
*
* Also modifies the package.json to point to the SDK with an absolute path.
*/
async function prepareTemporaryIntegrationCopy(integrationPath: string) {
const temporaryIntegrationPath = path.resolve(
os.tmpdir(),
`resend-node-integration-${path.basename(integrationPath)}`,
);
if (fs.existsSync(temporaryIntegrationPath)) {
await fs.promises.rm(temporaryIntegrationPath, {
recursive: true,
force: true,
});
}
await fs.promises.mkdir(temporaryIntegrationPath, { recursive: true });
await fs.promises.cp(
path.resolve(__dirname, integrationPath),
temporaryIntegrationPath,
{
recursive: true,
},
);

const testingLockPackageJson: { dependencies: Record<string, string> } =
JSON.parse(
await fs.promises.readFile(
path.resolve(temporaryIntegrationPath, 'package.json'),
'utf8',
),
);
testingLockPackageJson.dependencies.resend = sdkPath;
await fs.promises.writeFile(
path.resolve(temporaryIntegrationPath, 'package.json'),

Check failure

Code scanning / CodeQL

Insecure temporary file High test

Insecure creation of file in
the os temp dir
.

Copilot Autofix

AI 9 days ago

To securely create a temporary directory, use a well-established library like tmp, which ensures random directory names, exclusive creation, and appropriate permissions.
Steps:

  • Import tmp in the test file.
  • Instead of path.resolve(os.tmpdir(), ...), call tmp.dirSync() (or its async equivalent), which returns a unique, safely created temp directory path.
  • Remove the current logic for custom temp dir paths and creation checks—let tmp handle creation and permissions.
  • Use that directory as the base for all test operations (copying, modifying the package).

Required changes:

  • Add import tmp from 'tmp'; near the top.
  • In prepareTemporaryIntegrationCopy, replace the explicit construction of temporaryIntegrationPath, its manual deletion, and the explicit mkdir with a single tmp.dirSync() call.
  • Update all subsequent operations that use temporaryIntegrationPath as needed.

Suggested changeset 2
integrations/integrations.spec.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/integrations/integrations.spec.ts b/integrations/integrations.spec.ts
--- a/integrations/integrations.spec.ts
+++ b/integrations/integrations.spec.ts
@@ -2,6 +2,7 @@
 import fs from 'node:fs';
 import os from 'node:os';
 import path from 'node:path';
+import tmp from 'tmp';
 
 describe('integrations', () => {
   const sdkPath = path.resolve(__dirname, '..');
@@ -23,17 +24,9 @@
    * Also modifies the package.json to point to the SDK with an absolute path.
    */
   async function prepareTemporaryIntegrationCopy(integrationPath: string) {
-    const temporaryIntegrationPath = path.resolve(
-      os.tmpdir(),
-      `resend-node-integration-${path.basename(integrationPath)}`,
-    );
-    if (fs.existsSync(temporaryIntegrationPath)) {
-      await fs.promises.rm(temporaryIntegrationPath, {
-        recursive: true,
-        force: true,
-      });
-    }
-    await fs.promises.mkdir(temporaryIntegrationPath, { recursive: true });
+    // Securely create a unique temporary directory
+    const { name: temporaryIntegrationPath } = tmp.dirSync({ unsafeCleanup: true });
+
     await fs.promises.cp(
       path.resolve(__dirname, integrationPath),
       temporaryIntegrationPath,
EOF
@@ -2,6 +2,7 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import tmp from 'tmp';

describe('integrations', () => {
const sdkPath = path.resolve(__dirname, '..');
@@ -23,17 +24,9 @@
* Also modifies the package.json to point to the SDK with an absolute path.
*/
async function prepareTemporaryIntegrationCopy(integrationPath: string) {
const temporaryIntegrationPath = path.resolve(
os.tmpdir(),
`resend-node-integration-${path.basename(integrationPath)}`,
);
if (fs.existsSync(temporaryIntegrationPath)) {
await fs.promises.rm(temporaryIntegrationPath, {
recursive: true,
force: true,
});
}
await fs.promises.mkdir(temporaryIntegrationPath, { recursive: true });
// Securely create a unique temporary directory
const { name: temporaryIntegrationPath } = tmp.dirSync({ unsafeCleanup: true });

await fs.promises.cp(
path.resolve(__dirname, integrationPath),
temporaryIntegrationPath,
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -49,7 +49,8 @@
   },
   "homepage": "https://github.com/resend/resend-node#readme",
   "dependencies": {
-    "svix": "1.76.1"
+    "svix": "1.76.1",
+    "tmp": "^0.2.5"
   },
   "peerDependencies": {
     "@react-email/render": "*"
EOF
@@ -49,7 +49,8 @@
},
"homepage": "https://github.com/resend/resend-node#readme",
"dependencies": {
"svix": "1.76.1"
"svix": "1.76.1",
"tmp": "^0.2.5"
},
"peerDependencies": {
"@react-email/render": "*"
This fix introduces these dependencies
Package Version Security advisories
tmp (npm) 0.2.5 None
Copilot is powered by AI and may make mistakes. Always verify output.
JSON.stringify(testingLockPackageJson, null, 2),
);

return temporaryIntegrationPath;
}

test('nextjs', { timeout: 30_000 }, async () => {
const temporaryNextApp = await prepareTemporaryIntegrationCopy('./nextjs');

const buildInstall = spawnSync(
'npm install --install-links && npm run build',
{
stdio: 'inherit',
cwd: temporaryNextApp,
shell: true,
},
);
if (buildInstall.status !== 0) {
throw new Error('next.js build failed');
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The esbuild integration test throws an error saying "next.js build failed", so an esbuild failure would surface with the wrong integration name, making it harder to triage.

Prompt for AI agents
Address the following comment on integrations/integrations.spec.ts at line 73:

<comment>The esbuild integration test throws an error saying &quot;next.js build failed&quot;, so an esbuild failure would surface with the wrong integration name, making it harder to triage.</comment>

<file context>
@@ -0,0 +1,93 @@
+      },
+    );
+    if (buildInstall.status !== 0) {
+      throw new Error(&#39;next.js build failed&#39;);
+    }
+  });
</file context>
Suggested change
throw new Error('next.js build failed');
throw new Error('esbuild build failed');
Fix with Cubic

}
});

test('esbuild', { timeout: 30_000 }, async () => {
const temporaryIntegration =
await prepareTemporaryIntegrationCopy('./esbuild');

const buildInstall = spawnSync(
'npm install --install-links && npm run build',
{
stdio: 'inherit',
cwd: temporaryIntegration,
shell: true,
},
);
if (buildInstall.status !== 0) {
throw new Error('next.js build failed');
}
});
});
42 changes: 42 additions & 0 deletions integrations/nextjs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
package-lock.json
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
7 changes: 7 additions & 0 deletions integrations/nextjs/app/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Resend } from 'resend';

export function GET() {
new Resend('');

return new Response('Hello from this API route!', { status: 200 });
}
1 change: 1 addition & 0 deletions integrations/nextjs/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
11 changes: 11 additions & 0 deletions integrations/nextjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"dependencies": {
"next": "15.5.6",
"react": "19.2.0",
"react-dom": "19.2.0",
"resend": "../.."
},
"scripts": {
"build": "next build"
}
}
31 changes: 21 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "resend",
"version": "6.2.2",
"version": "6.3.0-canary.1",
"description": "Node.js library for the Resend API",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
Expand Down Expand Up @@ -28,21 +28,26 @@
"format": "biome format --write .",
"format:apply": "biome check --write .",
"format:check": "biome format .",
"integration:nextjs": "cd ./integrations/nextjs && next build --turbopack",
"lint": "biome check .",
"prepublishOnly": "pnpm run build",
"test": "vitest run",
"test:watch": "vitest"
"test:dev": "cross-env TEST_MODE=dev vitest run",
"test:integrations": "vitest run integrations",
"test:record": "rimraf --glob \"**/__recordings__\" && cross-env TEST_MODE=record vitest run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit"
},
"repository": {
"type": "git",
"url": "git+https://github.com/resendlabs/resend-node.git"
"url": "git+https://github.com/resend/resend-node.git"
},
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/resendlabs/resend-node/issues"
"url": "https://github.com/resend/resend-node/issues"
},
"homepage": "https://github.com/resendlabs/resend-node#readme",
"homepage": "https://github.com/resend/resend-node#readme",
"dependencies": {
"svix": "1.76.1"
},
Expand All @@ -55,14 +60,20 @@
}
},
"devDependencies": {
"@biomejs/biome": "2.2.0",
"@types/node": "22.18.6",
"@types/react": "19.1.15",
"@biomejs/biome": "2.2.5",
"@pollyjs/adapter-fetch": "6.0.7",
"@pollyjs/core": "6.0.6",
"@pollyjs/persister-fs": "6.0.6",
"@types/node": "22.18.9",
"@types/react": "19.2.0",
"cross-env": "10.1.0",
"dotenv": "17.2.3",
"pkg-pr-new": "0.0.60",
"rimraf": "6.0.1",
"tsup": "8.5.0",
"typescript": "5.9.2",
"typescript": "5.9.3",
"vitest": "3.2.4",
"vitest-fetch-mock": "0.4.5"
},
"packageManager": "pnpm@10.17.1"
"packageManager": "pnpm@10.18.2"
}
Loading