diff --git a/src/broadcasts/broadcasts.spec.ts b/src/broadcasts/broadcasts.spec.ts index 70908a8c..ca768136 100644 --- a/src/broadcasts/broadcasts.spec.ts +++ b/src/broadcasts/broadcasts.spec.ts @@ -83,6 +83,26 @@ describe('Broadcasts', () => { `); }); + it('does not mutate payload when using React component', async () => { + const mockReact = { type: 'div', props: { children: 'Hi' } }; + const payload: CreateBroadcastOptions = { + from: 'bu@resend.com', + segmentId: '0192f4ed-c2e9-7112-9c13-b04a043e23ee', + subject: 'React Broadcast', + react: mockReact as React.ReactElement, + }; + const originalPayload = { ...payload }; + + fetchMock.mockOnce(JSON.stringify({ id: 'id-123' }), { + status: 200, + headers: { 'content-type': 'application/json' }, + }); + + await resend.broadcasts.create(payload); + + expect(payload).toEqual(originalPayload); + }); + it('creates and sends a broadcast', async () => { const response: CreateBroadcastResponseSuccess = { id: '71cdfe68-cf79-473a-a9d7-21f91db6a526', diff --git a/src/broadcasts/broadcasts.ts b/src/broadcasts/broadcasts.ts index 018aa2a4..20cdbf04 100644 --- a/src/broadcasts/broadcasts.ts +++ b/src/broadcasts/broadcasts.ts @@ -36,9 +36,7 @@ export class Broadcasts { payload: CreateBroadcastOptions, options: CreateBroadcastRequestOptions = {}, ): Promise { - if (payload.react) { - payload.html = await render(payload.react); - } + const html = payload.react ? await render(payload.react) : payload.html; const data = await this.resend.post( '/broadcasts', @@ -48,7 +46,7 @@ export class Broadcasts { audience_id: payload.audienceId, preview_text: payload.previewText, from: payload.from, - html: payload.html, + html, reply_to: payload.replyTo, subject: payload.subject, text: payload.text, @@ -102,9 +100,7 @@ export class Broadcasts { id: string, payload: UpdateBroadcastOptions, ): Promise { - if (payload.react) { - payload.html = await render(payload.react); - } + const html = payload.react ? await render(payload.react) : payload.html; const data = await this.resend.patch( `/broadcasts/${id}`, @@ -113,7 +109,7 @@ export class Broadcasts { segment_id: payload.segmentId, audience_id: payload.audienceId, from: payload.from, - html: payload.html, + html, text: payload.text, subject: payload.subject, reply_to: payload.replyTo, diff --git a/src/emails/emails.spec.ts b/src/emails/emails.spec.ts index 5bd26820..0c0dd7fd 100644 --- a/src/emails/emails.spec.ts +++ b/src/emails/emails.spec.ts @@ -122,6 +122,26 @@ describe('Emails', () => { expect(headers.has('Idempotency-Key')).toBe(true); expect(headers.get('Idempotency-Key')).toBe(idempotencyKey); }); + + it('does not mutate payload when using React component', async () => { + const mockReact = { type: 'div', props: { children: 'Hi' } }; + const payload: CreateEmailOptions = { + from: 'admin@resend.com', + to: 'user@resend.com', + subject: 'React Email', + react: mockReact as React.ReactElement, + }; + const originalPayload = { ...payload }; + + fetchMock.mockOnce(JSON.stringify({ id: 'id-123' }), { + status: 200, + headers: { 'content-type': 'application/json' }, + }); + + await resend.emails.create(payload); + + expect(payload).toEqual(originalPayload); + }); }); describe('send', () => { diff --git a/src/emails/emails.ts b/src/emails/emails.ts index 61a9dda7..ad3461dd 100644 --- a/src/emails/emails.ts +++ b/src/emails/emails.ts @@ -50,13 +50,15 @@ export class Emails { payload: CreateEmailOptions, options: CreateEmailRequestOptions = {}, ): Promise { + const body: CreateEmailOptions = { ...payload }; + if (payload.react) { - payload.html = await render(payload.react as React.ReactElement); + body.html = await render(payload.react as React.ReactElement); } const data = await this.resend.post( '/emails', - parseEmailToApiOptions(payload), + parseEmailToApiOptions(body), options, ); diff --git a/src/templates/templates.spec.ts b/src/templates/templates.spec.ts index 02ef1c41..cacb6bc7 100644 --- a/src/templates/templates.spec.ts +++ b/src/templates/templates.spec.ts @@ -247,6 +247,34 @@ describe('Templates', () => { expect(mockRenderAsync).toHaveBeenCalledWith(mockReactComponent); }); + it('does not mutate payload when using React component', async () => { + const mockReactComponent = { + type: 'div', + props: { children: 'Welcome!' }, + } as React.ReactElement; + + mockRenderAsync.mockResolvedValueOnce('
Welcome!
'); + + const payload: CreateTemplateOptions = { + name: 'Welcome Email', + react: mockReactComponent, + }; + + const originalPayload = { ...payload }; + + const response: CreateTemplateResponseSuccess = { + object: 'template', + id: 'non-mutating-payload-id', + }; + + mockSuccessResponse(response); + + const resend = new Resend(TEST_API_KEY); + await resend.templates.create(payload); + + expect(payload).toEqual(originalPayload); + }); + it('throws error when React renderer fails to load', async () => { const mockReactComponent = { type: 'div', diff --git a/src/templates/templates.ts b/src/templates/templates.ts index 85551e66..8220a8a3 100644 --- a/src/templates/templates.ts +++ b/src/templates/templates.ts @@ -51,6 +51,8 @@ export class Templates { private async performCreate( payload: CreateTemplateOptions, ): Promise { + const body: CreateTemplateOptions = { ...payload }; + if (payload.react) { if (!this.renderAsync) { try { @@ -63,14 +65,12 @@ export class Templates { } } - payload.html = await this.renderAsync( - payload.react as React.ReactElement, - ); + body.html = await this.renderAsync(payload.react as React.ReactElement); } return this.resend.post( '/templates', - parseTemplateToApiOptions(payload), + parseTemplateToApiOptions(body), ); }