@absolutejs/发送
绝对JS 的提供者不可识别输出消息发送器 生态系统.
文档 : 绝对js.com/文档/发送
何等为相. 1个工厂createDispatcher) 返回a
Dispatcher 有三个频道(email 编号 : messaging 编号 : push) (中文(简体) ). 适配器
插入每个频道。 适配器周围的底物——度量衡,
OTel,审计排放,错误处理——是统一的.
它能解决什么. 每个需要发送交易包
信件目前带给自己的SMTP / SMS / 推取客户端 .
@absolutejs/auth魔力链接路由假设主机的电子邮件为
符号 . @absolutejs/sync-pack-digest 明确说: "提供的主机
电子邮件发送者。 包不拥有运输" .. 控制飞机
需要发票失效/配额警告电子邮件。 集中到一个
调度员是指每个消费者插头进入一个地方.
安装
bun add @absolutejs/dispatch
# Plus one or more adapter siblings:
bun add @absolutejs/dispatch-resend # Resend (email)
bun add @absolutejs/dispatch-postmark # Postmark (email)
bun add @absolutejs/dispatch-twilio # Twilio messaging
bun add @absolutejs/dispatch-telnyx # Telnyx messaging
# ...etc
使用量
import { createDispatcher } from "@absolutejs/dispatch";
import { createResendAdapter } from "@absolutejs/dispatch-resend";
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_KEY!);
const dispatcher = createDispatcher({
email: createResendAdapter({ client: resend }),
defaultFrom: { email: "[email protected]" },
});
await dispatcher.email({
to: "[email protected]",
subject: "Welcome to Acme",
text: "Hi Alice, click here to verify: ...",
tenant: "tenant-A", // optional — propagates to OTel + audit
});
API 密码
createDispatcher(options)
type DispatcherOptions = {
email?: EmailAdapter;
messaging?: MessagingAdapter;
push?: PushAdapter;
policies?: readonly DispatchPolicy[];
defaultFrom?: { email?: string; messaging?: MessagingEndpoint };
onError?: (error: unknown, channel: DispatchChannel, message) => void;
tracerProvider?: TracerProvider; // OTel
audit?: AuditLike; // @absolutejs/audit instance
};
回返 { email, messaging, push, metrics }调用一个没有
适配器抛出 DispatchUnsupportedError。 。 。 .
dispatcher.email(message) 编号 : .messaging(message) 编号 : .push(message)
type EmailMessage = {
to: string | ReadonlyArray<string>;
from?: string;
subject: string;
text?: string;
html?: string;
replyTo?: string;
cc?: string | ReadonlyArray<string>;
bcc?: string | ReadonlyArray<string>;
headers?: Record<string, string>;
tenant?: string;
metadata?: Record<string, unknown>; // adapter-specific extras
};
type MessagingMessage = {
to: { address: string; transport: "sms" | "mms" | "rcs" | "whatsapp" };
from?: { address: string; transport: "sms" | "mms" | "rcs" | "whatsapp" };
content:
| { kind: "text"; text: string }
| {
kind: "media";
mediaUrls: readonly string[];
text?: string;
subject?: string;
}
| {
kind: "template";
id: string;
variables?: Readonly<Record<string, string>>;
}
| {
kind: "rich";
text: string;
title?: string;
mediaUrl?: string;
actions?: readonly MessagingAction[];
};
fallbacks?: readonly {
transport: "sms" | "mms" | "whatsapp";
from?: MessagingEndpoint;
content?: MessagingContent;
}[];
sendAt?: string;
idempotencyKey?: string;
privacy?: {
addressRetention?: "obfuscate" | "retain";
contentRetention?: "discard" | "retain";
};
consent?: {
programId: string;
purpose: string;
};
extensions?: Readonly<Record<string, unknown>>;
tenant?: string;
metadata?: Record<string, unknown>;
};
type PushMessage = {
to: string; // device token OR topic
title?: string;
body: string;
idempotencyKey?: string;
actions?: ReadonlyArray<{ id: string; label: string; deepLink?: string }>;
badge?: number;
deepLink?: string;
sound?: string;
data?: Record<string, unknown>;
tenant?: string;
metadata?: Record<string, unknown>;
};
生产推力生命周期
createPushLifecycle() 拥有APN上方的提供者中性设备层,
FCM,和Web Push: 租户同化登记,用户/设备/专题定位,定界
货币,回扣,无效退休, 和一能的幻想。 决定
每一订阅的适配器,以支持特定租户的证书.
import { createPushLifecycle } from "@absolutejs/dispatch";
import {
createPostgresPushFanoutClaimStore,
createPostgresPushSubscriptionStore,
} from "@absolutejs/dispatch-push-postgres";
const push = createPushLifecycle({
adapterFor: ({ platform, tenant }) => adapters.forTenant(tenant, platform),
claimStore: createPostgresPushFanoutClaimStore(idempotentOperations),
store: createPostgresPushSubscriptionStore(transactionRunner),
});
await push.register({
deviceId: "iphone-15",
platform: "apns",
tenant: "acme",
token,
topics: ["incidents"],
userId: "user-42",
});
// Authenticated apps use server-issued installation identities. Pass this
// lifecycle directly as Auth's push registrar; ownership is checked on every
// native-token or browser-subscription rotation and removal.
const installation = await push.registerInstallation({
platform: "apns",
tenant: "acme",
token,
topics: ["incidents"],
userId: "user-42",
});
await push.registerInstallation({
platform: "webpush",
subscription: {
endpoint: "https://push.example/subscription-id",
keys: { auth: "browser-auth-key", p256dh: "browser-public-key" },
},
tenant: "acme",
topics: ["incidents"],
userId: "user-42",
});
await push.send(
{ tenant: "acme", topic: "incidents" },
{
body: "Database latency is elevated",
deepLink: "absolute://incidents/42",
idempotencyKey: "incident-42:opened",
sound: "default",
title: "Production alert",
},
);
模糊的提供者故障记录为 indeterminate 而不是成为
租赁期满后默默复试. 这可以防止一个坠毁的工人
双重发送通知,其提供者的确认被丢失.
回返 MessagingDispatchResult { id?, provider, at, delivery }编辑
delivery 记录载有所要求和实际运输量外加正常运输量
初级/后退尝试。 丢入适配器
失败; onError 在重投前开火.
送出前授权政策
政策在适配方或供应商收到信息之前运行,并可能
进行持久的同步检查. @absolutejs/compliance 用品a
消息同意政策.
const dispatcher = createDispatcher({
policies: [messagingConsentPolicy],
messaging,
});
await dispatcher.messaging({
content: { kind: "text", text: "Database latency is elevated" },
consent: { programId: "acme-alerts", purpose: "incident-alerts" },
to: { address: "+12025550100", transport: "sms" },
});
dispatcher.metrics()
{
sent: number;
failed: number;
byChannel: {
email: {
sent, failed;
}
messaging: {
sent, failed;
}
push: {
sent, failed;
}
}
}
底片图案
打开几何
何时 tracerProvider 设定,每个发送
dispatch.<channel>.send 间距( n)dispatch.email.send页:1
abs.tenant(当message.tenant已提供)dispatch.channel(单位:千美元)email编号 :messaging编号 :push(中文(简体) )dispatch.provider(适配器名称,成功后更新到结果提供者)dispatch.recipient_count(地址被故意排除在跨度之外)dispatch.message_id(当适配器返回一个)
从结果中更新提供者使适配器成为中介/委托 观察时不会泄露其路径执行.
状态 OK 关于成功; ERROR + 键 recordException 失败.
审计排放
何时 audit 提供(任何) @absolutejs/audit Audit 实例),
每一次发送 dispatch.<channel>.<outcome> 事件:
kind数字 :dispatch.email.sent编号 :dispatch.email.failed(中文(简体) )actor数字 :message.tenant(设定时),其他'system'target: 收件人metadata数字 :{ channel, provider, messageId?, error? }
审计排放是火灾和遗忘——如果你的审计汇失败
发送仍然成功。 审计失败记入 console.warn。 。 。 .
审计司是 选项 —— 说 @absolutejs/audit 是一个同位素
peerDependenciesMeta.optional: true所以消费者没有它
刚刚安装通过 audit: undefined。 。 。 .
套装适配器
这些在核心的飞船用于测试 + dev. 生产部署使用 姐妹卖家适配器.
memoryEmailAdapter({ max?, idGenerator? })
正在处理的FIFO尾. inspect() 返回最古老的已抓取的信息 -
首先是:; clear() 虚空相. 我们发邮件给爱丽丝
在没有真正的SMTP服务器的测试中.
memoryMessagingAdapter({ max? }) 编号 : memoryPushAdapter({ max? })
形状相同,用法相同.
consoleEmailAdapter({ stream? }) 编号 : consoleMessagingAdapter 编号 : consolePushAdapter
JSON-每行为stdout(或作stderr). 用于 bun --watch 说到做到
您可以在不燃烧提供者的情况下看到所发送的精确信息
学分.
此软件包没有做什么
- 模板渲染. 消息被预发. 与
@react-email, (中文(简体) ),mjml, (中文(简体) ),handlebars或者你喜欢的. - 受收人率受限. 编曲:
@absolutejs/rate-limit每小时5个魔法链接 g. - 弹出/投诉处理. 每个供应商的SDK曝光 网络呼喊这些.
- 排队/排队. 对于延迟发送使用
@absolutejs/queue有个接线员打电话dispatcher.email(...)。 。 。 .
许可证
BSL-1.1 (英语) 带有一级例外: 你不能使用此选项 操作主机信件发送/ 交易邮件/ 短消息/ 推 与主办方相竞争的SaaS(Resend, Postmark, SendGrid, 邮件枪、AWS SES管理、Twilio、Vonage、Pushwoush、OneSignal、FCM 管理,敲门。 你可以把它当作你自己的应用程序的一部分 (包括你自己的SaaS). 2030-05-30自动转换为Apache 2.0.