@krizaka/mail

中文文档
0.1.3发布于 2026/11/01Apache-2.0TypeScriptES Module构建来源已验证

每个Krizaka产品的邮件:通过发件箱在调用者的事务中排队,每个幂等键发送一次,从不发送到被抑制的地址(作为散列存储);提供商(Mailgun, Resend, log)通过注入获取;反弹和抱怨

中文使用文档

@krizaka /邮件

每个Krizaka产品的邮件:在调用者的事务通过中排队 @krizaka/outbox,每个幂等键传递一次,永远不会发送到被抑制的地址,由管理员选择提供程序。没有供应商SDK,没有环境变量,没有挂钟,没有数据库客户端。

  • 它需要已经送达的邮件。 模板是 @krizaka/email’s(这个UI包永远不会导入)。 emailPorts(store, clock) 给了 @krizaka/email 它的三个港口(DeliveryLog, SuppressionList, IdempotencyStore)在同一张桌子上,所以是直接的 sendEmail 并且队列共享一个日志、一个抑制列表和一组键。
  • 至少有一次,从未输过。 deliver 检查抑制列表,检查密钥,发送,然后才声明密钥:发送和声明再次发送之间的崩溃(重复的重置邮件胜过丢失的邮件);索赔后的再发货是无效的。
  • 地址是散列的 (标准格式的SHA-256)在抑制列表和交付日志中。排队的邮件将保留该地址,直到发件箱将其清除。
  • 供应商 适配器是通过注入的吗 fetch: mailgunProvider, resendProvider, logProvider (发展,写无体)。 providerFor(settings.provider, keys, fetch, write) 选择管理员选择的,并拒绝没有密钥的提供者,而不是退回到 log.
  • 反弹和抱怨 通过提供商的签名webhook返回: parseMailgunWebhook 和 parseResendWebhook 验证签名和年龄,并回答地址抑制(永久失败或硬反弹,投诉,退订)。瞬态反弹被忽略。
  • 管理员的设置 (@krizaka/mail/settings): mail.provider (log, mailgun, resend), mail.from, mail.replyTo, mail.retentionDays. 密钥、Mailgun域和webhook签名密钥留在环境中(MAIL_PROVIDER样式名称,由产品读取并传入)。
import { createOutbox, runWorker } from "@krizaka/outbox";
import { createMail, dbMailStore, MAIL_TOPIC, providerFor } from "@krizaka/mail";
import { mailSettings, readMailSettings } from "@krizaka/mail/settings";

const mail = createMail({
  clock,
  outbox: createOutbox({ clock, settings: outboxSettings }),
  settings: () => readMailSettings((key) => settings.get(key)),
  store: dbMailStore(query),
  provider: (s) => providerFor(s.provider, { apiKey: env.MAIL_API_KEY, domain: env.MAIL_DOMAIN }, fetch, console.log),
});

// in the use case's transaction: the mail leaves only if the transaction commits
await mail.queue(tx, { to, subject, text, category: "transactional", template: "password-reset", locale: "fr", idempotencyKey: `reset:${resetId}` });

// in the worker process
await runWorker({ outbox, query, owner, topic: MAIL_TOPIC, deliver: (m) => mail.deliver(m).then(() => undefined), sleep });

// in the webhook route
const suppression = parseMailgunWebhook(rawBody, env.MAIL_WEBHOOK_SIGNING_KEY, nowSeconds);
if (suppression) await mail.suppress(suppression);

表(模式 mail, mailSchema): delivery, suppression, idempotency. 事件: evt.mail.requested.v1 在主题 mail. mailStoreContract (@krizaka/mail/testkit)是每家商店都遵守的合同。

apache - 2.0。

版本与发布记录

@krizaka/mail 共发布 5 个版本,首次发布于 2026/10/27;过去 12 个月发布了 5 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.3latest2026/11/011 天前
0.1.22026/10/304 天前
0.1.12026/10/295 天前
0.1.02026/10/277 天前
0.0.0-stage2026/10/277 天前

关于 @krizaka/mail 的常见问题

@krizaka/mail 是什么?

每个Krizaka产品的邮件:通过发件箱在调用者的事务中排队,每个幂等键发送一次,从不发送到被抑制的地址(作为散列存储);提供商(Mailgun, Resend, log)通过注入获取;反弹和抱怨

如何安装 @krizaka/mail?

在项目目录运行 npm install @krizaka/mail,或使用 pnpm add / yarn add / bun add。

@krizaka/mail 支持 TypeScript 吗?

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

@krizaka/mail 支持 ESM 还是 CommonJS?

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

@krizaka/mail 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@krizaka/mail 可以商用吗?

@krizaka/mail 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@krizaka/mail 还在维护吗?

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