# @clipboard-health/notifications 中文文档

规范地址：https://npmcn.com/package/%40clipboard-health/notifications/

版本：3\.17\.17
npm 元数据更新时间：2026-11-05T04:55:31.543Z
中文译文更新时间：2026-11-10T01:53:42.531Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40clipboard-health/notifications
原文对照：https://npmcn.com/package/%40clipboard-health/notifications/?lang=original
项目主页：https://github.com/ClipboardHealth/core-utils#readme
源码仓库：https://github.com/ClipboardHealth/core-utils

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

- 包体积（解压后）：138 kB，74 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- 运行依赖：5 个
- 首次发布：2025-09-16T20:38:41.148Z
- 最新版本发布：2026-11-05T04:55:31.379Z
- 近 30 天下载量：21764（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：436（近 12 个月 393 个）

## 中文简介

通过第三方供应商发送通知.

## 安装

```sh
npm install @clipboard-health/notifications
```

也可以使用：`pnpm add @clipboard-health/notifications`、`yarn add @clipboard-health/notifications`、`bun add @clipboard-health/notifications`

## README

# @ 剪贴板- 健康/ 通知

通过第三方供应商发送通知.

## 目录

-   [使用量](https://npmcn.com/package/%40clipboard-health/notifications/#readme-usage)
    -   [`triggerChunked`](https://npmcn.com/package/%40clipboard-health/notifications/#readme-triggerchunked)
-   [调试](https://npmcn.com/package/%40clipboard-health/notifications/#readme-debugging)
-   [地方发展指挥部](https://npmcn.com/package/%40clipboard-health/notifications/#readme-local-development-commands)

## 使用量

### `triggerChunked`

`triggerChunked` 将全部、不可改变的触发请求保存在工作有序时间,消除有呆滞数据的问题,将请求排入提供者限制范围,以及在工作执行时间更新请求时可能出现的一能关键冲突.

1.  搜索您的服务 `triggerNotification.constants.ts`, (中文(简体) ), `triggerNotification.job.ts` 财务报告和审定财务报表 `notificationClient.provider.ts`如果它们不存在,则创建它们:
    
    ```ts
    // triggerNotification.constants.ts
    export const TRIGGER_NOTIFICATION_JOB_NAME = "TriggerNotificationJob";
    ```
    
    ```ts
    // triggerNotification.job.ts
    import type { BaseHandler } from "@clipboard-health/background-jobs-adapter";
    import {
      ERROR_CODES,
      isClientError,
      type NotificationClient,
      type SerializableTriggerChunkedRequest,
      toTriggerChunkedRequest,
    } from "@clipboard-health/notifications";
    import { isFailure } from "@clipboard-health/util-ts";
    
    import { CBHLogger } from "./setup";
    import { TRIGGER_NOTIFICATION_JOB_NAME } from "./triggerNotification.constants";
    
    /**
     * For @clipboard-health/mongo-jobs:
     * 1. Implement `HandlerInterface<SerializableTriggerChunkedRequest>`.
     * 2. The 10 default `maxAttempts` with exponential backoff of `2^attemptsCount` means ~17 minutes
     *    of cumulative delay. If your notification could be stale before this, set
     *    `SerializableTriggerChunkedRequest.expiresAt` when enqueueing.
     *
     * For @clipboard-health/background-jobs-postgres:
     * 1. Implement `Handler<SerializableTriggerChunkedRequest>`.
     * 2. The 20 default `maxRetryAttempts` with exponential backoff of `10s * 2^(attempt - 1)` means
     *    ~121 days of cumulative delay. If your notification could be stale before this, set
     *    `maxRetryAttempts` (and `SerializableTriggerChunkedRequest.expiresAt`) when enqueueing.
     */
    export class TriggerNotificationJob implements BaseHandler<SerializableTriggerChunkedRequest> {
      // For background-jobs-postgres, use `public static queueName = TRIGGER_NOTIFICATION_JOB_NAME;`
      public name = TRIGGER_NOTIFICATION_JOB_NAME;
      private readonly logger = new CBHLogger({
        defaultMeta: {
          logContext: TRIGGER_NOTIFICATION_JOB_NAME,
        },
      });
    
      public constructor(
        // @Inject(NOTIFICATION_CLIENT_TOKEN)
        private readonly client: NotificationClient,
      ) {}
    
      public async perform(
        data: SerializableTriggerChunkedRequest,
        /**
         * For mongo-jobs, implement `BackgroundJobType<SerializableTriggerChunkedRequest>`, which has
         *    `_id`, `attemptsCount`, and `uniqueKey`.
         *
         * For background-jobs-postgres, implement `Job<SerializableTriggerChunkedRequest>`, which has
         *    `id`, `retryAttempts`, and `idempotencyKey`.
         */
        job: { _id: string; attemptsCount: number; uniqueKey?: string },
      ) {
        const metadata = {
          // For background-jobs-postgres, this is called `retryAttempts`.
          attempt: job.attemptsCount + 1,
          jobId: job._id,
          recipientCount: data.body.recipients.length,
          workflowKey: data.workflowKey,
        };
        this.logger.info("TriggerNotificationJob processing", metadata);
    
        try {
          const request = toTriggerChunkedRequest(data, {
            attempt: metadata.attempt,
            idempotencyKey: job.uniqueKey ?? metadata.jobId,
            // In case the tests are moving the time forward we need to ensure notifications don't expire.
            // ...(isTestMode && { expiresAt: new Date(3000, 0, 1) }),
          });
          const result = await this.client.triggerChunked(request);
    
          if (isFailure(result)) {
            const code = result.error.issues[0]?.code;
    
            // Expired: the notification is no longer relevant (e.g. a reminder for an event that
            // has already started). Not actionable, so log at WARN and move on.
            if (code === ERROR_CODES.expired) {
              this.logger.warn("TriggerNotificationJob skipped due to expiry", { ...metadata });
              return;
            }
    
            // The request itself is broken (empty/over-max recipients, invalid expiresAt or
            // idempotency key, missing signing key, or a non-429 4xx from the provider).
            // Retrying won't help — log at ERROR so it pages and we can fix the caller.
            if (isClientError(code)) {
              this.logger.error("TriggerNotificationJob skipped due to client error", {
                ...metadata,
                error: result.error,
              });
              return;
            }
    
            // Everything else (rateLimited, unknown, 5xx, network) is transient — throw so the
            // job retries with backoff.
            throw result.error;
          }
    
          const success = "TriggerNotificationJob success";
          this.logger.info(success, { ...metadata, response: result.value });
          // For background-jobs-postgres, return the `success` string result.
        } catch (error) {
          this.logger.error("TriggerNotificationJob failure", { ...metadata, error });
          throw error;
        }
      }
    }
    ```
    
    ```ts
    import { NotificationClient } from "@clipboard-health/notifications";
    
    import { CBHLogger, type Provider, toLogger, tracer } from "./setup";
    
    export const NOTIFICATION_CLIENT_TOKEN = "NOTIFICATION_CLIENT";
    
    export const NotificationClientProvider: Provider<NotificationClient> = {
      provide: NOTIFICATION_CLIENT_TOKEN,
      useFactory: (): NotificationClient =>
        new NotificationClient({
          apiKey: process.env["KNOCK_API_KEY"]!,
          logger: toLogger(
            new CBHLogger({
              defaultMeta: { context: "NotificationClient" },
            }),
          ),
          tracer,
        }),
    };
    ```
    
2.  搜索服务中存储工作流程密钥的常数 。 如果没有,就创造它:
    
    ```ts
    /* eslint sort-keys: "error" */
    
    /**
     * Alphabetical list of workflow keys.
     */
    export const WORKFLOW_KEYS = {
      eventStartingReminder: "event-starting-reminder",
    } as const;
    ```
    
3.  构建您的 `SerializableTriggerChunkedRequest` 并调整你的工作。 想着排队 `TriggerNotificationJob` 作为一项职能,要求以最佳做法的方式发送通知。 你不应该打电话 `triggerChunked` 直接说 例如,如果您的通知被延迟, 创建一个未来运行的背景任务, 做任何必要的检查以确保您应该通知, 然后排队 `TriggerNotificationJob`。 。 。 .
    
    ```ts
    import type { BackgroundJobsAdapter } from "@clipboard-health/background-jobs-adapter";
    import type { SerializableTriggerChunkedRequest } from "@clipboard-health/notifications";
    
    import { BackgroundJobsService } from "./setup";
    import { TRIGGER_NOTIFICATION_JOB_NAME } from "./triggerNotification.constants";
    import { WORKFLOW_KEYS } from "./workflowKeys";
    
    /**
     * Enqueue a notification job in the same database transaction as the changes it's notifying about.
     * The `session` option is called `transaction` in `background-jobs-postgres`.
     */
    async function enqueueTriggerNotificationJob(adapter: BackgroundJobsAdapter) {
      // Assume this comes from a database and are used as template variables...
      const notificationData = {
        favoriteColor: "blue",
        // Use @clipboard-health/date-time's formatShortDateTime in your service for consistency.
        favoriteAt: new Date().toISOString(),
        secret: "2",
      };
    
      const jobData: SerializableTriggerChunkedRequest = {
        // Important: Read the TypeDoc documentation for additional context.
        body: {
          recipients: ["userId-1", "userId-2"],
          data: notificationData,
        },
        // Helpful when controlling notifications with feature flags.
        dryRun: false,
        // Set expiresAt at enqueue-time so it remains stable across job retries. Use date-fns in your
        // service instead of this manual calculation.
        expiresAt: new Date(Date.now() + 60 * 60_000).toISOString(),
        // Keys to redact from logs
        keysToRedact: ["secret"],
        workflowKey: WORKFLOW_KEYS.eventStartingReminder,
      };
    
      // Option 1 (default): Automatically use background job ID as idempotency key.
      await adapter.enqueue(TRIGGER_NOTIFICATION_JOB_NAME, jobData, { session: "..." });
    
      // Option 2 (advanced): Provide custom idempotency key to job and notification libraries for more
      // control. You'd use this to provide enqueue-time deduplication. For example, if you enqueue when
      // a user clicks a button and only want them to receive one notification.
      await adapter.enqueue(TRIGGER_NOTIFICATION_JOB_NAME, jobData, {
        // Called `idempotencyKey` in `background-jobs-postgres`.
        unique: `meeting-123-reminder`,
        session: "...",
      });
    }
    
    void enqueueTriggerNotificationJob(
      // Use your instance of `@clipboard-health/mongo-jobs` or `@clipboard-health/background-jobs-postgres` here.
      new BackgroundJobsService(),
    );
    ```
    

## 调试

Knock 的回复和错误的日志条目包括 `knockRequestId`敲门的身份证明. 过滤器由 **请求编号** 下级 **观察性** > 翻译: **日志** 内 [敲打仪表板](https://dashboard.knock.app/) 看着Knock自己对请求和回应的看法 尸体 尸体 尸体 尸体 尸体 敲打其中的长值, 标记其下降 `__knock_truncated__` 或者说 `[TRUNCATED]`,并保留30天 [API 日志](https://docs.knock.app/developer-tools/api-logs)。 。 。 .

## 地方发展指挥部

见 [`package.json`](https://npmcn.com/package/%40clipboard-health/notifications/package.json) `scripts` 用于命令列表.

## 常见问题

### @clipboard-health/notifications 是什么？

通过第三方供应商发送通知\.

### 如何安装 @clipboard-health/notifications？

在项目目录运行 npm install @clipboard-health/notifications，或使用 pnpm add / yarn add / bun add。

### @clipboard-health/notifications 支持 TypeScript 吗？

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

### @clipboard-health/notifications 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @clipboard-health/notifications 可以商用吗？

@clipboard-health/notifications 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @clipboard-health/notifications 的下载量有多少？

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

### @clipboard-health/notifications 还在维护吗？

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

