# @absolutejs/dispatch 中文文档

规范地址：https://npmcn.com/package/%40absolutejs/dispatch/

版本：0\.9\.1
npm 元数据更新时间：2026-10-22T13:36:06.956Z
中文译文更新时间：2026-11-06T20:15:58.825Z
软件包许可：BSL-1\.1

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40absolutejs/dispatch
原文对照：https://npmcn.com/package/%40absolutejs/dispatch/?lang=original
项目主页：https://github.com/absolutejs/dispatch
源码仓库：https://github.com/absolutejs/dispatch

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：602 kB，16 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：3 个
- peer 依赖：@absolutejs/audit \>= 0\.0\.1
- 首次发布：2026-05-30T20:07:07.425Z
- 最新版本发布：2026-10-22T13:36:06.429Z
- 近 30 天下载量：2389（2026-10-05 至 2026-11-03，npm 官方下载统计）
- 已发布版本数：22（近 12 个月 22 个）

## 中文简介

提供不可知的电子邮件、载体和丰富的信息发送器,并在绝对JS生态系统中推动通知.

## 安装

```sh
npm install @absolutejs/dispatch
```

也可以使用：`pnpm add @absolutejs/dispatch`、`yarn add @absolutejs/dispatch`、`bun add @absolutejs/dispatch`

## README

# @absolutejs/发送

绝对JS 的提供者不可识别输出消息发送器 生态系统.

**文档 :** [绝对js.com/文档/发送](https://absolutejs.com/documentation/dispatch)

**何等为相.** 1个工厂`createDispatcher`) 返回a `Dispatcher` 有三个频道(`email` 编号 : `messaging` 编号 : `push`) (中文(简体) ). 适配器 插入每个频道。 适配器周围的底物——度量衡, OTel,审计排放,错误处理——是统一的.

**它能解决什么.** 每个需要发送交易包 信件目前带给自己的SMTP / SMS / 推取客户端 . `@absolutejs/auth`魔力链接路由假设主机的电子邮件为 符号 . `@absolutejs/sync-pack-digest` 明确说: "_提供的主机 电子邮件发送者。 包不拥有运输_" .. 控制飞机 需要发票失效/配额警告电子邮件。 集中到一个 调度员是指每个消费者插头进入一个地方.

## 安装

```sh
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
```

## 使用量

```ts
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: "no-reply@acme.io" },
});

await dispatcher.email({
  to: "alice@example.com",
  subject: "Welcome to Acme",
  text: "Hi Alice, click here to verify: ...",
  tenant: "tenant-A", // optional — propagates to OTel + audit
});
```

## API 密码

### `createDispatcher(options)`

```ts
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)`

```ts
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: 租户同化登记,用户/设备/专题定位,定界 货币,回扣,无效退休, 和一能的幻想。 决定 每一订阅的适配器,以支持特定租户的证书.

```ts
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 消息同意政策.

```ts
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()`

```ts
{
  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 (英语)](https://npmcn.com/package/%40absolutejs/dispatch/LICENSE) 带有一级例外: 你不能使用此选项 操作主机信件发送/ 交易邮件/ 短消息/ 推 与主办方相竞争的SaaS(Resend, Postmark, SendGrid, 邮件枪、AWS SES管理、Twilio、Vonage、Pushwoush、OneSignal、FCM 管理,敲门。 你可以把它当作你自己的应用程序的一部分 (包括你自己的SaaS). 2030-05-30自动转换为Apache 2.0.

## 常见问题

### @absolutejs/dispatch 是什么？

提供不可知的电子邮件、载体和丰富的信息发送器,并在绝对JS生态系统中推动通知\.

### 如何安装 @absolutejs/dispatch？

在项目目录运行 npm install @absolutejs/dispatch，或使用 pnpm add / yarn add / bun add。

### @absolutejs/dispatch 支持 TypeScript 吗？

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

### @absolutejs/dispatch 支持 ESM 还是 CommonJS？

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

### @absolutejs/dispatch 可以商用吗？

@absolutejs/dispatch 使用 BSL-1\.1 许可。声明的许可为 BSL-1\.1，请阅读许可原文确认使用条件。以上为概括，不构成法律意见，请以许可原文为准。

### @absolutejs/dispatch 的下载量有多少？

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

### @absolutejs/dispatch 还在维护吗？

最新版本 0\.9\.1 发布于 2026/10/22（16 天前）；至今共发布 22 个版本，其中过去 12 个月发布了 22 个；首次发布于 2026/05/31。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

