@bernierllc/电子邮件发送器
用提供者-不可知界面发送基本电子邮件的核心工具 .
特征
- 提供者-不可知论: 支持多个电子邮件提供者( SMTP, SendGrid, SES, Mailgun, Postmark)
- 显示电子邮件安全: 防止非生产环境中的意外电子邮件发送
- 类型脚本: 完整类型脚本支持并严格打字
- 验证: 全面的电子邮件和配置验证
- 附录: 文件附件的支持
- 批量发送: 高效发送多封电子邮件
- 处理错误: 有力的错误处理和报告
- 测试: 全面试验覆盖率
安装
npm install @bernierllc/email-sender
说明: 此软件包使用对等依赖性 。 您必须分别安装 :
# For SMTP provider
npm install nodemailer
# For AWS SES provider
npm install aws-sdk
快速启动
基本用途
import { EmailSender } from '@bernierllc/email-sender';
// Configure SMTP provider
const config = {
provider: 'smtp' as const,
host: 'smtp.gmail.com',
port: 587,
secure: false,
username: '[email protected]',
password: 'your-app-password',
fromEmail: '[email protected]',
fromName: 'Your App',
};
// Create email sender
const emailSender = new EmailSender(config);
// Send an email
const result = await emailSender.sendEmail({
toEmail: '[email protected]',
toName: 'John Doe',
subject: 'Welcome to our app!',
htmlContent: '<h1>Welcome!</h1><p>Thank you for joining us.</p>',
textContent: 'Welcome! Thank you for joining us.',
});
if (result.success) {
console.log('Email sent successfully:', result.messageId);
} else {
console.error('Failed to send email:', result.errorMessage);
}
发送Grid 配置
import { EmailSender } from '@bernierllc/email-sender';
const config = {
provider: 'sendgrid' as const,
apiKey: 'your-sendgrid-api-key',
fromEmail: '[email protected]',
fromName: 'Your App',
// Optional: point every SendGrid call (mail send, templates, senders) at
// another host, e.g. a local mock in tests. Give the bare origin; a
// trailing `/v3` is also accepted. Defaults to https://api.sendgrid.com.
endpoint: process.env.SENDGRID_API_BASE_URL,
};
const emailSender = new EmailSender(config);
AWS SES 配置
import { EmailSender } from '@bernierllc/email-sender';
const config = {
provider: 'ses' as const,
apiKey: 'your-aws-access-key',
secretKey: 'your-aws-secret-key',
region: 'us-east-1',
fromEmail: '[email protected]',
fromName: 'Your App',
sandbox: false, // Set to true if in SES sandbox mode
};
const emailSender = new EmailSender(config);
邮件枪配置
import { EmailSender } from '@bernierllc/email-sender';
const config = {
provider: 'mailgun' as const,
apiKey: 'your-mailgun-api-key',
domain: 'yourdomain.com',
region: 'us', // or 'eu' for European region
fromEmail: '[email protected]',
fromName: 'Your App',
};
const emailSender = new EmailSender(config);
邮戳配置
import { EmailSender } from '@bernierllc/email-sender';
const config = {
provider: 'postmark' as const,
apiKey: 'your-postmark-api-key',
accountToken: 'your-postmark-account-token', // Optional, for template operations
fromEmail: '[email protected]',
fromName: 'Your App',
};
const emailSender = new EmailSender(config);
API 参考
电子邮件发送器
发送邮件的主课.
构造器
new EmailSender(config: EmailProviderConfig)
方法
sendEmail(email: EmailMessage): Promise<SendResult>
发送一个电子邮件.
const result = await emailSender.sendEmail({
toEmail: '[email protected]',
subject: 'Test Email',
htmlContent: '<h1>Hello</h1>',
});
sendBatch(emails: EmailMessage[]): Promise<SendResult[]>
分批发送多封邮件 .
const results = await emailSender.sendBatch([
{ toEmail: '[email protected]', subject: 'Email 1', htmlContent: '<h1>Email 1</h1>' },
{ toEmail: '[email protected]', subject: 'Email 2', htmlContent: '<h1>Email 2</h1>' },
]);
validateConfiguration(): EmailValidationResult
验证提供者配置 .
const validation = emailSender.validateConfiguration();
if (!validation.isValid) {
console.error('Configuration errors:', validation.errors);
}
validateEmailMessage(email: EmailMessage): EmailValidationResult
发送前验证电子邮件信息 .
const validation = emailSender.validateEmailMessage(email);
if (!validation.isValid) {
console.error('Email validation errors:', validation.errors);
}
testConnection(): Promise<boolean>
测试连接到电子邮件提供者 .
const isConnected = await emailSender.testConnection();
if (isConnected) {
console.log('Connection successful');
} else {
console.error('Connection failed');
}
getCapabilities(): EmailProviderCapabilities
获取当前提供者的能力.
const capabilities = emailSender.getCapabilities();
console.log('Supports attachments:', capabilities.supportsAttachments);
console.log('Max batch size:', capabilities.maxBatchSize);
类型
电子邮件信
interface EmailMessage {
toEmail: string;
toName?: string;
fromEmail?: string;
fromName?: string;
subject: string;
htmlContent?: string;
textContent?: string;
replyTo?: string;
headers?: Record<string, string>;
attachments?: EmailAttachment[];
metadata?: Record<string, any>;
/** Per-message open, click and UTM tracking; omitted = the provider account's default. */
tracking?: { opens?: boolean; clicks?: boolean; utm?: boolean };
}
tracking: { clicks: false } 在下面的提供者上保留无法重写HTML和文本部分的链接,这是一次性链接(签名、邀请、密码重置)所需要的。 它映射到 SendGrid tracking_settings邮件枪 o:tracking-clicks编号 :o:tracking-opens邮戳 TrackLinks编号 :TrackOpens (邮戳忽略 opens: false 在服务器一级打开跟踪)和Mandrill track_clicks编号 :track_opens;不跟踪的提供者忽略了它. SMTP2GO设定只跟踪每个API密钥,并忽略了该覆盖,所以使用一个按下跟踪键进行一次性链接.
tracking: { utm: false } 停止将分析查询参数(UTM标签)附加到链接中;使其未定义账户默认值。 它不停止点击跟踪: 链接只按逐字记录 { clicks: false, utm: false }。 。 。 . utm: true 强制标记使用账户的 UTM 值 。 只有SendGrid尊重它(tracking_settings.ganalytics每个供养者都忽视它 getCapabilities().features.utmTracking 说是哪个.
电子邮件连接
interface EmailAttachment {
filename: string;
content: string; // Base64 encoded
contentType?: string;
disposition?: 'attachment' | 'inline';
}
发送结果
interface SendResult {
success: boolean;
messageId?: string;
errorMessage?: string;
providerResponse?: any;
metadata?: Record<string, any>;
}
电子邮件 ProviderConfig
interface EmailProviderConfig {
provider: 'sendgrid' | 'ses' | 'mailgun' | 'postmark' | 'smtp';
apiKey?: string;
region?: string;
endpoint?: string;
fromEmail: string;
fromName?: string;
replyTo?: string;
webhookUrl?: string;
webhookSecret?: string;
sandbox?: boolean;
// SMTP specific options
host?: string;
port?: number;
secure?: boolean;
username?: string;
password?: string;
}
提供者的能力
每个供应商通过下列途径暴露其能力: getCapabilities() 方法。 能力接口既包括遗留布尔旗,也包括扩大的旗帜 features 用于精细能力检查的地图.
电子邮件提供能力接口
interface EmailProviderCapabilities {
// Legacy boolean flags (always available)
supportsBatch: boolean;
supportsTemplates: boolean;
supportsWebhooks: boolean;
supportsAttachments: boolean;
supportsCustomHeaders: boolean;
supportsDeliveryTracking: boolean;
maxBatchSize: number;
maxAttachmentSize: number;
// Expanded feature map (optional, populated by capability-aware providers)
features?: Record<string, CapabilityEntry>;
}
能力输入
表格中的每个条目 features 映射描述特定特性是如何被支持的 :
type CapabilitySource = 'provider' | 'platform' | 'enhanced' | 'unsupported';
interface CapabilityEntry {
source: CapabilitySource; // How the feature is implemented
description: string; // Human-readable description
limitations?: string; // Known limitations (e.g., "72hr max scheduling window")
degradation?: { // Present when platform polyfill has a degradation strategy
strategy: string;
description: string;
docUrl: string;
} | null;
overridable: boolean; // Whether the entry can be overridden at runtime
}
提供能力摘要
| 提供者 | 批次 | 模板 | 网络呼号 | 附录 | 自定义信头 | 交货跟踪 |
|---|---|---|---|---|---|---|
| 发送Grid | 有(个性化) | 是(动态模板) | 是 (Event Webhook) (英语) | 对 | 对 | 对 |
| 邮件枪 | 是(接收变量) | 是(存储模板) | 对 | 对 | 对 | 对 |
| 邮戳 | 是(批发API) | 是(服务器侧) | 对 | 对 | 对 | 对 |
| 用户 | 是 (发送Blk电子邮件) | 是(电子邮件模板) | 是(通过SNS) | 是( raw 电子邮件) | 对 | 是(通过SNS) |
| SMTP 软件 | 顺序* | 没有服务器侧 | 没有 | 是(微小中型企业) | 对 | 没有 |
*SMTP批发由平台相继处理,而不是由供应商自行处理.
宣布自定义提供者的能力
执行自定义提供者时,通过返回适当的值来声明能力 getCapabilities()数字 :
import { EmailProvider, EmailProviderCapabilities, CapabilityEntry } from '@bernierllc/email-sender';
class CustomProvider extends EmailProvider {
getCapabilities(): EmailProviderCapabilities {
return {
supportsBatch: true,
supportsTemplates: false,
supportsWebhooks: true,
supportsAttachments: true,
supportsCustomHeaders: true,
supportsDeliveryTracking: true,
maxBatchSize: 500,
maxAttachmentSize: 10 * 1024 * 1024, // 10 MB
// Optional: declare fine-grained features for capability-aware routing
features: {
sendEmail: {
source: 'provider',
description: 'Send email via custom API',
degradation: null,
overridable: false,
},
batchSend: {
source: 'provider',
description: 'Batch send via custom API bulk endpoint',
degradation: null,
overridable: true,
},
scheduledSend: {
source: 'unsupported',
description: 'Custom provider does not support scheduled send',
degradation: null,
overridable: false,
},
},
};
}
}
当 features 地图已存在 @bernierllc/email-manager 能力路由器将其用于智能供应商选择. 缺席时,路由器会倒回静态能力矩阵.
Zod 验证
两者 CapabilityEntry 财务报告和审定财务报表 DegradationInfo 拥有运行时间验证的 Zod 计划 :
import { capabilityEntrySchema, degradationInfoSchema } from '@bernierllc/email-sender';
const result = capabilityEntrySchema.safeParse(entry);
if (!result.success) {
console.error('Invalid capability entry:', result.error.issues);
}
显示电子邮件安全
使用中转安全功能,防止在非生产环境中发送意外邮件.
以白名单粘贴( Block 非白名单)
import { EmailSender } from '@bernierllc/email-sender';
const emailSender = new EmailSender({
provider: 'smtp' as const,
host: 'smtp.example.com',
port: 587,
username: 'user',
password: 'pass',
fromEmail: '[email protected]',
stagingSafety: {
enabled: true,
environment: 'staging',
whitelist: ['[email protected]', '[email protected]', '[email protected]'],
blockNonWhitelisted: true,
logBlocked: true,
},
});
// This will send (whitelisted)
await emailSender.sendEmail({
toEmail: '[email protected]',
subject: 'Test Email',
htmlContent: '<p>This email will be sent</p>',
});
// This will be blocked (not whitelisted)
const result = await emailSender.sendEmail({
toEmail: '[email protected]',
subject: 'Test Email',
htmlContent: '<p>This email will be blocked</p>',
});
console.log(result.success); // false
console.log(result.errorMessage); // "Email blocked by safety manager: ..."
用重写粘接( 重定向到测试电子邮件)
const emailSender = new EmailSender({
provider: 'smtp' as const,
host: 'smtp.example.com',
port: 587,
username: 'user',
password: 'pass',
fromEmail: '[email protected]',
stagingSafety: {
enabled: true,
environment: 'staging',
whitelist: ['[email protected]'], // Admin emails go through normally
rewriteTo: '[email protected]', // All other emails redirected here
blockNonWhitelisted: false,
logBlocked: true,
},
});
// This will be sent to [email protected] instead
await emailSender.sendEmail({
toEmail: '[email protected]',
subject: 'Test Email',
htmlContent: '<p>This will be sent to [email protected]</p>',
});
制作(所有电子邮件)
const emailSender = new EmailSender({
provider: 'smtp' as const,
host: 'smtp.example.com',
port: 587,
username: 'user',
password: 'pass',
fromEmail: '[email protected]',
stagingSafety: {
enabled: true,
environment: 'production',
whitelist: [], // Not used in production
blockNonWhitelisted: false,
logBlocked: false,
},
});
// All emails are allowed in production
await emailSender.sendEmail({
toEmail: '[email protected]',
subject: 'Production Email',
htmlContent: '<p>This email will be sent</p>',
});
发送前检查收件人安全性
const safetyResult = emailSender.checkRecipientSafety('[email protected]');
console.log('Safety check result:', safetyResult);
if (safetyResult && !safetyResult.allowed) {
console.log('Email would be blocked:', safetyResult.reason);
}
基于环境的动态配置
const environment = process.env.NODE_ENV || 'development';
const emailSender = new EmailSender({
provider: 'smtp' as const,
host: 'smtp.example.com',
port: 587,
username: 'user',
password: 'pass',
fromEmail: '[email protected]',
stagingSafety: {
enabled: environment !== 'production',
environment: environment as 'development' | 'staging' | 'production',
whitelist: environment === 'staging'
? ['[email protected]', '[email protected]']
: [],
rewriteTo: environment === 'development' ? '[email protected]' : undefined,
blockNonWhitelisted: environment === 'staging',
logBlocked: true,
},
});
console.log('Current environment:', environment);
console.log('Safety enabled:', emailSender.isSafetyEnabled());
console.log('Safety config:', emailSender.getSafetyConfig());
实例
以附件发送电子邮件
import { EmailSender } from '@bernierllc/email-sender';
import fs from 'fs';
const emailSender = new EmailSender(config);
const attachment = {
filename: 'report.pdf',
content: fs.readFileSync('report.pdf').toString('base64'),
contentType: 'application/pdf',
disposition: 'attachment' as const,
};
const result = await emailSender.sendEmail({
toEmail: '[email protected]',
subject: 'Monthly Report',
htmlContent: '<h1>Monthly Report</h1><p>Please find the attached report.</p>',
attachments: [attachment],
});
用自定义信头发送电子邮件
const result = await emailSender.sendEmail({
toEmail: '[email protected]',
subject: 'Priority Message',
htmlContent: '<h1>Important Update</h1>',
headers: {
'X-Priority': '1',
'X-Custom-Header': 'custom-value',
},
});
带有不同内容的批次电子邮件
const emails = [
{
toEmail: '[email protected]',
toName: 'User One',
subject: 'Personalized Email 1',
htmlContent: '<h1>Hello User One!</h1>',
},
{
toEmail: '[email protected]',
toName: 'User Two',
subject: 'Personalized Email 2',
htmlContent: '<h1>Hello User Two!</h1>',
},
];
const results = await emailSender.sendBatch(emails);
处理错误
try {
const result = await emailSender.sendEmail(email);
if (result.success) {
console.log('Email sent successfully');
} else {
console.error('Email failed:', result.errorMessage);
// Check if it's a validation error
const validation = emailSender.validateEmailMessage(email);
if (!validation.isValid) {
console.error('Validation errors:', validation.errors);
}
}
} catch (error) {
console.error('Unexpected error:', error);
}
提供者支助
目前得到支持
- SMTP 软件: 用节点符完全支持
- 发送Grid: 完全支持 SendGrid API
- 警报器: 用 AWS SDK 提供完全支持( 需要)
aws-sdk依赖同伴) - 邮件枪: 完全支持 Mailgun API
- 邮戳: 完全支持 Postmark API
添加新供应商
要添加一个新的提供者,请扩展 EmailProvider 基准类 :
import { EmailProvider, EmailMessage, SendResult } from '@bernierllc/email-sender';
export class CustomProvider extends EmailProvider {
async sendEmail(email: EmailMessage): Promise<SendResult> {
// Implement your provider logic here
}
async sendBatch(emails: EmailMessage[]): Promise<SendResult[]> {
// Implement batch sending logic
}
async getDeliveryStatus(messageId: string): Promise<DeliveryStatus | null> {
// Implement delivery status checking
}
async processWebhook(payload: any, signature: string): Promise<WebhookEvent[]> {
// Implement webhook processing
}
}
测试
运行测试套件 :
npm test
进行覆盖测试 :
npm run test:coverage
捐款
- 叉子存储器
- 创建特性分支
- 做你的改变
- 添加新功能测试
- 确保所有测试通过
- 提交拉动请求
一体化状况
- 伐木机集成:不适用 - Email-sender是一个低等的用途,不需要@bernierllc/logger集成. 消费者申请应视需要处理伐木问题.
- 多克套装: 准备 - 完整的类型Doc/JSDoc文档。 基于 Markdown 的 README 有广泛的示例 .
- NeverHub 整合:不适用 -- -- 电子邮件发送器是一个无国籍的功能,侧重于电子邮件发送。 @bernierllc/ 绝不需要适应器.
重大退化
这个软件包的设计是为了在配置的电子邮件提供者之外,在没有外部依赖的情况下独立工作. 所有功能都是自成一体的,有明显的错误处理和验证.
许可证
ISC 许可证 - 详情见LICENSE文件.
支助
请在GitHub上打开一个问题或联系Bernier LLC.
信息分析参数
SendGrid 通过现有的跟踪对象支持清晰的每个消息的 UTM 值 :
tracking: {
utm: true,
utmParameters: {
source: 'newsletter', medium: 'email', campaign: 'launch',
term: '', content: 'hero',
},
}
参数仅以 utm: true。 。 。 . utm: false 只发送 ganalytics.enable: false; 省略 utm 保留账户行为。 Oinducted/null 参数字段将账户值留在控制中;空字符串是准确传输的. 除字符串/ null 抛出之外启用的参数值 EmailValidationError 在HTTP之前。 库不发明提供者长度限制. 提供商: utmTracking 能力是不支持的,忽略这些参数.
点击和开放跟踪仍然可独立配置。 有效载荷合同并不保证某一提供商对空值或有点击跟踪功能的分析进行送出-链接处理;在作为应用行为提供这些组合之前,对照用于交付的账户对这些组合进行验证.