@bernierllc/email-manager

中文文档
0.12.3发布于 2026/11/05Bernier LLCTypeScriptES Module近 30 天下载 1974

完整的电子邮件管理套件,包含模板管理,调度,分析,以及提供者的管弦乐(仅节点.js - 浏览器使用@bernierllc/电子邮件-manager-客户端)

中文使用文档
文档目录57 节
  1. 只有节点.js
  2. 特征
  3. 🚀 电子邮件管弦乐团
  4. 📝 模板管理
  5. 🔧 供应商管理
  6. 📊 分析和报告
  7. ⏰ 日程安排系统
  8. 安装
  9. 依赖关系
  10. 快速启动
  11. 核心 API
  12. 电子邮件操作
  13. 模板管理
  14. 供应商管理
  15. 分析和报告
  16. 时间安排管理
  17. 配置
  18. 电子邮件管理员连接
  19. 提供者配置
  20. 模板配置
  21. 提供者类型
  22. 发送Grid
  23. 邮件枪
  24. 警报器
  25. SMTP 软件
  26. 邮戳
  27. 模板变量
  28. 处理错误
  29. 事件跟踪
  30. Webhook 整合
  31. 魔法链接集成
  32. 性能优化
  33. 模板缓存
  34. 供应商集合
  35. 批处理
  36. 限制费率
  37. 系统状态
  38. 关闭
  39. 增强特性
  40. 邀请
  41. 批量发送
  42. 订阅管理
  43. 取消订阅流程
  44. 取缔
  45. 自定义订阅存储器
  46. 自定义信头
  47. 附录
  48. 能力矩阵和智能运行
  49. 能力矩阵
  50. 运行配置
  51. 退化行为
  52. Redis 解析器设置
  53. 回复中的搜索元数据
  54. V.4.x的迁移指南
  55. 再出口类型
  56. 许可证
  57. 另见

@bernierllc/电子邮件管理员

一个全面的电子邮件管理服务,负责管理电子邮件发送、模板管理、时间安排和分析。 此服务与 @bernierllc/email-sender, (中文(简体) ), @bernierllc/template-engine,以及 @bernierllc/magic-link 提供完整的电子邮件解决方案.

只有节点.js

此软件包是 Node.js- only 并且由于Node.js的依赖关系(nodemailer等)而无法被捆绑用于浏览器.

用于浏览器/前端应用程序,使用时 @bernierllc/email-manager-client 相反,它通过HTTP呼叫提供相同的API.

特征

🚀 电子邮件管弦乐团

  • 多供应商支助 自动失效
  • 基于模板的电子邮件 有动态内容渲染
  • 电子邮件时间安排 重试逻辑
  • 批量电子邮件处理 用于大宗业务

📝 模板管理

  • CRUD 操作 电子邮件模板
  • 模板版本 生命周期管理
  • 变量验证 和语法检查
  • 基于职类的组织

🔧 供应商管理

  • 多提供者配置 (SendGrid、Mailgun、AWS SES、SMTP、邮戳)
  • 提供者健康监测 和状态跟踪
  • 限制费率 和使用情况跟踪
  • 自动失效 提供者之间

📊 分析和报告

  • 电子邮件性能跟踪 (打开,点击,弹出)
  • 交付报告 弹出分析
  • 供应商业绩衡量标准
  • 历史数据 和趋势分析

⏰ 日程安排系统

  • 基于队列的排程 准确的时间
  • 重试逻辑 交付失败
  • 附表管理 (取消,修改)
  • 背景处理 自动清理

安装

npm install @bernierllc/email-manager

依赖关系

这个软件包需要以下伯尼RLC核心软件包:

npm install @bernierllc/email-sender @bernierllc/template-engine @bernierllc/magic-link

快速启动

import { EmailManager, EmailManagerConfig } from '@bernierllc/email-manager';

// Configure providers
const config: EmailManagerConfig = {
  providers: [
    {
      id: 'sendgrid',
      name: 'SendGrid',
      type: 'sendgrid',
      config: { apiKey: process.env.SENDGRID_API_KEY },
      isActive: true,
      priority: 1
    }
  ],
  analytics: { enabled: true },
  scheduling: { enabled: true }
};

// Initialize manager
const emailManager = new EmailManager(config);

// Send email
const result = await emailManager.sendEmail({
  to: '[email protected]',
  subject: 'Hello World',
  html: '<h1>Hello!</h1><p>This is a test email.</p>',
  text: 'Hello! This is a test email.'
});

console.log('Email sent:', result.success);

核心 API

电子邮件操作

发送电子邮件

const result = await emailManager.sendEmail({
  to: ['[email protected]', '[email protected]'],
  cc: ['[email protected]'],
  subject: 'Important Update',
  html: '<h1>Update</h1><p>Important information...</p>',
  text: 'Update: Important information...',
  priority: 'high',
  metadata: { campaign: 'newsletter' }
});

发送模板电子邮件

const result = await emailManager.sendTemplatedEmail(
  'welcome-email',
  { 
    user: { name: 'John Doe' },
    company: 'MyApp',
    confirmUrl: 'https://myapp.com/confirm?token=abc123'
  },
  ['[email protected]']
);

计划电子邮件

const scheduleTime = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours
const result = await emailManager.scheduleEmail({
  to: '[email protected]',
  subject: 'Reminder',
  html: '<p>This is your scheduled reminder</p>'
}, scheduleTime);

模板管理

创建模板

const template = await emailManager.createTemplate({
  id: 'welcome-email',
  name: 'Welcome Email',
  subject: 'Welcome {{ user.name }}!',
  htmlTemplate: '<h1>Welcome {{ user.name }}!</h1><p>Thanks for joining {{ company }}!</p>',
  textTemplate: 'Welcome {{ user.name }}! Thanks for joining {{ company }}!',
  variables: [
    { name: 'user.name', type: 'string', required: true },
    { name: 'company', type: 'string', required: true }
  ],
  category: 'onboarding',
  version: '1.0.0',
  isActive: true
});

列表模板

const templates = await emailManager.listTemplates({
  page: 1,
  pageSize: 20,
  category: 'onboarding',
  active: true,
  search: 'welcome'
});

更新模板

const result = await emailManager.updateTemplate('welcome-email', {
  subject: 'Welcome to our platform, {{ user.name }}!',
  isActive: false
});

供应商管理

添加提供者

const result = await emailManager.addProvider({
  id: 'mailgun-backup',
  name: 'Mailgun Backup',
  type: 'mailgun',
  config: {
    apiKey: process.env.MAILGUN_API_KEY,
    domain: process.env.MAILGUN_DOMAIN
  },
  isActive: true,
  priority: 2,
  rateLimit: {
    maxPerMinute: 60,
    maxPerHour: 1000,
    maxPerDay: 10000
  }
});

获取提供者状态

const status = await emailManager.getProviderStatus('sendgrid');
console.log('Provider health:', status.isHealthy);
console.log('Current usage:', status.currentUsage);

测试提供者连接

const testResult = await emailManager.testConnection('sendgrid');
console.log('Connection test:', testResult.success, testResult.message);

分析和报告

获取电子邮件统计

const stats = await emailManager.getEmailStats('email-message-id');
console.log('Delivery rate:', stats.deliveryRate);
console.log('Open rate:', stats.openRate);
console.log('Click rate:', stats.clickRate);

获取交付报告

const report = await emailManager.getDeliveryReport({
  startDate: new Date('2024-01-01'),
  endDate: new Date('2024-01-31'),
  provider: 'sendgrid'
});
console.log('Total sent:', report.totalSent);
console.log('Delivery rate:', report.deliveryRate);

获取分析摘要

const summary = await emailManager.getAnalyticsSummary(30); // Last 30 days
console.log('Overview:', summary.overview);
console.log('Top providers:', summary.topProviders);
console.log('Daily stats:', summary.dailyStats);

时间安排管理

取消已排定的电子邮件

const result = await emailManager.cancelScheduledEmail('schedule-id');
console.log('Cancelled:', result.success);

配置

电子邮件管理员连接

interface EmailManagerConfig {
  providers: EmailProvider[];          // Email provider configurations
  templates?: EmailTemplate[];         // Pre-loaded templates
  scheduling?: SchedulingConfig;       // Scheduler settings
  analytics?: AnalyticsConfig;         // Analytics settings
  retry?: RetryConfig;                // Retry policy settings
}

提供者配置

interface EmailProvider {
  id: string;                         // Unique provider ID
  name: string;                       // Display name
  type: 'sendgrid' | 'mailgun' | 'ses' | 'smtp' | 'postmark';
  config: ProviderConfig;             // Provider-specific config
  isActive: boolean;                  // Enable/disable provider
  priority: number;                   // Provider priority (lower = higher priority)
  rateLimit?: RateLimit;              // Rate limiting settings
}

模板配置

interface EmailTemplate {
  id: string;                         // Unique template ID
  name: string;                       // Display name
  subject: string;                    // Email subject (supports variables)
  htmlTemplate: string;               // HTML template content
  textTemplate?: string;              // Plain text template content
  variables: TemplateVariable[];      // Template variables
  category?: string;                  // Template category
  version: string;                    // Template version
  isActive: boolean;                  // Enable/disable template
}

提供者类型

发送Grid

{
  type: 'sendgrid',
  config: {
    apiKey: 'your-sendgrid-api-key'
  }
}

邮件枪

{
  type: 'mailgun',
  config: {
    apiKey: 'your-mailgun-api-key',
    domain: 'your-domain.mailgun.org'
  }
}

警报器

{
  type: 'ses',
  config: {
    accessKeyId: 'your-access-key',
    secretAccessKey: 'your-secret-key',
    region: 'us-east-1'
  }
}

SMTP 软件

{
  type: 'smtp',
  config: {
    host: 'smtp.gmail.com',
    port: 587,
    secure: false,
    auth: {
      user: '[email protected]',
      pass: 'your-password'
    }
  }
}

邮戳

{
  type: 'postmark',
  config: {
    serverToken: 'your-postmark-server-token'
  }
}

模板变量

模板通过变量支持动态内容 :

<!-- HTML Template -->
<h1>Welcome {{ user.name }}!</h1>
<p>Your account with {{ company }} is ready.</p>
<a href="{{ confirmUrl }}">Confirm Email</a>

<!-- Text Template -->
Welcome {{ user.name }}!
Your account with {{ company }} is ready.
Confirm your email: {{ confirmUrl }}

在模板配置中定义变量 :

variables: [
  { name: 'user.name', type: 'string', required: true },
  { name: 'company', type: 'string', required: true },
  { name: 'confirmUrl', type: 'string', required: true }
]

处理错误

电子邮件管理器提供全面的错误处理 :

const result = await emailManager.sendEmail(emailData);

if (!result.success) {
  console.error('Failed to send email:');
  result.errors?.forEach(error => {
    console.error(`${error.code}: ${error.message}`);
    if (error.recipient) {
      console.error(`Recipient: ${error.recipient}`);
    }
  });
}

事件跟踪

跟踪分析的电子邮件事件 :

// Track email opened (typically from email tracking pixel)
emailManager.trackEmailEvent('opened', 'email-id', '[email protected]');

// Track email clicked (typically from link tracking)
emailManager.trackEmailEvent('clicked', 'email-id', '[email protected]', {
  url: 'https://example.com/clicked-link'
});

// Track email bounced (typically from webhook)
emailManager.trackEmailEvent('bounced', 'email-id', '[email protected]', {
  reason: 'Invalid email address'
});

Webhook 整合

与您的电子邮件提供商建立网络搜索以跟踪事件 :

// Express.js webhook endpoint example
app.post('/webhook/sendgrid', (req, res) => {
  const events = req.body;
  
  events.forEach(event => {
    const { event: eventType, email, sg_message_id } = event;
    
    switch (eventType) {
      case 'delivered':
        emailManager.trackEmailEvent('delivered', sg_message_id, email);
        break;
      case 'open':
        emailManager.trackEmailEvent('opened', sg_message_id, email);
        break;
      case 'click':
        emailManager.trackEmailEvent('clicked', sg_message_id, email, { url: event.url });
        break;
      case 'bounce':
        emailManager.trackEmailEvent('bounced', sg_message_id, email, { reason: event.reason });
        break;
    }
  });
  
  res.status(200).send('OK');
});

生成安全认证链接 :

// Generate magic link
const magicLink = await emailManager.generateMagicLink(
  '[email protected]',
  'login',
  { userId: '123', redirectUrl: '/dashboard' }
);

// Send magic link email
await emailManager.sendTemplatedEmail('magic-link-email', {
  magicLink: magicLink.url,
  user: { email: '[email protected]' }
}, ['[email protected]']);

// Verify magic link (in your authentication endpoint)
const verification = await emailManager.verifyMagicLink(token);
if (verification.valid) {
  // Log user in
  console.log('User authenticated:', verification.payload);
}

性能优化

模板缓存

模板在编译后被缓存,以便更好的性能.

供应商集合

电子邮件提供者在可能情况下重复使用连接.

批处理

高效处理多封电子邮件:

const emails = [
  { to: '[email protected]', subject: 'Email 1', html: '<p>Content 1</p>' },
  { to: '[email protected]', subject: 'Email 2', html: '<p>Content 2</p>' },
  // ... more emails
];

const results = await Promise.all(
  emails.map(email => emailManager.sendEmail(email))
);

限制费率

配置每个提供者的费率限制以遵守 API 限制 :

{
  rateLimit: {
    maxPerMinute: 100,
    maxPerHour: 1000,
    maxPerDay: 10000
  }
}

系统状态

获得全面的系统统计:

const stats = emailManager.getSystemStats();

console.log('Templates:', stats.templates);
// { total: 5, active: 4, inactive: 1, byCategory: { onboarding: 2, marketing: 3 } }

console.log('Providers:', stats.providers);
// { total: 2, active: 2, inactive: 0, byType: { sendgrid: 1, mailgun: 1 } }

console.log('Scheduler:', stats.scheduler);
// { isRunning: true, totalInQueue: 3, scheduled: 3, overdue: 0 }

关闭

总是正确关闭电子邮件管理器 :

emailManager.shutdown();

这停止了调度器并清理了资源.


增强特性

这个 EnhancedEmailManager 扩展底线 EmailManager 包含日历邀请,批发,订阅管理,不订阅流量等. 所有增强功能都通过单一导入提供:

import { EnhancedEmailManager, EnhancedEmailManagerConfig } from '@bernierllc/email-manager';

邀请

发送日历邀请(ICS)并取消作为电子邮件附件. 管理器使用 @bernierllc/email-calendar 包并自动附加.

发送日历邀请

import { EnhancedEmailManager, CalendarEvent } from '@bernierllc/email-manager';

const event: CalendarEvent = {
  uid: '[email protected]',
  title: 'Q2 Business Review',
  description: 'Quarterly review of business metrics.',
  start: new Date('2025-07-15T14:00:00Z'),
  end: new Date('2025-07-15T15:30:00Z'),
  location: 'Conference Room A',
  organizer: { email: '[email protected]', name: 'Alice Johnson' },
  attendees: [
    { email: '[email protected]', name: 'Bob Smith', role: 'REQ-PARTICIPANT' },
    { email: '[email protected]', name: 'Carol Lee', role: 'OPT-PARTICIPANT' },
  ],
};

const result = await manager.sendCalendarInvite(
  event,
  ['[email protected]', '[email protected]'],
);

console.log('Invite sent:', result.success);

您可以自定义主题和身体 :

const result = await manager.sendCalendarInvite(event, recipients, {
  subject: 'You are invited: Q2 Business Review',
  htmlBody: '<h2>Q2 Review</h2><p>Please join us for the quarterly review.</p>',
  textBody: 'Please join us for the Q2 quarterly review.',
  metadata: { campaign: 'internal-meetings' },
});

取消日历事件

import { CalendarAttendee } from '@bernierllc/email-manager';

const organizer: CalendarAttendee = { email: '[email protected]', name: 'Alice' };
const attendees: CalendarAttendee[] = [
  { email: '[email protected]', name: 'Bob' },
  { email: '[email protected]', name: 'Carol' },
];

const result = await manager.sendCalendarCancel(
  '[email protected]', // UID of the original event
  organizer,
  attendees,
  {
    subject: 'Cancelled: Q2 Business Review',
    htmlBody: '<p>The Q2 Business Review has been cancelled.</p>',
  },
);

见 examples/calendar-invites.ts 是一个完整的范例.


批量发送

发送大量带有内置货币控制的电子邮件, 速率限制, 重试逻辑和进度跟踪 。 电源 @bernierllc/email-batch-sender。 。 。 .

基本批发

import { BatchEmailInput } from '@bernierllc/email-manager';

const emails: BatchEmailInput[] = [
  {
    toEmail: '[email protected]',
    subject: 'Newsletter - July',
    htmlContent: '<h1>Newsletter</h1><p>Hello Alice...</p>',
    textContent: 'Newsletter\n\nHello Alice...',
  },
  {
    toEmail: '[email protected]',
    subject: 'Newsletter - July',
    htmlContent: '<h1>Newsletter</h1><p>Hello Bob...</p>',
  },
];

const result = await manager.sendBatch(emails);
console.log(`Sent: ${result.succeeded}/${result.total}, Failed: ${result.failed}`);

批量发送, 限制费率并重试

import { BatchSenderOptions } from '@bernierllc/email-manager';

const options: BatchSenderOptions = {
  concurrency: 5,               // Up to 5 emails in parallel
  rateLimit: {
    maxPerSecond: 10,            // Respect provider rate limits
  },
  retry: {
    maxRetries: 3,               // Retry failed sends
    initialDelayMs: 1000,        // 1 second initial delay
    backoffMultiplier: 2,        // Exponential backoff
  },
  failureStrategy: 'continue',  // Keep sending even if some fail
};

const result = await manager.sendBatch(emails, options);

批次随进度跟踪发送

const result = await manager.sendBatch(emails, {
  concurrency: 3,
  onProgress: (progress) => {
    const pct = ((progress.completed / progress.total) * 100).toFixed(0);
    console.log(`Progress: ${pct}% - ${progress.succeeded} sent, ${progress.failed} failed`);
  },
});

失败时中止

对于不能接受部分发送的关键邮件:

const result = await manager.sendBatch(criticalEmails, {
  concurrency: 1,
  failureStrategy: 'abort',  // Stop immediately on first failure
  retry: { maxRetries: 5 },
});

if (result.aborted) {
  console.error('Batch was aborted due to a failure');
}

见 examples/batch-sending.ts 用于完整的例子.


订阅管理

管理订阅者列表,添加/移动订阅者,并处理批量进口. 订阅系统使用 @bernierllc/email-subscription 在引擎盖下.

创建订阅者列表

const newsletter = await manager.createSubscriberList('Monthly Newsletter', {
  description: 'Monthly product newsletter subscribers',
  metadata: { category: 'marketing' },
});

console.log('List ID:', newsletter.id);

添加订阅者

// Individual subscriber
await manager.addSubscriber(newsletter.id, {
  email: '[email protected]',
  name: 'Alice Johnson',
});

// Bulk add (skips duplicates and suppressed emails)
const result = await manager.addSubscribers(newsletter.id, [
  { email: '[email protected]', name: 'User One' },
  { email: '[email protected]', name: 'User Two' },
  { email: '[email protected]', name: 'User Three' },
]);

console.log(`Added: ${result.added}, Skipped: ${result.skipped}`);

向上查找并删除订阅者

// Look up a subscriber
const subscriber = await manager.getSubscriber(newsletter.id, '[email protected]');
if (subscriber) {
  console.log('Status:', subscriber.status);
}

// Remove a subscriber
const removed = await manager.removeSubscriber(newsletter.id, '[email protected]');

删除列表

const deleted = await manager.deleteSubscriberList(newsletter.id);

取消订阅流程

生成已签名的未订阅 URL 并处理未订阅请求 。 要求 subscription.unsubscribe 配置 .

配置

const config: EnhancedEmailManagerConfig = {
  providers: [/* ... */],
  subscription: {
    enabled: true,
    unsubscribe: {
      baseUrl: 'https://example.com/unsubscribe',
      secret: process.env.UNSUBSCRIBE_SECRET!,
      expiresIn: 30 * 24 * 60 * 60, // 30 days
    },
  },
};

生成未订阅的 URL

const url = manager.generateUnsubscribeUrl('[email protected]', listId);
// Returns: https://example.com/unsubscribe?token=<signed-jwt>

取消订阅请求

在您的网络服务器未订阅端点 :

app.get('/unsubscribe', async (req, res) => {
  const token = req.query.token as string;

  try {
    await manager.processUnsubscribe(token);
    res.send('You have been unsubscribed successfully.');
  } catch (error) {
    res.status(400).send('Invalid or expired unsubscribe link.');
  }
});

用列表- 取消订阅信头发送电子邮件

自动生成并附加 RFC 2369 / RFC 8058 List-Unsubscribe 标题 :

const result = await manager.sendWithUnsubscribe(
  {
    to: '[email protected]',
    subject: 'Monthly Newsletter',
    html: '<p>Newsletter content...</p>',
  },
  listId,                  // Subscriber list ID
  '[email protected]',     // Recipient for token generation
  true,                    // Enable one-click unsubscribe (RFC 8058)
);

见 examples/subscription-management.ts 整个生命周期.


取缔

检查发送前是否关闭电子邮件地址 。 当订阅者不订阅或处理回弹/投诉时,电子邮件就会被压制.

// Check global suppression
const isSuppressed = await manager.isSuppressed('[email protected]');

// Check list-specific suppression
const isListSuppressed = await manager.isSuppressed('[email protected]', listId);

if (isSuppressed) {
  console.log('Skipping send - email is suppressed');
}

自定义订阅存储器

默认情况下,管理者会使用内存订阅商店. 生产时,取而代之的是数据库支持的执行:

import { SubscriptionStore } from '@bernierllc/email-manager';

class DatabaseSubscriptionStore implements SubscriptionStore {
  // Implement all SubscriptionStore methods using your database
  // (createList, getList, addSubscriber, etc.)
}

const store = new DatabaseSubscriptionStore();
manager.setSubscriptionStore(store);

自定义信头

添加用于线程的自定义电子邮件信头、 读取收据和其他目的, 将它们包含到 metadata.headers 字段.

电子邮件线索

const result = await manager.sendEmail({
  to: '[email protected]',
  subject: 'Re: Project Discussion',
  html: '<p>I agree with the proposed approach.</p>',
  metadata: {
    headers: {
      'In-Reply-To': '<[email protected]>',
      'References': '<[email protected]>',
    },
  },
});

列表- 未订阅信头( 单独)

使用 createListUnsubscribeHeaders 工具栏直接 :

import { createListUnsubscribeHeaders } from '@bernierllc/email-manager';

const headers = createListUnsubscribeHeaders(
  'https://example.com/unsubscribe?id=123',   // HTTPS unsubscribe URL
  'mailto:[email protected]?subject=unsub',    // Optional mailto fallback
  true,                                         // One-click unsubscribe (RFC 8058)
);

const result = await manager.sendEmail({
  to: '[email protected]',
  subject: 'Weekly Digest',
  html: '<p>Your digest...</p>',
  metadata: { headers },
});

MDN( 读取) 请求

const result = await manager.sendEmail({
  to: '[email protected]',
  subject: 'Contract for Review',
  html: '<p>Please confirm receipt of the attached contract.</p>',
  metadata: {
    headers: {
      'Disposition-Notification-To': '[email protected]',
    },
  },
});

见 examples/attachments-and-headers.ts 用于完整的例子.


附录

发送带有文件附件的邮件, 内置图像, 或者两者兼有 .

文件附件

const result = await manager.sendEmail({
  to: '[email protected]',
  subject: 'Report Attached',
  html: '<p>Please find the report attached.</p>',
  attachments: [
    {
      filename: 'report.pdf',
      content: fs.readFileSync('/path/to/report.pdf'),
      contentType: 'application/pdf',
    },
  ],
});

字符串内容附件

const csvData = 'Name,Email\nAlice,[email protected]\nBob,[email protected]\n';

const result = await manager.sendEmail({
  to: '[email protected]',
  subject: 'User Export',
  html: '<p>User export attached.</p>',
  attachments: [
    {
      filename: 'users.csv',
      content: csvData,  // String content is also supported
      contentType: 'text/csv',
    },
  ],
});

内嵌/ 内嵌图像

使用内容ID( CID) 的内置引用图像 :

const result = await manager.sendEmail({
  to: '[email protected]',
  subject: 'Order Confirmation',
  html: `
    <img src="cid:company-logo" alt="Logo" />
    <h2>Order Confirmed!</h2>
  `,
  attachments: [
    {
      filename: 'logo.png',
      content: fs.readFileSync('/path/to/logo.png'),
      contentType: 'image/png',
      contentDisposition: 'inline',
      cid: 'company-logo',  // Matches src="cid:company-logo" in HTML
    },
  ],
});

多个附件

const result = await manager.sendEmail({
  to: '[email protected]',
  subject: 'Brand Kit Assets',
  html: '<p>Attached are the brand assets.</p>',
  attachments: [
    {
      filename: 'guidelines.pdf',
      content: fs.readFileSync('/path/to/guidelines.pdf'),
      contentType: 'application/pdf',
    },
    {
      filename: 'logo.png',
      content: fs.readFileSync('/path/to/logo.png'),
      contentType: 'image/png',
    },
    {
      filename: 'palette.json',
      content: JSON.stringify({ primary: '#0066CC' }, null, 2),
      contentType: 'application/json',
    },
  ],
});

见 examples/attachments-and-headers.ts 用于完整的例子.


能力矩阵和智能运行

电子邮件管理器包括一个完整的能力矩阵,将每个功能映射给每个提供者,从而能够作出智能的路由决定和优雅地退化.

能力矩阵

每个特性按提供者分类,有四种来源类型之一:

来源含义
provider由电子邮件提供者的 API 原生支持
platform由平台执行(在当地填充)
enhanced建在具有附加值的原始供应商之上
unsupported此提供者不可用

提供者特性支持

特性发送Grid邮件枪邮戳用户SMTP 软件
发送电子邮件提供者提供者提供者提供者提供者
批发提供者提供者提供者提供者平台
供应商模板提供者提供者提供者提供者不支持
本地模板平台平台平台平台平台
日历邀请平台平台平台平台平台
日历事件Mgmt平台平台平台平台平台
网络用户接收提供者提供者提供者提供者不支持
网点规范化增强增强增强增强不支持
交付 跟踪提供者提供者提供者提供者不支持
正在输入电子邮件增强增强增强增强不支持
订阅Mgmt增强增强平台平台平台
压制提供者提供者提供者提供者平台
未订阅 UrlGeneration平台平台平台平台平台
预定发送提供者提供者平台平台平台
打开点击跟踪提供者提供者供应商*不支持不支持
自动跟踪提供者不支持不支持不支持不支持
电子邮件连接平台平台平台平台平台
电子邮件页眉Mgmt平台平台平台平台平台
附录提供者提供者提供者提供者提供者
高级随附处理平台平台平台平台平台
dkimSpf 数据提供者提供者提供者提供者不支持
链接显示提供者提供者不支持不支持不支持
复试平台平台平台平台平台
多服务失败平台平台平台平台平台

* 邮政标记支持每个消息的开放和链接跟踪; TrackOpens: false 在服务器级别上打开跟踪时被忽略.

特定提供者的显著限制:

  • 发送Grid scheduledSend: 72小时最大排程窗口
  • 邮件枪 scheduledSend: 3天最大排程窗口
  • 用户 webhooksReceive 编号 : deliveryTracking: 需要 SNS 专题配置

运行配置

能力感知路由是选入. 添加一个 routing 区域到您的配置 :

import { EmailManager, EmailManagerConfig } from '@bernierllc/email-manager';

const config: EmailManagerConfig = {
  providers: [
    { id: 'sg', name: 'SendGrid', type: 'sendgrid', config: { apiKey: '...' }, isActive: true, priority: 1 },
    { id: 'mg', name: 'Mailgun', type: 'mailgun', config: { apiKey: '...', domain: '...' }, isActive: true, priority: 2 },
  ],
  routing: {
    strategy: 'primary-failover',
    featureOverrides: {
      // Always use platform implementation for local templates
      localTemplates: { behavior: 'platform-always' },
      // Throw an error if no native provider supports the feature
      openClickTracking: { behavior: 'error' },
    },
  },
  degradation: {
    logLevel: 'warn',
    includeDocLinks: true,
    docsBaseUrl: 'https://docs.example.com/email',
  },
};

const manager = new EmailManager(config);

特性覆盖行为

行为说明
native-first(默认) 优先提供本地支持或增强支持的供应商; 如果找不到, 则返回平台
platform-always总是使用平台执行, 即使提供者在本地支持它
error掷出 FeatureUnsupportedError 如果没有本地或强化支持的提供者

退化行为

当一个特性通过平台多填充(非原生)被路由给提供者时,系统记录降解信息:

  • platform 源特性 (例如,SMTP) batchSend:电子邮件按顺序发送,而不是按真实批次发送. 这个 SendResult.routing 字段显示 degraded: true 带有 degradationReason。 。 。 .
  • unsupported 特性 (例如,SMTP) webhooksReceive:路由器完全跳过提供者,按优先级顺序尝试下一个. 如果没有提供者支持该特性, a FeatureUnsupportedError 被抛出.
  • 退化伐木: 由 degradation.logLevel (单位:千美元)'silent' | 'info' | 'warn' | 'error') (中文(简体) ). 何时 includeDocLinks 实值 true,日志信息包括与降解文件的链接.

Redis 解析器设置

默认情况下,能力矩阵会从内映快照中解决. 对于跨多个实例的共享或动态能力数据,使用Redis后置的解析器:

# Set the environment variable to enable Redis resolver auto-detection
export EMAIL_MANAGER_REDIS_URL="redis://localhost:6379"

工厂createCapabilityResolver)自动检测环境变量并创建一个 SafeCapabilityResolver 将Redis解析器包裹在记忆回落中 若为 ioredis 没有安装或 Redis 无法使用, 它会悄悄地回到内膜解析器.

Redis 解析器选项

import { createCapabilityResolver } from '@bernierllc/email-manager';

const resolver = await createCapabilityResolver({
  namespace: 'myapp:capabilities', // Redis key namespace (default: 'email-manager:capabilities')
  ttl: 3600,                       // Cache TTL in seconds (default: varies by implementation)
});

您也可以提供自己的解析器执行:

const config: EmailManagerConfig = {
  providers: [/* ... */],
  routing: {
    strategy: 'primary-failover',
    capabilityResolver: myCustomResolver, // Must implement CapabilityResolver interface
  },
};

回复中的搜索元数据

当路由活动时, SendResult 包含a routing 字段 :

const result = await manager.sendEmail(emailData);

if (result.routing) {
  console.log('Provider used:', result.routing.provider);
  console.log('Feature:', result.routing.feature);
  console.log('Source:', result.routing.source);       // 'provider' | 'platform' | 'enhanced' | 'unsupported'
  console.log('Degraded:', result.routing.degraded);   // true if platform polyfill was used
  console.log('Reason:', result.routing.degradationReason);
  console.log('Tried:', result.routing.attemptedProviders); // providers checked before selecting
}

这个 RoutingMetadata 接口 :

interface RoutingMetadata {
  provider: string;
  feature: string;
  source: 'provider' | 'platform' | 'enhanced' | 'unsupported';
  degraded: boolean;
  degradationReason?: string;
  attemptedProviders?: string[];
}

V.4.x的迁移指南

能力矩阵和路由系统完全后向相容. 现有代码继续工作,没有变化:

  • 没有路由配置: 当 routing 在配置中被省略,管理器使用与之前相同的提供者选择逻辑(基于优先级的故障).
  • 无降解配置: 当 degradation 不记录降解事件.
  • 选入:为了实现能感知到的路由,增加一个 routing 区域到您的配置。 这个 RoutingMetadata 打开 SendResult 仅在路由活动时才会出现.
  • 无新的依赖关系: Redis 解析器是可选的,只有在 EMAIL_MANAGER_REDIS_URL 设置和 ioredis 已安装.

再出口类型

为了方便起见,电子邮件管理者从它的子包中再出口类型和公用事业,因此消费者不需要直接依赖:

软件包再出口物品
@bernierllc/email-calendarCalendarEvent, (中文(简体) ), CalendarAttendee, (中文(简体) ), CalendarMethod, (中文(简体) ), createCalendarInvite, (中文(简体) ), createCalendarCancel, (中文(简体) ), toCalendarAttachment
@bernierllc/email-batch-senderBatchSenderOptions, (中文(简体) ), BatchResult, (中文(简体) ), BatchEmailInput, (中文(简体) ), BatchProgress, (中文(简体) ), BatchSender, (中文(简体) ), createBatchSender
@bernierllc/email-subscriptionSubscriberList, (中文(简体) ), Subscriber, (中文(简体) ), SubscriptionStore, (中文(简体) ), AddSubscriberInput, (中文(简体) ), CreateListOptions, (中文(简体) ), BulkAddResult, (中文(简体) ), UnsubscribeManager, (中文(简体) ), SuppressionManager, (中文(简体) ), InMemorySubscriptionStore
@bernierllc/email-headersEmailHeaders, (中文(简体) ), ThreadingHeaders, (中文(简体) ), ListUnsubscribeHeaders, (中文(简体) ), createListUnsubscribeHeaders
@bernierllc/email-attachmentsAttachmentInput, (中文(简体) ), AttachmentLimits, (中文(简体) ), AttachmentValidationResult

许可证

版权(c)2025 Bernier有限责任公司。 版权所有.

另见

版本与发布记录

@bernierllc/email-manager 共发布 38 个版本,首次发布于 2025/08/22;过去 12 个月发布了 37 个版本。

版本发布日期距今
0.12.3latest2026/11/053 天前
0.12.22026/11/053 天前
0.12.12026/10/2415 天前
0.12.02026/10/2316 天前
0.11.02026/10/1920 天前
0.10.42026/10/021 个月前

关于 @bernierllc/email-manager 的常见问题

@bernierllc/email-manager 是什么?

完整的电子邮件管理套件,包含模板管理,调度,分析,以及提供者的管弦乐(仅节点.js - 浏览器使用@bernierllc/电子邮件-manager-客户端)

如何安装 @bernierllc/email-manager?

在项目目录运行 npm install @bernierllc/email-manager,或使用 pnpm add / yarn add / bun add。

@bernierllc/email-manager 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bernierllc/email-manager 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bernierllc/email-manager 可以商用吗?

@bernierllc/email-manager 使用 Bernier LLC 许可。声明的许可为 Bernier LLC,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@bernierllc/email-manager 的下载量有多少?

根据 npm 官方下载统计,@bernierllc/email-manager 在 2026/10/08 – 2026/11/06 这 30 天内被下载 1,974 次,日均约 66 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bernierllc/email-manager 还在维护吗?

最新版本 0.12.3 发布于 2026/11/05(3 天前);至今共发布 38 个版本,其中过去 12 个月发布了 37 个;首次发布于 2025/08/22。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。