Skip to content
Merged
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
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Support `isPlaintext` boolean for messages send and drafts create requests
- Expose raw response headers on all responses via non-enumerable `rawHeaders` while keeping existing `headers` camelCased

## [7.12.0] - 2025-08-01

### Changed
- Upgraded node-fetch from v2 to v3 for better ESM support and compatibility with edge environments

Expand All @@ -15,8 +21,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated Jest configuration to properly handle ESM modules from node-fetch v3
- Removed incompatible AbortSignal import from node-fetch externals (now uses native Node.js AbortSignal)

### Added
- Expose raw response headers on all responses via non-enumerable `rawHeaders` while keeping existing `headers` camelCased

## [7.11.0] - 2025-06-23

Expand Down
32 changes: 21 additions & 11 deletions examples/messages/cli-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface CliOptions {
attachmentSize: 'small' | 'large';
format: FileFormat;
testEmail?: string;
isPlaintext: boolean;
}

async function getCliOptions(fileManager: TestFileManager): Promise<CliOptions> {
Expand Down Expand Up @@ -51,42 +52,48 @@ async function getCliOptions(fileManager: TestFileManager): Promise<CliOptions>
message: 'Recipient email address:',
default: process.env.TEST_EMAIL || '',
validate: (input: string) => input.includes('@') || 'Please enter a valid email address'
},
{
type: 'confirm',
name: 'isPlaintext',
message: 'Send as plaintext (no HTML rendering)?',
default: false
}
]);

return answers as CliOptions;
}

async function runExample(examples: SendAttachmentsExamples, fileManager: TestFileManager, options: CliOptions): Promise<void> {
const { format, testEmail, attachmentSize } = options;
const { format, testEmail, attachmentSize, isPlaintext } = options;

if (!testEmail) {
console.log(chalk.yellow('⚠️ No email provided. Skipping send.'));
return;
}

try {
console.log(chalk.blue(`\n📤 Running ${format} attachment example (${attachmentSize} files)...\n`));
console.log(chalk.blue(`\n📤 Running ${format} attachment example (${attachmentSize} files)${isPlaintext ? ' in plaintext mode' : ''}...\n`));

let result: NylasResponse<Message>;
const isLarge = attachmentSize === 'large';

// Route to the appropriate example based on format
switch (format) {
case 'file':
result = await examples.sendFilePathAttachments(fileManager, testEmail, isLarge);
result = await examples.sendFilePathAttachments(fileManager, testEmail, isLarge, isPlaintext);
break;
case 'stream':
result = await examples.sendStreamAttachments(fileManager, testEmail, isLarge);
result = await examples.sendStreamAttachments(fileManager, testEmail, isLarge, isPlaintext);
break;
case 'buffer':
result = await examples.sendBufferAttachments(fileManager, testEmail, isLarge);
result = await examples.sendBufferAttachments(fileManager, testEmail, isLarge, isPlaintext);
break;
case 'string':
result = await examples.sendStringAttachments(fileManager, testEmail, isLarge);
result = await examples.sendStringAttachments(fileManager, testEmail, isLarge, isPlaintext);
break;
default:
result = await examples.sendAttachmentsByFormat(fileManager, format, testEmail, attachmentSize);
result = await examples.sendAttachmentsByFormat(fileManager, format, testEmail, attachmentSize, isPlaintext);
}

console.log(chalk.green.bold('\n✅ Message sent successfully!'));
Expand All @@ -103,11 +110,12 @@ async function runExample(examples: SendAttachmentsExamples, fileManager: TestFi
}
}

async function runBatchMode(examples: SendAttachmentsExamples, fileManager: TestFileManager, size: 'small' | 'large', format: FileFormat, email?: string): Promise<void> {
async function runBatchMode(examples: SendAttachmentsExamples, fileManager: TestFileManager, size: 'small' | 'large', format: FileFormat, email?: string, isPlaintext: boolean = false): Promise<void> {
const options: CliOptions = {
attachmentSize: size,
format,
testEmail: email
testEmail: email,
isPlaintext
};

console.log(chalk.blue.bold('\n🚀 Nylas Send Attachments (Batch Mode)\n'));
Expand Down Expand Up @@ -137,17 +145,19 @@ export async function startCli(examples: SendAttachmentsExamples, fileManager: T
.description('Send small attachments')
.option('-f, --format <format>', 'format (file|stream|buffer|string)', 'file')
.option('-e, --email <email>', 'recipient email')
.option('--plaintext', 'send as plaintext', false)
.action(async (options) => {
await runBatchMode(examples, fileManager, 'small', options.format as FileFormat, options.email || testEmail);
await runBatchMode(examples, fileManager, 'small', options.format as FileFormat, options.email || testEmail, Boolean(options.plaintext));
});

program
.command('large')
.description('Send large attachment')
.option('-f, --format <format>', 'format (file|stream|buffer|string)', 'file')
.option('-e, --email <email>', 'recipient email')
.option('--plaintext', 'send as plaintext', false)
.action(async (options) => {
await runBatchMode(examples, fileManager, 'large', options.format as FileFormat, options.email || testEmail);
await runBatchMode(examples, fileManager, 'large', options.format as FileFormat, options.email || testEmail, Boolean(options.plaintext));
});

program
Expand Down
9 changes: 6 additions & 3 deletions examples/messages/examples/buffer-attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const grantId: string = process.env.NYLAS_GRANT_ID || '';
* Loads the entire file into memory as a Buffer.
* Good for small files or when you need to process content.
*/
export async function sendBufferAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false): Promise<NylasResponse<Message>> {
export async function sendBufferAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false, isPlaintext: boolean = false): Promise<NylasResponse<Message>> {
console.log('💾 Sending attachments using buffers...');

let sizeDescription;
Expand All @@ -48,12 +48,15 @@ export async function sendBufferAttachments(fileManager: TestFileManager, recipi
const requestBody: SendMessageRequest = {
to: [{ name: 'Test Recipient', email: recipientEmail }],
subject: 'Nylas SDK - Buffer Attachments',
body: `
body: isPlaintext
? 'Buffer Attachments Example\nThis demonstrates sending attachments using Node.js Buffer objects.'
: `
<h2>Buffer Attachments Example</h2>
<p>This demonstrates sending attachments using Node.js Buffer objects.</p>
<p>Good for small files when you need the content in memory.</p>
`,
attachments: bufferAttachments
attachments: bufferAttachments,
isPlaintext
};

// For large files, use a longer timeout (5 minutes)
Expand Down
9 changes: 6 additions & 3 deletions examples/messages/examples/file-path-attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const grantId: string = process.env.NYLAS_GRANT_ID || '';
* This is the recommended approach for most use cases.
* Uses streams internally for memory efficiency.
*/
export async function sendFilePathAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false): Promise<NylasResponse<Message>> {
export async function sendFilePathAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false, isPlaintext: boolean = false): Promise<NylasResponse<Message>> {
console.log('📁 Sending attachments using file paths...');

let attachments;
Expand All @@ -42,13 +42,16 @@ export async function sendFilePathAttachments(fileManager: TestFileManager, reci
const requestBody: SendMessageRequest = {
to: [{ name: 'Test Recipient', email: recipientEmail }],
subject: `Nylas SDK - File Path Attachments (${sizeDescription})`,
body: `
body: isPlaintext
? `File Path Attachments Example\nThis demonstrates sending attachments using file paths.\nAttachment size: ${sizeDescription} (${attachments.length} file${attachments.length > 1 ? 's' : ''})`
: `
<h2>File Path Attachments Example</h2>
<p>This demonstrates the most common way to send attachments using file paths.</p>
<p>The SDK uses streams internally for memory efficiency.</p>
<p>Attachment size: ${sizeDescription} (${attachments.length} file${attachments.length > 1 ? 's' : ''})</p>
`,
attachments
attachments,
isPlaintext
};

// For large files, use a longer timeout (5 minutes)
Expand Down
9 changes: 6 additions & 3 deletions examples/messages/examples/flexible-attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const grantId: string = process.env.NYLAS_GRANT_ID || '';
/**
* Flexible attachment sending based on format choice
*/
export async function sendAttachmentsByFormat(fileManager: TestFileManager, format: FileFormat, recipientEmail: string, attachmentSize: 'small' | 'large' = 'small'): Promise<NylasResponse<Message>> {
export async function sendAttachmentsByFormat(fileManager: TestFileManager, format: FileFormat, recipientEmail: string, attachmentSize: 'small' | 'large' = 'small', isPlaintext: boolean = false): Promise<NylasResponse<Message>> {

let attachments: CreateAttachmentRequest[] = [];
let subject: string;
Expand All @@ -40,15 +40,18 @@ export async function sendAttachmentsByFormat(fileManager: TestFileManager, form
const requestBody: SendMessageRequest = {
to: [{ name: 'Test Recipient', email: recipientEmail }],
subject,
body: `
body: isPlaintext
? `Attachment Format Test: ${format}\nThis message demonstrates sending attachments using the ${format} format.\nFiles attached: ${attachments.length}`
: `
<h2>Attachment Format Test: ${format}</h2>
<p>This message demonstrates sending attachments using the ${format} format.</p>
<p>Files attached: ${attachments.length}</p>
<ul>
${attachments.map(att => `<li>${att.filename} (${att.size} bytes)</li>`).join('')}
</ul>
`,
attachments
attachments,
isPlaintext
};

// For large files, use a longer timeout (5 minutes)
Expand Down
10 changes: 5 additions & 5 deletions examples/messages/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { sendStringAttachments } from './string-attachments';
import { sendAttachmentsByFormat } from './flexible-attachments';

export type SendAttachmentsExamples = {
sendFilePathAttachments: typeof sendFilePathAttachments,
sendStreamAttachments: typeof sendStreamAttachments,
sendBufferAttachments: typeof sendBufferAttachments,
sendStringAttachments: typeof sendStringAttachments,
sendAttachmentsByFormat: typeof sendAttachmentsByFormat
sendFilePathAttachments: (fileManager: Parameters<typeof sendFilePathAttachments>[0], recipientEmail: string, large?: boolean, isPlaintext?: boolean) => ReturnType<typeof sendFilePathAttachments>,
sendStreamAttachments: (fileManager: Parameters<typeof sendStreamAttachments>[0], recipientEmail: string, large?: boolean, isPlaintext?: boolean) => ReturnType<typeof sendStreamAttachments>,
sendBufferAttachments: (fileManager: Parameters<typeof sendBufferAttachments>[0], recipientEmail: string, large?: boolean, isPlaintext?: boolean) => ReturnType<typeof sendBufferAttachments>,
sendStringAttachments: (fileManager: Parameters<typeof sendStringAttachments>[0], recipientEmail: string, large?: boolean, isPlaintext?: boolean) => ReturnType<typeof sendStringAttachments>,
sendAttachmentsByFormat: (fileManager: Parameters<typeof sendAttachmentsByFormat>[0], format: Parameters<typeof sendAttachmentsByFormat>[1], recipientEmail: string, attachmentSize?: Parameters<typeof sendAttachmentsByFormat>[3], isPlaintext?: boolean) => ReturnType<typeof sendAttachmentsByFormat>
};
9 changes: 6 additions & 3 deletions examples/messages/examples/stream-attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const grantId: string = process.env.NYLAS_GRANT_ID || '';
* Useful when you're working with streams from other sources
* or need more control over the stream processing.
*/
export async function sendStreamAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false): Promise<NylasResponse<Message>> {
export async function sendStreamAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false, isPlaintext: boolean = false): Promise<NylasResponse<Message>> {
console.log('🌊 Sending attachments using streams...');

let attachments: CreateAttachmentRequest[] = [];
Expand Down Expand Up @@ -52,13 +52,16 @@ export async function sendStreamAttachments(fileManager: TestFileManager, recipi
const requestBody: SendMessageRequest = {
to: [{ name: 'Test Recipient', email: recipientEmail }],
subject: `Nylas SDK - Stream Attachments (${sizeDescription})`,
body: `
body: isPlaintext
? `Stream Attachments Example\nThis demonstrates sending attachments using readable streams.\nAttachment size: ${sizeDescription} (${attachments.length} file${attachments.length > 1 ? 's' : ''})`
: `
<h2>Stream Attachments Example</h2>
<p>This demonstrates sending attachments using readable streams.</p>
<p>Useful when you have streams from other sources.</p>
<p>Attachment size: ${sizeDescription} (${attachments.length} file${attachments.length > 1 ? 's' : ''})</p>
`,
attachments
attachments,
isPlaintext
};

// For large files, use a longer timeout (5 minutes)
Expand Down
9 changes: 6 additions & 3 deletions examples/messages/examples/string-attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const grantId: string = process.env.NYLAS_GRANT_ID || '';
* Perfect for sending existing files as base64 encoded strings.
* This example pulls the same files used by other examples but encodes them as base64 strings.
*/
export async function sendStringAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false): Promise<NylasResponse<Message>> {
export async function sendStringAttachments(fileManager: TestFileManager, recipientEmail: string, large: boolean = false, isPlaintext: boolean = false): Promise<NylasResponse<Message>> {
console.log('📝 Sending base64 encoded file attachments as strings...');

let stringAttachments: CreateAttachmentRequest[] = [];
Expand Down Expand Up @@ -75,7 +75,9 @@ export async function sendStringAttachments(fileManager: TestFileManager, recipi
const requestBody: SendMessageRequest = {
to: [{ name: 'Test Recipient', email: recipientEmail }],
subject: `Nylas SDK - Base64 String Attachments (${sizeDescription})`,
body: `
body: isPlaintext
? `Base64 String Attachments Example\nThis demonstrates sending existing files as base64 encoded strings.\nAttachment size: ${sizeDescription} (${stringAttachments.length} file${stringAttachments.length > 1 ? 's' : ''})`
: `
<h2>Base64 String Attachments Example</h2>
<p>This demonstrates sending existing files as base64 encoded strings.</p>
<p>Files are converted from the same test files used in other examples.</p>
Expand All @@ -84,7 +86,8 @@ export async function sendStringAttachments(fileManager: TestFileManager, recipi
${stringAttachments.map(att => `<li>${att.filename} (${att.size} bytes base64 encoded)</li>`).join('')}
</ul>
`,
attachments: stringAttachments
attachments: stringAttachments,
isPlaintext
};

// For large files, use a longer timeout (5 minutes)
Expand Down
42 changes: 42 additions & 0 deletions examples/messages/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,47 @@ async function demonstrateMessageSending(): Promise<NylasResponse<Message> | nul
}
}

/**
* Demonstrates sending a plaintext-only message (no attachments)
*/
async function demonstratePlaintextMessageSending(): Promise<NylasResponse<Message> | null> {
console.log('\n=== Demonstrating Plaintext Message Sending ===');
try {
const testEmail = process.env.TEST_EMAIL;
if (!testEmail) {
console.log('TEST_EMAIL environment variable not set. Skipping plaintext message sending demo.');
return null;
}

const requestBody: SendMessageRequest = {
to: [{ name: 'Plaintext Recipient', email: testEmail }],
subject: 'Nylas SDK Messages Example - Plaintext',
body: 'This message is sent as plain text only.',
isPlaintext: true,
};

const sentMessage = await nylas.messages.send({
identifier: grantId,
requestBody,
});

console.log('Plaintext message sent successfully!');
console.log(`- Message ID: ${sentMessage.data.id}`);
console.log(`- Subject: ${sentMessage.data.subject}`);
console.log(`- To: ${sentMessage.data.to?.map(t => `${t.name} <${t.email}>`).join(', ')}`);

return sentMessage;
} catch (error) {
if (error instanceof NylasApiError) {
console.error(`Error sending plaintext message: ${error.message}`);
console.error(`Error details: ${JSON.stringify(error, null, 2)}`);
} else if (error instanceof Error) {
console.error(`Unexpected error in demonstratePlaintextMessageSending: ${error.message}`);
}
return null;
}
}

/**
* Demonstrates updating a message
*/
Expand Down Expand Up @@ -464,6 +505,7 @@ async function main(): Promise<void> {

// Run all demonstrations
await demonstrateMessageFields();
await demonstratePlaintextMessageSending();
await demonstrateRawMime();
await demonstrateMessageQuerying();

Expand Down
10 changes: 9 additions & 1 deletion src/models/drafts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ export interface CreateDraftRequest {
* An array of custom headers to add to the message.
*/
customHeaders?: CustomHeader[];
/**
* When true, the message body is sent as plain text and the MIME data doesn't include the HTML version of the message.
* When false, the message body is sent as HTML. Defaults to false.
*/
isPlaintext?: boolean;
}

/**
Expand Down Expand Up @@ -103,7 +108,10 @@ export interface Draft
/**
* Interface representing a request to update a draft.
*/
export type UpdateDraftRequest = Partial<CreateDraftRequest> & {
export type UpdateDraftRequest = Omit<
Partial<CreateDraftRequest>,
'isPlaintext'
> & {
/**
* Return drafts that are unread.
*/
Expand Down
Loading