Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4290ccb
feat: get inbound emails v0 (#641)
lucasfcosta Sep 30, 2025
3e9e39c
chore(deps): update dependency @biomejs/biome to v2.2.4 (#537)
renovate[bot] Sep 30, 2025
24d4745
feat: bump version for inbound release (#642)
lucasfcosta Sep 30, 2025
79ac64d
chore: bump to 6.2.0-canary.1 (#649)
gabrielmfern Oct 1, 2025
12734ec
chore(deps): update dependency @types/node to v22.18.8 (#638)
renovate[bot] Oct 3, 2025
207a4aa
chore(deps): update dependency typescript to v5.9.3 (#645)
renovate[bot] Oct 3, 2025
7920f0e
chore(deps): update tj-actions/changed-files digest to d6f020b (#651)
renovate[bot] Oct 3, 2025
2915382
chore(deps): update pnpm to v10.18.0 (#653)
renovate[bot] Oct 3, 2025
1e1ed85
chore(deps): update dependency @biomejs/biome to v2.2.5 (#652)
renovate[bot] Oct 3, 2025
6b7529c
chore(deps): update dependency @types/react to v19.2.0 (#640)
renovate[bot] Oct 3, 2025
97dc4f1
feat: show headers in response (#657)
lucasfcosta Oct 4, 2025
1d74eae
chore: release new canary for headers (#658)
lucasfcosta Oct 4, 2025
b0491c4
chore: improve PR title check error (#664)
TyMick Oct 9, 2025
98c4861
feat: move emails inbound method to emails.receiving.<method> (#666)
lucasfcosta Oct 9, 2025
f4fe9f2
feat: move attachment inbound methods to be nested (#667)
lucasfcosta Oct 9, 2025
c220601
feat: add inbound listing method (#668)
lucasfcosta Oct 9, 2025
9d592bc
feat: add pagination for inbound email attachments (#670)
lucasfcosta Oct 10, 2025
4cb63b2
chore(deps): update pnpm to v10.18.2 (#659)
renovate[bot] Oct 10, 2025
169874b
chore(deps): update dependency @types/node to v22.18.9 (#669)
renovate[bot] Oct 10, 2025
99bca1d
chore(deps): update pnpm/action-setup digest to 41ff726 (#665)
renovate[bot] Oct 10, 2025
6f3b9ea
feat: partial (API Keys, Audiences, and Contacts) non-mocked test cov…
TyMick Oct 14, 2025
c63f8d6
feat: get html & text columns for broadcasts (#673)
isabellaaquino Oct 14, 2025
e516568
feat: bump version (#675)
isabellaaquino Oct 14, 2025
a59b055
chore: sync with canary (#688)
joaopcm Oct 17, 2025
8382413
chore: remove template language related aspects (#691)
joaopcm Oct 17, 2025
dfc1b0e
fix: conflicts with canary
joaopcm Oct 17, 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
18 changes: 1 addition & 17 deletions src/templates/interfaces/create-template-options.interface.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import type { PostOptions } from '../../common/interfaces';
import type { RequireAtLeastOne } from '../../common/interfaces/require-at-least-one';
import type { ErrorResponse } from '../../interfaces';
import type {
Template,
TemplateVariable,
TemplateVariableListFallbackType,
} from './template';
import type { Template, TemplateVariable } from './template';

type TemplateContentCreationOptions = RequireAtLeastOne<{
html: string;
Expand All @@ -22,18 +18,6 @@ type TemplateVariableCreationOptions = Pick<TemplateVariable, 'key' | 'type'> &
type: 'number';
fallbackValue?: number | null;
}
| {
type: 'boolean';
fallbackValue?: boolean | null;
}
| {
type: 'object';
fallbackValue: Record<string, unknown>;
}
| {
type: 'list';
fallbackValue: TemplateVariableListFallbackType;
}
);

type TemplateOptionalFieldsForCreation = Partial<
Expand Down
16 changes: 2 additions & 14 deletions src/templates/interfaces/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,10 @@ export interface Template {
current_version_id: string;
}

export type TemplateVariableListFallbackType =
| string[]
| number[]
| boolean[]
| Record<string, unknown>[];

export interface TemplateVariable {
key: string;
fallback_value:
| string
| number
| boolean
| Record<string, unknown>
| TemplateVariableListFallbackType
| null;
type: 'string' | 'number' | 'boolean' | 'object' | 'list';
fallback_value: string | number | null;
type: 'string' | 'number';
created_at: string;
updated_at: string;
}
18 changes: 1 addition & 17 deletions src/templates/interfaces/update-template.interface.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { ErrorResponse } from '../../interfaces';
import type {
Template,
TemplateVariable,
TemplateVariableListFallbackType,
} from './template';
import type { Template, TemplateVariable } from './template';

type TemplateVariableUpdateOptions = Pick<TemplateVariable, 'key' | 'type'> &
(
Expand All @@ -15,18 +11,6 @@ type TemplateVariableUpdateOptions = Pick<TemplateVariable, 'key' | 'type'> &
type: 'number';
fallbackValue?: number | null;
}
| {
type: 'boolean';
fallbackValue?: boolean | null;
}
| {
type: 'object';
fallbackValue: Record<string, unknown>;
}
| {
type: 'list';
fallbackValue: TemplateVariableListFallbackType;
}
);

export interface UpdateTemplateOptions
Expand Down
141 changes: 20 additions & 121 deletions src/templates/templates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,61 +264,6 @@ describe('Templates', () => {
'Failed to render React component. Make sure to install `@react-email/render`',
);
});

it('creates a template with object and list variable types', async () => {
const payload: CreateTemplateOptions = {
name: 'Complex Variables Template',
html: '<h1>Welcome {{{userProfile.name}}}!</h1><p>Your tags: {{{tags}}}</p>',
variables: [
{
key: 'userProfile',
type: 'object',
fallbackValue: { name: 'John', age: 30 },
},
{
key: 'tags',
type: 'list',
fallbackValue: ['premium', 'vip'],
},
{
key: 'scores',
type: 'list',
fallbackValue: [95, 87, 92],
},
{
key: 'flags',
type: 'list',
fallbackValue: [true, false, true],
},
{
key: 'items',
type: 'list',
fallbackValue: [{ id: 1 }, { id: 2 }],
},
],
};
const response: CreateTemplateResponseSuccess = {
object: 'template',
id: 'fd61172c-cafc-40f5-b049-b45947779a29',
};

mockSuccessResponse(response, {
headers: { Authorization: `Bearer ${TEST_API_KEY}` },
});

const resend = new Resend(TEST_API_KEY);
await expect(
resend.templates.create(payload),
).resolves.toMatchInlineSnapshot(`
{
"data": {
"id": "fd61172c-cafc-40f5-b049-b45947779a29",
"object": "template",
},
"error": null,
}
`);
});
});

describe('remove', () => {
Expand Down Expand Up @@ -541,65 +486,6 @@ describe('Templates', () => {
}
`);
});

it('updates a template with object and list variable types', async () => {
const id = 'fd61172c-cafc-40f5-b049-b45947779a29';
const payload: UpdateTemplateOptions = {
name: 'Updated Complex Variables Template',
html: '<h1>Updated Welcome {{{config.theme}}}!</h1><p>Permissions: {{{permissions}}}</p>',
variables: [
{
key: 'config',
type: 'object',
fallbackValue: { theme: 'dark', lang: 'en' },
},
{
key: 'permissions',
type: 'list',
fallbackValue: ['read', 'write'],
},
{
key: 'counts',
type: 'list',
fallbackValue: [10, 20, 30],
},
{
key: 'enabled',
type: 'list',
fallbackValue: [true, false],
},
{
key: 'metadata',
type: 'list',
fallbackValue: [{ key: 'a' }, { key: 'b' }],
},
],
};
const response = {
object: 'template',
id,
};

mockSuccessResponse(response, {
headers: {
Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
},
});

const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');

await expect(
resend.templates.update(id, payload),
).resolves.toMatchInlineSnapshot(`
{
"data": {
"id": "fd61172c-cafc-40f5-b049-b45947779a29",
"object": "template",
},
"error": null,
}
`);
});
});

describe('get', () => {
Expand Down Expand Up @@ -1015,20 +901,33 @@ describe('Templates', () => {

const resend = new Resend(TEST_API_KEY);

// Test before and limit together
await resend.templates.list({
before: 'cursor1',
limit: 25,
});

// Verify before and limit pagination parameters are included
const [firstUrl] = fetchMock.mock.calls[0];
const firstParsedUrl = new URL(firstUrl as string);

expect(firstParsedUrl.pathname).toBe('/templates');
expect(firstParsedUrl.searchParams.get('before')).toBe('cursor1');
expect(firstParsedUrl.searchParams.get('limit')).toBe('25');

// Test after and limit together
await resend.templates.list({
after: 'cursor2',
limit: 25,
});

// Verify all pagination parameters are included
const [url] = fetchMock.mock.calls[0];
const parsedUrl = new URL(url as string);
// Verify after and limit pagination parameters are included
const [secondUrl] = fetchMock.mock.calls[1];
const secondParsedUrl = new URL(secondUrl as string);

expect(parsedUrl.pathname).toBe('/templates');
expect(parsedUrl.searchParams.get('before')).toBe('cursor1');
expect(parsedUrl.searchParams.get('after')).toBe('cursor2');
expect(parsedUrl.searchParams.get('limit')).toBe('25');
expect(secondParsedUrl.pathname).toBe('/templates');
expect(secondParsedUrl.searchParams.get('after')).toBe('cursor2');
expect(secondParsedUrl.searchParams.get('limit')).toBe('25');
});
});
});
Loading